This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / sys / net / route.c
CommitLineData
15637ed4
RG
1/*
2 * Copyright (c) 1980, 1986, 1991 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
78ed81a3 33 * from: @(#)route.c 7.22 (Berkeley) 6/27/91
34 * $Id$
15637ed4
RG
35 */
36
37#include "param.h"
38#include "systm.h"
39#include "proc.h"
40#include "mbuf.h"
41#include "socket.h"
42#include "socketvar.h"
43#include "domain.h"
44#include "protosw.h"
45#include "ioctl.h"
46
47#include "if.h"
48#include "af.h"
49#include "route.h"
50#include "raw_cb.h"
51
52#include "../netinet/in.h"
53#include "../netinet/in_var.h"
54
55#ifdef NS
56#include "../netns/ns.h"
57#endif
58#include "machine/mtpr.h"
59#include "netisr.h"
60
61#define SA(p) ((struct sockaddr *)(p))
62
63int rttrash; /* routes not in table but not freed */
64struct sockaddr wildcard; /* zero valued cookie for wildcard searches */
65int rthashsize = RTHASHSIZ; /* for netstat, etc. */
66
67static int rtinits_done = 0;
68struct radix_node_head *ns_rnhead, *in_rnhead;
69struct radix_node *rn_match(), *rn_delete(), *rn_addroute();
70
71rtinitheads()
72{
73 if (rtinits_done == 0 &&
74#ifdef NS
75 rn_inithead(&ns_rnhead, 16, AF_NS) &&
76#endif
77 rn_inithead(&in_rnhead, 32, AF_INET))
78 rtinits_done = 1;
79}
80
81/*
82 * Packet routing routines.
83 */
84rtalloc(ro)
85 register struct route *ro;
86{
87 if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
88 return; /* XXX */
89 ro->ro_rt = rtalloc1(&ro->ro_dst, 1);
90}
91
92struct rtentry *
93rtalloc1(dst, report)
94 register struct sockaddr *dst;
95 int report;
96{
97 register struct radix_node_head *rnh;
98 register struct rtentry *rt;
99 register struct radix_node *rn;
100 struct rtentry *newrt = 0;
101 int s = splnet(), err = 0, msgtype = RTM_MISS;
102
103 for (rnh = radix_node_head; rnh && (dst->sa_family != rnh->rnh_af); )
104 rnh = rnh->rnh_next;
105 if (rnh && rnh->rnh_treetop &&
106 (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) &&
107 ((rn->rn_flags & RNF_ROOT) == 0)) {
108 newrt = rt = (struct rtentry *)rn;
109 if (report && (rt->rt_flags & RTF_CLONING)) {
110 if ((err = rtrequest(RTM_RESOLVE, dst, SA(0),
111 SA(0), 0, &newrt)) ||
112 ((rt->rt_flags & RTF_XRESOLVE)
113 && (msgtype = RTM_RESOLVE))) /* intended! */
114 goto miss;
115 } else
116 rt->rt_refcnt++;
117 } else {
118 rtstat.rts_unreach++;
119 miss: if (report)
120 rt_missmsg(msgtype, dst, SA(0), SA(0), SA(0), 0, err);
121 }
122 splx(s);
123 return (newrt);
124}
125
126rtfree(rt)
127 register struct rtentry *rt;
128{
129 register struct ifaddr *ifa;
130 if (rt == 0)
131 panic("rtfree");
132 rt->rt_refcnt--;
133 if (rt->rt_refcnt <= 0 && (rt->rt_flags & RTF_UP) == 0) {
134 rttrash--;
135 if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
136 panic ("rtfree 2");
137 free((caddr_t)rt, M_RTABLE);
138 }
139}
140
141/*
142 * Force a routing table entry to the specified
143 * destination to go through the given gateway.
144 * Normally called as a result of a routing redirect
145 * message from the network layer.
146 *
147 * N.B.: must be called at splnet
148 *
149 */
150rtredirect(dst, gateway, netmask, flags, src, rtp)
151 struct sockaddr *dst, *gateway, *netmask, *src;
152 int flags;
153 struct rtentry **rtp;
154{
155 register struct rtentry *rt = 0;
156 int error = 0;
157 short *stat = 0;
158
159 /* verify the gateway is directly reachable */
160 if (ifa_ifwithnet(gateway) == 0) {
161 error = ENETUNREACH;
162 goto done;
163 }
164 rt = rtalloc1(dst, 0);
165 /*
166 * If the redirect isn't from our current router for this dst,
167 * it's either old or wrong. If it redirects us to ourselves,
168 * we have a routing loop, perhaps as a result of an interface
169 * going down recently.
170 */
171#define equal(a1, a2) (bcmp((caddr_t)(a1), (caddr_t)(a2), (a1)->sa_len) == 0)
172 if (!(flags & RTF_DONE) && rt && !equal(src, rt->rt_gateway))
173 error = EINVAL;
174 else if (ifa_ifwithaddr(gateway))
175 error = EHOSTUNREACH;
176 if (error)
177 goto done;
178 /*
179 * Create a new entry if we just got back a wildcard entry
180 * or the the lookup failed. This is necessary for hosts
181 * which use routing redirects generated by smart gateways
182 * to dynamically build the routing tables.
183 */
184 if ((rt == 0) || (rt_mask(rt) && rt_mask(rt)->sa_len < 2))
185 goto create;
186 /*
187 * Don't listen to the redirect if it's
188 * for a route to an interface.
189 */
190 if (rt->rt_flags & RTF_GATEWAY) {
191 if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
192 /*
193 * Changing from route to net => route to host.
194 * Create new route, rather than smashing route to net.
195 */
196 create:
197 flags |= RTF_GATEWAY | RTF_DYNAMIC;
198 error = rtrequest((int)RTM_ADD, dst, gateway,
199 SA(0), flags,
200 (struct rtentry **)0);
201 stat = &rtstat.rts_dynamic;
202 } else {
203 /*
204 * Smash the current notion of the gateway to
205 * this destination. Should check about netmask!!!
206 */
207 if (gateway->sa_len <= rt->rt_gateway->sa_len) {
208 Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
209 rt->rt_flags |= RTF_MODIFIED;
210 flags |= RTF_MODIFIED;
211 stat = &rtstat.rts_newgateway;
212 } else
213 error = ENOSPC;
214 }
215 } else
216 error = EHOSTUNREACH;
217done:
218 if (rt) {
219 if (rtp && !error)
220 *rtp = rt;
221 else
222 rtfree(rt);
223 }
224 if (error)
225 rtstat.rts_badredirect++;
226 else
227 (stat && (*stat)++);
228 rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src, flags, error);
229}
230
231/*
232* Routing table ioctl interface.
233*/
234rtioctl(req, data, p)
235 int req;
236 caddr_t data;
237 struct proc *p;
238{
239#ifndef COMPAT_43
240 return (EOPNOTSUPP);
241#else
242 register struct ortentry *entry = (struct ortentry *)data;
243 int error;
244 struct sockaddr *netmask = 0;
245
246 if (req == SIOCADDRT)
247 req = RTM_ADD;
248 else if (req == SIOCDELRT)
249 req = RTM_DELETE;
250 else
251 return (EINVAL);
252
253 if (error = suser(p->p_ucred, &p->p_acflag))
254 return (error);
255#if BYTE_ORDER != BIG_ENDIAN
256 if (entry->rt_dst.sa_family == 0 && entry->rt_dst.sa_len < 16) {
257 entry->rt_dst.sa_family = entry->rt_dst.sa_len;
258 entry->rt_dst.sa_len = 16;
259 }
260 if (entry->rt_gateway.sa_family == 0 && entry->rt_gateway.sa_len < 16) {
261 entry->rt_gateway.sa_family = entry->rt_gateway.sa_len;
262 entry->rt_gateway.sa_len = 16;
263 }
264#else
265 if (entry->rt_dst.sa_len == 0)
266 entry->rt_dst.sa_len = 16;
267 if (entry->rt_gateway.sa_len == 0)
268 entry->rt_gateway.sa_len = 16;
269#endif
270 if ((entry->rt_flags & RTF_HOST) == 0)
271 switch (entry->rt_dst.sa_family) {
272#ifdef INET
273 case AF_INET:
274 {
275 extern struct sockaddr_in icmpmask;
276 struct sockaddr_in *dst_in =
277 (struct sockaddr_in *)&entry->rt_dst;
278
279 in_sockmaskof(dst_in->sin_addr, &icmpmask);
280 netmask = (struct sockaddr *)&icmpmask;
281 }
282 break;
283#endif
284#ifdef NS
285 case AF_NS:
286 {
287 extern struct sockaddr_ns ns_netmask;
288 netmask = (struct sockaddr *)&ns_netmask;
289 }
290#endif
291 }
292 error = rtrequest(req, &(entry->rt_dst), &(entry->rt_gateway), netmask,
293 entry->rt_flags, (struct rtentry **)0);
294 rt_missmsg((req == RTM_ADD ? RTM_OLDADD : RTM_OLDDEL),
295 &(entry->rt_dst), &(entry->rt_gateway),
296 netmask, SA(0), entry->rt_flags, error);
297 return (error);
298#endif
299}
300
301struct ifaddr *
302ifa_ifwithroute(flags, dst, gateway)
303int flags;
304struct sockaddr *dst, *gateway;
305{
306 register struct ifaddr *ifa;
307 if ((flags & RTF_GATEWAY) == 0) {
308 /*
309 * If we are adding a route to an interface,
310 * and the interface is a pt to pt link
311 * we should search for the destination
312 * as our clue to the interface. Otherwise
313 * we can use the local address.
314 */
315 ifa = 0;
316 if (flags & RTF_HOST)
317 ifa = ifa_ifwithdstaddr(dst);
318 if (ifa == 0)
319 ifa = ifa_ifwithaddr(gateway);
320 } else {
321 /*
322 * If we are adding a route to a remote net
323 * or host, the gateway may still be on the
324 * other end of a pt to pt link.
325 */
326 ifa = ifa_ifwithdstaddr(gateway);
327 }
328 if (ifa == 0)
329 ifa = ifa_ifwithnet(gateway);
330 if (ifa == 0) {
331 struct rtentry *rt = rtalloc1(dst, 0);
332 if (rt == 0)
333 return (0);
334 rt->rt_refcnt--;
335 if ((ifa = rt->rt_ifa) == 0)
336 return (0);
337 }
338 if (ifa->ifa_addr->sa_family != dst->sa_family) {
339 struct ifaddr *oifa = ifa, *ifaof_ifpforaddr();
340 ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
341 if (ifa == 0)
342 ifa = oifa;
343 }
344 return (ifa);
345}
346
347#define ROUNDUP(a) (a>0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
348
349rtrequest(req, dst, gateway, netmask, flags, ret_nrt)
350 int req, flags;
351 struct sockaddr *dst, *gateway, *netmask;
352 struct rtentry **ret_nrt;
353{
354 int s = splnet(), len, error = 0;
355 register struct rtentry *rt;
356 register struct radix_node *rn;
357 register struct radix_node_head *rnh;
358 struct ifaddr *ifa, *ifa_ifwithdstaddr();
359 struct sockaddr *ndst;
360 u_char af = dst->sa_family;
361#define senderr(x) { error = x ; goto bad; }
362
363 if (rtinits_done == 0)
364 rtinitheads();
365 for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); )
366 rnh = rnh->rnh_next;
367 if (rnh == 0)
368 senderr(ESRCH);
369 if (flags & RTF_HOST)
370 netmask = 0;
371 switch (req) {
372 case RTM_DELETE:
373 if (ret_nrt && (rt = *ret_nrt)) {
374 RTFREE(rt);
375 *ret_nrt = 0;
376 }
377 if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask,
378 rnh->rnh_treetop)) == 0)
379 senderr(ESRCH);
380 if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
381 panic ("rtrequest delete");
382 rt = (struct rtentry *)rn;
383 rt->rt_flags &= ~RTF_UP;
384 if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
385 ifa->ifa_rtrequest(RTM_DELETE, rt, SA(0));
386 rttrash++;
387 if (rt->rt_refcnt <= 0)
388 rtfree(rt);
389 break;
390
391 case RTM_RESOLVE:
392 if (ret_nrt == 0 || (rt = *ret_nrt) == 0)
393 senderr(EINVAL);
394 ifa = rt->rt_ifa;
395 flags = rt->rt_flags & ~RTF_CLONING;
396 gateway = rt->rt_gateway;
397 if ((netmask = rt->rt_genmask) == 0)
398 flags |= RTF_HOST;
399 goto makeroute;
400
401 case RTM_ADD:
402 if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == 0)
403 senderr(ENETUNREACH);
404 makeroute:
405 len = sizeof (*rt) + ROUNDUP(gateway->sa_len)
406 + ROUNDUP(dst->sa_len);
407 R_Malloc(rt, struct rtentry *, len);
408 if (rt == 0)
409 senderr(ENOBUFS);
410 Bzero(rt, len);
411 ndst = (struct sockaddr *)(rt + 1);
412 if (netmask) {
413 rt_maskedcopy(dst, ndst, netmask);
414 } else
415 Bcopy(dst, ndst, dst->sa_len);
416 rn = rn_addroute((caddr_t)ndst, (caddr_t)netmask,
417 rnh->rnh_treetop, rt->rt_nodes);
418 if (rn == 0) {
419 free((caddr_t)rt, M_RTABLE);
420 senderr(EEXIST);
421 }
422 rt->rt_ifa = ifa;
423 rt->rt_ifp = ifa->ifa_ifp;
424 rt->rt_flags = RTF_UP | flags;
425 rt->rt_gateway = (struct sockaddr *)
426 (rn->rn_key + ROUNDUP(dst->sa_len));
427 Bcopy(gateway, rt->rt_gateway, gateway->sa_len);
428 if (req == RTM_RESOLVE)
429 rt->rt_rmx = (*ret_nrt)->rt_rmx; /* copy metrics */
430 if (ifa->ifa_rtrequest)
431 ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));
432 if (ret_nrt) {
433 *ret_nrt = rt;
434 rt->rt_refcnt++;
435 }
436 break;
437 }
438bad:
439 splx(s);
440 return (error);
441}
442
443rt_maskedcopy(src, dst, netmask)
444struct sockaddr *src, *dst, *netmask;
445{
446 register u_char *cp1 = (u_char *)src;
447 register u_char *cp2 = (u_char *)dst;
448 register u_char *cp3 = (u_char *)netmask;
449 u_char *cplim = cp2 + *cp3;
450 u_char *cplim2 = cp2 + *cp1;
451
452 *cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
453 cp3 += 2;
454 if (cplim > cplim2)
455 cplim = cplim2;
456 while (cp2 < cplim)
457 *cp2++ = *cp1++ & *cp3++;
458 if (cp2 < cplim2)
459 bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2));
460}
461/*
462 * Set up a routing table entry, normally
463 * for an interface.
464 */
465rtinit(ifa, cmd, flags)
466 register struct ifaddr *ifa;
467 int cmd, flags;
468{
469 register struct rtentry *rt;
470 register struct sockaddr *dst;
471 register struct sockaddr *deldst;
472 struct mbuf *m = 0;
473 int error;
474
475 dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
476 if (ifa->ifa_flags & IFA_ROUTE) {
477 if ((rt = ifa->ifa_rt) && (rt->rt_flags & RTF_UP) == 0) {
478 RTFREE(rt);
479 ifa->ifa_rt = 0;
480 }
481 }
482 if (cmd == RTM_DELETE) {
483 if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
484 m = m_get(M_WAIT, MT_SONAME);
485 deldst = mtod(m, struct sockaddr *);
486 rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
487 dst = deldst;
488 }
489 if (rt = rtalloc1(dst, 0)) {
490 rt->rt_refcnt--;
491 if (rt->rt_ifa != ifa) {
492 if (m)
493 (void) m_free(m);
494 return (flags & RTF_HOST ? EHOSTUNREACH
495 : ENETUNREACH);
496 }
497 }
498 }
499 error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
500 flags | ifa->ifa_flags, &ifa->ifa_rt);
501 if (m)
502 (void) m_free(m);
503 if (cmd == RTM_ADD && error == 0 && (rt = ifa->ifa_rt)
504 && rt->rt_ifa != ifa) {
505 rt->rt_ifa = ifa;
506 rt->rt_ifp = ifa->ifa_ifp;
507 }
508 return (error);
509}