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 | * |
ea372425 | 17 | * @(#)route.c 7.8 (Berkeley) %G% |
cb1c44c2 | 18 | */ |
ea372425 MK |
19 | #include "../machine/reg.h" |
20 | ||
a0369dcf JB |
21 | #include "param.h" |
22 | #include "systm.h" | |
a0369dcf JB |
23 | #include "dir.h" |
24 | #include "user.h" | |
ea372425 MK |
25 | #include "inode.h" |
26 | #include "proc.h" | |
27 | #include "mbuf.h" | |
28 | #include "socket.h" | |
29 | #include "socketvar.h" | |
30 | #include "domain.h" | |
31 | #include "protosw.h" | |
a0369dcf | 32 | #include "errno.h" |
ea372425 | 33 | #include "ioctl.h" |
f4d55810 | 34 | |
a0369dcf JB |
35 | #include "if.h" |
36 | #include "af.h" | |
37 | #include "route.h" | |
ea372425 | 38 | #include "raw_cb.h" |
8bbafbd9 MK |
39 | #include "../netinet/in.h" |
40 | #include "../netinet/in_var.h" | |
ea372425 MK |
41 | |
42 | #include "../machine/mtpr.h" | |
43 | #include "netisr.h" | |
44 | ||
45 | #include "rtsock.c" | |
9d03c806 | 46 | |
a13c006d | 47 | int rttrash; /* routes not in table but not freed */ |
a9ea8834 | 48 | struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ |
c50b0999 | 49 | int rthashsize = RTHASHSIZ; /* for netstat, etc. */ |
a9ea8834 | 50 | |
8bbafbd9 MK |
51 | static int rtinits_done = 0; |
52 | struct radix_node_head *ns_rnhead, *in_rnhead; | |
ea372425 | 53 | struct radix_node *rn_match(), *rn_delete(), *rn_addroute(); |
8bbafbd9 MK |
54 | rtinitheads() |
55 | { | |
56 | if (rtinits_done == 0 && | |
57 | rn_inithead(&ns_rnhead, 16, AF_NS) && | |
58 | rn_inithead(&in_rnhead, 32, AF_INET)) | |
59 | rtinits_done = 1; | |
60 | } | |
61 | ||
9d03c806 SL |
62 | /* |
63 | * Packet routing routines. | |
64 | */ | |
f6311fb6 | 65 | rtalloc(ro) |
9d03c806 | 66 | register struct route *ro; |
ea372425 MK |
67 | { |
68 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) | |
69 | return; /* XXX */ | |
70 | ro->ro_rt = rtalloc1(&ro->ro_dst, 1); | |
71 | } | |
72 | ||
73 | struct rtentry * | |
74 | rtalloc1(dst, report) | |
75 | struct sockaddr *dst; | |
76 | int report; | |
9d03c806 | 77 | { |
8bbafbd9 MK |
78 | register struct radix_node_head *rnh; |
79 | register struct radix_node *rn; | |
80 | register struct rtentry *rt = 0; | |
ea372425 MK |
81 | u_char af = dst->sa_family; |
82 | int s = splnet(); | |
9d03c806 | 83 | |
8bbafbd9 MK |
84 | for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) |
85 | rnh = rnh->rnh_next; | |
86 | if (rnh && rnh->rnh_treetop && | |
ea372425 | 87 | (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) && |
8bbafbd9 | 88 | ((rn->rn_flags & RNF_ROOT) == 0)) { |
ea372425 | 89 | rt = (struct rtentry *)rn; |
c50b0999 | 90 | rt->rt_refcnt++; |
ea372425 | 91 | } else { |
8bbafbd9 | 92 | rtstat.rts_unreach++; |
ea372425 MK |
93 | if (report && route_cb.any_count) |
94 | rt_missmsg(RTM_MISS, dst, (struct sockaddr *)0, | |
95 | (struct sockaddr *)0, (struct sockaddr *)0, 0); | |
96 | } | |
c50b0999 | 97 | splx(s); |
ea372425 | 98 | return (rt); |
9d03c806 SL |
99 | } |
100 | ||
f6311fb6 | 101 | rtfree(rt) |
c124e997 SL |
102 | register struct rtentry *rt; |
103 | { | |
8bbafbd9 | 104 | u_char *af; |
c124e997 | 105 | if (rt == 0) |
a1edc12b | 106 | panic("rtfree"); |
c124e997 | 107 | rt->rt_refcnt--; |
8bbafbd9 | 108 | if (rt->rt_refcnt <= 0 && (rt->rt_flags&RTF_UP) == 0) { |
a13c006d | 109 | rttrash--; |
ea372425 | 110 | if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
8bbafbd9 | 111 | panic ("rtfree 2"); |
ea372425 | 112 | free((caddr_t)rt, M_RTABLE); |
a13c006d | 113 | } |
c124e997 SL |
114 | } |
115 | ||
464f931f SL |
116 | /* |
117 | * Force a routing table entry to the specified | |
118 | * destination to go through the given gateway. | |
119 | * Normally called as a result of a routing redirect | |
120 | * message from the network layer. | |
121 | * | |
8bbafbd9 | 122 | * N.B.: must be called at splnet |
464f931f | 123 | * |
464f931f | 124 | */ |
ea372425 | 125 | rtredirect(dst, gateway, netmask, flags, src) |
92c26501 | 126 | struct sockaddr *dst, *gateway, *src; |
7eb1f827 | 127 | int flags; |
464f931f | 128 | { |
464f931f SL |
129 | register struct rtentry *rt; |
130 | ||
131 | /* verify the gateway is directly reachable */ | |
cba69651 | 132 | if (ifa_ifwithnet(gateway) == 0) { |
a9ea8834 | 133 | rtstat.rts_badredirect++; |
464f931f | 134 | return; |
a9ea8834 | 135 | } |
ea372425 | 136 | rt = rtalloc1(dst, 1); |
92c26501 | 137 | #define equal(a1, a2) \ |
a7b1cd9a | 138 | (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) |
92c26501 MK |
139 | /* |
140 | * If the redirect isn't from our current router for this dst, | |
2e9c4868 MK |
141 | * it's either old or wrong. If it redirects us to ourselves, |
142 | * we have a routing loop, perhaps as a result of an interface | |
143 | * going down recently. | |
92c26501 | 144 | */ |
2e9c4868 | 145 | if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { |
92c26501 | 146 | rtstat.rts_badredirect++; |
703b8607 MK |
147 | if (rt) |
148 | rtfree(rt); | |
92c26501 MK |
149 | return; |
150 | } | |
a9ea8834 | 151 | /* |
8bbafbd9 | 152 | * Old comment: |
48afbdc5 SL |
153 | * Create a new entry if we just got back a wildcard entry |
154 | * or the the lookup failed. This is necessary for hosts | |
155 | * which use routing redirects generated by smart gateways | |
156 | * to dynamically build the routing tables. | |
8bbafbd9 MK |
157 | * |
158 | * New comment: | |
159 | * If we survived the previous tests, it doesn't matter | |
160 | * what sort of entry we got when we looked it up; | |
161 | * we should just go ahead and free the reference to | |
162 | * the route we created. rtalloc will not give a | |
163 | * pointer to the root node. And if we got a pointer | |
164 | * to a default gateway, we should free the reference | |
165 | * in any case. | |
166 | if (rt) { | |
48afbdc5 SL |
167 | rtfree(rt); |
168 | rt = 0; | |
169 | } | |
ea372425 MK |
170 | if (route_cb.any_count) |
171 | rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, | |
172 | (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC); | |
8bbafbd9 | 173 | */ |
a9ea8834 | 174 | if (rt == 0) { |
ea372425 MK |
175 | rtrequest((int)RTM_ADD, dst, gateway, 0, |
176 | (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC, 0); | |
a9ea8834 SL |
177 | rtstat.rts_dynamic++; |
178 | return; | |
179 | } | |
464f931f SL |
180 | /* |
181 | * Don't listen to the redirect if it's | |
182 | * for a route to an interface. | |
464f931f | 183 | */ |
a9ea8834 | 184 | if (rt->rt_flags & RTF_GATEWAY) { |
7eb1f827 MK |
185 | if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) { |
186 | /* | |
66798f86 | 187 | * Changing from route to net => route to host. |
7eb1f827 MK |
188 | * Create new route, rather than smashing route to net. |
189 | */ | |
ea372425 MK |
190 | rtrequest((int)RTM_ADD, dst, gateway, 0, |
191 | (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC, 0); | |
3b66e456 | 192 | rtstat.rts_dynamic++; |
7eb1f827 MK |
193 | } else { |
194 | /* | |
195 | * Smash the current notion of the gateway to | |
3b66e456 | 196 | * this destination. |
7eb1f827 | 197 | */ |
ea372425 MK |
198 | if (gateway->sa_len <= rt->rt_gateway->sa_len) { |
199 | Bcopy(gateway, rt->rt_gateway, gateway->sa_len); | |
200 | rt->rt_flags |= RTF_MODIFIED; | |
201 | rtstat.rts_newgateway++; | |
202 | } else | |
203 | rtstat.rts_badredirect++; | |
7eb1f827 | 204 | } |
92c26501 MK |
205 | } else |
206 | rtstat.rts_badredirect++; | |
ff320836 | 207 | rtfree(rt); |
464f931f SL |
208 | } |
209 | ||
a62dd253 SL |
210 | /* |
211 | * Routing table ioctl interface. | |
212 | */ | |
ea372425 MK |
213 | rtioctl(req, data) |
214 | int req; | |
a62dd253 SL |
215 | caddr_t data; |
216 | { | |
ea372425 MK |
217 | #ifndef COMPAT_43 |
218 | return (EOPNOTSUPP); | |
219 | #else | |
220 | register struct ortentry *entry = (struct ortentry *)data; | |
221 | int error; | |
222 | struct sockaddr *netmask = 0; | |
a62dd253 | 223 | |
ea372425 MK |
224 | if (req == SIOCADDRT) |
225 | req = RTM_ADD; | |
226 | else if (req == SIOCDELRT) | |
227 | req = RTM_DELETE; | |
228 | else | |
a62dd253 | 229 | return (EINVAL); |
ea372425 | 230 | |
a62dd253 SL |
231 | if (!suser()) |
232 | return (u.u_error); | |
a7b1cd9a MK |
233 | #if BYTE_ORDER != BIG_ENDIAN |
234 | if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { | |
235 | entry->rt_dst.sa_family = entry->rt_dst.sa_len; | |
236 | entry->rt_dst.sa_len = 16; | |
237 | } | |
238 | if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { | |
239 | entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; | |
240 | entry->rt_gateway.sa_len = 16; | |
241 | } | |
242 | #else | |
243 | if (entry->rt_dst.sa_len == 0) | |
244 | entry->rt_dst.sa_len = 16; | |
245 | if (entry->rt_gateway.sa_len == 0) | |
246 | entry->rt_gateway.sa_len = 16; | |
247 | #endif | |
ea372425 MK |
248 | if ((entry->rt_flags & RTF_HOST) == 0) |
249 | switch (entry->rt_dst.sa_family) { | |
250 | #ifdef INET | |
251 | case AF_INET: | |
252 | { | |
253 | extern struct sockaddr_in icmpmask; | |
254 | u_long in_maskof(); | |
255 | struct sockaddr_in *dst_in = | |
256 | (struct sockaddr_in *)&entry->rt_dst; | |
257 | u_long i = ntohl(dst_in->sin_addr.s_addr); | |
258 | ||
259 | icmpmask.sin_addr.s_addr = ntohl(in_maskof(i)); | |
260 | netmask = (struct sockaddr *)&icmpmask; | |
261 | } | |
262 | break; | |
263 | #endif | |
264 | #ifdef NS | |
265 | case AF_NS: | |
266 | { | |
267 | extern struct sockaddr_ns ns_netmask; | |
268 | netmask = (struct sockaddr *)&ns_netmask; | |
269 | } | |
270 | #endif | |
271 | } | |
272 | error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask, | |
273 | entry->rt_flags, 0); | |
274 | rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL), | |
275 | &(entry->rt_dst), &(entry->rt_gateway), | |
276 | netmask, (struct sockaddr *)error, entry->rt_flags); | |
277 | return (error); | |
a7b1cd9a | 278 | #endif |
ea372425 MK |
279 | } |
280 | ||
281 | rtrequest(req, dst, gateway, netmask, flags, ret_nrt) | |
282 | int req, flags; | |
283 | struct sockaddr *dst, *gateway, *netmask; | |
284 | struct rtentry **ret_nrt; | |
285 | { | |
286 | int s = splnet(), len, error = 0; | |
287 | register struct rtentry *rt; | |
288 | register struct radix_node *rn; | |
289 | register struct radix_node_head *rnh; | |
290 | struct ifaddr *ifa, *ifa_ifwithdstaddr(); | |
291 | u_char af = dst->sa_family; | |
292 | ||
8bbafbd9 MK |
293 | if (rtinits_done == 0) |
294 | rtinitheads(); | |
8bbafbd9 MK |
295 | for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) |
296 | rnh = rnh->rnh_next; | |
297 | if (rnh == 0) { | |
298 | error = ESRCH; | |
299 | goto bad; | |
300 | } | |
9d03c806 | 301 | switch (req) { |
ea372425 MK |
302 | case RTM_DELETE: |
303 | if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask, | |
304 | rnh->rnh_treetop)) == 0) { | |
d7887ae9 BJ |
305 | error = ESRCH; |
306 | goto bad; | |
307 | } | |
8bbafbd9 MK |
308 | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
309 | panic ("rtrequest delete"); | |
ea372425 MK |
310 | rt = (struct rtentry *)rn; |
311 | rt->rt_flags &= ~RTF_UP; | |
312 | if (rt->rt_refcnt > 0) | |
a13c006d | 313 | rttrash++; |
8bbafbd9 | 314 | else |
ea372425 | 315 | free((caddr_t)rt, M_RTABLE); |
9d03c806 SL |
316 | break; |
317 | ||
ea372425 MK |
318 | case RTM_ADD: |
319 | if ((flags & RTF_GATEWAY) == 0) { | |
320 | /* | |
321 | * If we are adding a route to an interface, | |
322 | * and the interface is a pt to pt link | |
323 | * we should search for the destination | |
324 | * as our clue to the interface. Otherwise | |
325 | * we can use the local address. | |
326 | */ | |
327 | ifa = 0; | |
328 | if (flags & RTF_HOST) | |
329 | ifa = ifa_ifwithdstaddr(dst); | |
330 | if (ifa == 0) | |
331 | ifa = ifa_ifwithaddr(gateway); | |
332 | } else { | |
333 | /* | |
334 | * If we are adding a route to a remote net | |
335 | * or host, the gateway may still be on the | |
336 | * other end of a pt to pt link. | |
337 | */ | |
338 | ifa = ifa_ifwithdstaddr(gateway); | |
339 | } | |
340 | if (ifa == 0) { | |
341 | ifa = ifa_ifwithnet(gateway); | |
342 | if (ifa == 0 && req == RTM_ADD) { | |
343 | error = ENETUNREACH; | |
344 | goto bad; | |
345 | } | |
346 | } | |
347 | len = sizeof (*rt) + ROUNDUP(gateway->sa_len) | |
348 | + ROUNDUP(dst->sa_len); | |
349 | R_Malloc(rt, struct rtentry *, len); | |
350 | if (rt == 0) { | |
8bbafbd9 | 351 | error = ENOBUFS; |
d7887ae9 BJ |
352 | goto bad; |
353 | } | |
ea372425 MK |
354 | Bzero(rt, len); |
355 | rn = rn_addroute((caddr_t)dst, (caddr_t)netmask, | |
356 | rnh->rnh_treetop, rt->rt_nodes); | |
8bbafbd9 | 357 | if (rn == 0) { |
ea372425 | 358 | free((caddr_t)rt, M_RTABLE); |
8bbafbd9 | 359 | error = EEXIST; |
d7887ae9 | 360 | goto bad; |
c124e997 | 361 | } |
ea372425 MK |
362 | if (ret_nrt) |
363 | *ret_nrt = rt; /* == (struct rtentry *)rn */ | |
364 | rt->rt_ifp = ifa->ifa_ifp; | |
a13c006d | 365 | rt->rt_use = 0; |
8bbafbd9 | 366 | rt->rt_refcnt = 0; |
ea372425 MK |
367 | rt->rt_flags = RTF_UP | |
368 | (flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); | |
369 | rn->rn_key = (caddr_t) (rt + 1); /* == rt_dst */ | |
370 | Bcopy(dst, rn->rn_key, dst->sa_len); | |
371 | rt->rt_gateway = (struct sockaddr *) | |
372 | (rn->rn_key + ROUNDUP(dst->sa_len)); | |
373 | Bcopy(gateway, rt->rt_gateway, gateway->sa_len); | |
9d03c806 SL |
374 | break; |
375 | } | |
c124e997 SL |
376 | bad: |
377 | splx(s); | |
378 | return (error); | |
9d03c806 | 379 | } |
f6311fb6 SL |
380 | /* |
381 | * Set up a routing table entry, normally | |
382 | * for an interface. | |
383 | */ | |
ea372425 MK |
384 | rtinit(ifa, cmd, flags) |
385 | register struct ifaddr *ifa; | |
2a63f7ba | 386 | int cmd, flags; |
f6311fb6 | 387 | { |
ea372425 MK |
388 | struct sockaddr net, *netp; |
389 | register caddr_t cp, cp2, cp3; | |
390 | caddr_t cplim, freeit = 0; | |
391 | int len; | |
f6311fb6 | 392 | |
ea372425 MK |
393 | if (flags & RTF_HOST || ifa->ifa_netmask == 0) { |
394 | (void) rtrequest(cmd, ifa->ifa_dstaddr, ifa->ifa_addr, | |
395 | 0, flags, 0); | |
396 | } else { | |
397 | if ((len = ifa->ifa_addr->sa_len) >= sizeof (net)) { | |
398 | R_Malloc(freeit, caddr_t, len); | |
399 | if (freeit == 0) | |
400 | return; | |
401 | netp = (struct sockaddr *)freeit; | |
402 | } | |
403 | netp->sa_len = len; | |
404 | cp2 = 1 + (caddr_t)ifa->ifa_addr; | |
405 | netp->sa_family = *cp2++; | |
406 | cp3 = (caddr_t) ifa->ifa_netmask->sa_data; | |
407 | cp = (caddr_t) netp->sa_data; | |
408 | cplim = cp + len - 2; | |
409 | while (cp < cplim) | |
410 | *cp++ = *cp2++ & *cp3++; | |
411 | (void) rtrequest(cmd, netp, ifa->ifa_addr, ifa->ifa_netmask, | |
412 | flags, 0); | |
413 | if (freeit) | |
414 | Free(freeit); | |
415 | } | |
f6311fb6 | 416 | } |
ea372425 | 417 | #include "radix.c" |