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 | * |
8bbafbd9 | 17 | * @(#)route.c 7.7 (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" | |
8bbafbd9 MK |
33 | #include "radix.h" |
34 | ||
35 | #include "radix.c" | |
36 | #ifdef INET | |
37 | #include "../netinet/in.h" | |
38 | #include "../netinet/in_var.h" | |
39 | #endif | |
9d03c806 | 40 | |
a13c006d | 41 | int rttrash; /* routes not in table but not freed */ |
a9ea8834 | 42 | struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ |
c50b0999 | 43 | int rthashsize = RTHASHSIZ; /* for netstat, etc. */ |
a9ea8834 | 44 | |
8bbafbd9 MK |
45 | static int rtinits_done = 0; |
46 | struct radix_node_head *ns_rnhead, *in_rnhead; | |
47 | rtinitheads() | |
48 | { | |
49 | if (rtinits_done == 0 && | |
50 | rn_inithead(&ns_rnhead, 16, AF_NS) && | |
51 | rn_inithead(&in_rnhead, 32, AF_INET)) | |
52 | rtinits_done = 1; | |
53 | } | |
54 | ||
9d03c806 SL |
55 | /* |
56 | * Packet routing routines. | |
57 | */ | |
f6311fb6 | 58 | rtalloc(ro) |
9d03c806 SL |
59 | register struct route *ro; |
60 | { | |
8bbafbd9 MK |
61 | register struct radix_node_head *rnh; |
62 | register struct radix_node *rn; | |
63 | register struct rtentry *rt = 0; | |
64 | u_char af = ro->ro_dst.sa_family; | |
65 | int s; | |
9d03c806 | 66 | |
7eb1f827 MK |
67 | if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP)) |
68 | return; /* XXX */ | |
c50b0999 | 69 | s = splnet(); |
8bbafbd9 MK |
70 | for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) |
71 | rnh = rnh->rnh_next; | |
72 | if (rnh && rnh->rnh_treetop && | |
73 | (rn = rn_match((char *)&(ro->ro_dst), rnh->rnh_treetop)) && | |
74 | ((rn->rn_flags & RNF_ROOT) == 0)) { | |
75 | rt = &(((struct nrtentry *)rn)->nrt_rt); | |
c50b0999 | 76 | rt->rt_refcnt++; |
8bbafbd9 MK |
77 | } else |
78 | rtstat.rts_unreach++; | |
79 | ro->ro_rt = rt; | |
c50b0999 | 80 | splx(s); |
9d03c806 SL |
81 | } |
82 | ||
f6311fb6 | 83 | rtfree(rt) |
c124e997 SL |
84 | register struct rtentry *rt; |
85 | { | |
8bbafbd9 MK |
86 | register struct nrtentry *nrt; |
87 | u_char *af; | |
c124e997 | 88 | if (rt == 0) |
a1edc12b | 89 | panic("rtfree"); |
c124e997 | 90 | rt->rt_refcnt--; |
8bbafbd9 | 91 | if (rt->rt_refcnt <= 0 && (rt->rt_flags&RTF_UP) == 0) { |
a13c006d | 92 | rttrash--; |
8bbafbd9 MK |
93 | nrt = (struct nrtentry *) (((struct radix_node *)rt) - 2); |
94 | if (nrt->nrt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) | |
95 | panic ("rtfree 2"); | |
96 | free((caddr_t)nrt, M_RTABLE); | |
a13c006d | 97 | } |
c124e997 SL |
98 | } |
99 | ||
464f931f SL |
100 | /* |
101 | * Force a routing table entry to the specified | |
102 | * destination to go through the given gateway. | |
103 | * Normally called as a result of a routing redirect | |
104 | * message from the network layer. | |
105 | * | |
8bbafbd9 | 106 | * N.B.: must be called at splnet |
464f931f | 107 | * |
464f931f | 108 | */ |
92c26501 MK |
109 | rtredirect(dst, gateway, flags, src) |
110 | struct sockaddr *dst, *gateway, *src; | |
7eb1f827 | 111 | int flags; |
464f931f SL |
112 | { |
113 | struct route ro; | |
114 | register struct rtentry *rt; | |
115 | ||
116 | /* verify the gateway is directly reachable */ | |
cba69651 | 117 | if (ifa_ifwithnet(gateway) == 0) { |
a9ea8834 | 118 | rtstat.rts_badredirect++; |
464f931f | 119 | return; |
a9ea8834 | 120 | } |
464f931f | 121 | ro.ro_dst = *dst; |
a9ea8834 | 122 | ro.ro_rt = 0; |
464f931f SL |
123 | rtalloc(&ro); |
124 | rt = ro.ro_rt; | |
92c26501 | 125 | #define equal(a1, a2) \ |
a7b1cd9a | 126 | (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0) |
92c26501 MK |
127 | /* |
128 | * If the redirect isn't from our current router for this dst, | |
2e9c4868 MK |
129 | * it's either old or wrong. If it redirects us to ourselves, |
130 | * we have a routing loop, perhaps as a result of an interface | |
131 | * going down recently. | |
92c26501 | 132 | */ |
2e9c4868 | 133 | if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) { |
92c26501 | 134 | rtstat.rts_badredirect++; |
703b8607 MK |
135 | if (rt) |
136 | rtfree(rt); | |
92c26501 MK |
137 | return; |
138 | } | |
a9ea8834 | 139 | /* |
8bbafbd9 | 140 | * Old comment: |
48afbdc5 SL |
141 | * Create a new entry if we just got back a wildcard entry |
142 | * or the the lookup failed. This is necessary for hosts | |
143 | * which use routing redirects generated by smart gateways | |
144 | * to dynamically build the routing tables. | |
8bbafbd9 MK |
145 | * |
146 | * New comment: | |
147 | * If we survived the previous tests, it doesn't matter | |
148 | * what sort of entry we got when we looked it up; | |
149 | * we should just go ahead and free the reference to | |
150 | * the route we created. rtalloc will not give a | |
151 | * pointer to the root node. And if we got a pointer | |
152 | * to a default gateway, we should free the reference | |
153 | * in any case. | |
154 | if (rt) { | |
48afbdc5 SL |
155 | rtfree(rt); |
156 | rt = 0; | |
157 | } | |
8bbafbd9 | 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 | 182 | */ |
8bbafbd9 | 183 | rt->rt_gateway = *gateway; /*XXX -- size? */ |
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 | } | |
8bbafbd9 MK |
206 | /* |
207 | * This routine will go away soon. | |
208 | * Tries to guess which netmask is appropriate for a given net. | |
209 | */ | |
210 | static struct sockaddr_in rtgmask = { 8, 0 }; | |
211 | ||
212 | char * | |
213 | rtgetmask(sa, ifa) | |
214 | register struct sockaddr *sa; | |
215 | register struct ifaddr *ifa; | |
216 | { | |
217 | u_long i, net, mask, subnet; | |
218 | ||
219 | switch (sa->sa_family) { | |
220 | #ifdef INET | |
221 | register struct in_ifaddr *ia; | |
222 | ||
223 | case AF_INET: | |
224 | ||
225 | i = ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr); | |
226 | if (i == 0) { | |
227 | rtgmask.sin_addr.s_addr = 0; | |
228 | return ((char *)&rtgmask); | |
229 | } else if (IN_CLASSA(i)) { | |
230 | net = i & IN_CLASSA_NET; | |
231 | mask = IN_CLASSA_NET; | |
232 | } else if (IN_CLASSB(i)) { | |
233 | net = i & IN_CLASSB_NET; | |
234 | mask = IN_CLASSB_NET; | |
235 | } else if (IN_CLASSC(i)) { | |
236 | net = i & IN_CLASSC_NET; | |
237 | mask = IN_CLASSC_NET; | |
238 | } else { | |
239 | net = i; | |
240 | mask = 0xffffffff; | |
241 | } | |
242 | ||
243 | /* | |
244 | * Check whether network is a subnet; | |
245 | * if so, return subnet number. | |
246 | */ | |
247 | for (ia = in_ifaddr; ia; ia = ia->ia_next) | |
248 | if (net == ia->ia_net) { | |
249 | ifa = &ia->ia_ifa; | |
250 | break; | |
251 | } | |
252 | if (ia == 0) { | |
253 | rtgmask.sin_addr.s_addr = ntohl(mask); | |
254 | return ((char *)&rtgmask); | |
255 | } | |
256 | #endif | |
257 | } | |
258 | return ((char *)ifa->ifa_netmask); | |
259 | } | |
a62dd253 | 260 | |
9d03c806 | 261 | /* |
c124e997 | 262 | * Carry out a request to change the routing table. Called by |
a9ea8834 SL |
263 | * interfaces at boot time to make their ``local routes'' known, |
264 | * for ioctl's, and as the result of routing redirects. | |
9d03c806 | 265 | */ |
a13c006d | 266 | rtrequest(req, entry) |
9d03c806 | 267 | int req; |
a13c006d | 268 | register struct rtentry *entry; |
9d03c806 | 269 | { |
a13c006d | 270 | register struct rtentry *rt; |
8bbafbd9 MK |
271 | int s, error = 0, found; |
272 | u_char af; | |
cba69651 | 273 | struct ifaddr *ifa; |
ae674e00 | 274 | struct ifaddr *ifa_ifwithdstaddr(); |
8bbafbd9 MK |
275 | register struct nrtentry *nrt; |
276 | register struct radix_node *rn; | |
277 | register struct radix_node_head *rnh; | |
278 | struct radix_node *head; | |
279 | char *netmask; | |
9d03c806 | 280 | |
a7b1cd9a MK |
281 | #ifdef COMPAT_43 |
282 | #if BYTE_ORDER != BIG_ENDIAN | |
8bbafbd9 | 283 | s = splnet(); |
a7b1cd9a MK |
284 | if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) { |
285 | entry->rt_dst.sa_family = entry->rt_dst.sa_len; | |
286 | entry->rt_dst.sa_len = 16; | |
287 | } | |
288 | if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) { | |
289 | entry->rt_gateway.sa_family = entry->rt_gateway.sa_len; | |
290 | entry->rt_gateway.sa_len = 16; | |
291 | } | |
292 | #else | |
293 | if (entry->rt_dst.sa_len == 0) | |
294 | entry->rt_dst.sa_len = 16; | |
295 | if (entry->rt_gateway.sa_len == 0) | |
296 | entry->rt_gateway.sa_len = 16; | |
297 | #endif | |
298 | #endif | |
8bbafbd9 MK |
299 | if (rtinits_done == 0) |
300 | rtinitheads(); | |
a13c006d | 301 | af = entry->rt_dst.sa_family; |
8bbafbd9 MK |
302 | for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); ) |
303 | rnh = rnh->rnh_next; | |
304 | if (rnh == 0) { | |
305 | error = ESRCH; | |
306 | goto bad; | |
307 | } | |
308 | head = rnh->rnh_treetop; | |
309 | if ((entry->rt_flags & RTF_GATEWAY) == 0) { | |
310 | /* | |
311 | * If we are adding a route to an interface, | |
312 | * and the interface is a pt to pt link | |
313 | * we should search for the destination | |
314 | * as our clue to the interface. Otherwise | |
315 | * we can use the local address. | |
316 | */ | |
317 | ifa = 0; | |
318 | if (entry->rt_flags & RTF_HOST) | |
319 | ifa = ifa_ifwithdstaddr(&entry->rt_dst); | |
320 | if (ifa == 0) | |
321 | ifa = ifa_ifwithaddr(&entry->rt_gateway); | |
d7887ae9 | 322 | } else { |
8bbafbd9 MK |
323 | /* |
324 | * If we are adding a route to a remote net | |
325 | * or host, the gateway may still be on the | |
326 | * other end of a pt to pt link. | |
327 | */ | |
328 | ifa = ifa_ifwithdstaddr(&entry->rt_gateway); | |
d7887ae9 | 329 | } |
8bbafbd9 MK |
330 | if (ifa == 0) { |
331 | ifa = ifa_ifwithnet(&entry->rt_gateway); | |
332 | if (ifa == 0 && req == SIOCADDRT) { | |
333 | error = ENETUNREACH; | |
334 | goto bad; | |
9d03c806 | 335 | } |
c124e997 | 336 | } |
8bbafbd9 MK |
337 | if (entry->rt_flags & RTF_HOST) |
338 | netmask = 0; | |
339 | else | |
340 | netmask = rtgetmask(&entry->rt_dst, ifa); | |
9d03c806 SL |
341 | switch (req) { |
342 | ||
343 | case SIOCDELRT: | |
8bbafbd9 MK |
344 | if ((rn = rn_delete((char *)&entry->rt_dst, |
345 | netmask, head)) == 0) { | |
d7887ae9 BJ |
346 | error = ESRCH; |
347 | goto bad; | |
348 | } | |
8bbafbd9 MK |
349 | if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) |
350 | panic ("rtrequest delete"); | |
351 | nrt = (struct nrtentry *)rn; | |
352 | nrt->nrt_rt.rt_flags &= ~RTF_UP; | |
353 | if (nrt->nrt_rt.rt_refcnt > 0) | |
a13c006d | 354 | rttrash++; |
8bbafbd9 MK |
355 | else |
356 | free((caddr_t)nrt, M_RTABLE); | |
9d03c806 SL |
357 | break; |
358 | ||
359 | case SIOCADDRT: | |
8bbafbd9 MK |
360 | Malloc(nrt, struct nrtentry *, sizeof *nrt); |
361 | if (nrt == 0) { | |
362 | error = ENOBUFS; | |
d7887ae9 BJ |
363 | goto bad; |
364 | } | |
8bbafbd9 MK |
365 | Bzero(nrt, sizeof *nrt); |
366 | rn = rn_addroute((char *)&entry->rt_dst, netmask, | |
367 | head, nrt->nrt_nodes); | |
368 | if (rn == 0) { | |
369 | free((caddr_t)nrt, M_RTABLE); | |
370 | error = EEXIST; | |
d7887ae9 | 371 | goto bad; |
c124e997 | 372 | } |
8bbafbd9 MK |
373 | rt = &nrt->nrt_rt; |
374 | rn->rn_key = (char *)&(nrt->nrt_rt.rt_dst); | |
a13c006d BJ |
375 | rt->rt_dst = entry->rt_dst; |
376 | rt->rt_gateway = entry->rt_gateway; | |
92c26501 MK |
377 | rt->rt_flags = RTF_UP | |
378 | (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC)); | |
a13c006d | 379 | rt->rt_use = 0; |
8bbafbd9 | 380 | rt->rt_refcnt = 0; |
cba69651 | 381 | rt->rt_ifp = ifa->ifa_ifp; |
9d03c806 SL |
382 | break; |
383 | } | |
c124e997 SL |
384 | bad: |
385 | splx(s); | |
386 | return (error); | |
9d03c806 | 387 | } |
f6311fb6 SL |
388 | |
389 | /* | |
390 | * Set up a routing table entry, normally | |
391 | * for an interface. | |
392 | */ | |
2a63f7ba | 393 | rtinit(dst, gateway, cmd, flags) |
f6311fb6 | 394 | struct sockaddr *dst, *gateway; |
2a63f7ba | 395 | int cmd, flags; |
f6311fb6 SL |
396 | { |
397 | struct rtentry route; | |
f6311fb6 | 398 | |
a13c006d | 399 | bzero((caddr_t)&route, sizeof (route)); |
f6311fb6 SL |
400 | route.rt_dst = *dst; |
401 | route.rt_gateway = *gateway; | |
402 | route.rt_flags = flags; | |
0c33c832 | 403 | (void) rtrequest(cmd, &route); |
f6311fb6 | 404 | } |