xref: /csrg-svn/sbin/routed/routed.c (revision 7250)
16920Ssam #ifndef lint
2*7250Ssam static char sccsid[] = "@(#)routed.c	4.18 82/06/20";
36920Ssam #endif
46920Ssam 
56934Ssam /*
66934Ssam  * Routing Table Management Daemon
76934Ssam  */
86934Ssam #include <sys/types.h>
96920Ssam #include <sys/ioctl.h>
106920Ssam #include <sys/socket.h>
116920Ssam #include <net/in.h>
126920Ssam #include <net/if.h>
136920Ssam #include <errno.h>
146920Ssam #include <stdio.h>
156920Ssam #include <nlist.h>
166920Ssam #include <signal.h>
177029Ssam #include <time.h>
187139Ssam #define	RIPCMDS
196920Ssam #include "rip.h"
206920Ssam #include "router.h"
216920Ssam 
226920Ssam #define	LOOPBACKNET	0177
236920Ssam /* casts to keep lint happy */
246920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
256920Ssam #define	remque(q)	_remque((caddr_t)q)
266920Ssam #define equal(a1, a2) \
276920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
286929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
296920Ssam 
306920Ssam struct nlist nl[] = {
316920Ssam #define	N_IFNET		0
326920Ssam 	{ "_ifnet" },
336920Ssam 	0,
346920Ssam };
356920Ssam 
367139Ssam struct	sockaddr_in routingaddr = { AF_INET, IPPORT_ROUTESERVER };
377139Ssam struct	sockaddr_in noroutingaddr = { AF_INET, IPPORT_ROUTESERVER+1 };
386920Ssam 
396920Ssam int	s;
407130Swnj int	snoroute;		/* socket with no routing */
416929Ssam int	kmem = -1;
427133Swnj int	supplier = -1;		/* process should supply updates */
436962Ssam int	install = 1;		/* if 1 call kernel */
447145Ssam int	lookforinterfaces = 1;
457145Ssam int	performnlist = 1;
46*7250Ssam int	externalinterfaces = 0;	/* # of remote and local interfaces */
477133Swnj int	timeval = -TIMER_RATE;
486920Ssam int	timer();
496920Ssam int	cleanup();
507133Swnj 
517133Swnj #define tprintf if (trace) printf
526920Ssam int	trace = 0;
537029Ssam FILE	*ftrace;
546920Ssam 
557133Swnj char	packet[MAXPACKETSIZE+1];
567133Swnj struct	rip *msg = (struct rip *)packet;
576920Ssam 
586934Ssam struct in_addr if_makeaddr();
597145Ssam struct interface *if_ifwithaddr(), *if_ifwithnet();
607145Ssam extern char *malloc(), *sys_errlist[];
616922Ssam extern int errno, exit();
627133Swnj char	**argv0;
636920Ssam 
647139Ssam int	sendmsg(), supply();
657133Swnj 
666920Ssam main(argc, argv)
676920Ssam 	int argc;
686920Ssam 	char *argv[];
696920Ssam {
706920Ssam 	int cc;
716920Ssam 	struct sockaddr from;
726920Ssam 
737133Swnj 	argv0 = argv;
747029Ssam #ifndef DEBUG
757029Ssam 	if (fork())
767029Ssam 		exit(0);
777029Ssam 	for (cc = 0; cc < 10; cc++)
787029Ssam 		(void) close(cc);
797029Ssam 	(void) open("/", 0);
807029Ssam 	(void) dup2(0, 1);
817029Ssam 	(void) dup2(0, 2);
827029Ssam 	{ int t = open("/dev/tty", 2);
837029Ssam 	  if (t >= 0) {
847029Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
857029Ssam 		(void) close(t);
867029Ssam 	  }
876920Ssam 	}
887029Ssam #endif
896920Ssam 	if (trace) {
907029Ssam 		ftrace = fopen("/etc/routerlog", "w");
917029Ssam 		dup2(fileno(ftrace), 1);
927029Ssam 		dup2(fileno(ftrace), 2);
936920Ssam 	}
94*7250Ssam 
95*7250Ssam 	/*
96*7250Ssam 	 * We use two sockets.  One for which outgoing
97*7250Ssam 	 * packets are routed and for which they're not.
98*7250Ssam 	 * The latter allows us to delete routing table
99*7250Ssam 	 * entries in the kernel for network interfaces
100*7250Ssam 	 * attached to our host which we believe are down
101*7250Ssam 	 * while still polling it to see when/if it comes
102*7250Ssam 	 * back up.  With the new ipc interface we'll be
103*7250Ssam 	 * able to specify ``don't route'' as an option
104*7250Ssam 	 * to send, but until then we utilize a second port.
105*7250Ssam 	 */
1066937Ssam #ifdef vax || pdp11
1077139Ssam 	routingaddr.sin_port = htons(routingaddr.sin_port);
1087139Ssam 	noroutingaddr.sin_port = htons(noroutingaddr.sin_port);
1096920Ssam #endif
1106920Ssam again:
1117139Ssam 	s = socket(SOCK_DGRAM, 0, &routingaddr, 0);
1126920Ssam 	if (s < 0) {
1136920Ssam 		perror("socket");
1146920Ssam 		sleep(30);
1156920Ssam 		goto again;
1166920Ssam 	}
1177130Swnj again2:
1187139Ssam 	snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE);
1197130Swnj 	if (snoroute < 0) {
1207130Swnj 		perror("socket");
1217130Swnj 		sleep(30);
1227130Swnj 		goto again2;
1237130Swnj 	}
1246920Ssam 	argv++, argc--;
1257133Swnj 	while (argc > 0 && **argv == '-') {
1267133Swnj 		if (!strcmp(*argv, "-s") == 0) {
1276929Ssam 			supplier = 1;
1287133Swnj 			argv++, argc--;
1297133Swnj 			continue;
1307133Swnj 		}
1317133Swnj 		if (!strcmp(*argv, "-q") == 0) {
1326920Ssam 			supplier = 0;
1337133Swnj 			argv++, argc--;
1347133Swnj 			continue;
1357133Swnj 		}
1367133Swnj 		goto usage;
1376920Ssam 	}
1387133Swnj 	if (argc > 0) {
1397133Swnj usage:
1407145Ssam 		fprintf(stderr, "usage: routed [ -sq ]\n");
1417133Swnj 		exit(1);
1427133Swnj 	}
143*7250Ssam 	/*
144*7250Ssam 	 * Collect an initial view of the world by
145*7250Ssam 	 * snooping in the kernel and the gateway kludge
146*7250Ssam 	 * file.  Then, send a request packet on all
147*7250Ssam 	 * directly connected networks to find out what
148*7250Ssam 	 * everyone else thinks.
149*7250Ssam 	 */
1507133Swnj 	rtinit();
151*7250Ssam 	gwkludge();
1527133Swnj 	ifinit();
1537133Swnj 	if (supplier < 0)
1547133Swnj 		supplier = 0;
1557133Swnj 	msg->rip_cmd = RIPCMD_REQUEST;
1567133Swnj 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
1577133Swnj 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
1587133Swnj 	toall(sendmsg);
1596920Ssam 	sigset(SIGALRM, timer);
1606929Ssam 	timer();
1616920Ssam 
1626920Ssam 	for (;;) {
1636920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1646920Ssam 		if (cc <= 0) {
1656920Ssam 			if (cc < 0 && errno != EINTR)
1666920Ssam 				perror("receive");
1676920Ssam 			continue;
1686920Ssam 		}
1696920Ssam 		sighold(SIGALRM);
1706920Ssam 		rip_input(&from, cc);
1716920Ssam 		sigrelse(SIGALRM);
1726920Ssam 	}
1736920Ssam }
1746920Ssam 
1757133Swnj rtinit()
1766920Ssam {
1776934Ssam 	register struct rthash *rh;
1786920Ssam 
1797133Swnj 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
1807133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1817133Swnj 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
1827133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1836920Ssam }
1846920Ssam 
1857145Ssam struct	interface *ifnet;
1867133Swnj 
187*7250Ssam /*
188*7250Ssam  * Probe the kernel through /dev/kmem to find the network
189*7250Ssam  * interfaces which have configured themselves.  If the
190*7250Ssam  * interface is present but not yet up (for example an
191*7250Ssam  * ARPANET IMP), set the lookforinterfaces flag so we'll
192*7250Ssam  * come back later and look again.
193*7250Ssam  */
1947133Swnj ifinit()
1956920Ssam {
1967145Ssam 	struct interface *ifp;
1977145Ssam 	struct ifnet ifs, *next;
1986920Ssam 
1997145Ssam 	if (performnlist) {
2007145Ssam 		nlist("/vmunix", nl);
2017145Ssam 		if (nl[N_IFNET].n_value == 0) {
2027145Ssam 			printf("ifnet: not in namelist\n");
2037145Ssam 			goto bad;
2047145Ssam 		}
2057145Ssam 		performnlist = 0;
2066920Ssam 	}
2076920Ssam 	if (kmem < 0) {
2087145Ssam 		kmem = open("/dev/kmem", 0);
2097145Ssam 		if (kmem < 0) {
2107145Ssam 			perror("/dev/kmem");
2117145Ssam 			goto bad;
2127145Ssam 		}
2136920Ssam 	}
2146929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2157133Swnj 	    read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) {
2167133Swnj 		printf("ifnet: error reading kmem\n");
2177133Swnj 		goto bad;
2186929Ssam 	}
2197145Ssam 	lookforinterfaces = 0;
2207133Swnj 	while (next) {
2217133Swnj 		if (lseek(kmem, (long)next, 0) == -1 ||
2227145Ssam 		    read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) {
2237133Swnj 			perror("read");
2247133Swnj 			goto bad;
2256929Ssam 		}
2267145Ssam 		next = ifs.if_next;
2277145Ssam 		if ((ifs.if_flags & IFF_UP) == 0) {
2287145Ssam 			lookforinterfaces = 1;
2297133Swnj 			continue;
2307145Ssam 		}
231*7250Ssam 		/* already known to us? */
2327145Ssam 		if (if_ifwithaddr(&ifs.if_addr))
2337133Swnj 			continue;
234*7250Ssam 		/* argh, this'll have to change sometime */
2357145Ssam 		if (ifs.if_addr.sa_family != AF_INET)
2367145Ssam 			continue;
237*7250Ssam 		/* no one cares about software loopback interfaces */
2387145Ssam 		if (ifs.if_net == LOOPBACKNET)
2397145Ssam 			continue;
2407145Ssam 		ifp = (struct interface *)malloc(sizeof (struct interface));
2417145Ssam 		if (ifp == 0) {
2427145Ssam 			printf("routed: out of memory\n");
2437145Ssam 			break;
2447145Ssam 		}
2457145Ssam 		/*
2467145Ssam 		 * Count the # of directly connected networks
2477145Ssam 		 * and point to point links which aren't looped
2487145Ssam 		 * back to ourself.  This is used below to
249*7250Ssam 		 * decide if we should be a routing ``supplier''.
2507145Ssam 		 */
2517145Ssam 		if ((ifs.if_flags & IFF_POINTOPOINT) == 0 ||
2527145Ssam 		    if_ifwithaddr(&ifs.if_dstaddr) == 0)
2537145Ssam 			externalinterfaces++;
2547145Ssam 		ifp->int_addr = ifs.if_addr;
2557145Ssam 		ifp->int_flags = ifs.if_flags | IFF_INTERFACE;
2567145Ssam 		/* this works because broadaddr overlaps dstaddr */
2577145Ssam 		ifp->int_broadaddr = ifs.if_broadaddr;
2587145Ssam 		ifp->int_net = ifs.if_net;
2597145Ssam 		ifp->int_metric = 0;
2607145Ssam 		ifp->int_next = ifnet;
2616929Ssam 		ifnet = ifp;
2627133Swnj 		addrouteforif(ifp);
2637133Swnj 	}
2647145Ssam 	if (externalinterfaces > 1 && supplier < 0)
2657133Swnj 		supplier = 1;
2667133Swnj 	return;
2677133Swnj bad:
2687133Swnj 	sleep(60);
2697145Ssam 	close(kmem), close(s), close(snoroute);
2707133Swnj 	execv("/etc/routed", argv0);
2717133Swnj 	_exit(0177);
2727133Swnj }
2737130Swnj 
2747133Swnj addrouteforif(ifp)
2757145Ssam 	struct interface *ifp;
2767133Swnj {
2777133Swnj 	struct sockaddr_in net;
2787133Swnj 	struct sockaddr *dst;
2797145Ssam 	int state, metric;
2807133Swnj 
2817145Ssam 	if (ifp->int_flags & IFF_POINTOPOINT)
2827145Ssam 		dst = &ifp->int_dstaddr;
2837133Swnj 	else {
2847133Swnj 		bzero((char *)&net, sizeof (net));
2857133Swnj 		net.sin_family = AF_INET;
2867145Ssam 		net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY);
2877133Swnj 		dst = (struct sockaddr *)&net;
2886920Ssam 	}
289*7250Ssam 	if (rtlookup(dst) == 0)
290*7250Ssam 		rtadd(dst, &ifp->int_addr, ifp->int_metric,
291*7250Ssam 		  ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
2926920Ssam }
2936920Ssam 
294*7250Ssam /*
295*7250Ssam  * As a concession to the ARPANET we read a list of gateways
296*7250Ssam  * from /etc/gateways and add them to our tables.  This file
297*7250Ssam  * exists at each ARPANET gateway and indicates a set of ``remote''
298*7250Ssam  * gateways (i.e. a gateway which we can't immediately determine
299*7250Ssam  * if it's present or not as we can do for those directly connected
300*7250Ssam  * at the hardware level).  If a gateway is marked ``passive''
301*7250Ssam  * in the file, then we assume it doesn't have a routing process
302*7250Ssam  * of our design and simply assume it's always present.  Those
303*7250Ssam  * not marked passive are treated as if they were directly
304*7250Ssam  * connected -- they're added into the interface list so we'll
305*7250Ssam  * send them routing updates.
306*7250Ssam  */
3077133Swnj gwkludge()
3087130Swnj {
3097130Swnj 	struct sockaddr_in dst, gate;
3107130Swnj 	FILE *fp;
3117139Ssam 	char buf[BUFSIZ];
3127145Ssam 	struct interface *ifp;
3137145Ssam 	int metric;
3147130Swnj 
3157130Swnj 	fp = fopen("/etc/gateways", "r");
3167130Swnj 	if (fp == NULL)
3177130Swnj 		return;
3187130Swnj 	bzero((char *)&dst, sizeof (dst));
3197130Swnj 	bzero((char *)&gate, sizeof (gate));
3207145Ssam 	dst.sin_family = gate.sin_family = AF_INET;
3217145Ssam 	/* format: dst XX gateway XX metric DD [passive]\n */
3227145Ssam #define	readentry(fp) \
3237145Ssam 	fscanf((fp), "dst %x gateway %x metric %d %s\n", \
3247145Ssam 	&dst.sin_addr.s_addr, &gate.sin_addr.s_addr, &metric, buf)
3257133Swnj 	for (;;) {
3267145Ssam 		if (readentry(fp) == EOF)
3277133Swnj 			break;
3287145Ssam 		ifp = (struct interface *)malloc(sizeof (*ifp));
3297145Ssam 		bzero((char *)ifp, sizeof (*ifp));
3307145Ssam 		ifp->int_flags = IFF_REMOTE;
3317145Ssam 		/* can't identify broadcast capability */
3327212Ssam 		ifp->int_net = IN_NETOF(dst.sin_addr);
3337145Ssam 		if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) {
3347145Ssam 			ifp->int_flags |= IFF_POINTOPOINT;
3357145Ssam 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
3367212Ssam 		}
3377145Ssam 		if (strcmp(buf, "passive") == 0)
3387145Ssam 			ifp->int_flags |= IFF_PASSIVE;
339*7250Ssam 		else
340*7250Ssam 			/* assume no duplicate entries */
341*7250Ssam 			externalinterfaces++;
3427145Ssam 		ifp->int_addr = *((struct sockaddr *)&gate);
3437145Ssam 		ifp->int_metric = metric;
3447145Ssam 		ifp->int_next = ifnet;
3457145Ssam 		ifnet = ifp;
3467145Ssam 		addrouteforif(ifp);
3477130Swnj 	}
3487130Swnj 	fclose(fp);
3497130Swnj }
3507130Swnj 
351*7250Ssam /*
352*7250Ssam  * Timer routine.  Performs routing information supply
353*7250Ssam  * duties and manages timers on routing table entries.
354*7250Ssam  */
3557133Swnj timer()
3566920Ssam {
3576934Ssam 	register struct rthash *rh;
3586920Ssam 	register struct rt_entry *rt;
3596934Ssam 	struct rthash *base = hosthash;
360*7250Ssam 	int doinghost = 1, timetobroadcast;
3616920Ssam 
3627133Swnj 	timeval += TIMER_RATE;
3637145Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
3647145Ssam 		ifinit();
365*7250Ssam 	timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
3667133Swnj 	tprintf(">>> time %d >>>\n", timeval);
3676920Ssam again:
3687133Swnj 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
3697133Swnj 		rt = rh->rt_forw;
3707133Swnj 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
371*7250Ssam 			/*
372*7250Ssam 			 * We don't advance time on a routing entry for
373*7250Ssam 			 * a passive gateway or that for our only interface.
374*7250Ssam 			 * The latter is excused because we don't act as
375*7250Ssam 			 * a routing information supplier and hence would
376*7250Ssam 			 * time it out.  This is fair as if it's down
377*7250Ssam 			 * we're cut off from the world anyway and it's
378*7250Ssam 			 * not likely we'll grow any new hardware in
379*7250Ssam 			 * the mean time.
380*7250Ssam 			 */
381*7250Ssam 			if (!(rt->rt_state & RTS_PASSIVE) &&
382*7250Ssam 			    (supplier || !(rt->rt_state & RTS_INTERFACE)))
3837133Swnj 				rt->rt_timer += TIMER_RATE;
3847133Swnj 			if (rt->rt_timer >= EXPIRE_TIME)
3857133Swnj 				rt->rt_metric = HOPCNT_INFINITY;
3867145Ssam 			log("", rt);
3877145Ssam 			if (rt->rt_timer >= GARBAGE_TIME) {
3887133Swnj 				rt = rt->rt_back;
3897133Swnj 				rtdelete(rt->rt_forw);
3907133Swnj 				continue;
3917133Swnj 			}
3927145Ssam 			if (rt->rt_state & RTS_CHANGED) {
3937145Ssam 				rt->rt_state &= ~RTS_CHANGED;
3947145Ssam 				/* don't send extraneous packets */
395*7250Ssam 				if (!supplier || timetobroadcast)
3967145Ssam 					continue;
3977133Swnj 				log("broadcast", rt);
3987133Swnj 				msg->rip_cmd = RIPCMD_RESPONSE;
3997133Swnj 				msg->rip_nets[0].rip_dst = rt->rt_dst;
4007133Swnj 				msg->rip_nets[0].rip_metric =
4017133Swnj 				    min(rt->rt_metric+1, HOPCNT_INFINITY);
4027139Ssam 				toall(sendmsg);
4037133Swnj 			}
4047133Swnj 		}
4056920Ssam 	}
4066920Ssam 	if (doinghost) {
4077133Swnj 		doinghost = 0;
4086929Ssam 		base = nethash;
4096920Ssam 		goto again;
4106920Ssam 	}
411*7250Ssam 	if (timetobroadcast)
4127133Swnj 		toall(supply);
4137133Swnj 	tprintf("<<< time %d <<<\n", timeval);
4147133Swnj 	alarm(TIMER_RATE);
4156920Ssam }
4166920Ssam 
4177133Swnj toall(f)
4187133Swnj 	int (*f)();
4196920Ssam {
4207145Ssam 	register struct interface *ifp;
4216920Ssam 	register struct sockaddr *dst;
4226920Ssam 
4237145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
4247145Ssam 		if (ifp->int_flags & IFF_PASSIVE)
4256920Ssam 			continue;
4267145Ssam 		dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
4277145Ssam 		      ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
4287145Ssam 		      &ifp->int_addr;
4297145Ssam 		(*f)(dst, ifp->int_flags & IFF_INTERFACE);
4306920Ssam 	}
4316920Ssam }
4326920Ssam 
4337139Ssam /*ARGSUSED*/
4347139Ssam sendmsg(dst, dontroute)
4357133Swnj 	struct sockaddr *dst;
4367139Ssam 	int dontroute;
4377133Swnj {
4387133Swnj 	(*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
4397133Swnj }
4407133Swnj 
441*7250Ssam /*
442*7250Ssam  * Supply dst with the contents of the routing tables.
443*7250Ssam  * If this won't fit in one packet, chop it up into several.
444*7250Ssam  */
4457139Ssam supply(dst, dontroute)
4467139Ssam 	struct sockaddr *dst;
4477139Ssam 	int dontroute;
4487139Ssam {
4497133Swnj 	register struct rt_entry *rt;
4506920Ssam 	struct netinfo *n = msg->rip_nets;
4516934Ssam 	register struct rthash *rh;
4526934Ssam 	struct rthash *base = hosthash;
4536929Ssam 	int doinghost = 1, size;
4547139Ssam 	int (*output)() = afswitch[dst->sa_family].af_output;
4557139Ssam 	int sto = dontroute ? snoroute : s;
4566920Ssam 
4577212Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4586920Ssam again:
4596920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4606920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4616929Ssam 		size = (char *)n - packet;
4626929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4637139Ssam 			(*output)(sto, dst, size);
4646920Ssam 			n = msg->rip_nets;
4656920Ssam 		}
4666920Ssam 		n->rip_dst = rt->rt_dst;
4676929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4686929Ssam 		n++;
4696920Ssam 	}
4706920Ssam 	if (doinghost) {
4716920Ssam 		doinghost = 0;
4726920Ssam 		base = nethash;
4736920Ssam 		goto again;
4746920Ssam 	}
4756929Ssam 	if (n != msg->rip_nets)
4767139Ssam 		(*output)(sto, dst, (char *)n - packet);
4776920Ssam }
4786920Ssam 
4796920Ssam /*
4806920Ssam  * Handle an incoming routing packet.
4816920Ssam  */
4826920Ssam rip_input(from, size)
4836920Ssam 	struct sockaddr *from;
4846920Ssam 	int size;
4856920Ssam {
4866920Ssam 	struct rt_entry *rt;
4876920Ssam 	struct netinfo *n;
4887145Ssam 	struct interface *ifp;
4897133Swnj 	time_t t;
4907139Ssam 	int newsize;
4917139Ssam 	struct afswitch *afp;
4926920Ssam 
4937139Ssam 	if (trace) {
4947139Ssam 		if (msg->rip_cmd < RIPCMD_MAX)
4957139Ssam 			printf("%s from %x\n", ripcmds[msg->rip_cmd],
4967139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
4977139Ssam 		else
4987139Ssam 			printf("%x from %x\n", msg->rip_cmd,
4997139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5007139Ssam 	}
5017139Ssam 	if (from->sa_family >= AF_MAX)
5027139Ssam 		return;
5037139Ssam 	afp = &afswitch[from->sa_family];
5046937Ssam 	switch (msg->rip_cmd) {
5056937Ssam 
5067139Ssam 	case RIPCMD_REQUEST:
507*7250Ssam 		if (!supplier)
508*7250Ssam 			return;
5097139Ssam 		newsize = 0;
5107139Ssam 		size -= 4 * sizeof (char);
5117139Ssam 		n = msg->rip_nets;
5127139Ssam 		while (size > 0) {
5137139Ssam 			if (size < sizeof (struct netinfo))
5147139Ssam 				break;
5157139Ssam 			size -= sizeof (struct netinfo);
5166920Ssam 
5177139Ssam 			/*
5187139Ssam 			 * A single entry with sa_family == AF_UNSPEC and
5197139Ssam 			 * metric ``infinity'' means ``all routes''.
5207139Ssam 			 */
5217139Ssam 			if (n->rip_dst.sa_family == AF_UNSPEC &&
5227139Ssam 			    n->rip_metric == HOPCNT_INFINITY && size == 0) {
5237212Ssam 				supply(from, 0);
5247139Ssam 				return;
5257139Ssam 			}
5267139Ssam 			rt = rtlookup(&n->rip_dst);
5277139Ssam 			n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
5287139Ssam 				min(rt->rt_metric+1, HOPCNT_INFINITY);
5297139Ssam 			n++, newsize += sizeof (struct netinfo);
5307139Ssam 		}
5317139Ssam 		if (newsize > 0) {
5327139Ssam 			msg->rip_cmd = RIPCMD_RESPONSE;
5337139Ssam 			newsize += sizeof (int);
5347139Ssam 			(*afp->af_output)(s, from, newsize);
5357139Ssam 		}
5366920Ssam 		return;
5376937Ssam 
5387029Ssam 	case RIPCMD_TRACEON:
5397139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5407029Ssam 			return;
5417133Swnj 		if (trace)
5427133Swnj 			return;
5437133Swnj 		packet[size] = '\0';
5447133Swnj 		ftrace = fopen(msg->rip_tracefile, "a");
5457133Swnj 		if (ftrace == NULL)
5467133Swnj 			return;
5477133Swnj 		(void) dup2(fileno(ftrace), 1);
5487133Swnj 		(void) dup2(fileno(ftrace), 2);
5497133Swnj 		trace = 1;
5507133Swnj 		t = time(0);
5517133Swnj 		printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
5527029Ssam 		return;
5537029Ssam 
5547133Swnj 	case RIPCMD_TRACEOFF:
5557133Swnj 		/* verify message came from a priviledged port */
5567139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5576937Ssam 			return;
5587029Ssam 		if (!trace)
5597029Ssam 			return;
5607029Ssam 		t = time(0);
5617029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
5627029Ssam 		fflush(stdout), fflush(stderr);
5637029Ssam 		if (ftrace)
5647029Ssam 			fclose(ftrace);
5657029Ssam 		(void) close(1), (void) close(2);
5667029Ssam 		trace = 0;
5677029Ssam 		return;
5687133Swnj 
5697133Swnj 	case RIPCMD_RESPONSE:
5707133Swnj 		/* verify message came from a router */
5717139Ssam 		if ((*afp->af_portmatch)(from) == 0)
5727133Swnj 			return;
5737139Ssam 		(*afp->af_canon)(from);
5747133Swnj 		/* are we talking to ourselves? */
5757133Swnj 		ifp = if_ifwithaddr(from);
5767133Swnj 		if (ifp) {
5777145Ssam 			rt = rtfind(from);
5787145Ssam 			if (rt == 0)
5797133Swnj 				addrouteforif(ifp);
5807145Ssam 			else
5817145Ssam 				rt->rt_timer = 0;
5827133Swnj 			return;
5837133Swnj 		}
5847133Swnj 		size -= 4 * sizeof (char);
5857133Swnj 		n = msg->rip_nets;
5867133Swnj 		for (; size > 0; size -= sizeof (struct netinfo), n++) {
5877133Swnj 			if (size < sizeof (struct netinfo))
5887133Swnj 				break;
5897133Swnj 			if (n->rip_metric >= HOPCNT_INFINITY)
5907133Swnj 				continue;
5917133Swnj 			tprintf("dst %x hc %d...",
5927133Swnj 			    ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
5937133Swnj 			    n->rip_metric);
5947133Swnj 			rt = rtlookup(&n->rip_dst);
5957133Swnj 			if (rt == 0) {
5967133Swnj 				rtadd(&n->rip_dst, from, n->rip_metric, 0);
5977133Swnj 				continue;
5987133Swnj 			}
5997133Swnj 			tprintf("ours: gate %x hc %d timer %d\n",
6007133Swnj 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6017133Swnj 			  rt->rt_metric, rt->rt_timer);
6027139Ssam 
6037133Swnj 			/*
604*7250Ssam 			 * Update if from gateway, shorter, or getting
6057139Ssam 			 * stale and equivalent.
6067133Swnj 			 */
6077133Swnj 			if (equal(from, &rt->rt_router) ||
6087133Swnj 			    n->rip_metric < rt->rt_metric ||
6097133Swnj 			    (rt->rt_timer > (EXPIRE_TIME/2) &&
6107133Swnj 			    rt->rt_metric == n->rip_metric)) {
6117133Swnj 				rtchange(rt, from, n->rip_metric);
6127133Swnj 				rt->rt_timer = 0;
6137133Swnj 			}
6147133Swnj 		}
6157133Swnj 		return;
6167029Ssam 	}
6177145Ssam 	tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
6187029Ssam }
6197029Ssam 
620*7250Ssam /*
621*7250Ssam  * Lookup dst in the tables for an exact match.
622*7250Ssam  */
6236920Ssam struct rt_entry *
6246920Ssam rtlookup(dst)
6256920Ssam 	struct sockaddr *dst;
6266920Ssam {
6276920Ssam 	register struct rt_entry *rt;
6286934Ssam 	register struct rthash *rh;
6297011Ssam 	register int hash;
6306920Ssam 	struct afhash h;
6317011Ssam 	int doinghost = 1;
6326920Ssam 
6337011Ssam 	if (dst->sa_family >= AF_MAX)
6347011Ssam 		return (0);
6357011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
6367011Ssam 	hash = h.afh_hosthash;
6377011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6387011Ssam again:
6397011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6407145Ssam 		if (rt->rt_hash != hash)
6417011Ssam 			continue;
6427011Ssam 		if (equal(&rt->rt_dst, dst))
6437011Ssam 			return (rt);
6447011Ssam 	}
6457011Ssam 	if (doinghost) {
6467011Ssam 		doinghost = 0;
6477011Ssam 		hash = h.afh_nethash;
6487011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6497011Ssam 		goto again;
6507011Ssam 	}
6517011Ssam 	return (0);
6527011Ssam }
6537011Ssam 
654*7250Ssam /*
655*7250Ssam  * Find a route to dst as the kernel would.
656*7250Ssam  */
6577011Ssam struct rt_entry *
6587145Ssam rtfind(dst)
6597011Ssam 	struct sockaddr *dst;
6607011Ssam {
6617011Ssam 	register struct rt_entry *rt;
6627011Ssam 	register struct rthash *rh;
6637011Ssam 	register int hash;
6647011Ssam 	struct afhash h;
6657011Ssam 	int af = dst->sa_family;
6667011Ssam 	int doinghost = 1, (*match)();
6677011Ssam 
6686920Ssam 	if (af >= AF_MAX)
6696920Ssam 		return (0);
6706920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6716920Ssam 	hash = h.afh_hosthash;
6726920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6736920Ssam 
6746920Ssam again:
6756920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6766920Ssam 		if (rt->rt_hash != hash)
6776920Ssam 			continue;
6786920Ssam 		if (doinghost) {
6796920Ssam 			if (equal(&rt->rt_dst, dst))
6806920Ssam 				return (rt);
6816920Ssam 		} else {
6826920Ssam 			if (rt->rt_dst.sa_family == af &&
6836920Ssam 			    (*match)(&rt->rt_dst, dst))
6846920Ssam 				return (rt);
6856920Ssam 		}
6866920Ssam 	}
6876920Ssam 	if (doinghost) {
6886920Ssam 		doinghost = 0;
6896920Ssam 		hash = h.afh_nethash;
6907011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6916920Ssam 		match = afswitch[af].af_netmatch;
6926920Ssam 		goto again;
6936920Ssam 	}
6946920Ssam 	return (0);
6956920Ssam }
6966920Ssam 
6977139Ssam rtadd(dst, gate, metric, state)
6986920Ssam 	struct sockaddr *dst, *gate;
6997139Ssam 	int metric, state;
7006920Ssam {
7016920Ssam 	struct afhash h;
7026920Ssam 	register struct rt_entry *rt;
7036934Ssam 	struct rthash *rh;
7046920Ssam 	int af = dst->sa_family, flags, hash;
7056920Ssam 
7066920Ssam 	if (af >= AF_MAX)
7076920Ssam 		return;
7086920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7096920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7106920Ssam 	if (flags & RTF_HOST) {
7116920Ssam 		hash = h.afh_hosthash;
7126920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
7136920Ssam 	} else {
7146920Ssam 		hash = h.afh_nethash;
7156920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7166920Ssam 	}
7176920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
7186920Ssam 	if (rt == 0)
7196920Ssam 		return;
7206920Ssam 	rt->rt_hash = hash;
7216920Ssam 	rt->rt_dst = *dst;
7227130Swnj 	rt->rt_router = *gate;
7236920Ssam 	rt->rt_metric = metric;
7246920Ssam 	rt->rt_timer = 0;
7257139Ssam 	rt->rt_flags = RTF_UP | flags;
7267145Ssam 	rt->rt_state = state | RTS_CHANGED;
7277130Swnj 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
7287130Swnj 	if (metric)
7297130Swnj 		rt->rt_flags |= RTF_GATEWAY;
7306920Ssam 	insque(rt, rh);
7316929Ssam 	log("add", rt);
7327145Ssam 	if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7337145Ssam 		tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7346920Ssam }
7356920Ssam 
7366920Ssam rtchange(rt, gate, metric)
7376920Ssam 	struct rt_entry *rt;
7386920Ssam 	struct sockaddr *gate;
7396920Ssam 	short metric;
7406920Ssam {
7417139Ssam 	int doioctl = 0, metricchanged = 0;
7427145Ssam 	struct rtentry oldroute;
7436920Ssam 
7447145Ssam 	if (!equal(&rt->rt_router, gate))
7457139Ssam 		doioctl++;
7466920Ssam 	if (metric != rt->rt_metric) {
7477139Ssam 		metricchanged++;
7486920Ssam 		rt->rt_metric = metric;
7496920Ssam 	}
7507145Ssam 	if (doioctl || metricchanged) {
7517145Ssam 		log("change", rt);
7527145Ssam 		rt->rt_state |= RTS_CHANGED;
7537145Ssam 	}
7547139Ssam 	if (doioctl) {
7557145Ssam 		oldroute = rt->rt_rt;
7567139Ssam 		rt->rt_router = *gate;
7577145Ssam 		if (install) {
7587145Ssam 			if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7597145Ssam 				tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7607145Ssam 			if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
7617145Ssam 				tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7627145Ssam 		}
7637139Ssam 	}
7646920Ssam }
7656920Ssam 
7666920Ssam rtdelete(rt)
7676920Ssam 	struct rt_entry *rt;
7686920Ssam {
7696929Ssam 	log("delete", rt);
7707130Swnj 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
7717145Ssam 		tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7726920Ssam 	remque(rt);
7736920Ssam 	free((char *)rt);
7746920Ssam }
7756920Ssam 
7766920Ssam log(operation, rt)
7776920Ssam 	char *operation;
7786920Ssam 	struct rt_entry *rt;
7796920Ssam {
7806920Ssam 	struct sockaddr_in *dst, *gate;
7816937Ssam 	static struct bits {
7826920Ssam 		int	t_bits;
7836920Ssam 		char	*t_name;
7846937Ssam 	} flagbits[] = {
7856920Ssam 		{ RTF_UP,	"UP" },
7867130Swnj 		{ RTF_GATEWAY,	"GATEWAY" },
7876920Ssam 		{ RTF_HOST,	"HOST" },
7886920Ssam 		{ 0 }
7896937Ssam 	}, statebits[] = {
7907130Swnj 		{ RTS_PASSIVE,	"PASSIVE" },
7917145Ssam 		{ RTS_REMOTE,	"REMOTE" },
7927145Ssam 		{ RTS_INTERFACE,"INTERFACE" },
7937145Ssam 		{ RTS_CHANGED,	"CHANGED" },
7946937Ssam 		{ 0 }
7956920Ssam 	};
7966937Ssam 	register struct bits *p;
7976920Ssam 	register int first;
7986920Ssam 	char *cp;
7996920Ssam 
8006929Ssam 	if (trace == 0)
8016929Ssam 		return;
8026920Ssam 	printf("%s ", operation);
8036920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8047130Swnj 	gate = (struct sockaddr_in *)&rt->rt_router;
8057145Ssam 	printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
8067145Ssam 		gate->sin_addr, rt->rt_metric);
8076937Ssam 	cp = " %s";
8086937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8096920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8106920Ssam 			continue;
8116920Ssam 		printf(cp, p->t_name);
8126920Ssam 		if (first) {
8136920Ssam 			cp = "|%s";
8146920Ssam 			first = 0;
8156920Ssam 		}
8166920Ssam 	}
8176937Ssam 	printf(" state");
8186937Ssam 	cp = " %s";
8196937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
8206937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
8216937Ssam 			continue;
8226937Ssam 		printf(cp, p->t_name);
8236937Ssam 		if (first) {
8246937Ssam 			cp = "|%s";
8256937Ssam 			first = 0;
8266937Ssam 		}
8276937Ssam 	}
8286920Ssam 	putchar('\n');
8296920Ssam }
8306929Ssam 
8317145Ssam struct interface *
8326929Ssam if_ifwithaddr(addr)
8336929Ssam 	struct sockaddr *addr;
8346929Ssam {
8357145Ssam 	register struct interface *ifp;
8366929Ssam 
8376929Ssam #define	same(a1, a2) \
8386929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8397145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8407145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8416929Ssam 			continue;
8427145Ssam 		if (ifp->int_addr.sa_family != addr->sa_family)
8437145Ssam 			continue;
8447145Ssam 		if (same(&ifp->int_addr, addr))
8456929Ssam 			break;
8467145Ssam 		if ((ifp->int_flags & IFF_BROADCAST) &&
8477145Ssam 		    same(&ifp->int_broadaddr, addr))
8486929Ssam 			break;
8496929Ssam 	}
8506929Ssam 	return (ifp);
8516929Ssam #undef same
8526929Ssam }
8536929Ssam 
8547145Ssam struct interface *
8556929Ssam if_ifwithnet(addr)
8566929Ssam 	register struct sockaddr *addr;
8576929Ssam {
8587145Ssam 	register struct interface *ifp;
8596929Ssam 	register int af = addr->sa_family;
8606929Ssam 	register int (*netmatch)();
8616929Ssam 
8626929Ssam 	if (af >= AF_MAX)
8636929Ssam 		return (0);
8646929Ssam 	netmatch = afswitch[af].af_netmatch;
8657145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8667145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8676929Ssam 			continue;
8687145Ssam 		if (af != ifp->int_addr.sa_family)
8697145Ssam 			continue;
8707145Ssam 		if ((*netmatch)(addr, &ifp->int_addr))
8716929Ssam 			break;
8726929Ssam 	}
8736929Ssam 	return (ifp);
8746929Ssam }
8756929Ssam 
8766929Ssam struct in_addr
8776929Ssam if_makeaddr(net, host)
8786929Ssam 	int net, host;
8796929Ssam {
8806929Ssam 	u_long addr;
8816929Ssam 
8826929Ssam 	if (net < 128)
8836929Ssam 		addr = (net << 24) | host;
8846929Ssam 	else if (net < 65536)
8856929Ssam 		addr = (net << 16) | host;
8866929Ssam 	else
8876929Ssam 		addr = (net << 8) | host;
8886929Ssam #ifdef vax
8896929Ssam 	addr = htonl(addr);
8906929Ssam #endif
8916929Ssam 	return (*(struct in_addr *)&addr);
8926929Ssam }
893