Commit | Line | Data |
---|---|---|
cb1c44c2 | 1 | /* |
1810611d | 2 | * Copyright (c) 1980, 1986 Regents of the University of California. |
5b519e94 | 3 | * All rights reserved. |
cb1c44c2 | 4 | * |
5b519e94 KB |
5 | * Redistribution and use in source and binary forms are permitted |
6 | * provided that this notice is preserved and that due credit is given | |
7 | * to the University of California at Berkeley. The name of the University | |
8 | * may not be used to endorse or promote products derived from this | |
9 | * software without specific prior written permission. This software | |
10 | * is provided ``as is'' without express or implied warranty. | |
11 | * | |
12 | * @(#)route.c 7.3 (Berkeley) %G% | |
cb1c44c2 | 13 | */ |
9d03c806 | 14 | |
a0369dcf JB |
15 | #include "param.h" |
16 | #include "systm.h" | |
17 | #include "mbuf.h" | |
18 | #include "protosw.h" | |
19 | #include "socket.h" | |
20 | #include "dir.h" | |
21 | #include "user.h" | |
22 | #include "ioctl.h" | |
23 | #include "errno.h" | |
f4d55810 | 24 | |
a0369dcf JB |
25 | #include "if.h" |
26 | #include "af.h" | |
27 | #include "route.h" | |
9d03c806 | 28 | |
a13c006d | 29 | int rttrash; /* routes not in table but not freed */ |
a9ea8834 | 30 | struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ |
c50b0999 | 31 | int rthashsize = RTHASHSIZ; /* for netstat, etc. */ |
a9ea8834 | 32 | |
9d03c806 SL |
33 | /* |
34 | * Packet routing routines. | |
35 | */ | |
f6311fb6 | 36 | rtalloc(ro) |
9d03c806 SL |
37 | register struct route *ro; |
38 | { | |
a9ea8834 | 39 | register struct rtentry *rt; |
9d03c806 | 40 | register struct mbuf *m; |
0b33b6b5 | 41 | register u_long hash; |
9d03c806 | 42 | struct sockaddr *dst = &ro->ro_dst; |
c50b0999 | 43 | int (*match)(), doinghost, s; |
a9ea8834 | 44 | struct afhash h; |
e8de4b5e | 45 | u_int af = dst->sa_family; |
a9ea8834 | 46 | struct mbuf **table; |
9d03c806 | 47 | |
7eb1f827 MK |
48 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) |
49 | return; /* XXX */ | |
e65dcd4c SL |
50 | if (af >= AF_MAX) |
51 | return; | |
9d03c806 | 52 | (*afswitch[af].af_hash)(dst, &h); |
fc74f0c9 | 53 | match = afswitch[af].af_netmatch; |
a9ea8834 | 54 | hash = h.afh_hosthash, table = rthost, doinghost = 1; |
c50b0999 | 55 | s = splnet(); |
a9ea8834 | 56 | again: |
b50b85d9 | 57 | for (m = table[RTHASHMOD(hash)]; m; m = m->m_next) { |
fc74f0c9 SL |
58 | rt = mtod(m, struct rtentry *); |
59 | if (rt->rt_hash != hash) | |
60 | continue; | |
90137845 SL |
61 | if ((rt->rt_flags & RTF_UP) == 0 || |
62 | (rt->rt_ifp->if_flags & IFF_UP) == 0) | |
63 | continue; | |
a9ea8834 SL |
64 | if (doinghost) { |
65 | if (bcmp((caddr_t)&rt->rt_dst, (caddr_t)dst, | |
66 | sizeof (*dst))) | |
67 | continue; | |
68 | } else { | |
69 | if (rt->rt_dst.sa_family != af || | |
70 | !(*match)(&rt->rt_dst, dst)) | |
71 | continue; | |
72 | } | |
c50b0999 MK |
73 | rt->rt_refcnt++; |
74 | splx(s); | |
75 | if (dst == &wildcard) | |
76 | rtstat.rts_wildcard++; | |
77 | ro->ro_rt = rt; | |
78 | return; | |
9d03c806 | 79 | } |
c50b0999 | 80 | if (doinghost) { |
a9ea8834 SL |
81 | doinghost = 0; |
82 | hash = h.afh_nethash, table = rtnet; | |
83 | goto again; | |
84 | } | |
85 | /* | |
86 | * Check for wildcard gateway, by convention network 0. | |
87 | */ | |
c50b0999 | 88 | if (dst != &wildcard) { |
a9ea8834 SL |
89 | dst = &wildcard, hash = 0; |
90 | goto again; | |
91 | } | |
c50b0999 MK |
92 | splx(s); |
93 | rtstat.rts_unreach++; | |
9d03c806 SL |
94 | } |
95 | ||
f6311fb6 | 96 | rtfree(rt) |
c124e997 SL |
97 | register struct rtentry *rt; |
98 | { | |
f6311fb6 | 99 | |
c124e997 | 100 | if (rt == 0) |
a1edc12b | 101 | panic("rtfree"); |
c124e997 | 102 | rt->rt_refcnt--; |
a13c006d BJ |
103 | if (rt->rt_refcnt == 0 && (rt->rt_flags&RTF_UP) == 0) { |
104 | rttrash--; | |
105 | (void) m_free(dtom(rt)); | |
106 | } | |
c124e997 SL |
107 | } |
108 | ||
464f931f SL |
109 | /* |
110 | * Force a routing table entry to the specified | |
111 | * destination to go through the given gateway. | |
112 | * Normally called as a result of a routing redirect | |
113 | * message from the network layer. | |
114 | * | |
115 | * N.B.: must be called at splnet or higher | |
116 | * | |
464f931f | 117 | */ |
92c26501 MK |
118 | rtredirect(dst, gateway, flags, src) |
119 | struct sockaddr *dst, *gateway, *src; | |
7eb1f827 | 120 | int flags; |
464f931f SL |
121 | { |
122 | struct route ro; | |
123 | register struct rtentry *rt; | |
124 | ||
125 | /* verify the gateway is directly reachable */ | |
cba69651 | 126 | if (ifa_ifwithnet(gateway) == 0) { |
a9ea8834 | 127 | rtstat.rts_badredirect++; |
464f931f | 128 | return; |
a9ea8834 | 129 | } |
464f931f | 130 | ro.ro_dst = *dst; |
a9ea8834 | 131 | ro.ro_rt = 0; |
464f931f SL |
132 | rtalloc(&ro); |
133 | rt = ro.ro_rt; | |
92c26501 MK |
134 | #define equal(a1, a2) \ |
135 | (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof(struct sockaddr)) == 0) | |
136 | /* | |
137 | * If the redirect isn't from our current router for this dst, | |
2e9c4868 MK |
138 | * it's either old or wrong. If it redirects us to ourselves, |
139 | * we have a routing loop, perhaps as a result of an interface | |
140 | * going down recently. | |
92c26501 | 141 | */ |
2e9c4868 | 142 | if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { |
92c26501 | 143 | rtstat.rts_badredirect++; |
703b8607 MK |
144 | if (rt) |
145 | rtfree(rt); | |
92c26501 MK |
146 | return; |
147 | } | |
a9ea8834 | 148 | /* |
48afbdc5 SL |
149 | * Create a new entry if we just got back a wildcard entry |
150 | * or the the lookup failed. This is necessary for hosts | |
151 | * which use routing redirects generated by smart gateways | |
152 | * to dynamically build the routing tables. | |
a9ea8834 | 153 | */ |
48afbdc5 SL |
154 | if (rt && |
155 | (*afswitch[dst->sa_family].af_netmatch)(&wildcard, &rt->rt_dst)) { | |
156 | rtfree(rt); | |
157 | rt = 0; | |
158 | } | |
a9ea8834 | 159 | if (rt == 0) { |
157d4f92 | 160 | rtinit(dst, gateway, (int)SIOCADDRT, |
92c26501 | 161 | (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC); |
a9ea8834 SL |
162 | rtstat.rts_dynamic++; |
163 | return; | |
164 | } | |
464f931f SL |
165 | /* |
166 | * Don't listen to the redirect if it's | |
167 | * for a route to an interface. | |
464f931f | 168 | */ |
a9ea8834 | 169 | if (rt->rt_flags & RTF_GATEWAY) { |
7eb1f827 MK |
170 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
171 | /* | |
66798f86 | 172 | * Changing from route to net => route to host. |
7eb1f827 MK |
173 | * Create new route, rather than smashing route to net. |
174 | */ | |
157d4f92 MK |
175 | rtinit(dst, gateway, (int)SIOCADDRT, |
176 | flags | RTF_DYNAMIC); | |
3b66e456 | 177 | rtstat.rts_dynamic++; |
7eb1f827 MK |
178 | } else { |
179 | /* | |
180 | * Smash the current notion of the gateway to | |
3b66e456 | 181 | * this destination. |
7eb1f827 MK |
182 | */ |
183 | rt->rt_gateway = *gateway; | |
31150b52 MK |
184 | rt->rt_flags |= RTF_MODIFIED; |
185 | rtstat.rts_newgateway++; | |
7eb1f827 | 186 | } |
92c26501 MK |
187 | } else |
188 | rtstat.rts_badredirect++; | |
ff320836 | 189 | rtfree(rt); |
464f931f SL |
190 | } |
191 | ||
a62dd253 SL |
192 | /* |
193 | * Routing table ioctl interface. | |
194 | */ | |
195 | rtioctl(cmd, data) | |
196 | int cmd; | |
197 | caddr_t data; | |
198 | { | |
199 | ||
200 | if (cmd != SIOCADDRT && cmd != SIOCDELRT) | |
201 | return (EINVAL); | |
202 | if (!suser()) | |
203 | return (u.u_error); | |
204 | return (rtrequest(cmd, (struct rtentry *)data)); | |
205 | } | |
206 | ||
9d03c806 | 207 | /* |
c124e997 | 208 | * Carry out a request to change the routing table. Called by |
a9ea8834 SL |
209 | * interfaces at boot time to make their ``local routes'' known, |
210 | * for ioctl's, and as the result of routing redirects. | |
9d03c806 | 211 | */ |
a13c006d | 212 | rtrequest(req, entry) |
9d03c806 | 213 | int req; |
a13c006d | 214 | register struct rtentry *entry; |
9d03c806 | 215 | { |
9d03c806 | 216 | register struct mbuf *m, **mprev; |
92c26501 | 217 | struct mbuf **mfirst; |
a13c006d | 218 | register struct rtentry *rt; |
9d03c806 | 219 | struct afhash h; |
0b33b6b5 | 220 | int s, error = 0, (*match)(); |
e8de4b5e | 221 | u_int af; |
0b33b6b5 | 222 | u_long hash; |
cba69651 | 223 | struct ifaddr *ifa; |
ae674e00 | 224 | struct ifaddr *ifa_ifwithdstaddr(); |
9d03c806 | 225 | |
a13c006d | 226 | af = entry->rt_dst.sa_family; |
e65dcd4c SL |
227 | if (af >= AF_MAX) |
228 | return (EAFNOSUPPORT); | |
a13c006d BJ |
229 | (*afswitch[af].af_hash)(&entry->rt_dst, &h); |
230 | if (entry->rt_flags & RTF_HOST) { | |
d7887ae9 | 231 | hash = h.afh_hosthash; |
b50b85d9 | 232 | mprev = &rthost[RTHASHMOD(hash)]; |
d7887ae9 BJ |
233 | } else { |
234 | hash = h.afh_nethash; | |
b50b85d9 | 235 | mprev = &rtnet[RTHASHMOD(hash)]; |
d7887ae9 BJ |
236 | } |
237 | match = afswitch[af].af_netmatch; | |
c124e997 | 238 | s = splimp(); |
92c26501 | 239 | for (mfirst = mprev; m = *mprev; mprev = &m->m_next) { |
9d03c806 | 240 | rt = mtod(m, struct rtentry *); |
fc74f0c9 | 241 | if (rt->rt_hash != hash) |
9d03c806 | 242 | continue; |
a13c006d | 243 | if (entry->rt_flags & RTF_HOST) { |
a13c006d | 244 | if (!equal(&rt->rt_dst, &entry->rt_dst)) |
9d03c806 SL |
245 | continue; |
246 | } else { | |
a13c006d BJ |
247 | if (rt->rt_dst.sa_family != entry->rt_dst.sa_family || |
248 | (*match)(&rt->rt_dst, &entry->rt_dst) == 0) | |
9d03c806 SL |
249 | continue; |
250 | } | |
a13c006d | 251 | if (equal(&rt->rt_gateway, &entry->rt_gateway)) |
d7887ae9 | 252 | break; |
c124e997 | 253 | } |
9d03c806 SL |
254 | switch (req) { |
255 | ||
256 | case SIOCDELRT: | |
d7887ae9 BJ |
257 | if (m == 0) { |
258 | error = ESRCH; | |
259 | goto bad; | |
260 | } | |
a13c006d | 261 | *mprev = m->m_next; |
d7887ae9 | 262 | if (rt->rt_refcnt > 0) { |
a13c006d BJ |
263 | rt->rt_flags &= ~RTF_UP; |
264 | rttrash++; | |
265 | m->m_next = 0; | |
266 | } else | |
267 | (void) m_free(m); | |
9d03c806 SL |
268 | break; |
269 | ||
270 | case SIOCADDRT: | |
a13c006d | 271 | if (m) { |
d7887ae9 BJ |
272 | error = EEXIST; |
273 | goto bad; | |
274 | } | |
92c26501 MK |
275 | if ((entry->rt_flags & RTF_GATEWAY) == 0) { |
276 | /* | |
277 | * If we are adding a route to an interface, | |
278 | * and the interface is a pt to pt link | |
279 | * we should search for the destination | |
280 | * as our clue to the interface. Otherwise | |
281 | * we can use the local address. | |
282 | */ | |
a25c073d | 283 | ifa = 0; |
ae674e00 KS |
284 | if (entry->rt_flags & RTF_HOST) |
285 | ifa = ifa_ifwithdstaddr(&entry->rt_dst); | |
a25c073d | 286 | if (ifa == 0) |
ae674e00 KS |
287 | ifa = ifa_ifwithaddr(&entry->rt_gateway); |
288 | } else { | |
92c26501 MK |
289 | /* |
290 | * If we are adding a route to a remote net | |
291 | * or host, the gateway may still be on the | |
292 | * other end of a pt to pt link. | |
293 | */ | |
ae674e00 KS |
294 | ifa = ifa_ifwithdstaddr(&entry->rt_gateway); |
295 | } | |
cba69651 MK |
296 | if (ifa == 0) { |
297 | ifa = ifa_ifwithnet(&entry->rt_gateway); | |
298 | if (ifa == 0) { | |
a13c006d BJ |
299 | error = ENETUNREACH; |
300 | goto bad; | |
301 | } | |
302 | } | |
cce93e4b | 303 | m = m_get(M_DONTWAIT, MT_RTABLE); |
c124e997 SL |
304 | if (m == 0) { |
305 | error = ENOBUFS; | |
d7887ae9 | 306 | goto bad; |
c124e997 | 307 | } |
92c26501 MK |
308 | m->m_next = *mfirst; |
309 | *mfirst = m; | |
9d03c806 | 310 | m->m_off = MMINOFF; |
f6311fb6 | 311 | m->m_len = sizeof (struct rtentry); |
9d03c806 | 312 | rt = mtod(m, struct rtentry *); |
d7887ae9 | 313 | rt->rt_hash = hash; |
a13c006d BJ |
314 | rt->rt_dst = entry->rt_dst; |
315 | rt->rt_gateway = entry->rt_gateway; | |
92c26501 MK |
316 | rt->rt_flags = RTF_UP | |
317 | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); | |
fc74f0c9 | 318 | rt->rt_refcnt = 0; |
a13c006d | 319 | rt->rt_use = 0; |
cba69651 | 320 | rt->rt_ifp = ifa->ifa_ifp; |
9d03c806 SL |
321 | break; |
322 | } | |
c124e997 SL |
323 | bad: |
324 | splx(s); | |
325 | return (error); | |
9d03c806 | 326 | } |
f6311fb6 SL |
327 | |
328 | /* | |
329 | * Set up a routing table entry, normally | |
330 | * for an interface. | |
331 | */ | |
2a63f7ba | 332 | rtinit(dst, gateway, cmd, flags) |
f6311fb6 | 333 | struct sockaddr *dst, *gateway; |
2a63f7ba | 334 | int cmd, flags; |
f6311fb6 SL |
335 | { |
336 | struct rtentry route; | |
f6311fb6 | 337 | |
a13c006d | 338 | bzero((caddr_t)&route, sizeof (route)); |
f6311fb6 SL |
339 | route.rt_dst = *dst; |
340 | route.rt_gateway = *gateway; | |
341 | route.rt_flags = flags; | |
0c33c832 | 342 | (void) rtrequest(cmd, &route); |
f6311fb6 | 343 | } |