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