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$
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 *
67189369Sedrtlookup(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 *
92189369Sedrtfind(struct sockaddr *dst)
9311820Sjulian{
9411820Sjulian	register struct rt_entry *rt;
9511820Sjulian	register struct rthash *rh;
9611820Sjulian	register u_int hash;
9711820Sjulian	struct afhash h;
9811820Sjulian	int af = dst->sa_family;
9927244Sjhay	int (*match)() = 0;
10011820Sjulian
10111820Sjulian	if (af >= AF_MAX)
10211820Sjulian		return (0);
10311820Sjulian	(*afswitch[af].af_hash)(dst, &h);
10411820Sjulian
10527244Sjhay	hash = h.afh_nethash;
10627244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
10727244Sjhay	match = afswitch[af].af_netmatch;
10811820Sjulian	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
10911820Sjulian		if (rt->rt_hash != hash)
11011820Sjulian			continue;
11127244Sjhay		if (rt->rt_dst.sa_family == af &&
11227244Sjhay		    (*match)(&rt->rt_dst, dst))
11327244Sjhay			return (rt);
11411820Sjulian	}
11511820Sjulian	return (0);
11611820Sjulian}
11711820Sjulian
11811820Sjulianvoid
119189369Sedrtadd(struct sockaddr *dst, struct sockaddr *gate, short metric,
120189369Sed    short ticks, int state)
12111820Sjulian{
12211820Sjulian	struct afhash h;
12311820Sjulian	register struct rt_entry *rt;
12411820Sjulian	struct rthash *rh;
12511820Sjulian	int af = dst->sa_family, flags;
12611820Sjulian	u_int hash;
12711820Sjulian
12811820Sjulian	FIXLEN(dst);
12911820Sjulian	FIXLEN(gate);
13011820Sjulian	if (af >= AF_MAX)
13111820Sjulian		return;
13211820Sjulian	(*afswitch[af].af_hash)(dst, &h);
13311820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
13427244Sjhay	hash = h.afh_nethash;
13527244Sjhay	rh = &nethash[hash & ROUTEHASHMASK];
13611820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
13711820Sjulian	if (rt == 0)
13811820Sjulian		return;
13911820Sjulian	rt->rt_hash = hash;
14011820Sjulian	rt->rt_dst = *dst;
14111820Sjulian	rt->rt_router = *gate;
14211820Sjulian	rt->rt_metric = metric;
14311820Sjulian	rt->rt_ticks = ticks;
14411820Sjulian	rt->rt_timer = 0;
14511820Sjulian	rt->rt_flags = RTF_UP | flags;
14611820Sjulian	rt->rt_state = state | RTS_CHANGED;
14711820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
14811820Sjulian	rt->rt_clone = NULL;
14911820Sjulian	if (metric)
15011820Sjulian		rt->rt_flags |= RTF_GATEWAY;
15111820Sjulian	insque(rt, rh);
15227244Sjhay	TRACE_ACTION("ADD", rt);
15311820Sjulian	/*
15411820Sjulian	 * If the ioctl fails because the gateway is unreachable
15511820Sjulian	 * from this host, discard the entry.  This should only
15611820Sjulian	 * occur because of an incorrect entry in /etc/gateways.
15711820Sjulian	 */
15811820Sjulian	if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
15911820Sjulian		if (errno != EEXIST)
16011820Sjulian			perror("SIOCADDRT");
16111820Sjulian		if (errno == ENETUNREACH) {
16227244Sjhay			TRACE_ACTION("DELETE", rt);
16311820Sjulian			remque(rt);
16411820Sjulian			free((char *)rt);
16511820Sjulian		}
16611820Sjulian	}
16711820Sjulian}
16811820Sjulian
16911820Sjulianvoid
170189369Sedrtadd_clone(struct rt_entry *ort, struct sockaddr *dst,
171189369Sed    struct sockaddr *gate, short metric, short ticks, int state)
17211820Sjulian{
17311820Sjulian	struct afhash h;
17411820Sjulian	register struct rt_entry *rt;
17511820Sjulian	int af = dst->sa_family, flags;
17611820Sjulian	u_int hash;
17711820Sjulian
17811820Sjulian	FIXLEN(dst);
17911820Sjulian	FIXLEN(gate);
18011820Sjulian	if (af >= AF_MAX)
18111820Sjulian		return;
18211820Sjulian	(*afswitch[af].af_hash)(dst, &h);
18311820Sjulian	flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
18427244Sjhay	hash = h.afh_nethash;
18511820Sjulian	rt = (struct rt_entry *)malloc(sizeof (*rt));
18611820Sjulian	if (rt == 0)
18711820Sjulian		return;
18811820Sjulian	rt->rt_hash = hash;
18911820Sjulian	rt->rt_dst = *dst;
19011820Sjulian	rt->rt_router = *gate;
19111820Sjulian	rt->rt_metric = metric;
19211820Sjulian	rt->rt_ticks = ticks;
19311820Sjulian	rt->rt_timer = 0;
19411820Sjulian	rt->rt_flags = RTF_UP | flags;
19511820Sjulian	rt->rt_state = state | RTS_CHANGED;
19611820Sjulian	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
19711820Sjulian	rt->rt_clone = NULL;
19811820Sjulian	rt->rt_forw = NULL;
19911820Sjulian	rt->rt_back = NULL;
20011820Sjulian	if (metric)
20111820Sjulian		rt->rt_flags |= RTF_GATEWAY;
20211820Sjulian
20311820Sjulian	while(ort->rt_clone != NULL)
20411820Sjulian		ort = ort->rt_clone;
20511820Sjulian	ort->rt_clone = rt;
20627244Sjhay	TRACE_ACTION("ADD_CLONE", rt);
20711820Sjulian}
20811820Sjulian
20911820Sjulianvoid
210189369Sedrtchange(struct rt_entry *rt, struct sockaddr *gate, short metric,
211189369Sed    short ticks)
21211820Sjulian{
21311820Sjulian	int doioctl = 0, metricchanged = 0;
21411820Sjulian
21511820Sjulian	FIXLEN(gate);
21611820Sjulian	/*
21711820Sjulian 	 * Handling of clones.
21811820Sjulian 	 * When the route changed and it had clones, handle it special.
21911820Sjulian 	 * 1. If the new route is cheaper than the clone(s), free the clones.
22011820Sjulian	 * 2. If the new route is the same cost, it may be one of the clones,
22111820Sjulian	 *    search for it and free it.
22211820Sjulian 	 * 3. If the new route is more expensive than the clone(s), use the
22311820Sjulian 	 *    values of the clone(s).
22411820Sjulian 	 */
22511820Sjulian	if (rt->rt_clone) {
22611820Sjulian		if ((ticks < rt->rt_clone->rt_ticks) ||
22711820Sjulian		    ((ticks == rt->rt_clone->rt_ticks) &&
22811820Sjulian		     (metric < rt->rt_clone->rt_metric))) {
22911820Sjulian			/*
23011820Sjulian			 * Free all clones.
23111820Sjulian			 */
23211820Sjulian			struct rt_entry *trt, *nrt;
23311820Sjulian
23411820Sjulian			trt = rt->rt_clone;
23511820Sjulian			rt->rt_clone = NULL;
23611820Sjulian			while(trt) {
23711820Sjulian				nrt = trt->rt_clone;
23811820Sjulian				free((char *)trt);
23911820Sjulian				trt = nrt;
24011820Sjulian			}
24111820Sjulian		} else if ((ticks == rt->rt_clone->rt_ticks) &&
24211820Sjulian		     (metric == rt->rt_clone->rt_metric)) {
24311820Sjulian			struct rt_entry *prt, *trt;
24411820Sjulian
24511820Sjulian			prt = rt;
24611820Sjulian			trt = rt->rt_clone;
24711820Sjulian
24811820Sjulian			while(trt) {
24911820Sjulian				if (equal(&trt->rt_router, gate)) {
25011820Sjulian					prt->rt_clone = trt->rt_clone;
25111820Sjulian					free(trt);
25211820Sjulian					trt = prt->rt_clone;
25311820Sjulian				} else {
25411820Sjulian					prt = trt;
25511820Sjulian					trt = trt->rt_clone;
25611820Sjulian				}
25711820Sjulian			}
25811820Sjulian		} else {
25911820Sjulian			/*
26011820Sjulian			 * Use the values of the first clone.
26111820Sjulian			 * Delete the corresponding clone.
26211820Sjulian			 */
26311820Sjulian			struct rt_entry *trt;
26411820Sjulian
26511820Sjulian			trt = rt->rt_clone;
26611820Sjulian			rt->rt_clone = rt->rt_clone->rt_clone;
26711820Sjulian			metric = trt->rt_metric;
26811820Sjulian			ticks = trt->rt_ticks;
26911820Sjulian			*gate = trt->rt_router;
27011820Sjulian			free((char *)trt);
27111820Sjulian		}
27211820Sjulian	}
27311820Sjulian
27411820Sjulian	if (!equal(&rt->rt_router, gate))
27511820Sjulian		doioctl++;
27611820Sjulian	if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
27711820Sjulian		metricchanged++;
27811820Sjulian	if (doioctl || metricchanged) {
27927244Sjhay		TRACE_ACTION("CHANGE FROM", rt);
28011820Sjulian		if (doioctl) {
28111820Sjulian			rt->rt_router = *gate;
28211820Sjulian		}
28311820Sjulian		rt->rt_metric = metric;
28411820Sjulian		rt->rt_ticks = ticks;
28511820Sjulian		if ((rt->rt_state & RTS_INTERFACE) && metric) {
28611820Sjulian			rt->rt_state &= ~RTS_INTERFACE;
28711820Sjulian			if(rt->rt_ifp)
28811820Sjulian				syslog(LOG_ERR,
28911820Sjulian				"changing route from interface %s (timed out)",
29011820Sjulian				rt->rt_ifp->int_name);
29111820Sjulian			else
29211820Sjulian				syslog(LOG_ERR,
29311820Sjulian				"changing route from interface ??? (timed out)");
29411820Sjulian		}
29511820Sjulian		if (metric)
29611820Sjulian			rt->rt_flags |= RTF_GATEWAY;
29711820Sjulian		else
29811820Sjulian			rt->rt_flags &= ~RTF_GATEWAY;
29927244Sjhay		rt->rt_ifp = if_ifwithnet(&rt->rt_router);
30011820Sjulian		rt->rt_state |= RTS_CHANGED;
30127244Sjhay		TRACE_ACTION("CHANGE TO", rt);
30211820Sjulian	}
30311820Sjulian	if (doioctl && install) {
30411820Sjulian#ifndef RTM_ADD
30511820Sjulian		if (rtioctl(ADD, &rt->rt_rt) < 0)
30611820Sjulian		  syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
30727244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
30827244Sjhay		   ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
30911820Sjulian		if (delete && rtioctl(DELETE, &oldroute) < 0)
31011820Sjulian			perror("rtioctl DELETE");
31111820Sjulian#else
31211820Sjulian		if (delete == 0) {
31311820Sjulian			if (rtioctl(ADD, &rt->rt_rt) >= 0)
31411820Sjulian				return;
31511820Sjulian		} else {
31611820Sjulian			if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
31711820Sjulian				return;
31811820Sjulian		}
31911820Sjulian	        syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
32011820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
32111820Sjulian		   ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
32211820Sjulian#endif
32311820Sjulian	}
32411820Sjulian}
32511820Sjulian
32611820Sjulianvoid
327189369Sedrtdelete(struct rt_entry *rt)
32811820Sjulian{
32911820Sjulian
33011820Sjulian	struct sockaddr *sa = &(rt->rt_router);
33111820Sjulian	FIXLEN(sa);
33211820Sjulian	sa = &(rt->rt_dst);
33311820Sjulian	FIXLEN(sa);
33411820Sjulian	if (rt->rt_clone) {
33511820Sjulian		/*
33611820Sjulian		 * If there is a clone we just do a rt_change to it.
33711820Sjulian		 */
33811820Sjulian		struct rt_entry *trt = rt->rt_clone;
33911820Sjulian		rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
34011820Sjulian		return;
34111820Sjulian	}
34211820Sjulian	if (rt->rt_state & RTS_INTERFACE) {
34311820Sjulian		if (rt->rt_ifp)
34411820Sjulian			syslog(LOG_ERR,
34511820Sjulian				"deleting route to interface %s (timed out)",
34611820Sjulian				rt->rt_ifp->int_name);
34711820Sjulian		else
34811820Sjulian			syslog(LOG_ERR,
34911820Sjulian				"deleting route to interface ??? (timed out)");
35011820Sjulian	}
35127244Sjhay	TRACE_ACTION("DELETE", rt);
35211820Sjulian	if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
35311820Sjulian		perror("rtioctl DELETE");
35411820Sjulian	remque(rt);
35511820Sjulian	free((char *)rt);
35611820Sjulian}
35711820Sjulian
35811820Sjulianvoid
35911820Sjulianrtinit(void)
36011820Sjulian{
36111820Sjulian	register struct rthash *rh;
36211820Sjulian
36311820Sjulian	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
36411820Sjulian		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
36511820Sjulian}
36611820Sjulianint seqno;
36711820Sjulian
36811820Sjulianint
369189369Sedrtioctl(int action, struct rtuentry *ort)
37011820Sjulian{
37111820Sjulian#ifndef RTM_ADD
37211820Sjulian	if (install == 0)
37311820Sjulian		return (errno = 0);
37411820Sjulian
37511820Sjulian	ort->rtu_rtflags = ort->rtu_flags;
37611820Sjulian
37711820Sjulian	switch (action) {
37811820Sjulian
37911820Sjulian	case ADD:
38011820Sjulian		return (ioctl(s, SIOCADDRT, (char *)ort));
38111820Sjulian
38211820Sjulian	case DELETE:
38311820Sjulian		return (ioctl(s, SIOCDELRT, (char *)ort));
38411820Sjulian
38511820Sjulian	default:
38611820Sjulian		return (-1);
38711820Sjulian	}
38811820Sjulian#else /* RTM_ADD */
38911820Sjulian	struct {
39011820Sjulian		struct rt_msghdr w_rtm;
39111820Sjulian		struct sockaddr w_dst;
39211820Sjulian		struct sockaddr w_gate;
39311820Sjulian		struct sockaddr_ipx w_netmask;
39411820Sjulian	} w;
39511820Sjulian#define rtm w.w_rtm
39611820Sjulian
39711820Sjulian	bzero((char *)&w, sizeof(w));
39811820Sjulian	rtm.rtm_msglen = sizeof(w);
39911820Sjulian	rtm.rtm_version = RTM_VERSION;
40011820Sjulian	rtm.rtm_type = (action == ADD ? RTM_ADD :
40111820Sjulian				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
40211820Sjulian	rtm.rtm_flags = ort->rtu_flags;
40311820Sjulian	rtm.rtm_seq = ++seqno;
40411820Sjulian	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
40511820Sjulian	bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
40611820Sjulian	bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
40711820Sjulian	w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
40811820Sjulian	w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
40911820Sjulian	if (rtm.rtm_flags & RTF_HOST) {
41011820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask);
41111820Sjulian	} else {
41211820Sjulian		rtm.rtm_addrs |= RTA_NETMASK;
41311820Sjulian		w.w_netmask = ipx_netmask;
41411820Sjulian		rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
41511820Sjulian	}
41611820Sjulian	errno = 0;
41711820Sjulian	return write(r, (char *)&w, rtm.rtm_msglen);
41811820Sjulian#endif  /* RTM_ADD */
41911820Sjulian}
420