xref: /csrg-svn/sbin/routed/routed.c (revision 8386)
16920Ssam #ifndef lint
2*8386Ssam static char sccsid[] = "@(#)routed.c	4.25 10/08/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>
188339Ssam #include <netdb.h>
197139Ssam #define	RIPCMDS
206920Ssam #include "rip.h"
216920Ssam #include "router.h"
226920Ssam 
236920Ssam #define	LOOPBACKNET	0177
246920Ssam /* casts to keep lint happy */
256920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
266920Ssam #define	remque(q)	_remque((caddr_t)q)
276920Ssam #define equal(a1, a2) \
286920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
296929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
306920Ssam 
316920Ssam struct nlist nl[] = {
326920Ssam #define	N_IFNET		0
336920Ssam 	{ "_ifnet" },
346920Ssam 	0,
356920Ssam };
366920Ssam 
378376Ssam struct	sockaddr_in routingaddr = { AF_INET };
388376Ssam struct	sockaddr_in noroutingaddr = { AF_INET };
396920Ssam 
406920Ssam int	s;
417130Swnj int	snoroute;		/* socket with no routing */
426929Ssam int	kmem = -1;
437133Swnj int	supplier = -1;		/* process should supply updates */
446962Ssam int	install = 1;		/* if 1 call kernel */
457145Ssam int	lookforinterfaces = 1;
467145Ssam int	performnlist = 1;
477250Ssam int	externalinterfaces = 0;	/* # of remote and local interfaces */
487133Swnj int	timeval = -TIMER_RATE;
496920Ssam int	timer();
506920Ssam int	cleanup();
517133Swnj 
527133Swnj #define tprintf if (trace) printf
536920Ssam int	trace = 0;
547029Ssam FILE	*ftrace;
556920Ssam 
567133Swnj char	packet[MAXPACKETSIZE+1];
577133Swnj struct	rip *msg = (struct rip *)packet;
586920Ssam 
598376Ssam struct in_addr inet_makeaddr();
607145Ssam struct interface *if_ifwithaddr(), *if_ifwithnet();
617145Ssam extern char *malloc(), *sys_errlist[];
626922Ssam extern int errno, exit();
637133Swnj char	**argv0;
646920Ssam 
657139Ssam int	sendmsg(), supply();
667133Swnj 
main(argc,argv)676920Ssam main(argc, argv)
686920Ssam 	int argc;
696920Ssam 	char *argv[];
706920Ssam {
716920Ssam 	int cc;
726920Ssam 	struct sockaddr from;
738376Ssam 	struct servent *sp;
746920Ssam 
757133Swnj 	argv0 = argv;
767029Ssam #ifndef DEBUG
777029Ssam 	if (fork())
787029Ssam 		exit(0);
797029Ssam 	for (cc = 0; cc < 10; cc++)
807029Ssam 		(void) close(cc);
817029Ssam 	(void) open("/", 0);
827029Ssam 	(void) dup2(0, 1);
837029Ssam 	(void) dup2(0, 2);
847029Ssam 	{ int t = open("/dev/tty", 2);
857029Ssam 	  if (t >= 0) {
867029Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
877029Ssam 		(void) close(t);
887029Ssam 	  }
896920Ssam 	}
907029Ssam #endif
916920Ssam 	if (trace) {
927029Ssam 		ftrace = fopen("/etc/routerlog", "w");
937029Ssam 		dup2(fileno(ftrace), 1);
947029Ssam 		dup2(fileno(ftrace), 2);
956920Ssam 	}
967250Ssam 
977250Ssam 	/*
987250Ssam 	 * We use two sockets.  One for which outgoing
997250Ssam 	 * packets are routed and for which they're not.
1007250Ssam 	 * The latter allows us to delete routing table
1017250Ssam 	 * entries in the kernel for network interfaces
1027250Ssam 	 * attached to our host which we believe are down
1037250Ssam 	 * while still polling it to see when/if it comes
1047250Ssam 	 * back up.  With the new ipc interface we'll be
1057250Ssam 	 * able to specify ``don't route'' as an option
1067250Ssam 	 * to send, but until then we utilize a second port.
1077250Ssam 	 */
1088376Ssam 	sp = getservbyname("router", "udp");
1098376Ssam 	if (sp == 0) {
1108376Ssam 		fprintf(stderr, "routed: udp/router: unknown service\n");
1118376Ssam 		exit(1);
1128376Ssam 	}
1138376Ssam 	routingaddr.sin_port = htons(sp->s_port);
1148376Ssam 	noroutingaddr.sin_port = htons(sp->s_port + 1);
1156920Ssam again:
1167139Ssam 	s = socket(SOCK_DGRAM, 0, &routingaddr, 0);
1176920Ssam 	if (s < 0) {
1186920Ssam 		perror("socket");
1196920Ssam 		sleep(30);
1206920Ssam 		goto again;
1216920Ssam 	}
1227130Swnj again2:
1237139Ssam 	snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE);
1247130Swnj 	if (snoroute < 0) {
1257130Swnj 		perror("socket");
1267130Swnj 		sleep(30);
1277130Swnj 		goto again2;
1287130Swnj 	}
1296920Ssam 	argv++, argc--;
1307133Swnj 	while (argc > 0 && **argv == '-') {
1317133Swnj 		if (!strcmp(*argv, "-s") == 0) {
1326929Ssam 			supplier = 1;
1337133Swnj 			argv++, argc--;
1347133Swnj 			continue;
1357133Swnj 		}
1367133Swnj 		if (!strcmp(*argv, "-q") == 0) {
1376920Ssam 			supplier = 0;
1387133Swnj 			argv++, argc--;
1397133Swnj 			continue;
1407133Swnj 		}
1417133Swnj 		goto usage;
1426920Ssam 	}
1437133Swnj 	if (argc > 0) {
1447133Swnj usage:
1457145Ssam 		fprintf(stderr, "usage: routed [ -sq ]\n");
1467133Swnj 		exit(1);
1477133Swnj 	}
1487250Ssam 	/*
1497250Ssam 	 * Collect an initial view of the world by
1507250Ssam 	 * snooping in the kernel and the gateway kludge
1517250Ssam 	 * file.  Then, send a request packet on all
1527250Ssam 	 * directly connected networks to find out what
1537250Ssam 	 * everyone else thinks.
1547250Ssam 	 */
1557133Swnj 	rtinit();
1567250Ssam 	gwkludge();
1577133Swnj 	ifinit();
1587133Swnj 	if (supplier < 0)
1597133Swnj 		supplier = 0;
1607133Swnj 	msg->rip_cmd = RIPCMD_REQUEST;
1617133Swnj 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
1627133Swnj 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
1637133Swnj 	toall(sendmsg);
1646920Ssam 	sigset(SIGALRM, timer);
1656929Ssam 	timer();
1666920Ssam 
1676920Ssam 	for (;;) {
1686920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1696920Ssam 		if (cc <= 0) {
1706920Ssam 			if (cc < 0 && errno != EINTR)
1716920Ssam 				perror("receive");
1726920Ssam 			continue;
1736920Ssam 		}
1746920Ssam 		sighold(SIGALRM);
1756920Ssam 		rip_input(&from, cc);
1766920Ssam 		sigrelse(SIGALRM);
1776920Ssam 	}
1786920Ssam }
1796920Ssam 
rtinit()1807133Swnj rtinit()
1816920Ssam {
1826934Ssam 	register struct rthash *rh;
1836920Ssam 
1847133Swnj 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
1857133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1867133Swnj 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
1877133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1886920Ssam }
1896920Ssam 
1907145Ssam struct	interface *ifnet;
1917133Swnj 
1927250Ssam /*
1937250Ssam  * Probe the kernel through /dev/kmem to find the network
1947250Ssam  * interfaces which have configured themselves.  If the
1957250Ssam  * interface is present but not yet up (for example an
1967250Ssam  * ARPANET IMP), set the lookforinterfaces flag so we'll
1977250Ssam  * come back later and look again.
1987250Ssam  */
ifinit()1997133Swnj ifinit()
2006920Ssam {
2017145Ssam 	struct interface *ifp;
2027145Ssam 	struct ifnet ifs, *next;
2036920Ssam 
2047145Ssam 	if (performnlist) {
2057145Ssam 		nlist("/vmunix", nl);
2067145Ssam 		if (nl[N_IFNET].n_value == 0) {
2077145Ssam 			printf("ifnet: not in namelist\n");
2087145Ssam 			goto bad;
2097145Ssam 		}
2107145Ssam 		performnlist = 0;
2116920Ssam 	}
2126920Ssam 	if (kmem < 0) {
2137145Ssam 		kmem = open("/dev/kmem", 0);
2147145Ssam 		if (kmem < 0) {
2157145Ssam 			perror("/dev/kmem");
2167145Ssam 			goto bad;
2177145Ssam 		}
2186920Ssam 	}
2196929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2207133Swnj 	    read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) {
2217133Swnj 		printf("ifnet: error reading kmem\n");
2227133Swnj 		goto bad;
2236929Ssam 	}
2247145Ssam 	lookforinterfaces = 0;
2257133Swnj 	while (next) {
2267133Swnj 		if (lseek(kmem, (long)next, 0) == -1 ||
2277145Ssam 		    read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) {
2287133Swnj 			perror("read");
2297133Swnj 			goto bad;
2306929Ssam 		}
2317145Ssam 		next = ifs.if_next;
2327145Ssam 		if ((ifs.if_flags & IFF_UP) == 0) {
2337145Ssam 			lookforinterfaces = 1;
2347133Swnj 			continue;
2357145Ssam 		}
2367250Ssam 		/* already known to us? */
2377145Ssam 		if (if_ifwithaddr(&ifs.if_addr))
2387133Swnj 			continue;
2397250Ssam 		/* argh, this'll have to change sometime */
2407145Ssam 		if (ifs.if_addr.sa_family != AF_INET)
2417145Ssam 			continue;
2427250Ssam 		/* no one cares about software loopback interfaces */
2437145Ssam 		if (ifs.if_net == LOOPBACKNET)
2447145Ssam 			continue;
2457145Ssam 		ifp = (struct interface *)malloc(sizeof (struct interface));
2467145Ssam 		if (ifp == 0) {
2477145Ssam 			printf("routed: out of memory\n");
2487145Ssam 			break;
2497145Ssam 		}
2507145Ssam 		/*
2517145Ssam 		 * Count the # of directly connected networks
2527145Ssam 		 * and point to point links which aren't looped
2537145Ssam 		 * back to ourself.  This is used below to
2547250Ssam 		 * decide if we should be a routing ``supplier''.
2557145Ssam 		 */
2567145Ssam 		if ((ifs.if_flags & IFF_POINTOPOINT) == 0 ||
2577145Ssam 		    if_ifwithaddr(&ifs.if_dstaddr) == 0)
2587145Ssam 			externalinterfaces++;
2597145Ssam 		ifp->int_addr = ifs.if_addr;
2607145Ssam 		ifp->int_flags = ifs.if_flags | IFF_INTERFACE;
2617145Ssam 		/* this works because broadaddr overlaps dstaddr */
2627145Ssam 		ifp->int_broadaddr = ifs.if_broadaddr;
2637145Ssam 		ifp->int_net = ifs.if_net;
2647145Ssam 		ifp->int_metric = 0;
2657145Ssam 		ifp->int_next = ifnet;
2666929Ssam 		ifnet = ifp;
2677133Swnj 		addrouteforif(ifp);
2687133Swnj 	}
2697145Ssam 	if (externalinterfaces > 1 && supplier < 0)
2707133Swnj 		supplier = 1;
2717133Swnj 	return;
2727133Swnj bad:
2737133Swnj 	sleep(60);
2747145Ssam 	close(kmem), close(s), close(snoroute);
2757133Swnj 	execv("/etc/routed", argv0);
2767133Swnj 	_exit(0177);
2777133Swnj }
2787130Swnj 
2797133Swnj addrouteforif(ifp)
2807145Ssam 	struct interface *ifp;
2817133Swnj {
2827133Swnj 	struct sockaddr_in net;
2837133Swnj 	struct sockaddr *dst;
2847145Ssam 	int state, metric;
2857258Ssam 	struct rt_entry *rt;
2867133Swnj 
2877145Ssam 	if (ifp->int_flags & IFF_POINTOPOINT)
2887145Ssam 		dst = &ifp->int_dstaddr;
2897133Swnj 	else {
2907133Swnj 		bzero((char *)&net, sizeof (net));
2917133Swnj 		net.sin_family = AF_INET;
2928376Ssam 		net.sin_addr = inet_makeaddr(ifp->int_net, INADDR_ANY);
2937133Swnj 		dst = (struct sockaddr *)&net;
2946920Ssam 	}
2957258Ssam 	rt = rtlookup(dst);
2967258Ssam 	rtadd(dst, &ifp->int_addr, ifp->int_metric,
2977258Ssam 		ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
2987258Ssam 	if (rt)
2997258Ssam 		rtdelete(rt);
3006920Ssam }
3016920Ssam 
3027250Ssam /*
3037250Ssam  * As a concession to the ARPANET we read a list of gateways
3047250Ssam  * from /etc/gateways and add them to our tables.  This file
3057250Ssam  * exists at each ARPANET gateway and indicates a set of ``remote''
3067250Ssam  * gateways (i.e. a gateway which we can't immediately determine
3077250Ssam  * if it's present or not as we can do for those directly connected
3087250Ssam  * at the hardware level).  If a gateway is marked ``passive''
3097250Ssam  * in the file, then we assume it doesn't have a routing process
3107250Ssam  * of our design and simply assume it's always present.  Those
3117250Ssam  * not marked passive are treated as if they were directly
3127250Ssam  * connected -- they're added into the interface list so we'll
3137250Ssam  * send them routing updates.
3147250Ssam  */
gwkludge()3157133Swnj gwkludge()
3167130Swnj {
3177130Swnj 	struct sockaddr_in dst, gate;
3187130Swnj 	FILE *fp;
3198376Ssam 	char *type, *dname, *gname, *qual, buf[BUFSIZ];
3207145Ssam 	struct interface *ifp;
3217145Ssam 	int metric;
3227130Swnj 
3237130Swnj 	fp = fopen("/etc/gateways", "r");
3247130Swnj 	if (fp == NULL)
3257130Swnj 		return;
3268376Ssam 	qual = buf;
3278376Ssam 	dname = buf + 64;
3288376Ssam 	gname = buf + ((BUFSIZ - 64) / 3);
3298376Ssam 	type = buf + (((BUFSIZ - 64) * 2) / 3);
3307130Swnj 	bzero((char *)&dst, sizeof (dst));
3317130Swnj 	bzero((char *)&gate, sizeof (gate));
3327145Ssam 	dst.sin_family = gate.sin_family = AF_INET;
333*8386Ssam 	/* format: {net | host} XX gateway XX metric DD [passive]\n */
3347145Ssam #define	readentry(fp) \
335*8386Ssam 	fscanf((fp), "%s %s gateway %s metric %d %s\n", \
3368376Ssam 		type, dname, gname, &metric, qual)
3377133Swnj 	for (;;) {
3388339Ssam 		struct hostent *host;
3398376Ssam 		struct netent *net;
3408339Ssam 
3417145Ssam 		if (readentry(fp) == EOF)
3427133Swnj 			break;
3438376Ssam 		if (strcmp(type, "net") == 0) {
3448376Ssam 			net = getnetbyname(dname);
3458376Ssam 			if (net == 0 || net->n_addrtype != AF_INET)
3468376Ssam 				continue;
3478376Ssam 			dst.sin_addr = inet_makeaddr(net->n_net, INADDR_ANY);
3488376Ssam 		} else if (strcmp(type, "host") == 0) {
3498376Ssam 			host = gethostbyname(dname);
3508376Ssam 			if (host == 0)
3518376Ssam 				continue;
3528376Ssam 			bcopy(host->h_addr, &dst.sin_addr, host->h_length);
3538376Ssam 		} else
3547792Ssam 			continue;
3558339Ssam 		host = gethostbyname(gname);
3568339Ssam 		if (host == 0)
3577792Ssam 			continue;
3588339Ssam 		bcopy(host->h_addr, &gate.sin_addr, host->h_length);
3597145Ssam 		ifp = (struct interface *)malloc(sizeof (*ifp));
3607145Ssam 		bzero((char *)ifp, sizeof (*ifp));
3617145Ssam 		ifp->int_flags = IFF_REMOTE;
3627145Ssam 		/* can't identify broadcast capability */
3638376Ssam 		ifp->int_net = inet_netof(dst.sin_addr);
3648376Ssam 		if (strcmp(type, "host") == 0) {
3657145Ssam 			ifp->int_flags |= IFF_POINTOPOINT;
3667145Ssam 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
3677212Ssam 		}
3687792Ssam 		if (strcmp(qual, "passive") == 0)
3697145Ssam 			ifp->int_flags |= IFF_PASSIVE;
3707250Ssam 		else
3717250Ssam 			/* assume no duplicate entries */
3727250Ssam 			externalinterfaces++;
3737145Ssam 		ifp->int_addr = *((struct sockaddr *)&gate);
3747145Ssam 		ifp->int_metric = metric;
3757145Ssam 		ifp->int_next = ifnet;
3767145Ssam 		ifnet = ifp;
3777145Ssam 		addrouteforif(ifp);
3787130Swnj 	}
3797130Swnj 	fclose(fp);
3807130Swnj }
3817130Swnj 
3827250Ssam /*
3837250Ssam  * Timer routine.  Performs routing information supply
3847250Ssam  * duties and manages timers on routing table entries.
3857250Ssam  */
timer()3867133Swnj timer()
3876920Ssam {
3886934Ssam 	register struct rthash *rh;
3896920Ssam 	register struct rt_entry *rt;
3906934Ssam 	struct rthash *base = hosthash;
3917250Ssam 	int doinghost = 1, timetobroadcast;
3926920Ssam 
3937133Swnj 	timeval += TIMER_RATE;
3947145Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
3957145Ssam 		ifinit();
3967250Ssam 	timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
3977133Swnj 	tprintf(">>> time %d >>>\n", timeval);
3986920Ssam again:
3997133Swnj 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
4007133Swnj 		rt = rh->rt_forw;
4017133Swnj 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4027250Ssam 			/*
4037250Ssam 			 * We don't advance time on a routing entry for
4047250Ssam 			 * a passive gateway or that for our only interface.
4057250Ssam 			 * The latter is excused because we don't act as
4067250Ssam 			 * a routing information supplier and hence would
4077250Ssam 			 * time it out.  This is fair as if it's down
4087250Ssam 			 * we're cut off from the world anyway and it's
4097250Ssam 			 * not likely we'll grow any new hardware in
4107250Ssam 			 * the mean time.
4117250Ssam 			 */
4127250Ssam 			if (!(rt->rt_state & RTS_PASSIVE) &&
4137250Ssam 			    (supplier || !(rt->rt_state & RTS_INTERFACE)))
4147133Swnj 				rt->rt_timer += TIMER_RATE;
4157133Swnj 			if (rt->rt_timer >= EXPIRE_TIME)
4167133Swnj 				rt->rt_metric = HOPCNT_INFINITY;
4177145Ssam 			log("", rt);
4187145Ssam 			if (rt->rt_timer >= GARBAGE_TIME) {
4197133Swnj 				rt = rt->rt_back;
4207133Swnj 				rtdelete(rt->rt_forw);
4217133Swnj 				continue;
4227133Swnj 			}
4237145Ssam 			if (rt->rt_state & RTS_CHANGED) {
4247145Ssam 				rt->rt_state &= ~RTS_CHANGED;
4257145Ssam 				/* don't send extraneous packets */
4267250Ssam 				if (!supplier || timetobroadcast)
4277145Ssam 					continue;
4287133Swnj 				log("broadcast", rt);
4297133Swnj 				msg->rip_cmd = RIPCMD_RESPONSE;
4307133Swnj 				msg->rip_nets[0].rip_dst = rt->rt_dst;
4317133Swnj 				msg->rip_nets[0].rip_metric =
4327133Swnj 				    min(rt->rt_metric+1, HOPCNT_INFINITY);
4337139Ssam 				toall(sendmsg);
4347133Swnj 			}
4357133Swnj 		}
4366920Ssam 	}
4376920Ssam 	if (doinghost) {
4387133Swnj 		doinghost = 0;
4396929Ssam 		base = nethash;
4406920Ssam 		goto again;
4416920Ssam 	}
4427250Ssam 	if (timetobroadcast)
4437133Swnj 		toall(supply);
4447133Swnj 	tprintf("<<< time %d <<<\n", timeval);
4457133Swnj 	alarm(TIMER_RATE);
4466920Ssam }
4476920Ssam 
4487133Swnj toall(f)
4497133Swnj 	int (*f)();
4506920Ssam {
4517145Ssam 	register struct interface *ifp;
4526920Ssam 	register struct sockaddr *dst;
4536920Ssam 
4547145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
4557145Ssam 		if (ifp->int_flags & IFF_PASSIVE)
4566920Ssam 			continue;
4577145Ssam 		dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
4587145Ssam 		      ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
4597145Ssam 		      &ifp->int_addr;
4607145Ssam 		(*f)(dst, ifp->int_flags & IFF_INTERFACE);
4616920Ssam 	}
4626920Ssam }
4636920Ssam 
4647139Ssam /*ARGSUSED*/
4657139Ssam sendmsg(dst, dontroute)
4667133Swnj 	struct sockaddr *dst;
4677139Ssam 	int dontroute;
4687133Swnj {
4697133Swnj 	(*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
4707133Swnj }
4717133Swnj 
4727250Ssam /*
4737250Ssam  * Supply dst with the contents of the routing tables.
4747250Ssam  * If this won't fit in one packet, chop it up into several.
4757250Ssam  */
4767139Ssam supply(dst, dontroute)
4777139Ssam 	struct sockaddr *dst;
4787139Ssam 	int dontroute;
4797139Ssam {
4807133Swnj 	register struct rt_entry *rt;
4816920Ssam 	struct netinfo *n = msg->rip_nets;
4826934Ssam 	register struct rthash *rh;
4836934Ssam 	struct rthash *base = hosthash;
4846929Ssam 	int doinghost = 1, size;
4857139Ssam 	int (*output)() = afswitch[dst->sa_family].af_output;
4867139Ssam 	int sto = dontroute ? snoroute : s;
4876920Ssam 
4887212Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4896920Ssam again:
4906920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4916920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4926929Ssam 		size = (char *)n - packet;
4936929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4947139Ssam 			(*output)(sto, dst, size);
4956920Ssam 			n = msg->rip_nets;
4966920Ssam 		}
4976920Ssam 		n->rip_dst = rt->rt_dst;
4986929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4996929Ssam 		n++;
5006920Ssam 	}
5016920Ssam 	if (doinghost) {
5026920Ssam 		doinghost = 0;
5036920Ssam 		base = nethash;
5046920Ssam 		goto again;
5056920Ssam 	}
5066929Ssam 	if (n != msg->rip_nets)
5077139Ssam 		(*output)(sto, dst, (char *)n - packet);
5086920Ssam }
5096920Ssam 
5106920Ssam /*
5116920Ssam  * Handle an incoming routing packet.
5126920Ssam  */
5136920Ssam rip_input(from, size)
5146920Ssam 	struct sockaddr *from;
5156920Ssam 	int size;
5166920Ssam {
5176920Ssam 	struct rt_entry *rt;
5186920Ssam 	struct netinfo *n;
5197145Ssam 	struct interface *ifp;
5207133Swnj 	time_t t;
5217139Ssam 	int newsize;
5227139Ssam 	struct afswitch *afp;
5236920Ssam 
5247139Ssam 	if (trace) {
5257139Ssam 		if (msg->rip_cmd < RIPCMD_MAX)
5267139Ssam 			printf("%s from %x\n", ripcmds[msg->rip_cmd],
5277139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5287139Ssam 		else
5297139Ssam 			printf("%x from %x\n", msg->rip_cmd,
5307139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5317139Ssam 	}
5327139Ssam 	if (from->sa_family >= AF_MAX)
5337139Ssam 		return;
5347139Ssam 	afp = &afswitch[from->sa_family];
5356937Ssam 	switch (msg->rip_cmd) {
5366937Ssam 
5377139Ssam 	case RIPCMD_REQUEST:
5387139Ssam 		newsize = 0;
5397139Ssam 		size -= 4 * sizeof (char);
5407139Ssam 		n = msg->rip_nets;
5417139Ssam 		while (size > 0) {
5427139Ssam 			if (size < sizeof (struct netinfo))
5437139Ssam 				break;
5447139Ssam 			size -= sizeof (struct netinfo);
5456920Ssam 
5467139Ssam 			/*
5477139Ssam 			 * A single entry with sa_family == AF_UNSPEC and
5487139Ssam 			 * metric ``infinity'' means ``all routes''.
5497139Ssam 			 */
5507139Ssam 			if (n->rip_dst.sa_family == AF_UNSPEC &&
5517139Ssam 			    n->rip_metric == HOPCNT_INFINITY && size == 0) {
5527212Ssam 				supply(from, 0);
5537139Ssam 				return;
5547139Ssam 			}
5557139Ssam 			rt = rtlookup(&n->rip_dst);
5567139Ssam 			n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
5577139Ssam 				min(rt->rt_metric+1, HOPCNT_INFINITY);
5587139Ssam 			n++, newsize += sizeof (struct netinfo);
5597139Ssam 		}
5607139Ssam 		if (newsize > 0) {
5617139Ssam 			msg->rip_cmd = RIPCMD_RESPONSE;
5627139Ssam 			newsize += sizeof (int);
5637139Ssam 			(*afp->af_output)(s, from, newsize);
5647139Ssam 		}
5656920Ssam 		return;
5666937Ssam 
5677029Ssam 	case RIPCMD_TRACEON:
5687139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5697029Ssam 			return;
5707133Swnj 		if (trace)
5717133Swnj 			return;
5727133Swnj 		packet[size] = '\0';
5737133Swnj 		ftrace = fopen(msg->rip_tracefile, "a");
5747133Swnj 		if (ftrace == NULL)
5757133Swnj 			return;
5767133Swnj 		(void) dup2(fileno(ftrace), 1);
5777133Swnj 		(void) dup2(fileno(ftrace), 2);
5787133Swnj 		trace = 1;
5797133Swnj 		t = time(0);
5807133Swnj 		printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
5817029Ssam 		return;
5827029Ssam 
5837133Swnj 	case RIPCMD_TRACEOFF:
5847133Swnj 		/* verify message came from a priviledged port */
5857139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5866937Ssam 			return;
5877029Ssam 		if (!trace)
5887029Ssam 			return;
5897029Ssam 		t = time(0);
5907029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
5917029Ssam 		fflush(stdout), fflush(stderr);
5927029Ssam 		if (ftrace)
5937029Ssam 			fclose(ftrace);
5947029Ssam 		(void) close(1), (void) close(2);
5957029Ssam 		trace = 0;
5967029Ssam 		return;
5977133Swnj 
5987133Swnj 	case RIPCMD_RESPONSE:
5997133Swnj 		/* verify message came from a router */
6007139Ssam 		if ((*afp->af_portmatch)(from) == 0)
6017133Swnj 			return;
6027139Ssam 		(*afp->af_canon)(from);
6037133Swnj 		/* are we talking to ourselves? */
6047133Swnj 		ifp = if_ifwithaddr(from);
6057133Swnj 		if (ifp) {
6067145Ssam 			rt = rtfind(from);
6077145Ssam 			if (rt == 0)
6087133Swnj 				addrouteforif(ifp);
6097145Ssam 			else
6107145Ssam 				rt->rt_timer = 0;
6117133Swnj 			return;
6127133Swnj 		}
6137133Swnj 		size -= 4 * sizeof (char);
6147133Swnj 		n = msg->rip_nets;
6157133Swnj 		for (; size > 0; size -= sizeof (struct netinfo), n++) {
6167133Swnj 			if (size < sizeof (struct netinfo))
6177133Swnj 				break;
6187133Swnj 			if (n->rip_metric >= HOPCNT_INFINITY)
6197133Swnj 				continue;
6207133Swnj 			tprintf("dst %x hc %d...",
6217133Swnj 			    ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
6227133Swnj 			    n->rip_metric);
6237133Swnj 			rt = rtlookup(&n->rip_dst);
6247133Swnj 			if (rt == 0) {
6257133Swnj 				rtadd(&n->rip_dst, from, n->rip_metric, 0);
6267133Swnj 				continue;
6277133Swnj 			}
6287133Swnj 			tprintf("ours: gate %x hc %d timer %d\n",
6297133Swnj 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6307133Swnj 			  rt->rt_metric, rt->rt_timer);
6317139Ssam 
6327133Swnj 			/*
6337250Ssam 			 * Update if from gateway, shorter, or getting
6347139Ssam 			 * stale and equivalent.
6357133Swnj 			 */
6367133Swnj 			if (equal(from, &rt->rt_router) ||
6377133Swnj 			    n->rip_metric < rt->rt_metric ||
6387133Swnj 			    (rt->rt_timer > (EXPIRE_TIME/2) &&
6397133Swnj 			    rt->rt_metric == n->rip_metric)) {
6407133Swnj 				rtchange(rt, from, n->rip_metric);
6417133Swnj 				rt->rt_timer = 0;
6427133Swnj 			}
6437133Swnj 		}
6447133Swnj 		return;
6457029Ssam 	}
6467145Ssam 	tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
6477029Ssam }
6487029Ssam 
6497250Ssam /*
6507250Ssam  * Lookup dst in the tables for an exact match.
6517250Ssam  */
6526920Ssam struct rt_entry *
rtlookup(dst)6536920Ssam rtlookup(dst)
6546920Ssam 	struct sockaddr *dst;
6556920Ssam {
6566920Ssam 	register struct rt_entry *rt;
6576934Ssam 	register struct rthash *rh;
6587011Ssam 	register int hash;
6596920Ssam 	struct afhash h;
6607011Ssam 	int doinghost = 1;
6616920Ssam 
6627011Ssam 	if (dst->sa_family >= AF_MAX)
6637011Ssam 		return (0);
6647011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
6657011Ssam 	hash = h.afh_hosthash;
6667011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6677011Ssam again:
6687011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6697145Ssam 		if (rt->rt_hash != hash)
6707011Ssam 			continue;
6717011Ssam 		if (equal(&rt->rt_dst, dst))
6727011Ssam 			return (rt);
6737011Ssam 	}
6747011Ssam 	if (doinghost) {
6757011Ssam 		doinghost = 0;
6767011Ssam 		hash = h.afh_nethash;
6777011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6787011Ssam 		goto again;
6797011Ssam 	}
6807011Ssam 	return (0);
6817011Ssam }
6827011Ssam 
6837250Ssam /*
6847250Ssam  * Find a route to dst as the kernel would.
6857250Ssam  */
6867011Ssam struct rt_entry *
rtfind(dst)6877145Ssam rtfind(dst)
6887011Ssam 	struct sockaddr *dst;
6897011Ssam {
6907011Ssam 	register struct rt_entry *rt;
6917011Ssam 	register struct rthash *rh;
6927011Ssam 	register int hash;
6937011Ssam 	struct afhash h;
6947011Ssam 	int af = dst->sa_family;
6957011Ssam 	int doinghost = 1, (*match)();
6967011Ssam 
6976920Ssam 	if (af >= AF_MAX)
6986920Ssam 		return (0);
6996920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7006920Ssam 	hash = h.afh_hosthash;
7016920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
7026920Ssam 
7036920Ssam again:
7046920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7056920Ssam 		if (rt->rt_hash != hash)
7066920Ssam 			continue;
7076920Ssam 		if (doinghost) {
7086920Ssam 			if (equal(&rt->rt_dst, dst))
7096920Ssam 				return (rt);
7106920Ssam 		} else {
7116920Ssam 			if (rt->rt_dst.sa_family == af &&
7126920Ssam 			    (*match)(&rt->rt_dst, dst))
7136920Ssam 				return (rt);
7146920Ssam 		}
7156920Ssam 	}
7166920Ssam 	if (doinghost) {
7176920Ssam 		doinghost = 0;
7186920Ssam 		hash = h.afh_nethash;
7197011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7206920Ssam 		match = afswitch[af].af_netmatch;
7216920Ssam 		goto again;
7226920Ssam 	}
7236920Ssam 	return (0);
7246920Ssam }
7256920Ssam 
7267139Ssam rtadd(dst, gate, metric, state)
7276920Ssam 	struct sockaddr *dst, *gate;
7287139Ssam 	int metric, state;
7296920Ssam {
7306920Ssam 	struct afhash h;
7316920Ssam 	register struct rt_entry *rt;
7326934Ssam 	struct rthash *rh;
7336920Ssam 	int af = dst->sa_family, flags, hash;
7346920Ssam 
7356920Ssam 	if (af >= AF_MAX)
7366920Ssam 		return;
7376920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7386920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7396920Ssam 	if (flags & RTF_HOST) {
7406920Ssam 		hash = h.afh_hosthash;
7416920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
7426920Ssam 	} else {
7436920Ssam 		hash = h.afh_nethash;
7446920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7456920Ssam 	}
7466920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
7476920Ssam 	if (rt == 0)
7486920Ssam 		return;
7496920Ssam 	rt->rt_hash = hash;
7506920Ssam 	rt->rt_dst = *dst;
7517130Swnj 	rt->rt_router = *gate;
7526920Ssam 	rt->rt_metric = metric;
7536920Ssam 	rt->rt_timer = 0;
7547139Ssam 	rt->rt_flags = RTF_UP | flags;
7557145Ssam 	rt->rt_state = state | RTS_CHANGED;
7567130Swnj 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
7577130Swnj 	if (metric)
7587130Swnj 		rt->rt_flags |= RTF_GATEWAY;
7596920Ssam 	insque(rt, rh);
7606929Ssam 	log("add", rt);
7617145Ssam 	if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7627145Ssam 		tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7636920Ssam }
7646920Ssam 
7656920Ssam rtchange(rt, gate, metric)
7666920Ssam 	struct rt_entry *rt;
7676920Ssam 	struct sockaddr *gate;
7686920Ssam 	short metric;
7696920Ssam {
7707139Ssam 	int doioctl = 0, metricchanged = 0;
7717145Ssam 	struct rtentry oldroute;
7726920Ssam 
7737145Ssam 	if (!equal(&rt->rt_router, gate))
7747139Ssam 		doioctl++;
7756920Ssam 	if (metric != rt->rt_metric) {
7767139Ssam 		metricchanged++;
7776920Ssam 		rt->rt_metric = metric;
7786920Ssam 	}
7797145Ssam 	if (doioctl || metricchanged) {
7807145Ssam 		log("change", rt);
7817145Ssam 		rt->rt_state |= RTS_CHANGED;
7827145Ssam 	}
7837139Ssam 	if (doioctl) {
7847145Ssam 		oldroute = rt->rt_rt;
7857139Ssam 		rt->rt_router = *gate;
7867145Ssam 		if (install) {
7877145Ssam 			if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7887145Ssam 				tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7897145Ssam 			if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
7907145Ssam 				tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7917145Ssam 		}
7927139Ssam 	}
7936920Ssam }
7946920Ssam 
7956920Ssam rtdelete(rt)
7966920Ssam 	struct rt_entry *rt;
7976920Ssam {
7986929Ssam 	log("delete", rt);
7997130Swnj 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
8007145Ssam 		tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
8016920Ssam 	remque(rt);
8026920Ssam 	free((char *)rt);
8036920Ssam }
8046920Ssam 
log(operation,rt)8056920Ssam log(operation, rt)
8066920Ssam 	char *operation;
8076920Ssam 	struct rt_entry *rt;
8086920Ssam {
8096920Ssam 	struct sockaddr_in *dst, *gate;
8106937Ssam 	static struct bits {
8116920Ssam 		int	t_bits;
8126920Ssam 		char	*t_name;
8136937Ssam 	} flagbits[] = {
8146920Ssam 		{ RTF_UP,	"UP" },
8157130Swnj 		{ RTF_GATEWAY,	"GATEWAY" },
8166920Ssam 		{ RTF_HOST,	"HOST" },
8176920Ssam 		{ 0 }
8186937Ssam 	}, statebits[] = {
8197130Swnj 		{ RTS_PASSIVE,	"PASSIVE" },
8207145Ssam 		{ RTS_REMOTE,	"REMOTE" },
8217145Ssam 		{ RTS_INTERFACE,"INTERFACE" },
8227145Ssam 		{ RTS_CHANGED,	"CHANGED" },
8236937Ssam 		{ 0 }
8246920Ssam 	};
8256937Ssam 	register struct bits *p;
8266920Ssam 	register int first;
8276920Ssam 	char *cp;
8286920Ssam 
8296929Ssam 	if (trace == 0)
8306929Ssam 		return;
8316920Ssam 	printf("%s ", operation);
8326920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8337130Swnj 	gate = (struct sockaddr_in *)&rt->rt_router;
8347145Ssam 	printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
8357145Ssam 		gate->sin_addr, rt->rt_metric);
8366937Ssam 	cp = " %s";
8376937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8386920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8396920Ssam 			continue;
8406920Ssam 		printf(cp, p->t_name);
8416920Ssam 		if (first) {
8426920Ssam 			cp = "|%s";
8436920Ssam 			first = 0;
8446920Ssam 		}
8456920Ssam 	}
8466937Ssam 	printf(" state");
8476937Ssam 	cp = " %s";
8486937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
8496937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
8506937Ssam 			continue;
8516937Ssam 		printf(cp, p->t_name);
8526937Ssam 		if (first) {
8536937Ssam 			cp = "|%s";
8546937Ssam 			first = 0;
8556937Ssam 		}
8566937Ssam 	}
8576920Ssam 	putchar('\n');
8586920Ssam }
8596929Ssam 
8607145Ssam struct interface *
if_ifwithaddr(addr)8616929Ssam if_ifwithaddr(addr)
8626929Ssam 	struct sockaddr *addr;
8636929Ssam {
8647145Ssam 	register struct interface *ifp;
8656929Ssam 
8666929Ssam #define	same(a1, a2) \
8676929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8687145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8697145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8706929Ssam 			continue;
8717145Ssam 		if (ifp->int_addr.sa_family != addr->sa_family)
8727145Ssam 			continue;
8737145Ssam 		if (same(&ifp->int_addr, addr))
8746929Ssam 			break;
8757145Ssam 		if ((ifp->int_flags & IFF_BROADCAST) &&
8767145Ssam 		    same(&ifp->int_broadaddr, addr))
8776929Ssam 			break;
8786929Ssam 	}
8796929Ssam 	return (ifp);
8806929Ssam #undef same
8816929Ssam }
8826929Ssam 
8837145Ssam struct interface *
if_ifwithnet(addr)8846929Ssam if_ifwithnet(addr)
8856929Ssam 	register struct sockaddr *addr;
8866929Ssam {
8877145Ssam 	register struct interface *ifp;
8886929Ssam 	register int af = addr->sa_family;
8896929Ssam 	register int (*netmatch)();
8906929Ssam 
8916929Ssam 	if (af >= AF_MAX)
8926929Ssam 		return (0);
8936929Ssam 	netmatch = afswitch[af].af_netmatch;
8947145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8957145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8966929Ssam 			continue;
8977145Ssam 		if (af != ifp->int_addr.sa_family)
8987145Ssam 			continue;
8997145Ssam 		if ((*netmatch)(addr, &ifp->int_addr))
9006929Ssam 			break;
9016929Ssam 	}
9026929Ssam 	return (ifp);
9036929Ssam }
904