xref: /csrg-svn/sbin/routed/routed.c (revision 7792)
16920Ssam #ifndef lint
2*7792Ssam static char sccsid[] = "@(#)routed.c	4.21 08/18/82";
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;
467250Ssam 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 	}
947250Ssam 
957250Ssam 	/*
967250Ssam 	 * We use two sockets.  One for which outgoing
977250Ssam 	 * packets are routed and for which they're not.
987250Ssam 	 * The latter allows us to delete routing table
997250Ssam 	 * entries in the kernel for network interfaces
1007250Ssam 	 * attached to our host which we believe are down
1017250Ssam 	 * while still polling it to see when/if it comes
1027250Ssam 	 * back up.  With the new ipc interface we'll be
1037250Ssam 	 * able to specify ``don't route'' as an option
1047250Ssam 	 * to send, but until then we utilize a second port.
1057250Ssam 	 */
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 	}
1437250Ssam 	/*
1447250Ssam 	 * Collect an initial view of the world by
1457250Ssam 	 * snooping in the kernel and the gateway kludge
1467250Ssam 	 * file.  Then, send a request packet on all
1477250Ssam 	 * directly connected networks to find out what
1487250Ssam 	 * everyone else thinks.
1497250Ssam 	 */
1507133Swnj 	rtinit();
1517250Ssam 	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 
1877250Ssam /*
1887250Ssam  * Probe the kernel through /dev/kmem to find the network
1897250Ssam  * interfaces which have configured themselves.  If the
1907250Ssam  * interface is present but not yet up (for example an
1917250Ssam  * ARPANET IMP), set the lookforinterfaces flag so we'll
1927250Ssam  * come back later and look again.
1937250Ssam  */
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 		}
2317250Ssam 		/* already known to us? */
2327145Ssam 		if (if_ifwithaddr(&ifs.if_addr))
2337133Swnj 			continue;
2347250Ssam 		/* argh, this'll have to change sometime */
2357145Ssam 		if (ifs.if_addr.sa_family != AF_INET)
2367145Ssam 			continue;
2377250Ssam 		/* 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
2497250Ssam 		 * 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;
2807258Ssam 	struct rt_entry *rt;
2817133Swnj 
2827145Ssam 	if (ifp->int_flags & IFF_POINTOPOINT)
2837145Ssam 		dst = &ifp->int_dstaddr;
2847133Swnj 	else {
2857133Swnj 		bzero((char *)&net, sizeof (net));
2867133Swnj 		net.sin_family = AF_INET;
2877145Ssam 		net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY);
2887133Swnj 		dst = (struct sockaddr *)&net;
2896920Ssam 	}
2907258Ssam 	rt = rtlookup(dst);
2917258Ssam 	rtadd(dst, &ifp->int_addr, ifp->int_metric,
2927258Ssam 		ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
2937258Ssam 	if (rt)
2947258Ssam 		rtdelete(rt);
2956920Ssam }
2966920Ssam 
2977250Ssam /*
2987250Ssam  * As a concession to the ARPANET we read a list of gateways
2997250Ssam  * from /etc/gateways and add them to our tables.  This file
3007250Ssam  * exists at each ARPANET gateway and indicates a set of ``remote''
3017250Ssam  * gateways (i.e. a gateway which we can't immediately determine
3027250Ssam  * if it's present or not as we can do for those directly connected
3037250Ssam  * at the hardware level).  If a gateway is marked ``passive''
3047250Ssam  * in the file, then we assume it doesn't have a routing process
3057250Ssam  * of our design and simply assume it's always present.  Those
3067250Ssam  * not marked passive are treated as if they were directly
3077250Ssam  * connected -- they're added into the interface list so we'll
3087250Ssam  * send them routing updates.
3097250Ssam  */
3107133Swnj gwkludge()
3117130Swnj {
3127130Swnj 	struct sockaddr_in dst, gate;
3137130Swnj 	FILE *fp;
314*7792Ssam 	char *dname, *gname, *qual, buf[BUFSIZ];
3157145Ssam 	struct interface *ifp;
3167145Ssam 	int metric;
3177130Swnj 
3187130Swnj 	fp = fopen("/etc/gateways", "r");
3197130Swnj 	if (fp == NULL)
3207130Swnj 		return;
321*7792Ssam 	qual = buf; dname = buf + 64; gname = buf + ((BUFSIZ - 64) / 2);
3227130Swnj 	bzero((char *)&dst, sizeof (dst));
3237130Swnj 	bzero((char *)&gate, sizeof (gate));
3247145Ssam 	dst.sin_family = gate.sin_family = AF_INET;
3257145Ssam 	/* format: dst XX gateway XX metric DD [passive]\n */
3267145Ssam #define	readentry(fp) \
327*7792Ssam 	fscanf((fp), "dst %s gateway %s metric %d %s\n", \
328*7792Ssam 		dname, gname, &metric, qual)
3297133Swnj 	for (;;) {
3307145Ssam 		if (readentry(fp) == EOF)
3317133Swnj 			break;
332*7792Ssam 		dst.sin_addr.s_addr = rhost(&dname);
333*7792Ssam 		if (dst.sin_addr.s_addr == -1)
334*7792Ssam 			continue;
335*7792Ssam 		gate.sin_addr.s_addr = rhost(&gname);
336*7792Ssam 		if (gate.sin_addr.s_addr == -1)
337*7792Ssam 			continue;
3387145Ssam 		ifp = (struct interface *)malloc(sizeof (*ifp));
3397145Ssam 		bzero((char *)ifp, sizeof (*ifp));
3407145Ssam 		ifp->int_flags = IFF_REMOTE;
3417145Ssam 		/* can't identify broadcast capability */
3427212Ssam 		ifp->int_net = IN_NETOF(dst.sin_addr);
3437145Ssam 		if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) {
3447145Ssam 			ifp->int_flags |= IFF_POINTOPOINT;
3457145Ssam 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
3467212Ssam 		}
347*7792Ssam 		if (strcmp(qual, "passive") == 0)
3487145Ssam 			ifp->int_flags |= IFF_PASSIVE;
3497250Ssam 		else
3507250Ssam 			/* assume no duplicate entries */
3517250Ssam 			externalinterfaces++;
3527145Ssam 		ifp->int_addr = *((struct sockaddr *)&gate);
3537145Ssam 		ifp->int_metric = metric;
3547145Ssam 		ifp->int_next = ifnet;
3557145Ssam 		ifnet = ifp;
3567145Ssam 		addrouteforif(ifp);
3577130Swnj 	}
3587130Swnj 	fclose(fp);
3597130Swnj }
3607130Swnj 
3617250Ssam /*
3627250Ssam  * Timer routine.  Performs routing information supply
3637250Ssam  * duties and manages timers on routing table entries.
3647250Ssam  */
3657133Swnj timer()
3666920Ssam {
3676934Ssam 	register struct rthash *rh;
3686920Ssam 	register struct rt_entry *rt;
3696934Ssam 	struct rthash *base = hosthash;
3707250Ssam 	int doinghost = 1, timetobroadcast;
3716920Ssam 
3727133Swnj 	timeval += TIMER_RATE;
3737145Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
3747145Ssam 		ifinit();
3757250Ssam 	timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
3767133Swnj 	tprintf(">>> time %d >>>\n", timeval);
3776920Ssam again:
3787133Swnj 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
3797133Swnj 		rt = rh->rt_forw;
3807133Swnj 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3817250Ssam 			/*
3827250Ssam 			 * We don't advance time on a routing entry for
3837250Ssam 			 * a passive gateway or that for our only interface.
3847250Ssam 			 * The latter is excused because we don't act as
3857250Ssam 			 * a routing information supplier and hence would
3867250Ssam 			 * time it out.  This is fair as if it's down
3877250Ssam 			 * we're cut off from the world anyway and it's
3887250Ssam 			 * not likely we'll grow any new hardware in
3897250Ssam 			 * the mean time.
3907250Ssam 			 */
3917250Ssam 			if (!(rt->rt_state & RTS_PASSIVE) &&
3927250Ssam 			    (supplier || !(rt->rt_state & RTS_INTERFACE)))
3937133Swnj 				rt->rt_timer += TIMER_RATE;
3947133Swnj 			if (rt->rt_timer >= EXPIRE_TIME)
3957133Swnj 				rt->rt_metric = HOPCNT_INFINITY;
3967145Ssam 			log("", rt);
3977145Ssam 			if (rt->rt_timer >= GARBAGE_TIME) {
3987133Swnj 				rt = rt->rt_back;
3997133Swnj 				rtdelete(rt->rt_forw);
4007133Swnj 				continue;
4017133Swnj 			}
4027145Ssam 			if (rt->rt_state & RTS_CHANGED) {
4037145Ssam 				rt->rt_state &= ~RTS_CHANGED;
4047145Ssam 				/* don't send extraneous packets */
4057250Ssam 				if (!supplier || timetobroadcast)
4067145Ssam 					continue;
4077133Swnj 				log("broadcast", rt);
4087133Swnj 				msg->rip_cmd = RIPCMD_RESPONSE;
4097133Swnj 				msg->rip_nets[0].rip_dst = rt->rt_dst;
4107133Swnj 				msg->rip_nets[0].rip_metric =
4117133Swnj 				    min(rt->rt_metric+1, HOPCNT_INFINITY);
4127139Ssam 				toall(sendmsg);
4137133Swnj 			}
4147133Swnj 		}
4156920Ssam 	}
4166920Ssam 	if (doinghost) {
4177133Swnj 		doinghost = 0;
4186929Ssam 		base = nethash;
4196920Ssam 		goto again;
4206920Ssam 	}
4217250Ssam 	if (timetobroadcast)
4227133Swnj 		toall(supply);
4237133Swnj 	tprintf("<<< time %d <<<\n", timeval);
4247133Swnj 	alarm(TIMER_RATE);
4256920Ssam }
4266920Ssam 
4277133Swnj toall(f)
4287133Swnj 	int (*f)();
4296920Ssam {
4307145Ssam 	register struct interface *ifp;
4316920Ssam 	register struct sockaddr *dst;
4326920Ssam 
4337145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
4347145Ssam 		if (ifp->int_flags & IFF_PASSIVE)
4356920Ssam 			continue;
4367145Ssam 		dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
4377145Ssam 		      ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
4387145Ssam 		      &ifp->int_addr;
4397145Ssam 		(*f)(dst, ifp->int_flags & IFF_INTERFACE);
4406920Ssam 	}
4416920Ssam }
4426920Ssam 
4437139Ssam /*ARGSUSED*/
4447139Ssam sendmsg(dst, dontroute)
4457133Swnj 	struct sockaddr *dst;
4467139Ssam 	int dontroute;
4477133Swnj {
4487133Swnj 	(*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
4497133Swnj }
4507133Swnj 
4517250Ssam /*
4527250Ssam  * Supply dst with the contents of the routing tables.
4537250Ssam  * If this won't fit in one packet, chop it up into several.
4547250Ssam  */
4557139Ssam supply(dst, dontroute)
4567139Ssam 	struct sockaddr *dst;
4577139Ssam 	int dontroute;
4587139Ssam {
4597133Swnj 	register struct rt_entry *rt;
4606920Ssam 	struct netinfo *n = msg->rip_nets;
4616934Ssam 	register struct rthash *rh;
4626934Ssam 	struct rthash *base = hosthash;
4636929Ssam 	int doinghost = 1, size;
4647139Ssam 	int (*output)() = afswitch[dst->sa_family].af_output;
4657139Ssam 	int sto = dontroute ? snoroute : s;
4666920Ssam 
4677212Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4686920Ssam again:
4696920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4706920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4716929Ssam 		size = (char *)n - packet;
4726929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4737139Ssam 			(*output)(sto, dst, size);
4746920Ssam 			n = msg->rip_nets;
4756920Ssam 		}
4766920Ssam 		n->rip_dst = rt->rt_dst;
4776929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4786929Ssam 		n++;
4796920Ssam 	}
4806920Ssam 	if (doinghost) {
4816920Ssam 		doinghost = 0;
4826920Ssam 		base = nethash;
4836920Ssam 		goto again;
4846920Ssam 	}
4856929Ssam 	if (n != msg->rip_nets)
4867139Ssam 		(*output)(sto, dst, (char *)n - packet);
4876920Ssam }
4886920Ssam 
4896920Ssam /*
4906920Ssam  * Handle an incoming routing packet.
4916920Ssam  */
4926920Ssam rip_input(from, size)
4936920Ssam 	struct sockaddr *from;
4946920Ssam 	int size;
4956920Ssam {
4966920Ssam 	struct rt_entry *rt;
4976920Ssam 	struct netinfo *n;
4987145Ssam 	struct interface *ifp;
4997133Swnj 	time_t t;
5007139Ssam 	int newsize;
5017139Ssam 	struct afswitch *afp;
5026920Ssam 
5037139Ssam 	if (trace) {
5047139Ssam 		if (msg->rip_cmd < RIPCMD_MAX)
5057139Ssam 			printf("%s from %x\n", ripcmds[msg->rip_cmd],
5067139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5077139Ssam 		else
5087139Ssam 			printf("%x from %x\n", msg->rip_cmd,
5097139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5107139Ssam 	}
5117139Ssam 	if (from->sa_family >= AF_MAX)
5127139Ssam 		return;
5137139Ssam 	afp = &afswitch[from->sa_family];
5146937Ssam 	switch (msg->rip_cmd) {
5156937Ssam 
5167139Ssam 	case RIPCMD_REQUEST:
5177139Ssam 		newsize = 0;
5187139Ssam 		size -= 4 * sizeof (char);
5197139Ssam 		n = msg->rip_nets;
5207139Ssam 		while (size > 0) {
5217139Ssam 			if (size < sizeof (struct netinfo))
5227139Ssam 				break;
5237139Ssam 			size -= sizeof (struct netinfo);
5246920Ssam 
5257139Ssam 			/*
5267139Ssam 			 * A single entry with sa_family == AF_UNSPEC and
5277139Ssam 			 * metric ``infinity'' means ``all routes''.
5287139Ssam 			 */
5297139Ssam 			if (n->rip_dst.sa_family == AF_UNSPEC &&
5307139Ssam 			    n->rip_metric == HOPCNT_INFINITY && size == 0) {
5317212Ssam 				supply(from, 0);
5327139Ssam 				return;
5337139Ssam 			}
5347139Ssam 			rt = rtlookup(&n->rip_dst);
5357139Ssam 			n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
5367139Ssam 				min(rt->rt_metric+1, HOPCNT_INFINITY);
5377139Ssam 			n++, newsize += sizeof (struct netinfo);
5387139Ssam 		}
5397139Ssam 		if (newsize > 0) {
5407139Ssam 			msg->rip_cmd = RIPCMD_RESPONSE;
5417139Ssam 			newsize += sizeof (int);
5427139Ssam 			(*afp->af_output)(s, from, newsize);
5437139Ssam 		}
5446920Ssam 		return;
5456937Ssam 
5467029Ssam 	case RIPCMD_TRACEON:
5477139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5487029Ssam 			return;
5497133Swnj 		if (trace)
5507133Swnj 			return;
5517133Swnj 		packet[size] = '\0';
5527133Swnj 		ftrace = fopen(msg->rip_tracefile, "a");
5537133Swnj 		if (ftrace == NULL)
5547133Swnj 			return;
5557133Swnj 		(void) dup2(fileno(ftrace), 1);
5567133Swnj 		(void) dup2(fileno(ftrace), 2);
5577133Swnj 		trace = 1;
5587133Swnj 		t = time(0);
5597133Swnj 		printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
5607029Ssam 		return;
5617029Ssam 
5627133Swnj 	case RIPCMD_TRACEOFF:
5637133Swnj 		/* verify message came from a priviledged port */
5647139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5656937Ssam 			return;
5667029Ssam 		if (!trace)
5677029Ssam 			return;
5687029Ssam 		t = time(0);
5697029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
5707029Ssam 		fflush(stdout), fflush(stderr);
5717029Ssam 		if (ftrace)
5727029Ssam 			fclose(ftrace);
5737029Ssam 		(void) close(1), (void) close(2);
5747029Ssam 		trace = 0;
5757029Ssam 		return;
5767133Swnj 
5777133Swnj 	case RIPCMD_RESPONSE:
5787133Swnj 		/* verify message came from a router */
5797139Ssam 		if ((*afp->af_portmatch)(from) == 0)
5807133Swnj 			return;
5817139Ssam 		(*afp->af_canon)(from);
5827133Swnj 		/* are we talking to ourselves? */
5837133Swnj 		ifp = if_ifwithaddr(from);
5847133Swnj 		if (ifp) {
5857145Ssam 			rt = rtfind(from);
5867145Ssam 			if (rt == 0)
5877133Swnj 				addrouteforif(ifp);
5887145Ssam 			else
5897145Ssam 				rt->rt_timer = 0;
5907133Swnj 			return;
5917133Swnj 		}
5927133Swnj 		size -= 4 * sizeof (char);
5937133Swnj 		n = msg->rip_nets;
5947133Swnj 		for (; size > 0; size -= sizeof (struct netinfo), n++) {
5957133Swnj 			if (size < sizeof (struct netinfo))
5967133Swnj 				break;
5977133Swnj 			if (n->rip_metric >= HOPCNT_INFINITY)
5987133Swnj 				continue;
5997133Swnj 			tprintf("dst %x hc %d...",
6007133Swnj 			    ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
6017133Swnj 			    n->rip_metric);
6027133Swnj 			rt = rtlookup(&n->rip_dst);
6037133Swnj 			if (rt == 0) {
6047133Swnj 				rtadd(&n->rip_dst, from, n->rip_metric, 0);
6057133Swnj 				continue;
6067133Swnj 			}
6077133Swnj 			tprintf("ours: gate %x hc %d timer %d\n",
6087133Swnj 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6097133Swnj 			  rt->rt_metric, rt->rt_timer);
6107139Ssam 
6117133Swnj 			/*
6127250Ssam 			 * Update if from gateway, shorter, or getting
6137139Ssam 			 * stale and equivalent.
6147133Swnj 			 */
6157133Swnj 			if (equal(from, &rt->rt_router) ||
6167133Swnj 			    n->rip_metric < rt->rt_metric ||
6177133Swnj 			    (rt->rt_timer > (EXPIRE_TIME/2) &&
6187133Swnj 			    rt->rt_metric == n->rip_metric)) {
6197133Swnj 				rtchange(rt, from, n->rip_metric);
6207133Swnj 				rt->rt_timer = 0;
6217133Swnj 			}
6227133Swnj 		}
6237133Swnj 		return;
6247029Ssam 	}
6257145Ssam 	tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
6267029Ssam }
6277029Ssam 
6287250Ssam /*
6297250Ssam  * Lookup dst in the tables for an exact match.
6307250Ssam  */
6316920Ssam struct rt_entry *
6326920Ssam rtlookup(dst)
6336920Ssam 	struct sockaddr *dst;
6346920Ssam {
6356920Ssam 	register struct rt_entry *rt;
6366934Ssam 	register struct rthash *rh;
6377011Ssam 	register int hash;
6386920Ssam 	struct afhash h;
6397011Ssam 	int doinghost = 1;
6406920Ssam 
6417011Ssam 	if (dst->sa_family >= AF_MAX)
6427011Ssam 		return (0);
6437011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
6447011Ssam 	hash = h.afh_hosthash;
6457011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6467011Ssam again:
6477011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6487145Ssam 		if (rt->rt_hash != hash)
6497011Ssam 			continue;
6507011Ssam 		if (equal(&rt->rt_dst, dst))
6517011Ssam 			return (rt);
6527011Ssam 	}
6537011Ssam 	if (doinghost) {
6547011Ssam 		doinghost = 0;
6557011Ssam 		hash = h.afh_nethash;
6567011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6577011Ssam 		goto again;
6587011Ssam 	}
6597011Ssam 	return (0);
6607011Ssam }
6617011Ssam 
6627250Ssam /*
6637250Ssam  * Find a route to dst as the kernel would.
6647250Ssam  */
6657011Ssam struct rt_entry *
6667145Ssam rtfind(dst)
6677011Ssam 	struct sockaddr *dst;
6687011Ssam {
6697011Ssam 	register struct rt_entry *rt;
6707011Ssam 	register struct rthash *rh;
6717011Ssam 	register int hash;
6727011Ssam 	struct afhash h;
6737011Ssam 	int af = dst->sa_family;
6747011Ssam 	int doinghost = 1, (*match)();
6757011Ssam 
6766920Ssam 	if (af >= AF_MAX)
6776920Ssam 		return (0);
6786920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6796920Ssam 	hash = h.afh_hosthash;
6806920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6816920Ssam 
6826920Ssam again:
6836920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6846920Ssam 		if (rt->rt_hash != hash)
6856920Ssam 			continue;
6866920Ssam 		if (doinghost) {
6876920Ssam 			if (equal(&rt->rt_dst, dst))
6886920Ssam 				return (rt);
6896920Ssam 		} else {
6906920Ssam 			if (rt->rt_dst.sa_family == af &&
6916920Ssam 			    (*match)(&rt->rt_dst, dst))
6926920Ssam 				return (rt);
6936920Ssam 		}
6946920Ssam 	}
6956920Ssam 	if (doinghost) {
6966920Ssam 		doinghost = 0;
6976920Ssam 		hash = h.afh_nethash;
6987011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6996920Ssam 		match = afswitch[af].af_netmatch;
7006920Ssam 		goto again;
7016920Ssam 	}
7026920Ssam 	return (0);
7036920Ssam }
7046920Ssam 
7057139Ssam rtadd(dst, gate, metric, state)
7066920Ssam 	struct sockaddr *dst, *gate;
7077139Ssam 	int metric, state;
7086920Ssam {
7096920Ssam 	struct afhash h;
7106920Ssam 	register struct rt_entry *rt;
7116934Ssam 	struct rthash *rh;
7126920Ssam 	int af = dst->sa_family, flags, hash;
7136920Ssam 
7146920Ssam 	if (af >= AF_MAX)
7156920Ssam 		return;
7166920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7176920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7186920Ssam 	if (flags & RTF_HOST) {
7196920Ssam 		hash = h.afh_hosthash;
7206920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
7216920Ssam 	} else {
7226920Ssam 		hash = h.afh_nethash;
7236920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7246920Ssam 	}
7256920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
7266920Ssam 	if (rt == 0)
7276920Ssam 		return;
7286920Ssam 	rt->rt_hash = hash;
7296920Ssam 	rt->rt_dst = *dst;
7307130Swnj 	rt->rt_router = *gate;
7316920Ssam 	rt->rt_metric = metric;
7326920Ssam 	rt->rt_timer = 0;
7337139Ssam 	rt->rt_flags = RTF_UP | flags;
7347145Ssam 	rt->rt_state = state | RTS_CHANGED;
7357130Swnj 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
7367130Swnj 	if (metric)
7377130Swnj 		rt->rt_flags |= RTF_GATEWAY;
7386920Ssam 	insque(rt, rh);
7396929Ssam 	log("add", rt);
7407145Ssam 	if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7417145Ssam 		tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7426920Ssam }
7436920Ssam 
7446920Ssam rtchange(rt, gate, metric)
7456920Ssam 	struct rt_entry *rt;
7466920Ssam 	struct sockaddr *gate;
7476920Ssam 	short metric;
7486920Ssam {
7497139Ssam 	int doioctl = 0, metricchanged = 0;
7507145Ssam 	struct rtentry oldroute;
7516920Ssam 
7527145Ssam 	if (!equal(&rt->rt_router, gate))
7537139Ssam 		doioctl++;
7546920Ssam 	if (metric != rt->rt_metric) {
7557139Ssam 		metricchanged++;
7566920Ssam 		rt->rt_metric = metric;
7576920Ssam 	}
7587145Ssam 	if (doioctl || metricchanged) {
7597145Ssam 		log("change", rt);
7607145Ssam 		rt->rt_state |= RTS_CHANGED;
7617145Ssam 	}
7627139Ssam 	if (doioctl) {
7637145Ssam 		oldroute = rt->rt_rt;
7647139Ssam 		rt->rt_router = *gate;
7657145Ssam 		if (install) {
7667145Ssam 			if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7677145Ssam 				tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7687145Ssam 			if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
7697145Ssam 				tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7707145Ssam 		}
7717139Ssam 	}
7726920Ssam }
7736920Ssam 
7746920Ssam rtdelete(rt)
7756920Ssam 	struct rt_entry *rt;
7766920Ssam {
7776929Ssam 	log("delete", rt);
7787130Swnj 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
7797145Ssam 		tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7806920Ssam 	remque(rt);
7816920Ssam 	free((char *)rt);
7826920Ssam }
7836920Ssam 
7846920Ssam log(operation, rt)
7856920Ssam 	char *operation;
7866920Ssam 	struct rt_entry *rt;
7876920Ssam {
7886920Ssam 	struct sockaddr_in *dst, *gate;
7896937Ssam 	static struct bits {
7906920Ssam 		int	t_bits;
7916920Ssam 		char	*t_name;
7926937Ssam 	} flagbits[] = {
7936920Ssam 		{ RTF_UP,	"UP" },
7947130Swnj 		{ RTF_GATEWAY,	"GATEWAY" },
7956920Ssam 		{ RTF_HOST,	"HOST" },
7966920Ssam 		{ 0 }
7976937Ssam 	}, statebits[] = {
7987130Swnj 		{ RTS_PASSIVE,	"PASSIVE" },
7997145Ssam 		{ RTS_REMOTE,	"REMOTE" },
8007145Ssam 		{ RTS_INTERFACE,"INTERFACE" },
8017145Ssam 		{ RTS_CHANGED,	"CHANGED" },
8026937Ssam 		{ 0 }
8036920Ssam 	};
8046937Ssam 	register struct bits *p;
8056920Ssam 	register int first;
8066920Ssam 	char *cp;
8076920Ssam 
8086929Ssam 	if (trace == 0)
8096929Ssam 		return;
8106920Ssam 	printf("%s ", operation);
8116920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8127130Swnj 	gate = (struct sockaddr_in *)&rt->rt_router;
8137145Ssam 	printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
8147145Ssam 		gate->sin_addr, rt->rt_metric);
8156937Ssam 	cp = " %s";
8166937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8176920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8186920Ssam 			continue;
8196920Ssam 		printf(cp, p->t_name);
8206920Ssam 		if (first) {
8216920Ssam 			cp = "|%s";
8226920Ssam 			first = 0;
8236920Ssam 		}
8246920Ssam 	}
8256937Ssam 	printf(" state");
8266937Ssam 	cp = " %s";
8276937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
8286937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
8296937Ssam 			continue;
8306937Ssam 		printf(cp, p->t_name);
8316937Ssam 		if (first) {
8326937Ssam 			cp = "|%s";
8336937Ssam 			first = 0;
8346937Ssam 		}
8356937Ssam 	}
8366920Ssam 	putchar('\n');
8376920Ssam }
8386929Ssam 
8397145Ssam struct interface *
8406929Ssam if_ifwithaddr(addr)
8416929Ssam 	struct sockaddr *addr;
8426929Ssam {
8437145Ssam 	register struct interface *ifp;
8446929Ssam 
8456929Ssam #define	same(a1, a2) \
8466929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8477145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8487145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8496929Ssam 			continue;
8507145Ssam 		if (ifp->int_addr.sa_family != addr->sa_family)
8517145Ssam 			continue;
8527145Ssam 		if (same(&ifp->int_addr, addr))
8536929Ssam 			break;
8547145Ssam 		if ((ifp->int_flags & IFF_BROADCAST) &&
8557145Ssam 		    same(&ifp->int_broadaddr, addr))
8566929Ssam 			break;
8576929Ssam 	}
8586929Ssam 	return (ifp);
8596929Ssam #undef same
8606929Ssam }
8616929Ssam 
8627145Ssam struct interface *
8636929Ssam if_ifwithnet(addr)
8646929Ssam 	register struct sockaddr *addr;
8656929Ssam {
8667145Ssam 	register struct interface *ifp;
8676929Ssam 	register int af = addr->sa_family;
8686929Ssam 	register int (*netmatch)();
8696929Ssam 
8706929Ssam 	if (af >= AF_MAX)
8716929Ssam 		return (0);
8726929Ssam 	netmatch = afswitch[af].af_netmatch;
8737145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8747145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8756929Ssam 			continue;
8767145Ssam 		if (af != ifp->int_addr.sa_family)
8777145Ssam 			continue;
8787145Ssam 		if ((*netmatch)(addr, &ifp->int_addr))
8796929Ssam 			break;
8806929Ssam 	}
8816929Ssam 	return (ifp);
8826929Ssam }
8836929Ssam 
8846929Ssam struct in_addr
8856929Ssam if_makeaddr(net, host)
8866929Ssam 	int net, host;
8876929Ssam {
8886929Ssam 	u_long addr;
8896929Ssam 
8906929Ssam 	if (net < 128)
8916929Ssam 		addr = (net << 24) | host;
8926929Ssam 	else if (net < 65536)
8936929Ssam 		addr = (net << 16) | host;
8946929Ssam 	else
8956929Ssam 		addr = (net << 8) | host;
8966929Ssam #ifdef vax
8976929Ssam 	addr = htonl(addr);
8986929Ssam #endif
8996929Ssam 	return (*(struct in_addr *)&addr);
9006929Ssam }
901