tables.c revision 21673
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 *	$FreeBSD: head/usr.sbin/IPXrouted/tables.c 21673 1997-01-14 07:20:47Z jkh $
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
62struct  rthash nethash[ROUTEHASHSIZ];
63struct  rthash hosthash[ROUTEHASHSIZ];
64
65/*
66 * Lookup dst in the tables for an exact match.
67 */
68struct rt_entry *
69rtlookup(dst)
70	struct sockaddr *dst;
71{
72	register struct rt_entry *rt;
73	register struct rthash *rh;
74	register u_int hash;
75	struct afhash h;
76	int doinghost = 1;
77
78	if (dst->sa_family >= AF_MAX)
79		return (0);
80	(*afswitch[dst->sa_family].af_hash)(dst, &h);
81	hash = h.afh_hosthash;
82	rh = &hosthash[hash & ROUTEHASHMASK];
83again:
84	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
85		if (rt->rt_hash != hash)
86			continue;
87		if (equal(&rt->rt_dst, dst))
88			return (rt);
89	}
90	if (doinghost) {
91		doinghost = 0;
92		hash = h.afh_nethash;
93		rh = &nethash[hash & ROUTEHASHMASK];
94		goto again;
95	}
96	return (0);
97}
98
99/*
100 * Find a route to dst as the kernel would.
101 */
102struct rt_entry *
103rtfind(dst)
104	struct sockaddr *dst;
105{
106	register struct rt_entry *rt;
107	register struct rthash *rh;
108	register u_int hash;
109	struct afhash h;
110	int af = dst->sa_family;
111	int doinghost = 1, (*match)() = 0;
112
113	if (af >= AF_MAX)
114		return (0);
115	(*afswitch[af].af_hash)(dst, &h);
116	hash = h.afh_hosthash;
117	rh = &hosthash[hash & ROUTEHASHMASK];
118
119again:
120	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
121		if (rt->rt_hash != hash)
122			continue;
123		if (doinghost) {
124			if (equal(&rt->rt_dst, dst))
125				return (rt);
126		} else {
127			if (rt->rt_dst.sa_family == af &&
128			    (match != 0) &&
129			    (*match)(&rt->rt_dst, dst))
130				return (rt);
131		}
132	}
133	if (doinghost) {
134		doinghost = 0;
135		hash = h.afh_nethash;
136		rh = &nethash[hash & ROUTEHASHMASK];
137		match = afswitch[af].af_netmatch;
138		goto again;
139	}
140	return (0);
141}
142
143void
144rtadd(dst, gate, metric, ticks, state)
145	struct sockaddr *dst, *gate;
146	short metric, ticks;
147	int state;
148{
149	struct afhash h;
150	register struct rt_entry *rt;
151	struct rthash *rh;
152	int af = dst->sa_family, flags;
153	u_int hash;
154
155	FIXLEN(dst);
156	FIXLEN(gate);
157	if (af >= AF_MAX)
158		return;
159	(*afswitch[af].af_hash)(dst, &h);
160	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
161	if (flags & RTF_HOST) {
162		hash = h.afh_hosthash;
163		rh = &hosthash[hash & ROUTEHASHMASK];
164	} else {
165		hash = h.afh_nethash;
166		rh = &nethash[hash & ROUTEHASHMASK];
167	}
168	rt = (struct rt_entry *)malloc(sizeof (*rt));
169	if (rt == 0)
170		return;
171	rt->rt_hash = hash;
172	rt->rt_dst = *dst;
173	rt->rt_router = *gate;
174	rt->rt_metric = metric;
175	rt->rt_ticks = ticks;
176	rt->rt_timer = 0;
177	rt->rt_flags = RTF_UP | flags;
178	rt->rt_state = state | RTS_CHANGED;
179	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
180	rt->rt_clone = NULL;
181	if (metric)
182		rt->rt_flags |= RTF_GATEWAY;
183	insque(rt, rh);
184	TRACE_ACTION(ADD, rt);
185	/*
186	 * If the ioctl fails because the gateway is unreachable
187	 * from this host, discard the entry.  This should only
188	 * occur because of an incorrect entry in /etc/gateways.
189	 */
190	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
191		if (errno != EEXIST)
192			perror("SIOCADDRT");
193		if (errno == ENETUNREACH) {
194			TRACE_ACTION(DELETE, rt);
195			remque(rt);
196			free((char *)rt);
197		}
198	}
199}
200
201void
202rtadd_clone(ort, dst, gate, metric, ticks, state)
203	struct rt_entry *ort;
204	struct sockaddr *dst, *gate;
205	short metric, ticks;
206	int state;
207{
208	struct afhash h;
209	register struct rt_entry *rt;
210	struct rthash *rh;
211	int af = dst->sa_family, flags;
212	u_int hash;
213
214	FIXLEN(dst);
215	FIXLEN(gate);
216	if (af >= AF_MAX)
217		return;
218	(*afswitch[af].af_hash)(dst, &h);
219	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
220	if (flags & RTF_HOST) {
221		hash = h.afh_hosthash;
222		rh = &hosthash[hash & ROUTEHASHMASK];
223	} else {
224		hash = h.afh_nethash;
225		rh = &nethash[hash & ROUTEHASHMASK];
226	}
227	rt = (struct rt_entry *)malloc(sizeof (*rt));
228	if (rt == 0)
229		return;
230	rt->rt_hash = hash;
231	rt->rt_dst = *dst;
232	rt->rt_router = *gate;
233	rt->rt_metric = metric;
234	rt->rt_ticks = ticks;
235	rt->rt_timer = 0;
236	rt->rt_flags = RTF_UP | flags;
237	rt->rt_state = state | RTS_CHANGED;
238	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
239	rt->rt_clone = NULL;
240	rt->rt_forw = NULL;
241	rt->rt_back = NULL;
242	if (metric)
243		rt->rt_flags |= RTF_GATEWAY;
244
245	while(ort->rt_clone != NULL)
246		ort = ort->rt_clone;
247	ort->rt_clone = rt;
248	TRACE_ACTION(ADD_CLONE, rt);
249}
250
251void
252rtchange(rt, gate, metric, ticks)
253	struct rt_entry *rt;
254	struct sockaddr *gate;
255	short metric, ticks;
256{
257	int doioctl = 0, metricchanged = 0;
258	struct rtuentry oldroute;
259
260	FIXLEN(gate);
261	/*
262 	 * Handling of clones.
263 	 * When the route changed and it had clones, handle it special.
264 	 * 1. If the new route is cheaper than the clone(s), free the clones.
265	 * 2. If the new route is the same cost, it may be one of the clones,
266	 *    search for it and free it.
267 	 * 3. If the new route is more expensive than the clone(s), use the
268 	 *    values of the clone(s).
269 	 */
270	if (rt->rt_clone) {
271		if ((ticks < rt->rt_clone->rt_ticks) ||
272		    ((ticks == rt->rt_clone->rt_ticks) &&
273		     (metric < rt->rt_clone->rt_metric))) {
274			/*
275			 * Free all clones.
276			 */
277			struct rt_entry *trt, *nrt;
278
279			trt = rt->rt_clone;
280			rt->rt_clone = NULL;
281			while(trt) {
282				nrt = trt->rt_clone;
283				free((char *)trt);
284				trt = nrt;
285			}
286		} else if ((ticks == rt->rt_clone->rt_ticks) &&
287		     (metric == rt->rt_clone->rt_metric)) {
288			struct rt_entry *prt, *trt;
289
290			prt = rt;
291			trt = rt->rt_clone;
292
293			while(trt) {
294				if (equal(&trt->rt_router, gate)) {
295					prt->rt_clone = trt->rt_clone;
296					free(trt);
297					trt = prt->rt_clone;
298				} else {
299					prt = trt;
300					trt = trt->rt_clone;
301				}
302			}
303		} else {
304			/*
305			 * Use the values of the first clone.
306			 * Delete the corresponding clone.
307			 */
308			struct rt_entry *trt;
309
310			trt = rt->rt_clone;
311			rt->rt_clone = rt->rt_clone->rt_clone;
312			metric = trt->rt_metric;
313			ticks = trt->rt_ticks;
314			*gate = trt->rt_router;
315			free((char *)trt);
316		}
317	}
318
319	if (!equal(&rt->rt_router, gate))
320		doioctl++;
321	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
322		metricchanged++;
323	if (doioctl || metricchanged) {
324		TRACE_ACTION(CHANGE FROM, rt);
325		if (doioctl) {
326			oldroute = rt->rt_rt;
327			rt->rt_router = *gate;
328		}
329		rt->rt_metric = metric;
330		rt->rt_ticks = ticks;
331		if ((rt->rt_state & RTS_INTERFACE) && metric) {
332			rt->rt_state &= ~RTS_INTERFACE;
333			if(rt->rt_ifp)
334				syslog(LOG_ERR,
335				"changing route from interface %s (timed out)",
336				rt->rt_ifp->int_name);
337			else
338				syslog(LOG_ERR,
339				"changing route from interface ??? (timed out)");
340		}
341		if (metric)
342			rt->rt_flags |= RTF_GATEWAY;
343		else
344			rt->rt_flags &= ~RTF_GATEWAY;
345		rt->rt_state |= RTS_CHANGED;
346		TRACE_ACTION(CHANGE TO, rt);
347	}
348	if (doioctl && install) {
349#ifndef RTM_ADD
350		if (rtioctl(ADD, &rt->rt_rt) < 0)
351		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
352		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_dst)->sns_addr),
353		   xns_ntoa(&((struct sockaddr_ns *)&rt->rt_router)->sns_addr));
354		if (delete && rtioctl(DELETE, &oldroute) < 0)
355			perror("rtioctl DELETE");
356#else
357		if (delete == 0) {
358			if (rtioctl(ADD, &rt->rt_rt) >= 0)
359				return;
360		} else {
361			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
362				return;
363		}
364	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
365		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
366		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
367#endif
368	}
369}
370
371void
372rtdelete(rt)
373	struct rt_entry *rt;
374{
375
376	struct sockaddr *sa = &(rt->rt_router);
377	FIXLEN(sa);
378	sa = &(rt->rt_dst);
379	FIXLEN(sa);
380	if (rt->rt_clone) {
381		/*
382		 * If there is a clone we just do a rt_change to it.
383		 */
384		struct rt_entry *trt = rt->rt_clone;
385		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
386		return;
387	}
388	if (rt->rt_state & RTS_INTERFACE) {
389		if (rt->rt_ifp)
390			syslog(LOG_ERR,
391				"deleting route to interface %s (timed out)",
392				rt->rt_ifp->int_name);
393		else
394			syslog(LOG_ERR,
395				"deleting route to interface ??? (timed out)");
396	}
397	TRACE_ACTION(DELETE, rt);
398	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
399		perror("rtioctl DELETE");
400	remque(rt);
401	free((char *)rt);
402}
403
404void
405rtinit(void)
406{
407	register struct rthash *rh;
408
409	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
410		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
411	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
412		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
413}
414int seqno;
415
416int
417rtioctl(action, ort)
418	int action;
419	struct rtuentry *ort;
420{
421#ifndef RTM_ADD
422	if (install == 0)
423		return (errno = 0);
424
425	ort->rtu_rtflags = ort->rtu_flags;
426
427	switch (action) {
428
429	case ADD:
430		return (ioctl(s, SIOCADDRT, (char *)ort));
431
432	case DELETE:
433		return (ioctl(s, SIOCDELRT, (char *)ort));
434
435	default:
436		return (-1);
437	}
438#else /* RTM_ADD */
439	struct {
440		struct rt_msghdr w_rtm;
441		struct sockaddr w_dst;
442		struct sockaddr w_gate;
443		struct sockaddr_ipx w_netmask;
444	} w;
445#define rtm w.w_rtm
446
447	bzero((char *)&w, sizeof(w));
448	rtm.rtm_msglen = sizeof(w);
449	rtm.rtm_version = RTM_VERSION;
450	rtm.rtm_type = (action == ADD ? RTM_ADD :
451				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
452	rtm.rtm_flags = ort->rtu_flags;
453	rtm.rtm_seq = ++seqno;
454	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
455	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
456	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
457	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
458	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
459	if (rtm.rtm_flags & RTF_HOST) {
460		rtm.rtm_msglen -= sizeof(w.w_netmask);
461	} else {
462		rtm.rtm_addrs |= RTA_NETMASK;
463		w.w_netmask = ipx_netmask;
464		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
465	}
466	errno = 0;
467	return write(r, (char *)&w, rtm.rtm_msglen);
468#endif  /* RTM_ADD */
469}
470
471