xref: /csrg-svn/sbin/routed/tables.c (revision 69003)
121999Sdist /*
261540Sbostic  * Copyright (c) 1983, 1988, 1993
361540Sbostic  *	The Regents of the University of California.  All rights reserved.
433489Sbostic  *
542712Sbostic  * %sccs.include.redist.c%
621999Sdist  */
721999Sdist 
89020Ssam #ifndef lint
9*69003Sbostic static char sccsid[] = "@(#)tables.c	8.2 (Berkeley) 04/28/95";
1033489Sbostic #endif /* not lint */
119020Ssam 
129020Ssam /*
139020Ssam  * Routing Table Management Daemon
149020Ssam  */
1510245Ssam #include "defs.h"
169020Ssam #include <sys/ioctl.h>
179020Ssam #include <errno.h>
1834569Skarels #include <sys/syslog.h>
199020Ssam 
209020Ssam #ifndef DEBUG
219020Ssam #define	DEBUG	0
229020Ssam #endif
239020Ssam 
2438684Ssklower #ifdef RTM_ADD
2538684Ssklower #define FIXLEN(s) {if ((s)->sa_len == 0) (s)->sa_len = sizeof *(s);}
2638684Ssklower #else
2738684Ssklower #define FIXLEN(s) { }
2838684Ssklower #endif
2938684Ssklower 
309020Ssam int	install = !DEBUG;		/* if 1 call kernel */
319020Ssam 
329020Ssam /*
339020Ssam  * Lookup dst in the tables for an exact match.
349020Ssam  */
359020Ssam struct rt_entry *
rtlookup(dst)369020Ssam rtlookup(dst)
379020Ssam 	struct sockaddr *dst;
389020Ssam {
399020Ssam 	register struct rt_entry *rt;
409020Ssam 	register struct rthash *rh;
4116128Skarels 	register u_int hash;
429020Ssam 	struct afhash h;
439020Ssam 	int doinghost = 1;
449020Ssam 
4526342Skarels 	if (dst->sa_family >= af_max)
469020Ssam 		return (0);
479020Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
489020Ssam 	hash = h.afh_hosthash;
4916435Skarels 	rh = &hosthash[hash & ROUTEHASHMASK];
509020Ssam again:
519020Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
529020Ssam 		if (rt->rt_hash != hash)
539020Ssam 			continue;
549020Ssam 		if (equal(&rt->rt_dst, dst))
559020Ssam 			return (rt);
569020Ssam 	}
579020Ssam 	if (doinghost) {
589020Ssam 		doinghost = 0;
599020Ssam 		hash = h.afh_nethash;
6016435Skarels 		rh = &nethash[hash & ROUTEHASHMASK];
619020Ssam 		goto again;
629020Ssam 	}
639020Ssam 	return (0);
649020Ssam }
659020Ssam 
6634569Skarels struct sockaddr wildcard;	/* zero valued cookie for wildcard searches */
6734569Skarels 
689020Ssam /*
699020Ssam  * Find a route to dst as the kernel would.
709020Ssam  */
719020Ssam struct rt_entry *
rtfind(dst)729020Ssam rtfind(dst)
739020Ssam 	struct sockaddr *dst;
749020Ssam {
759020Ssam 	register struct rt_entry *rt;
769020Ssam 	register struct rthash *rh;
7716128Skarels 	register u_int hash;
789020Ssam 	struct afhash h;
799020Ssam 	int af = dst->sa_family;
809020Ssam 	int doinghost = 1, (*match)();
819020Ssam 
8226342Skarels 	if (af >= af_max)
839020Ssam 		return (0);
849020Ssam 	(*afswitch[af].af_hash)(dst, &h);
859020Ssam 	hash = h.afh_hosthash;
8616435Skarels 	rh = &hosthash[hash & ROUTEHASHMASK];
879020Ssam 
889020Ssam again:
899020Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
909020Ssam 		if (rt->rt_hash != hash)
919020Ssam 			continue;
929020Ssam 		if (doinghost) {
939020Ssam 			if (equal(&rt->rt_dst, dst))
949020Ssam 				return (rt);
959020Ssam 		} else {
969020Ssam 			if (rt->rt_dst.sa_family == af &&
979020Ssam 			    (*match)(&rt->rt_dst, dst))
989020Ssam 				return (rt);
999020Ssam 		}
1009020Ssam 	}
1019020Ssam 	if (doinghost) {
1029020Ssam 		doinghost = 0;
1039020Ssam 		hash = h.afh_nethash;
10416435Skarels 		rh = &nethash[hash & ROUTEHASHMASK];
1059020Ssam 		match = afswitch[af].af_netmatch;
1069020Ssam 		goto again;
1079020Ssam 	}
10834569Skarels #ifdef notyet
10934569Skarels 	/*
11034569Skarels 	 * Check for wildcard gateway, by convention network 0.
11134569Skarels 	 */
11234569Skarels 	if (dst != &wildcard) {
11334569Skarels 		dst = &wildcard, hash = 0;
11434569Skarels 		goto again;
11534569Skarels 	}
11634569Skarels #endif
1179020Ssam 	return (0);
1189020Ssam }
1199020Ssam 
1209020Ssam rtadd(dst, gate, metric, state)
1219020Ssam 	struct sockaddr *dst, *gate;
1229020Ssam 	int metric, state;
1239020Ssam {
1249020Ssam 	struct afhash h;
1259020Ssam 	register struct rt_entry *rt;
1269020Ssam 	struct rthash *rh;
12731220Skarels 	int af = dst->sa_family, flags;
12816128Skarels 	u_int hash;
1299020Ssam 
13026342Skarels 	if (af >= af_max)
1319020Ssam 		return;
1329020Ssam 	(*afswitch[af].af_hash)(dst, &h);
13327232Skarels 	flags = (*afswitch[af].af_rtflags)(dst);
13427232Skarels 	/*
13527232Skarels 	 * Subnet flag isn't visible to kernel, move to state.	XXX
13627232Skarels 	 */
13738684Ssklower 	FIXLEN(dst);
13838684Ssklower 	FIXLEN(gate);
13927232Skarels 	if (flags & RTF_SUBNET) {
14027232Skarels 		state |= RTS_SUBNET;
14127232Skarels 		flags &= ~RTF_SUBNET;
14227232Skarels 	}
1439020Ssam 	if (flags & RTF_HOST) {
1449020Ssam 		hash = h.afh_hosthash;
14516435Skarels 		rh = &hosthash[hash & ROUTEHASHMASK];
1469020Ssam 	} else {
1479020Ssam 		hash = h.afh_nethash;
14816435Skarels 		rh = &nethash[hash & ROUTEHASHMASK];
1499020Ssam 	}
1509020Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
1519020Ssam 	if (rt == 0)
1529020Ssam 		return;
1539020Ssam 	rt->rt_hash = hash;
1549020Ssam 	rt->rt_dst = *dst;
1559020Ssam 	rt->rt_router = *gate;
1569020Ssam 	rt->rt_timer = 0;
1579020Ssam 	rt->rt_flags = RTF_UP | flags;
1589020Ssam 	rt->rt_state = state | RTS_CHANGED;
15938684Ssklower 	rt->rt_ifp = if_ifwithdstaddr(&rt->rt_dst);
16027232Skarels 	if (rt->rt_ifp == 0)
16127232Skarels 		rt->rt_ifp = if_ifwithnet(&rt->rt_router);
16230745Skarels 	if ((state & RTS_INTERFACE) == 0)
1639020Ssam 		rt->rt_flags |= RTF_GATEWAY;
16431220Skarels 	rt->rt_metric = metric;
1659020Ssam 	insque(rt, rh);
16634569Skarels 	TRACE_ACTION("ADD", rt);
16715098Ssam 	/*
16815098Ssam 	 * If the ioctl fails because the gateway is unreachable
16915098Ssam 	 * from this host, discard the entry.  This should only
17015098Ssam 	 * occur because of an incorrect entry in /etc/gateways.
17115098Ssam 	 */
17255907Smckusick 	if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
17352583Ssklower 	    rtioctl(ADD, &rt->rt_rt) < 0) {
17431220Skarels 		if (errno != EEXIST && gate->sa_family < af_max)
17530961Skarels 			syslog(LOG_ERR,
17630961Skarels 			"adding route to net/host %s through gateway %s: %m\n",
17730961Skarels 			   (*afswitch[dst->sa_family].af_format)(dst),
17830961Skarels 			   (*afswitch[gate->sa_family].af_format)(gate));
17952583Ssklower 		perror("ADD ROUTE");
18015098Ssam 		if (errno == ENETUNREACH) {
18134569Skarels 			TRACE_ACTION("DELETE", rt);
18215098Ssam 			remque(rt);
18315098Ssam 			free((char *)rt);
18415098Ssam 		}
18515098Ssam 	}
1869020Ssam }
1879020Ssam 
1889020Ssam rtchange(rt, gate, metric)
1899020Ssam 	struct rt_entry *rt;
1909020Ssam 	struct sockaddr *gate;
1919020Ssam 	short metric;
1929020Ssam {
19336831Skarels 	int add = 0, delete = 0, newgateway = 0;
19455907Smckusick 	struct rtuentry oldroute;
1959020Ssam 
19638684Ssklower 	FIXLEN(gate);
19738684Ssklower 	FIXLEN(&(rt->rt_router));
19838684Ssklower 	FIXLEN(&(rt->rt_dst));
19936831Skarels 	if (!equal(&rt->rt_router, gate)) {
20036831Skarels 		newgateway++;
20136831Skarels 		TRACE_ACTION("CHANGE FROM ", rt);
20236831Skarels 	} else if (metric != rt->rt_metric)
20336831Skarels 		TRACE_NEWMETRIC(rt, metric);
20431220Skarels 	if ((rt->rt_state & RTS_INTERNAL) == 0) {
20531220Skarels 		/*
20631220Skarels 		 * If changing to different router, we need to add
20731220Skarels 		 * new route and delete old one if in the kernel.
20831220Skarels 		 * If the router is the same, we need to delete
20931220Skarels 		 * the route if has become unreachable, or re-add
21031220Skarels 		 * it if it had been unreachable.
21131220Skarels 		 */
21236831Skarels 		if (newgateway) {
21331220Skarels 			add++;
21431220Skarels 			if (rt->rt_metric != HOPCNT_INFINITY)
21531220Skarels 				delete++;
21631220Skarels 		} else if (metric == HOPCNT_INFINITY)
21729931Skarels 			delete++;
21831220Skarels 		else if (rt->rt_metric == HOPCNT_INFINITY)
21931220Skarels 			add++;
22029931Skarels 	}
22134569Skarels 	if (delete)
22234569Skarels 		oldroute = rt->rt_rt;
22331220Skarels 	if ((rt->rt_state & RTS_INTERFACE) && delete) {
22431220Skarels 		rt->rt_state &= ~RTS_INTERFACE;
22534569Skarels 		rt->rt_flags |= RTF_GATEWAY;
22634569Skarels 		if (metric > rt->rt_metric && delete)
22734569Skarels 			syslog(LOG_ERR, "%s route to interface %s (timed out)",
22834569Skarels 			    add? "changing" : "deleting",
22955907Smckusick 			    rt->rt_ifp ? rt->rt_ifp->int_name : "?");
23031220Skarels 	}
23131220Skarels 	if (add) {
23231220Skarels 		rt->rt_router = *gate;
23331220Skarels 		rt->rt_ifp = if_ifwithdstaddr(&rt->rt_router);
23431220Skarels 		if (rt->rt_ifp == 0)
23531220Skarels 			rt->rt_ifp = if_ifwithnet(&rt->rt_router);
23631220Skarels 	}
23731220Skarels 	rt->rt_metric = metric;
23831220Skarels 	rt->rt_state |= RTS_CHANGED;
23936831Skarels 	if (newgateway)
24034569Skarels 		TRACE_ACTION("CHANGE TO   ", rt);
24138684Ssklower #ifndef RTM_ADD
24252583Ssklower 	if (add && rtioctl(ADD, &rt->rt_rt) < 0)
24352583Ssklower 		perror("ADD ROUTE");
24452583Ssklower 	if (delete && rtioctl(DELETE, &oldroute) < 0)
24552583Ssklower 		perror("DELETE ROUTE");
24638684Ssklower #else
24752583Ssklower 	if (delete && !add) {
24852583Ssklower 		if (rtioctl(DELETE, &oldroute) < 0)
24952583Ssklower 			perror("DELETE ROUTE");
25052583Ssklower 	} else if (!delete && add) {
25152583Ssklower 		if (rtioctl(ADD, &rt->rt_rt) < 0)
25252583Ssklower 			perror("ADD ROUTE");
25352583Ssklower 	} else if (delete && add) {
25452583Ssklower 		if (rtioctl(CHANGE, &rt->rt_rt) < 0)
25552583Ssklower 			perror("CHANGE ROUTE");
25638684Ssklower 	}
25738684Ssklower #endif
2589020Ssam }
2599020Ssam 
2609020Ssam rtdelete(rt)
2619020Ssam 	struct rt_entry *rt;
2629020Ssam {
26315098Ssam 
26434569Skarels 	TRACE_ACTION("DELETE", rt);
26538684Ssklower 	FIXLEN(&(rt->rt_router));
26638684Ssklower 	FIXLEN(&(rt->rt_dst));
26734569Skarels 	if (rt->rt_metric < HOPCNT_INFINITY) {
26834569Skarels 	    if ((rt->rt_state & (RTS_INTERFACE|RTS_INTERNAL)) == RTS_INTERFACE)
26934569Skarels 		syslog(LOG_ERR,
27034569Skarels 		    "deleting route to interface %s? (timed out?)",
27134569Skarels 		    rt->rt_ifp->int_name);
27255907Smckusick 	    if ((rt->rt_state & (RTS_INTERNAL | RTS_EXTERNAL)) == 0 &&
27352583Ssklower 					    rtioctl(DELETE, &rt->rt_rt) < 0)
27452583Ssklower 		    perror("rtdelete");
27534569Skarels 	}
2769020Ssam 	remque(rt);
2779020Ssam 	free((char *)rt);
2789020Ssam }
2799020Ssam 
rtdeleteall(sig)28036831Skarels rtdeleteall(sig)
28136831Skarels 	int sig;
28231220Skarels {
28331220Skarels 	register struct rthash *rh;
28431220Skarels 	register struct rt_entry *rt;
28531220Skarels 	struct rthash *base = hosthash;
28631220Skarels 	int doinghost = 1;
28731220Skarels 
28831220Skarels again:
28931220Skarels 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
29031220Skarels 		rt = rh->rt_forw;
29131220Skarels 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
29234569Skarels 			if (rt->rt_state & RTS_INTERFACE ||
29334569Skarels 			    rt->rt_metric >= HOPCNT_INFINITY)
29431220Skarels 				continue;
29534569Skarels 			TRACE_ACTION("DELETE", rt);
29631220Skarels 			if ((rt->rt_state & (RTS_INTERNAL|RTS_EXTERNAL)) == 0 &&
29752583Ssklower 			    rtioctl(DELETE, &rt->rt_rt) < 0)
29852583Ssklower 				perror("rtdeleteall");
29931220Skarels 		}
30031220Skarels 	}
30131220Skarels 	if (doinghost) {
30231220Skarels 		doinghost = 0;
30331220Skarels 		base = nethash;
30431220Skarels 		goto again;
30531220Skarels 	}
30636831Skarels 	exit(sig);
30731220Skarels }
30831220Skarels 
30916328Skarels /*
31016328Skarels  * If we have an interface to the wide, wide world,
31116328Skarels  * add an entry for an Internet default route (wildcard) to the internal
31216328Skarels  * tables and advertise it.  This route is not added to the kernel routes,
31316328Skarels  * but this entry prevents us from listening to other people's defaults
31416328Skarels  * and installing them in the kernel here.
31516328Skarels  */
rtdefault()31616328Skarels rtdefault()
31716328Skarels {
31816328Skarels 	extern struct sockaddr inet_default;
31916328Skarels 
32034661Skarels 	rtadd(&inet_default, &inet_default, 1,
32127232Skarels 		RTS_CHANGED | RTS_PASSIVE | RTS_INTERNAL);
32216328Skarels }
32316328Skarels 
rtinit()3249020Ssam rtinit()
3259020Ssam {
3269020Ssam 	register struct rthash *rh;
3279020Ssam 
3289020Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
3299020Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
3309020Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
3319020Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
3329020Ssam }
33352583Ssklower 
rtioctl(action,ort)33452583Ssklower rtioctl(action, ort)
33552583Ssklower 	int action;
33655907Smckusick 	struct rtuentry *ort;
33752583Ssklower {
33852583Ssklower #ifndef RTM_ADD
33955907Smckusick 	if (install == 0)
34055907Smckusick 		return (errno = 0);
34155907Smckusick 	ort->rtu_rtflags = ort->rtu_flags;
34252583Ssklower 	switch (action) {
34352583Ssklower 
34452583Ssklower 	case ADD:
34552583Ssklower 		return (ioctl(s, SIOCADDRT, (char *)ort));
34652583Ssklower 
34752583Ssklower 	case DELETE:
34852583Ssklower 		return (ioctl(s, SIOCDELRT, (char *)ort));
34952583Ssklower 
35052583Ssklower 	default:
35152583Ssklower 		return (-1);
35252583Ssklower 	}
35352583Ssklower #else /* RTM_ADD */
35452583Ssklower 	struct {
35552583Ssklower 		struct rt_msghdr w_rtm;
35652583Ssklower 		struct sockaddr_in w_dst;
35752583Ssklower 		struct sockaddr w_gate;
35852583Ssklower 		struct sockaddr_in w_netmask;
35952583Ssklower 	} w;
36052583Ssklower #define rtm w.w_rtm
36152583Ssklower 
362*69003Sbostic 	memset(&w, 0, sizeof(w));
36352583Ssklower 	rtm.rtm_msglen = sizeof(w);
36452583Ssklower 	rtm.rtm_version = RTM_VERSION;
36552583Ssklower 	rtm.rtm_type = (action == ADD ? RTM_ADD :
36652583Ssklower 				(action == DELETE ? RTM_DELETE : RTM_CHANGE));
36752583Ssklower #undef rt_dst
36855907Smckusick 	rtm.rtm_flags = ort->rtu_flags;
36952583Ssklower 	rtm.rtm_seq = ++seqno;
37052583Ssklower 	rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
371*69003Sbostic 	memmove(&w.w_dst, &ort->rtu_dst, sizeof(w.w_dst));
372*69003Sbostic 	memmove(&w.w_gate, &ort->rtu_router, sizeof(w.w_gate));
37352583Ssklower 	w.w_dst.sin_family = AF_INET;
37452583Ssklower 	w.w_dst.sin_len = sizeof(w.w_dst);
37552583Ssklower 	w.w_gate.sa_family = AF_INET;
37652583Ssklower 	w.w_gate.sa_len = sizeof(w.w_gate);
37752583Ssklower 	if (rtm.rtm_flags & RTF_HOST) {
37852583Ssklower 		rtm.rtm_msglen -= sizeof(w.w_netmask);
37952583Ssklower 	} else {
38052620Ssklower 		register char *cp;
38152620Ssklower 		int len;
38252620Ssklower 
38352583Ssklower 		rtm.rtm_addrs |= RTA_NETMASK;
38452583Ssklower 		w.w_netmask.sin_addr.s_addr =
38552583Ssklower 			inet_maskof(w.w_dst.sin_addr.s_addr);
38652620Ssklower 		for (cp = (char *)(1 + &w.w_netmask.sin_addr);
38752620Ssklower 				    --cp > (char *) &w.w_netmask; )
38852620Ssklower 			if (*cp)
38952620Ssklower 				break;
39052620Ssklower 		len = cp - (char *)&w.w_netmask;
39152620Ssklower 		if (len) {
39252620Ssklower 			len++;
39352620Ssklower 			w.w_netmask.sin_len = len;
39452620Ssklower 			len = 1 + ((len - 1) | (sizeof(long) - 1));
39552620Ssklower 		} else
39652620Ssklower 			len = sizeof(long);
39752620Ssklower 		rtm.rtm_msglen -= (sizeof(w.w_netmask) - len);
39852583Ssklower 	}
39952583Ssklower 	errno = 0;
40055907Smckusick 	return (install ? write(r, (char *)&w, rtm.rtm_msglen) : (errno = 0));
40152583Ssklower #endif  /* RTM_ADD */
40252583Ssklower }
403