xref: /csrg-svn/sbin/routed/routed.c (revision 7130)
16920Ssam #ifndef lint
2*7130Swnj static char sccsid[] = "@(#)routed.c	4.13 06/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>
186920Ssam #include "rip.h"
196920Ssam #include "router.h"
206920Ssam 
216920Ssam #define	LOOPBACKNET	0177
226920Ssam /* casts to keep lint happy */
236920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
246920Ssam #define	remque(q)	_remque((caddr_t)q)
256920Ssam #define equal(a1, a2) \
266920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
276929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
286920Ssam 
296920Ssam struct nlist nl[] = {
306920Ssam #define	N_IFNET		0
316920Ssam 	{ "_ifnet" },
326920Ssam 	0,
336920Ssam };
346920Ssam 
356920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
366920Ssam 
376920Ssam int	s;
38*7130Swnj int	snoroute;		/* socket with no routing */
396929Ssam int	kmem = -1;
406920Ssam int	supplier;		/* process should supply updates */
416962Ssam int	install = 1;		/* if 1 call kernel */
426929Ssam int	lookforinterfaces = 1;
436929Ssam int	performnlist = 1;
446920Ssam int	timeval;
456920Ssam int	timer();
466920Ssam int	cleanup();
476920Ssam int	trace = 0;
487029Ssam FILE	*ftrace;
496920Ssam 
506920Ssam char	packet[MAXPACKETSIZE];
516920Ssam 
526934Ssam struct in_addr if_makeaddr();
536934Ssam struct ifnet *if_ifwithaddr(), *if_ifwithnet();
546920Ssam extern char *malloc();
556922Ssam extern int errno, exit();
566920Ssam 
576920Ssam main(argc, argv)
586920Ssam 	int argc;
596920Ssam 	char *argv[];
606920Ssam {
616920Ssam 	int cc;
626920Ssam 	struct sockaddr from;
636920Ssam 
647029Ssam #ifndef DEBUG
657029Ssam 	if (fork())
667029Ssam 		exit(0);
677029Ssam 	for (cc = 0; cc < 10; cc++)
687029Ssam 		(void) close(cc);
697029Ssam 	(void) open("/", 0);
707029Ssam 	(void) dup2(0, 1);
717029Ssam 	(void) dup2(0, 2);
727029Ssam 	{ int t = open("/dev/tty", 2);
737029Ssam 	  if (t >= 0) {
747029Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
757029Ssam 		(void) close(t);
767029Ssam 	  }
776920Ssam 	}
787029Ssam #endif
796920Ssam 	if (trace) {
807029Ssam 		ftrace = fopen("/etc/routerlog", "w");
817029Ssam 		dup2(fileno(ftrace), 1);
827029Ssam 		dup2(fileno(ftrace), 2);
836920Ssam 	}
846937Ssam #ifdef vax || pdp11
856920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
866920Ssam #endif
876920Ssam again:
886920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
896920Ssam 	if (s < 0) {
906920Ssam 		perror("socket");
916920Ssam 		sleep(30);
926920Ssam 		goto again;
936920Ssam 	}
94*7130Swnj again2:
95*7130Swnj 	snoroute = socket(SOCK_DGRAM, 0, 0, SO_DONTROUTE);
96*7130Swnj 	if (snoroute < 0) {
97*7130Swnj 		perror("socket");
98*7130Swnj 		sleep(30);
99*7130Swnj 		goto again2;
100*7130Swnj 	}
1016920Ssam 	rtinit();
1026920Ssam 	getinterfaces();
1036920Ssam 	request();
1046920Ssam 
1056920Ssam 	argv++, argc--;
1066920Ssam 	while (argc > 0) {
1076920Ssam 		if (strcmp(*argv, "-s") == 0)
1086929Ssam 			supplier = 1;
1096920Ssam 		else if (strcmp(*argv, "-q") == 0)
1106920Ssam 			supplier = 0;
1116920Ssam 		argv++, argc--;
1126920Ssam 	}
1136920Ssam 	sigset(SIGALRM, timer);
1146929Ssam 	timer();
1156920Ssam 
1166920Ssam 	/*
1176920Ssam 	 * Listen for routing packets
1186920Ssam 	 */
1196920Ssam 	for (;;) {
1206920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1216920Ssam 		if (cc <= 0) {
1226920Ssam 			if (cc < 0 && errno != EINTR)
1236920Ssam 				perror("receive");
1246920Ssam 			continue;
1256920Ssam 		}
1266920Ssam 		sighold(SIGALRM);
1276920Ssam 		rip_input(&from, cc);
1286920Ssam 		sigrelse(SIGALRM);
1296920Ssam 	}
1306920Ssam }
1316920Ssam 
1326920Ssam /*
1336929Ssam  * Timer routine:
1346929Ssam  *
1356929Ssam  * o handle timers on table entries,
1366929Ssam  * o invalidate entries which haven't been updated in a while,
1376929Ssam  * o delete entries which are too old,
1386929Ssam  * o if we're an internetwork router, supply routing updates
1396929Ssam  *   periodically
1406929Ssam  */
1416929Ssam timer()
1426920Ssam {
1436934Ssam 	register struct rthash *rh;
1446929Ssam 	register struct rt_entry *rt;
1456934Ssam 	struct rthash *base = hosthash;
1466929Ssam 	int doinghost = 1;
1476920Ssam 
1486929Ssam 	if (trace)
1496929Ssam 		printf(">>> time %d >>>\n", timeval);
1506929Ssam again:
1516929Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
1526929Ssam 		rt = rh->rt_forw;
1536929Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1546920Ssam 
1556929Ssam 			/*
1566929Ssam 			 * If the host is indicated to be
1576993Ssam 			 * "hidden" (i.e. it's one we got
1586929Ssam 			 * from the initialization file),
1596929Ssam 			 * don't time out it's entry.
1606929Ssam 			 */
161*7130Swnj 			if ((rt->rt_state & RTS_PASSIVE) == 0)
1626933Ssam 				rt->rt_timer += TIMER_RATE;
1636929Ssam 			log("", rt);
1646920Ssam 
1656929Ssam 			/*
1666929Ssam 			 * If the entry should be deleted
1676929Ssam 			 * attempt to do so and reclaim space.
1686929Ssam 			 */
1696929Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
1706937Ssam 			  (rt->rt_state & RTS_DELRT)) {
1716929Ssam 				rt = rt->rt_back;
1726937Ssam 				rtdelete(rt->rt_forw);
1736929Ssam 				continue;
1746929Ssam 			}
1756920Ssam 
1766929Ssam 			/*
1776929Ssam 			 * If we haven't heard from the router
1786929Ssam 			 * in a long time indicate the route is
1796929Ssam 			 * hard to reach.
1806929Ssam 			 */
1816929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
1826929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
1836920Ssam 
1846929Ssam 			/*
185*7130Swnj 			 * If a change or addition is to be made
186*7130Swnj 			 * and this isn't time to broadcast an
187*7130Swnj 			 * update, then broadcast the change.
1886929Ssam 			 */
189*7130Swnj 			if ((rt->rt_state & (RTS_CHGRT|RTS_ADDRT)) &&
190*7130Swnj 			    supplier &&
191*7130Swnj 			    (timeval + TIMER_RATE) % SUPPLY_INTERVAL)
192*7130Swnj 				broadcast(rt);
193*7130Swnj 
194*7130Swnj 			if (rt->rt_state & RTS_CHGRT) {
195*7130Swnj 				struct rtentry oldroute;
196*7130Swnj 
197*7130Swnj 				oldroute = rt->rt_rt;
198*7130Swnj 				rt->rt_router = rt->rt_newrouter;
199*7130Swnj 				if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
200*7130Swnj 					perror("SIOCADDRT");
201*7130Swnj 				if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
202*7130Swnj 					perror("SIOCDELRT");
203*7130Swnj 				rt->rt_state &= ~RTS_CHGRT;
204*7130Swnj 			}
2056937Ssam 			if (rt->rt_state & RTS_ADDRT) {
206*7130Swnj 				if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
207*7130Swnj 					perror("SIOCADDRT");
2086937Ssam 				rt->rt_state &= ~RTS_ADDRT;
2096929Ssam 			}
2106929Ssam 		}
2116929Ssam 	}
2126929Ssam 	if (doinghost) {
2136929Ssam 		doinghost = 0;
2146929Ssam 		base = nethash;
2156929Ssam 		goto again;
2166929Ssam 	}
2176929Ssam 	timeval += TIMER_RATE;
2186929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
2196929Ssam 		getinterfaces();
2206929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
2216929Ssam 		supplyall();
2226929Ssam 	if (trace)
2236929Ssam 		printf("<<< time %d <<<\n", timeval);
2246929Ssam 	alarm(TIMER_RATE);
2256920Ssam }
2266920Ssam 
2276934Ssam struct	ifnet *ifnet;
2286920Ssam /*
2296920Ssam  * Find the network interfaces attached to this machine.
2306929Ssam  * The info is used to:
2316920Ssam  *
2326920Ssam  * (1) initialize the routing tables, as done by the kernel.
2336920Ssam  * (2) ignore incoming packets we send.
2346920Ssam  * (3) figure out broadcast capability and addresses.
2356920Ssam  * (4) figure out if we're an internetwork gateway.
2366920Ssam  *
2376920Ssam  * We don't handle anything but Internet addresses.
2386934Ssam  *
2396934Ssam  * Note: this routine may be called periodically to
2406934Ssam  * scan for new interfaces.  In fact, the timer routine
2416934Ssam  * does so based on the flag lookforinterfaces.  The
2426934Ssam  * flag performnlist is set whenever something odd occurs
2436934Ssam  * while scanning the kernel; this is likely to occur
2446934Ssam  * if /vmunix isn't up to date (e.g. someone booted /ovmunix).
2456920Ssam  */
2466920Ssam getinterfaces()
2476920Ssam {
2486929Ssam 	struct ifnet *ifp;
2496929Ssam 	struct ifnet ifstruct, *next;
2506920Ssam 	struct sockaddr_in net;
2516929Ssam 	register struct sockaddr *dst;
2526920Ssam 	int nets;
2536920Ssam 
2546929Ssam 	if (performnlist) {
2556929Ssam 		nlist("/vmunix", nl);
2566929Ssam 		if (nl[N_IFNET].n_value == 0) {
2576929Ssam 			performnlist++;
2586929Ssam 			printf("ifnet: not in namelist\n");
2596929Ssam 			return;
2606929Ssam 		}
2616929Ssam 		performnlist = 0;
2626920Ssam 	}
2636920Ssam 	if (kmem < 0) {
2646929Ssam 		kmem = open("/dev/kmem", 0);
2656929Ssam 		if (kmem < 0) {
2666929Ssam 			perror("/dev/kmem");
2676929Ssam 			return;
2686929Ssam 		}
2696920Ssam 	}
2706929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2716929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
2726929Ssam 		performnlist = 1;
2736929Ssam 		return;
2746929Ssam 	}
2756920Ssam 	bzero((char *)&net, sizeof (net));
2766920Ssam 	net.sin_family = AF_INET;
2776961Ssam 	lookforinterfaces = 0;
2786920Ssam 	nets = 0;
2796929Ssam 	while (ifp) {
2806929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
2816929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
2826929Ssam 		  sizeof (ifstruct)) {
2836920Ssam 			perror("read");
2846961Ssam 			lookforinterfaces = performnlist = 1;
2856920Ssam 			break;
2866920Ssam 		}
2876929Ssam 		ifp = &ifstruct;
2886929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
2896961Ssam 			lookforinterfaces = 1;
2906929Ssam 	skip:
2916929Ssam 			ifp = ifp->if_next;
2926929Ssam 			continue;
2936929Ssam 		}
2946929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
2956929Ssam 			goto skip;
2966929Ssam 		/* ignore software loopback networks. */
2976920Ssam 		if (ifp->if_net == LOOPBACKNET)
2986920Ssam 			goto skip;
2996929Ssam 		/* check if we already know about this one */
3006929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
3016929Ssam 			nets++;
3026920Ssam 			goto skip;
3036920Ssam 		}
3046929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
3056929Ssam 		if (ifp == 0) {
3066929Ssam 			printf("routed: out of memory\n");
3076929Ssam 			break;
3086929Ssam 		}
3096929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3106920Ssam 
3116920Ssam 		/*
3126929Ssam 		 * Count the # of directly connected networks
3136929Ssam 		 * and point to point links which aren't looped
3146929Ssam 		 * back to ourself.  This is used below to
3156929Ssam 		 * decide if we should be a routing "supplier".
3166920Ssam 		 */
3176929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
3186929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
3196929Ssam 			nets++;
3206920Ssam 
3216929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
3226929Ssam 			dst = &ifp->if_dstaddr;
3236929Ssam 		else {
3246929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
3256929Ssam 			dst = (struct sockaddr *)&net;
3266929Ssam 		}
3276929Ssam 		next = ifp->if_next;
3286929Ssam 		ifp->if_next = ifnet;
3296929Ssam 		ifnet = ifp;
330*7130Swnj 		if (rtlookup(dst) == 0) {
331*7130Swnj 			struct rt_entry *rt;
332*7130Swnj 
3336929Ssam 			rtadd(dst, &ifp->if_addr, 0);
334*7130Swnj 			rt = rtlookup(dst);
335*7130Swnj 			if (rt)
336*7130Swnj 				rt->rt_state |= RTS_INTERFACE;
337*7130Swnj 		}
3386929Ssam 		ifp = next;
3396920Ssam 	}
3406920Ssam 	supplier = nets > 1;
341*7130Swnj 	getothers();
3426920Ssam }
3436920Ssam 
3446920Ssam /*
345*7130Swnj  * Look in a file for any gateways we should configure
346*7130Swnj  * outside the directly connected ones.  This is a kludge,
347*7130Swnj  * but until we can find out about gateways on the "other side"
348*7130Swnj  * of the ARPANET using GGP, it's a must.
349*7130Swnj  *
350*7130Swnj  * File format is one entry per line,
351*7130Swnj  *	destination gateway flags	(all hex #'s)
352*7130Swnj  *
353*7130Swnj  * We don't really know the distance to the gateway, so we
354*7130Swnj  * assume it's a neighbor.
355*7130Swnj  */
356*7130Swnj getothers()
357*7130Swnj {
358*7130Swnj 	struct sockaddr_in dst, gate;
359*7130Swnj 	FILE *fp;
360*7130Swnj 	struct rt_entry *rt;
361*7130Swnj 	char flags[132];
362*7130Swnj 
363*7130Swnj 	fp = fopen("/etc/gateways", "r");
364*7130Swnj 	if (fp == NULL)
365*7130Swnj 		return;
366*7130Swnj 	bzero((char *)&dst, sizeof (dst));
367*7130Swnj 	bzero((char *)&gate, sizeof (gate));
368*7130Swnj 	dst.sin_family = AF_INET;
369*7130Swnj 	gate.sin_family = AF_INET;
370*7130Swnj 	flags[0] = '\0';
371*7130Swnj 	while (fscanf(fp, "dst %x gateway %x %s\n", &dst.sin_addr.s_addr,
372*7130Swnj 	   &gate.sin_addr.s_addr, flags) != EOF) {
373*7130Swnj 		if (rt = rtlookup((struct sockaddr *)&dst)) {
374*7130Swnj 			flags[0] = '\0';
375*7130Swnj 			continue;
376*7130Swnj 		}
377*7130Swnj 		rtadd((struct sockaddr *)&dst,(struct sockaddr *)&gate,1);
378*7130Swnj 		rt = rtlookup(&dst);
379*7130Swnj 		if (strcmp(flags, "passive") == 0)
380*7130Swnj 			rt->rt_state |= RTS_PASSIVE;
381*7130Swnj 		flags[0] = '\0';
382*7130Swnj 	}
383*7130Swnj 	fclose(fp);
384*7130Swnj }
385*7130Swnj 
386*7130Swnj /*
3876920Ssam  * Send a request message to all directly
3886920Ssam  * connected hosts and networks.
3896920Ssam  */
3906920Ssam request()
3916920Ssam {
3926929Ssam 	register struct rip *msg = (struct rip *)packet;
3936920Ssam 
3946929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
3956929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
3966929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
3976929Ssam 	sendall();
3986920Ssam }
3996920Ssam 
4006920Ssam /*
4016920Ssam  * Broadcast a new, or modified, routing table entry
4026920Ssam  * to all directly connected hosts and networks.
4036920Ssam  */
4046920Ssam broadcast(entry)
4056920Ssam 	struct rt_entry *entry;
4066920Ssam {
4076929Ssam 	struct rip *msg = (struct rip *)packet;
4086929Ssam 
4096929Ssam 	log("broadcast", entry);
4106929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4116929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
4126929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
4136929Ssam 	sendall();
4146929Ssam }
4156929Ssam 
4166934Ssam /*
4176934Ssam  * Send "packet" to all neighbors.
4186934Ssam  */
4196929Ssam sendall()
4206929Ssam {
4216934Ssam 	register struct rthash *rh;
4226920Ssam 	register struct rt_entry *rt;
4236920Ssam 	register struct sockaddr *dst;
4246934Ssam 	struct rthash *base = hosthash;
4256920Ssam 	int doinghost = 1;
4266920Ssam 
4276920Ssam again:
4286920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4296920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
430*7130Swnj 		if ((rt->rt_state&RTS_PASSIVE) || rt->rt_metric > 0)
4316920Ssam 			continue;
4326920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4336920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4346920Ssam 		else
435*7130Swnj 			dst = &rt->rt_router;
436*7130Swnj 		(*afswitch[dst->sa_family].af_output)
437*7130Swnj 			(s, dst, sizeof (struct rip));
4386920Ssam 	}
4396920Ssam 	if (doinghost) {
4406929Ssam 		base = nethash;
4416920Ssam 		doinghost = 0;
4426920Ssam 		goto again;
4436920Ssam 	}
4446920Ssam }
4456920Ssam 
4466920Ssam /*
4476920Ssam  * Supply all directly connected neighbors with the
4486920Ssam  * current state of the routing tables.
4496920Ssam  */
4506920Ssam supplyall()
4516920Ssam {
4526920Ssam 	register struct rt_entry *rt;
4536934Ssam 	register struct rthash *rh;
4546920Ssam 	register struct sockaddr *dst;
4556934Ssam 	struct rthash *base = hosthash;
4566920Ssam 	int doinghost = 1;
4576920Ssam 
4586920Ssam again:
4596920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4606920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
461*7130Swnj 		if ((rt->rt_state&RTS_PASSIVE) ||
462*7130Swnj 		    (!(rt->rt_state&RTS_INTERFACE) && rt->rt_metric > 0))
4636920Ssam 			continue;
4646920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4656920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4666920Ssam 		else
467*7130Swnj 			dst = &rt->rt_router;
4686929Ssam 		log("supply", rt);
469*7130Swnj 		supply(rt->rt_state&RTS_INTERFACE ? snoroute : s, dst);
4706920Ssam 	}
4716920Ssam 	if (doinghost) {
4726920Ssam 		base = nethash;
4736920Ssam 		doinghost = 0;
4746920Ssam 		goto again;
4756920Ssam 	}
4766920Ssam }
4776920Ssam 
4786920Ssam /*
4796920Ssam  * Supply routing information to target "sa".
4806920Ssam  */
481*7130Swnj supply(s, sa)
482*7130Swnj 	int s;
4836920Ssam 	struct sockaddr *sa;
4846920Ssam {
4856920Ssam 	struct rip *msg = (struct rip *)packet;
4866920Ssam 	struct netinfo *n = msg->rip_nets;
4876934Ssam 	register struct rthash *rh;
4886920Ssam 	register struct rt_entry *rt;
4896934Ssam 	struct rthash *base = hosthash;
4906929Ssam 	int doinghost = 1, size;
4916920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4926920Ssam 
4936920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4946920Ssam again:
4956920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4966920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4976920Ssam 
4986920Ssam 		/*
4996920Ssam 		 * Flush packet out if not enough room for
5006920Ssam 		 * another routing table entry.
5016920Ssam 		 */
5026929Ssam 		size = (char *)n - packet;
5036929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
504*7130Swnj 			(*output)(s, sa, size);
5056920Ssam 			n = msg->rip_nets;
5066920Ssam 		}
5076920Ssam 		n->rip_dst = rt->rt_dst;
5086929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
5096929Ssam 		n++;
5106920Ssam 	}
5116920Ssam 	if (doinghost) {
5126920Ssam 		doinghost = 0;
5136920Ssam 		base = nethash;
5146920Ssam 		goto again;
5156920Ssam 	}
5166929Ssam 	if (n != msg->rip_nets)
517*7130Swnj 		(*output)(s, sa, (char *)n - packet);
5186920Ssam }
5196920Ssam 
5206920Ssam /*
5216920Ssam  * Respond to a routing info request.
5226920Ssam  */
5236920Ssam rip_respond(from, size)
5246920Ssam 	struct sockaddr *from;
5256920Ssam 	int size;
5266920Ssam {
5276920Ssam 	register struct rip *msg = (struct rip *)packet;
5286920Ssam 	struct netinfo *np = msg->rip_nets;
5296920Ssam 	struct rt_entry *rt;
5306920Ssam 	int newsize = 0;
5316920Ssam 
5326929Ssam 	size -= 4 * sizeof (char);
5336920Ssam 	while (size > 0) {
5346920Ssam 		if (size < sizeof (struct netinfo))
5356920Ssam 			break;
5366920Ssam 		size -= sizeof (struct netinfo);
5376920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
5386920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
539*7130Swnj 			supply(s, from);
5406920Ssam 			return;
5416920Ssam 		}
5426920Ssam 		rt = rtlookup(&np->rip_dst);
5436929Ssam 		np->rip_metric = rt == 0 ?
5446929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
5456920Ssam 		np++, newsize += sizeof (struct netinfo);
5466920Ssam 	}
5476920Ssam 	if (newsize > 0) {
5486920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5496920Ssam 		newsize += sizeof (int);
550*7130Swnj 		(*afswitch[from->sa_family].af_output)(s, from, newsize);
5516920Ssam 	}
5526920Ssam }
5536920Ssam 
5546920Ssam /*
5556920Ssam  * Handle an incoming routing packet.
5566920Ssam  */
5576920Ssam rip_input(from, size)
5586920Ssam 	struct sockaddr *from;
5596920Ssam 	int size;
5606920Ssam {
5616920Ssam 	register struct rip *msg = (struct rip *)packet;
5626920Ssam 	struct rt_entry *rt;
5636920Ssam 	struct netinfo *n;
5646920Ssam 
5656937Ssam 	switch (msg->rip_cmd) {
5666937Ssam 
5676937Ssam 	default:
5686920Ssam 		return;
5696920Ssam 
5706937Ssam 	case RIPCMD_REQUEST:
5716920Ssam 		rip_respond(from, size);
5726920Ssam 		return;
5736937Ssam 
5747029Ssam 	case RIPCMD_TRACEON:
5757029Ssam 	case RIPCMD_TRACEOFF:
5767029Ssam 		/* verify message came from a priviledged port */
5777029Ssam 		if ((*afswitch[from->sa_family].af_portcheck)(from) == 0)
5787029Ssam 			return;
5797029Ssam 		tracecmd(msg->rip_cmd, msg, size);
5807029Ssam 		return;
5817029Ssam 
5826937Ssam 	case RIPCMD_RESPONSE:
5837029Ssam 		/* verify message came from a router */
5846937Ssam 		if ((*afswitch[from->sa_family].af_portmatch)(from) == 0)
5856937Ssam 			return;
5866937Ssam 		break;
5876920Ssam 	}
5886920Ssam 
5896920Ssam 	/*
5906920Ssam 	 * Process updates.
5916920Ssam 	 * Extraneous information like Internet ports
5926920Ssam 	 * must first be purged from the sender's address for
5936920Ssam 	 * pattern matching below.
5946920Ssam 	 */
5956920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5966920Ssam 	if (trace)
5976934Ssam 		printf("input from %x\n",
5986934Ssam 			((struct sockaddr_in *)from)->sin_addr);
5996920Ssam 	/*
6006920Ssam 	 * If response packet is from ourselves, use it only
6016920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
6026920Ssam 	 * it as gospel (since it comes from the router) and
6036920Ssam 	 * unknowingly update the metric to show the outgoing
6046920Ssam 	 * cost (higher than our real cost).  I guess the protocol
6056920Ssam 	 * spec doesn't address this because Xerox Ethernets
6066920Ssam 	 * don't hear their own broadcasts?
6076920Ssam 	 */
6086920Ssam 	if (if_ifwithaddr(from)) {
6097011Ssam 		rt = rtfind(from);
6106920Ssam 		if (rt)
6116920Ssam 			rt->rt_timer = 0;
6126920Ssam 		return;
6136920Ssam 	}
6146929Ssam 	size -= 4 * sizeof (char);
6156920Ssam 	n = msg->rip_nets;
6166920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
6176920Ssam 		if (size < sizeof (struct netinfo))
6186920Ssam 			break;
6196920Ssam 		if (trace)
6206934Ssam 			printf("dst %x hc %d...",
6216934Ssam 				((struct sockaddr_in *)&n->rip_dst)->sin_addr,
6226920Ssam 				n->rip_metric);
6236920Ssam 		rt = rtlookup(&n->rip_dst);
6246920Ssam 
6256920Ssam 		/*
6266920Ssam 		 * Unknown entry, add it to the tables only if
6276920Ssam 		 * its interesting.
6286920Ssam 		 */
6296920Ssam 		if (rt == 0) {
6306920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
6316920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
6326920Ssam 			if (trace)
6336920Ssam 				printf("new\n");
6346920Ssam 			continue;
6356920Ssam 		}
6366920Ssam 
6376920Ssam 		if (trace)
6386920Ssam 			printf("ours: gate %x hc %d timer %d\n",
639*7130Swnj 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
6406934Ssam 			  rt->rt_metric, rt->rt_timer);
6416920Ssam 		/*
6426920Ssam 		 * Update the entry if one of the following is true:
6436920Ssam 		 *
6446920Ssam 		 * (1) The update came directly from the gateway.
6456920Ssam 		 * (2) A shorter path is provided.
6466920Ssam 		 * (3) The entry hasn't been updated in a while
6476929Ssam 		 *     and a path of equivalent cost is offered
6486929Ssam 		 *     (with the cost finite).
6496920Ssam 		 */
650*7130Swnj 		if (equal(from, &rt->rt_router) ||
6516920Ssam 		    rt->rt_metric > n->rip_metric ||
6526920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
6536929Ssam 		    rt->rt_metric == n->rip_metric &&
6546929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
6556920Ssam 			rtchange(rt, from, n->rip_metric);
6566920Ssam 			rt->rt_timer = 0;
6576920Ssam 		}
6586920Ssam 	}
6596920Ssam }
6606920Ssam 
6617029Ssam /*
6627029Ssam  * Handle tracing requests.
6637029Ssam  */
6647029Ssam tracecmd(cmd, msg, size)
6657029Ssam 	int cmd, size;
6667029Ssam 	struct rip *msg;
6677029Ssam {
6687029Ssam 	time_t t;
6697029Ssam 
6707029Ssam 	if (cmd == RIPCMD_TRACEOFF) {
6717029Ssam 		if (!trace)
6727029Ssam 			return;
6737029Ssam 		t = time(0);
6747029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
6757029Ssam 		fflush(stdout), fflush(stderr);
6767029Ssam 		if (ftrace)
6777029Ssam 			fclose(ftrace);
6787029Ssam 		(void) close(1), (void) close(2);
6797029Ssam 		trace = 0;
6807029Ssam 		return;
6817029Ssam 	}
6827029Ssam 	if (trace)
6837029Ssam 		return;
6847029Ssam 	packet[size] = '\0';
6857029Ssam 	ftrace = fopen(msg->rip_tracefile, "a");
6867029Ssam 	if (ftrace == NULL)
6877029Ssam 		return;
6887029Ssam 	(void) dup2(fileno(ftrace), 1);
6897029Ssam 	(void) dup2(fileno(ftrace), 2);
6907029Ssam 	trace = 1;
6917029Ssam 	t = time(0);
6927029Ssam 	printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
6937029Ssam }
6947029Ssam 
6956934Ssam rtinit()
6966934Ssam {
6976934Ssam 	register struct rthash *rh;
6986934Ssam 
6996934Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
7006934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
7016934Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
7026934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
7036934Ssam }
7046934Ssam 
7056920Ssam /*
7066920Ssam  * Lookup an entry to the appropriate dstination.
7076920Ssam  */
7086920Ssam struct rt_entry *
7096920Ssam rtlookup(dst)
7106920Ssam 	struct sockaddr *dst;
7116920Ssam {
7126920Ssam 	register struct rt_entry *rt;
7136934Ssam 	register struct rthash *rh;
7147011Ssam 	register int hash;
7156920Ssam 	struct afhash h;
7167011Ssam 	int doinghost = 1;
7176920Ssam 
7187011Ssam 	if (dst->sa_family >= AF_MAX)
7197011Ssam 		return (0);
7207011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
7217011Ssam 	hash = h.afh_hosthash;
7227011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
7237011Ssam again:
7247011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7257011Ssam 		if (rt->rt_hash != hash)
7267011Ssam 			continue;
7277011Ssam 		if (equal(&rt->rt_dst, dst))
7287011Ssam 			return (rt);
7297011Ssam 	}
7307011Ssam 	if (doinghost) {
7317011Ssam 		doinghost = 0;
7327011Ssam 		hash = h.afh_nethash;
7337011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7347011Ssam 		goto again;
7357011Ssam 	}
7367011Ssam 	return (0);
7377011Ssam }
7387011Ssam 
7397011Ssam /*
7407011Ssam  * Find an entry based on address "dst", as the kernel
7417011Ssam  * does in selecting routes.  This means we look first
7427011Ssam  * for a point to point link, settling for a route to
7437011Ssam  * the destination network if the former fails.
7447011Ssam  */
7457011Ssam struct rt_entry *
7467011Ssam rtfind(dst)
7477011Ssam 	struct sockaddr *dst;
7487011Ssam {
7497011Ssam 	register struct rt_entry *rt;
7507011Ssam 	register struct rthash *rh;
7517011Ssam 	register int hash;
7527011Ssam 	struct afhash h;
7537011Ssam 	int af = dst->sa_family;
7547011Ssam 	int doinghost = 1, (*match)();
7557011Ssam 
7566920Ssam 	if (af >= AF_MAX)
7576920Ssam 		return (0);
7586920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7596920Ssam 	hash = h.afh_hosthash;
7606920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
7616920Ssam 
7626920Ssam again:
7636920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7646920Ssam 		if (rt->rt_hash != hash)
7656920Ssam 			continue;
7666920Ssam 		if (doinghost) {
7676920Ssam 			if (equal(&rt->rt_dst, dst))
7686920Ssam 				return (rt);
7696920Ssam 		} else {
7706920Ssam 			if (rt->rt_dst.sa_family == af &&
7716920Ssam 			    (*match)(&rt->rt_dst, dst))
7726920Ssam 				return (rt);
7736920Ssam 		}
7746920Ssam 	}
7756920Ssam 	if (doinghost) {
7766920Ssam 		doinghost = 0;
7776920Ssam 		hash = h.afh_nethash;
7787011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7796920Ssam 		match = afswitch[af].af_netmatch;
7806920Ssam 		goto again;
7816920Ssam 	}
7826920Ssam 	return (0);
7836920Ssam }
7846920Ssam 
7856920Ssam /*
7866920Ssam  * Add a new entry.
7876920Ssam  */
7886920Ssam rtadd(dst, gate, metric)
7896920Ssam 	struct sockaddr *dst, *gate;
7906920Ssam 	short metric;
7916920Ssam {
7926920Ssam 	struct afhash h;
7936920Ssam 	register struct rt_entry *rt;
7946934Ssam 	struct rthash *rh;
7956920Ssam 	int af = dst->sa_family, flags, hash;
7966920Ssam 
7976920Ssam 	if (af >= AF_MAX)
7986920Ssam 		return;
7996920Ssam 	(*afswitch[af].af_hash)(dst, &h);
8006920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
8016920Ssam 	if (flags & RTF_HOST) {
8026920Ssam 		hash = h.afh_hosthash;
8036920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
8046920Ssam 	} else {
8056920Ssam 		hash = h.afh_nethash;
8066920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
8076920Ssam 	}
8086920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
8096920Ssam 	if (rt == 0)
8106920Ssam 		return;
8116920Ssam 	rt->rt_hash = hash;
8126920Ssam 	rt->rt_dst = *dst;
813*7130Swnj 	rt->rt_router = *gate;
8146920Ssam 	rt->rt_metric = metric;
8156920Ssam 	rt->rt_timer = 0;
8166920Ssam 	rt->rt_flags = RTF_UP | flags;
8176937Ssam 	rt->rt_state = 0;
818*7130Swnj 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
819*7130Swnj 	if (metric)
820*7130Swnj 		rt->rt_flags |= RTF_GATEWAY;
8216920Ssam 	insque(rt, rh);
8226929Ssam 	log("add", rt);
823*7130Swnj 	if (install)
8246937Ssam 		rt->rt_state |= RTS_ADDRT;
8256920Ssam }
8266920Ssam 
8276920Ssam /*
8286920Ssam  * Look to see if a change to an existing entry
8296920Ssam  * is warranted; if so, make it.
8306920Ssam  */
8316920Ssam rtchange(rt, gate, metric)
8326920Ssam 	struct rt_entry *rt;
8336920Ssam 	struct sockaddr *gate;
8346920Ssam 	short metric;
8356920Ssam {
8366920Ssam 	int change = 0;
8376920Ssam 
838*7130Swnj 	if (!equal(&rt->rt_router, gate)) {
839*7130Swnj 		rt->rt_newrouter = *gate;
8406920Ssam 		change++;
8416920Ssam 	}
8426920Ssam 	if (metric != rt->rt_metric) {
843*7130Swnj 		if (metric == 0)
844*7130Swnj 			rt->rt_flags |= RTF_GATEWAY;
8456920Ssam 		rt->rt_metric = metric;
8466920Ssam 		change++;
8476920Ssam 	}
8486920Ssam 	if (!change)
8496920Ssam 		return;
8506929Ssam 	log("change", rt);
851*7130Swnj 	if (install)
8526937Ssam 		rt->rt_state |= RTS_CHGRT;
8536920Ssam }
8546920Ssam 
8556920Ssam /*
8566920Ssam  * Delete a routing table entry.
8576920Ssam  */
8586920Ssam rtdelete(rt)
8596920Ssam 	struct rt_entry *rt;
8606920Ssam {
8616929Ssam 	log("delete", rt);
862*7130Swnj 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
863*7130Swnj 		perror("SIOCDELRT");
864*7130Swnj 	/* don't delete interface entries so we can poll them later */
865*7130Swnj 	if (rt->rt_state & RTS_INTERFACE)
866*7130Swnj 		return;
8676920Ssam 	remque(rt);
8686920Ssam 	free((char *)rt);
8696920Ssam }
8706920Ssam 
8716920Ssam log(operation, rt)
8726920Ssam 	char *operation;
8736920Ssam 	struct rt_entry *rt;
8746920Ssam {
8756920Ssam 	time_t t = time(0);
8766920Ssam 	struct sockaddr_in *dst, *gate;
8776937Ssam 	static struct bits {
8786920Ssam 		int	t_bits;
8796920Ssam 		char	*t_name;
8806937Ssam 	} flagbits[] = {
8816920Ssam 		{ RTF_UP,	"UP" },
882*7130Swnj 		{ RTF_GATEWAY,	"GATEWAY" },
8836920Ssam 		{ RTF_HOST,	"HOST" },
8846920Ssam 		{ 0 }
8856937Ssam 	}, statebits[] = {
8866937Ssam 		{ RTS_DELRT,	"DELETE" },
8876937Ssam 		{ RTS_CHGRT,	"CHANGE" },
888*7130Swnj 		{ RTS_PASSIVE,	"PASSIVE" },
889*7130Swnj 		{ RTS_INTERFACE,"INTERFACE" },
8906937Ssam 		{ 0 }
8916920Ssam 	};
8926937Ssam 	register struct bits *p;
8936920Ssam 	register int first;
8946920Ssam 	char *cp;
8956920Ssam 
8966929Ssam 	if (trace == 0)
8976929Ssam 		return;
8986920Ssam 	printf("%s ", operation);
8996920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
900*7130Swnj 	gate = (struct sockaddr_in *)&rt->rt_router;
9016937Ssam 	printf("dst %x, router %x, metric %d, flags",
9026920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
9036937Ssam 	cp = " %s";
9046937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
9056920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
9066920Ssam 			continue;
9076920Ssam 		printf(cp, p->t_name);
9086920Ssam 		if (first) {
9096920Ssam 			cp = "|%s";
9106920Ssam 			first = 0;
9116920Ssam 		}
9126920Ssam 	}
9136937Ssam 	printf(" state");
9146937Ssam 	cp = " %s";
9156937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
9166937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
9176937Ssam 			continue;
9186937Ssam 		printf(cp, p->t_name);
9196937Ssam 		if (first) {
9206937Ssam 			cp = "|%s";
9216937Ssam 			first = 0;
9226937Ssam 		}
9236937Ssam 	}
9246920Ssam 	putchar('\n');
9256920Ssam }
9266929Ssam 
9276934Ssam /*
9286934Ssam  * Find the interface with address "addr".
9296934Ssam  */
9306929Ssam struct ifnet *
9316929Ssam if_ifwithaddr(addr)
9326929Ssam 	struct sockaddr *addr;
9336929Ssam {
9346929Ssam 	register struct ifnet *ifp;
9356929Ssam 
9366929Ssam #define	same(a1, a2) \
9376929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
9386929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
9396929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
9406929Ssam 			continue;
9416929Ssam 		if (same(&ifp->if_addr, addr))
9426929Ssam 			break;
9436929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
9446929Ssam 		    same(&ifp->if_broadaddr, addr))
9456929Ssam 			break;
9466929Ssam 	}
9476929Ssam 	return (ifp);
9486929Ssam #undef same
9496929Ssam }
9506929Ssam 
9516934Ssam /*
9526934Ssam  * Find the interface with network imbedded in
9536934Ssam  * the sockaddr "addr".  Must use per-af routine
9546934Ssam  * look for match.
9556934Ssam  */
9566929Ssam struct ifnet *
9576929Ssam if_ifwithnet(addr)
9586929Ssam 	register struct sockaddr *addr;
9596929Ssam {
9606929Ssam 	register struct ifnet *ifp;
9616929Ssam 	register int af = addr->sa_family;
9626929Ssam 	register int (*netmatch)();
9636929Ssam 
9646929Ssam 	if (af >= AF_MAX)
9656929Ssam 		return (0);
9666929Ssam 	netmatch = afswitch[af].af_netmatch;
9676929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
9686929Ssam 		if (af != ifp->if_addr.sa_family)
9696929Ssam 			continue;
9706929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
9716929Ssam 			break;
9726929Ssam 	}
9736929Ssam 	return (ifp);
9746929Ssam }
9756929Ssam 
9766934Ssam /*
9776934Ssam  * Formulate an Internet address.
9786934Ssam  */
9796929Ssam struct in_addr
9806929Ssam if_makeaddr(net, host)
9816929Ssam 	int net, host;
9826929Ssam {
9836929Ssam 	u_long addr;
9846929Ssam 
9856929Ssam 	if (net < 128)
9866929Ssam 		addr = (net << 24) | host;
9876929Ssam 	else if (net < 65536)
9886929Ssam 		addr = (net << 16) | host;
9896929Ssam 	else
9906929Ssam 		addr = (net << 8) | host;
9916929Ssam #ifdef vax
9926929Ssam 	addr = htonl(addr);
9936929Ssam #endif
9946929Ssam 	return (*(struct in_addr *)&addr);
9956929Ssam }
996