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