Commit | Line | Data |
---|---|---|
cb1c44c2 | 1 | /* |
1810611d | 2 | * Copyright (c) 1980, 1986 Regents of the University of California. |
5b519e94 | 3 | * All rights reserved. |
cb1c44c2 | 4 | * |
dbf0c423 | 5 | * %sccs.include.redist.c% |
5b519e94 | 6 | * |
9bdb9951 | 7 | * @(#)route.c 7.19 (Berkeley) %G% |
cb1c44c2 | 8 | */ |
d301d150 | 9 | #include "machine/reg.h" |
ea372425 | 10 | |
a0369dcf JB |
11 | #include "param.h" |
12 | #include "systm.h" | |
a0369dcf | 13 | #include "user.h" |
ea372425 MK |
14 | #include "proc.h" |
15 | #include "mbuf.h" | |
16 | #include "socket.h" | |
17 | #include "socketvar.h" | |
18 | #include "domain.h" | |
19 | #include "protosw.h" | |
a0369dcf | 20 | #include "errno.h" |
ea372425 | 21 | #include "ioctl.h" |
f4d55810 | 22 | |
a0369dcf JB |
23 | #include "if.h" |
24 | #include "af.h" | |
25 | #include "route.h" | |
ea372425 | 26 | #include "raw_cb.h" |
8bbafbd9 MK |
27 | #include "../netinet/in.h" |
28 | #include "../netinet/in_var.h" | |
ea372425 | 29 | |
b72a6efb | 30 | #include "../netns/ns.h" |
d301d150 | 31 | #include "machine/mtpr.h" |
ea372425 | 32 | #include "netisr.h" |
983eada9 | 33 | #define SA(p) ((struct sockaddr *)(p)) |
ea372425 | 34 | |
a13c006d | 35 | int rttrash; /* routes not in table but not freed */ |
a9ea8834 | 36 | struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ |
c50b0999 | 37 | int rthashsize = RTHASHSIZ; /* for netstat, etc. */ |
a9ea8834 | 38 | |
8bbafbd9 MK |
39 | static int rtinits_done = 0; |
40 | struct radix_node_head *ns_rnhead, *in_rnhead; | |
ea372425 | 41 | struct radix_node *rn_match(), *rn_delete(), *rn_addroute(); |
8bbafbd9 MK |
42 | rtinitheads() |
43 | { | |
44 | if (rtinits_done == 0 && | |
45 | rn_inithead(&ns_rnhead, 16, AF_NS) && | |
46 | rn_inithead(&in_rnhead, 32, AF_INET)) | |
47 | rtinits_done = 1; | |
48 | } | |
49 | ||
9d03c806 SL |
50 | /* |
51 | * Packet routing routines. | |
52 | */ | |
f6311fb6 | 53 | rtalloc(ro) |
9d03c806 | 54 | register struct route *ro; |
ea372425 MK |
55 | { |
56 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) | |
57 | return; /* XXX */ | |
58 | ro->ro_rt = rtalloc1(&ro->ro_dst, 1); | |
59 | } | |
60 | ||
61 | struct rtentry * | |
62 | rtalloc1(dst, report) | |
983eada9 | 63 | register struct sockaddr *dst; |
ea372425 | 64 | int report; |
9d03c806 | 65 | { |
8bbafbd9 | 66 | register struct radix_node_head *rnh; |
983eada9 | 67 | register struct rtentry *rt; |
8bbafbd9 | 68 | register struct radix_node *rn; |
983eada9 | 69 | struct rtentry *newrt = 0; |
9bdb9951 | 70 | int s = splnet(), err = 0, msgtype = RTM_MISS; |
9d03c806 | 71 | |
983eada9 | 72 | for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); ) |
8bbafbd9 MK |
73 | rnh = rnh->rnh_next; |
74 | if (rnh && rnh->rnh_treetop && | |
ea372425 | 75 | (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) && |
8bbafbd9 | 76 | ((rn->rn_flags & RNF_ROOT) == 0)) { |
983eada9 | 77 | newrt = rt = (struct rtentry *)rn; |
044f6a7f | 78 | if (report && (rt->rt_flags & RTF_CLONING)) { |
9bdb9951 KS |
79 | if ((err = rtrequest(RTM_RESOLVE, dst, SA(0), |
80 | SA(0), 0, &newrt)) || | |
81 | ((rt->rt_flags & RTF_XRESOLVE) | |
82 | && (msgtype = RTM_RESOLVE))) /* intended! */ | |
83 | goto miss; | |
044f6a7f KS |
84 | } else |
85 | rt->rt_refcnt++; | |
ea372425 | 86 | } else { |
8bbafbd9 | 87 | rtstat.rts_unreach++; |
044f6a7f | 88 | miss: if (report) |
9bdb9951 | 89 | rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err); |
ea372425 | 90 | } |
c50b0999 | 91 | splx(s); |
983eada9 | 92 | return (newrt); |
9d03c806 SL |
93 | } |
94 | ||
f6311fb6 | 95 | rtfree(rt) |
c124e997 SL |
96 | register struct rtentry *rt; |
97 | { | |
044f6a7f | 98 | register struct ifaddr *ifa; |
c124e997 | 99 | if (rt == 0) |
a1edc12b | 100 | panic("rtfree"); |
c124e997 | 101 | rt->rt_refcnt--; |
044f6a7f | 102 | if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) { |
a13c006d | 103 | rttrash--; |
ea372425 | 104 | if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
8bbafbd9 | 105 | panic ("rtfree 2"); |
ea372425 | 106 | free((caddr_t)rt, M_RTABLE); |
a13c006d | 107 | } |
c124e997 SL |
108 | } |
109 | ||
464f931f SL |
110 | /* |
111 | * Force a routing table entry to the specified | |
112 | * destination to go through the given gateway. | |
113 | * Normally called as a result of a routing redirect | |
114 | * message from the network layer. | |
115 | * | |
8bbafbd9 | 116 | * N.B.: must be called at splnet |
464f931f | 117 | * |
464f931f | 118 | */ |
b72a6efb KS |
119 | rtredirect(dst, gateway, netmask, flags, src, rtp) |
120 | struct sockaddr *dst, *gateway, *netmask, *src; | |
7eb1f827 | 121 | int flags; |
b72a6efb | 122 | struct rtentry **rtp; |
464f931f | 123 | { |
464f931f | 124 | register struct rtentry *rt; |
b72a6efb KS |
125 | int error = 0; |
126 | short *stat = 0; | |
464f931f SL |
127 | |
128 | /* verify the gateway is directly reachable */ | |
cba69651 | 129 | if (ifa_ifwithnet(gateway) == 0) { |
b72a6efb KS |
130 | error = ENETUNREACH; |
131 | goto done; | |
a9ea8834 | 132 | } |
b72a6efb | 133 | rt = rtalloc1(dst, 0); |
92c26501 MK |
134 | /* |
135 | * If the redirect isn't from our current router for this dst, | |
2e9c4868 MK |
136 | * it's either old or wrong. If it redirects us to ourselves, |
137 | * we have a routing loop, perhaps as a result of an interface | |
138 | * going down recently. | |
92c26501 | 139 | */ |
b72a6efb KS |
140 | #define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0) |
141 | if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway)) | |
142 | error = EINVAL; | |
143 | else if (ifa_ifwithaddr(gateway)) | |
144 | error = EHOSTUNREACH; | |
145 | if (error) | |
146 | goto done; | |
a9ea8834 | 147 | /* |
48afbdc5 SL |
148 | * Create a new entry if we just got back a wildcard entry |
149 | * or the the lookup failed. This is necessary for hosts | |
150 | * which use routing redirects generated by smart gateways | |
151 | * to dynamically build the routing tables. | |
8bbafbd9 | 152 | */ |
b72a6efb KS |
153 | if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2)) |
154 | goto create; | |
464f931f SL |
155 | /* |
156 | * Don't listen to the redirect if it's | |
157 | * for a route to an interface. | |
464f931f | 158 | */ |
a9ea8834 | 159 | if (rt->rt_flags & RTF_GATEWAY) { |
7eb1f827 MK |
160 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
161 | /* | |
66798f86 | 162 | * Changing from route to net => route to host. |
7eb1f827 MK |
163 | * Create new route, rather than smashing route to net. |
164 | */ | |
b72a6efb KS |
165 | create: |
166 | flags |= RTF_GATEWAY | RTF_DYNAMIC; | |
167 | error = rtrequest((int)RTM_ADD, dst, gateway, | |
983eada9 | 168 | SA(0), flags, |
b72a6efb KS |
169 | (struct rtentry **)0); |
170 | stat = &rtstat.rts_dynamic; | |
7eb1f827 MK |
171 | } else { |
172 | /* | |
173 | * Smash the current notion of the gateway to | |
b72a6efb | 174 | * this destination. Should check about netmask!!! |
7eb1f827 | 175 | */ |
ea372425 MK |
176 | if (gateway->sa_len <= rt->rt_gateway->sa_len) { |
177 | Bcopy(gateway, rt->rt_gateway, gateway->sa_len); | |
178 | rt->rt_flags |= RTF_MODIFIED; | |
b72a6efb KS |
179 | flags |= RTF_MODIFIED; |
180 | stat = &rtstat.rts_newgateway; | |
ea372425 | 181 | } else |
b72a6efb | 182 | error = ENOSPC; |
7eb1f827 | 183 | } |
92c26501 | 184 | } else |
b72a6efb KS |
185 | error = EHOSTUNREACH; |
186 | done: | |
187 | if (rt) { | |
188 | if (rtp && !error) | |
189 | *rtp = rt; | |
190 | else | |
191 | rtfree(rt); | |
192 | } | |
193 | if (error) | |
92c26501 | 194 | rtstat.rts_badredirect++; |
b72a6efb KS |
195 | else |
196 | (stat && (*stat)++); | |
197 | rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error); | |
464f931f SL |
198 | } |
199 | ||
a62dd253 | 200 | /* |
b72a6efb KS |
201 | * Routing table ioctl interface. |
202 | */ | |
ea372425 MK |
203 | rtioctl(req, data) |
204 | int req; | |
a62dd253 SL |
205 | caddr_t data; |
206 | { | |
ea372425 MK |
207 | #ifndef COMPAT_43 |
208 | return (EOPNOTSUPP); | |
209 | #else | |
210 | register struct ortentry *entry = (struct ortentry *)data; | |
211 | int error; | |
212 | struct sockaddr *netmask = 0; | |
a62dd253 | 213 | |
ea372425 MK |
214 | if (req == SIOCADDRT) |
215 | req = RTM_ADD; | |
216 | else if (req == SIOCDELRT) | |
217 | req = RTM_DELETE; | |
218 | else | |
a62dd253 | 219 | return (EINVAL); |
ea372425 | 220 | |
06c16dfa KM |
221 | if (error = suser(u.u_cred, &u.u_acflag)) |
222 | return (error); | |
a7b1cd9a MK |
223 | #if BYTE_ORDER != BIG_ENDIAN |
224 | if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { | |
225 | entry->rt_dst.sa_family = entry->rt_dst.sa_len; | |
226 | entry->rt_dst.sa_len = 16; | |
227 | } | |
228 | if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { | |
229 | entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; | |
230 | entry->rt_gateway.sa_len = 16; | |
231 | } | |
232 | #else | |
233 | if (entry->rt_dst.sa_len == 0) | |
234 | entry->rt_dst.sa_len = 16; | |
235 | if (entry->rt_gateway.sa_len == 0) | |
236 | entry->rt_gateway.sa_len = 16; | |
237 | #endif | |
ea372425 MK |
238 | if ((entry->rt_flags & RTF_HOST) == 0) |
239 | switch (entry->rt_dst.sa_family) { | |
240 | #ifdef INET | |
241 | case AF_INET: | |
242 | { | |
243 | extern struct sockaddr_in icmpmask; | |
ea372425 MK |
244 | struct sockaddr_in *dst_in = |
245 | (struct sockaddr_in *)&entry->rt_dst; | |
ea372425 | 246 | |
b72a6efb | 247 | in_sockmaskof(dst_in->sin_addr, &icmpmask); |
ea372425 MK |
248 | netmask = (struct sockaddr *)&icmpmask; |
249 | } | |
250 | break; | |
251 | #endif | |
252 | #ifdef NS | |
253 | case AF_NS: | |
254 | { | |
255 | extern struct sockaddr_ns ns_netmask; | |
256 | netmask = (struct sockaddr *)&ns_netmask; | |
257 | } | |
258 | #endif | |
259 | } | |
260 | error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, | |
b72a6efb | 261 | entry->rt_flags, (struct rtentry **)0); |
ea372425 MK |
262 | rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), |
263 | &(entry->rt_dst), &(entry->rt_gateway), | |
983eada9 | 264 | netmask, SA(0), entry->rt_flags, error); |
ea372425 | 265 | return (error); |
a7b1cd9a | 266 | #endif |
ea372425 | 267 | } |
d6bc9b28 KS |
268 | |
269 | struct ifaddr * | |
270 | ifa_ifwithroute(flags, dst, gateway) | |
271 | int flags; | |
272 | struct sockaddr *dst, *gateway; | |
273 | { | |
274 | struct ifaddr *ifa; | |
275 | if ((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 | */ | |
283 | ifa = 0; | |
284 | if (flags & RTF_HOST) | |
285 | ifa = ifa_ifwithdstaddr(dst); | |
286 | if (ifa == 0) | |
287 | ifa = ifa_ifwithaddr(gateway); | |
288 | } else { | |
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 | */ | |
294 | ifa = ifa_ifwithdstaddr(gateway); | |
295 | } | |
296 | if (ifa == 0) | |
297 | ifa = ifa_ifwithnet(gateway); | |
298 | return (ifa); | |
299 | } | |
300 | ||
1c8ed0c8 | 301 | #define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) |
ea372425 MK |
302 | |
303 | rtrequest(req, dst, gateway, netmask, flags, ret_nrt) | |
304 | int req, flags; | |
305 | struct sockaddr *dst, *gateway, *netmask; | |
306 | struct rtentry **ret_nrt; | |
307 | { | |
308 | int s = splnet(), len, error = 0; | |
309 | register struct rtentry *rt; | |
310 | register struct radix_node *rn; | |
311 | register struct radix_node_head *rnh; | |
312 | struct ifaddr *ifa, *ifa_ifwithdstaddr(); | |
044f6a7f | 313 | struct sockaddr *ndst; |
ea372425 | 314 | u_char af = dst->sa_family; |
044f6a7f | 315 | #define senderr(x) { error = x ; goto bad; } |
ea372425 | 316 | |
8bbafbd9 MK |
317 | if (rtinits_done == 0) |
318 | rtinitheads(); | |
8bbafbd9 MK |
319 | for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) |
320 | rnh = rnh->rnh_next; | |
044f6a7f KS |
321 | if (rnh == 0) |
322 | senderr(ESRCH); | |
323 | if (flags & RTF_HOST) | |
324 | netmask = 0; | |
9d03c806 | 325 | switch (req) { |
ea372425 | 326 | case RTM_DELETE: |
044f6a7f KS |
327 | if (ret_nrt && (rt = *ret_nrt)) { |
328 | RTFREE(rt); | |
329 | *ret_nrt = 0; | |
d7887ae9 | 330 | } |
044f6a7f KS |
331 | if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, |
332 | rnh->rnh_treetop)) == 0) | |
333 | senderr(ESRCH); | |
8bbafbd9 MK |
334 | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
335 | panic ("rtrequest delete"); | |
ea372425 MK |
336 | rt = (struct rtentry *)rn; |
337 | rt->rt_flags &= ~RTF_UP; | |
044f6a7f | 338 | if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest) |
983eada9 | 339 | ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0)); |
044f6a7f KS |
340 | rttrash++; |
341 | if (rt->rt_refcnt <= 0) | |
342 | rtfree(rt); | |
9d03c806 SL |
343 | break; |
344 | ||
983eada9 | 345 | case RTM_RESOLVE: |
d6bc9b28 | 346 | if (ret_nrt == 0 || (rt = *ret_nrt) == 0) |
983eada9 KS |
347 | senderr(EINVAL); |
348 | ifa = rt->rt_ifa; | |
349 | flags = rt->rt_flags & ~RTF_CLONING; | |
350 | gateway = rt->rt_gateway; | |
351 | if ((netmask = rt->rt_genmask) == 0) | |
352 | flags |= RTF_HOST; | |
353 | goto makeroute; | |
354 | ||
ea372425 | 355 | case RTM_ADD: |
d6bc9b28 KS |
356 | if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0) |
357 | senderr(ENETUNREACH); | |
358 | makeroute: | |
359 | len = sizeof (*rt) + ROUNDUP(gateway->sa_len) | |
360 | + ROUNDUP(dst->sa_len); | |
ea372425 | 361 | R_Malloc(rt, struct rtentry *, len); |
044f6a7f KS |
362 | if (rt == 0) |
363 | senderr(ENOBUFS); | |
ea372425 | 364 | Bzero(rt, len); |
044f6a7f KS |
365 | ndst = (struct sockaddr *)(rt + 1); |
366 | if (netmask) { | |
367 | rt_maskedcopy(dst, ndst, netmask); | |
368 | } else | |
369 | Bcopy(dst, ndst, dst->sa_len); | |
370 | rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask, | |
ea372425 | 371 | rnh->rnh_treetop, rt->rt_nodes); |
8bbafbd9 | 372 | if (rn == 0) { |
ea372425 | 373 | free((caddr_t)rt, M_RTABLE); |
044f6a7f | 374 | senderr(EEXIST); |
c124e997 | 375 | } |
b72a6efb | 376 | rt->rt_ifa = ifa; |
983eada9 | 377 | rt->rt_ifp = ifa->ifa_ifp; |
044f6a7f | 378 | rt->rt_flags = RTF_UP | flags; |
ea372425 MK |
379 | rt->rt_gateway = (struct sockaddr *) |
380 | (rn->rn_key + ROUNDUP(dst->sa_len)); | |
381 | Bcopy(gateway, rt->rt_gateway, gateway->sa_len); | |
983eada9 KS |
382 | if (req == RTM_RESOLVE) |
383 | rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */ | |
044f6a7f | 384 | if (ifa->ifa_rtrequest) |
983eada9 KS |
385 | ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0)); |
386 | if (ret_nrt) { | |
387 | *ret_nrt = rt; | |
388 | rt->rt_refcnt++; | |
389 | } | |
9d03c806 SL |
390 | break; |
391 | } | |
c124e997 SL |
392 | bad: |
393 | splx(s); | |
394 | return (error); | |
9d03c806 | 395 | } |
044f6a7f KS |
396 | |
397 | rt_maskedcopy(src, dst, netmask) | |
398 | struct sockaddr *src, *dst, *netmask; | |
399 | { | |
be74b489 KS |
400 | register u_char *cp1 = (u_char *)src; |
401 | register u_char *cp2 = (u_char *)dst; | |
402 | register u_char *cp3 = (u_char *)netmask; | |
403 | u_char *cplim = cp2 + *cp3; | |
404 | u_char *cplim2 = cp2 + *cp1; | |
044f6a7f KS |
405 | |
406 | *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */ | |
be74b489 KS |
407 | cp3 += 2; |
408 | if (cplim > cplim2) | |
409 | cplim = cplim2; | |
410 | while (cp2 < cplim) | |
044f6a7f | 411 | *cp2++ = *cp1++ & *cp3++; |
be74b489 KS |
412 | if (cp2 < cplim2) |
413 | bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); | |
044f6a7f | 414 | } |
f6311fb6 SL |
415 | /* |
416 | * Set up a routing table entry, normally | |
417 | * for an interface. | |
418 | */ | |
ea372425 MK |
419 | rtinit(ifa, cmd, flags) |
420 | register struct ifaddr *ifa; | |
2a63f7ba | 421 | int cmd, flags; |
f6311fb6 | 422 | { |
9bdb9951 KS |
423 | register struct rtentry *rt; |
424 | register struct sockaddr *dst; | |
425 | register struct sockaddr *deldst; | |
426 | struct mbuf *m = 0; | |
427 | int error; | |
428 | ||
429 | dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr; | |
430 | if (ifa->ifa_flags & IFA_ROUTE) { | |
431 | if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) { | |
432 | RTFREE(rt); | |
433 | ifa->ifa_rt = 0; | |
434 | } | |
435 | } | |
436 | if (cmd == RTM_DELETE) { | |
437 | if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) { | |
438 | m = m_get(M_WAIT, MT_SONAME); | |
439 | deldst = mtod(m, struct sockaddr *); | |
440 | rt_maskedcopy(dst, deldst, ifa->ifa_netmask); | |
441 | dst = deldst; | |
442 | } | |
443 | if (rt = rtalloc1(dst, 0)) { | |
444 | rt->rt_refcnt--; | |
445 | if (rt->rt_ifa != ifa) { | |
446 | if (m) | |
447 | (void) m_free(m); | |
448 | return (flags & RTF_HOST ? EHOSTUNREACH | |
449 | : ENETUNREACH); | |
450 | } | |
451 | } | |
452 | } | |
453 | error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask, | |
1c8ed0c8 | 454 | flags | ifa->ifa_flags, &ifa->ifa_rt); |
9bdb9951 KS |
455 | if (m) |
456 | (void) m_free(m); | |
457 | if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt) | |
458 | && rt->rt_ifa != ifa) { | |
459 | rt->rt_ifa = ifa; | |
460 | rt->rt_ifp = ifa->ifa_ifp; | |
461 | } | |
462 | return (error); | |
f6311fb6 | 463 | } |