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