xref: /original-bsd/sys/net/route.c (revision 29d43723)
1 /*
2  * Copyright (c) 1980, 1986 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
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.
16  *
17  *	@(#)route.c	7.8 (Berkeley) 12/13/88
18  */
19 #include "../machine/reg.h"
20 
21 #include "param.h"
22 #include "systm.h"
23 #include "dir.h"
24 #include "user.h"
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"
32 #include "errno.h"
33 #include "ioctl.h"
34 
35 #include "if.h"
36 #include "af.h"
37 #include "route.h"
38 #include "raw_cb.h"
39 #include "../netinet/in.h"
40 #include "../netinet/in_var.h"
41 
42 #include "../machine/mtpr.h"
43 #include "netisr.h"
44 
45 #include "rtsock.c"
46 
47 int	rttrash;		/* routes not in table but not freed */
48 struct	sockaddr wildcard;	/* zero valued cookie for wildcard searches */
49 int	rthashsize = RTHASHSIZ;	/* for netstat, etc. */
50 
51 static int rtinits_done = 0;
52 struct radix_node_head *ns_rnhead, *in_rnhead;
53 struct radix_node *rn_match(), *rn_delete(), *rn_addroute();
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 
62 /*
63  * Packet routing routines.
64  */
65 rtalloc(ro)
66 	register struct route *ro;
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;
77 {
78 	register struct radix_node_head *rnh;
79 	register struct radix_node *rn;
80 	register struct rtentry *rt = 0;
81 	u_char af = dst->sa_family;
82 	int  s = splnet();
83 
84 	for (rnh = radix_node_head; rnh && (af != rnh->rnh_af); )
85 		rnh = rnh->rnh_next;
86 	if (rnh && rnh->rnh_treetop &&
87 	    (rn = rn_match((caddr_t)dst, rnh->rnh_treetop)) &&
88 	    ((rn->rn_flags & RNF_ROOT) == 0)) {
89 		rt = (struct rtentry *)rn;
90 		rt->rt_refcnt++;
91 	} else {
92 		rtstat.rts_unreach++;
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 	}
97 	splx(s);
98 	return (rt);
99 }
100 
101 rtfree(rt)
102 	register struct rtentry *rt;
103 {
104 	u_char *af;
105 	if (rt == 0)
106 		panic("rtfree");
107 	rt->rt_refcnt--;
108 	if (rt->rt_refcnt <= 0 && (rt->rt_flags&RTF_UP) == 0) {
109 		rttrash--;
110 		if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT))
111 			panic ("rtfree 2");
112 		free((caddr_t)rt, M_RTABLE);
113 	}
114 }
115 
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  *
122  * N.B.: must be called at splnet
123  *
124  */
125 rtredirect(dst, gateway, netmask, flags, src)
126 	struct sockaddr *dst, *gateway, *src;
127 	int flags;
128 {
129 	register struct rtentry *rt;
130 
131 	/* verify the gateway is directly reachable */
132 	if (ifa_ifwithnet(gateway) == 0) {
133 		rtstat.rts_badredirect++;
134 		return;
135 	}
136 	rt = rtalloc1(dst, 1);
137 #define	equal(a1, a2) \
138   (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
139 	/*
140 	 * If the redirect isn't from our current router for this dst,
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.
144 	 */
145 	if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) {
146 		rtstat.rts_badredirect++;
147 		if (rt)
148 			rtfree(rt);
149 		return;
150 	}
151 	/*
152 	 * Old comment:
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.
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) {
167 		rtfree(rt);
168 		rt = 0;
169 	}
170 	if (route_cb.any_count)
171 		rt_missmsg(RTM_REDIRECT, dst, gateway, netmask, src,
172 				(flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC);
173 	 */
174 	if (rt == 0) {
175 		rtrequest((int)RTM_ADD, dst, gateway, 0,
176 		    (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC, 0);
177 		rtstat.rts_dynamic++;
178 		return;
179 	}
180 	/*
181 	 * Don't listen to the redirect if it's
182 	 * for a route to an interface.
183 	 */
184 	if (rt->rt_flags & RTF_GATEWAY) {
185 		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
186 			/*
187 			 * Changing from route to net => route to host.
188 			 * Create new route, rather than smashing route to net.
189 			 */
190 			rtrequest((int)RTM_ADD, dst, gateway, 0,
191 			    (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC, 0);
192 			rtstat.rts_dynamic++;
193 		} else {
194 			/*
195 			 * Smash the current notion of the gateway to
196 			 * this destination.
197 			 */
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++;
204 		}
205 	} else
206 		rtstat.rts_badredirect++;
207 	rtfree(rt);
208 }
209 
210 /*
211  * Routing table ioctl interface.
212  */
213 rtioctl(req, data)
214 	int req;
215 	caddr_t data;
216 {
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;
223 
224 	if (req == SIOCADDRT)
225 		req = RTM_ADD;
226 	else if (req == SIOCDELRT)
227 		req = RTM_DELETE;
228 	else
229 		return (EINVAL);
230 
231 	if (!suser())
232 		return (u.u_error);
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
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);
278 #endif
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 
293 	if (rtinits_done == 0)
294 		rtinitheads();
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 	}
301 	switch (req) {
302 	case RTM_DELETE:
303 		if ((rn = rn_delete((caddr_t)dst, (caddr_t)netmask,
304 					rnh->rnh_treetop)) == 0) {
305 			error = ESRCH;
306 			goto bad;
307 		}
308 		if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT))
309 			panic ("rtrequest delete");
310 		rt = (struct rtentry *)rn;
311 		rt->rt_flags &= ~RTF_UP;
312 		if (rt->rt_refcnt > 0)
313 			rttrash++;
314 		else
315 			free((caddr_t)rt, M_RTABLE);
316 		break;
317 
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) {
351 			error = ENOBUFS;
352 			goto bad;
353 		}
354 		Bzero(rt, len);
355 		rn = rn_addroute((caddr_t)dst, (caddr_t)netmask,
356 					rnh->rnh_treetop, rt->rt_nodes);
357 		if (rn == 0) {
358 			free((caddr_t)rt, M_RTABLE);
359 			error = EEXIST;
360 			goto bad;
361 		}
362 		if (ret_nrt)
363 			*ret_nrt = rt; /* == (struct rtentry *)rn */
364 		rt->rt_ifp = ifa->ifa_ifp;
365 		rt->rt_use = 0;
366 		rt->rt_refcnt = 0;
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);
374 		break;
375 	}
376 bad:
377 	splx(s);
378 	return (error);
379 }
380 /*
381  * Set up a routing table entry, normally
382  * for an interface.
383  */
384 rtinit(ifa, cmd, flags)
385 	register struct ifaddr *ifa;
386 	int cmd, flags;
387 {
388 	struct sockaddr net, *netp;
389 	register caddr_t cp, cp2, cp3;
390 	caddr_t cplim, freeit = 0;
391 	int len;
392 
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 	}
416 }
417 #include "radix.c"
418