tables.c revision 50479
111820Sjulian/*
211820Sjulian * Copyright (c) 1985, 1993
311820Sjulian *	The Regents of the University of California.  All rights reserved.
411820Sjulian *
511820Sjulian * Copyright (c) 1995 John Hay.  All rights reserved.
611820Sjulian *
711820Sjulian * Redistribution and use in source and binary forms, with or without
811820Sjulian * modification, are permitted provided that the following conditions
911820Sjulian * are met:
1011820Sjulian * 1. Redistributions of source code must retain the above copyright
1111820Sjulian *    notice, this list of conditions and the following disclaimer.
1211820Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1311820Sjulian *    notice, this list of conditions and the following disclaimer in the
1411820Sjulian *    documentation and/or other materials provided with the distribution.
1511820Sjulian * 3. All advertising materials mentioning features or use of this software
1611820Sjulian *    must display the following acknowledgement:
1711820Sjulian *	This product includes software developed by the University of
1811820Sjulian *	California, Berkeley and its contributors.
1911820Sjulian * 4. Neither the name of the University nor the names of its contributors
2011820Sjulian *    may be used to endorse or promote products derived from this software
2111820Sjulian *    without specific prior written permission.
2211820Sjulian *
2311820Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2411820Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2511820Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2611820Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2711820Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2811820Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2911820Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3011820Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3111820Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3211820Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3311820Sjulian * SUCH DAMAGE.
3411820Sjulian *
3550479Speter * $FreeBSD: head/usr.sbin/IPXrouted/tables.c 50479 1999-08-28 01:35:59Z peter $
3611820Sjulian */
3711820Sjulian
3811820Sjulian#ifndef lint
3911820Sjulianstatic char sccsid[] = "@(#)tables.c	8.1 (Berkeley) 6/5/93";
4011820Sjulian#endif /* not lint */
4111820Sjulian
4211820Sjulian/*
4311820Sjulian * Routing Table Management Daemon
4411820Sjulian */
4511820Sjulian#include "defs.h"
4611820Sjulian#include <sys/ioctl.h>
4711820Sjulian#include <errno.h>
4811820Sjulian#include <stdlib.h>
4911820Sjulian#include <unistd.h>
5011820Sjulian
5111820Sjulian#ifndef DEBUG
5211820Sjulian#define	DEBUG	0
5311820Sjulian#endif
5411820Sjulian
5511820Sjulian#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
5611820Sjulian
5711820Sjulianint	install = !DEBUG;		/* if 1 call kernel */
5811820Sjulianint	delete = 1;
5915248Sjhay
6015248Sjhaystruct  rthash nethash[ROUTEHASHSIZ];
6115248Sjhay
6211820Sjulian/*
6311820Sjulian * Lookup dst in the tables for an exact match.
6411820Sjulian */
6511820Sjulianstruct rt_entry *
6611820Sjulianrtlookup(dst)
6711820Sjulian	struct sockaddr *dst;
6811820Sjulian{
6911820Sjulian	register struct rt_entry *rt;
7011820Sjulian	register struct rthash *rh;
7111820Sjulian	register u_int hash;
7211820Sjulian	struct afhash h;
7311820Sjulian
7411820Sjulian	if (dst->sa_family >= AF_MAX)
7511820Sjulian		return (0);
7611820Sjulian	(*afswitch[dst->sa_family].af_hash)(dst, &h);
7727244Sjhay	hash = h.afh_nethash;
7827244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
7911820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
8011820Sjulian		if (rt->rt_hash != hash)
8111820Sjulian			continue;
8211820Sjulian		if (equal(&rt->rt_dst, dst))
8311820Sjulian			return (rt);
8411820Sjulian	}
8511820Sjulian	return (0);
8611820Sjulian}
8711820Sjulian
8811820Sjulian/*
8911820Sjulian * Find a route to dst as the kernel would.
9011820Sjulian */
9111820Sjulianstruct rt_entry *
9211820Sjulianrtfind(dst)
9311820Sjulian	struct sockaddr *dst;
9411820Sjulian{
9511820Sjulian	register struct rt_entry *rt;
9611820Sjulian	register struct rthash *rh;
9711820Sjulian	register u_int hash;
9811820Sjulian	struct afhash h;
9911820Sjulian	int af = dst->sa_family;
10027244Sjhay	int (*match)() = 0;
10111820Sjulian
10211820Sjulian	if (af >= AF_MAX)
10311820Sjulian		return (0);
10411820Sjulian	(*afswitch[af].af_hash)(dst, &h);
10511820Sjulian
10627244Sjhay	hash = h.afh_nethash;
10727244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
10827244Sjhay	match = afswitch[af].af_netmatch;
10911820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
11011820Sjulian		if (rt->rt_hash != hash)
11111820Sjulian			continue;
11227244Sjhay		if (rt->rt_dst.sa_family == af &&
11327244Sjhay		    (*match)(&rt->rt_dst, dst))
11427244Sjhay			return (rt);
11511820Sjulian	}
11611820Sjulian	return (0);
11711820Sjulian}
11811820Sjulian
11911820Sjulianvoid
12011820Sjulianrtadd(dst, gate, metric, ticks, state)
12111820Sjulian	struct sockaddr *dst, *gate;
12211820Sjulian	short metric, ticks;
12311820Sjulian	int state;
12411820Sjulian{
12511820Sjulian	struct afhash h;
12611820Sjulian	register struct rt_entry *rt;
12711820Sjulian	struct rthash *rh;
12811820Sjulian	int af = dst->sa_family, flags;
12911820Sjulian	u_int hash;
13011820Sjulian
13111820Sjulian	FIXLEN(dst);
13211820Sjulian	FIXLEN(gate);
13311820Sjulian	if (af >= AF_MAX)
13411820Sjulian		return;
13511820Sjulian	(*afswitch[af].af_hash)(dst, &h);
13611820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
13727244Sjhay	hash = h.afh_nethash;
13827244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
13911820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
14011820Sjulian	if (rt == 0)
14111820Sjulian		return;
14211820Sjulian	rt->rt_hash = hash;
14311820Sjulian	rt->rt_dst = *dst;
14411820Sjulian	rt->rt_router = *gate;
14511820Sjulian	rt->rt_metric = metric;
14611820Sjulian	rt->rt_ticks = ticks;
14711820Sjulian	rt->rt_timer = 0;
14811820Sjulian	rt->rt_flags = RTF_UP | flags;
14911820Sjulian	rt->rt_state = state | RTS_CHANGED;
15011820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
15111820Sjulian	rt->rt_clone = NULL;
15211820Sjulian	if (metric)
15311820Sjulian		rt->rt_flags |= RTF_GATEWAY;
15411820Sjulian	insque(rt, rh);
15527244Sjhay	TRACE_ACTION("ADD", rt);
15611820Sjulian	/*
15711820Sjulian	 * If the ioctl fails because the gateway is unreachable
15811820Sjulian	 * from this host, discard the entry.  This should only
15911820Sjulian	 * occur because of an incorrect entry in /etc/gateways.
16011820Sjulian	 */
16111820Sjulian	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
16211820Sjulian		if (errno != EEXIST)
16311820Sjulian			perror("SIOCADDRT");
16411820Sjulian		if (errno == ENETUNREACH) {
16527244Sjhay			TRACE_ACTION("DELETE", rt);
16611820Sjulian			remque(rt);
16711820Sjulian			free((char *)rt);
16811820Sjulian		}
16911820Sjulian	}
17011820Sjulian}
17111820Sjulian
17211820Sjulianvoid
17311820Sjulianrtadd_clone(ort, dst, gate, metric, ticks, state)
17411820Sjulian	struct rt_entry *ort;
17511820Sjulian	struct sockaddr *dst, *gate;
17611820Sjulian	short metric, ticks;
17711820Sjulian	int state;
17811820Sjulian{
17911820Sjulian	struct afhash h;
18011820Sjulian	register struct rt_entry *rt;
18111820Sjulian	struct rthash *rh;
18211820Sjulian	int af = dst->sa_family, flags;
18311820Sjulian	u_int hash;
18411820Sjulian
18511820Sjulian	FIXLEN(dst);
18611820Sjulian	FIXLEN(gate);
18711820Sjulian	if (af >= AF_MAX)
18811820Sjulian		return;
18911820Sjulian	(*afswitch[af].af_hash)(dst, &h);
19011820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
19127244Sjhay	hash = h.afh_nethash;
19227244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
19311820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
19411820Sjulian	if (rt == 0)
19511820Sjulian		return;
19611820Sjulian	rt->rt_hash = hash;
19711820Sjulian	rt->rt_dst = *dst;
19811820Sjulian	rt->rt_router = *gate;
19911820Sjulian	rt->rt_metric = metric;
20011820Sjulian	rt->rt_ticks = ticks;
20111820Sjulian	rt->rt_timer = 0;
20211820Sjulian	rt->rt_flags = RTF_UP | flags;
20311820Sjulian	rt->rt_state = state | RTS_CHANGED;
20411820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
20511820Sjulian	rt->rt_clone = NULL;
20611820Sjulian	rt->rt_forw = NULL;
20711820Sjulian	rt->rt_back = NULL;
20811820Sjulian	if (metric)
20911820Sjulian		rt->rt_flags |= RTF_GATEWAY;
21011820Sjulian
21111820Sjulian	while(ort->rt_clone != NULL)
21211820Sjulian		ort = ort->rt_clone;
21311820Sjulian	ort->rt_clone = rt;
21427244Sjhay	TRACE_ACTION("ADD_CLONE", rt);
21511820Sjulian}
21611820Sjulian
21711820Sjulianvoid
21811820Sjulianrtchange(rt, gate, metric, ticks)
21911820Sjulian	struct rt_entry *rt;
22011820Sjulian	struct sockaddr *gate;
22111820Sjulian	short metric, ticks;
22211820Sjulian{
22311820Sjulian	int doioctl = 0, metricchanged = 0;
22411820Sjulian	struct rtuentry oldroute;
22511820Sjulian
22611820Sjulian	FIXLEN(gate);
22711820Sjulian	/*
22811820Sjulian 	 * Handling of clones.
22911820Sjulian 	 * When the route changed and it had clones, handle it special.
23011820Sjulian 	 * 1. If the new route is cheaper than the clone(s), free the clones.
23111820Sjulian	 * 2. If the new route is the same cost, it may be one of the clones,
23211820Sjulian	 *    search for it and free it.
23311820Sjulian 	 * 3. If the new route is more expensive than the clone(s), use the
23411820Sjulian 	 *    values of the clone(s).
23511820Sjulian 	 */
23611820Sjulian	if (rt->rt_clone) {
23711820Sjulian		if ((ticks < rt->rt_clone->rt_ticks) ||
23811820Sjulian		    ((ticks == rt->rt_clone->rt_ticks) &&
23911820Sjulian		     (metric < rt->rt_clone->rt_metric))) {
24011820Sjulian			/*
24111820Sjulian			 * Free all clones.
24211820Sjulian			 */
24311820Sjulian			struct rt_entry *trt, *nrt;
24411820Sjulian
24511820Sjulian			trt = rt->rt_clone;
24611820Sjulian			rt->rt_clone = NULL;
24711820Sjulian			while(trt) {
24811820Sjulian				nrt = trt->rt_clone;
24911820Sjulian				free((char *)trt);
25011820Sjulian				trt = nrt;
25111820Sjulian			}
25211820Sjulian		} else if ((ticks == rt->rt_clone->rt_ticks) &&
25311820Sjulian		     (metric == rt->rt_clone->rt_metric)) {
25411820Sjulian			struct rt_entry *prt, *trt;
25511820Sjulian
25611820Sjulian			prt = rt;
25711820Sjulian			trt = rt->rt_clone;
25811820Sjulian
25911820Sjulian			while(trt) {
26011820Sjulian				if (equal(&trt->rt_router, gate)) {
26111820Sjulian					prt->rt_clone = trt->rt_clone;
26211820Sjulian					free(trt);
26311820Sjulian					trt = prt->rt_clone;
26411820Sjulian				} else {
26511820Sjulian					prt = trt;
26611820Sjulian					trt = trt->rt_clone;
26711820Sjulian				}
26811820Sjulian			}
26911820Sjulian		} else {
27011820Sjulian			/*
27111820Sjulian			 * Use the values of the first clone.
27211820Sjulian			 * Delete the corresponding clone.
27311820Sjulian			 */
27411820Sjulian			struct rt_entry *trt;
27511820Sjulian
27611820Sjulian			trt = rt->rt_clone;
27711820Sjulian			rt->rt_clone = rt->rt_clone->rt_clone;
27811820Sjulian			metric = trt->rt_metric;
27911820Sjulian			ticks = trt->rt_ticks;
28011820Sjulian			*gate = trt->rt_router;
28111820Sjulian			free((char *)trt);
28211820Sjulian		}
28311820Sjulian	}
28411820Sjulian
28511820Sjulian	if (!equal(&rt->rt_router, gate))
28611820Sjulian		doioctl++;
28711820Sjulian	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
28811820Sjulian		metricchanged++;
28911820Sjulian	if (doioctl || metricchanged) {
29027244Sjhay		TRACE_ACTION("CHANGE FROM", rt);
29111820Sjulian		if (doioctl) {
29211820Sjulian			oldroute = rt->rt_rt;
29311820Sjulian			rt->rt_router = *gate;
29411820Sjulian		}
29511820Sjulian		rt->rt_metric = metric;
29611820Sjulian		rt->rt_ticks = ticks;
29711820Sjulian		if ((rt->rt_state & RTS_INTERFACE) && metric) {
29811820Sjulian			rt->rt_state &= ~RTS_INTERFACE;
29911820Sjulian			if(rt->rt_ifp)
30011820Sjulian				syslog(LOG_ERR,
30111820Sjulian				"changing route from interface %s (timed out)",
30211820Sjulian				rt->rt_ifp->int_name);
30311820Sjulian			else
30411820Sjulian				syslog(LOG_ERR,
30511820Sjulian				"changing route from interface ??? (timed out)");
30611820Sjulian		}
30711820Sjulian		if (metric)
30811820Sjulian			rt->rt_flags |= RTF_GATEWAY;
30911820Sjulian		else
31011820Sjulian			rt->rt_flags &= ~RTF_GATEWAY;
31127244Sjhay		rt->rt_ifp = if_ifwithnet(&rt->rt_router);
31211820Sjulian		rt->rt_state |= RTS_CHANGED;
31327244Sjhay		TRACE_ACTION("CHANGE TO", rt);
31411820Sjulian	}
31511820Sjulian	if (doioctl && install) {
31611820Sjulian#ifndef RTM_ADD
31711820Sjulian		if (rtioctl(ADD, &rt->rt_rt) < 0)
31811820Sjulian		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
31927244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
32027244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
32111820Sjulian		if (delete && rtioctl(DELETE, &oldroute) < 0)
32211820Sjulian			perror("rtioctl DELETE");
32311820Sjulian#else
32411820Sjulian		if (delete == 0) {
32511820Sjulian			if (rtioctl(ADD, &rt->rt_rt) >= 0)
32611820Sjulian				return;
32711820Sjulian		} else {
32811820Sjulian			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
32911820Sjulian				return;
33011820Sjulian		}
33111820Sjulian	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
33211820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
33311820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
33411820Sjulian#endif
33511820Sjulian	}
33611820Sjulian}
33711820Sjulian
33811820Sjulianvoid
33911820Sjulianrtdelete(rt)
34011820Sjulian	struct rt_entry *rt;
34111820Sjulian{
34211820Sjulian
34311820Sjulian	struct sockaddr *sa = &(rt->rt_router);
34411820Sjulian	FIXLEN(sa);
34511820Sjulian	sa = &(rt->rt_dst);
34611820Sjulian	FIXLEN(sa);
34711820Sjulian	if (rt->rt_clone) {
34811820Sjulian		/*
34911820Sjulian		 * If there is a clone we just do a rt_change to it.
35011820Sjulian		 */
35111820Sjulian		struct rt_entry *trt = rt->rt_clone;
35211820Sjulian		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
35311820Sjulian		return;
35411820Sjulian	}
35511820Sjulian	if (rt->rt_state & RTS_INTERFACE) {
35611820Sjulian		if (rt->rt_ifp)
35711820Sjulian			syslog(LOG_ERR,
35811820Sjulian				"deleting route to interface %s (timed out)",
35911820Sjulian				rt->rt_ifp->int_name);
36011820Sjulian		else
36111820Sjulian			syslog(LOG_ERR,
36211820Sjulian				"deleting route to interface ??? (timed out)");
36311820Sjulian	}
36427244Sjhay	TRACE_ACTION("DELETE", rt);
36511820Sjulian	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
36611820Sjulian		perror("rtioctl DELETE");
36711820Sjulian	remque(rt);
36811820Sjulian	free((char *)rt);
36911820Sjulian}
37011820Sjulian
37111820Sjulianvoid
37211820Sjulianrtinit(void)
37311820Sjulian{
37411820Sjulian	register struct rthash *rh;
37511820Sjulian
37611820Sjulian	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
37711820Sjulian		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
37811820Sjulian}
37911820Sjulianint seqno;
38011820Sjulian
38111820Sjulianint
38211820Sjulianrtioctl(action, ort)
38311820Sjulian	int action;
38411820Sjulian	struct rtuentry *ort;
38511820Sjulian{
38611820Sjulian#ifndef RTM_ADD
38711820Sjulian	if (install == 0)
38811820Sjulian		return (errno = 0);
38911820Sjulian
39011820Sjulian	ort->rtu_rtflags = ort->rtu_flags;
39111820Sjulian
39211820Sjulian	switch (action) {
39311820Sjulian
39411820Sjulian	case ADD:
39511820Sjulian		return (ioctl(s, SIOCADDRT, (char *)ort));
39611820Sjulian
39711820Sjulian	case DELETE:
39811820Sjulian		return (ioctl(s, SIOCDELRT, (char *)ort));
39911820Sjulian
40011820Sjulian	default:
40111820Sjulian		return (-1);
40211820Sjulian	}
40311820Sjulian#else /* RTM_ADD */
40411820Sjulian	struct {
40511820Sjulian		struct rt_msghdr w_rtm;
40611820Sjulian		struct sockaddr w_dst;
40711820Sjulian		struct sockaddr w_gate;
40811820Sjulian		struct sockaddr_ipx w_netmask;
40911820Sjulian	} w;
41011820Sjulian#define rtm w.w_rtm
41111820Sjulian
41211820Sjulian	bzero((char *)&w, sizeof(w));
41311820Sjulian	rtm.rtm_msglen = sizeof(w);
41411820Sjulian	rtm.rtm_version = RTM_VERSION;
41511820Sjulian	rtm.rtm_type = (action == ADD ? RTM_ADD :
41611820Sjulian				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
41711820Sjulian	rtm.rtm_flags = ort->rtu_flags;
41811820Sjulian	rtm.rtm_seq = ++seqno;
41911820Sjulian	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
42011820Sjulian	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
42111820Sjulian	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
42211820Sjulian	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
42311820Sjulian	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
42411820Sjulian	if (rtm.rtm_flags & RTF_HOST) {
42511820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask);
42611820Sjulian	} else {
42711820Sjulian		rtm.rtm_addrs |= RTA_NETMASK;
42811820Sjulian		w.w_netmask = ipx_netmask;
42911820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
43011820Sjulian	}
43111820Sjulian	errno = 0;
43211820Sjulian	return write(r, (char *)&w, rtm.rtm_msglen);
43311820Sjulian#endif  /* RTM_ADD */
43411820Sjulian}
435