xref: /csrg-svn/sbin/routed/routed.c (revision 6993)
16920Ssam #ifndef lint
2*6993Ssam static char sccsid[] = "@(#)routed.c	4.10 05/31/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 */
406962Ssam int	install = 1;		/* 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 	}
736937Ssam #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*6993Ssam 			rt->rt_state |= RTS_HIDDEN;
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
175*6993Ssam 			 * "hidden" (i.e. it's one we got
1766929Ssam 			 * from the initialization file),
1776929Ssam 			 * don't time out it's entry.
1786929Ssam 			 */
179*6993Ssam 			if ((rt->rt_state & RTS_HIDDEN) == 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 ||
1886937Ssam 			  (rt->rt_state & RTS_DELRT)) {
1896929Ssam 				rt = rt->rt_back;
1906937Ssam 				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;
2016937Ssam 			if (rt->rt_state & RTS_CHGRT)
2026934Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) ||
2036929Ssam 				  --rt->rt_retry == 0)
2046937Ssam 					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 			 */
2146937Ssam 			if (rt->rt_state & RTS_ADDRT) {
2156934Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) {
2166929Ssam 					if (errno == EEXIST) {
2176937Ssam 						rt->rt_state &= ~RTS_ADDRT;
2186937Ssam 						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 				}
2266937Ssam 				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;
2956961Ssam 	lookforinterfaces = 0;
2966920Ssam 	nets = 0;
2976929Ssam 	while (ifp) {
2986929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
2996929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
3006929Ssam 		  sizeof (ifstruct)) {
3016920Ssam 			perror("read");
3026961Ssam 			lookforinterfaces = performnlist = 1;
3036920Ssam 			break;
3046920Ssam 		}
3056929Ssam 		ifp = &ifstruct;
3066929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
3076961Ssam 			lookforinterfaces = 1;
3086929Ssam 	skip:
3096929Ssam 			ifp = ifp->if_next;
3106929Ssam 			continue;
3116929Ssam 		}
3126929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
3136929Ssam 			goto skip;
3146929Ssam 		/* ignore software loopback networks. */
3156920Ssam 		if (ifp->if_net == LOOPBACKNET)
3166920Ssam 			goto skip;
3176929Ssam 		/* check if we already know about this one */
3186929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
3196929Ssam 			nets++;
3206920Ssam 			goto skip;
3216920Ssam 		}
3226929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
3236929Ssam 		if (ifp == 0) {
3246929Ssam 			printf("routed: out of memory\n");
3256929Ssam 			break;
3266929Ssam 		}
3276929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3286920Ssam 
3296920Ssam 		/*
3306929Ssam 		 * Count the # of directly connected networks
3316929Ssam 		 * and point to point links which aren't looped
3326929Ssam 		 * back to ourself.  This is used below to
3336929Ssam 		 * decide if we should be a routing "supplier".
3346920Ssam 		 */
3356929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
3366929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
3376929Ssam 			nets++;
3386920Ssam 
3396929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
3406929Ssam 			dst = &ifp->if_dstaddr;
3416929Ssam 		else {
3426929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
3436929Ssam 			dst = (struct sockaddr *)&net;
3446929Ssam 		}
3456929Ssam 		next = ifp->if_next;
3466929Ssam 		ifp->if_next = ifnet;
3476929Ssam 		ifnet = ifp;
3486929Ssam 		if (rtlookup(dst) == 0)
3496929Ssam 			rtadd(dst, &ifp->if_addr, 0);
3506929Ssam 		ifp = next;
3516920Ssam 	}
3526920Ssam 	supplier = nets > 1;
3536920Ssam }
3546920Ssam 
3556920Ssam /*
3566920Ssam  * Send a request message to all directly
3576920Ssam  * connected hosts and networks.
3586920Ssam  */
3596920Ssam request()
3606920Ssam {
3616929Ssam 	register struct rip *msg = (struct rip *)packet;
3626920Ssam 
3636929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
3646929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
3656929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
3666929Ssam 	sendall();
3676920Ssam }
3686920Ssam 
3696920Ssam /*
3706920Ssam  * Broadcast a new, or modified, routing table entry
3716920Ssam  * to all directly connected hosts and networks.
3726920Ssam  */
3736920Ssam broadcast(entry)
3746920Ssam 	struct rt_entry *entry;
3756920Ssam {
3766929Ssam 	struct rip *msg = (struct rip *)packet;
3776929Ssam 
3786929Ssam 	log("broadcast", entry);
3796929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
3806929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
3816929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
3826929Ssam 	sendall();
3836929Ssam }
3846929Ssam 
3856934Ssam /*
3866934Ssam  * Send "packet" to all neighbors.
3876934Ssam  */
3886929Ssam sendall()
3896929Ssam {
3906934Ssam 	register struct rthash *rh;
3916920Ssam 	register struct rt_entry *rt;
3926920Ssam 	register struct sockaddr *dst;
3936934Ssam 	struct rthash *base = hosthash;
3946920Ssam 	int doinghost = 1;
3956920Ssam 
3966920Ssam again:
3976920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3986920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
399*6993Ssam 		if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0)
4006920Ssam 			continue;
4016920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4026920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4036920Ssam 		else
4046920Ssam 			dst = &rt->rt_gateway;
4056920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
4066920Ssam 	}
4076920Ssam 	if (doinghost) {
4086929Ssam 		base = nethash;
4096920Ssam 		doinghost = 0;
4106920Ssam 		goto again;
4116920Ssam 	}
4126920Ssam }
4136920Ssam 
4146920Ssam /*
4156920Ssam  * Supply all directly connected neighbors with the
4166920Ssam  * current state of the routing tables.
4176920Ssam  */
4186920Ssam supplyall()
4196920Ssam {
4206920Ssam 	register struct rt_entry *rt;
4216934Ssam 	register struct rthash *rh;
4226920Ssam 	register struct sockaddr *dst;
4236934Ssam 	struct rthash *base = hosthash;
4246920Ssam 	int doinghost = 1;
4256920Ssam 
4266920Ssam again:
4276920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4286920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
429*6993Ssam 		if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0)
4306920Ssam 			continue;
4316920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4326920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4336920Ssam 		else
4346920Ssam 			dst = &rt->rt_gateway;
4356929Ssam 		log("supply", rt);
4366920Ssam 		supply(dst);
4376920Ssam 	}
4386920Ssam 	if (doinghost) {
4396920Ssam 		base = nethash;
4406920Ssam 		doinghost = 0;
4416920Ssam 		goto again;
4426920Ssam 	}
4436920Ssam }
4446920Ssam 
4456920Ssam /*
4466920Ssam  * Supply routing information to target "sa".
4476920Ssam  */
4486920Ssam supply(sa)
4496920Ssam 	struct sockaddr *sa;
4506920Ssam {
4516920Ssam 	struct rip *msg = (struct rip *)packet;
4526920Ssam 	struct netinfo *n = msg->rip_nets;
4536934Ssam 	register struct rthash *rh;
4546920Ssam 	register struct rt_entry *rt;
4556934Ssam 	struct rthash *base = hosthash;
4566929Ssam 	int doinghost = 1, size;
4576920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4586920Ssam 
4596920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4606920Ssam again:
4616920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4626920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4636920Ssam 
4646920Ssam 		/*
4656920Ssam 		 * Flush packet out if not enough room for
4666920Ssam 		 * another routing table entry.
4676920Ssam 		 */
4686929Ssam 		size = (char *)n - packet;
4696929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4706929Ssam 			(*output)(sa, size);
4716920Ssam 			n = msg->rip_nets;
4726920Ssam 		}
4736920Ssam 		n->rip_dst = rt->rt_dst;
4746929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4756929Ssam 		n++;
4766920Ssam 	}
4776920Ssam 	if (doinghost) {
4786920Ssam 		doinghost = 0;
4796920Ssam 		base = nethash;
4806920Ssam 		goto again;
4816920Ssam 	}
4826929Ssam 	if (n != msg->rip_nets)
4836929Ssam 		(*output)(sa, (char *)n - packet);
4846920Ssam }
4856920Ssam 
4866920Ssam /*
4876920Ssam  * Respond to a routing info request.
4886920Ssam  */
4896920Ssam rip_respond(from, size)
4906920Ssam 	struct sockaddr *from;
4916920Ssam 	int size;
4926920Ssam {
4936920Ssam 	register struct rip *msg = (struct rip *)packet;
4946920Ssam 	struct netinfo *np = msg->rip_nets;
4956920Ssam 	struct rt_entry *rt;
4966920Ssam 	int newsize = 0;
4976920Ssam 
4986929Ssam 	size -= 4 * sizeof (char);
4996920Ssam 	while (size > 0) {
5006920Ssam 		if (size < sizeof (struct netinfo))
5016920Ssam 			break;
5026920Ssam 		size -= sizeof (struct netinfo);
5036920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
5046920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
5056920Ssam 			supply(from);
5066920Ssam 			return;
5076920Ssam 		}
5086920Ssam 		rt = rtlookup(&np->rip_dst);
5096929Ssam 		np->rip_metric = rt == 0 ?
5106929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
5116920Ssam 		np++, newsize += sizeof (struct netinfo);
5126920Ssam 	}
5136920Ssam 	if (newsize > 0) {
5146920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5156920Ssam 		newsize += sizeof (int);
5166920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
5176920Ssam 	}
5186920Ssam }
5196920Ssam 
5206920Ssam /*
5216920Ssam  * Handle an incoming routing packet.
5226920Ssam  */
5236920Ssam rip_input(from, size)
5246920Ssam 	struct sockaddr *from;
5256920Ssam 	int size;
5266920Ssam {
5276920Ssam 	register struct rip *msg = (struct rip *)packet;
5286920Ssam 	struct rt_entry *rt;
5296920Ssam 	struct netinfo *n;
5306920Ssam 
5316937Ssam 	switch (msg->rip_cmd) {
5326937Ssam 
5336937Ssam 	default:
5346920Ssam 		return;
5356920Ssam 
5366937Ssam 	case RIPCMD_REQUEST:
5376920Ssam 		rip_respond(from, size);
5386920Ssam 		return;
5396937Ssam 
5406937Ssam 	case RIPCMD_RESPONSE:
5416937Ssam 		/* verify message came from a priviledged port */
5426937Ssam 		if ((*afswitch[from->sa_family].af_portmatch)(from) == 0)
5436937Ssam 			return;
5446937Ssam 		break;
5456920Ssam 	}
5466920Ssam 
5476920Ssam 	/*
5486920Ssam 	 * Process updates.
5496920Ssam 	 * Extraneous information like Internet ports
5506920Ssam 	 * must first be purged from the sender's address for
5516920Ssam 	 * pattern matching below.
5526920Ssam 	 */
5536920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5546920Ssam 	if (trace)
5556934Ssam 		printf("input from %x\n",
5566934Ssam 			((struct sockaddr_in *)from)->sin_addr);
5576920Ssam 	/*
5586920Ssam 	 * If response packet is from ourselves, use it only
5596920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5606920Ssam 	 * it as gospel (since it comes from the router) and
5616920Ssam 	 * unknowingly update the metric to show the outgoing
5626920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5636920Ssam 	 * spec doesn't address this because Xerox Ethernets
5646920Ssam 	 * don't hear their own broadcasts?
5656920Ssam 	 */
5666920Ssam 	if (if_ifwithaddr(from)) {
5676920Ssam 		rt = rtlookup(from);
5686920Ssam 		if (rt)
5696920Ssam 			rt->rt_timer = 0;
5706920Ssam 		return;
5716920Ssam 	}
5726929Ssam 	size -= 4 * sizeof (char);
5736920Ssam 	n = msg->rip_nets;
5746920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5756920Ssam 		if (size < sizeof (struct netinfo))
5766920Ssam 			break;
5776920Ssam 		if (trace)
5786934Ssam 			printf("dst %x hc %d...",
5796934Ssam 				((struct sockaddr_in *)&n->rip_dst)->sin_addr,
5806920Ssam 				n->rip_metric);
5816920Ssam 		rt = rtlookup(&n->rip_dst);
5826920Ssam 
5836920Ssam 		/*
5846920Ssam 		 * Unknown entry, add it to the tables only if
5856920Ssam 		 * its interesting.
5866920Ssam 		 */
5876920Ssam 		if (rt == 0) {
5886920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5896920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5906920Ssam 			if (trace)
5916920Ssam 				printf("new\n");
5926920Ssam 			continue;
5936920Ssam 		}
5946920Ssam 
5956920Ssam 		if (trace)
5966920Ssam 			printf("ours: gate %x hc %d timer %d\n",
5976934Ssam 			  ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr,
5986934Ssam 			  rt->rt_metric, rt->rt_timer);
5996920Ssam 		/*
6006920Ssam 		 * Update the entry if one of the following is true:
6016920Ssam 		 *
6026920Ssam 		 * (1) The update came directly from the gateway.
6036920Ssam 		 * (2) A shorter path is provided.
6046920Ssam 		 * (3) The entry hasn't been updated in a while
6056929Ssam 		 *     and a path of equivalent cost is offered
6066929Ssam 		 *     (with the cost finite).
6076920Ssam 		 */
6086920Ssam 		if (equal(from, &rt->rt_gateway) ||
6096920Ssam 		    rt->rt_metric > n->rip_metric ||
6106920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
6116929Ssam 		    rt->rt_metric == n->rip_metric &&
6126929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
6136920Ssam 			rtchange(rt, from, n->rip_metric);
6146920Ssam 			rt->rt_timer = 0;
6156920Ssam 		}
6166920Ssam 	}
6176920Ssam }
6186920Ssam 
6196934Ssam rtinit()
6206934Ssam {
6216934Ssam 	register struct rthash *rh;
6226934Ssam 
6236934Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6246934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6256934Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6266934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6276934Ssam }
6286934Ssam 
6296920Ssam /*
6306920Ssam  * Lookup an entry to the appropriate dstination.
6316920Ssam  */
6326920Ssam struct rt_entry *
6336920Ssam rtlookup(dst)
6346920Ssam 	struct sockaddr *dst;
6356920Ssam {
6366920Ssam 	register struct rt_entry *rt;
6376934Ssam 	register struct rthash *rh;
6386920Ssam 	register int hash, (*match)();
6396920Ssam 	struct afhash h;
6406920Ssam 	int af = dst->sa_family, doinghost = 1;
6416920Ssam 
6426920Ssam 	if (af >= AF_MAX)
6436920Ssam 		return (0);
6446920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6456920Ssam 	hash = h.afh_hosthash;
6466920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6476920Ssam 
6486920Ssam again:
6496920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6506920Ssam 		if (rt->rt_hash != hash)
6516920Ssam 			continue;
6526920Ssam 		if (doinghost) {
6536920Ssam 			if (equal(&rt->rt_dst, dst))
6546920Ssam 				return (rt);
6556920Ssam 		} else {
6566920Ssam 			if (rt->rt_dst.sa_family == af &&
6576920Ssam 			    (*match)(&rt->rt_dst, dst))
6586920Ssam 				return (rt);
6596920Ssam 		}
6606920Ssam 	}
6616920Ssam 	if (doinghost) {
6626920Ssam 		doinghost = 0;
6636920Ssam 		hash = h.afh_nethash;
6646920Ssam 		match = afswitch[af].af_netmatch;
6656920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6666920Ssam 		goto again;
6676920Ssam 	}
6686920Ssam 	return (0);
6696920Ssam }
6706920Ssam 
6716920Ssam /*
6726920Ssam  * Add a new entry.
6736920Ssam  */
6746920Ssam rtadd(dst, gate, metric)
6756920Ssam 	struct sockaddr *dst, *gate;
6766920Ssam 	short metric;
6776920Ssam {
6786920Ssam 	struct afhash h;
6796920Ssam 	register struct rt_entry *rt;
6806934Ssam 	struct rthash *rh;
6816920Ssam 	int af = dst->sa_family, flags, hash;
6826920Ssam 
6836920Ssam 	if (af >= AF_MAX)
6846920Ssam 		return;
6856920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6866920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6876920Ssam 	if (flags & RTF_HOST) {
6886920Ssam 		hash = h.afh_hosthash;
6896920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6906920Ssam 	} else {
6916920Ssam 		hash = h.afh_nethash;
6926920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6936920Ssam 	}
6946920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6956920Ssam 	if (rt == 0)
6966920Ssam 		return;
6976920Ssam 	rt->rt_hash = hash;
6986920Ssam 	rt->rt_dst = *dst;
6996920Ssam 	rt->rt_gateway = *gate;
7006920Ssam 	rt->rt_metric = metric;
7016920Ssam 	rt->rt_timer = 0;
7026920Ssam 	rt->rt_flags = RTF_UP | flags;
7036937Ssam 	rt->rt_state = 0;
7046920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
7056920Ssam 	if (metric == 0)
7066920Ssam 		rt->rt_flags |= RTF_DIRECT;
7076920Ssam 	insque(rt, rh);
7086929Ssam 	log("add", rt);
7096920Ssam 	if (initializing)
7106920Ssam 		return;
7116920Ssam 	if (supplier)
7126920Ssam 		broadcast(rt);
7136920Ssam 	if (install) {
7146937Ssam 		rt->rt_state |= RTS_ADDRT;
7156920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7166920Ssam 	}
7176920Ssam }
7186920Ssam 
7196920Ssam /*
7206920Ssam  * Look to see if a change to an existing entry
7216920Ssam  * is warranted; if so, make it.
7226920Ssam  */
7236920Ssam rtchange(rt, gate, metric)
7246920Ssam 	struct rt_entry *rt;
7256920Ssam 	struct sockaddr *gate;
7266920Ssam 	short metric;
7276920Ssam {
7286920Ssam 	int change = 0;
7296920Ssam 
7306920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
7316920Ssam 		rt->rt_gateway = *gate;
7326920Ssam 		change++;
7336920Ssam 	}
7346920Ssam 
7356920Ssam 	/*
7366920Ssam 	 * If the hop count has changed, adjust
7376920Ssam 	 * the flags in the routing table entry accordingly.
7386920Ssam 	 */
7396920Ssam 	if (metric != rt->rt_metric) {
7406920Ssam 		if (rt->rt_metric == 0)
7416920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7426920Ssam 		rt->rt_metric = metric;
7436920Ssam 		if (metric >= HOPCNT_INFINITY)
7446920Ssam 			rt->rt_flags &= ~RTF_UP;
7456920Ssam 		else
7466920Ssam 			rt->rt_flags |= RTF_UP;
7476920Ssam 		change++;
7486920Ssam 	}
7496920Ssam 
7506920Ssam 	if (!change)
7516920Ssam 		return;
7526920Ssam 	if (supplier)
7536920Ssam 		broadcast(rt);
7546929Ssam 	log("change", rt);
7556920Ssam 	if (install) {
7566937Ssam 		rt->rt_state |= RTS_CHGRT;
7576920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7586920Ssam 	}
7596920Ssam }
7606920Ssam 
7616920Ssam /*
7626920Ssam  * Delete a routing table entry.
7636920Ssam  */
7646920Ssam rtdelete(rt)
7656920Ssam 	struct rt_entry *rt;
7666920Ssam {
7676929Ssam 	log("delete", rt);
7686920Ssam 	if (install)
7696934Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) &&
7706920Ssam 		  errno == EBUSY)
7716937Ssam 			rt->rt_state |= RTS_DELRT;
7726920Ssam 	remque(rt);
7736920Ssam 	free((char *)rt);
7746920Ssam }
7756920Ssam 
7766920Ssam log(operation, rt)
7776920Ssam 	char *operation;
7786920Ssam 	struct rt_entry *rt;
7796920Ssam {
7806920Ssam 	time_t t = time(0);
7816920Ssam 	struct sockaddr_in *dst, *gate;
7826937Ssam 	static struct bits {
7836920Ssam 		int	t_bits;
7846920Ssam 		char	*t_name;
7856937Ssam 	} flagbits[] = {
7866920Ssam 		{ RTF_UP,	"UP" },
7876920Ssam 		{ RTF_DIRECT,	"DIRECT" },
7886920Ssam 		{ RTF_HOST,	"HOST" },
7896920Ssam 		{ 0 }
7906937Ssam 	}, statebits[] = {
7916937Ssam 		{ RTS_DELRT,	"DELETE" },
7926937Ssam 		{ RTS_CHGRT,	"CHANGE" },
793*6993Ssam 		{ RTS_HIDDEN,	"HIDDEN" },
7946937Ssam 		{ 0 }
7956920Ssam 	};
7966937Ssam 	register struct bits *p;
7976920Ssam 	register int first;
7986920Ssam 	char *cp;
7996920Ssam 
8006929Ssam 	if (trace == 0)
8016929Ssam 		return;
8026920Ssam 	printf("%s ", operation);
8036920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8046920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
8056937Ssam 	printf("dst %x, router %x, metric %d, flags",
8066920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
8076937Ssam 	cp = " %s";
8086937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
8096920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8106920Ssam 			continue;
8116920Ssam 		printf(cp, p->t_name);
8126920Ssam 		if (first) {
8136920Ssam 			cp = "|%s";
8146920Ssam 			first = 0;
8156920Ssam 		}
8166920Ssam 	}
8176937Ssam 	printf(" state");
8186937Ssam 	cp = " %s";
8196937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
8206937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
8216937Ssam 			continue;
8226937Ssam 		printf(cp, p->t_name);
8236937Ssam 		if (first) {
8246937Ssam 			cp = "|%s";
8256937Ssam 			first = 0;
8266937Ssam 		}
8276937Ssam 	}
8286920Ssam 	putchar('\n');
8296920Ssam }
8306929Ssam 
8316934Ssam /*
8326934Ssam  * Find the interface with address "addr".
8336934Ssam  */
8346929Ssam struct ifnet *
8356929Ssam if_ifwithaddr(addr)
8366929Ssam 	struct sockaddr *addr;
8376929Ssam {
8386929Ssam 	register struct ifnet *ifp;
8396929Ssam 
8406929Ssam #define	same(a1, a2) \
8416929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8426929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8436929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
8446929Ssam 			continue;
8456929Ssam 		if (same(&ifp->if_addr, addr))
8466929Ssam 			break;
8476929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
8486929Ssam 		    same(&ifp->if_broadaddr, addr))
8496929Ssam 			break;
8506929Ssam 	}
8516929Ssam 	return (ifp);
8526929Ssam #undef same
8536929Ssam }
8546929Ssam 
8556934Ssam /*
8566934Ssam  * Find the interface with network imbedded in
8576934Ssam  * the sockaddr "addr".  Must use per-af routine
8586934Ssam  * look for match.
8596934Ssam  */
8606929Ssam struct ifnet *
8616929Ssam if_ifwithnet(addr)
8626929Ssam 	register struct sockaddr *addr;
8636929Ssam {
8646929Ssam 	register struct ifnet *ifp;
8656929Ssam 	register int af = addr->sa_family;
8666929Ssam 	register int (*netmatch)();
8676929Ssam 
8686929Ssam 	if (af >= AF_MAX)
8696929Ssam 		return (0);
8706929Ssam 	netmatch = afswitch[af].af_netmatch;
8716929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8726929Ssam 		if (af != ifp->if_addr.sa_family)
8736929Ssam 			continue;
8746929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
8756929Ssam 			break;
8766929Ssam 	}
8776929Ssam 	return (ifp);
8786929Ssam }
8796929Ssam 
8806934Ssam /*
8816934Ssam  * Formulate an Internet address.
8826934Ssam  */
8836929Ssam struct in_addr
8846929Ssam if_makeaddr(net, host)
8856929Ssam 	int net, host;
8866929Ssam {
8876929Ssam 	u_long addr;
8886929Ssam 
8896929Ssam 	if (net < 128)
8906929Ssam 		addr = (net << 24) | host;
8916929Ssam 	else if (net < 65536)
8926929Ssam 		addr = (net << 16) | host;
8936929Ssam 	else
8946929Ssam 		addr = (net << 8) | host;
8956929Ssam #ifdef vax
8966929Ssam 	addr = htonl(addr);
8976929Ssam #endif
8986929Ssam 	return (*(struct in_addr *)&addr);
8996929Ssam }
900