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