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