xref: /csrg-svn/sbin/routed/routed.c (revision 6933)
16920Ssam #ifndef lint
2*6933Ssam static char sccsid[] = "@(#)routed.c	4.5 05/25/82";
36920Ssam #endif
46920Ssam 
56920Ssam #include <sys/param.h>
66920Ssam #include <sys/protosw.h>
76920Ssam #include <sys/ioctl.h>
86920Ssam #include <sys/socket.h>
96920Ssam #include <net/in.h>
106920Ssam #define	KERNEL
116920Ssam #include <net/route.h>
126920Ssam #include <net/if.h>
136920Ssam #include <errno.h>
146920Ssam #include <stdio.h>
156920Ssam #include <nlist.h>
166920Ssam #include <signal.h>
176920Ssam #include "rip.h"
186920Ssam #include "router.h"
196920Ssam 
206920Ssam #define	LOOPBACKNET	0177
216920Ssam /* casts to keep lint happy */
226920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
236920Ssam #define	remque(q)	_remque((caddr_t)q)
246920Ssam #define equal(a1, a2) \
256920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
266929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
276920Ssam 
286920Ssam struct nlist nl[] = {
296920Ssam #define	N_IFNET		0
306920Ssam 	{ "_ifnet" },
316920Ssam 	0,
326920Ssam };
336920Ssam 
346920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
356920Ssam 
366920Ssam int	s;
376929Ssam int	kmem = -1;
386920Ssam int	supplier;		/* process should supply updates */
396920Ssam int	initializing;		/* stem off broadcast() calls */
406920Ssam int	install = 0;		/* if 1 call kernel */
416929Ssam int	lookforinterfaces = 1;
426929Ssam int	performnlist = 1;
436920Ssam int	timeval;
446920Ssam int	timer();
456920Ssam int	cleanup();
466920Ssam int	trace = 0;
476920Ssam 
486920Ssam char	packet[MAXPACKETSIZE];
496920Ssam 
506920Ssam extern char *malloc();
516922Ssam extern int errno, exit();
526920Ssam 
536920Ssam main(argc, argv)
546920Ssam 	int argc;
556920Ssam 	char *argv[];
566920Ssam {
576920Ssam 	int cc;
586920Ssam 	struct sockaddr from;
596920Ssam 
606920Ssam 	{   int t = open("/dev/tty", 2);
616920Ssam 	    if (t >= 0) {
626920Ssam 		ioctl(t, TIOCNOTTY, 0);
636920Ssam 		close(t);
646920Ssam 	    }
656920Ssam 	}
666920Ssam 	if (trace) {
676929Ssam 		(void) freopen("/etc/routerlog", "a", stdout);
686929Ssam 		(void) dup2(fileno(stdout), 2);
696920Ssam 		setbuf(stdout, NULL);
706920Ssam 	}
716920Ssam #ifdef vax
726920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
736920Ssam #endif
746920Ssam again:
756920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
766920Ssam 	if (s < 0) {
776920Ssam 		perror("socket");
786920Ssam 		sleep(30);
796920Ssam 		goto again;
806920Ssam 	}
816920Ssam 	rtinit();
826920Ssam 	getothers();
836929Ssam 	initializing = 1;
846920Ssam 	getinterfaces();
856929Ssam 	initializing = 0;
866920Ssam 	request();
876920Ssam 
886920Ssam 	argv++, argc--;
896920Ssam 	while (argc > 0) {
906920Ssam 		if (strcmp(*argv, "-s") == 0)
916929Ssam 			supplier = 1;
926920Ssam 		else if (strcmp(*argv, "-q") == 0)
936920Ssam 			supplier = 0;
946920Ssam 		argv++, argc--;
956920Ssam 	}
966920Ssam 	sigset(SIGALRM, timer);
976929Ssam 	timer();
986920Ssam 
996920Ssam 	/*
1006920Ssam 	 * Listen for routing packets
1016920Ssam 	 */
1026920Ssam 	for (;;) {
1036920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1046920Ssam 		if (cc <= 0) {
1056920Ssam 			if (cc < 0 && errno != EINTR)
1066920Ssam 				perror("receive");
1076920Ssam 			continue;
1086920Ssam 		}
1096920Ssam 		sighold(SIGALRM);
1106920Ssam 		rip_input(&from, cc);
1116920Ssam 		sigrelse(SIGALRM);
1126920Ssam 	}
1136920Ssam }
1146920Ssam 
1156920Ssam /*
1166920Ssam  * Look in a file for any gateways we should configure
1176920Ssam  * outside the directly connected ones.  This is a kludge,
1186920Ssam  * but until we can find out about gateways on the "other side"
1196920Ssam  * of the ARPANET using GGP, it's a must.
1206920Ssam  *
1216920Ssam  * We don't really know the distance to the gateway, so we
1226920Ssam  * assume it's a neighbor.
1236920Ssam  */
1246920Ssam getothers()
1256920Ssam {
1266920Ssam 	struct sockaddr_in dst, gate;
1276920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
1286920Ssam 	struct rt_entry *rt;
1296920Ssam 
1306920Ssam 	if (fp == NULL)
1316920Ssam 		return;
1326920Ssam 	bzero((char *)&dst, sizeof (dst));
1336920Ssam 	bzero((char *)&gate, sizeof (gate));
1346920Ssam 	dst.sin_family = AF_INET;
1356920Ssam 	gate.sin_family = AF_INET;
1366920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
1376920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
1386920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
1396920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
1406920Ssam 		if (rt)
1416920Ssam 			rt->rt_flags |= RTF_SILENT;
1426920Ssam 	}
1436920Ssam 	fclose(fp);
1446920Ssam }
1456920Ssam 
1466929Ssam /*
1476929Ssam  * Timer routine:
1486929Ssam  *
1496929Ssam  * o handle timers on table entries,
1506929Ssam  * o invalidate entries which haven't been updated in a while,
1516929Ssam  * o delete entries which are too old,
1526929Ssam  * o retry ioctl's which weren't successful the first
1536929Ssam  *   time due to the kernel entry being busy
1546929Ssam  * o if we're an internetwork router, supply routing updates
1556929Ssam  *   periodically
1566929Ssam  */
1576929Ssam timer()
1586920Ssam {
1596929Ssam 	register struct rt_hash *rh;
1606929Ssam 	register struct rt_entry *rt;
1616929Ssam 	struct rt_hash *base = hosthash;
1626929Ssam 	int doinghost = 1;
1636920Ssam 
1646929Ssam 	if (trace)
1656929Ssam 		printf(">>> time %d >>>\n", timeval);
1666929Ssam again:
1676929Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
1686929Ssam 		rt = rh->rt_forw;
1696929Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1706920Ssam 
1716929Ssam 			/*
1726929Ssam 			 * If the host is indicated to be
1736929Ssam 			 * "silent" (i.e. it's one we got
1746929Ssam 			 * from the initialization file),
1756929Ssam 			 * don't time out it's entry.
1766929Ssam 			 */
177*6933Ssam 			if ((rt->rt_flags & RTF_SILENT) == 0)
178*6933Ssam 				rt->rt_timer += TIMER_RATE;
1796929Ssam 			log("", rt);
1806920Ssam 
1816929Ssam 			/*
1826929Ssam 			 * If the entry should be deleted
1836929Ssam 			 * attempt to do so and reclaim space.
1846929Ssam 			 */
1856929Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
1866929Ssam 			  (rt->rt_flags & RTF_DELRT)) {
1876929Ssam 				rt = rt->rt_forw;
1886929Ssam 				rtdelete(rt->rt_back);
1896929Ssam 				rt = rt->rt_back;
1906929Ssam 				continue;
1916929Ssam 			}
1926920Ssam 
1936929Ssam 			/*
1946929Ssam 			 * If we haven't heard from the router
1956929Ssam 			 * in a long time indicate the route is
1966929Ssam 			 * hard to reach.
1976929Ssam 			 */
1986929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
1996929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
2006929Ssam 			if (rt->rt_flags & RTF_CHGRT)
2016929Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_hash) ||
2026929Ssam 				  --rt->rt_retry == 0)
2036929Ssam 					rt->rt_flags &= ~RTF_CHGRT;
2046920Ssam 
2056929Ssam 			/*
2066929Ssam 			 * Try to add the route to the kernel tables.
2076929Ssam 			 * If this fails because the entry already exists
2086929Ssam 			 * (perhaps because someone manually added it)
2096929Ssam 			 * change the add to a change.  If the operation
2106929Ssam 			 * fails otherwise (likely because the entry is
2116929Ssam 			 * in use), retry the operation a few more times.
2126929Ssam 			 */
2136929Ssam 			if (rt->rt_flags & RTF_ADDRT) {
2146929Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_hash)) {
2156929Ssam 					if (errno == EEXIST) {
2166929Ssam 						rt->rt_flags &= ~RTF_ADDRT;
2176929Ssam 						rt->rt_flags |= RTF_CHGRT;
2186929Ssam 						rt->rt_retry =
2196929Ssam 						    (EXPIRE_TIME/TIMER_RATE);
2206929Ssam 						continue;
2216929Ssam 					}
2226929Ssam 					if (--rt->rt_retry)
2236929Ssam 						continue;
2246929Ssam 				}
2256929Ssam 				rt->rt_flags &= ~RTF_ADDRT;
2266929Ssam 			}
2276929Ssam 		}
2286929Ssam 	}
2296929Ssam 	if (doinghost) {
2306929Ssam 		doinghost = 0;
2316929Ssam 		base = nethash;
2326929Ssam 		goto again;
2336929Ssam 	}
2346929Ssam 	timeval += TIMER_RATE;
2356929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
2366929Ssam 		getinterfaces();
2376929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
2386929Ssam 		supplyall();
2396929Ssam 	if (trace)
2406929Ssam 		printf("<<< time %d <<<\n", timeval);
2416929Ssam 	alarm(TIMER_RATE);
2426920Ssam }
2436920Ssam 
2446920Ssam /*
2456920Ssam  * Find the network interfaces attached to this machine.
2466929Ssam  * The info is used to:
2476920Ssam  *
2486920Ssam  * (1) initialize the routing tables, as done by the kernel.
2496920Ssam  * (2) ignore incoming packets we send.
2506920Ssam  * (3) figure out broadcast capability and addresses.
2516920Ssam  * (4) figure out if we're an internetwork gateway.
2526920Ssam  *
2536920Ssam  * We don't handle anything but Internet addresses.
2546920Ssam  */
2556920Ssam getinterfaces()
2566920Ssam {
2576929Ssam 	struct ifnet *ifp;
2586929Ssam 	struct ifnet ifstruct, *next;
2596920Ssam 	struct sockaddr_in net;
2606929Ssam 	register struct sockaddr *dst;
2616920Ssam 	int nets;
2626920Ssam 
2636929Ssam 	if (performnlist) {
2646929Ssam 		nlist("/vmunix", nl);
2656929Ssam 		if (nl[N_IFNET].n_value == 0) {
2666929Ssam 			performnlist++;
2676929Ssam 			printf("ifnet: not in namelist\n");
2686929Ssam 			return;
2696929Ssam 		}
2706929Ssam 		performnlist = 0;
2716920Ssam 	}
2726920Ssam 	if (kmem < 0) {
2736929Ssam 		kmem = open("/dev/kmem", 0);
2746929Ssam 		if (kmem < 0) {
2756929Ssam 			perror("/dev/kmem");
2766929Ssam 			return;
2776929Ssam 		}
2786920Ssam 	}
2796929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2806929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
2816929Ssam 		performnlist = 1;
2826929Ssam 		return;
2836929Ssam 	}
2846920Ssam 	bzero((char *)&net, sizeof (net));
2856920Ssam 	net.sin_family = AF_INET;
2866920Ssam 	nets = 0;
2876929Ssam 	while (ifp) {
2886929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
2896929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
2906929Ssam 		  sizeof (ifstruct)) {
2916920Ssam 			perror("read");
2926929Ssam 			performnlist = 1;
2936920Ssam 			break;
2946920Ssam 		}
2956929Ssam 		ifp = &ifstruct;
2966929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
2976929Ssam 			lookforinterfaces++;
2986929Ssam 	skip:
2996929Ssam 			ifp = ifp->if_next;
3006929Ssam 			continue;
3016929Ssam 		}
3026929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
3036929Ssam 			goto skip;
3046929Ssam 		/* ignore software loopback networks. */
3056920Ssam 		if (ifp->if_net == LOOPBACKNET)
3066920Ssam 			goto skip;
3076929Ssam 		/* check if we already know about this one */
3086929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
3096929Ssam 			nets++;
3106920Ssam 			goto skip;
3116920Ssam 		}
3126929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
3136929Ssam 		if (ifp == 0) {
3146929Ssam 			printf("routed: out of memory\n");
3156929Ssam 			break;
3166929Ssam 		}
3176929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3186920Ssam 
3196920Ssam 		/*
3206929Ssam 		 * Count the # of directly connected networks
3216929Ssam 		 * and point to point links which aren't looped
3226929Ssam 		 * back to ourself.  This is used below to
3236929Ssam 		 * decide if we should be a routing "supplier".
3246920Ssam 		 */
3256929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
3266929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
3276929Ssam 			nets++;
3286920Ssam 
3296929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
3306929Ssam 			dst = &ifp->if_dstaddr;
3316929Ssam 		else {
3326929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
3336929Ssam 			dst = (struct sockaddr *)&net;
3346929Ssam 		}
3356929Ssam 		next = ifp->if_next;
3366929Ssam 		ifp->if_next = ifnet;
3376929Ssam 		ifnet = ifp;
3386929Ssam 		if (rtlookup(dst) == 0)
3396929Ssam 			rtadd(dst, &ifp->if_addr, 0);
3406929Ssam 		ifp = next;
3416920Ssam 	}
3426920Ssam 	supplier = nets > 1;
3436920Ssam }
3446920Ssam 
3456920Ssam /*
3466920Ssam  * Send a request message to all directly
3476920Ssam  * connected hosts and networks.
3486920Ssam  */
3496920Ssam request()
3506920Ssam {
3516929Ssam 	register struct rip *msg = (struct rip *)packet;
3526920Ssam 
3536929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
3546929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
3556929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
3566929Ssam 	sendall();
3576920Ssam }
3586920Ssam 
3596920Ssam /*
3606920Ssam  * Broadcast a new, or modified, routing table entry
3616920Ssam  * to all directly connected hosts and networks.
3626920Ssam  */
3636920Ssam broadcast(entry)
3646920Ssam 	struct rt_entry *entry;
3656920Ssam {
3666929Ssam 	struct rip *msg = (struct rip *)packet;
3676929Ssam 
3686929Ssam 	log("broadcast", entry);
3696929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
3706929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
3716929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
3726929Ssam 	sendall();
3736929Ssam }
3746929Ssam 
3756929Ssam sendall()
3766929Ssam {
3776920Ssam 	register struct rt_hash *rh;
3786920Ssam 	register struct rt_entry *rt;
3796920Ssam 	register struct sockaddr *dst;
3806920Ssam 	struct rt_hash *base = hosthash;
3816920Ssam 	int doinghost = 1;
3826920Ssam 
3836920Ssam again:
3846920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3856920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3866920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
3876920Ssam 			continue;
3886920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
3896920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
3906920Ssam 		else
3916920Ssam 			dst = &rt->rt_gateway;
3926920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
3936920Ssam 	}
3946920Ssam 	if (doinghost) {
3956929Ssam 		base = nethash;
3966920Ssam 		doinghost = 0;
3976920Ssam 		goto again;
3986920Ssam 	}
3996920Ssam }
4006920Ssam 
4016920Ssam /*
4026920Ssam  * Supply all directly connected neighbors with the
4036920Ssam  * current state of the routing tables.
4046920Ssam  */
4056920Ssam supplyall()
4066920Ssam {
4076920Ssam 	register struct rt_entry *rt;
4086920Ssam 	register struct rt_hash *rh;
4096920Ssam 	register struct sockaddr *dst;
4106920Ssam 	struct rt_hash *base = hosthash;
4116920Ssam 	int doinghost = 1;
4126920Ssam 
4136920Ssam again:
4146920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4156920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4166920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
4176920Ssam 			continue;
4186920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4196920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4206920Ssam 		else
4216920Ssam 			dst = &rt->rt_gateway;
4226929Ssam 		log("supply", rt);
4236920Ssam 		supply(dst);
4246920Ssam 	}
4256920Ssam 	if (doinghost) {
4266920Ssam 		base = nethash;
4276920Ssam 		doinghost = 0;
4286920Ssam 		goto again;
4296920Ssam 	}
4306920Ssam }
4316920Ssam 
4326920Ssam /*
4336920Ssam  * Supply routing information to target "sa".
4346920Ssam  */
4356920Ssam supply(sa)
4366920Ssam 	struct sockaddr *sa;
4376920Ssam {
4386920Ssam 	struct rip *msg = (struct rip *)packet;
4396920Ssam 	struct netinfo *n = msg->rip_nets;
4406920Ssam 	register struct rt_hash *rh;
4416920Ssam 	register struct rt_entry *rt;
4426920Ssam 	struct rt_hash *base = hosthash;
4436929Ssam 	int doinghost = 1, size;
4446920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4456920Ssam 
4466920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4476920Ssam again:
4486920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4496920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4506920Ssam 
4516920Ssam 		/*
4526920Ssam 		 * Flush packet out if not enough room for
4536920Ssam 		 * another routing table entry.
4546920Ssam 		 */
4556929Ssam 		size = (char *)n - packet;
4566929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4576929Ssam 			(*output)(sa, size);
4586920Ssam 			n = msg->rip_nets;
4596920Ssam 		}
4606920Ssam 		n->rip_dst = rt->rt_dst;
4616929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4626929Ssam 		n++;
4636920Ssam 	}
4646920Ssam 	if (doinghost) {
4656920Ssam 		doinghost = 0;
4666920Ssam 		base = nethash;
4676920Ssam 		goto again;
4686920Ssam 	}
4696929Ssam 	if (n != msg->rip_nets)
4706929Ssam 		(*output)(sa, (char *)n - packet);
4716920Ssam }
4726920Ssam 
4736920Ssam /*
4746920Ssam  * Respond to a routing info request.
4756920Ssam  */
4766920Ssam rip_respond(from, size)
4776920Ssam 	struct sockaddr *from;
4786920Ssam 	int size;
4796920Ssam {
4806920Ssam 	register struct rip *msg = (struct rip *)packet;
4816920Ssam 	struct netinfo *np = msg->rip_nets;
4826920Ssam 	struct rt_entry *rt;
4836920Ssam 	int newsize = 0;
4846920Ssam 
4856929Ssam 	size -= 4 * sizeof (char);
4866920Ssam 	while (size > 0) {
4876920Ssam 		if (size < sizeof (struct netinfo))
4886920Ssam 			break;
4896920Ssam 		size -= sizeof (struct netinfo);
4906920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
4916920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
4926920Ssam 			supply(from);
4936920Ssam 			return;
4946920Ssam 		}
4956920Ssam 		rt = rtlookup(&np->rip_dst);
4966929Ssam 		np->rip_metric = rt == 0 ?
4976929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
4986920Ssam 		np++, newsize += sizeof (struct netinfo);
4996920Ssam 	}
5006920Ssam 	if (newsize > 0) {
5016920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5026920Ssam 		newsize += sizeof (int);
5036920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
5046920Ssam 	}
5056920Ssam }
5066920Ssam 
5076920Ssam /*
5086920Ssam  * Handle an incoming routing packet.
5096920Ssam  */
5106920Ssam rip_input(from, size)
5116920Ssam 	struct sockaddr *from;
5126920Ssam 	int size;
5136920Ssam {
5146920Ssam 	register struct rip *msg = (struct rip *)packet;
5156920Ssam 	struct rt_entry *rt;
5166920Ssam 	struct netinfo *n;
5176920Ssam 
5186920Ssam 	if (msg->rip_cmd != RIPCMD_RESPONSE &&
5196920Ssam 	    msg->rip_cmd != RIPCMD_REQUEST)
5206920Ssam 		return;
5216920Ssam 
5226920Ssam 	if (msg->rip_cmd == RIPCMD_RESPONSE &&
5236920Ssam 	    (*afswitch[from->sa_family].af_portmatch)(from) == 0)
5246920Ssam 		return;
5256920Ssam 	if (msg->rip_cmd == RIPCMD_REQUEST) {
5266920Ssam 		rip_respond(from, size);
5276920Ssam 		return;
5286920Ssam 	}
5296920Ssam 
5306920Ssam 	/*
5316920Ssam 	 * Process updates.
5326920Ssam 	 * Extraneous information like Internet ports
5336920Ssam 	 * must first be purged from the sender's address for
5346920Ssam 	 * pattern matching below.
5356920Ssam 	 */
5366920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5376920Ssam 	if (trace)
5386920Ssam 		printf("input from %x\n", from->sin_addr);
5396920Ssam 	/*
5406920Ssam 	 * If response packet is from ourselves, use it only
5416920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5426920Ssam 	 * it as gospel (since it comes from the router) and
5436920Ssam 	 * unknowingly update the metric to show the outgoing
5446920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5456920Ssam 	 * spec doesn't address this because Xerox Ethernets
5466920Ssam 	 * don't hear their own broadcasts?
5476920Ssam 	 */
5486920Ssam 	if (if_ifwithaddr(from)) {
5496920Ssam 		rt = rtlookup(from);
5506920Ssam 		if (rt)
5516920Ssam 			rt->rt_timer = 0;
5526920Ssam 		return;
5536920Ssam 	}
5546929Ssam 	size -= 4 * sizeof (char);
5556920Ssam 	n = msg->rip_nets;
5566920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5576920Ssam 		if (size < sizeof (struct netinfo))
5586920Ssam 			break;
5596920Ssam 		if (trace)
5606920Ssam 			printf("dst %x hc %d...", n->rip_dst.sin_addr,
5616920Ssam 				n->rip_metric);
5626920Ssam 		rt = rtlookup(&n->rip_dst);
5636920Ssam 
5646920Ssam 		/*
5656920Ssam 		 * Unknown entry, add it to the tables only if
5666920Ssam 		 * its interesting.
5676920Ssam 		 */
5686920Ssam 		if (rt == 0) {
5696920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5706920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5716920Ssam 			if (trace)
5726920Ssam 				printf("new\n");
5736920Ssam 			continue;
5746920Ssam 		}
5756920Ssam 
5766920Ssam 		if (trace)
5776920Ssam 			printf("ours: gate %x hc %d timer %d\n",
5786920Ssam 			rt->rt_gateway.sin_addr,
5796920Ssam 			rt->rt_metric, rt->rt_timer);
5806920Ssam 		/*
5816920Ssam 		 * Update the entry if one of the following is true:
5826920Ssam 		 *
5836920Ssam 		 * (1) The update came directly from the gateway.
5846920Ssam 		 * (2) A shorter path is provided.
5856920Ssam 		 * (3) The entry hasn't been updated in a while
5866929Ssam 		 *     and a path of equivalent cost is offered
5876929Ssam 		 *     (with the cost finite).
5886920Ssam 		 */
5896920Ssam 		if (equal(from, &rt->rt_gateway) ||
5906920Ssam 		    rt->rt_metric > n->rip_metric ||
5916920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
5926929Ssam 		    rt->rt_metric == n->rip_metric &&
5936929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
5946920Ssam 			rtchange(rt, from, n->rip_metric);
5956920Ssam 			rt->rt_timer = 0;
5966920Ssam 		}
5976920Ssam 	}
5986920Ssam }
5996920Ssam 
6006920Ssam /*
6016920Ssam  * Lookup an entry to the appropriate dstination.
6026920Ssam  */
6036920Ssam struct rt_entry *
6046920Ssam rtlookup(dst)
6056920Ssam 	struct sockaddr *dst;
6066920Ssam {
6076920Ssam 	register struct rt_entry *rt;
6086920Ssam 	register struct rt_hash *rh;
6096920Ssam 	register int hash, (*match)();
6106920Ssam 	struct afhash h;
6116920Ssam 	int af = dst->sa_family, doinghost = 1;
6126920Ssam 
6136920Ssam 	if (af >= AF_MAX)
6146920Ssam 		return (0);
6156920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6166920Ssam 	hash = h.afh_hosthash;
6176920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6186920Ssam 
6196920Ssam again:
6206920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6216920Ssam 		if (rt->rt_hash != hash)
6226920Ssam 			continue;
6236920Ssam 		if (doinghost) {
6246920Ssam 			if (equal(&rt->rt_dst, dst))
6256920Ssam 				return (rt);
6266920Ssam 		} else {
6276920Ssam 			if (rt->rt_dst.sa_family == af &&
6286920Ssam 			    (*match)(&rt->rt_dst, dst))
6296920Ssam 				return (rt);
6306920Ssam 		}
6316920Ssam 	}
6326920Ssam 	if (doinghost) {
6336920Ssam 		doinghost = 0;
6346920Ssam 		hash = h.afh_nethash;
6356920Ssam 		match = afswitch[af].af_netmatch;
6366920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6376920Ssam 		goto again;
6386920Ssam 	}
6396920Ssam 	return (0);
6406920Ssam }
6416920Ssam 
6426920Ssam rtinit()
6436920Ssam {
6446920Ssam 	register struct rt_hash *rh;
6456920Ssam 
6466920Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6476920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6486920Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6496920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6506920Ssam }
6516920Ssam 
6526920Ssam /*
6536920Ssam  * Add a new entry.
6546920Ssam  */
6556920Ssam rtadd(dst, gate, metric)
6566920Ssam 	struct sockaddr *dst, *gate;
6576920Ssam 	short metric;
6586920Ssam {
6596920Ssam 	struct afhash h;
6606920Ssam 	register struct rt_entry *rt;
6616920Ssam 	struct rt_hash *rh;
6626920Ssam 	int af = dst->sa_family, flags, hash;
6636920Ssam 
6646920Ssam 	if (af >= AF_MAX)
6656920Ssam 		return;
6666920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6676920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6686920Ssam 	if (flags & RTF_HOST) {
6696920Ssam 		hash = h.afh_hosthash;
6706920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6716920Ssam 	} else {
6726920Ssam 		hash = h.afh_nethash;
6736920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6746920Ssam 	}
6756920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6766920Ssam 	if (rt == 0)
6776920Ssam 		return;
6786920Ssam 	rt->rt_hash = hash;
6796920Ssam 	rt->rt_dst = *dst;
6806920Ssam 	rt->rt_gateway = *gate;
6816920Ssam 	rt->rt_metric = metric;
6826920Ssam 	rt->rt_timer = 0;
6836920Ssam 	rt->rt_flags = RTF_UP | flags;
6846920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
6856920Ssam 	if (metric == 0)
6866920Ssam 		rt->rt_flags |= RTF_DIRECT;
6876920Ssam 	insque(rt, rh);
6886929Ssam 	log("add", rt);
6896920Ssam 	if (initializing)
6906920Ssam 		return;
6916920Ssam 	if (supplier)
6926920Ssam 		broadcast(rt);
6936920Ssam 	if (install) {
6946920Ssam 		rt->rt_flags |= RTF_ADDRT;
6956920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
6966920Ssam 	}
6976920Ssam }
6986920Ssam 
6996920Ssam /*
7006920Ssam  * Look to see if a change to an existing entry
7016920Ssam  * is warranted; if so, make it.
7026920Ssam  */
7036920Ssam rtchange(rt, gate, metric)
7046920Ssam 	struct rt_entry *rt;
7056920Ssam 	struct sockaddr *gate;
7066920Ssam 	short metric;
7076920Ssam {
7086920Ssam 	int change = 0;
7096920Ssam 
7106920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
7116920Ssam 		rt->rt_gateway = *gate;
7126920Ssam 		change++;
7136920Ssam 	}
7146920Ssam 
7156920Ssam 	/*
7166920Ssam 	 * If the hop count has changed, adjust
7176920Ssam 	 * the flags in the routing table entry accordingly.
7186920Ssam 	 */
7196920Ssam 	if (metric != rt->rt_metric) {
7206920Ssam 		if (rt->rt_metric == 0)
7216920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7226920Ssam 		rt->rt_metric = metric;
7236920Ssam 		if (metric >= HOPCNT_INFINITY)
7246920Ssam 			rt->rt_flags &= ~RTF_UP;
7256920Ssam 		else
7266920Ssam 			rt->rt_flags |= RTF_UP;
7276920Ssam 		change++;
7286920Ssam 	}
7296920Ssam 
7306920Ssam 	if (!change)
7316920Ssam 		return;
7326920Ssam 	if (supplier)
7336920Ssam 		broadcast(rt);
7346929Ssam 	log("change", rt);
7356920Ssam 	if (install) {
7366920Ssam 		rt->rt_flags |= RTF_CHGRT;
7376920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7386920Ssam 	}
7396920Ssam }
7406920Ssam 
7416920Ssam /*
7426920Ssam  * Delete a routing table entry.
7436920Ssam  */
7446920Ssam rtdelete(rt)
7456920Ssam 	struct rt_entry *rt;
7466920Ssam {
7476929Ssam 	log("delete", rt);
7486920Ssam 	if (install)
7496920Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_hash) &&
7506920Ssam 		  errno == EBUSY)
7516920Ssam 			rt->rt_flags |= RTF_DELRT;
7526920Ssam 	remque(rt);
7536920Ssam 	free((char *)rt);
7546920Ssam }
7556920Ssam 
7566920Ssam log(operation, rt)
7576920Ssam 	char *operation;
7586920Ssam 	struct rt_entry *rt;
7596920Ssam {
7606920Ssam 	time_t t = time(0);
7616920Ssam 	struct sockaddr_in *dst, *gate;
7626920Ssam 	static struct flagbits {
7636920Ssam 		int	t_bits;
7646920Ssam 		char	*t_name;
7656920Ssam 	} bits[] = {
7666920Ssam 		{ RTF_UP,	"UP" },
7676920Ssam 		{ RTF_DIRECT,	"DIRECT" },
7686920Ssam 		{ RTF_HOST,	"HOST" },
7696920Ssam 		{ RTF_DELRT,	"DELETE" },
7706920Ssam 		{ RTF_CHGRT,	"CHANGE" },
7716920Ssam 		{ RTF_SILENT,	"SILENT" },
7726920Ssam 		{ 0 }
7736920Ssam 	};
7746920Ssam 	register struct flagbits *p;
7756920Ssam 	register int first;
7766920Ssam 	char *cp;
7776920Ssam 
7786929Ssam 	if (trace == 0)
7796929Ssam 		return;
7806920Ssam 	printf("%s ", operation);
7816920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
7826920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
7836920Ssam 	printf("dst %x, router %x, metric %d, flags ",
7846920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
7856920Ssam 	cp = "%s";
7866920Ssam 	for (first = 1, p = bits; p->t_bits > 0; p++) {
7876920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
7886920Ssam 			continue;
7896920Ssam 		printf(cp, p->t_name);
7906920Ssam 		if (first) {
7916920Ssam 			cp = "|%s";
7926920Ssam 			first = 0;
7936920Ssam 		}
7946920Ssam 	}
7956920Ssam 	putchar('\n');
7966920Ssam }
7976929Ssam 
7986929Ssam struct ifnet *
7996929Ssam if_ifwithaddr(addr)
8006929Ssam 	struct sockaddr *addr;
8016929Ssam {
8026929Ssam 	register struct ifnet *ifp;
8036929Ssam 
8046929Ssam #define	same(a1, a2) \
8056929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8066929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8076929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
8086929Ssam 			continue;
8096929Ssam 		if (same(&ifp->if_addr, addr))
8106929Ssam 			break;
8116929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
8126929Ssam 		    same(&ifp->if_broadaddr, addr))
8136929Ssam 			break;
8146929Ssam 	}
8156929Ssam 	return (ifp);
8166929Ssam #undef same
8176929Ssam }
8186929Ssam 
8196929Ssam struct ifnet *
8206929Ssam if_ifwithnet(addr)
8216929Ssam 	register struct sockaddr *addr;
8226929Ssam {
8236929Ssam 	register struct ifnet *ifp;
8246929Ssam 	register int af = addr->sa_family;
8256929Ssam 	register int (*netmatch)();
8266929Ssam 
8276929Ssam 	if (af >= AF_MAX)
8286929Ssam 		return (0);
8296929Ssam 	netmatch = afswitch[af].af_netmatch;
8306929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8316929Ssam 		if (af != ifp->if_addr.sa_family)
8326929Ssam 			continue;
8336929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
8346929Ssam 			break;
8356929Ssam 	}
8366929Ssam 	return (ifp);
8376929Ssam }
8386929Ssam 
8396929Ssam struct in_addr
8406929Ssam if_makeaddr(net, host)
8416929Ssam 	int net, host;
8426929Ssam {
8436929Ssam 	u_long addr;
8446929Ssam 
8456929Ssam 	if (net < 128)
8466929Ssam 		addr = (net << 24) | host;
8476929Ssam 	else if (net < 65536)
8486929Ssam 		addr = (net << 16) | host;
8496929Ssam 	else
8506929Ssam 		addr = (net << 8) | host;
8516929Ssam #ifdef vax
8526929Ssam 	addr = htonl(addr);
8536929Ssam #endif
8546929Ssam 	return (*(struct in_addr *)&addr);
8556929Ssam }
856