xref: /original-bsd/sys/net/route.c (revision 81f57ac7)
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.7 (Berkeley) 11/09/88
18  */
19 
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"
29 
30 #include "if.h"
31 #include "af.h"
32 #include "route.h"
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
40 
41 int	rttrash;		/* routes not in table but not freed */
42 struct	sockaddr wildcard;	/* zero valued cookie for wildcard searches */
43 int	rthashsize = RTHASHSIZ;	/* for netstat, etc. */
44 
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 
55 /*
56  * Packet routing routines.
57  */
58 rtalloc(ro)
59 	register struct route *ro;
60 {
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;
66 
67 	if (ro->ro_rt && ro->ro_rt->rt_ifp && (ro->ro_rt->rt_flags & RTF_UP))
68 		return;				 /* XXX */
69 	s = splnet();
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);
76 		rt->rt_refcnt++;
77 	} else
78 		rtstat.rts_unreach++;
79 	ro->ro_rt = rt;
80 	splx(s);
81 }
82 
83 rtfree(rt)
84 	register struct rtentry *rt;
85 {
86 	register struct nrtentry *nrt;
87 	u_char *af;
88 	if (rt == 0)
89 		panic("rtfree");
90 	rt->rt_refcnt--;
91 	if (rt->rt_refcnt <= 0 && (rt->rt_flags&RTF_UP) == 0) {
92 		rttrash--;
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);
97 	}
98 }
99 
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  *
106  * N.B.: must be called at splnet
107  *
108  */
109 rtredirect(dst, gateway, flags, src)
110 	struct sockaddr *dst, *gateway, *src;
111 	int flags;
112 {
113 	struct route ro;
114 	register struct rtentry *rt;
115 
116 	/* verify the gateway is directly reachable */
117 	if (ifa_ifwithnet(gateway) == 0) {
118 		rtstat.rts_badredirect++;
119 		return;
120 	}
121 	ro.ro_dst = *dst;
122 	ro.ro_rt = 0;
123 	rtalloc(&ro);
124 	rt = ro.ro_rt;
125 #define	equal(a1, a2) \
126   (bcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
127 	/*
128 	 * If the redirect isn't from our current router for this dst,
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.
132 	 */
133 	if ((rt && !equal(src, &rt->rt_gateway)) || ifa_ifwithaddr(gateway)) {
134 		rtstat.rts_badredirect++;
135 		if (rt)
136 			rtfree(rt);
137 		return;
138 	}
139 	/*
140 	 * Old comment:
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.
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) {
155 		rtfree(rt);
156 		rt = 0;
157 	}
158 	 */
159 	if (rt == 0) {
160 		rtinit(dst, gateway, (int)SIOCADDRT,
161 		    (flags & RTF_HOST) | RTF_GATEWAY | RTF_DYNAMIC);
162 		rtstat.rts_dynamic++;
163 		return;
164 	}
165 	/*
166 	 * Don't listen to the redirect if it's
167 	 * for a route to an interface.
168 	 */
169 	if (rt->rt_flags & RTF_GATEWAY) {
170 		if (((rt->rt_flags & RTF_HOST) == 0) && (flags & RTF_HOST)) {
171 			/*
172 			 * Changing from route to net => route to host.
173 			 * Create new route, rather than smashing route to net.
174 			 */
175 			rtinit(dst, gateway, (int)SIOCADDRT,
176 			    flags | RTF_DYNAMIC);
177 			rtstat.rts_dynamic++;
178 		} else {
179 			/*
180 			 * Smash the current notion of the gateway to
181 			 * this destination.
182 			 */
183 			rt->rt_gateway = *gateway; /*XXX -- size? */
184 			rt->rt_flags |= RTF_MODIFIED;
185 			rtstat.rts_newgateway++;
186 		}
187 	} else
188 		rtstat.rts_badredirect++;
189 	rtfree(rt);
190 }
191 
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 }
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 }
260 
261 /*
262  * Carry out a request to change the routing table.  Called by
263  * interfaces at boot time to make their ``local routes'' known,
264  * for ioctl's, and as the result of routing redirects.
265  */
266 rtrequest(req, entry)
267 	int req;
268 	register struct rtentry *entry;
269 {
270 	register struct rtentry *rt;
271 	int s, error = 0, found;
272 	u_char af;
273 	struct ifaddr *ifa;
274 	struct ifaddr *ifa_ifwithdstaddr();
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;
280 
281 #ifdef COMPAT_43
282 #if BYTE_ORDER != BIG_ENDIAN
283 	s = splnet();
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
299 	if (rtinits_done == 0)
300 		rtinitheads();
301 	af = entry->rt_dst.sa_family;
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);
322 	} else {
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);
329 	}
330 	if (ifa == 0) {
331 		ifa = ifa_ifwithnet(&entry->rt_gateway);
332 		if (ifa == 0 && req == SIOCADDRT) {
333 			error = ENETUNREACH;
334 			goto bad;
335 		}
336 	}
337 	if (entry->rt_flags & RTF_HOST)
338 		netmask = 0;
339 	else
340 		netmask = rtgetmask(&entry->rt_dst, ifa);
341 	switch (req) {
342 
343 	case SIOCDELRT:
344 		if ((rn = rn_delete((char *)&entry->rt_dst,
345 					netmask, head)) == 0) {
346 			error = ESRCH;
347 			goto bad;
348 		}
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)
354 			rttrash++;
355 		else
356 			free((caddr_t)nrt, M_RTABLE);
357 		break;
358 
359 	case SIOCADDRT:
360 		Malloc(nrt, struct nrtentry *, sizeof *nrt);
361 		if (nrt == 0) {
362 			error = ENOBUFS;
363 			goto bad;
364 		}
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;
371 			goto bad;
372 		}
373 		rt = &nrt->nrt_rt;
374 		rn->rn_key = (char *)&(nrt->nrt_rt.rt_dst);
375 		rt->rt_dst = entry->rt_dst;
376 		rt->rt_gateway = entry->rt_gateway;
377 		rt->rt_flags = RTF_UP |
378 		    (entry->rt_flags & (RTF_HOST|RTF_GATEWAY|RTF_DYNAMIC));
379 		rt->rt_use = 0;
380 		rt->rt_refcnt = 0;
381 		rt->rt_ifp = ifa->ifa_ifp;
382 		break;
383 	}
384 bad:
385 	splx(s);
386 	return (error);
387 }
388 
389 /*
390  * Set up a routing table entry, normally
391  * for an interface.
392  */
393 rtinit(dst, gateway, cmd, flags)
394 	struct sockaddr *dst, *gateway;
395 	int cmd, flags;
396 {
397 	struct rtentry route;
398 
399 	bzero((caddr_t)&route, sizeof (route));
400 	route.rt_dst = *dst;
401 	route.rt_gateway = *gateway;
402 	route.rt_flags = flags;
403 	(void) rtrequest(cmd, &route);
404 }
405