xref: /csrg-svn/sbin/routed/routed.c (revision 6922)
16920Ssam #ifndef lint
2*6922Ssam static char sccsid[] = "@(#)routed.c	4.2 05/23/82";
36920Ssam #endif
46920Ssam 
56920Ssam #include <sys/param.h>
66920Ssam #include <sys/protosw.h>
76920Ssam #include <sys/ioctl.h>
86920Ssam #include <sys/socket.h>
96920Ssam #include <net/in.h>
106920Ssam #define	KERNEL
116920Ssam #include <net/route.h>
126920Ssam #include <net/if.h>
136920Ssam #include <errno.h>
146920Ssam #include <stdio.h>
156920Ssam #include <nlist.h>
166920Ssam #include <signal.h>
176920Ssam #include "rip.h"
186920Ssam #include "router.h"
196920Ssam 
206920Ssam #define	LOOPBACKNET	0177
216920Ssam /* casts to keep lint happy */
226920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
236920Ssam #define	remque(q)	_remque((caddr_t)q)
246920Ssam #define equal(a1, a2) \
256920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
266920Ssam 
276920Ssam struct nlist nl[] = {
286920Ssam #define	N_IFNET		0
296920Ssam 	{ "_ifnet" },
306920Ssam 	0,
316920Ssam };
326920Ssam 
336920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
346920Ssam 
356920Ssam int	s;
366920Ssam int	kmem;
376920Ssam int	supplier;		/* process should supply updates */
386920Ssam int	initializing;		/* stem off broadcast() calls */
396920Ssam int	install = 0;		/* if 1 call kernel */
406920Ssam int	timeval;
416920Ssam int	timer();
426920Ssam int	cleanup();
436920Ssam int	trace = 0;
446920Ssam 
456920Ssam char	packet[MAXPACKETSIZE];
466920Ssam 
476920Ssam extern char *malloc();
48*6922Ssam extern int errno, exit();
496920Ssam 
506920Ssam main(argc, argv)
516920Ssam 	int argc;
526920Ssam 	char *argv[];
536920Ssam {
546920Ssam 	int cc;
556920Ssam 	struct sockaddr from;
566920Ssam 
576920Ssam 	{   int t = open("/dev/tty", 2);
586920Ssam 	    if (t >= 0) {
596920Ssam 		ioctl(t, TIOCNOTTY, 0);
606920Ssam 		close(t);
616920Ssam 	    }
626920Ssam 	}
636920Ssam 	if (trace) {
646920Ssam 		(void) fclose(stdout);
656920Ssam 		(void) fclose(stderr);
666920Ssam 		(void) fopen("trace", "a");
676920Ssam 		(void) dup(fileno(stdout));
686920Ssam 		setbuf(stdout, NULL);
696920Ssam 
706920Ssam 	}
716920Ssam #ifdef vax
726920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
736920Ssam #endif
746920Ssam again:
756920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
766920Ssam 	if (s < 0) {
776920Ssam 		perror("socket");
786920Ssam 		sleep(30);
796920Ssam 		goto again;
806920Ssam 	}
816920Ssam 	rtinit();
826920Ssam 	getothers();
836920Ssam 	getinterfaces();
846920Ssam 	request();
856920Ssam 
866920Ssam 	argv++, argc--;
876920Ssam 	while (argc > 0) {
886920Ssam 		if (strcmp(*argv, "-s") == 0)
896920Ssam 			supplier++;
906920Ssam 		else if (strcmp(*argv, "-q") == 0)
916920Ssam 			supplier = 0;
926920Ssam 		argv++, argc--;
936920Ssam 	}
94*6922Ssam 	sigset(SIGTERM, exit);
956920Ssam 	sigset(SIGALRM, timer);
966920Ssam 	alarm(TIMER_RATE);
976920Ssam 
986920Ssam 	/*
996920Ssam 	 * Listen for routing packets
1006920Ssam 	 */
1016920Ssam 	for (;;) {
1026920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1036920Ssam 		if (cc <= 0) {
1046920Ssam 			if (cc < 0 && errno != EINTR)
1056920Ssam 				perror("receive");
1066920Ssam 			continue;
1076920Ssam 		}
1086920Ssam 		sighold(SIGALRM);
1096920Ssam 		rip_input(&from, cc);
1106920Ssam 		sigrelse(SIGALRM);
1116920Ssam 	}
1126920Ssam }
1136920Ssam 
1146920Ssam /*
1156920Ssam  * Look in a file for any gateways we should configure
1166920Ssam  * outside the directly connected ones.  This is a kludge,
1176920Ssam  * but until we can find out about gateways on the "other side"
1186920Ssam  * of the ARPANET using GGP, it's a must.
1196920Ssam  *
1206920Ssam  * We don't really know the distance to the gateway, so we
1216920Ssam  * assume it's a neighbor.
1226920Ssam  */
1236920Ssam getothers()
1246920Ssam {
1256920Ssam 	struct sockaddr_in dst, gate;
1266920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
1276920Ssam 	struct rt_entry *rt;
1286920Ssam 
1296920Ssam 	if (fp == NULL)
1306920Ssam 		return;
1316920Ssam 	bzero((char *)&dst, sizeof (dst));
1326920Ssam 	bzero((char *)&gate, sizeof (gate));
1336920Ssam 	dst.sin_family = AF_INET;
1346920Ssam 	gate.sin_family = AF_INET;
1356920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
1366920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
1376920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
1386920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
1396920Ssam 		if (rt)
1406920Ssam 			rt->rt_flags |= RTF_SILENT;
1416920Ssam 	}
1426920Ssam 	fclose(fp);
1436920Ssam }
1446920Ssam 
1456920Ssam struct ifnet *
1466920Ssam if_ifwithaddr(addr)
1476920Ssam 	struct sockaddr *addr;
1486920Ssam {
1496920Ssam 	register struct ifnet *ifp;
1506920Ssam 
1516920Ssam #define	same(a1, a2) \
1526920Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
1536920Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
1546920Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
1556920Ssam 			continue;
1566920Ssam 		if (same(&ifp->if_addr, addr))
1576920Ssam 			break;
1586920Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
1596920Ssam 		    same(&ifp->if_broadaddr, addr))
1606920Ssam 			break;
1616920Ssam 	}
1626920Ssam 	return (ifp);
1636920Ssam #undef same
1646920Ssam }
1656920Ssam 
1666920Ssam struct ifnet *
1676920Ssam if_ifwithnet(addr)
1686920Ssam 	register struct sockaddr *addr;
1696920Ssam {
1706920Ssam 	register struct ifnet *ifp;
1716920Ssam 	register int af = addr->sa_family;
1726920Ssam 	register int (*netmatch)();
1736920Ssam 
1746920Ssam 	if (af >= AF_MAX)
1756920Ssam 		return (0);
1766920Ssam 	netmatch = afswitch[af].af_netmatch;
1776920Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
1786920Ssam 		if (af != ifp->if_addr.sa_family)
1796920Ssam 			continue;
1806920Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
1816920Ssam 			break;
1826920Ssam 	}
1836920Ssam 	return (ifp);
1846920Ssam }
1856920Ssam 
1866920Ssam struct in_addr
1876920Ssam if_makeaddr(net, host)
1886920Ssam 	int net, host;
1896920Ssam {
1906920Ssam 	u_long addr;
1916920Ssam 
1926920Ssam 	if (net < 128)
1936920Ssam 		addr = (net << 24) | host;
1946920Ssam 	else if (net < 65536)
1956920Ssam 		addr = (net << 16) | host;
1966920Ssam 	else
1976920Ssam 		addr = (net << 8) | host;
1986920Ssam #ifdef vax
1996920Ssam 	addr = htonl(addr);
2006920Ssam #endif
2016920Ssam 	return (*(struct in_addr *)&addr);
2026920Ssam }
2036920Ssam 
2046920Ssam /*
2056920Ssam  * Find the network interfaces attached to this machine.
2066920Ssam  * The info is used to::
2076920Ssam  *
2086920Ssam  * (1) initialize the routing tables, as done by the kernel.
2096920Ssam  * (2) ignore incoming packets we send.
2106920Ssam  * (3) figure out broadcast capability and addresses.
2116920Ssam  * (4) figure out if we're an internetwork gateway.
2126920Ssam  *
2136920Ssam  * We don't handle anything but Internet addresses.
2146920Ssam  */
2156920Ssam getinterfaces()
2166920Ssam {
2176920Ssam 	register struct ifnet **pifp, *ifp;
2186920Ssam 	struct sockaddr_in net;
2196920Ssam 	struct in_addr logicaladdr;
2206920Ssam 	int nets;
2216920Ssam 
2226920Ssam 	nlist("/vmunix", nl);
2236920Ssam 	if (nl[N_IFNET].n_value == 0) {
2246920Ssam 		printf("ifnet: symbol not in namelist\n");
2256920Ssam 		exit(1);
2266920Ssam 	}
2276920Ssam 	kmem = open("/dev/kmem", 0);
2286920Ssam 	if (kmem < 0) {
2296920Ssam 		perror("/dev/kmem");
2306920Ssam 		exit(1);
2316920Ssam 	}
2326920Ssam 	(void) lseek(kmem, (long)nl[N_IFNET].n_value, 0);
2336920Ssam 	(void) read(kmem, (char *)&ifnet, sizeof (ifnet));
2346920Ssam 	bzero((char *)&net, sizeof (net));
2356920Ssam 	net.sin_family = AF_INET;
2366920Ssam 	logicaladdr.s_addr = 0;
2376920Ssam 	nets = 0;
2386920Ssam 	pifp = &ifnet;
2396920Ssam 	initializing = 1;
2406920Ssam 	while (*pifp) {
2416920Ssam 		struct sockaddr_in *sin;
2426920Ssam 
2436920Ssam 		(void) lseek(kmem, (long)*pifp, 0);
2446920Ssam 		ifp = *pifp = (struct ifnet *)malloc(sizeof (struct ifnet));
2456920Ssam 		if (ifp == 0) {
2466920Ssam 			printf("routed: out of memory\n");
2476920Ssam 			break;
2486920Ssam 		}
2496920Ssam 		if (read(kmem, (char *)ifp, sizeof (*ifp)) != sizeof (*ifp)) {
2506920Ssam 			perror("read");
2516920Ssam 			break;
2526920Ssam 		}
2536920Ssam 		if (ifp->if_net == LOOPBACKNET)
2546920Ssam 			goto skip;
2556920Ssam 		nets++;
2566920Ssam 		if ((ifp->if_flags & IFF_UP) == 0)
2576920Ssam 			goto skip;
2586920Ssam 
2596920Ssam 		/*
2606920Ssam 		 * Kludge: don't treat logical host pseudo-interface
2616920Ssam 		 *	   as a net route, instead fabricate route
2626920Ssam 		 *	   to get packets back from the gateway.
2636920Ssam 		 */
2646920Ssam 		sin = (struct sockaddr_in *)&ifp->if_addr;
2656920Ssam 		if (sin->sin_family == AF_INET && ifp->if_net == 10 &&
2666920Ssam 		    sin->sin_addr.s_lh) {
2676920Ssam 			logicaladdr = sin->sin_addr;
2686920Ssam 			goto skip;
2696920Ssam 		}
2706920Ssam 
2716920Ssam 		/*
2726920Ssam 		 * Before we can handle point-point links, the interface
2736920Ssam 		 * structure will have to include an indicator to allow
2746920Ssam 		 * us to distinguish entries from "network" entries.
2756920Ssam 		 */
2766920Ssam 		net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
2776920Ssam 		rtadd((struct sockaddr *)&net, (struct sockaddr *)sin, 0);
2786920Ssam 	skip:
2796920Ssam 		pifp = &ifp->if_next;
2806920Ssam 	}
2816920Ssam 	if (logicaladdr.s_addr) {
2826920Ssam 		struct rt_entry *rt;
2836920Ssam 
2846920Ssam 		net.sin_addr = logicaladdr;
2856920Ssam 		if (ifnet)
2866920Ssam 			rtadd((struct sockaddr *)&net, &ifnet->if_addr, 0);
2876920Ssam 		/* yech...yet another logical host kludge */
2886920Ssam 		rt = rtlookup((struct sockaddr *)&net);
2896920Ssam 		if (rt)
2906920Ssam 			rt->rt_flags |= RTF_SILENT;
2916920Ssam 	}
2926920Ssam 	(void) close(kmem);
2936920Ssam 	initializing = 0;
2946920Ssam 	supplier = nets > 1;
2956920Ssam }
2966920Ssam 
2976920Ssam /*
2986920Ssam  * Send a request message to all directly
2996920Ssam  * connected hosts and networks.
3006920Ssam  */
3016920Ssam request()
3026920Ssam {
3036920Ssam 	register struct rt_entry *rt;
3046920Ssam 	register struct rt_hash *rh;
3056920Ssam 	struct rt_hash *base = hosthash;
3066920Ssam 	int doinghost = 1;
3076920Ssam 
3086920Ssam again:
3096920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3106920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3116920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
3126920Ssam 			continue;
3136920Ssam 		getall(rt);
3146920Ssam 	}
3156920Ssam 	if (doinghost) {
3166920Ssam 		base = nethash;
3176920Ssam 		doinghost = 0;
3186920Ssam 		goto again;
3196920Ssam 	}
3206920Ssam }
3216920Ssam 
3226920Ssam /*
3236920Ssam  * Broadcast a new, or modified, routing table entry
3246920Ssam  * to all directly connected hosts and networks.
3256920Ssam  */
3266920Ssam broadcast(entry)
3276920Ssam 	struct rt_entry *entry;
3286920Ssam {
3296920Ssam 	register struct rt_hash *rh;
3306920Ssam 	register struct rt_entry *rt;
3316920Ssam 	register struct sockaddr *dst;
3326920Ssam 	struct rt_hash *base = hosthash;
3336920Ssam 	int doinghost = 1;
3346920Ssam 	struct rip *msg = (struct rip *)packet;
3356920Ssam 
3366920Ssam 	if (trace)
3376920Ssam 		log("broadcast", entry);
3386920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
3396920Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
3406920Ssam 	msg->rip_nets[0].rip_metric = entry->rt_metric + 1;
3416920Ssam 
3426920Ssam again:
3436920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3446920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3456920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
3466920Ssam 			continue;
3476920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
3486920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
3496920Ssam 		else
3506920Ssam 			dst = &rt->rt_gateway;
3516920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
3526920Ssam 	}
3536920Ssam 	if (doinghost) {
3546920Ssam 		doinghost = 0;
3556920Ssam 		base = nethash;
3566920Ssam 		goto again;
3576920Ssam 	}
3586920Ssam }
3596920Ssam 
3606920Ssam /*
3616920Ssam  * Supply all directly connected neighbors with the
3626920Ssam  * current state of the routing tables.
3636920Ssam  */
3646920Ssam supplyall()
3656920Ssam {
3666920Ssam 	register struct rt_entry *rt;
3676920Ssam 	register struct rt_hash *rh;
3686920Ssam 	register struct sockaddr *dst;
3696920Ssam 	struct rt_hash *base = hosthash;
3706920Ssam 	int doinghost = 1;
3716920Ssam 
3726920Ssam again:
3736920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3746920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3756920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
3766920Ssam 			continue;
3776920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
3786920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
3796920Ssam 		else
3806920Ssam 			dst = &rt->rt_gateway;
3816920Ssam 		if (trace)
3826920Ssam 			log("supply", rt);
3836920Ssam 		supply(dst);
3846920Ssam 	}
3856920Ssam 	if (doinghost) {
3866920Ssam 		base = nethash;
3876920Ssam 		doinghost = 0;
3886920Ssam 		goto again;
3896920Ssam 	}
3906920Ssam }
3916920Ssam 
3926920Ssam /*
3936920Ssam  * Supply routing information to target "sa".
3946920Ssam  */
3956920Ssam supply(sa)
3966920Ssam 	struct sockaddr *sa;
3976920Ssam {
3986920Ssam 	struct rip *msg = (struct rip *)packet;
3996920Ssam 	struct netinfo *n = msg->rip_nets;
4006920Ssam 	register struct rt_hash *rh;
4016920Ssam 	register struct rt_entry *rt;
4026920Ssam 	struct rt_hash *base = hosthash;
4036920Ssam 	int space = MAXPACKETSIZE - sizeof (int), doinghost = 1;
4046920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4056920Ssam 
4066920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
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) {
4106920Ssam 
4116920Ssam 		/*
4126920Ssam 		 * Flush packet out if not enough room for
4136920Ssam 		 * another routing table entry.
4146920Ssam 		 */
4156920Ssam 		if (space < sizeof (struct netinfo)) {
4166920Ssam 			(*output)(sa, MAXPACKETSIZE - space);
4176920Ssam 			space = MAXPACKETSIZE - sizeof (int);
4186920Ssam 			n = msg->rip_nets;
4196920Ssam 		}
4206920Ssam 		n->rip_dst = rt->rt_dst;
4216920Ssam 		n->rip_metric = rt->rt_metric + 1;
4226920Ssam 		n++, space -= sizeof (struct netinfo);
4236920Ssam 	}
4246920Ssam 	if (doinghost) {
4256920Ssam 		doinghost = 0;
4266920Ssam 		base = nethash;
4276920Ssam 		goto again;
4286920Ssam 	}
4296920Ssam 
4306920Ssam 	if (space < MAXPACKETSIZE - sizeof (int))
4316920Ssam 		(*output)(sa, MAXPACKETSIZE - space);
4326920Ssam }
4336920Ssam 
4346920Ssam getall(rt)
4356920Ssam 	struct rt_entry *rt;
4366920Ssam {
4376920Ssam 	register struct rip *msg = (struct rip *)packet;
4386920Ssam 	struct sockaddr *dst;
4396920Ssam 
4406920Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
4416920Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
4426920Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
4436920Ssam 	if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4446920Ssam 		dst = &rt->rt_ifp->if_broadaddr;
4456920Ssam 	else
4466920Ssam 		dst = &rt->rt_gateway;
4476920Ssam 	(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
4486920Ssam }
4496920Ssam 
4506920Ssam /*
4516920Ssam  * Respond to a routing info request.
4526920Ssam  */
4536920Ssam rip_respond(from, size)
4546920Ssam 	struct sockaddr *from;
4556920Ssam 	int size;
4566920Ssam {
4576920Ssam 	register struct rip *msg = (struct rip *)packet;
4586920Ssam 	struct netinfo *np = msg->rip_nets;
4596920Ssam 	struct rt_entry *rt;
4606920Ssam 	int newsize = 0;
4616920Ssam 
4626920Ssam 	size -= sizeof (int);
4636920Ssam 	while (size > 0) {
4646920Ssam 		if (size < sizeof (struct netinfo))
4656920Ssam 			break;
4666920Ssam 		size -= sizeof (struct netinfo);
4676920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
4686920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
4696920Ssam 			supply(from);
4706920Ssam 			return;
4716920Ssam 		}
4726920Ssam 		rt = rtlookup(&np->rip_dst);
4736920Ssam 		np->rip_metric = rt == 0 ? HOPCNT_INFINITY : rt->rt_metric + 1;
4746920Ssam 		np++, newsize += sizeof (struct netinfo);
4756920Ssam 	}
4766920Ssam 	if (newsize > 0) {
4776920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
4786920Ssam 		newsize += sizeof (int);
4796920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
4806920Ssam 	}
4816920Ssam }
4826920Ssam 
4836920Ssam /*
4846920Ssam  * Handle an incoming routing packet.
4856920Ssam  */
4866920Ssam rip_input(from, size)
4876920Ssam 	struct sockaddr *from;
4886920Ssam 	int size;
4896920Ssam {
4906920Ssam 	register struct rip *msg = (struct rip *)packet;
4916920Ssam 	struct rt_entry *rt;
4926920Ssam 	struct netinfo *n;
4936920Ssam 
4946920Ssam 	if (msg->rip_cmd != RIPCMD_RESPONSE &&
4956920Ssam 	    msg->rip_cmd != RIPCMD_REQUEST)
4966920Ssam 		return;
4976920Ssam 
4986920Ssam 	/*
4996920Ssam 	 * The router port is in the lower 1K of the UDP port space,
5006920Ssam 	 * and so is priviledged.  Hence we can "authenticate" incoming
5016920Ssam 	 * updates simply by checking the source port.
5026920Ssam 	 */
5036920Ssam 	if (msg->rip_cmd == RIPCMD_RESPONSE &&
5046920Ssam 	    (*afswitch[from->sa_family].af_portmatch)(from) == 0)
5056920Ssam 		return;
5066920Ssam 	if (msg->rip_cmd == RIPCMD_REQUEST) {
5076920Ssam 		rip_respond(from, size);
5086920Ssam 		return;
5096920Ssam 	}
5106920Ssam 
5116920Ssam 	/*
5126920Ssam 	 * Process updates.
5136920Ssam 	 * Extraneous information like Internet ports
5146920Ssam 	 * must first be purged from the sender's address for
5156920Ssam 	 * pattern matching below.
5166920Ssam 	 */
5176920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5186920Ssam 	if (trace)
5196920Ssam 		printf("input from %x\n", from->sin_addr);
5206920Ssam 	/*
5216920Ssam 	 * If response packet is from ourselves, use it only
5226920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5236920Ssam 	 * it as gospel (since it comes from the router) and
5246920Ssam 	 * unknowingly update the metric to show the outgoing
5256920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5266920Ssam 	 * spec doesn't address this because Xerox Ethernets
5276920Ssam 	 * don't hear their own broadcasts?
5286920Ssam 	 */
5296920Ssam 	if (if_ifwithaddr(from)) {
5306920Ssam 		rt = rtlookup(from);
5316920Ssam 		if (rt)
5326920Ssam 			rt->rt_timer = 0;
5336920Ssam 		return;
5346920Ssam 	}
5356920Ssam 	size -= sizeof (int);
5366920Ssam 	n = msg->rip_nets;
5376920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5386920Ssam 		if (size < sizeof (struct netinfo))
5396920Ssam 			break;
5406920Ssam 		if (trace)
5416920Ssam 			printf("dst %x hc %d...", n->rip_dst.sin_addr,
5426920Ssam 				n->rip_metric);
5436920Ssam 		rt = rtlookup(&n->rip_dst);
5446920Ssam 
5456920Ssam 		/*
5466920Ssam 		 * Unknown entry, add it to the tables only if
5476920Ssam 		 * its interesting.
5486920Ssam 		 */
5496920Ssam 		if (rt == 0) {
5506920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5516920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5526920Ssam 			if (trace)
5536920Ssam 				printf("new\n");
5546920Ssam 			continue;
5556920Ssam 		}
5566920Ssam 
5576920Ssam 		if (trace)
5586920Ssam 			printf("ours: gate %x hc %d timer %d\n",
5596920Ssam 			rt->rt_gateway.sin_addr,
5606920Ssam 			rt->rt_metric, rt->rt_timer);
5616920Ssam 		/*
5626920Ssam 		 * Update the entry if one of the following is true:
5636920Ssam 		 *
5646920Ssam 		 * (1) The update came directly from the gateway.
5656920Ssam 		 * (2) A shorter path is provided.
5666920Ssam 		 * (3) The entry hasn't been updated in a while
5676920Ssam 		 *     and a path of equivalent cost is offered.
5686920Ssam 		 */
5696920Ssam 		if (equal(from, &rt->rt_gateway) ||
5706920Ssam 		    rt->rt_metric > n->rip_metric ||
5716920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
5726920Ssam 		    rt->rt_metric == n->rip_metric)) {
5736920Ssam 			rtchange(rt, from, n->rip_metric);
5746920Ssam 			rt->rt_timer = 0;
5756920Ssam 		}
5766920Ssam 	}
5776920Ssam }
5786920Ssam 
5796920Ssam /*
5806920Ssam  * Lookup an entry to the appropriate dstination.
5816920Ssam  */
5826920Ssam struct rt_entry *
5836920Ssam rtlookup(dst)
5846920Ssam 	struct sockaddr *dst;
5856920Ssam {
5866920Ssam 	register struct rt_entry *rt;
5876920Ssam 	register struct rt_hash *rh;
5886920Ssam 	register int hash, (*match)();
5896920Ssam 	struct afhash h;
5906920Ssam 	int af = dst->sa_family, doinghost = 1;
5916920Ssam 
5926920Ssam 	if (af >= AF_MAX)
5936920Ssam 		return (0);
5946920Ssam 	(*afswitch[af].af_hash)(dst, &h);
5956920Ssam 	hash = h.afh_hosthash;
5966920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
5976920Ssam 
5986920Ssam again:
5996920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6006920Ssam 		if (rt->rt_hash != hash)
6016920Ssam 			continue;
6026920Ssam 		if (doinghost) {
6036920Ssam 			if (equal(&rt->rt_dst, dst))
6046920Ssam 				return (rt);
6056920Ssam 		} else {
6066920Ssam 			if (rt->rt_dst.sa_family == af &&
6076920Ssam 			    (*match)(&rt->rt_dst, dst))
6086920Ssam 				return (rt);
6096920Ssam 		}
6106920Ssam 	}
6116920Ssam 	if (doinghost) {
6126920Ssam 		doinghost = 0;
6136920Ssam 		hash = h.afh_nethash;
6146920Ssam 		match = afswitch[af].af_netmatch;
6156920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6166920Ssam 		goto again;
6176920Ssam 	}
6186920Ssam 	return (0);
6196920Ssam }
6206920Ssam 
6216920Ssam rtinit()
6226920Ssam {
6236920Ssam 	register struct rt_hash *rh;
6246920Ssam 
6256920Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6266920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6276920Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6286920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6296920Ssam }
6306920Ssam 
6316920Ssam /*
6326920Ssam  * Add a new entry.
6336920Ssam  */
6346920Ssam rtadd(dst, gate, metric)
6356920Ssam 	struct sockaddr *dst, *gate;
6366920Ssam 	short metric;
6376920Ssam {
6386920Ssam 	struct afhash h;
6396920Ssam 	register struct rt_entry *rt;
6406920Ssam 	struct rt_hash *rh;
6416920Ssam 	int af = dst->sa_family, flags, hash;
6426920Ssam 
6436920Ssam 	if (af >= AF_MAX)
6446920Ssam 		return;
6456920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6466920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6476920Ssam 	if (flags & RTF_HOST) {
6486920Ssam 		hash = h.afh_hosthash;
6496920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6506920Ssam 	} else {
6516920Ssam 		hash = h.afh_nethash;
6526920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6536920Ssam 	}
6546920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6556920Ssam 	if (rt == 0)
6566920Ssam 		return;
6576920Ssam 	rt->rt_hash = hash;
6586920Ssam 	rt->rt_dst = *dst;
6596920Ssam 	rt->rt_gateway = *gate;
6606920Ssam 	rt->rt_metric = metric;
6616920Ssam 	rt->rt_timer = 0;
6626920Ssam 	rt->rt_flags = RTF_UP | flags;
6636920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
6646920Ssam 	if (metric == 0)
6656920Ssam 		rt->rt_flags |= RTF_DIRECT;
6666920Ssam 	insque(rt, rh);
6676920Ssam 	if (trace)
6686920Ssam 		log("add", rt);
6696920Ssam 	if (initializing)
6706920Ssam 		return;
6716920Ssam 	if (supplier)
6726920Ssam 		broadcast(rt);
6736920Ssam 	if (install) {
6746920Ssam 		rt->rt_flags |= RTF_ADDRT;
6756920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
6766920Ssam 	}
6776920Ssam }
6786920Ssam 
6796920Ssam /*
6806920Ssam  * Look to see if a change to an existing entry
6816920Ssam  * is warranted; if so, make it.
6826920Ssam  */
6836920Ssam rtchange(rt, gate, metric)
6846920Ssam 	struct rt_entry *rt;
6856920Ssam 	struct sockaddr *gate;
6866920Ssam 	short metric;
6876920Ssam {
6886920Ssam 	int change = 0;
6896920Ssam 
6906920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
6916920Ssam 		rt->rt_gateway = *gate;
6926920Ssam 		change++;
6936920Ssam 	}
6946920Ssam 
6956920Ssam 	/*
6966920Ssam 	 * If the hop count has changed, adjust
6976920Ssam 	 * the flags in the routing table entry accordingly.
6986920Ssam 	 */
6996920Ssam 	if (metric != rt->rt_metric) {
7006920Ssam 		if (rt->rt_metric == 0)
7016920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7026920Ssam 		rt->rt_metric = metric;
7036920Ssam 		if (metric >= HOPCNT_INFINITY)
7046920Ssam 			rt->rt_flags &= ~RTF_UP;
7056920Ssam 		else
7066920Ssam 			rt->rt_flags |= RTF_UP;
7076920Ssam 		change++;
7086920Ssam 	}
7096920Ssam 
7106920Ssam 	if (!change)
7116920Ssam 		return;
7126920Ssam 	if (supplier)
7136920Ssam 		broadcast(rt);
7146920Ssam 	if (trace)
7156920Ssam 		log("change", rt);
7166920Ssam 	if (install) {
7176920Ssam 		rt->rt_flags |= RTF_CHGRT;
7186920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7196920Ssam 	}
7206920Ssam }
7216920Ssam 
7226920Ssam /*
7236920Ssam  * Delete a routing table entry.
7246920Ssam  */
7256920Ssam rtdelete(rt)
7266920Ssam 	struct rt_entry *rt;
7276920Ssam {
7286920Ssam 	if (trace)
7296920Ssam 		log("delete", rt);
7306920Ssam 	if (install)
7316920Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_hash) &&
7326920Ssam 		  errno == EBUSY)
7336920Ssam 			rt->rt_flags |= RTF_DELRT;
7346920Ssam 	remque(rt);
7356920Ssam 	free((char *)rt);
7366920Ssam }
7376920Ssam 
7386920Ssam /*
7396920Ssam  * Timer routine:
7406920Ssam  *
7416920Ssam  * o handle timers on table entries,
7426920Ssam  * o invalidate entries which haven't been updated in a while,
7436920Ssam  * o delete entries which are too old,
7446920Ssam  * o retry ioctl's which weren't successful the first
7456920Ssam  *   time due to the kernel entry being busy
7466920Ssam  * o if we're an internetwork router, supply routing updates
7476920Ssam  *   periodically
7486920Ssam  */
7496920Ssam timer()
7506920Ssam {
7516920Ssam 	register struct rt_hash *rh;
7526920Ssam 	register struct rt_entry *rt;
7536920Ssam 	struct rt_hash *base = hosthash;
7546920Ssam 	int doinghost = 1;
7556920Ssam 
7566920Ssam 	if (trace)
7576920Ssam 		printf(">>> time %d >>>\n", timeval);
7586920Ssam again:
7596920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
7606920Ssam 		rt = rh->rt_forw;
7616920Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
7626920Ssam 
7636920Ssam 			/*
7646920Ssam 			 * If the host is indicated to be
7656920Ssam 			 * "silent" (i.e. it's a logical host,
7666920Ssam 			 * or one we got from the initialization
7676920Ssam 			 * file), don't time out it's entry.
7686920Ssam 			 */
7696920Ssam 			if (rt->rt_flags & RTF_SILENT)
7706920Ssam 				continue;
7716920Ssam 			if (trace)
7726920Ssam 				log("", rt);
7736920Ssam 			rt->rt_timer += TIMER_RATE;
7746920Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
7756920Ssam 			  (rt->rt_flags & RTF_DELRT)) {
7766920Ssam 				rt = rt->rt_forw;
7776920Ssam 				rtdelete(rt->rt_back);
7786920Ssam 				rt = rt->rt_back;
7796920Ssam 				continue;
7806920Ssam 			}
7816920Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
7826920Ssam 				rt->rt_metric = HOPCNT_INFINITY;
7836920Ssam 			if (rt->rt_flags & RTF_CHGRT)
7846920Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_hash) ||
7856920Ssam 				  --rt->rt_retry == 0)
7866920Ssam 					rt->rt_flags &= ~RTF_CHGRT;
7876920Ssam 			if (rt->rt_flags & RTF_ADDRT)
7886920Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_hash) ||
7896920Ssam 				  --rt->rt_retry == 0)
7906920Ssam 					rt->rt_flags &= ~RTF_ADDRT;
7916920Ssam 		}
7926920Ssam 	}
7936920Ssam 	if (doinghost) {
7946920Ssam 		doinghost = 0;
7956920Ssam 		base = nethash;
7966920Ssam 		goto again;
7976920Ssam 	}
7986920Ssam 	timeval += TIMER_RATE;
7996920Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
8006920Ssam 		supplyall();
8016920Ssam 	if (trace)
8026920Ssam 		printf("<<< time %d <<<\n", timeval);
8036920Ssam 	alarm(TIMER_RATE);
8046920Ssam }
8056920Ssam 
8066920Ssam log(operation, rt)
8076920Ssam 	char *operation;
8086920Ssam 	struct rt_entry *rt;
8096920Ssam {
8106920Ssam 	time_t t = time(0);
8116920Ssam 	struct sockaddr_in *dst, *gate;
8126920Ssam 	static struct flagbits {
8136920Ssam 		int	t_bits;
8146920Ssam 		char	*t_name;
8156920Ssam 	} bits[] = {
8166920Ssam 		{ RTF_UP,	"UP" },
8176920Ssam 		{ RTF_DIRECT,	"DIRECT" },
8186920Ssam 		{ RTF_HOST,	"HOST" },
8196920Ssam 		{ RTF_DELRT,	"DELETE" },
8206920Ssam 		{ RTF_CHGRT,	"CHANGE" },
8216920Ssam 		{ RTF_SILENT,	"SILENT" },
8226920Ssam 		{ 0 }
8236920Ssam 	};
8246920Ssam 	register struct flagbits *p;
8256920Ssam 	register int first;
8266920Ssam 	char *cp;
8276920Ssam 
8286920Ssam 	printf("%s ", operation);
8296920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
8306920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
8316920Ssam 	printf("dst %x, router %x, metric %d, flags ",
8326920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
8336920Ssam 	cp = "%s";
8346920Ssam 	for (first = 1, p = bits; p->t_bits > 0; p++) {
8356920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
8366920Ssam 			continue;
8376920Ssam 		printf(cp, p->t_name);
8386920Ssam 		if (first) {
8396920Ssam 			cp = "|%s";
8406920Ssam 			first = 0;
8416920Ssam 		}
8426920Ssam 	}
8436920Ssam 	putchar('\n');
8446920Ssam }
845