xref: /386bsd/usr/src/libexec/routed/tables.c (revision a2142627)
1 /*
2  * Copyright (c) 1983, 1988 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  */
33 
34 #ifndef lint
35 static char sccsid[] = "@(#)tables.c	5.17 (Berkeley) 6/1/90";
36 #endif /* not lint */
37 
38 /*
39  * Routing Table Management Daemon
40  */
41 #include "defs.h"
42 #include <sys/ioctl.h>
43 #include <errno.h>
44 #include <sys/syslog.h>
45 
46 #ifndef DEBUG
47 #define	DEBUG	0
48 #endif
49 
50 #ifdef RTM_ADD
51 #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
52 #else
53 #define FIXLEN(s) { }
54 #endif
55 
56 int	install = !DEBUG;		/* if 1 call kernel */
57 
58 /*
59  * Lookup dst in the tables for an exact match.
60  */
61 struct rt_entry *
rtlookup(dst)62 rtlookup(dst)
63 	struct sockaddr *dst;
64 {
65 	register struct rt_entry *rt;
66 	register struct rthash *rh;
67 	register u_int hash;
68 	struct afhash h;
69 	int doinghost = 1;
70 
71 	if (dst->sa_family >= af_max)
72 		return (0);
73 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
74 	hash = h.afh_hosthash;
75 	rh = &hosthash[hash & ROUTEHASHMASK];
76 again:
77 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
78 		if (rt->rt_hash != hash)
79 			continue;
80 		if (equal(&rt->rt_dst, dst))
81 			return (rt);
82 	}
83 	if (doinghost) {
84 		doinghost = 0;
85 		hash = h.afh_nethash;
86 		rh = &nethash[hash & ROUTEHASHMASK];
87 		goto again;
88 	}
89 	return (0);
90 }
91 
92 struct sockaddr wildcard;	/* zero valued cookie for wildcard searches */
93 
94 /*
95  * Find a route to dst as the kernel would.
96  */
97 struct rt_entry *
rtfind(dst)98 rtfind(dst)
99 	struct sockaddr *dst;
100 {
101 	register struct rt_entry *rt;
102 	register struct rthash *rh;
103 	register u_int hash;
104 	struct afhash h;
105 	int af = dst->sa_family;
106 	int doinghost = 1, (*match)();
107 
108 	if (af >= af_max)
109 		return (0);
110 	(*afswitch[af].af_hash)(dst, &h);
111 	hash = h.afh_hosthash;
112 	rh = &hosthash[hash & ROUTEHASHMASK];
113 
114 again:
115 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
116 		if (rt->rt_hash != hash)
117 			continue;
118 		if (doinghost) {
119 			if (equal(&rt->rt_dst, dst))
120 				return (rt);
121 		} else {
122 			if (rt->rt_dst.sa_family == af &&
123 			    (*match)(&rt->rt_dst, dst))
124 				return (rt);
125 		}
126 	}
127 	if (doinghost) {
128 		doinghost = 0;
129 		hash = h.afh_nethash;
130 		rh = &nethash[hash & ROUTEHASHMASK];
131 		match = afswitch[af].af_netmatch;
132 		goto again;
133 	}
134 	/*
135 	 * Check for wildcard gateway, by convention network 0.
136 	 */
137 	if (dst != &wildcard) {
138 		dst = &wildcard, hash = 0;
139 		goto again;
140 	}
141 	return (0);
142 }
143 
144 rtadd(dst, gate, metric, state)
145 	struct sockaddr *dst, *gate;
146 	int metric, state;
147 {
148 	struct afhash h;
149 	register struct rt_entry *rt;
150 	struct rthash *rh;
151 	int af = dst->sa_family, flags;
152 	u_int hash;
153 
154 	if (af >= af_max)
155 		return;
156 	(*afswitch[af].af_hash)(dst, &h);
157 	flags = (*afswitch[af].af_rtflags)(dst);
158 	/*
159 	 * Subnet flag isn't visible to kernel, move to state.	XXX
160 	 */
161 	FIXLEN(dst);
162 	FIXLEN(gate);
163 	if (flags & RTF_SUBNET) {
164 		state |= RTS_SUBNET;
165 		flags &= ~RTF_SUBNET;
166 	}
167 	if (flags & RTF_HOST) {
168 		hash = h.afh_hosthash;
169 		rh = &hosthash[hash & ROUTEHASHMASK];
170 	} else {
171 		hash = h.afh_nethash;
172 		rh = &nethash[hash & ROUTEHASHMASK];
173 	}
174 	rt = (struct rt_entry *)malloc(sizeof (*rt));
175 	if (rt == 0)
176 		return;
177 	rt->rt_hash = hash;
178 	rt->rt_dst = *dst;
179 	rt->rt_router = *gate;
180 	rt->rt_timer = 0;
181 	rt->rt_flags = RTF_UP | flags;
182 	rt->rt_state = state | RTS_CHANGED;
183 	rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
184 	if (rt->rt_ifp == 0)
185 		rt->rt_ifp = if_ifwithnet(&rt->rt_router);
186 	if ((state & RTS_INTERFACE) == 0)
187 		rt->rt_flags |= RTF_GATEWAY;
188 	rt->rt_metric = metric;
189 	insque(rt, rh);
190 	TRACE_ACTION("ADD", rt);
191 	/*
192 	 * If the ioctl fails because the gateway is unreachable
193 	 * from this host, discard the entry.  This should only
194 	 * occur because of an incorrect entry in /etc/gateways.
195 	 */
196 	if (install && (rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
197 	    ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0) {
198 		if (errno != EEXIST && gate->sa_family < af_max)
199 			syslog(LOG_ERR,
200 			"adding route to net/host %s through gateway %s: %m\n",
201 			   (*afswitch[dst->sa_family].af_format)(dst),
202 			   (*afswitch[gate->sa_family].af_format)(gate));
203 		perror("SIOCADDRT");
204 		if (errno == ENETUNREACH) {
205 			TRACE_ACTION("DELETE", rt);
206 			remque(rt);
207 			free((char *)rt);
208 		}
209 	}
210 }
211 
212 rtchange(rt, gate, metric)
213 	struct rt_entry *rt;
214 	struct sockaddr *gate;
215 	short metric;
216 {
217 	int add = 0, delete = 0, newgateway = 0;
218 	struct rtentry oldroute;
219 
220 	FIXLEN(gate);
221 	FIXLEN(&(rt->rt_router));
222 	FIXLEN(&(rt->rt_dst));
223 	if (!equal(&rt->rt_router, gate)) {
224 		newgateway++;
225 		TRACE_ACTION("CHANGE FROM ", rt);
226 	} else if (metric != rt->rt_metric)
227 		TRACE_NEWMETRIC(rt, metric);
228 	if ((rt->rt_state & RTS_INTERNAL) == 0) {
229 		/*
230 		 * If changing to different router, we need to add
231 		 * new route and delete old one if in the kernel.
232 		 * If the router is the same, we need to delete
233 		 * the route if has become unreachable, or re-add
234 		 * it if it had been unreachable.
235 		 */
236 		if (newgateway) {
237 			add++;
238 			if (rt->rt_metric != HOPCNT_INFINITY)
239 				delete++;
240 		} else if (metric == HOPCNT_INFINITY)
241 			delete++;
242 		else if (rt->rt_metric == HOPCNT_INFINITY)
243 			add++;
244 	}
245 	if (delete)
246 		oldroute = rt->rt_rt;
247 	if ((rt->rt_state & RTS_INTERFACE) && delete) {
248 		rt->rt_state &= ~RTS_INTERFACE;
249 		rt->rt_flags |= RTF_GATEWAY;
250 		if (metric > rt->rt_metric && delete)
251 			syslog(LOG_ERR, "%s route to interface %s (timed out)",
252 			    add? "changing" : "deleting",
253 			    rt->rt_ifp->int_name);
254 	}
255 	if (add) {
256 		rt->rt_router = *gate;
257 		rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
258 		if (rt->rt_ifp == 0)
259 			rt->rt_ifp = if_ifwithnet(&rt->rt_router);
260 	}
261 	rt->rt_metric = metric;
262 	rt->rt_state |= RTS_CHANGED;
263 	if (newgateway)
264 		TRACE_ACTION("CHANGE TO   ", rt);
265 #ifndef RTM_ADD
266 	if (add && install)
267 		if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
268 			perror("SIOCADDRT");
269 	if (delete && install)
270 		if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
271 			perror("SIOCDELRT");
272 #else
273 	if (delete && install)
274 		if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
275 			perror("SIOCDELRT");
276 	if (add && install) {
277 		if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
278 			perror("SIOCADDRT");
279 	}
280 #endif
281 }
282 
283 rtdelete(rt)
284 	struct rt_entry *rt;
285 {
286 
287 	TRACE_ACTION("DELETE", rt);
288 	FIXLEN(&(rt->rt_router));
289 	FIXLEN(&(rt->rt_dst));
290 	if (rt->rt_metric < HOPCNT_INFINITY) {
291 	    if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
292 		syslog(LOG_ERR,
293 		    "deleting route to interface %s? (timed out?)",
294 		    rt->rt_ifp->int_name);
295 	    if (install &&
296 		(rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
297 		ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
298 		    perror("SIOCDELRT");
299 	}
300 	remque(rt);
301 	free((char *)rt);
302 }
303 
rtdeleteall(sig)304 rtdeleteall(sig)
305 	int sig;
306 {
307 	register struct rthash *rh;
308 	register struct rt_entry *rt;
309 	struct rthash *base = hosthash;
310 	int doinghost = 1;
311 
312 again:
313 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
314 		rt = rh->rt_forw;
315 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
316 			if (rt->rt_state & RTS_INTERFACE ||
317 			    rt->rt_metric >= HOPCNT_INFINITY)
318 				continue;
319 			TRACE_ACTION("DELETE", rt);
320 			if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
321 			    ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
322 				perror("SIOCDELRT");
323 		}
324 	}
325 	if (doinghost) {
326 		doinghost = 0;
327 		base = nethash;
328 		goto again;
329 	}
330 	exit(sig);
331 }
332 
333 /*
334  * If we have an interface to the wide, wide world,
335  * add an entry for an Internet default route (wildcard) to the internal
336  * tables and advertise it.  This route is not added to the kernel routes,
337  * but this entry prevents us from listening to other people's defaults
338  * and installing them in the kernel here.
339  */
rtdefault()340 rtdefault()
341 {
342 	extern struct sockaddr inet_default;
343 
344 	rtadd(&inet_default, &inet_default, 1,
345 		RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
346 }
347 
rtinit()348 rtinit()
349 {
350 	register struct rthash *rh;
351 
352 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
353 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
354 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
355 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
356 }
357 
358 
359 /* ffrom /sys/i386/i386/machdep.c */
360 /*
361  * insert an element into a queue
362  */
insque(element,head)363 insque(element, head)
364 	register struct rthash *element, *head;
365 {
366 	element->rt_forw = head->rt_forw;
367 	head->rt_forw = (struct rt_entry *)element;
368 	element->rt_back = (struct rt_entry *)head;
369 	((struct rthash *)(element->rt_forw))->rt_back=(struct rt_entry *)element;
370 }
371 
372 /*
373  * remove an element from a queue
374  */
remque(element)375 remque(element)
376 	register struct rthash *element;
377 {
378 	((struct rthash *)(element->rt_forw))->rt_back = element->rt_back;
379 	((struct rthash *)(element->rt_back))->rt_forw = element->rt_forw;
380 	element->rt_back = (struct rt_entry *)0;
381 }
382