xref: /csrg-svn/sbin/routed/routed.c (revision 8339)
16920Ssam #ifndef lint
2*8339Ssam static char sccsid[] = "@(#)routed.c	4.23 10/06/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>
18*8339Ssam #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 
377139Ssam struct	sockaddr_in routingaddr = { AF_INET, IPPORT_ROUTESERVER };
387139Ssam struct	sockaddr_in noroutingaddr = { AF_INET, IPPORT_ROUTESERVER+1 };
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 
596934Ssam struct in_addr if_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 
676920Ssam main(argc, argv)
686920Ssam 	int argc;
696920Ssam 	char *argv[];
706920Ssam {
716920Ssam 	int cc;
726920Ssam 	struct sockaddr from;
736920Ssam 
747133Swnj 	argv0 = argv;
757029Ssam #ifndef DEBUG
767029Ssam 	if (fork())
777029Ssam 		exit(0);
787029Ssam 	for (cc = 0; cc < 10; cc++)
797029Ssam 		(void) close(cc);
807029Ssam 	(void) open("/", 0);
817029Ssam 	(void) dup2(0, 1);
827029Ssam 	(void) dup2(0, 2);
837029Ssam 	{ int t = open("/dev/tty", 2);
847029Ssam 	  if (t >= 0) {
857029Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
867029Ssam 		(void) close(t);
877029Ssam 	  }
886920Ssam 	}
897029Ssam #endif
906920Ssam 	if (trace) {
917029Ssam 		ftrace = fopen("/etc/routerlog", "w");
927029Ssam 		dup2(fileno(ftrace), 1);
937029Ssam 		dup2(fileno(ftrace), 2);
946920Ssam 	}
957250Ssam 
967250Ssam 	/*
977250Ssam 	 * We use two sockets.  One for which outgoing
987250Ssam 	 * packets are routed and for which they're not.
997250Ssam 	 * The latter allows us to delete routing table
1007250Ssam 	 * entries in the kernel for network interfaces
1017250Ssam 	 * attached to our host which we believe are down
1027250Ssam 	 * while still polling it to see when/if it comes
1037250Ssam 	 * back up.  With the new ipc interface we'll be
1047250Ssam 	 * able to specify ``don't route'' as an option
1057250Ssam 	 * to send, but until then we utilize a second port.
1067250Ssam 	 */
1076937Ssam #ifdef vax || pdp11
1087139Ssam 	routingaddr.sin_port = htons(routingaddr.sin_port);
1097139Ssam 	noroutingaddr.sin_port = htons(noroutingaddr.sin_port);
1106920Ssam #endif
1116920Ssam again:
1127139Ssam 	s = socket(SOCK_DGRAM, 0, &routingaddr, 0);
1136920Ssam 	if (s < 0) {
1146920Ssam 		perror("socket");
1156920Ssam 		sleep(30);
1166920Ssam 		goto again;
1176920Ssam 	}
1187130Swnj again2:
1197139Ssam 	snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE);
1207130Swnj 	if (snoroute < 0) {
1217130Swnj 		perror("socket");
1227130Swnj 		sleep(30);
1237130Swnj 		goto again2;
1247130Swnj 	}
1256920Ssam 	argv++, argc--;
1267133Swnj 	while (argc > 0 && **argv == '-') {
1277133Swnj 		if (!strcmp(*argv, "-s") == 0) {
1286929Ssam 			supplier = 1;
1297133Swnj 			argv++, argc--;
1307133Swnj 			continue;
1317133Swnj 		}
1327133Swnj 		if (!strcmp(*argv, "-q") == 0) {
1336920Ssam 			supplier = 0;
1347133Swnj 			argv++, argc--;
1357133Swnj 			continue;
1367133Swnj 		}
1377133Swnj 		goto usage;
1386920Ssam 	}
1397133Swnj 	if (argc > 0) {
1407133Swnj usage:
1417145Ssam 		fprintf(stderr, "usage: routed [ -sq ]\n");
1427133Swnj 		exit(1);
1437133Swnj 	}
1447250Ssam 	/*
1457250Ssam 	 * Collect an initial view of the world by
1467250Ssam 	 * snooping in the kernel and the gateway kludge
1477250Ssam 	 * file.  Then, send a request packet on all
1487250Ssam 	 * directly connected networks to find out what
1497250Ssam 	 * everyone else thinks.
1507250Ssam 	 */
1517133Swnj 	rtinit();
1527250Ssam 	gwkludge();
1537133Swnj 	ifinit();
1547133Swnj 	if (supplier < 0)
1557133Swnj 		supplier = 0;
1567133Swnj 	msg->rip_cmd = RIPCMD_REQUEST;
1577133Swnj 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
1587133Swnj 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
1597133Swnj 	toall(sendmsg);
1606920Ssam 	sigset(SIGALRM, timer);
1616929Ssam 	timer();
1626920Ssam 
1636920Ssam 	for (;;) {
1646920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1656920Ssam 		if (cc <= 0) {
1666920Ssam 			if (cc < 0 && errno != EINTR)
1676920Ssam 				perror("receive");
1686920Ssam 			continue;
1696920Ssam 		}
1706920Ssam 		sighold(SIGALRM);
1716920Ssam 		rip_input(&from, cc);
1726920Ssam 		sigrelse(SIGALRM);
1736920Ssam 	}
1746920Ssam }
1756920Ssam 
1767133Swnj rtinit()
1776920Ssam {
1786934Ssam 	register struct rthash *rh;
1796920Ssam 
1807133Swnj 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
1817133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1827133Swnj 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
1837133Swnj 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
1846920Ssam }
1856920Ssam 
1867145Ssam struct	interface *ifnet;
1877133Swnj 
1887250Ssam /*
1897250Ssam  * Probe the kernel through /dev/kmem to find the network
1907250Ssam  * interfaces which have configured themselves.  If the
1917250Ssam  * interface is present but not yet up (for example an
1927250Ssam  * ARPANET IMP), set the lookforinterfaces flag so we'll
1937250Ssam  * come back later and look again.
1947250Ssam  */
1957133Swnj ifinit()
1966920Ssam {
1977145Ssam 	struct interface *ifp;
1987145Ssam 	struct ifnet ifs, *next;
1996920Ssam 
2007145Ssam 	if (performnlist) {
2017145Ssam 		nlist("/vmunix", nl);
2027145Ssam 		if (nl[N_IFNET].n_value == 0) {
2037145Ssam 			printf("ifnet: not in namelist\n");
2047145Ssam 			goto bad;
2057145Ssam 		}
2067145Ssam 		performnlist = 0;
2076920Ssam 	}
2086920Ssam 	if (kmem < 0) {
2097145Ssam 		kmem = open("/dev/kmem", 0);
2107145Ssam 		if (kmem < 0) {
2117145Ssam 			perror("/dev/kmem");
2127145Ssam 			goto bad;
2137145Ssam 		}
2146920Ssam 	}
2156929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2167133Swnj 	    read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) {
2177133Swnj 		printf("ifnet: error reading kmem\n");
2187133Swnj 		goto bad;
2196929Ssam 	}
2207145Ssam 	lookforinterfaces = 0;
2217133Swnj 	while (next) {
2227133Swnj 		if (lseek(kmem, (long)next, 0) == -1 ||
2237145Ssam 		    read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) {
2247133Swnj 			perror("read");
2257133Swnj 			goto bad;
2266929Ssam 		}
2277145Ssam 		next = ifs.if_next;
2287145Ssam 		if ((ifs.if_flags & IFF_UP) == 0) {
2297145Ssam 			lookforinterfaces = 1;
2307133Swnj 			continue;
2317145Ssam 		}
2327250Ssam 		/* already known to us? */
2337145Ssam 		if (if_ifwithaddr(&ifs.if_addr))
2347133Swnj 			continue;
2357250Ssam 		/* argh, this'll have to change sometime */
2367145Ssam 		if (ifs.if_addr.sa_family != AF_INET)
2377145Ssam 			continue;
2387250Ssam 		/* no one cares about software loopback interfaces */
2397145Ssam 		if (ifs.if_net == LOOPBACKNET)
2407145Ssam 			continue;
2417145Ssam 		ifp = (struct interface *)malloc(sizeof (struct interface));
2427145Ssam 		if (ifp == 0) {
2437145Ssam 			printf("routed: out of memory\n");
2447145Ssam 			break;
2457145Ssam 		}
2467145Ssam 		/*
2477145Ssam 		 * Count the # of directly connected networks
2487145Ssam 		 * and point to point links which aren't looped
2497145Ssam 		 * back to ourself.  This is used below to
2507250Ssam 		 * decide if we should be a routing ``supplier''.
2517145Ssam 		 */
2527145Ssam 		if ((ifs.if_flags & IFF_POINTOPOINT) == 0 ||
2537145Ssam 		    if_ifwithaddr(&ifs.if_dstaddr) == 0)
2547145Ssam 			externalinterfaces++;
2557145Ssam 		ifp->int_addr = ifs.if_addr;
2567145Ssam 		ifp->int_flags = ifs.if_flags | IFF_INTERFACE;
2577145Ssam 		/* this works because broadaddr overlaps dstaddr */
2587145Ssam 		ifp->int_broadaddr = ifs.if_broadaddr;
2597145Ssam 		ifp->int_net = ifs.if_net;
2607145Ssam 		ifp->int_metric = 0;
2617145Ssam 		ifp->int_next = ifnet;
2626929Ssam 		ifnet = ifp;
2637133Swnj 		addrouteforif(ifp);
2647133Swnj 	}
2657145Ssam 	if (externalinterfaces > 1 && supplier < 0)
2667133Swnj 		supplier = 1;
2677133Swnj 	return;
2687133Swnj bad:
2697133Swnj 	sleep(60);
2707145Ssam 	close(kmem), close(s), close(snoroute);
2717133Swnj 	execv("/etc/routed", argv0);
2727133Swnj 	_exit(0177);
2737133Swnj }
2747130Swnj 
2757133Swnj addrouteforif(ifp)
2767145Ssam 	struct interface *ifp;
2777133Swnj {
2787133Swnj 	struct sockaddr_in net;
2797133Swnj 	struct sockaddr *dst;
2807145Ssam 	int state, metric;
2817258Ssam 	struct rt_entry *rt;
2827133Swnj 
2837145Ssam 	if (ifp->int_flags & IFF_POINTOPOINT)
2847145Ssam 		dst = &ifp->int_dstaddr;
2857133Swnj 	else {
2867133Swnj 		bzero((char *)&net, sizeof (net));
2877133Swnj 		net.sin_family = AF_INET;
2887145Ssam 		net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY);
2897133Swnj 		dst = (struct sockaddr *)&net;
2906920Ssam 	}
2917258Ssam 	rt = rtlookup(dst);
2927258Ssam 	rtadd(dst, &ifp->int_addr, ifp->int_metric,
2937258Ssam 		ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
2947258Ssam 	if (rt)
2957258Ssam 		rtdelete(rt);
2966920Ssam }
2976920Ssam 
2987250Ssam /*
2997250Ssam  * As a concession to the ARPANET we read a list of gateways
3007250Ssam  * from /etc/gateways and add them to our tables.  This file
3017250Ssam  * exists at each ARPANET gateway and indicates a set of ``remote''
3027250Ssam  * gateways (i.e. a gateway which we can't immediately determine
3037250Ssam  * if it's present or not as we can do for those directly connected
3047250Ssam  * at the hardware level).  If a gateway is marked ``passive''
3057250Ssam  * in the file, then we assume it doesn't have a routing process
3067250Ssam  * of our design and simply assume it's always present.  Those
3077250Ssam  * not marked passive are treated as if they were directly
3087250Ssam  * connected -- they're added into the interface list so we'll
3097250Ssam  * send them routing updates.
3107250Ssam  */
3117133Swnj gwkludge()
3127130Swnj {
3137130Swnj 	struct sockaddr_in dst, gate;
3147130Swnj 	FILE *fp;
3157792Ssam 	char *dname, *gname, *qual, buf[BUFSIZ];
3167145Ssam 	struct interface *ifp;
3177145Ssam 	int metric;
3187130Swnj 
3197130Swnj 	fp = fopen("/etc/gateways", "r");
3207130Swnj 	if (fp == NULL)
3217130Swnj 		return;
3227792Ssam 	qual = buf; dname = buf + 64; gname = buf + ((BUFSIZ - 64) / 2);
3237130Swnj 	bzero((char *)&dst, sizeof (dst));
3247130Swnj 	bzero((char *)&gate, sizeof (gate));
3257145Ssam 	dst.sin_family = gate.sin_family = AF_INET;
3267145Ssam 	/* format: dst XX gateway XX metric DD [passive]\n */
3277145Ssam #define	readentry(fp) \
3287792Ssam 	fscanf((fp), "dst %s gateway %s metric %d %s\n", \
3297792Ssam 		dname, gname, &metric, qual)
3307133Swnj 	for (;;) {
331*8339Ssam 		struct hostent *host;
332*8339Ssam 
3337145Ssam 		if (readentry(fp) == EOF)
3347133Swnj 			break;
335*8339Ssam 		host = gethostbyname(dname);
336*8339Ssam 		if (host == 0)
3377792Ssam 			continue;
338*8339Ssam 		bcopy(host->h_addr, &dst.sin_addr, host->h_length);
339*8339Ssam 		host = gethostbyname(gname);
340*8339Ssam 		if (host == 0)
3417792Ssam 			continue;
342*8339Ssam 		bcopy(host->h_addr, &gate.sin_addr, host->h_length);
3437145Ssam 		ifp = (struct interface *)malloc(sizeof (*ifp));
3447145Ssam 		bzero((char *)ifp, sizeof (*ifp));
3457145Ssam 		ifp->int_flags = IFF_REMOTE;
3467145Ssam 		/* can't identify broadcast capability */
3477794Ssam 		ifp->int_net = in_netof(dst.sin_addr);
3487145Ssam 		if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) {
3497145Ssam 			ifp->int_flags |= IFF_POINTOPOINT;
3507145Ssam 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
3517212Ssam 		}
3527792Ssam 		if (strcmp(qual, "passive") == 0)
3537145Ssam 			ifp->int_flags |= IFF_PASSIVE;
3547250Ssam 		else
3557250Ssam 			/* assume no duplicate entries */
3567250Ssam 			externalinterfaces++;
3577145Ssam 		ifp->int_addr = *((struct sockaddr *)&gate);
3587145Ssam 		ifp->int_metric = metric;
3597145Ssam 		ifp->int_next = ifnet;
3607145Ssam 		ifnet = ifp;
3617145Ssam 		addrouteforif(ifp);
3627130Swnj 	}
3637130Swnj 	fclose(fp);
3647130Swnj }
3657130Swnj 
3667250Ssam /*
3677250Ssam  * Timer routine.  Performs routing information supply
3687250Ssam  * duties and manages timers on routing table entries.
3697250Ssam  */
3707133Swnj timer()
3716920Ssam {
3726934Ssam 	register struct rthash *rh;
3736920Ssam 	register struct rt_entry *rt;
3746934Ssam 	struct rthash *base = hosthash;
3757250Ssam 	int doinghost = 1, timetobroadcast;
3766920Ssam 
3777133Swnj 	timeval += TIMER_RATE;
3787145Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
3797145Ssam 		ifinit();
3807250Ssam 	timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
3817133Swnj 	tprintf(">>> time %d >>>\n", timeval);
3826920Ssam again:
3837133Swnj 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
3847133Swnj 		rt = rh->rt_forw;
3857133Swnj 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3867250Ssam 			/*
3877250Ssam 			 * We don't advance time on a routing entry for
3887250Ssam 			 * a passive gateway or that for our only interface.
3897250Ssam 			 * The latter is excused because we don't act as
3907250Ssam 			 * a routing information supplier and hence would
3917250Ssam 			 * time it out.  This is fair as if it's down
3927250Ssam 			 * we're cut off from the world anyway and it's
3937250Ssam 			 * not likely we'll grow any new hardware in
3947250Ssam 			 * the mean time.
3957250Ssam 			 */
3967250Ssam 			if (!(rt->rt_state & RTS_PASSIVE) &&
3977250Ssam 			    (supplier || !(rt->rt_state & RTS_INTERFACE)))
3987133Swnj 				rt->rt_timer += TIMER_RATE;
3997133Swnj 			if (rt->rt_timer >= EXPIRE_TIME)
4007133Swnj 				rt->rt_metric = HOPCNT_INFINITY;
4017145Ssam 			log("", rt);
4027145Ssam 			if (rt->rt_timer >= GARBAGE_TIME) {
4037133Swnj 				rt = rt->rt_back;
4047133Swnj 				rtdelete(rt->rt_forw);
4057133Swnj 				continue;
4067133Swnj 			}
4077145Ssam 			if (rt->rt_state & RTS_CHANGED) {
4087145Ssam 				rt->rt_state &= ~RTS_CHANGED;
4097145Ssam 				/* don't send extraneous packets */
4107250Ssam 				if (!supplier || timetobroadcast)
4117145Ssam 					continue;
4127133Swnj 				log("broadcast", rt);
4137133Swnj 				msg->rip_cmd = RIPCMD_RESPONSE;
4147133Swnj 				msg->rip_nets[0].rip_dst = rt->rt_dst;
4157133Swnj 				msg->rip_nets[0].rip_metric =
4167133Swnj 				    min(rt->rt_metric+1, HOPCNT_INFINITY);
4177139Ssam 				toall(sendmsg);
4187133Swnj 			}
4197133Swnj 		}
4206920Ssam 	}
4216920Ssam 	if (doinghost) {
4227133Swnj 		doinghost = 0;
4236929Ssam 		base = nethash;
4246920Ssam 		goto again;
4256920Ssam 	}
4267250Ssam 	if (timetobroadcast)
4277133Swnj 		toall(supply);
4287133Swnj 	tprintf("<<< time %d <<<\n", timeval);
4297133Swnj 	alarm(TIMER_RATE);
4306920Ssam }
4316920Ssam 
4327133Swnj toall(f)
4337133Swnj 	int (*f)();
4346920Ssam {
4357145Ssam 	register struct interface *ifp;
4366920Ssam 	register struct sockaddr *dst;
4376920Ssam 
4387145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
4397145Ssam 		if (ifp->int_flags & IFF_PASSIVE)
4406920Ssam 			continue;
4417145Ssam 		dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
4427145Ssam 		      ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
4437145Ssam 		      &ifp->int_addr;
4447145Ssam 		(*f)(dst, ifp->int_flags & IFF_INTERFACE);
4456920Ssam 	}
4466920Ssam }
4476920Ssam 
4487139Ssam /*ARGSUSED*/
4497139Ssam sendmsg(dst, dontroute)
4507133Swnj 	struct sockaddr *dst;
4517139Ssam 	int dontroute;
4527133Swnj {
4537133Swnj 	(*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
4547133Swnj }
4557133Swnj 
4567250Ssam /*
4577250Ssam  * Supply dst with the contents of the routing tables.
4587250Ssam  * If this won't fit in one packet, chop it up into several.
4597250Ssam  */
4607139Ssam supply(dst, dontroute)
4617139Ssam 	struct sockaddr *dst;
4627139Ssam 	int dontroute;
4637139Ssam {
4647133Swnj 	register struct rt_entry *rt;
4656920Ssam 	struct netinfo *n = msg->rip_nets;
4666934Ssam 	register struct rthash *rh;
4676934Ssam 	struct rthash *base = hosthash;
4686929Ssam 	int doinghost = 1, size;
4697139Ssam 	int (*output)() = afswitch[dst->sa_family].af_output;
4707139Ssam 	int sto = dontroute ? snoroute : s;
4716920Ssam 
4727212Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4736920Ssam again:
4746920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4756920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4766929Ssam 		size = (char *)n - packet;
4776929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4787139Ssam 			(*output)(sto, dst, size);
4796920Ssam 			n = msg->rip_nets;
4806920Ssam 		}
4816920Ssam 		n->rip_dst = rt->rt_dst;
4826929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4836929Ssam 		n++;
4846920Ssam 	}
4856920Ssam 	if (doinghost) {
4866920Ssam 		doinghost = 0;
4876920Ssam 		base = nethash;
4886920Ssam 		goto again;
4896920Ssam 	}
4906929Ssam 	if (n != msg->rip_nets)
4917139Ssam 		(*output)(sto, dst, (char *)n - packet);
4926920Ssam }
4936920Ssam 
4946920Ssam /*
4956920Ssam  * Handle an incoming routing packet.
4966920Ssam  */
4976920Ssam rip_input(from, size)
4986920Ssam 	struct sockaddr *from;
4996920Ssam 	int size;
5006920Ssam {
5016920Ssam 	struct rt_entry *rt;
5026920Ssam 	struct netinfo *n;
5037145Ssam 	struct interface *ifp;
5047133Swnj 	time_t t;
5057139Ssam 	int newsize;
5067139Ssam 	struct afswitch *afp;
5076920Ssam 
5087139Ssam 	if (trace) {
5097139Ssam 		if (msg->rip_cmd < RIPCMD_MAX)
5107139Ssam 			printf("%s from %x\n", ripcmds[msg->rip_cmd],
5117139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5127139Ssam 		else
5137139Ssam 			printf("%x from %x\n", msg->rip_cmd,
5147139Ssam 			    ((struct sockaddr_in *)from)->sin_addr);
5157139Ssam 	}
5167139Ssam 	if (from->sa_family >= AF_MAX)
5177139Ssam 		return;
5187139Ssam 	afp = &afswitch[from->sa_family];
5196937Ssam 	switch (msg->rip_cmd) {
5206937Ssam 
5217139Ssam 	case RIPCMD_REQUEST:
5227139Ssam 		newsize = 0;
5237139Ssam 		size -= 4 * sizeof (char);
5247139Ssam 		n = msg->rip_nets;
5257139Ssam 		while (size > 0) {
5267139Ssam 			if (size < sizeof (struct netinfo))
5277139Ssam 				break;
5287139Ssam 			size -= sizeof (struct netinfo);
5296920Ssam 
5307139Ssam 			/*
5317139Ssam 			 * A single entry with sa_family == AF_UNSPEC and
5327139Ssam 			 * metric ``infinity'' means ``all routes''.
5337139Ssam 			 */
5347139Ssam 			if (n->rip_dst.sa_family == AF_UNSPEC &&
5357139Ssam 			    n->rip_metric == HOPCNT_INFINITY && size == 0) {
5367212Ssam 				supply(from, 0);
5377139Ssam 				return;
5387139Ssam 			}
5397139Ssam 			rt = rtlookup(&n->rip_dst);
5407139Ssam 			n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
5417139Ssam 				min(rt->rt_metric+1, HOPCNT_INFINITY);
5427139Ssam 			n++, newsize += sizeof (struct netinfo);
5437139Ssam 		}
5447139Ssam 		if (newsize > 0) {
5457139Ssam 			msg->rip_cmd = RIPCMD_RESPONSE;
5467139Ssam 			newsize += sizeof (int);
5477139Ssam 			(*afp->af_output)(s, from, newsize);
5487139Ssam 		}
5496920Ssam 		return;
5506937Ssam 
5517029Ssam 	case RIPCMD_TRACEON:
5527139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5537029Ssam 			return;
5547133Swnj 		if (trace)
5557133Swnj 			return;
5567133Swnj 		packet[size] = '\0';
5577133Swnj 		ftrace = fopen(msg->rip_tracefile, "a");
5587133Swnj 		if (ftrace == NULL)
5597133Swnj 			return;
5607133Swnj 		(void) dup2(fileno(ftrace), 1);
5617133Swnj 		(void) dup2(fileno(ftrace), 2);
5627133Swnj 		trace = 1;
5637133Swnj 		t = time(0);
5647133Swnj 		printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
5657029Ssam 		return;
5667029Ssam 
5677133Swnj 	case RIPCMD_TRACEOFF:
5687133Swnj 		/* verify message came from a priviledged port */
5697139Ssam 		if ((*afp->af_portcheck)(from) == 0)
5706937Ssam 			return;
5717029Ssam 		if (!trace)
5727029Ssam 			return;
5737029Ssam 		t = time(0);
5747029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
5757029Ssam 		fflush(stdout), fflush(stderr);
5767029Ssam 		if (ftrace)
5777029Ssam 			fclose(ftrace);
5787029Ssam 		(void) close(1), (void) close(2);
5797029Ssam 		trace = 0;
5807029Ssam 		return;
5817133Swnj 
5827133Swnj 	case RIPCMD_RESPONSE:
5837133Swnj 		/* verify message came from a router */
5847139Ssam 		if ((*afp->af_portmatch)(from) == 0)
5857133Swnj 			return;
5867139Ssam 		(*afp->af_canon)(from);
5877133Swnj 		/* are we talking to ourselves? */
5887133Swnj 		ifp = if_ifwithaddr(from);
5897133Swnj 		if (ifp) {
5907145Ssam 			rt = rtfind(from);
5917145Ssam 			if (rt == 0)
5927133Swnj 				addrouteforif(ifp);
5937145Ssam 			else
5947145Ssam 				rt->rt_timer = 0;
5957133Swnj 			return;
5967133Swnj 		}
5977133Swnj 		size -= 4 * sizeof (char);
5987133Swnj 		n = msg->rip_nets;
5997133Swnj 		for (; size > 0; size -= sizeof (struct netinfo), n++) {
6007133Swnj 			if (size < sizeof (struct netinfo))
6017133Swnj 				break;
6027133Swnj 			if (n->rip_metric >= HOPCNT_INFINITY)
6037133Swnj 				continue;
6047133Swnj 			tprintf("dst %x hc %d...",
6057133Swnj 			    ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
6067133Swnj 			    n->rip_metric);
6077133Swnj 			rt = rtlookup(&n->rip_dst);
6087133Swnj 			if (rt == 0) {
6097133Swnj 				rtadd(&n->rip_dst, from, n->rip_metric, 0);
6107133Swnj 				continue;
6117133Swnj 			}
6127133Swnj 			tprintf("ours: gate %x hc %d timer %d\n",
6137133Swnj 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6147133Swnj 			  rt->rt_metric, rt->rt_timer);
6157139Ssam 
6167133Swnj 			/*
6177250Ssam 			 * Update if from gateway, shorter, or getting
6187139Ssam 			 * stale and equivalent.
6197133Swnj 			 */
6207133Swnj 			if (equal(from, &rt->rt_router) ||
6217133Swnj 			    n->rip_metric < rt->rt_metric ||
6227133Swnj 			    (rt->rt_timer > (EXPIRE_TIME/2) &&
6237133Swnj 			    rt->rt_metric == n->rip_metric)) {
6247133Swnj 				rtchange(rt, from, n->rip_metric);
6257133Swnj 				rt->rt_timer = 0;
6267133Swnj 			}
6277133Swnj 		}
6287133Swnj 		return;
6297029Ssam 	}
6307145Ssam 	tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
6317029Ssam }
6327029Ssam 
6337250Ssam /*
6347250Ssam  * Lookup dst in the tables for an exact match.
6357250Ssam  */
6366920Ssam struct rt_entry *
6376920Ssam rtlookup(dst)
6386920Ssam 	struct sockaddr *dst;
6396920Ssam {
6406920Ssam 	register struct rt_entry *rt;
6416934Ssam 	register struct rthash *rh;
6427011Ssam 	register int hash;
6436920Ssam 	struct afhash h;
6447011Ssam 	int doinghost = 1;
6456920Ssam 
6467011Ssam 	if (dst->sa_family >= AF_MAX)
6477011Ssam 		return (0);
6487011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
6497011Ssam 	hash = h.afh_hosthash;
6507011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6517011Ssam again:
6527011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6537145Ssam 		if (rt->rt_hash != hash)
6547011Ssam 			continue;
6557011Ssam 		if (equal(&rt->rt_dst, dst))
6567011Ssam 			return (rt);
6577011Ssam 	}
6587011Ssam 	if (doinghost) {
6597011Ssam 		doinghost = 0;
6607011Ssam 		hash = h.afh_nethash;
6617011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6627011Ssam 		goto again;
6637011Ssam 	}
6647011Ssam 	return (0);
6657011Ssam }
6667011Ssam 
6677250Ssam /*
6687250Ssam  * Find a route to dst as the kernel would.
6697250Ssam  */
6707011Ssam struct rt_entry *
6717145Ssam rtfind(dst)
6727011Ssam 	struct sockaddr *dst;
6737011Ssam {
6747011Ssam 	register struct rt_entry *rt;
6757011Ssam 	register struct rthash *rh;
6767011Ssam 	register int hash;
6777011Ssam 	struct afhash h;
6787011Ssam 	int af = dst->sa_family;
6797011Ssam 	int doinghost = 1, (*match)();
6807011Ssam 
6816920Ssam 	if (af >= AF_MAX)
6826920Ssam 		return (0);
6836920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6846920Ssam 	hash = h.afh_hosthash;
6856920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6866920Ssam 
6876920Ssam again:
6886920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6896920Ssam 		if (rt->rt_hash != hash)
6906920Ssam 			continue;
6916920Ssam 		if (doinghost) {
6926920Ssam 			if (equal(&rt->rt_dst, dst))
6936920Ssam 				return (rt);
6946920Ssam 		} else {
6956920Ssam 			if (rt->rt_dst.sa_family == af &&
6966920Ssam 			    (*match)(&rt->rt_dst, dst))
6976920Ssam 				return (rt);
6986920Ssam 		}
6996920Ssam 	}
7006920Ssam 	if (doinghost) {
7016920Ssam 		doinghost = 0;
7026920Ssam 		hash = h.afh_nethash;
7037011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7046920Ssam 		match = afswitch[af].af_netmatch;
7056920Ssam 		goto again;
7066920Ssam 	}
7076920Ssam 	return (0);
7086920Ssam }
7096920Ssam 
7107139Ssam rtadd(dst, gate, metric, state)
7116920Ssam 	struct sockaddr *dst, *gate;
7127139Ssam 	int metric, state;
7136920Ssam {
7146920Ssam 	struct afhash h;
7156920Ssam 	register struct rt_entry *rt;
7166934Ssam 	struct rthash *rh;
7176920Ssam 	int af = dst->sa_family, flags, hash;
7186920Ssam 
7196920Ssam 	if (af >= AF_MAX)
7206920Ssam 		return;
7216920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7226920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7236920Ssam 	if (flags & RTF_HOST) {
7246920Ssam 		hash = h.afh_hosthash;
7256920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
7266920Ssam 	} else {
7276920Ssam 		hash = h.afh_nethash;
7286920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7296920Ssam 	}
7306920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
7316920Ssam 	if (rt == 0)
7326920Ssam 		return;
7336920Ssam 	rt->rt_hash = hash;
7346920Ssam 	rt->rt_dst = *dst;
7357130Swnj 	rt->rt_router = *gate;
7366920Ssam 	rt->rt_metric = metric;
7376920Ssam 	rt->rt_timer = 0;
7387139Ssam 	rt->rt_flags = RTF_UP | flags;
7397145Ssam 	rt->rt_state = state | RTS_CHANGED;
7407130Swnj 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
7417130Swnj 	if (metric)
7427130Swnj 		rt->rt_flags |= RTF_GATEWAY;
7436920Ssam 	insque(rt, rh);
7446929Ssam 	log("add", rt);
7457145Ssam 	if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7467145Ssam 		tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7476920Ssam }
7486920Ssam 
7496920Ssam rtchange(rt, gate, metric)
7506920Ssam 	struct rt_entry *rt;
7516920Ssam 	struct sockaddr *gate;
7526920Ssam 	short metric;
7536920Ssam {
7547139Ssam 	int doioctl = 0, metricchanged = 0;
7557145Ssam 	struct rtentry oldroute;
7566920Ssam 
7577145Ssam 	if (!equal(&rt->rt_router, gate))
7587139Ssam 		doioctl++;
7596920Ssam 	if (metric != rt->rt_metric) {
7607139Ssam 		metricchanged++;
7616920Ssam 		rt->rt_metric = metric;
7626920Ssam 	}
7637145Ssam 	if (doioctl || metricchanged) {
7647145Ssam 		log("change", rt);
7657145Ssam 		rt->rt_state |= RTS_CHANGED;
7667145Ssam 	}
7677139Ssam 	if (doioctl) {
7687145Ssam 		oldroute = rt->rt_rt;
7697139Ssam 		rt->rt_router = *gate;
7707145Ssam 		if (install) {
7717145Ssam 			if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
7727145Ssam 				tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
7737145Ssam 			if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
7747145Ssam 				tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7757145Ssam 		}
7767139Ssam 	}
7776920Ssam }
7786920Ssam 
7796920Ssam rtdelete(rt)
7806920Ssam 	struct rt_entry *rt;
7816920Ssam {
7826929Ssam 	log("delete", rt);
7837130Swnj 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
7847145Ssam 		tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
7856920Ssam 	remque(rt);
7866920Ssam 	free((char *)rt);
7876920Ssam }
7886920Ssam 
7896920Ssam log(operation, rt)
7906920Ssam 	char *operation;
7916920Ssam 	struct rt_entry *rt;
7926920Ssam {
7936920Ssam 	struct sockaddr_in *dst, *gate;
7946937Ssam 	static struct bits {
7956920Ssam 		int	t_bits;
7966920Ssam 		char	*t_name;
7976937Ssam 	} flagbits[] = {
7986920Ssam 		{ RTF_UP,	"UP" },
7997130Swnj 		{ RTF_GATEWAY,	"GATEWAY" },
8006920Ssam 		{ RTF_HOST,	"HOST" },
8016920Ssam 		{ 0 }
8026937Ssam 	}, statebits[] = {
8037130Swnj 		{ RTS_PASSIVE,	"PASSIVE" },
8047145Ssam 		{ RTS_REMOTE,	"REMOTE" },
8057145Ssam 		{ RTS_INTERFACE,"INTERFACE" },
8067145Ssam 		{ RTS_CHANGED,	"CHANGED" },
8076937Ssam 		{ 0 }
8086920Ssam 	};
8096937Ssam 	register struct bits *p;
8106920Ssam 	register int first;
8116920Ssam 	char *cp;
8126920Ssam 
8136929Ssam 	if (trace == 0)
8146929Ssam 		return;
8156920Ssam 	printf("%s ", operation);
8166920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8177130Swnj 	gate = (struct sockaddr_in *)&rt->rt_router;
8187145Ssam 	printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
8197145Ssam 		gate->sin_addr, rt->rt_metric);
8206937Ssam 	cp = " %s";
8216937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8226920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8236920Ssam 			continue;
8246920Ssam 		printf(cp, p->t_name);
8256920Ssam 		if (first) {
8266920Ssam 			cp = "|%s";
8276920Ssam 			first = 0;
8286920Ssam 		}
8296920Ssam 	}
8306937Ssam 	printf(" state");
8316937Ssam 	cp = " %s";
8326937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
8336937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
8346937Ssam 			continue;
8356937Ssam 		printf(cp, p->t_name);
8366937Ssam 		if (first) {
8376937Ssam 			cp = "|%s";
8386937Ssam 			first = 0;
8396937Ssam 		}
8406937Ssam 	}
8416920Ssam 	putchar('\n');
8426920Ssam }
8436929Ssam 
8447145Ssam struct interface *
8456929Ssam if_ifwithaddr(addr)
8466929Ssam 	struct sockaddr *addr;
8476929Ssam {
8487145Ssam 	register struct interface *ifp;
8496929Ssam 
8506929Ssam #define	same(a1, a2) \
8516929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8527145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8537145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8546929Ssam 			continue;
8557145Ssam 		if (ifp->int_addr.sa_family != addr->sa_family)
8567145Ssam 			continue;
8577145Ssam 		if (same(&ifp->int_addr, addr))
8586929Ssam 			break;
8597145Ssam 		if ((ifp->int_flags & IFF_BROADCAST) &&
8607145Ssam 		    same(&ifp->int_broadaddr, addr))
8616929Ssam 			break;
8626929Ssam 	}
8636929Ssam 	return (ifp);
8646929Ssam #undef same
8656929Ssam }
8666929Ssam 
8677145Ssam struct interface *
8686929Ssam if_ifwithnet(addr)
8696929Ssam 	register struct sockaddr *addr;
8706929Ssam {
8717145Ssam 	register struct interface *ifp;
8726929Ssam 	register int af = addr->sa_family;
8736929Ssam 	register int (*netmatch)();
8746929Ssam 
8756929Ssam 	if (af >= AF_MAX)
8766929Ssam 		return (0);
8776929Ssam 	netmatch = afswitch[af].af_netmatch;
8787145Ssam 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
8797145Ssam 		if (ifp->int_flags & IFF_REMOTE)
8806929Ssam 			continue;
8817145Ssam 		if (af != ifp->int_addr.sa_family)
8827145Ssam 			continue;
8837145Ssam 		if ((*netmatch)(addr, &ifp->int_addr))
8846929Ssam 			break;
8856929Ssam 	}
8866929Ssam 	return (ifp);
8876929Ssam }
8886929Ssam 
8896929Ssam struct in_addr
8906929Ssam if_makeaddr(net, host)
8916929Ssam 	int net, host;
8926929Ssam {
8936929Ssam 	u_long addr;
8946929Ssam 
8956929Ssam 	if (net < 128)
8966929Ssam 		addr = (net << 24) | host;
8976929Ssam 	else if (net < 65536)
8986929Ssam 		addr = (net << 16) | host;
8996929Ssam 	else
9006929Ssam 		addr = (net << 8) | host;
9017794Ssam #if vax || pdp11
9026929Ssam 	addr = htonl(addr);
9036929Ssam #endif
9046929Ssam 	return (*(struct in_addr *)&addr);
9056929Ssam }
9067794Ssam 
9077794Ssam /*
9087794Ssam  * Return the network number from an internet
9097794Ssam  * address; handles class a/b/c network #'s.
9107794Ssam  */
9117794Ssam in_netof(in)
9127794Ssam 	struct in_addr in;
9137794Ssam {
9147794Ssam #if vax || pdp11
9157794Ssam 	register u_long net;
9167794Ssam 
9177794Ssam 	if ((in.s_addr&IN_CLASSA) == 0)
9187794Ssam 		return (in.s_addr & IN_CLASSA_NET);
9197794Ssam 	if ((in.s_addr&IN_CLASSB) == 0)
9207794Ssam 		return ((int)htons((u_short)(in.s_addr & IN_CLASSB_NET)));
9217794Ssam 	net = htonl((u_long)(in.s_addr & IN_CLASSC_NET));
9227794Ssam 	net >>= 8;
9237794Ssam 	return ((int)net);
9247794Ssam #else
9257794Ssam 	return (IN_NETOF(in));
9267794Ssam #endif
9277794Ssam }
9287794Ssam 
9297794Ssam /*
9307794Ssam  * Return the local network address portion of an
9317794Ssam  * internet address; handles class a/b/c network
9327794Ssam  * number formats.
9337794Ssam  */
9347794Ssam in_lnaof(in)
9357794Ssam 	struct in_addr in;
9367794Ssam {
9377794Ssam #if vax || pdp11
9387794Ssam #define	IN_LNAOF(in) \
9397794Ssam 	(((in).s_addr&IN_CLASSA) == 0 ? (in).s_addr&IN_CLASSA_LNA : \
9407794Ssam 		((in).s_addr&IN_CLASSB) == 0 ? (in).s_addr&IN_CLASSB_LNA : \
9417794Ssam 			(in).s_addr&IN_CLASSC_LNA)
9427794Ssam 	return ((int)htonl((u_long)IN_LNAOF(in)));
9437794Ssam #else
9447794Ssam 	return (IN_LNAOF(in));
9457794Ssam #endif
9467794Ssam }
947