tables.c revision 11820
1/*
2 * Copyright (c) 1985, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Copyright (c) 1995 John Hay.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the University of
18 *	California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *	$Id: tables.c,v 1.6 1995/10/11 18:57:31 jhay Exp $
36 */
37
38#ifndef lint
39static char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 6/5/93";
40#endif /* not lint */
41
42/*
43 * Routing Table Management Daemon
44 */
45#include "defs.h"
46#include <sys/ioctl.h>
47#include <errno.h>
48#include <stdlib.h>
49#include <unistd.h>
50/* XXX I thought that this should work! #include <sys/systm.h> */
51#include <machine/cpufunc.h>
52
53#ifndef DEBUG
54#define	DEBUG	0
55#endif
56
57#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
58
59int	install = !DEBUG;		/* if 1 call kernel */
60int	delete = 1;
61/*
62 * Lookup dst in the tables for an exact match.
63 */
64struct rt_entry *
65rtlookup(dst)
66	struct sockaddr *dst;
67{
68	register struct rt_entry *rt;
69	register struct rthash *rh;
70	register u_int hash;
71	struct afhash h;
72	int doinghost = 1;
73
74	if (dst->sa_family >= AF_MAX)
75		return (0);
76	(*afswitch[dst->sa_family].af_hash)(dst, &h);
77	hash = h.afh_hosthash;
78	rh = &hosthash[hash & ROUTEHASHMASK];
79again:
80	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
81		if (rt->rt_hash != hash)
82			continue;
83		if (equal(&rt->rt_dst, dst))
84			return (rt);
85	}
86	if (doinghost) {
87		doinghost = 0;
88		hash = h.afh_nethash;
89		rh = &nethash[hash & ROUTEHASHMASK];
90		goto again;
91	}
92	return (0);
93}
94
95/*
96 * Find a route to dst as the kernel would.
97 */
98struct rt_entry *
99rtfind(dst)
100	struct sockaddr *dst;
101{
102	register struct rt_entry *rt;
103	register struct rthash *rh;
104	register u_int hash;
105	struct afhash h;
106	int af = dst->sa_family;
107	int doinghost = 1, (*match)() = 0;
108
109	if (af >= AF_MAX)
110		return (0);
111	(*afswitch[af].af_hash)(dst, &h);
112	hash = h.afh_hosthash;
113	rh = &hosthash[hash & ROUTEHASHMASK];
114
115again:
116	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
117		if (rt->rt_hash != hash)
118			continue;
119		if (doinghost) {
120			if (equal(&rt->rt_dst, dst))
121				return (rt);
122		} else {
123			if (rt->rt_dst.sa_family == af &&
124			    (match != 0) &&
125			    (*match)(&rt->rt_dst, dst))
126				return (rt);
127		}
128	}
129	if (doinghost) {
130		doinghost = 0;
131		hash = h.afh_nethash;
132		rh = &nethash[hash & ROUTEHASHMASK];
133		match = afswitch[af].af_netmatch;
134		goto again;
135	}
136	return (0);
137}
138
139void
140rtadd(dst, gate, metric, ticks, state)
141	struct sockaddr *dst, *gate;
142	short metric, ticks;
143	int state;
144{
145	struct afhash h;
146	register struct rt_entry *rt;
147	struct rthash *rh;
148	int af = dst->sa_family, flags;
149	u_int hash;
150
151	FIXLEN(dst);
152	FIXLEN(gate);
153	if (af >= AF_MAX)
154		return;
155	(*afswitch[af].af_hash)(dst, &h);
156	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
157	if (flags & RTF_HOST) {
158		hash = h.afh_hosthash;
159		rh = &hosthash[hash & ROUTEHASHMASK];
160	} else {
161		hash = h.afh_nethash;
162		rh = &nethash[hash & ROUTEHASHMASK];
163	}
164	rt = (struct rt_entry *)malloc(sizeof (*rt));
165	if (rt == 0)
166		return;
167	rt->rt_hash = hash;
168	rt->rt_dst = *dst;
169	rt->rt_router = *gate;
170	rt->rt_metric = metric;
171	rt->rt_ticks = ticks;
172	rt->rt_timer = 0;
173	rt->rt_flags = RTF_UP | flags;
174	rt->rt_state = state | RTS_CHANGED;
175	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
176	rt->rt_clone = NULL;
177	if (metric)
178		rt->rt_flags |= RTF_GATEWAY;
179	insque(rt, rh);
180	TRACE_ACTION(ADD, rt);
181	/*
182	 * If the ioctl fails because the gateway is unreachable
183	 * from this host, discard the entry.  This should only
184	 * occur because of an incorrect entry in /etc/gateways.
185	 */
186	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
187		if (errno != EEXIST)
188			perror("SIOCADDRT");
189		if (errno == ENETUNREACH) {
190			TRACE_ACTION(DELETE, rt);
191			remque(rt);
192			free((char *)rt);
193		}
194	}
195}
196
197void
198rtadd_clone(ort, dst, gate, metric, ticks, state)
199	struct rt_entry *ort;
200	struct sockaddr *dst, *gate;
201	short metric, ticks;
202	int state;
203{
204	struct afhash h;
205	register struct rt_entry *rt;
206	struct rthash *rh;
207	int af = dst->sa_family, flags;
208	u_int hash;
209
210	FIXLEN(dst);
211	FIXLEN(gate);
212	if (af >= AF_MAX)
213		return;
214	(*afswitch[af].af_hash)(dst, &h);
215	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
216	if (flags & RTF_HOST) {
217		hash = h.afh_hosthash;
218		rh = &hosthash[hash & ROUTEHASHMASK];
219	} else {
220		hash = h.afh_nethash;
221		rh = &nethash[hash & ROUTEHASHMASK];
222	}
223	rt = (struct rt_entry *)malloc(sizeof (*rt));
224	if (rt == 0)
225		return;
226	rt->rt_hash = hash;
227	rt->rt_dst = *dst;
228	rt->rt_router = *gate;
229	rt->rt_metric = metric;
230	rt->rt_ticks = ticks;
231	rt->rt_timer = 0;
232	rt->rt_flags = RTF_UP | flags;
233	rt->rt_state = state | RTS_CHANGED;
234	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
235	rt->rt_clone = NULL;
236	rt->rt_forw = NULL;
237	rt->rt_back = NULL;
238	if (metric)
239		rt->rt_flags |= RTF_GATEWAY;
240
241	while(ort->rt_clone != NULL)
242		ort = ort->rt_clone;
243	ort->rt_clone = rt;
244	TRACE_ACTION(ADD_CLONE, rt);
245}
246
247void
248rtchange(rt, gate, metric, ticks)
249	struct rt_entry *rt;
250	struct sockaddr *gate;
251	short metric, ticks;
252{
253	int doioctl = 0, metricchanged = 0;
254	struct rtuentry oldroute;
255
256	FIXLEN(gate);
257	/*
258 	 * Handling of clones.
259 	 * When the route changed and it had clones, handle it special.
260 	 * 1. If the new route is cheaper than the clone(s), free the clones.
261	 * 2. If the new route is the same cost, it may be one of the clones,
262	 *    search for it and free it.
263 	 * 3. If the new route is more expensive than the clone(s), use the
264 	 *    values of the clone(s).
265 	 */
266	if (rt->rt_clone) {
267		if ((ticks < rt->rt_clone->rt_ticks) ||
268		    ((ticks == rt->rt_clone->rt_ticks) &&
269		     (metric < rt->rt_clone->rt_metric))) {
270			/*
271			 * Free all clones.
272			 */
273			struct rt_entry *trt, *nrt;
274
275			trt = rt->rt_clone;
276			rt->rt_clone = NULL;
277			while(trt) {
278				nrt = trt->rt_clone;
279				free((char *)trt);
280				trt = nrt;
281			}
282		} else if ((ticks == rt->rt_clone->rt_ticks) &&
283		     (metric == rt->rt_clone->rt_metric)) {
284			struct rt_entry *prt, *trt;
285
286			prt = rt;
287			trt = rt->rt_clone;
288
289			while(trt) {
290				if (equal(&trt->rt_router, gate)) {
291					prt->rt_clone = trt->rt_clone;
292					free(trt);
293					trt = prt->rt_clone;
294				} else {
295					prt = trt;
296					trt = trt->rt_clone;
297				}
298			}
299		} else {
300			/*
301			 * Use the values of the first clone.
302			 * Delete the corresponding clone.
303			 */
304			struct rt_entry *trt;
305
306			trt = rt->rt_clone;
307			rt->rt_clone = rt->rt_clone->rt_clone;
308			metric = trt->rt_metric;
309			ticks = trt->rt_ticks;
310			*gate = trt->rt_router;
311			free((char *)trt);
312		}
313	}
314
315	if (!equal(&rt->rt_router, gate))
316		doioctl++;
317	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
318		metricchanged++;
319	if (doioctl || metricchanged) {
320		TRACE_ACTION(CHANGE FROM, rt);
321		if (doioctl) {
322			oldroute = rt->rt_rt;
323			rt->rt_router = *gate;
324		}
325		rt->rt_metric = metric;
326		rt->rt_ticks = ticks;
327		if ((rt->rt_state & RTS_INTERFACE) && metric) {
328			rt->rt_state &= ~RTS_INTERFACE;
329			if(rt->rt_ifp)
330				syslog(LOG_ERR,
331				"changing route from interface %s (timed out)",
332				rt->rt_ifp->int_name);
333			else
334				syslog(LOG_ERR,
335				"changing route from interface ??? (timed out)");
336		}
337		if (metric)
338			rt->rt_flags |= RTF_GATEWAY;
339		else
340			rt->rt_flags &= ~RTF_GATEWAY;
341		rt->rt_state |= RTS_CHANGED;
342		TRACE_ACTION(CHANGE TO, rt);
343	}
344	if (doioctl && install) {
345#ifndef RTM_ADD
346		if (rtioctl(ADD, &rt->rt_rt) < 0)
347		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
348		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
349		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
350		if (delete && rtioctl(DELETE, &oldroute) < 0)
351			perror("rtioctl DELETE");
352#else
353		if (delete == 0) {
354			if (rtioctl(ADD, &rt->rt_rt) >= 0)
355				return;
356		} else {
357			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
358				return;
359		}
360	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
361		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
362		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
363#endif
364	}
365}
366
367void
368rtdelete(rt)
369	struct rt_entry *rt;
370{
371
372	struct sockaddr *sa = &(rt->rt_router);
373	FIXLEN(sa);
374	sa = &(rt->rt_dst);
375	FIXLEN(sa);
376	if (rt->rt_clone) {
377		/*
378		 * If there is a clone we just do a rt_change to it.
379		 */
380		struct rt_entry *trt = rt->rt_clone;
381		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
382		return;
383	}
384	if (rt->rt_state & RTS_INTERFACE) {
385		if (rt->rt_ifp)
386			syslog(LOG_ERR,
387				"deleting route to interface %s (timed out)",
388				rt->rt_ifp->int_name);
389		else
390			syslog(LOG_ERR,
391				"deleting route to interface ??? (timed out)");
392	}
393	TRACE_ACTION(DELETE, rt);
394	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
395		perror("rtioctl DELETE");
396	remque(rt);
397	free((char *)rt);
398}
399
400void
401rtinit(void)
402{
403	register struct rthash *rh;
404
405	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
406		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
407	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
408		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
409}
410int seqno;
411
412int
413rtioctl(action, ort)
414	int action;
415	struct rtuentry *ort;
416{
417#ifndef RTM_ADD
418	if (install == 0)
419		return (errno = 0);
420
421	ort->rtu_rtflags = ort->rtu_flags;
422
423	switch (action) {
424
425	case ADD:
426		return (ioctl(s, SIOCADDRT, (char *)ort));
427
428	case DELETE:
429		return (ioctl(s, SIOCDELRT, (char *)ort));
430
431	default:
432		return (-1);
433	}
434#else /* RTM_ADD */
435	struct {
436		struct rt_msghdr w_rtm;
437		struct sockaddr w_dst;
438		struct sockaddr w_gate;
439		struct sockaddr_ipx w_netmask;
440	} w;
441#define rtm w.w_rtm
442
443	bzero((char *)&w, sizeof(w));
444	rtm.rtm_msglen = sizeof(w);
445	rtm.rtm_version = RTM_VERSION;
446	rtm.rtm_type = (action == ADD ? RTM_ADD :
447				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
448	rtm.rtm_flags = ort->rtu_flags;
449	rtm.rtm_seq = ++seqno;
450	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
451	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
452	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
453	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
454	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
455	if (rtm.rtm_flags & RTF_HOST) {
456		rtm.rtm_msglen -= sizeof(w.w_netmask);
457	} else {
458		rtm.rtm_addrs |= RTA_NETMASK;
459		w.w_netmask = ipx_netmask;
460		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
461	}
462	errno = 0;
463	return write(r, (char *)&w, rtm.rtm_msglen);
464#endif  /* RTM_ADD */
465}
466
467