xref: /csrg-svn/sbin/routed/routed.c (revision 6934)
16920Ssam #ifndef lint
2*6934Ssam static char sccsid[] = "@(#)routed.c	4.6 05/25/82";
36920Ssam #endif
46920Ssam 
5*6934Ssam /*
6*6934Ssam  * Routing Table Management Daemon
7*6934Ssam  */
8*6934Ssam #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 
50*6934Ssam struct in_addr if_makeaddr();
51*6934Ssam 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 	}
736920Ssam #ifdef vax
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)
1436920Ssam 			rt->rt_flags |= RTF_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 {
161*6934Ssam 	register struct rthash *rh;
1626929Ssam 	register struct rt_entry *rt;
163*6934Ssam 	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 			 */
1796933Ssam 			if ((rt->rt_flags & RTF_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 ||
1886929Ssam 			  (rt->rt_flags & RTF_DELRT)) {
1896929Ssam 				rt = rt->rt_forw;
1906929Ssam 				rtdelete(rt->rt_back);
1916929Ssam 				rt = rt->rt_back;
1926929Ssam 				continue;
1936929Ssam 			}
1946920Ssam 
1956929Ssam 			/*
1966929Ssam 			 * If we haven't heard from the router
1976929Ssam 			 * in a long time indicate the route is
1986929Ssam 			 * hard to reach.
1996929Ssam 			 */
2006929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
2016929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
2026929Ssam 			if (rt->rt_flags & RTF_CHGRT)
203*6934Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) ||
2046929Ssam 				  --rt->rt_retry == 0)
2056929Ssam 					rt->rt_flags &= ~RTF_CHGRT;
2066920Ssam 
2076929Ssam 			/*
2086929Ssam 			 * Try to add the route to the kernel tables.
2096929Ssam 			 * If this fails because the entry already exists
2106929Ssam 			 * (perhaps because someone manually added it)
2116929Ssam 			 * change the add to a change.  If the operation
2126929Ssam 			 * fails otherwise (likely because the entry is
2136929Ssam 			 * in use), retry the operation a few more times.
2146929Ssam 			 */
2156929Ssam 			if (rt->rt_flags & RTF_ADDRT) {
216*6934Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) {
2176929Ssam 					if (errno == EEXIST) {
2186929Ssam 						rt->rt_flags &= ~RTF_ADDRT;
2196929Ssam 						rt->rt_flags |= RTF_CHGRT;
2206929Ssam 						rt->rt_retry =
2216929Ssam 						    (EXPIRE_TIME/TIMER_RATE);
2226929Ssam 						continue;
2236929Ssam 					}
2246929Ssam 					if (--rt->rt_retry)
2256929Ssam 						continue;
2266929Ssam 				}
2276929Ssam 				rt->rt_flags &= ~RTF_ADDRT;
2286929Ssam 			}
2296929Ssam 		}
2306929Ssam 	}
2316929Ssam 	if (doinghost) {
2326929Ssam 		doinghost = 0;
2336929Ssam 		base = nethash;
2346929Ssam 		goto again;
2356929Ssam 	}
2366929Ssam 	timeval += TIMER_RATE;
2376929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
2386929Ssam 		getinterfaces();
2396929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
2406929Ssam 		supplyall();
2416929Ssam 	if (trace)
2426929Ssam 		printf("<<< time %d <<<\n", timeval);
2436929Ssam 	alarm(TIMER_RATE);
2446920Ssam }
2456920Ssam 
246*6934Ssam struct	ifnet *ifnet;
2476920Ssam /*
2486920Ssam  * Find the network interfaces attached to this machine.
2496929Ssam  * The info is used to:
2506920Ssam  *
2516920Ssam  * (1) initialize the routing tables, as done by the kernel.
2526920Ssam  * (2) ignore incoming packets we send.
2536920Ssam  * (3) figure out broadcast capability and addresses.
2546920Ssam  * (4) figure out if we're an internetwork gateway.
2556920Ssam  *
2566920Ssam  * We don't handle anything but Internet addresses.
257*6934Ssam  *
258*6934Ssam  * Note: this routine may be called periodically to
259*6934Ssam  * scan for new interfaces.  In fact, the timer routine
260*6934Ssam  * does so based on the flag lookforinterfaces.  The
261*6934Ssam  * flag performnlist is set whenever something odd occurs
262*6934Ssam  * while scanning the kernel; this is likely to occur
263*6934Ssam  * if /vmunix isn't up to date (e.g. someone booted /ovmunix).
2646920Ssam  */
2656920Ssam getinterfaces()
2666920Ssam {
2676929Ssam 	struct ifnet *ifp;
2686929Ssam 	struct ifnet ifstruct, *next;
2696920Ssam 	struct sockaddr_in net;
2706929Ssam 	register struct sockaddr *dst;
2716920Ssam 	int nets;
2726920Ssam 
2736929Ssam 	if (performnlist) {
2746929Ssam 		nlist("/vmunix", nl);
2756929Ssam 		if (nl[N_IFNET].n_value == 0) {
2766929Ssam 			performnlist++;
2776929Ssam 			printf("ifnet: not in namelist\n");
2786929Ssam 			return;
2796929Ssam 		}
2806929Ssam 		performnlist = 0;
2816920Ssam 	}
2826920Ssam 	if (kmem < 0) {
2836929Ssam 		kmem = open("/dev/kmem", 0);
2846929Ssam 		if (kmem < 0) {
2856929Ssam 			perror("/dev/kmem");
2866929Ssam 			return;
2876929Ssam 		}
2886920Ssam 	}
2896929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
2906929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
2916929Ssam 		performnlist = 1;
2926929Ssam 		return;
2936929Ssam 	}
2946920Ssam 	bzero((char *)&net, sizeof (net));
2956920Ssam 	net.sin_family = AF_INET;
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");
3026929Ssam 			performnlist = 1;
3036920Ssam 			break;
3046920Ssam 		}
3056929Ssam 		ifp = &ifstruct;
3066929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
3076929Ssam 			lookforinterfaces++;
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 
385*6934Ssam /*
386*6934Ssam  * Send "packet" to all neighbors.
387*6934Ssam  */
3886929Ssam sendall()
3896929Ssam {
390*6934Ssam 	register struct rthash *rh;
3916920Ssam 	register struct rt_entry *rt;
3926920Ssam 	register struct sockaddr *dst;
393*6934Ssam 	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) {
3996920Ssam 		if ((rt->rt_flags & RTF_SILENT) || 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;
421*6934Ssam 	register struct rthash *rh;
4226920Ssam 	register struct sockaddr *dst;
423*6934Ssam 	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) {
4296920Ssam 		if ((rt->rt_flags & RTF_SILENT) || 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;
453*6934Ssam 	register struct rthash *rh;
4546920Ssam 	register struct rt_entry *rt;
455*6934Ssam 	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 
5316920Ssam 	if (msg->rip_cmd != RIPCMD_RESPONSE &&
5326920Ssam 	    msg->rip_cmd != RIPCMD_REQUEST)
5336920Ssam 		return;
5346920Ssam 
5356920Ssam 	if (msg->rip_cmd == RIPCMD_RESPONSE &&
5366920Ssam 	    (*afswitch[from->sa_family].af_portmatch)(from) == 0)
5376920Ssam 		return;
5386920Ssam 	if (msg->rip_cmd == RIPCMD_REQUEST) {
5396920Ssam 		rip_respond(from, size);
5406920Ssam 		return;
5416920Ssam 	}
5426920Ssam 
5436920Ssam 	/*
5446920Ssam 	 * Process updates.
5456920Ssam 	 * Extraneous information like Internet ports
5466920Ssam 	 * must first be purged from the sender's address for
5476920Ssam 	 * pattern matching below.
5486920Ssam 	 */
5496920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5506920Ssam 	if (trace)
551*6934Ssam 		printf("input from %x\n",
552*6934Ssam 			((struct sockaddr_in *)from)->sin_addr);
5536920Ssam 	/*
5546920Ssam 	 * If response packet is from ourselves, use it only
5556920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5566920Ssam 	 * it as gospel (since it comes from the router) and
5576920Ssam 	 * unknowingly update the metric to show the outgoing
5586920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5596920Ssam 	 * spec doesn't address this because Xerox Ethernets
5606920Ssam 	 * don't hear their own broadcasts?
5616920Ssam 	 */
5626920Ssam 	if (if_ifwithaddr(from)) {
5636920Ssam 		rt = rtlookup(from);
5646920Ssam 		if (rt)
5656920Ssam 			rt->rt_timer = 0;
5666920Ssam 		return;
5676920Ssam 	}
5686929Ssam 	size -= 4 * sizeof (char);
5696920Ssam 	n = msg->rip_nets;
5706920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5716920Ssam 		if (size < sizeof (struct netinfo))
5726920Ssam 			break;
5736920Ssam 		if (trace)
574*6934Ssam 			printf("dst %x hc %d...",
575*6934Ssam 				((struct sockaddr_in *)&n->rip_dst)->sin_addr,
5766920Ssam 				n->rip_metric);
5776920Ssam 		rt = rtlookup(&n->rip_dst);
5786920Ssam 
5796920Ssam 		/*
5806920Ssam 		 * Unknown entry, add it to the tables only if
5816920Ssam 		 * its interesting.
5826920Ssam 		 */
5836920Ssam 		if (rt == 0) {
5846920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5856920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5866920Ssam 			if (trace)
5876920Ssam 				printf("new\n");
5886920Ssam 			continue;
5896920Ssam 		}
5906920Ssam 
5916920Ssam 		if (trace)
5926920Ssam 			printf("ours: gate %x hc %d timer %d\n",
593*6934Ssam 			  ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr,
594*6934Ssam 			  rt->rt_metric, rt->rt_timer);
5956920Ssam 		/*
5966920Ssam 		 * Update the entry if one of the following is true:
5976920Ssam 		 *
5986920Ssam 		 * (1) The update came directly from the gateway.
5996920Ssam 		 * (2) A shorter path is provided.
6006920Ssam 		 * (3) The entry hasn't been updated in a while
6016929Ssam 		 *     and a path of equivalent cost is offered
6026929Ssam 		 *     (with the cost finite).
6036920Ssam 		 */
6046920Ssam 		if (equal(from, &rt->rt_gateway) ||
6056920Ssam 		    rt->rt_metric > n->rip_metric ||
6066920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
6076929Ssam 		    rt->rt_metric == n->rip_metric &&
6086929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
6096920Ssam 			rtchange(rt, from, n->rip_metric);
6106920Ssam 			rt->rt_timer = 0;
6116920Ssam 		}
6126920Ssam 	}
6136920Ssam }
6146920Ssam 
615*6934Ssam rtinit()
616*6934Ssam {
617*6934Ssam 	register struct rthash *rh;
618*6934Ssam 
619*6934Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
620*6934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
621*6934Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
622*6934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
623*6934Ssam }
624*6934Ssam 
6256920Ssam /*
6266920Ssam  * Lookup an entry to the appropriate dstination.
6276920Ssam  */
6286920Ssam struct rt_entry *
6296920Ssam rtlookup(dst)
6306920Ssam 	struct sockaddr *dst;
6316920Ssam {
6326920Ssam 	register struct rt_entry *rt;
633*6934Ssam 	register struct rthash *rh;
6346920Ssam 	register int hash, (*match)();
6356920Ssam 	struct afhash h;
6366920Ssam 	int af = dst->sa_family, doinghost = 1;
6376920Ssam 
6386920Ssam 	if (af >= AF_MAX)
6396920Ssam 		return (0);
6406920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6416920Ssam 	hash = h.afh_hosthash;
6426920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6436920Ssam 
6446920Ssam again:
6456920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6466920Ssam 		if (rt->rt_hash != hash)
6476920Ssam 			continue;
6486920Ssam 		if (doinghost) {
6496920Ssam 			if (equal(&rt->rt_dst, dst))
6506920Ssam 				return (rt);
6516920Ssam 		} else {
6526920Ssam 			if (rt->rt_dst.sa_family == af &&
6536920Ssam 			    (*match)(&rt->rt_dst, dst))
6546920Ssam 				return (rt);
6556920Ssam 		}
6566920Ssam 	}
6576920Ssam 	if (doinghost) {
6586920Ssam 		doinghost = 0;
6596920Ssam 		hash = h.afh_nethash;
6606920Ssam 		match = afswitch[af].af_netmatch;
6616920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6626920Ssam 		goto again;
6636920Ssam 	}
6646920Ssam 	return (0);
6656920Ssam }
6666920Ssam 
6676920Ssam /*
6686920Ssam  * Add a new entry.
6696920Ssam  */
6706920Ssam rtadd(dst, gate, metric)
6716920Ssam 	struct sockaddr *dst, *gate;
6726920Ssam 	short metric;
6736920Ssam {
6746920Ssam 	struct afhash h;
6756920Ssam 	register struct rt_entry *rt;
676*6934Ssam 	struct rthash *rh;
6776920Ssam 	int af = dst->sa_family, flags, hash;
6786920Ssam 
6796920Ssam 	if (af >= AF_MAX)
6806920Ssam 		return;
6816920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6826920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6836920Ssam 	if (flags & RTF_HOST) {
6846920Ssam 		hash = h.afh_hosthash;
6856920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6866920Ssam 	} else {
6876920Ssam 		hash = h.afh_nethash;
6886920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6896920Ssam 	}
6906920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6916920Ssam 	if (rt == 0)
6926920Ssam 		return;
6936920Ssam 	rt->rt_hash = hash;
6946920Ssam 	rt->rt_dst = *dst;
6956920Ssam 	rt->rt_gateway = *gate;
6966920Ssam 	rt->rt_metric = metric;
6976920Ssam 	rt->rt_timer = 0;
6986920Ssam 	rt->rt_flags = RTF_UP | flags;
6996920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
7006920Ssam 	if (metric == 0)
7016920Ssam 		rt->rt_flags |= RTF_DIRECT;
7026920Ssam 	insque(rt, rh);
7036929Ssam 	log("add", rt);
7046920Ssam 	if (initializing)
7056920Ssam 		return;
7066920Ssam 	if (supplier)
7076920Ssam 		broadcast(rt);
7086920Ssam 	if (install) {
7096920Ssam 		rt->rt_flags |= RTF_ADDRT;
7106920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7116920Ssam 	}
7126920Ssam }
7136920Ssam 
7146920Ssam /*
7156920Ssam  * Look to see if a change to an existing entry
7166920Ssam  * is warranted; if so, make it.
7176920Ssam  */
7186920Ssam rtchange(rt, gate, metric)
7196920Ssam 	struct rt_entry *rt;
7206920Ssam 	struct sockaddr *gate;
7216920Ssam 	short metric;
7226920Ssam {
7236920Ssam 	int change = 0;
7246920Ssam 
7256920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
7266920Ssam 		rt->rt_gateway = *gate;
7276920Ssam 		change++;
7286920Ssam 	}
7296920Ssam 
7306920Ssam 	/*
7316920Ssam 	 * If the hop count has changed, adjust
7326920Ssam 	 * the flags in the routing table entry accordingly.
7336920Ssam 	 */
7346920Ssam 	if (metric != rt->rt_metric) {
7356920Ssam 		if (rt->rt_metric == 0)
7366920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7376920Ssam 		rt->rt_metric = metric;
7386920Ssam 		if (metric >= HOPCNT_INFINITY)
7396920Ssam 			rt->rt_flags &= ~RTF_UP;
7406920Ssam 		else
7416920Ssam 			rt->rt_flags |= RTF_UP;
7426920Ssam 		change++;
7436920Ssam 	}
7446920Ssam 
7456920Ssam 	if (!change)
7466920Ssam 		return;
7476920Ssam 	if (supplier)
7486920Ssam 		broadcast(rt);
7496929Ssam 	log("change", rt);
7506920Ssam 	if (install) {
7516920Ssam 		rt->rt_flags |= RTF_CHGRT;
7526920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7536920Ssam 	}
7546920Ssam }
7556920Ssam 
7566920Ssam /*
7576920Ssam  * Delete a routing table entry.
7586920Ssam  */
7596920Ssam rtdelete(rt)
7606920Ssam 	struct rt_entry *rt;
7616920Ssam {
7626929Ssam 	log("delete", rt);
7636920Ssam 	if (install)
764*6934Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) &&
7656920Ssam 		  errno == EBUSY)
7666920Ssam 			rt->rt_flags |= RTF_DELRT;
7676920Ssam 	remque(rt);
7686920Ssam 	free((char *)rt);
7696920Ssam }
7706920Ssam 
7716920Ssam log(operation, rt)
7726920Ssam 	char *operation;
7736920Ssam 	struct rt_entry *rt;
7746920Ssam {
7756920Ssam 	time_t t = time(0);
7766920Ssam 	struct sockaddr_in *dst, *gate;
7776920Ssam 	static struct flagbits {
7786920Ssam 		int	t_bits;
7796920Ssam 		char	*t_name;
7806920Ssam 	} bits[] = {
7816920Ssam 		{ RTF_UP,	"UP" },
7826920Ssam 		{ RTF_DIRECT,	"DIRECT" },
7836920Ssam 		{ RTF_HOST,	"HOST" },
7846920Ssam 		{ RTF_DELRT,	"DELETE" },
7856920Ssam 		{ RTF_CHGRT,	"CHANGE" },
7866920Ssam 		{ RTF_SILENT,	"SILENT" },
7876920Ssam 		{ 0 }
7886920Ssam 	};
7896920Ssam 	register struct flagbits *p;
7906920Ssam 	register int first;
7916920Ssam 	char *cp;
7926920Ssam 
7936929Ssam 	if (trace == 0)
7946929Ssam 		return;
7956920Ssam 	printf("%s ", operation);
7966920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
7976920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
7986920Ssam 	printf("dst %x, router %x, metric %d, flags ",
7996920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
8006920Ssam 	cp = "%s";
8016920Ssam 	for (first = 1, p = bits; p->t_bits > 0; p++) {
8026920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8036920Ssam 			continue;
8046920Ssam 		printf(cp, p->t_name);
8056920Ssam 		if (first) {
8066920Ssam 			cp = "|%s";
8076920Ssam 			first = 0;
8086920Ssam 		}
8096920Ssam 	}
8106920Ssam 	putchar('\n');
8116920Ssam }
8126929Ssam 
813*6934Ssam /*
814*6934Ssam  * Find the interface with address "addr".
815*6934Ssam  */
8166929Ssam struct ifnet *
8176929Ssam if_ifwithaddr(addr)
8186929Ssam 	struct sockaddr *addr;
8196929Ssam {
8206929Ssam 	register struct ifnet *ifp;
8216929Ssam 
8226929Ssam #define	same(a1, a2) \
8236929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
8246929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8256929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
8266929Ssam 			continue;
8276929Ssam 		if (same(&ifp->if_addr, addr))
8286929Ssam 			break;
8296929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
8306929Ssam 		    same(&ifp->if_broadaddr, addr))
8316929Ssam 			break;
8326929Ssam 	}
8336929Ssam 	return (ifp);
8346929Ssam #undef same
8356929Ssam }
8366929Ssam 
837*6934Ssam /*
838*6934Ssam  * Find the interface with network imbedded in
839*6934Ssam  * the sockaddr "addr".  Must use per-af routine
840*6934Ssam  * look for match.
841*6934Ssam  */
8426929Ssam struct ifnet *
8436929Ssam if_ifwithnet(addr)
8446929Ssam 	register struct sockaddr *addr;
8456929Ssam {
8466929Ssam 	register struct ifnet *ifp;
8476929Ssam 	register int af = addr->sa_family;
8486929Ssam 	register int (*netmatch)();
8496929Ssam 
8506929Ssam 	if (af >= AF_MAX)
8516929Ssam 		return (0);
8526929Ssam 	netmatch = afswitch[af].af_netmatch;
8536929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
8546929Ssam 		if (af != ifp->if_addr.sa_family)
8556929Ssam 			continue;
8566929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
8576929Ssam 			break;
8586929Ssam 	}
8596929Ssam 	return (ifp);
8606929Ssam }
8616929Ssam 
862*6934Ssam /*
863*6934Ssam  * Formulate an Internet address.
864*6934Ssam  */
8656929Ssam struct in_addr
8666929Ssam if_makeaddr(net, host)
8676929Ssam 	int net, host;
8686929Ssam {
8696929Ssam 	u_long addr;
8706929Ssam 
8716929Ssam 	if (net < 128)
8726929Ssam 		addr = (net << 24) | host;
8736929Ssam 	else if (net < 65536)
8746929Ssam 		addr = (net << 16) | host;
8756929Ssam 	else
8766929Ssam 		addr = (net << 8) | host;
8776929Ssam #ifdef vax
8786929Ssam 	addr = htonl(addr);
8796929Ssam #endif
8806929Ssam 	return (*(struct in_addr *)&addr);
8816929Ssam }
882