xref: /csrg-svn/sbin/routed/routed.c (revision 7029)
16920Ssam #ifndef lint
2*7029Ssam static char sccsid[] = "@(#)routed.c	4.12 06/05/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>
17*7029Ssam #include <time.h>
186920Ssam #include "rip.h"
196920Ssam #include "router.h"
206920Ssam 
216920Ssam #define	LOOPBACKNET	0177
226920Ssam /* casts to keep lint happy */
236920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
246920Ssam #define	remque(q)	_remque((caddr_t)q)
256920Ssam #define equal(a1, a2) \
266920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
276929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
286920Ssam 
296920Ssam struct nlist nl[] = {
306920Ssam #define	N_IFNET		0
316920Ssam 	{ "_ifnet" },
326920Ssam 	0,
336920Ssam };
346920Ssam 
356920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
366920Ssam 
376920Ssam int	s;
386929Ssam int	kmem = -1;
396920Ssam int	supplier;		/* process should supply updates */
406920Ssam int	initializing;		/* stem off broadcast() calls */
416962Ssam int	install = 1;		/* if 1 call kernel */
426929Ssam int	lookforinterfaces = 1;
436929Ssam int	performnlist = 1;
446920Ssam int	timeval;
456920Ssam int	timer();
466920Ssam int	cleanup();
476920Ssam int	trace = 0;
48*7029Ssam FILE	*ftrace;
496920Ssam 
506920Ssam char	packet[MAXPACKETSIZE];
516920Ssam 
526934Ssam struct in_addr if_makeaddr();
536934Ssam struct ifnet *if_ifwithaddr(), *if_ifwithnet();
546920Ssam extern char *malloc();
556922Ssam extern int errno, exit();
566920Ssam 
576920Ssam main(argc, argv)
586920Ssam 	int argc;
596920Ssam 	char *argv[];
606920Ssam {
616920Ssam 	int cc;
626920Ssam 	struct sockaddr from;
636920Ssam 
64*7029Ssam #ifndef DEBUG
65*7029Ssam 	if (fork())
66*7029Ssam 		exit(0);
67*7029Ssam 	for (cc = 0; cc < 10; cc++)
68*7029Ssam 		(void) close(cc);
69*7029Ssam 	(void) open("/", 0);
70*7029Ssam 	(void) dup2(0, 1);
71*7029Ssam 	(void) dup2(0, 2);
72*7029Ssam 	{ int t = open("/dev/tty", 2);
73*7029Ssam 	  if (t >= 0) {
74*7029Ssam 		ioctl(t, TIOCNOTTY, (char *)0);
75*7029Ssam 		(void) close(t);
76*7029Ssam 	  }
776920Ssam 	}
78*7029Ssam #endif
796920Ssam 	if (trace) {
80*7029Ssam 		ftrace = fopen("/etc/routerlog", "w");
81*7029Ssam 		dup2(fileno(ftrace), 1);
82*7029Ssam 		dup2(fileno(ftrace), 2);
836920Ssam 	}
846937Ssam #ifdef vax || pdp11
856920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
866920Ssam #endif
876920Ssam again:
886920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
896920Ssam 	if (s < 0) {
906920Ssam 		perror("socket");
916920Ssam 		sleep(30);
926920Ssam 		goto again;
936920Ssam 	}
946920Ssam 	rtinit();
956920Ssam 	getothers();
966929Ssam 	initializing = 1;
976920Ssam 	getinterfaces();
986929Ssam 	initializing = 0;
996920Ssam 	request();
1006920Ssam 
1016920Ssam 	argv++, argc--;
1026920Ssam 	while (argc > 0) {
1036920Ssam 		if (strcmp(*argv, "-s") == 0)
1046929Ssam 			supplier = 1;
1056920Ssam 		else if (strcmp(*argv, "-q") == 0)
1066920Ssam 			supplier = 0;
1076920Ssam 		argv++, argc--;
1086920Ssam 	}
1096920Ssam 	sigset(SIGALRM, timer);
1106929Ssam 	timer();
1116920Ssam 
1126920Ssam 	/*
1136920Ssam 	 * Listen for routing packets
1146920Ssam 	 */
1156920Ssam 	for (;;) {
1166920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1176920Ssam 		if (cc <= 0) {
1186920Ssam 			if (cc < 0 && errno != EINTR)
1196920Ssam 				perror("receive");
1206920Ssam 			continue;
1216920Ssam 		}
1226920Ssam 		sighold(SIGALRM);
1236920Ssam 		rip_input(&from, cc);
1246920Ssam 		sigrelse(SIGALRM);
1256920Ssam 	}
1266920Ssam }
1276920Ssam 
1286920Ssam /*
1296920Ssam  * Look in a file for any gateways we should configure
1306920Ssam  * outside the directly connected ones.  This is a kludge,
1316920Ssam  * but until we can find out about gateways on the "other side"
1326920Ssam  * of the ARPANET using GGP, it's a must.
1336920Ssam  *
1346920Ssam  * We don't really know the distance to the gateway, so we
1356920Ssam  * assume it's a neighbor.
1366920Ssam  */
1376920Ssam getothers()
1386920Ssam {
1396920Ssam 	struct sockaddr_in dst, gate;
1406920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
1416920Ssam 	struct rt_entry *rt;
1426920Ssam 
1436920Ssam 	if (fp == NULL)
1446920Ssam 		return;
1456920Ssam 	bzero((char *)&dst, sizeof (dst));
1466920Ssam 	bzero((char *)&gate, sizeof (gate));
1476920Ssam 	dst.sin_family = AF_INET;
1486920Ssam 	gate.sin_family = AF_INET;
1496920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
1506920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
1516920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
1526920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
1536920Ssam 		if (rt)
1546993Ssam 			rt->rt_state |= RTS_HIDDEN;
1556920Ssam 	}
1566920Ssam 	fclose(fp);
1576920Ssam }
1586920Ssam 
1596929Ssam /*
1606929Ssam  * Timer routine:
1616929Ssam  *
1626929Ssam  * o handle timers on table entries,
1636929Ssam  * o invalidate entries which haven't been updated in a while,
1646929Ssam  * o delete entries which are too old,
1656929Ssam  * o retry ioctl's which weren't successful the first
1666929Ssam  *   time due to the kernel entry being busy
1676929Ssam  * o if we're an internetwork router, supply routing updates
1686929Ssam  *   periodically
1696929Ssam  */
1706929Ssam timer()
1716920Ssam {
1726934Ssam 	register struct rthash *rh;
1736929Ssam 	register struct rt_entry *rt;
1746934Ssam 	struct rthash *base = hosthash;
1756929Ssam 	int doinghost = 1;
1766920Ssam 
1776929Ssam 	if (trace)
1786929Ssam 		printf(">>> time %d >>>\n", timeval);
1796929Ssam again:
1806929Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
1816929Ssam 		rt = rh->rt_forw;
1826929Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1836920Ssam 
1846929Ssam 			/*
1856929Ssam 			 * If the host is indicated to be
1866993Ssam 			 * "hidden" (i.e. it's one we got
1876929Ssam 			 * from the initialization file),
1886929Ssam 			 * don't time out it's entry.
1896929Ssam 			 */
1906993Ssam 			if ((rt->rt_state & RTS_HIDDEN) == 0)
1916933Ssam 				rt->rt_timer += TIMER_RATE;
1926929Ssam 			log("", rt);
1936920Ssam 
1946929Ssam 			/*
1956929Ssam 			 * If the entry should be deleted
1966929Ssam 			 * attempt to do so and reclaim space.
1976929Ssam 			 */
1986929Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
1996937Ssam 			  (rt->rt_state & RTS_DELRT)) {
2006929Ssam 				rt = rt->rt_back;
2016937Ssam 				rtdelete(rt->rt_forw);
2026929Ssam 				continue;
2036929Ssam 			}
2046920Ssam 
2056929Ssam 			/*
2066929Ssam 			 * If we haven't heard from the router
2076929Ssam 			 * in a long time indicate the route is
2086929Ssam 			 * hard to reach.
2096929Ssam 			 */
2106929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
2116929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
2126937Ssam 			if (rt->rt_state & RTS_CHGRT)
2136934Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_rt) ||
2146929Ssam 				  --rt->rt_retry == 0)
2156937Ssam 					rt->rt_state &= ~RTS_CHGRT;
2166920Ssam 
2176929Ssam 			/*
2186929Ssam 			 * Try to add the route to the kernel tables.
2196929Ssam 			 * If this fails because the entry already exists
2206929Ssam 			 * (perhaps because someone manually added it)
2216929Ssam 			 * change the add to a change.  If the operation
2226929Ssam 			 * fails otherwise (likely because the entry is
2236929Ssam 			 * in use), retry the operation a few more times.
2246929Ssam 			 */
2256937Ssam 			if (rt->rt_state & RTS_ADDRT) {
2266934Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_rt)) {
2276929Ssam 					if (errno == EEXIST) {
2286937Ssam 						rt->rt_state &= ~RTS_ADDRT;
2296937Ssam 						rt->rt_state |= RTS_CHGRT;
2306929Ssam 						rt->rt_retry =
2316929Ssam 						    (EXPIRE_TIME/TIMER_RATE);
2326929Ssam 						continue;
2336929Ssam 					}
2346929Ssam 					if (--rt->rt_retry)
2356929Ssam 						continue;
2366929Ssam 				}
2376937Ssam 				rt->rt_state &= ~RTS_ADDRT;
2386929Ssam 			}
2396929Ssam 		}
2406929Ssam 	}
2416929Ssam 	if (doinghost) {
2426929Ssam 		doinghost = 0;
2436929Ssam 		base = nethash;
2446929Ssam 		goto again;
2456929Ssam 	}
2466929Ssam 	timeval += TIMER_RATE;
2476929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
2486929Ssam 		getinterfaces();
2496929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
2506929Ssam 		supplyall();
2516929Ssam 	if (trace)
2526929Ssam 		printf("<<< time %d <<<\n", timeval);
2536929Ssam 	alarm(TIMER_RATE);
2546920Ssam }
2556920Ssam 
2566934Ssam struct	ifnet *ifnet;
2576920Ssam /*
2586920Ssam  * Find the network interfaces attached to this machine.
2596929Ssam  * The info is used to:
2606920Ssam  *
2616920Ssam  * (1) initialize the routing tables, as done by the kernel.
2626920Ssam  * (2) ignore incoming packets we send.
2636920Ssam  * (3) figure out broadcast capability and addresses.
2646920Ssam  * (4) figure out if we're an internetwork gateway.
2656920Ssam  *
2666920Ssam  * We don't handle anything but Internet addresses.
2676934Ssam  *
2686934Ssam  * Note: this routine may be called periodically to
2696934Ssam  * scan for new interfaces.  In fact, the timer routine
2706934Ssam  * does so based on the flag lookforinterfaces.  The
2716934Ssam  * flag performnlist is set whenever something odd occurs
2726934Ssam  * while scanning the kernel; this is likely to occur
2736934Ssam  * if /vmunix isn't up to date (e.g. someone booted /ovmunix).
2746920Ssam  */
2756920Ssam getinterfaces()
2766920Ssam {
2776929Ssam 	struct ifnet *ifp;
2786929Ssam 	struct ifnet ifstruct, *next;
2796920Ssam 	struct sockaddr_in net;
2806929Ssam 	register struct sockaddr *dst;
2816920Ssam 	int nets;
2826920Ssam 
2836929Ssam 	if (performnlist) {
2846929Ssam 		nlist("/vmunix", nl);
2856929Ssam 		if (nl[N_IFNET].n_value == 0) {
2866929Ssam 			performnlist++;
2876929Ssam 			printf("ifnet: not in namelist\n");
2886929Ssam 			return;
2896929Ssam 		}
2906929Ssam 		performnlist = 0;
2916920Ssam 	}
2926920Ssam 	if (kmem < 0) {
2936929Ssam 		kmem = open("/dev/kmem", 0);
2946929Ssam 		if (kmem < 0) {
2956929Ssam 			perror("/dev/kmem");
2966929Ssam 			return;
2976929Ssam 		}
2986920Ssam 	}
2996929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
3006929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
3016929Ssam 		performnlist = 1;
3026929Ssam 		return;
3036929Ssam 	}
3046920Ssam 	bzero((char *)&net, sizeof (net));
3056920Ssam 	net.sin_family = AF_INET;
3066961Ssam 	lookforinterfaces = 0;
3076920Ssam 	nets = 0;
3086929Ssam 	while (ifp) {
3096929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
3106929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
3116929Ssam 		  sizeof (ifstruct)) {
3126920Ssam 			perror("read");
3136961Ssam 			lookforinterfaces = performnlist = 1;
3146920Ssam 			break;
3156920Ssam 		}
3166929Ssam 		ifp = &ifstruct;
3176929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
3186961Ssam 			lookforinterfaces = 1;
3196929Ssam 	skip:
3206929Ssam 			ifp = ifp->if_next;
3216929Ssam 			continue;
3226929Ssam 		}
3236929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
3246929Ssam 			goto skip;
3256929Ssam 		/* ignore software loopback networks. */
3266920Ssam 		if (ifp->if_net == LOOPBACKNET)
3276920Ssam 			goto skip;
3286929Ssam 		/* check if we already know about this one */
3296929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
3306929Ssam 			nets++;
3316920Ssam 			goto skip;
3326920Ssam 		}
3336929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
3346929Ssam 		if (ifp == 0) {
3356929Ssam 			printf("routed: out of memory\n");
3366929Ssam 			break;
3376929Ssam 		}
3386929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3396920Ssam 
3406920Ssam 		/*
3416929Ssam 		 * Count the # of directly connected networks
3426929Ssam 		 * and point to point links which aren't looped
3436929Ssam 		 * back to ourself.  This is used below to
3446929Ssam 		 * decide if we should be a routing "supplier".
3456920Ssam 		 */
3466929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
3476929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
3486929Ssam 			nets++;
3496920Ssam 
3506929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
3516929Ssam 			dst = &ifp->if_dstaddr;
3526929Ssam 		else {
3536929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
3546929Ssam 			dst = (struct sockaddr *)&net;
3556929Ssam 		}
3566929Ssam 		next = ifp->if_next;
3576929Ssam 		ifp->if_next = ifnet;
3586929Ssam 		ifnet = ifp;
3596929Ssam 		if (rtlookup(dst) == 0)
3606929Ssam 			rtadd(dst, &ifp->if_addr, 0);
3616929Ssam 		ifp = next;
3626920Ssam 	}
3636920Ssam 	supplier = nets > 1;
3646920Ssam }
3656920Ssam 
3666920Ssam /*
3676920Ssam  * Send a request message to all directly
3686920Ssam  * connected hosts and networks.
3696920Ssam  */
3706920Ssam request()
3716920Ssam {
3726929Ssam 	register struct rip *msg = (struct rip *)packet;
3736920Ssam 
3746929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
3756929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
3766929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
3776929Ssam 	sendall();
3786920Ssam }
3796920Ssam 
3806920Ssam /*
3816920Ssam  * Broadcast a new, or modified, routing table entry
3826920Ssam  * to all directly connected hosts and networks.
3836920Ssam  */
3846920Ssam broadcast(entry)
3856920Ssam 	struct rt_entry *entry;
3866920Ssam {
3876929Ssam 	struct rip *msg = (struct rip *)packet;
3886929Ssam 
3896929Ssam 	log("broadcast", entry);
3906929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
3916929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
3926929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
3936929Ssam 	sendall();
3946929Ssam }
3956929Ssam 
3966934Ssam /*
3976934Ssam  * Send "packet" to all neighbors.
3986934Ssam  */
3996929Ssam sendall()
4006929Ssam {
4016934Ssam 	register struct rthash *rh;
4026920Ssam 	register struct rt_entry *rt;
4036920Ssam 	register struct sockaddr *dst;
4046934Ssam 	struct rthash *base = hosthash;
4056920Ssam 	int doinghost = 1;
4066920Ssam 
4076920Ssam again:
4086920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4096920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4106993Ssam 		if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0)
4116920Ssam 			continue;
4126920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4136920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4146920Ssam 		else
4156920Ssam 			dst = &rt->rt_gateway;
4166920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
4176920Ssam 	}
4186920Ssam 	if (doinghost) {
4196929Ssam 		base = nethash;
4206920Ssam 		doinghost = 0;
4216920Ssam 		goto again;
4226920Ssam 	}
4236920Ssam }
4246920Ssam 
4256920Ssam /*
4266920Ssam  * Supply all directly connected neighbors with the
4276920Ssam  * current state of the routing tables.
4286920Ssam  */
4296920Ssam supplyall()
4306920Ssam {
4316920Ssam 	register struct rt_entry *rt;
4326934Ssam 	register struct rthash *rh;
4336920Ssam 	register struct sockaddr *dst;
4346934Ssam 	struct rthash *base = hosthash;
4356920Ssam 	int doinghost = 1;
4366920Ssam 
4376920Ssam again:
4386920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4396920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4406993Ssam 		if ((rt->rt_state & RTS_HIDDEN) || rt->rt_metric > 0)
4416920Ssam 			continue;
4426920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4436920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4446920Ssam 		else
4456920Ssam 			dst = &rt->rt_gateway;
4466929Ssam 		log("supply", rt);
4476920Ssam 		supply(dst);
4486920Ssam 	}
4496920Ssam 	if (doinghost) {
4506920Ssam 		base = nethash;
4516920Ssam 		doinghost = 0;
4526920Ssam 		goto again;
4536920Ssam 	}
4546920Ssam }
4556920Ssam 
4566920Ssam /*
4576920Ssam  * Supply routing information to target "sa".
4586920Ssam  */
4596920Ssam supply(sa)
4606920Ssam 	struct sockaddr *sa;
4616920Ssam {
4626920Ssam 	struct rip *msg = (struct rip *)packet;
4636920Ssam 	struct netinfo *n = msg->rip_nets;
4646934Ssam 	register struct rthash *rh;
4656920Ssam 	register struct rt_entry *rt;
4666934Ssam 	struct rthash *base = hosthash;
4676929Ssam 	int doinghost = 1, size;
4686920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4696920Ssam 
4706920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4716920Ssam again:
4726920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4736920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4746920Ssam 
4756920Ssam 		/*
4766920Ssam 		 * Flush packet out if not enough room for
4776920Ssam 		 * another routing table entry.
4786920Ssam 		 */
4796929Ssam 		size = (char *)n - packet;
4806929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
4816929Ssam 			(*output)(sa, size);
4826920Ssam 			n = msg->rip_nets;
4836920Ssam 		}
4846920Ssam 		n->rip_dst = rt->rt_dst;
4856929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
4866929Ssam 		n++;
4876920Ssam 	}
4886920Ssam 	if (doinghost) {
4896920Ssam 		doinghost = 0;
4906920Ssam 		base = nethash;
4916920Ssam 		goto again;
4926920Ssam 	}
4936929Ssam 	if (n != msg->rip_nets)
4946929Ssam 		(*output)(sa, (char *)n - packet);
4956920Ssam }
4966920Ssam 
4976920Ssam /*
4986920Ssam  * Respond to a routing info request.
4996920Ssam  */
5006920Ssam rip_respond(from, size)
5016920Ssam 	struct sockaddr *from;
5026920Ssam 	int size;
5036920Ssam {
5046920Ssam 	register struct rip *msg = (struct rip *)packet;
5056920Ssam 	struct netinfo *np = msg->rip_nets;
5066920Ssam 	struct rt_entry *rt;
5076920Ssam 	int newsize = 0;
5086920Ssam 
5096929Ssam 	size -= 4 * sizeof (char);
5106920Ssam 	while (size > 0) {
5116920Ssam 		if (size < sizeof (struct netinfo))
5126920Ssam 			break;
5136920Ssam 		size -= sizeof (struct netinfo);
5146920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
5156920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
5166920Ssam 			supply(from);
5176920Ssam 			return;
5186920Ssam 		}
5196920Ssam 		rt = rtlookup(&np->rip_dst);
5206929Ssam 		np->rip_metric = rt == 0 ?
5216929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
5226920Ssam 		np++, newsize += sizeof (struct netinfo);
5236920Ssam 	}
5246920Ssam 	if (newsize > 0) {
5256920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5266920Ssam 		newsize += sizeof (int);
5276920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
5286920Ssam 	}
5296920Ssam }
5306920Ssam 
5316920Ssam /*
5326920Ssam  * Handle an incoming routing packet.
5336920Ssam  */
5346920Ssam rip_input(from, size)
5356920Ssam 	struct sockaddr *from;
5366920Ssam 	int size;
5376920Ssam {
5386920Ssam 	register struct rip *msg = (struct rip *)packet;
5396920Ssam 	struct rt_entry *rt;
5406920Ssam 	struct netinfo *n;
5416920Ssam 
5426937Ssam 	switch (msg->rip_cmd) {
5436937Ssam 
5446937Ssam 	default:
5456920Ssam 		return;
5466920Ssam 
5476937Ssam 	case RIPCMD_REQUEST:
5486920Ssam 		rip_respond(from, size);
5496920Ssam 		return;
5506937Ssam 
551*7029Ssam 	case RIPCMD_TRACEON:
552*7029Ssam 	case RIPCMD_TRACEOFF:
553*7029Ssam 		/* verify message came from a priviledged port */
554*7029Ssam 		if ((*afswitch[from->sa_family].af_portcheck)(from) == 0)
555*7029Ssam 			return;
556*7029Ssam 		tracecmd(msg->rip_cmd, msg, size);
557*7029Ssam 		return;
558*7029Ssam 
5596937Ssam 	case RIPCMD_RESPONSE:
560*7029Ssam 		/* verify message came from a router */
5616937Ssam 		if ((*afswitch[from->sa_family].af_portmatch)(from) == 0)
5626937Ssam 			return;
5636937Ssam 		break;
5646920Ssam 	}
5656920Ssam 
5666920Ssam 	/*
5676920Ssam 	 * Process updates.
5686920Ssam 	 * Extraneous information like Internet ports
5696920Ssam 	 * must first be purged from the sender's address for
5706920Ssam 	 * pattern matching below.
5716920Ssam 	 */
5726920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5736920Ssam 	if (trace)
5746934Ssam 		printf("input from %x\n",
5756934Ssam 			((struct sockaddr_in *)from)->sin_addr);
5766920Ssam 	/*
5776920Ssam 	 * If response packet is from ourselves, use it only
5786920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5796920Ssam 	 * it as gospel (since it comes from the router) and
5806920Ssam 	 * unknowingly update the metric to show the outgoing
5816920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5826920Ssam 	 * spec doesn't address this because Xerox Ethernets
5836920Ssam 	 * don't hear their own broadcasts?
5846920Ssam 	 */
5856920Ssam 	if (if_ifwithaddr(from)) {
5867011Ssam 		rt = rtfind(from);
5876920Ssam 		if (rt)
5886920Ssam 			rt->rt_timer = 0;
5896920Ssam 		return;
5906920Ssam 	}
5916929Ssam 	size -= 4 * sizeof (char);
5926920Ssam 	n = msg->rip_nets;
5936920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5946920Ssam 		if (size < sizeof (struct netinfo))
5956920Ssam 			break;
5966920Ssam 		if (trace)
5976934Ssam 			printf("dst %x hc %d...",
5986934Ssam 				((struct sockaddr_in *)&n->rip_dst)->sin_addr,
5996920Ssam 				n->rip_metric);
6006920Ssam 		rt = rtlookup(&n->rip_dst);
6016920Ssam 
6026920Ssam 		/*
6036920Ssam 		 * Unknown entry, add it to the tables only if
6046920Ssam 		 * its interesting.
6056920Ssam 		 */
6066920Ssam 		if (rt == 0) {
6076920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
6086920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
6096920Ssam 			if (trace)
6106920Ssam 				printf("new\n");
6116920Ssam 			continue;
6126920Ssam 		}
6136920Ssam 
6146920Ssam 		if (trace)
6156920Ssam 			printf("ours: gate %x hc %d timer %d\n",
6166934Ssam 			  ((struct sockaddr_in *)&rt->rt_gateway)->sin_addr,
6176934Ssam 			  rt->rt_metric, rt->rt_timer);
6186920Ssam 		/*
6196920Ssam 		 * Update the entry if one of the following is true:
6206920Ssam 		 *
6216920Ssam 		 * (1) The update came directly from the gateway.
6226920Ssam 		 * (2) A shorter path is provided.
6236920Ssam 		 * (3) The entry hasn't been updated in a while
6246929Ssam 		 *     and a path of equivalent cost is offered
6256929Ssam 		 *     (with the cost finite).
6266920Ssam 		 */
6276920Ssam 		if (equal(from, &rt->rt_gateway) ||
6286920Ssam 		    rt->rt_metric > n->rip_metric ||
6296920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
6306929Ssam 		    rt->rt_metric == n->rip_metric &&
6316929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
6326920Ssam 			rtchange(rt, from, n->rip_metric);
6336920Ssam 			rt->rt_timer = 0;
6346920Ssam 		}
6356920Ssam 	}
6366920Ssam }
6376920Ssam 
638*7029Ssam /*
639*7029Ssam  * Handle tracing requests.
640*7029Ssam  */
641*7029Ssam tracecmd(cmd, msg, size)
642*7029Ssam 	int cmd, size;
643*7029Ssam 	struct rip *msg;
644*7029Ssam {
645*7029Ssam 	time_t t;
646*7029Ssam 
647*7029Ssam 	if (cmd == RIPCMD_TRACEOFF) {
648*7029Ssam 		if (!trace)
649*7029Ssam 			return;
650*7029Ssam 		t = time(0);
651*7029Ssam 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
652*7029Ssam 		fflush(stdout), fflush(stderr);
653*7029Ssam 		if (ftrace)
654*7029Ssam 			fclose(ftrace);
655*7029Ssam 		(void) close(1), (void) close(2);
656*7029Ssam 		trace = 0;
657*7029Ssam 		return;
658*7029Ssam 	}
659*7029Ssam 	if (trace)
660*7029Ssam 		return;
661*7029Ssam 	packet[size] = '\0';
662*7029Ssam 	ftrace = fopen(msg->rip_tracefile, "a");
663*7029Ssam 	if (ftrace == NULL)
664*7029Ssam 		return;
665*7029Ssam 	(void) dup2(fileno(ftrace), 1);
666*7029Ssam 	(void) dup2(fileno(ftrace), 2);
667*7029Ssam 	trace = 1;
668*7029Ssam 	t = time(0);
669*7029Ssam 	printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
670*7029Ssam }
671*7029Ssam 
6726934Ssam rtinit()
6736934Ssam {
6746934Ssam 	register struct rthash *rh;
6756934Ssam 
6766934Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6776934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6786934Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6796934Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6806934Ssam }
6816934Ssam 
6826920Ssam /*
6836920Ssam  * Lookup an entry to the appropriate dstination.
6846920Ssam  */
6856920Ssam struct rt_entry *
6866920Ssam rtlookup(dst)
6876920Ssam 	struct sockaddr *dst;
6886920Ssam {
6896920Ssam 	register struct rt_entry *rt;
6906934Ssam 	register struct rthash *rh;
6917011Ssam 	register int hash;
6926920Ssam 	struct afhash h;
6937011Ssam 	int doinghost = 1;
6946920Ssam 
6957011Ssam 	if (dst->sa_family >= AF_MAX)
6967011Ssam 		return (0);
6977011Ssam 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
6987011Ssam 	hash = h.afh_hosthash;
6997011Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
7007011Ssam again:
7017011Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7027011Ssam 		if (rt->rt_hash != hash)
7037011Ssam 			continue;
7047011Ssam 		if (equal(&rt->rt_dst, dst))
7057011Ssam 			return (rt);
7067011Ssam 	}
7077011Ssam 	if (doinghost) {
7087011Ssam 		doinghost = 0;
7097011Ssam 		hash = h.afh_nethash;
7107011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7117011Ssam 		goto again;
7127011Ssam 	}
7137011Ssam 	return (0);
7147011Ssam }
7157011Ssam 
7167011Ssam /*
7177011Ssam  * Find an entry based on address "dst", as the kernel
7187011Ssam  * does in selecting routes.  This means we look first
7197011Ssam  * for a point to point link, settling for a route to
7207011Ssam  * the destination network if the former fails.
7217011Ssam  */
7227011Ssam struct rt_entry *
7237011Ssam rtfind(dst)
7247011Ssam 	struct sockaddr *dst;
7257011Ssam {
7267011Ssam 	register struct rt_entry *rt;
7277011Ssam 	register struct rthash *rh;
7287011Ssam 	register int hash;
7297011Ssam 	struct afhash h;
7307011Ssam 	int af = dst->sa_family;
7317011Ssam 	int doinghost = 1, (*match)();
7327011Ssam 
7336920Ssam 	if (af >= AF_MAX)
7346920Ssam 		return (0);
7356920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7366920Ssam 	hash = h.afh_hosthash;
7376920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
7386920Ssam 
7396920Ssam again:
7406920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7416920Ssam 		if (rt->rt_hash != hash)
7426920Ssam 			continue;
7436920Ssam 		if (doinghost) {
7446920Ssam 			if (equal(&rt->rt_dst, dst))
7456920Ssam 				return (rt);
7466920Ssam 		} else {
7476920Ssam 			if (rt->rt_dst.sa_family == af &&
7486920Ssam 			    (*match)(&rt->rt_dst, dst))
7496920Ssam 				return (rt);
7506920Ssam 		}
7516920Ssam 	}
7526920Ssam 	if (doinghost) {
7536920Ssam 		doinghost = 0;
7546920Ssam 		hash = h.afh_nethash;
7557011Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7566920Ssam 		match = afswitch[af].af_netmatch;
7576920Ssam 		goto again;
7586920Ssam 	}
7596920Ssam 	return (0);
7606920Ssam }
7616920Ssam 
7626920Ssam /*
7636920Ssam  * Add a new entry.
7646920Ssam  */
7656920Ssam rtadd(dst, gate, metric)
7666920Ssam 	struct sockaddr *dst, *gate;
7676920Ssam 	short metric;
7686920Ssam {
7696920Ssam 	struct afhash h;
7706920Ssam 	register struct rt_entry *rt;
7716934Ssam 	struct rthash *rh;
7726920Ssam 	int af = dst->sa_family, flags, hash;
7736920Ssam 
7746920Ssam 	if (af >= AF_MAX)
7756920Ssam 		return;
7766920Ssam 	(*afswitch[af].af_hash)(dst, &h);
7776920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
7786920Ssam 	if (flags & RTF_HOST) {
7796920Ssam 		hash = h.afh_hosthash;
7806920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
7816920Ssam 	} else {
7826920Ssam 		hash = h.afh_nethash;
7836920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
7846920Ssam 	}
7856920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
7866920Ssam 	if (rt == 0)
7876920Ssam 		return;
7886920Ssam 	rt->rt_hash = hash;
7896920Ssam 	rt->rt_dst = *dst;
7906920Ssam 	rt->rt_gateway = *gate;
7916920Ssam 	rt->rt_metric = metric;
7926920Ssam 	rt->rt_timer = 0;
7936920Ssam 	rt->rt_flags = RTF_UP | flags;
7946937Ssam 	rt->rt_state = 0;
7956920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
7966920Ssam 	if (metric == 0)
7976920Ssam 		rt->rt_flags |= RTF_DIRECT;
7986920Ssam 	insque(rt, rh);
7996929Ssam 	log("add", rt);
8006920Ssam 	if (initializing)
8016920Ssam 		return;
8026920Ssam 	if (supplier)
8036920Ssam 		broadcast(rt);
8046920Ssam 	if (install) {
8056937Ssam 		rt->rt_state |= RTS_ADDRT;
8066920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
8076920Ssam 	}
8086920Ssam }
8096920Ssam 
8106920Ssam /*
8116920Ssam  * Look to see if a change to an existing entry
8126920Ssam  * is warranted; if so, make it.
8136920Ssam  */
8146920Ssam rtchange(rt, gate, metric)
8156920Ssam 	struct rt_entry *rt;
8166920Ssam 	struct sockaddr *gate;
8176920Ssam 	short metric;
8186920Ssam {
8196920Ssam 	int change = 0;
8206920Ssam 
8216920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
8226920Ssam 		rt->rt_gateway = *gate;
8236920Ssam 		change++;
8246920Ssam 	}
8256920Ssam 
8266920Ssam 	/*
8276920Ssam 	 * If the hop count has changed, adjust
8286920Ssam 	 * the flags in the routing table entry accordingly.
8296920Ssam 	 */
8306920Ssam 	if (metric != rt->rt_metric) {
8316920Ssam 		if (rt->rt_metric == 0)
8326920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
8336920Ssam 		rt->rt_metric = metric;
8346920Ssam 		if (metric >= HOPCNT_INFINITY)
8356920Ssam 			rt->rt_flags &= ~RTF_UP;
8366920Ssam 		else
8376920Ssam 			rt->rt_flags |= RTF_UP;
8386920Ssam 		change++;
8396920Ssam 	}
8406920Ssam 
8416920Ssam 	if (!change)
8426920Ssam 		return;
8436920Ssam 	if (supplier)
8446920Ssam 		broadcast(rt);
8456929Ssam 	log("change", rt);
8466920Ssam 	if (install) {
8476937Ssam 		rt->rt_state |= RTS_CHGRT;
8486920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
8496920Ssam 	}
8506920Ssam }
8516920Ssam 
8526920Ssam /*
8536920Ssam  * Delete a routing table entry.
8546920Ssam  */
8556920Ssam rtdelete(rt)
8566920Ssam 	struct rt_entry *rt;
8576920Ssam {
8586929Ssam 	log("delete", rt);
8596920Ssam 	if (install)
8606934Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_rt) &&
8616920Ssam 		  errno == EBUSY)
8626937Ssam 			rt->rt_state |= RTS_DELRT;
8636920Ssam 	remque(rt);
8646920Ssam 	free((char *)rt);
8656920Ssam }
8666920Ssam 
8676920Ssam log(operation, rt)
8686920Ssam 	char *operation;
8696920Ssam 	struct rt_entry *rt;
8706920Ssam {
8716920Ssam 	time_t t = time(0);
8726920Ssam 	struct sockaddr_in *dst, *gate;
8736937Ssam 	static struct bits {
8746920Ssam 		int	t_bits;
8756920Ssam 		char	*t_name;
8766937Ssam 	} flagbits[] = {
8776920Ssam 		{ RTF_UP,	"UP" },
8786920Ssam 		{ RTF_DIRECT,	"DIRECT" },
8796920Ssam 		{ RTF_HOST,	"HOST" },
8806920Ssam 		{ 0 }
8816937Ssam 	}, statebits[] = {
8826937Ssam 		{ RTS_DELRT,	"DELETE" },
8836937Ssam 		{ RTS_CHGRT,	"CHANGE" },
8846993Ssam 		{ RTS_HIDDEN,	"HIDDEN" },
8856937Ssam 		{ 0 }
8866920Ssam 	};
8876937Ssam 	register struct bits *p;
8886920Ssam 	register int first;
8896920Ssam 	char *cp;
8906920Ssam 
8916929Ssam 	if (trace == 0)
8926929Ssam 		return;
8936920Ssam 	printf("%s ", operation);
8946920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8956920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
8966937Ssam 	printf("dst %x, router %x, metric %d, flags",
8976920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
8986937Ssam 	cp = " %s";
8996937Ssam 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
9006920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
9016920Ssam 			continue;
9026920Ssam 		printf(cp, p->t_name);
9036920Ssam 		if (first) {
9046920Ssam 			cp = "|%s";
9056920Ssam 			first = 0;
9066920Ssam 		}
9076920Ssam 	}
9086937Ssam 	printf(" state");
9096937Ssam 	cp = " %s";
9106937Ssam 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
9116937Ssam 		if ((rt->rt_state & p->t_bits) == 0)
9126937Ssam 			continue;
9136937Ssam 		printf(cp, p->t_name);
9146937Ssam 		if (first) {
9156937Ssam 			cp = "|%s";
9166937Ssam 			first = 0;
9176937Ssam 		}
9186937Ssam 	}
9196920Ssam 	putchar('\n');
9206920Ssam }
9216929Ssam 
9226934Ssam /*
9236934Ssam  * Find the interface with address "addr".
9246934Ssam  */
9256929Ssam struct ifnet *
9266929Ssam if_ifwithaddr(addr)
9276929Ssam 	struct sockaddr *addr;
9286929Ssam {
9296929Ssam 	register struct ifnet *ifp;
9306929Ssam 
9316929Ssam #define	same(a1, a2) \
9326929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
9336929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
9346929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
9356929Ssam 			continue;
9366929Ssam 		if (same(&ifp->if_addr, addr))
9376929Ssam 			break;
9386929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
9396929Ssam 		    same(&ifp->if_broadaddr, addr))
9406929Ssam 			break;
9416929Ssam 	}
9426929Ssam 	return (ifp);
9436929Ssam #undef same
9446929Ssam }
9456929Ssam 
9466934Ssam /*
9476934Ssam  * Find the interface with network imbedded in
9486934Ssam  * the sockaddr "addr".  Must use per-af routine
9496934Ssam  * look for match.
9506934Ssam  */
9516929Ssam struct ifnet *
9526929Ssam if_ifwithnet(addr)
9536929Ssam 	register struct sockaddr *addr;
9546929Ssam {
9556929Ssam 	register struct ifnet *ifp;
9566929Ssam 	register int af = addr->sa_family;
9576929Ssam 	register int (*netmatch)();
9586929Ssam 
9596929Ssam 	if (af >= AF_MAX)
9606929Ssam 		return (0);
9616929Ssam 	netmatch = afswitch[af].af_netmatch;
9626929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
9636929Ssam 		if (af != ifp->if_addr.sa_family)
9646929Ssam 			continue;
9656929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
9666929Ssam 			break;
9676929Ssam 	}
9686929Ssam 	return (ifp);
9696929Ssam }
9706929Ssam 
9716934Ssam /*
9726934Ssam  * Formulate an Internet address.
9736934Ssam  */
9746929Ssam struct in_addr
9756929Ssam if_makeaddr(net, host)
9766929Ssam 	int net, host;
9776929Ssam {
9786929Ssam 	u_long addr;
9796929Ssam 
9806929Ssam 	if (net < 128)
9816929Ssam 		addr = (net << 24) | host;
9826929Ssam 	else if (net < 65536)
9836929Ssam 		addr = (net << 16) | host;
9846929Ssam 	else
9856929Ssam 		addr = (net << 8) | host;
9866929Ssam #ifdef vax
9876929Ssam 	addr = htonl(addr);
9886929Ssam #endif
9896929Ssam 	return (*(struct in_addr *)&addr);
9906929Ssam }
991