xref: /csrg-svn/sbin/routed/routed.c (revision 6937)
16920Ssam #ifndef lint
2*6937Ssam static char sccsid[] = "@(#)routed.c	4.7 05/26/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>
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 
506934Ssam struct in_addr if_makeaddr();
516934Ssam struct ifnet *if_ifwithaddr(), *if_ifwithnet();
526920Ssam extern char *malloc();
536922Ssam extern int errno, exit();
546920Ssam 
556920Ssam main(argc, argv)
566920Ssam 	int argc;
576920Ssam 	char *argv[];
586920Ssam {
596920Ssam 	int cc;
606920Ssam 	struct sockaddr from;
616920Ssam 
626920Ssam 	{   int t = open("/dev/tty", 2);
636920Ssam 	    if (t >= 0) {
646920Ssam 		ioctl(t, TIOCNOTTY, 0);
656920Ssam 		close(t);
666920Ssam 	    }
676920Ssam 	}
686920Ssam 	if (trace) {
696929Ssam 		(void) freopen("/etc/routerlog", "a", stdout);
706929Ssam 		(void) dup2(fileno(stdout), 2);
716920Ssam 		setbuf(stdout, NULL);
726920Ssam 	}
73*6937Ssam #ifdef vax || pdp11
746920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
756920Ssam #endif
766920Ssam again:
776920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
786920Ssam 	if (s < 0) {
796920Ssam 		perror("socket");
806920Ssam 		sleep(30);
816920Ssam 		goto again;
826920Ssam 	}
836920Ssam 	rtinit();
846920Ssam 	getothers();
856929Ssam 	initializing = 1;
866920Ssam 	getinterfaces();
876929Ssam 	initializing = 0;
886920Ssam 	request();
896920Ssam 
906920Ssam 	argv++, argc--;
916920Ssam 	while (argc > 0) {
926920Ssam 		if (strcmp(*argv, "-s") == 0)
936929Ssam 			supplier = 1;
946920Ssam 		else if (strcmp(*argv, "-q") == 0)
956920Ssam 			supplier = 0;
966920Ssam 		argv++, argc--;
976920Ssam 	}
986920Ssam 	sigset(SIGALRM, timer);
996929Ssam 	timer();
1006920Ssam 
1016920Ssam 	/*
1026920Ssam 	 * Listen for routing packets
1036920Ssam 	 */
1046920Ssam 	for (;;) {
1056920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1066920Ssam 		if (cc <= 0) {
1076920Ssam 			if (cc < 0 && errno != EINTR)
1086920Ssam 				perror("receive");
1096920Ssam 			continue;
1106920Ssam 		}
1116920Ssam 		sighold(SIGALRM);
1126920Ssam 		rip_input(&from, cc);
1136920Ssam 		sigrelse(SIGALRM);
1146920Ssam 	}
1156920Ssam }
1166920Ssam 
1176920Ssam /*
1186920Ssam  * Look in a file for any gateways we should configure
1196920Ssam  * outside the directly connected ones.  This is a kludge,
1206920Ssam  * but until we can find out about gateways on the "other side"
1216920Ssam  * of the ARPANET using GGP, it's a must.
1226920Ssam  *
1236920Ssam  * We don't really know the distance to the gateway, so we
1246920Ssam  * assume it's a neighbor.
1256920Ssam  */
1266920Ssam getothers()
1276920Ssam {
1286920Ssam 	struct sockaddr_in dst, gate;
1296920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
1306920Ssam 	struct rt_entry *rt;
1316920Ssam 
1326920Ssam 	if (fp == NULL)
1336920Ssam 		return;
1346920Ssam 	bzero((char *)&dst, sizeof (dst));
1356920Ssam 	bzero((char *)&gate, sizeof (gate));
1366920Ssam 	dst.sin_family = AF_INET;
1376920Ssam 	gate.sin_family = AF_INET;
1386920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
1396920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
1406920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
1416920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
1426920Ssam 		if (rt)
143*6937Ssam 			rt->rt_state |= RTS_SILENT;
1446920Ssam 	}
1456920Ssam 	fclose(fp);
1466920Ssam }
1476920Ssam 
1486929Ssam /*
1496929Ssam  * Timer routine:
1506929Ssam  *
1516929Ssam  * o handle timers on table entries,
1526929Ssam  * o invalidate entries which haven't been updated in a while,
1536929Ssam  * o delete entries which are too old,
1546929Ssam  * o retry ioctl's which weren't successful the first
1556929Ssam  *   time due to the kernel entry being busy
1566929Ssam  * o if we're an internetwork router, supply routing updates
1576929Ssam  *   periodically
1586929Ssam  */
1596929Ssam timer()
1606920Ssam {
1616934Ssam 	register struct rthash *rh;
1626929Ssam 	register struct rt_entry *rt;
1636934Ssam 	struct rthash *base = hosthash;
1646929Ssam 	int doinghost = 1;
1656920Ssam 
1666929Ssam 	if (trace)
1676929Ssam 		printf(">>> time %d >>>\n", timeval);
1686929Ssam again:
1696929Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
1706929Ssam 		rt = rh->rt_forw;
1716929Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1726920Ssam 
1736929Ssam 			/*
1746929Ssam 			 * If the host is indicated to be
1756929Ssam 			 * "silent" (i.e. it's one we got
1766929Ssam 			 * from the initialization file),
1776929Ssam 			 * don't time out it's entry.
1786929Ssam 			 */
179*6937Ssam 			if ((rt->rt_state & RTS_SILENT) == 0)
1806933Ssam 				rt->rt_timer += TIMER_RATE;
1816929Ssam 			log("", rt);
1826920Ssam 
1836929Ssam 			/*
1846929Ssam 			 * If the entry should be deleted
1856929Ssam 			 * attempt to do so and reclaim space.
1866929Ssam 			 */
1876929Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
188*6937Ssam 			  (rt->rt_state & RTS_DELRT)) {
1896929Ssam 				rt = rt->rt_back;
190*6937Ssam 				rtdelete(rt->rt_forw);
1916929Ssam 				continue;
1926929Ssam 			}
1936920Ssam 
1946929Ssam 			/*
1956929Ssam 			 * If we haven't heard from the router
1966929Ssam 			 * in a long time indicate the route is
1976929Ssam 			 * hard to reach.
1986929Ssam 			 */
1996929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
2006929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
201*6937Ssam 			if (rt->rt_state & RTS_CHGRT)
2026934Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) ||
2036929Ssam 				  --rt->rt_retry == 0)
204*6937Ssam 					rt->rt_state &= ~RTS_CHGRT;
2056920Ssam 
2066929Ssam 			/*
2076929Ssam 			 * Try to add the route to the kernel tables.
2086929Ssam 			 * If this fails because the entry already exists
2096929Ssam 			 * (perhaps because someone manually added it)
2106929Ssam 			 * change the add to a change.  If the operation
2116929Ssam 			 * fails otherwise (likely because the entry is
2126929Ssam 			 * in use), retry the operation a few more times.
2136929Ssam 			 */
214*6937Ssam 			if (rt->rt_state & RTS_ADDRT) {
2156934Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) {
2166929Ssam 					if (errno == EEXIST) {
217*6937Ssam 						rt->rt_state &= ~RTS_ADDRT;
218*6937Ssam 						rt->rt_state |= RTS_CHGRT;
2196929Ssam 						rt->rt_retry =
2206929Ssam 						    (EXPIRE_TIME/TIMER_RATE);
2216929Ssam 						continue;
2226929Ssam 					}
2236929Ssam 					if (--rt->rt_retry)
2246929Ssam 						continue;
2256929Ssam 				}
226*6937Ssam 				rt->rt_state &= ~RTS_ADDRT;
2276929Ssam 			}
2286929Ssam 		}
2296929Ssam 	}
2306929Ssam 	if (doinghost) {
2316929Ssam 		doinghost = 0;
2326929Ssam 		base = nethash;
2336929Ssam 		goto again;
2346929Ssam 	}
2356929Ssam 	timeval += TIMER_RATE;
2366929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
2376929Ssam 		getinterfaces();
2386929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
2396929Ssam 		supplyall();
2406929Ssam 	if (trace)
2416929Ssam 		printf("<<< time %d <<<\n", timeval);
2426929Ssam 	alarm(TIMER_RATE);
2436920Ssam }
2446920Ssam 
2456934Ssam struct	ifnet *ifnet;
2466920Ssam /*
2476920Ssam  * Find the network interfaces attached to this machine.
2486929Ssam  * The info is used to:
2496920Ssam  *
2506920Ssam  * (1) initialize the routing tables, as done by the kernel.
2516920Ssam  * (2) ignore incoming packets we send.
2526920Ssam  * (3) figure out broadcast capability and addresses.
2536920Ssam  * (4) figure out if we're an internetwork gateway.
2546920Ssam  *
2556920Ssam  * We don't handle anything but Internet addresses.
2566934Ssam  *
2576934Ssam  * Note: this routine may be called periodically to
2586934Ssam  * scan for new interfaces.  In fact, the timer routine
2596934Ssam  * does so based on the flag lookforinterfaces.  The
2606934Ssam  * flag performnlist is set whenever something odd occurs
2616934Ssam  * while scanning the kernel; this is likely to occur
2626934Ssam  * if /vmunix isn't up to date (e.g. someone booted /ovmunix).
2636920Ssam  */
2646920Ssam getinterfaces()
2656920Ssam {
2666929Ssam 	struct ifnet *ifp;
2676929Ssam 	struct ifnet ifstruct, *next;
2686920Ssam 	struct sockaddr_in net;
2696929Ssam 	register struct sockaddr *dst;
2706920Ssam 	int nets;
2716920Ssam 
2726929Ssam 	if (performnlist) {
2736929Ssam 		nlist("/vmunix", nl);
2746929Ssam 		if (nl[N_IFNET].n_value == 0) {
2756929Ssam 			performnlist++;
2766929Ssam 			printf("ifnet: not in namelist\n");
2776929Ssam 			return;
2786929Ssam 		}
2796929Ssam 		performnlist = 0;
2806920Ssam 	}
2816920Ssam 	if (kmem < 0) {
2826929Ssam 		kmem = open("/dev/kmem", 0);
2836929Ssam 		if (kmem < 0) {
2846929Ssam 			perror("/dev/kmem");
2856929Ssam 			return;
2866929Ssam 		}
2876920Ssam 	}
2886929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2896929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
2906929Ssam 		performnlist = 1;
2916929Ssam 		return;
2926929Ssam 	}
2936920Ssam 	bzero((char *)&net, sizeof (net));
2946920Ssam 	net.sin_family = AF_INET;
2956920Ssam 	nets = 0;
2966929Ssam 	while (ifp) {
2976929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
2986929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
2996929Ssam 		  sizeof (ifstruct)) {
3006920Ssam 			perror("read");
3016929Ssam 			performnlist = 1;
3026920Ssam 			break;
3036920Ssam 		}
3046929Ssam 		ifp = &ifstruct;
3056929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
3066929Ssam 			lookforinterfaces++;
3076929Ssam 	skip:
3086929Ssam 			ifp = ifp->if_next;
3096929Ssam 			continue;
3106929Ssam 		}
3116929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
3126929Ssam 			goto skip;
3136929Ssam 		/* ignore software loopback networks. */
3146920Ssam 		if (ifp->if_net == LOOPBACKNET)
3156920Ssam 			goto skip;
3166929Ssam 		/* check if we already know about this one */
3176929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
3186929Ssam 			nets++;
3196920Ssam 			goto skip;
3206920Ssam 		}
3216929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
3226929Ssam 		if (ifp == 0) {
3236929Ssam 			printf("routed: out of memory\n");
3246929Ssam 			break;
3256929Ssam 		}
3266929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3276920Ssam 
3286920Ssam 		/*
3296929Ssam 		 * Count the # of directly connected networks
3306929Ssam 		 * and point to point links which aren't looped
3316929Ssam 		 * back to ourself.  This is used below to
3326929Ssam 		 * decide if we should be a routing "supplier".
3336920Ssam 		 */
3346929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
3356929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
3366929Ssam 			nets++;
3376920Ssam 
3386929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
3396929Ssam 			dst = &ifp->if_dstaddr;
3406929Ssam 		else {
3416929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
3426929Ssam 			dst = (struct sockaddr *)&net;
3436929Ssam 		}
3446929Ssam 		next = ifp->if_next;
3456929Ssam 		ifp->if_next = ifnet;
3466929Ssam 		ifnet = ifp;
3476929Ssam 		if (rtlookup(dst) == 0)
3486929Ssam 			rtadd(dst, &ifp->if_addr, 0);
3496929Ssam 		ifp = next;
3506920Ssam 	}
3516920Ssam 	supplier = nets > 1;
3526920Ssam }
3536920Ssam 
3546920Ssam /*
3556920Ssam  * Send a request message to all directly
3566920Ssam  * connected hosts and networks.
3576920Ssam  */
3586920Ssam request()
3596920Ssam {
3606929Ssam 	register struct rip *msg = (struct rip *)packet;
3616920Ssam 
3626929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
3636929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
3646929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
3656929Ssam 	sendall();
3666920Ssam }
3676920Ssam 
3686920Ssam /*
3696920Ssam  * Broadcast a new, or modified, routing table entry
3706920Ssam  * to all directly connected hosts and networks.
3716920Ssam  */
3726920Ssam broadcast(entry)
3736920Ssam 	struct rt_entry *entry;
3746920Ssam {
3756929Ssam 	struct rip *msg = (struct rip *)packet;
3766929Ssam 
3776929Ssam 	log("broadcast", entry);
3786929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
3796929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
3806929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
3816929Ssam 	sendall();
3826929Ssam }
3836929Ssam 
3846934Ssam /*
3856934Ssam  * Send "packet" to all neighbors.
3866934Ssam  */
3876929Ssam sendall()
3886929Ssam {
3896934Ssam 	register struct rthash *rh;
3906920Ssam 	register struct rt_entry *rt;
3916920Ssam 	register struct sockaddr *dst;
3926934Ssam 	struct rthash *base = hosthash;
3936920Ssam 	int doinghost = 1;
3946920Ssam 
3956920Ssam again:
3966920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3976920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
398*6937Ssam 		if ((rt->rt_state & RTS_SILENT) || rt->rt_metric > 0)
3996920Ssam 			continue;
4006920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4016920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4026920Ssam 		else
4036920Ssam 			dst = &rt->rt_gateway;
4046920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
4056920Ssam 	}
4066920Ssam 	if (doinghost) {
4076929Ssam 		base = nethash;
4086920Ssam 		doinghost = 0;
4096920Ssam 		goto again;
4106920Ssam 	}
4116920Ssam }
4126920Ssam 
4136920Ssam /*
4146920Ssam  * Supply all directly connected neighbors with the
4156920Ssam  * current state of the routing tables.
4166920Ssam  */
4176920Ssam supplyall()
4186920Ssam {
4196920Ssam 	register struct rt_entry *rt;
4206934Ssam 	register struct rthash *rh;
4216920Ssam 	register struct sockaddr *dst;
4226934Ssam 	struct rthash *base = hosthash;
4236920Ssam 	int doinghost = 1;
4246920Ssam 
4256920Ssam again:
4266920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4276920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
428*6937Ssam 		if ((rt->rt_state & RTS_SILENT) || rt->rt_metric > 0)
4296920Ssam 			continue;
4306920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4316920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4326920Ssam 		else
4336920Ssam 			dst = &rt->rt_gateway;
4346929Ssam 		log("supply", rt);
4356920Ssam 		supply(dst);
4366920Ssam 	}
4376920Ssam 	if (doinghost) {
4386920Ssam 		base = nethash;
4396920Ssam 		doinghost = 0;
4406920Ssam 		goto again;
4416920Ssam 	}
4426920Ssam }
4436920Ssam 
4446920Ssam /*
4456920Ssam  * Supply routing information to target "sa".
4466920Ssam  */
4476920Ssam supply(sa)
4486920Ssam 	struct sockaddr *sa;
4496920Ssam {
4506920Ssam 	struct rip *msg = (struct rip *)packet;
4516920Ssam 	struct netinfo *n = msg->rip_nets;
4526934Ssam 	register struct rthash *rh;
4536920Ssam 	register struct rt_entry *rt;
4546934Ssam 	struct rthash *base = hosthash;
4556929Ssam 	int doinghost = 1, size;
4566920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4576920Ssam 
4586920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4596920Ssam again:
4606920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4616920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4626920Ssam 
4636920Ssam 		/*
4646920Ssam 		 * Flush packet out if not enough room for
4656920Ssam 		 * another routing table entry.
4666920Ssam 		 */
4676929Ssam 		size = (char *)n - packet;
4686929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4696929Ssam 			(*output)(sa, size);
4706920Ssam 			n = msg->rip_nets;
4716920Ssam 		}
4726920Ssam 		n->rip_dst = rt->rt_dst;
4736929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4746929Ssam 		n++;
4756920Ssam 	}
4766920Ssam 	if (doinghost) {
4776920Ssam 		doinghost = 0;
4786920Ssam 		base = nethash;
4796920Ssam 		goto again;
4806920Ssam 	}
4816929Ssam 	if (n != msg->rip_nets)
4826929Ssam 		(*output)(sa, (char *)n - packet);
4836920Ssam }
4846920Ssam 
4856920Ssam /*
4866920Ssam  * Respond to a routing info request.
4876920Ssam  */
4886920Ssam rip_respond(from, size)
4896920Ssam 	struct sockaddr *from;
4906920Ssam 	int size;
4916920Ssam {
4926920Ssam 	register struct rip *msg = (struct rip *)packet;
4936920Ssam 	struct netinfo *np = msg->rip_nets;
4946920Ssam 	struct rt_entry *rt;
4956920Ssam 	int newsize = 0;
4966920Ssam 
4976929Ssam 	size -= 4 * sizeof (char);
4986920Ssam 	while (size > 0) {
4996920Ssam 		if (size < sizeof (struct netinfo))
5006920Ssam 			break;
5016920Ssam 		size -= sizeof (struct netinfo);
5026920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
5036920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
5046920Ssam 			supply(from);
5056920Ssam 			return;
5066920Ssam 		}
5076920Ssam 		rt = rtlookup(&np->rip_dst);
5086929Ssam 		np->rip_metric = rt == 0 ?
5096929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
5106920Ssam 		np++, newsize += sizeof (struct netinfo);
5116920Ssam 	}
5126920Ssam 	if (newsize > 0) {
5136920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5146920Ssam 		newsize += sizeof (int);
5156920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
5166920Ssam 	}
5176920Ssam }
5186920Ssam 
5196920Ssam /*
5206920Ssam  * Handle an incoming routing packet.
5216920Ssam  */
5226920Ssam rip_input(from, size)
5236920Ssam 	struct sockaddr *from;
5246920Ssam 	int size;
5256920Ssam {
5266920Ssam 	register struct rip *msg = (struct rip *)packet;
5276920Ssam 	struct rt_entry *rt;
5286920Ssam 	struct netinfo *n;
5296920Ssam 
530*6937Ssam 	switch (msg->rip_cmd) {
531*6937Ssam 
532*6937Ssam 	default:
5336920Ssam 		return;
5346920Ssam 
535*6937Ssam 	case RIPCMD_REQUEST:
5366920Ssam 		rip_respond(from, size);
5376920Ssam 		return;
538*6937Ssam 
539*6937Ssam 	case RIPCMD_RESPONSE:
540*6937Ssam 		/* verify message came from a priviledged port */
541*6937Ssam 		if ((*afswitch[from->sa_family].af_portmatch)(from) == 0)
542*6937Ssam 			return;
543*6937Ssam 		break;
5446920Ssam 	}
5456920Ssam 
5466920Ssam 	/*
5476920Ssam 	 * Process updates.
5486920Ssam 	 * Extraneous information like Internet ports
5496920Ssam 	 * must first be purged from the sender's address for
5506920Ssam 	 * pattern matching below.
5516920Ssam 	 */
5526920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5536920Ssam 	if (trace)
5546934Ssam 		printf("input from %x\n",
5556934Ssam 			((struct sockaddr_in *)from)->sin_addr);
5566920Ssam 	/*
5576920Ssam 	 * If response packet is from ourselves, use it only
5586920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5596920Ssam 	 * it as gospel (since it comes from the router) and
5606920Ssam 	 * unknowingly update the metric to show the outgoing
5616920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5626920Ssam 	 * spec doesn't address this because Xerox Ethernets
5636920Ssam 	 * don't hear their own broadcasts?
5646920Ssam 	 */
5656920Ssam 	if (if_ifwithaddr(from)) {
5666920Ssam 		rt = rtlookup(from);
5676920Ssam 		if (rt)
5686920Ssam 			rt->rt_timer = 0;
5696920Ssam 		return;
5706920Ssam 	}
5716929Ssam 	size -= 4 * sizeof (char);
5726920Ssam 	n = msg->rip_nets;
5736920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5746920Ssam 		if (size < sizeof (struct netinfo))
5756920Ssam 			break;
5766920Ssam 		if (trace)
5776934Ssam 			printf("dst %x hc %d...",
5786934Ssam 				((struct sockaddr_in *)&n->rip_dst)->sin_addr,
5796920Ssam 				n->rip_metric);
5806920Ssam 		rt = rtlookup(&n->rip_dst);
5816920Ssam 
5826920Ssam 		/*
5836920Ssam 		 * Unknown entry, add it to the tables only if
5846920Ssam 		 * its interesting.
5856920Ssam 		 */
5866920Ssam 		if (rt == 0) {
5876920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5886920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5896920Ssam 			if (trace)
5906920Ssam 				printf("new\n");
5916920Ssam 			continue;
5926920Ssam 		}
5936920Ssam 
5946920Ssam 		if (trace)
5956920Ssam 			printf("ours: gate %x hc %d timer %d\n",
5966934Ssam 			  ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr,
5976934Ssam 			  rt->rt_metric, rt->rt_timer);
5986920Ssam 		/*
5996920Ssam 		 * Update the entry if one of the following is true:
6006920Ssam 		 *
6016920Ssam 		 * (1) The update came directly from the gateway.
6026920Ssam 		 * (2) A shorter path is provided.
6036920Ssam 		 * (3) The entry hasn't been updated in a while
6046929Ssam 		 *     and a path of equivalent cost is offered
6056929Ssam 		 *     (with the cost finite).
6066920Ssam 		 */
6076920Ssam 		if (equal(from, &rt->rt_gateway) ||
6086920Ssam 		    rt->rt_metric > n->rip_metric ||
6096920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
6106929Ssam 		    rt->rt_metric == n->rip_metric &&
6116929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
6126920Ssam 			rtchange(rt, from, n->rip_metric);
6136920Ssam 			rt->rt_timer = 0;
6146920Ssam 		}
6156920Ssam 	}
6166920Ssam }
6176920Ssam 
6186934Ssam rtinit()
6196934Ssam {
6206934Ssam 	register struct rthash *rh;
6216934Ssam 
6226934Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6236934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6246934Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6256934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6266934Ssam }
6276934Ssam 
6286920Ssam /*
6296920Ssam  * Lookup an entry to the appropriate dstination.
6306920Ssam  */
6316920Ssam struct rt_entry *
6326920Ssam rtlookup(dst)
6336920Ssam 	struct sockaddr *dst;
6346920Ssam {
6356920Ssam 	register struct rt_entry *rt;
6366934Ssam 	register struct rthash *rh;
6376920Ssam 	register int hash, (*match)();
6386920Ssam 	struct afhash h;
6396920Ssam 	int af = dst->sa_family, doinghost = 1;
6406920Ssam 
6416920Ssam 	if (af >= AF_MAX)
6426920Ssam 		return (0);
6436920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6446920Ssam 	hash = h.afh_hosthash;
6456920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6466920Ssam 
6476920Ssam again:
6486920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6496920Ssam 		if (rt->rt_hash != hash)
6506920Ssam 			continue;
6516920Ssam 		if (doinghost) {
6526920Ssam 			if (equal(&rt->rt_dst, dst))
6536920Ssam 				return (rt);
6546920Ssam 		} else {
6556920Ssam 			if (rt->rt_dst.sa_family == af &&
6566920Ssam 			    (*match)(&rt->rt_dst, dst))
6576920Ssam 				return (rt);
6586920Ssam 		}
6596920Ssam 	}
6606920Ssam 	if (doinghost) {
6616920Ssam 		doinghost = 0;
6626920Ssam 		hash = h.afh_nethash;
6636920Ssam 		match = afswitch[af].af_netmatch;
6646920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6656920Ssam 		goto again;
6666920Ssam 	}
6676920Ssam 	return (0);
6686920Ssam }
6696920Ssam 
6706920Ssam /*
6716920Ssam  * Add a new entry.
6726920Ssam  */
6736920Ssam rtadd(dst, gate, metric)
6746920Ssam 	struct sockaddr *dst, *gate;
6756920Ssam 	short metric;
6766920Ssam {
6776920Ssam 	struct afhash h;
6786920Ssam 	register struct rt_entry *rt;
6796934Ssam 	struct rthash *rh;
6806920Ssam 	int af = dst->sa_family, flags, hash;
6816920Ssam 
6826920Ssam 	if (af >= AF_MAX)
6836920Ssam 		return;
6846920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6856920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6866920Ssam 	if (flags & RTF_HOST) {
6876920Ssam 		hash = h.afh_hosthash;
6886920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6896920Ssam 	} else {
6906920Ssam 		hash = h.afh_nethash;
6916920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6926920Ssam 	}
6936920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6946920Ssam 	if (rt == 0)
6956920Ssam 		return;
6966920Ssam 	rt->rt_hash = hash;
6976920Ssam 	rt->rt_dst = *dst;
6986920Ssam 	rt->rt_gateway = *gate;
6996920Ssam 	rt->rt_metric = metric;
7006920Ssam 	rt->rt_timer = 0;
7016920Ssam 	rt->rt_flags = RTF_UP | flags;
702*6937Ssam 	rt->rt_state = 0;
7036920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
7046920Ssam 	if (metric == 0)
7056920Ssam 		rt->rt_flags |= RTF_DIRECT;
7066920Ssam 	insque(rt, rh);
7076929Ssam 	log("add", rt);
7086920Ssam 	if (initializing)
7096920Ssam 		return;
7106920Ssam 	if (supplier)
7116920Ssam 		broadcast(rt);
7126920Ssam 	if (install) {
713*6937Ssam 		rt->rt_state |= RTS_ADDRT;
7146920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7156920Ssam 	}
7166920Ssam }
7176920Ssam 
7186920Ssam /*
7196920Ssam  * Look to see if a change to an existing entry
7206920Ssam  * is warranted; if so, make it.
7216920Ssam  */
7226920Ssam rtchange(rt, gate, metric)
7236920Ssam 	struct rt_entry *rt;
7246920Ssam 	struct sockaddr *gate;
7256920Ssam 	short metric;
7266920Ssam {
7276920Ssam 	int change = 0;
7286920Ssam 
7296920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
7306920Ssam 		rt->rt_gateway = *gate;
7316920Ssam 		change++;
7326920Ssam 	}
7336920Ssam 
7346920Ssam 	/*
7356920Ssam 	 * If the hop count has changed, adjust
7366920Ssam 	 * the flags in the routing table entry accordingly.
7376920Ssam 	 */
7386920Ssam 	if (metric != rt->rt_metric) {
7396920Ssam 		if (rt->rt_metric == 0)
7406920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7416920Ssam 		rt->rt_metric = metric;
7426920Ssam 		if (metric >= HOPCNT_INFINITY)
7436920Ssam 			rt->rt_flags &= ~RTF_UP;
7446920Ssam 		else
7456920Ssam 			rt->rt_flags |= RTF_UP;
7466920Ssam 		change++;
7476920Ssam 	}
7486920Ssam 
7496920Ssam 	if (!change)
7506920Ssam 		return;
7516920Ssam 	if (supplier)
7526920Ssam 		broadcast(rt);
7536929Ssam 	log("change", rt);
7546920Ssam 	if (install) {
755*6937Ssam 		rt->rt_state |= RTS_CHGRT;
7566920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7576920Ssam 	}
7586920Ssam }
7596920Ssam 
7606920Ssam /*
7616920Ssam  * Delete a routing table entry.
7626920Ssam  */
7636920Ssam rtdelete(rt)
7646920Ssam 	struct rt_entry *rt;
7656920Ssam {
7666929Ssam 	log("delete", rt);
7676920Ssam 	if (install)
7686934Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) &&
7696920Ssam 		  errno == EBUSY)
770*6937Ssam 			rt->rt_state |= RTS_DELRT;
7716920Ssam 	remque(rt);
7726920Ssam 	free((char *)rt);
7736920Ssam }
7746920Ssam 
7756920Ssam log(operation, rt)
7766920Ssam 	char *operation;
7776920Ssam 	struct rt_entry *rt;
7786920Ssam {
7796920Ssam 	time_t t = time(0);
7806920Ssam 	struct sockaddr_in *dst, *gate;
781*6937Ssam 	static struct bits {
7826920Ssam 		int	t_bits;
7836920Ssam 		char	*t_name;
784*6937Ssam 	} flagbits[] = {
7856920Ssam 		{ RTF_UP,	"UP" },
7866920Ssam 		{ RTF_DIRECT,	"DIRECT" },
7876920Ssam 		{ RTF_HOST,	"HOST" },
7886920Ssam 		{ 0 }
789*6937Ssam 	}, statebits[] = {
790*6937Ssam 		{ RTS_DELRT,	"DELETE" },
791*6937Ssam 		{ RTS_CHGRT,	"CHANGE" },
792*6937Ssam 		{ RTS_SILENT,	"SILENT" },
793*6937Ssam 		{ 0 }
7946920Ssam 	};
795*6937Ssam 	register struct bits *p;
7966920Ssam 	register int first;
7976920Ssam 	char *cp;
7986920Ssam 
7996929Ssam 	if (trace == 0)
8006929Ssam 		return;
8016920Ssam 	printf("%s ", operation);
8026920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8036920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
804*6937Ssam 	printf("dst %x, router %x, metric %d, flags",
8056920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
806*6937Ssam 	cp = " %s";
807*6937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8086920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8096920Ssam 			continue;
8106920Ssam 		printf(cp, p->t_name);
8116920Ssam 		if (first) {
8126920Ssam 			cp = "|%s";
8136920Ssam 			first = 0;
8146920Ssam 		}
8156920Ssam 	}
816*6937Ssam 	printf(" state");
817*6937Ssam 	cp = " %s";
818*6937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
819*6937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
820*6937Ssam 			continue;
821*6937Ssam 		printf(cp, p->t_name);
822*6937Ssam 		if (first) {
823*6937Ssam 			cp = "|%s";
824*6937Ssam 			first = 0;
825*6937Ssam 		}
826*6937Ssam 	}
8276920Ssam 	putchar('\n');
8286920Ssam }
8296929Ssam 
8306934Ssam /*
8316934Ssam  * Find the interface with address "addr".
8326934Ssam  */
8336929Ssam struct ifnet *
8346929Ssam if_ifwithaddr(addr)
8356929Ssam 	struct sockaddr *addr;
8366929Ssam {
8376929Ssam 	register struct ifnet *ifp;
8386929Ssam 
8396929Ssam #define	same(a1, a2) \
8406929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8416929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8426929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
8436929Ssam 			continue;
8446929Ssam 		if (same(&ifp->if_addr, addr))
8456929Ssam 			break;
8466929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
8476929Ssam 		    same(&ifp->if_broadaddr, addr))
8486929Ssam 			break;
8496929Ssam 	}
8506929Ssam 	return (ifp);
8516929Ssam #undef same
8526929Ssam }
8536929Ssam 
8546934Ssam /*
8556934Ssam  * Find the interface with network imbedded in
8566934Ssam  * the sockaddr "addr".  Must use per-af routine
8576934Ssam  * look for match.
8586934Ssam  */
8596929Ssam struct ifnet *
8606929Ssam if_ifwithnet(addr)
8616929Ssam 	register struct sockaddr *addr;
8626929Ssam {
8636929Ssam 	register struct ifnet *ifp;
8646929Ssam 	register int af = addr->sa_family;
8656929Ssam 	register int (*netmatch)();
8666929Ssam 
8676929Ssam 	if (af >= AF_MAX)
8686929Ssam 		return (0);
8696929Ssam 	netmatch = afswitch[af].af_netmatch;
8706929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8716929Ssam 		if (af != ifp->if_addr.sa_family)
8726929Ssam 			continue;
8736929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
8746929Ssam 			break;
8756929Ssam 	}
8766929Ssam 	return (ifp);
8776929Ssam }
8786929Ssam 
8796934Ssam /*
8806934Ssam  * Formulate an Internet address.
8816934Ssam  */
8826929Ssam struct in_addr
8836929Ssam if_makeaddr(net, host)
8846929Ssam 	int net, host;
8856929Ssam {
8866929Ssam 	u_long addr;
8876929Ssam 
8886929Ssam 	if (net < 128)
8896929Ssam 		addr = (net << 24) | host;
8906929Ssam 	else if (net < 65536)
8916929Ssam 		addr = (net << 16) | host;
8926929Ssam 	else
8936929Ssam 		addr = (net << 8) | host;
8946929Ssam #ifdef vax
8956929Ssam 	addr = htonl(addr);
8966929Ssam #endif
8976929Ssam 	return (*(struct in_addr *)&addr);
8986929Ssam }
899