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