tables.c revision 122760
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 122760 2003-11-15 17:10:56Z trhodes $
3611820Sjulian */
3711820Sjulian
3811820Sjulian#ifndef lint
39122760Strhodesstatic const 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>
48122760Strhodes#include <search.h>
4911820Sjulian#include <stdlib.h>
5011820Sjulian#include <unistd.h>
5111820Sjulian
5211820Sjulian#ifndef DEBUG
5311820Sjulian#define	DEBUG	0
5411820Sjulian#endif
5511820Sjulian
5611820Sjulian#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
5711820Sjulian
5811820Sjulianint	install = !DEBUG;		/* if 1 call kernel */
5911820Sjulianint	delete = 1;
6015248Sjhay
6115248Sjhaystruct  rthash nethash[ROUTEHASHSIZ];
6215248Sjhay
6311820Sjulian/*
6411820Sjulian * Lookup dst in the tables for an exact match.
6511820Sjulian */
6611820Sjulianstruct rt_entry *
6711820Sjulianrtlookup(dst)
6811820Sjulian	struct sockaddr *dst;
6911820Sjulian{
7011820Sjulian	register struct rt_entry *rt;
7111820Sjulian	register struct rthash *rh;
7211820Sjulian	register u_int hash;
7311820Sjulian	struct afhash h;
7411820Sjulian
7511820Sjulian	if (dst->sa_family >= AF_MAX)
7611820Sjulian		return (0);
7711820Sjulian	(*afswitch[dst->sa_family].af_hash)(dst, &h);
7827244Sjhay	hash = h.afh_nethash;
7927244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
8011820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
8111820Sjulian		if (rt->rt_hash != hash)
8211820Sjulian			continue;
8311820Sjulian		if (equal(&rt->rt_dst, dst))
8411820Sjulian			return (rt);
8511820Sjulian	}
8611820Sjulian	return (0);
8711820Sjulian}
8811820Sjulian
8911820Sjulian/*
9011820Sjulian * Find a route to dst as the kernel would.
9111820Sjulian */
9211820Sjulianstruct rt_entry *
9311820Sjulianrtfind(dst)
9411820Sjulian	struct sockaddr *dst;
9511820Sjulian{
9611820Sjulian	register struct rt_entry *rt;
9711820Sjulian	register struct rthash *rh;
9811820Sjulian	register u_int hash;
9911820Sjulian	struct afhash h;
10011820Sjulian	int af = dst->sa_family;
10127244Sjhay	int (*match)() = 0;
10211820Sjulian
10311820Sjulian	if (af >= AF_MAX)
10411820Sjulian		return (0);
10511820Sjulian	(*afswitch[af].af_hash)(dst, &h);
10611820Sjulian
10727244Sjhay	hash = h.afh_nethash;
10827244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
10927244Sjhay	match = afswitch[af].af_netmatch;
11011820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
11111820Sjulian		if (rt->rt_hash != hash)
11211820Sjulian			continue;
11327244Sjhay		if (rt->rt_dst.sa_family == af &&
11427244Sjhay		    (*match)(&rt->rt_dst, dst))
11527244Sjhay			return (rt);
11611820Sjulian	}
11711820Sjulian	return (0);
11811820Sjulian}
11911820Sjulian
12011820Sjulianvoid
12111820Sjulianrtadd(dst, gate, metric, ticks, state)
12211820Sjulian	struct sockaddr *dst, *gate;
12311820Sjulian	short metric, ticks;
12411820Sjulian	int state;
12511820Sjulian{
12611820Sjulian	struct afhash h;
12711820Sjulian	register struct rt_entry *rt;
12811820Sjulian	struct rthash *rh;
12911820Sjulian	int af = dst->sa_family, flags;
13011820Sjulian	u_int hash;
13111820Sjulian
13211820Sjulian	FIXLEN(dst);
13311820Sjulian	FIXLEN(gate);
13411820Sjulian	if (af >= AF_MAX)
13511820Sjulian		return;
13611820Sjulian	(*afswitch[af].af_hash)(dst, &h);
13711820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
13827244Sjhay	hash = h.afh_nethash;
13927244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
14011820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
14111820Sjulian	if (rt == 0)
14211820Sjulian		return;
14311820Sjulian	rt->rt_hash = hash;
14411820Sjulian	rt->rt_dst = *dst;
14511820Sjulian	rt->rt_router = *gate;
14611820Sjulian	rt->rt_metric = metric;
14711820Sjulian	rt->rt_ticks = ticks;
14811820Sjulian	rt->rt_timer = 0;
14911820Sjulian	rt->rt_flags = RTF_UP | flags;
15011820Sjulian	rt->rt_state = state | RTS_CHANGED;
15111820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
15211820Sjulian	rt->rt_clone = NULL;
15311820Sjulian	if (metric)
15411820Sjulian		rt->rt_flags |= RTF_GATEWAY;
15511820Sjulian	insque(rt, rh);
15627244Sjhay	TRACE_ACTION("ADD", rt);
15711820Sjulian	/*
15811820Sjulian	 * If the ioctl fails because the gateway is unreachable
15911820Sjulian	 * from this host, discard the entry.  This should only
16011820Sjulian	 * occur because of an incorrect entry in /etc/gateways.
16111820Sjulian	 */
16211820Sjulian	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
16311820Sjulian		if (errno != EEXIST)
16411820Sjulian			perror("SIOCADDRT");
16511820Sjulian		if (errno == ENETUNREACH) {
16627244Sjhay			TRACE_ACTION("DELETE", rt);
16711820Sjulian			remque(rt);
16811820Sjulian			free((char *)rt);
16911820Sjulian		}
17011820Sjulian	}
17111820Sjulian}
17211820Sjulian
17311820Sjulianvoid
17411820Sjulianrtadd_clone(ort, dst, gate, metric, ticks, state)
17511820Sjulian	struct rt_entry *ort;
17611820Sjulian	struct sockaddr *dst, *gate;
17711820Sjulian	short metric, ticks;
17811820Sjulian	int state;
17911820Sjulian{
18011820Sjulian	struct afhash h;
18111820Sjulian	register struct rt_entry *rt;
18211820Sjulian	struct rthash *rh;
18311820Sjulian	int af = dst->sa_family, flags;
18411820Sjulian	u_int hash;
18511820Sjulian
18611820Sjulian	FIXLEN(dst);
18711820Sjulian	FIXLEN(gate);
18811820Sjulian	if (af >= AF_MAX)
18911820Sjulian		return;
19011820Sjulian	(*afswitch[af].af_hash)(dst, &h);
19111820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
19227244Sjhay	hash = h.afh_nethash;
19327244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
19411820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
19511820Sjulian	if (rt == 0)
19611820Sjulian		return;
19711820Sjulian	rt->rt_hash = hash;
19811820Sjulian	rt->rt_dst = *dst;
19911820Sjulian	rt->rt_router = *gate;
20011820Sjulian	rt->rt_metric = metric;
20111820Sjulian	rt->rt_ticks = ticks;
20211820Sjulian	rt->rt_timer = 0;
20311820Sjulian	rt->rt_flags = RTF_UP | flags;
20411820Sjulian	rt->rt_state = state | RTS_CHANGED;
20511820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
20611820Sjulian	rt->rt_clone = NULL;
20711820Sjulian	rt->rt_forw = NULL;
20811820Sjulian	rt->rt_back = NULL;
20911820Sjulian	if (metric)
21011820Sjulian		rt->rt_flags |= RTF_GATEWAY;
21111820Sjulian
21211820Sjulian	while(ort->rt_clone != NULL)
21311820Sjulian		ort = ort->rt_clone;
21411820Sjulian	ort->rt_clone = rt;
21527244Sjhay	TRACE_ACTION("ADD_CLONE", rt);
21611820Sjulian}
21711820Sjulian
21811820Sjulianvoid
21911820Sjulianrtchange(rt, gate, metric, ticks)
22011820Sjulian	struct rt_entry *rt;
22111820Sjulian	struct sockaddr *gate;
22211820Sjulian	short metric, ticks;
22311820Sjulian{
22411820Sjulian	int doioctl = 0, metricchanged = 0;
22511820Sjulian	struct rtuentry oldroute;
22611820Sjulian
22711820Sjulian	FIXLEN(gate);
22811820Sjulian	/*
22911820Sjulian 	 * Handling of clones.
23011820Sjulian 	 * When the route changed and it had clones, handle it special.
23111820Sjulian 	 * 1. If the new route is cheaper than the clone(s), free the clones.
23211820Sjulian	 * 2. If the new route is the same cost, it may be one of the clones,
23311820Sjulian	 *    search for it and free it.
23411820Sjulian 	 * 3. If the new route is more expensive than the clone(s), use the
23511820Sjulian 	 *    values of the clone(s).
23611820Sjulian 	 */
23711820Sjulian	if (rt->rt_clone) {
23811820Sjulian		if ((ticks < rt->rt_clone->rt_ticks) ||
23911820Sjulian		    ((ticks == rt->rt_clone->rt_ticks) &&
24011820Sjulian		     (metric < rt->rt_clone->rt_metric))) {
24111820Sjulian			/*
24211820Sjulian			 * Free all clones.
24311820Sjulian			 */
24411820Sjulian			struct rt_entry *trt, *nrt;
24511820Sjulian
24611820Sjulian			trt = rt->rt_clone;
24711820Sjulian			rt->rt_clone = NULL;
24811820Sjulian			while(trt) {
24911820Sjulian				nrt = trt->rt_clone;
25011820Sjulian				free((char *)trt);
25111820Sjulian				trt = nrt;
25211820Sjulian			}
25311820Sjulian		} else if ((ticks == rt->rt_clone->rt_ticks) &&
25411820Sjulian		     (metric == rt->rt_clone->rt_metric)) {
25511820Sjulian			struct rt_entry *prt, *trt;
25611820Sjulian
25711820Sjulian			prt = rt;
25811820Sjulian			trt = rt->rt_clone;
25911820Sjulian
26011820Sjulian			while(trt) {
26111820Sjulian				if (equal(&trt->rt_router, gate)) {
26211820Sjulian					prt->rt_clone = trt->rt_clone;
26311820Sjulian					free(trt);
26411820Sjulian					trt = prt->rt_clone;
26511820Sjulian				} else {
26611820Sjulian					prt = trt;
26711820Sjulian					trt = trt->rt_clone;
26811820Sjulian				}
26911820Sjulian			}
27011820Sjulian		} else {
27111820Sjulian			/*
27211820Sjulian			 * Use the values of the first clone.
27311820Sjulian			 * Delete the corresponding clone.
27411820Sjulian			 */
27511820Sjulian			struct rt_entry *trt;
27611820Sjulian
27711820Sjulian			trt = rt->rt_clone;
27811820Sjulian			rt->rt_clone = rt->rt_clone->rt_clone;
27911820Sjulian			metric = trt->rt_metric;
28011820Sjulian			ticks = trt->rt_ticks;
28111820Sjulian			*gate = trt->rt_router;
28211820Sjulian			free((char *)trt);
28311820Sjulian		}
28411820Sjulian	}
28511820Sjulian
28611820Sjulian	if (!equal(&rt->rt_router, gate))
28711820Sjulian		doioctl++;
28811820Sjulian	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
28911820Sjulian		metricchanged++;
29011820Sjulian	if (doioctl || metricchanged) {
29127244Sjhay		TRACE_ACTION("CHANGE FROM", rt);
29211820Sjulian		if (doioctl) {
29311820Sjulian			oldroute = rt->rt_rt;
29411820Sjulian			rt->rt_router = *gate;
29511820Sjulian		}
29611820Sjulian		rt->rt_metric = metric;
29711820Sjulian		rt->rt_ticks = ticks;
29811820Sjulian		if ((rt->rt_state & RTS_INTERFACE) && metric) {
29911820Sjulian			rt->rt_state &= ~RTS_INTERFACE;
30011820Sjulian			if(rt->rt_ifp)
30111820Sjulian				syslog(LOG_ERR,
30211820Sjulian				"changing route from interface %s (timed out)",
30311820Sjulian				rt->rt_ifp->int_name);
30411820Sjulian			else
30511820Sjulian				syslog(LOG_ERR,
30611820Sjulian				"changing route from interface ??? (timed out)");
30711820Sjulian		}
30811820Sjulian		if (metric)
30911820Sjulian			rt->rt_flags |= RTF_GATEWAY;
31011820Sjulian		else
31111820Sjulian			rt->rt_flags &= ~RTF_GATEWAY;
31227244Sjhay		rt->rt_ifp = if_ifwithnet(&rt->rt_router);
31311820Sjulian		rt->rt_state |= RTS_CHANGED;
31427244Sjhay		TRACE_ACTION("CHANGE TO", rt);
31511820Sjulian	}
31611820Sjulian	if (doioctl && install) {
31711820Sjulian#ifndef RTM_ADD
31811820Sjulian		if (rtioctl(ADD, &rt->rt_rt) < 0)
31911820Sjulian		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
32027244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
32127244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
32211820Sjulian		if (delete && rtioctl(DELETE, &oldroute) < 0)
32311820Sjulian			perror("rtioctl DELETE");
32411820Sjulian#else
32511820Sjulian		if (delete == 0) {
32611820Sjulian			if (rtioctl(ADD, &rt->rt_rt) >= 0)
32711820Sjulian				return;
32811820Sjulian		} else {
32911820Sjulian			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
33011820Sjulian				return;
33111820Sjulian		}
33211820Sjulian	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
33311820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
33411820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
33511820Sjulian#endif
33611820Sjulian	}
33711820Sjulian}
33811820Sjulian
33911820Sjulianvoid
34011820Sjulianrtdelete(rt)
34111820Sjulian	struct rt_entry *rt;
34211820Sjulian{
34311820Sjulian
34411820Sjulian	struct sockaddr *sa = &(rt->rt_router);
34511820Sjulian	FIXLEN(sa);
34611820Sjulian	sa = &(rt->rt_dst);
34711820Sjulian	FIXLEN(sa);
34811820Sjulian	if (rt->rt_clone) {
34911820Sjulian		/*
35011820Sjulian		 * If there is a clone we just do a rt_change to it.
35111820Sjulian		 */
35211820Sjulian		struct rt_entry *trt = rt->rt_clone;
35311820Sjulian		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
35411820Sjulian		return;
35511820Sjulian	}
35611820Sjulian	if (rt->rt_state & RTS_INTERFACE) {
35711820Sjulian		if (rt->rt_ifp)
35811820Sjulian			syslog(LOG_ERR,
35911820Sjulian				"deleting route to interface %s (timed out)",
36011820Sjulian				rt->rt_ifp->int_name);
36111820Sjulian		else
36211820Sjulian			syslog(LOG_ERR,
36311820Sjulian				"deleting route to interface ??? (timed out)");
36411820Sjulian	}
36527244Sjhay	TRACE_ACTION("DELETE", rt);
36611820Sjulian	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
36711820Sjulian		perror("rtioctl DELETE");
36811820Sjulian	remque(rt);
36911820Sjulian	free((char *)rt);
37011820Sjulian}
37111820Sjulian
37211820Sjulianvoid
37311820Sjulianrtinit(void)
37411820Sjulian{
37511820Sjulian	register struct rthash *rh;
37611820Sjulian
37711820Sjulian	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
37811820Sjulian		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
37911820Sjulian}
38011820Sjulianint seqno;
38111820Sjulian
38211820Sjulianint
38311820Sjulianrtioctl(action, ort)
38411820Sjulian	int action;
38511820Sjulian	struct rtuentry *ort;
38611820Sjulian{
38711820Sjulian#ifndef RTM_ADD
38811820Sjulian	if (install == 0)
38911820Sjulian		return (errno = 0);
39011820Sjulian
39111820Sjulian	ort->rtu_rtflags = ort->rtu_flags;
39211820Sjulian
39311820Sjulian	switch (action) {
39411820Sjulian
39511820Sjulian	case ADD:
39611820Sjulian		return (ioctl(s, SIOCADDRT, (char *)ort));
39711820Sjulian
39811820Sjulian	case DELETE:
39911820Sjulian		return (ioctl(s, SIOCDELRT, (char *)ort));
40011820Sjulian
40111820Sjulian	default:
40211820Sjulian		return (-1);
40311820Sjulian	}
40411820Sjulian#else /* RTM_ADD */
40511820Sjulian	struct {
40611820Sjulian		struct rt_msghdr w_rtm;
40711820Sjulian		struct sockaddr w_dst;
40811820Sjulian		struct sockaddr w_gate;
40911820Sjulian		struct sockaddr_ipx w_netmask;
41011820Sjulian	} w;
41111820Sjulian#define rtm w.w_rtm
41211820Sjulian
41311820Sjulian	bzero((char *)&w, sizeof(w));
41411820Sjulian	rtm.rtm_msglen = sizeof(w);
41511820Sjulian	rtm.rtm_version = RTM_VERSION;
41611820Sjulian	rtm.rtm_type = (action == ADD ? RTM_ADD :
41711820Sjulian				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
41811820Sjulian	rtm.rtm_flags = ort->rtu_flags;
41911820Sjulian	rtm.rtm_seq = ++seqno;
42011820Sjulian	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
42111820Sjulian	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
42211820Sjulian	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
42311820Sjulian	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
42411820Sjulian	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
42511820Sjulian	if (rtm.rtm_flags & RTF_HOST) {
42611820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask);
42711820Sjulian	} else {
42811820Sjulian		rtm.rtm_addrs |= RTA_NETMASK;
42911820Sjulian		w.w_netmask = ipx_netmask;
43011820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
43111820Sjulian	}
43211820Sjulian	errno = 0;
43311820Sjulian	return write(r, (char *)&w, rtm.rtm_msglen);
43411820Sjulian#endif  /* RTM_ADD */
43511820Sjulian}
436