xref: /csrg-svn/sbin/routed/routed.c (revision 6920)
1*6920Ssam #ifndef lint
2*6920Ssam static char sccsid[] = "@(#)routed.c	4.1 05/22/82";
3*6920Ssam #endif
4*6920Ssam 
5*6920Ssam #include <sys/param.h>
6*6920Ssam #include <sys/protosw.h>
7*6920Ssam #include <sys/ioctl.h>
8*6920Ssam #include <sys/socket.h>
9*6920Ssam #include <net/in.h>
10*6920Ssam #define	KERNEL
11*6920Ssam #include <net/route.h>
12*6920Ssam #include <net/if.h>
13*6920Ssam #include <errno.h>
14*6920Ssam #include <stdio.h>
15*6920Ssam #include <nlist.h>
16*6920Ssam #include <signal.h>
17*6920Ssam #include "rip.h"
18*6920Ssam #include "router.h"
19*6920Ssam 
20*6920Ssam #define	LOOPBACKNET	0177
21*6920Ssam /* casts to keep lint happy */
22*6920Ssam #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
23*6920Ssam #define	remque(q)	_remque((caddr_t)q)
24*6920Ssam #define equal(a1, a2) \
25*6920Ssam 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
26*6920Ssam 
27*6920Ssam struct nlist nl[] = {
28*6920Ssam #define	N_IFNET		0
29*6920Ssam 	{ "_ifnet" },
30*6920Ssam 	0,
31*6920Ssam };
32*6920Ssam 
33*6920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
34*6920Ssam 
35*6920Ssam int	s;
36*6920Ssam int	kmem;
37*6920Ssam int	supplier;		/* process should supply updates */
38*6920Ssam int	initializing;		/* stem off broadcast() calls */
39*6920Ssam int	install = 0;		/* if 1 call kernel */
40*6920Ssam int	timeval;
41*6920Ssam int	timer();
42*6920Ssam int	cleanup();
43*6920Ssam int	trace = 0;
44*6920Ssam 
45*6920Ssam char	packet[MAXPACKETSIZE];
46*6920Ssam 
47*6920Ssam extern char *malloc();
48*6920Ssam extern int errno;
49*6920Ssam 
50*6920Ssam main(argc, argv)
51*6920Ssam 	int argc;
52*6920Ssam 	char *argv[];
53*6920Ssam {
54*6920Ssam 	int cc;
55*6920Ssam 	struct sockaddr from;
56*6920Ssam 
57*6920Ssam 	{   int t = open("/dev/tty", 2);
58*6920Ssam 	    if (t >= 0) {
59*6920Ssam 		ioctl(t, TIOCNOTTY, 0);
60*6920Ssam 		close(t);
61*6920Ssam 	    }
62*6920Ssam 	}
63*6920Ssam 	if (trace) {
64*6920Ssam 		(void) fclose(stdout);
65*6920Ssam 		(void) fclose(stderr);
66*6920Ssam 		(void) fopen("trace", "a");
67*6920Ssam 		(void) dup(fileno(stdout));
68*6920Ssam 		setbuf(stdout, NULL);
69*6920Ssam 
70*6920Ssam 	}
71*6920Ssam #ifdef vax
72*6920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
73*6920Ssam #endif
74*6920Ssam again:
75*6920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
76*6920Ssam 	if (s < 0) {
77*6920Ssam 		perror("socket");
78*6920Ssam 		sleep(30);
79*6920Ssam 		goto again;
80*6920Ssam 	}
81*6920Ssam 	rtinit();
82*6920Ssam 	getothers();
83*6920Ssam 	getinterfaces();
84*6920Ssam 	request();
85*6920Ssam 
86*6920Ssam 	argv++, argc--;
87*6920Ssam 	while (argc > 0) {
88*6920Ssam 		if (strcmp(*argv, "-s") == 0)
89*6920Ssam 			supplier++;
90*6920Ssam 		else if (strcmp(*argv, "-q") == 0)
91*6920Ssam 			supplier = 0;
92*6920Ssam 		argv++, argc--;
93*6920Ssam 	}
94*6920Ssam 	sigset(SIGALRM, timer);
95*6920Ssam 	alarm(TIMER_RATE);
96*6920Ssam 
97*6920Ssam 	/*
98*6920Ssam 	 * Listen for routing packets
99*6920Ssam 	 */
100*6920Ssam 	for (;;) {
101*6920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
102*6920Ssam 		if (cc <= 0) {
103*6920Ssam 			if (cc < 0 && errno != EINTR)
104*6920Ssam 				perror("receive");
105*6920Ssam 			continue;
106*6920Ssam 		}
107*6920Ssam 		sighold(SIGALRM);
108*6920Ssam 		rip_input(&from, cc);
109*6920Ssam 		sigrelse(SIGALRM);
110*6920Ssam 	}
111*6920Ssam }
112*6920Ssam 
113*6920Ssam /*
114*6920Ssam  * Look in a file for any gateways we should configure
115*6920Ssam  * outside the directly connected ones.  This is a kludge,
116*6920Ssam  * but until we can find out about gateways on the "other side"
117*6920Ssam  * of the ARPANET using GGP, it's a must.
118*6920Ssam  *
119*6920Ssam  * We don't really know the distance to the gateway, so we
120*6920Ssam  * assume it's a neighbor.
121*6920Ssam  */
122*6920Ssam getothers()
123*6920Ssam {
124*6920Ssam 	struct sockaddr_in dst, gate;
125*6920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
126*6920Ssam 	struct rt_entry *rt;
127*6920Ssam 
128*6920Ssam 	if (fp == NULL)
129*6920Ssam 		return;
130*6920Ssam 	bzero((char *)&dst, sizeof (dst));
131*6920Ssam 	bzero((char *)&gate, sizeof (gate));
132*6920Ssam 	dst.sin_family = AF_INET;
133*6920Ssam 	gate.sin_family = AF_INET;
134*6920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
135*6920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
136*6920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
137*6920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
138*6920Ssam 		if (rt)
139*6920Ssam 			rt->rt_flags |= RTF_SILENT;
140*6920Ssam 	}
141*6920Ssam 	fclose(fp);
142*6920Ssam }
143*6920Ssam 
144*6920Ssam struct ifnet *
145*6920Ssam if_ifwithaddr(addr)
146*6920Ssam 	struct sockaddr *addr;
147*6920Ssam {
148*6920Ssam 	register struct ifnet *ifp;
149*6920Ssam 
150*6920Ssam #define	same(a1, a2) \
151*6920Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
152*6920Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
153*6920Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
154*6920Ssam 			continue;
155*6920Ssam 		if (same(&ifp->if_addr, addr))
156*6920Ssam 			break;
157*6920Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
158*6920Ssam 		    same(&ifp->if_broadaddr, addr))
159*6920Ssam 			break;
160*6920Ssam 	}
161*6920Ssam 	return (ifp);
162*6920Ssam #undef same
163*6920Ssam }
164*6920Ssam 
165*6920Ssam struct ifnet *
166*6920Ssam if_ifwithnet(addr)
167*6920Ssam 	register struct sockaddr *addr;
168*6920Ssam {
169*6920Ssam 	register struct ifnet *ifp;
170*6920Ssam 	register int af = addr->sa_family;
171*6920Ssam 	register int (*netmatch)();
172*6920Ssam 
173*6920Ssam 	if (af >= AF_MAX)
174*6920Ssam 		return (0);
175*6920Ssam 	netmatch = afswitch[af].af_netmatch;
176*6920Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
177*6920Ssam 		if (af != ifp->if_addr.sa_family)
178*6920Ssam 			continue;
179*6920Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
180*6920Ssam 			break;
181*6920Ssam 	}
182*6920Ssam 	return (ifp);
183*6920Ssam }
184*6920Ssam 
185*6920Ssam struct in_addr
186*6920Ssam if_makeaddr(net, host)
187*6920Ssam 	int net, host;
188*6920Ssam {
189*6920Ssam 	u_long addr;
190*6920Ssam 
191*6920Ssam 	if (net < 128)
192*6920Ssam 		addr = (net << 24) | host;
193*6920Ssam 	else if (net < 65536)
194*6920Ssam 		addr = (net << 16) | host;
195*6920Ssam 	else
196*6920Ssam 		addr = (net << 8) | host;
197*6920Ssam #ifdef vax
198*6920Ssam 	addr = htonl(addr);
199*6920Ssam #endif
200*6920Ssam 	return (*(struct in_addr *)&addr);
201*6920Ssam }
202*6920Ssam 
203*6920Ssam /*
204*6920Ssam  * Find the network interfaces attached to this machine.
205*6920Ssam  * The info is used to::
206*6920Ssam  *
207*6920Ssam  * (1) initialize the routing tables, as done by the kernel.
208*6920Ssam  * (2) ignore incoming packets we send.
209*6920Ssam  * (3) figure out broadcast capability and addresses.
210*6920Ssam  * (4) figure out if we're an internetwork gateway.
211*6920Ssam  *
212*6920Ssam  * We don't handle anything but Internet addresses.
213*6920Ssam  */
214*6920Ssam getinterfaces()
215*6920Ssam {
216*6920Ssam 	register struct ifnet **pifp, *ifp;
217*6920Ssam 	struct sockaddr_in net;
218*6920Ssam 	struct in_addr logicaladdr;
219*6920Ssam 	int nets;
220*6920Ssam 
221*6920Ssam 	nlist("/vmunix", nl);
222*6920Ssam 	if (nl[N_IFNET].n_value == 0) {
223*6920Ssam 		printf("ifnet: symbol not in namelist\n");
224*6920Ssam 		exit(1);
225*6920Ssam 	}
226*6920Ssam 	kmem = open("/dev/kmem", 0);
227*6920Ssam 	if (kmem < 0) {
228*6920Ssam 		perror("/dev/kmem");
229*6920Ssam 		exit(1);
230*6920Ssam 	}
231*6920Ssam 	(void) lseek(kmem, (long)nl[N_IFNET].n_value, 0);
232*6920Ssam 	(void) read(kmem, (char *)&ifnet, sizeof (ifnet));
233*6920Ssam 	bzero((char *)&net, sizeof (net));
234*6920Ssam 	net.sin_family = AF_INET;
235*6920Ssam 	logicaladdr.s_addr = 0;
236*6920Ssam 	nets = 0;
237*6920Ssam 	pifp = &ifnet;
238*6920Ssam 	initializing = 1;
239*6920Ssam 	while (*pifp) {
240*6920Ssam 		struct sockaddr_in *sin;
241*6920Ssam 
242*6920Ssam 		(void) lseek(kmem, (long)*pifp, 0);
243*6920Ssam 		ifp = *pifp = (struct ifnet *)malloc(sizeof (struct ifnet));
244*6920Ssam 		if (ifp == 0) {
245*6920Ssam 			printf("routed: out of memory\n");
246*6920Ssam 			break;
247*6920Ssam 		}
248*6920Ssam 		if (read(kmem, (char *)ifp, sizeof (*ifp)) != sizeof (*ifp)) {
249*6920Ssam 			perror("read");
250*6920Ssam 			break;
251*6920Ssam 		}
252*6920Ssam 		if (ifp->if_net == LOOPBACKNET)
253*6920Ssam 			goto skip;
254*6920Ssam 		nets++;
255*6920Ssam 		if ((ifp->if_flags & IFF_UP) == 0)
256*6920Ssam 			goto skip;
257*6920Ssam 
258*6920Ssam 		/*
259*6920Ssam 		 * Kludge: don't treat logical host pseudo-interface
260*6920Ssam 		 *	   as a net route, instead fabricate route
261*6920Ssam 		 *	   to get packets back from the gateway.
262*6920Ssam 		 */
263*6920Ssam 		sin = (struct sockaddr_in *)&ifp->if_addr;
264*6920Ssam 		if (sin->sin_family == AF_INET && ifp->if_net == 10 &&
265*6920Ssam 		    sin->sin_addr.s_lh) {
266*6920Ssam 			logicaladdr = sin->sin_addr;
267*6920Ssam 			goto skip;
268*6920Ssam 		}
269*6920Ssam 
270*6920Ssam 		/*
271*6920Ssam 		 * Before we can handle point-point links, the interface
272*6920Ssam 		 * structure will have to include an indicator to allow
273*6920Ssam 		 * us to distinguish entries from "network" entries.
274*6920Ssam 		 */
275*6920Ssam 		net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
276*6920Ssam 		rtadd((struct sockaddr *)&net, (struct sockaddr *)sin, 0);
277*6920Ssam 	skip:
278*6920Ssam 		pifp = &ifp->if_next;
279*6920Ssam 	}
280*6920Ssam 	if (logicaladdr.s_addr) {
281*6920Ssam 		struct rt_entry *rt;
282*6920Ssam 
283*6920Ssam 		net.sin_addr = logicaladdr;
284*6920Ssam 		if (ifnet)
285*6920Ssam 			rtadd((struct sockaddr *)&net, &ifnet->if_addr, 0);
286*6920Ssam 		/* yech...yet another logical host kludge */
287*6920Ssam 		rt = rtlookup((struct sockaddr *)&net);
288*6920Ssam 		if (rt)
289*6920Ssam 			rt->rt_flags |= RTF_SILENT;
290*6920Ssam 	}
291*6920Ssam 	(void) close(kmem);
292*6920Ssam 	initializing = 0;
293*6920Ssam 	supplier = nets > 1;
294*6920Ssam }
295*6920Ssam 
296*6920Ssam /*
297*6920Ssam  * Send a request message to all directly
298*6920Ssam  * connected hosts and networks.
299*6920Ssam  */
300*6920Ssam request()
301*6920Ssam {
302*6920Ssam 	register struct rt_entry *rt;
303*6920Ssam 	register struct rt_hash *rh;
304*6920Ssam 	struct rt_hash *base = hosthash;
305*6920Ssam 	int doinghost = 1;
306*6920Ssam 
307*6920Ssam again:
308*6920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
309*6920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
310*6920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
311*6920Ssam 			continue;
312*6920Ssam 		getall(rt);
313*6920Ssam 	}
314*6920Ssam 	if (doinghost) {
315*6920Ssam 		base = nethash;
316*6920Ssam 		doinghost = 0;
317*6920Ssam 		goto again;
318*6920Ssam 	}
319*6920Ssam }
320*6920Ssam 
321*6920Ssam /*
322*6920Ssam  * Broadcast a new, or modified, routing table entry
323*6920Ssam  * to all directly connected hosts and networks.
324*6920Ssam  */
325*6920Ssam broadcast(entry)
326*6920Ssam 	struct rt_entry *entry;
327*6920Ssam {
328*6920Ssam 	register struct rt_hash *rh;
329*6920Ssam 	register struct rt_entry *rt;
330*6920Ssam 	register struct sockaddr *dst;
331*6920Ssam 	struct rt_hash *base = hosthash;
332*6920Ssam 	int doinghost = 1;
333*6920Ssam 	struct rip *msg = (struct rip *)packet;
334*6920Ssam 
335*6920Ssam 	if (trace)
336*6920Ssam 		log("broadcast", entry);
337*6920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
338*6920Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
339*6920Ssam 	msg->rip_nets[0].rip_metric = entry->rt_metric + 1;
340*6920Ssam 
341*6920Ssam again:
342*6920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
343*6920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
344*6920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
345*6920Ssam 			continue;
346*6920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
347*6920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
348*6920Ssam 		else
349*6920Ssam 			dst = &rt->rt_gateway;
350*6920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
351*6920Ssam 	}
352*6920Ssam 	if (doinghost) {
353*6920Ssam 		doinghost = 0;
354*6920Ssam 		base = nethash;
355*6920Ssam 		goto again;
356*6920Ssam 	}
357*6920Ssam }
358*6920Ssam 
359*6920Ssam /*
360*6920Ssam  * Supply all directly connected neighbors with the
361*6920Ssam  * current state of the routing tables.
362*6920Ssam  */
363*6920Ssam supplyall()
364*6920Ssam {
365*6920Ssam 	register struct rt_entry *rt;
366*6920Ssam 	register struct rt_hash *rh;
367*6920Ssam 	register struct sockaddr *dst;
368*6920Ssam 	struct rt_hash *base = hosthash;
369*6920Ssam 	int doinghost = 1;
370*6920Ssam 
371*6920Ssam again:
372*6920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
373*6920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
374*6920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
375*6920Ssam 			continue;
376*6920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
377*6920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
378*6920Ssam 		else
379*6920Ssam 			dst = &rt->rt_gateway;
380*6920Ssam 		if (trace)
381*6920Ssam 			log("supply", rt);
382*6920Ssam 		supply(dst);
383*6920Ssam 	}
384*6920Ssam 	if (doinghost) {
385*6920Ssam 		base = nethash;
386*6920Ssam 		doinghost = 0;
387*6920Ssam 		goto again;
388*6920Ssam 	}
389*6920Ssam }
390*6920Ssam 
391*6920Ssam /*
392*6920Ssam  * Supply routing information to target "sa".
393*6920Ssam  */
394*6920Ssam supply(sa)
395*6920Ssam 	struct sockaddr *sa;
396*6920Ssam {
397*6920Ssam 	struct rip *msg = (struct rip *)packet;
398*6920Ssam 	struct netinfo *n = msg->rip_nets;
399*6920Ssam 	register struct rt_hash *rh;
400*6920Ssam 	register struct rt_entry *rt;
401*6920Ssam 	struct rt_hash *base = hosthash;
402*6920Ssam 	int space = MAXPACKETSIZE - sizeof (int), doinghost = 1;
403*6920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
404*6920Ssam 
405*6920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
406*6920Ssam again:
407*6920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
408*6920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
409*6920Ssam 
410*6920Ssam 		/*
411*6920Ssam 		 * Flush packet out if not enough room for
412*6920Ssam 		 * another routing table entry.
413*6920Ssam 		 */
414*6920Ssam 		if (space < sizeof (struct netinfo)) {
415*6920Ssam 			(*output)(sa, MAXPACKETSIZE - space);
416*6920Ssam 			space = MAXPACKETSIZE - sizeof (int);
417*6920Ssam 			n = msg->rip_nets;
418*6920Ssam 		}
419*6920Ssam 		n->rip_dst = rt->rt_dst;
420*6920Ssam 		n->rip_metric = rt->rt_metric + 1;
421*6920Ssam 		n++, space -= sizeof (struct netinfo);
422*6920Ssam 	}
423*6920Ssam 	if (doinghost) {
424*6920Ssam 		doinghost = 0;
425*6920Ssam 		base = nethash;
426*6920Ssam 		goto again;
427*6920Ssam 	}
428*6920Ssam 
429*6920Ssam 	if (space < MAXPACKETSIZE - sizeof (int))
430*6920Ssam 		(*output)(sa, MAXPACKETSIZE - space);
431*6920Ssam }
432*6920Ssam 
433*6920Ssam getall(rt)
434*6920Ssam 	struct rt_entry *rt;
435*6920Ssam {
436*6920Ssam 	register struct rip *msg = (struct rip *)packet;
437*6920Ssam 	struct sockaddr *dst;
438*6920Ssam 
439*6920Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
440*6920Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
441*6920Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
442*6920Ssam 	if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
443*6920Ssam 		dst = &rt->rt_ifp->if_broadaddr;
444*6920Ssam 	else
445*6920Ssam 		dst = &rt->rt_gateway;
446*6920Ssam 	(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
447*6920Ssam }
448*6920Ssam 
449*6920Ssam /*
450*6920Ssam  * Respond to a routing info request.
451*6920Ssam  */
452*6920Ssam rip_respond(from, size)
453*6920Ssam 	struct sockaddr *from;
454*6920Ssam 	int size;
455*6920Ssam {
456*6920Ssam 	register struct rip *msg = (struct rip *)packet;
457*6920Ssam 	struct netinfo *np = msg->rip_nets;
458*6920Ssam 	struct rt_entry *rt;
459*6920Ssam 	int newsize = 0;
460*6920Ssam 
461*6920Ssam 	size -= sizeof (int);
462*6920Ssam 	while (size > 0) {
463*6920Ssam 		if (size < sizeof (struct netinfo))
464*6920Ssam 			break;
465*6920Ssam 		size -= sizeof (struct netinfo);
466*6920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
467*6920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
468*6920Ssam 			supply(from);
469*6920Ssam 			return;
470*6920Ssam 		}
471*6920Ssam 		rt = rtlookup(&np->rip_dst);
472*6920Ssam 		np->rip_metric = rt == 0 ? HOPCNT_INFINITY : rt->rt_metric + 1;
473*6920Ssam 		np++, newsize += sizeof (struct netinfo);
474*6920Ssam 	}
475*6920Ssam 	if (newsize > 0) {
476*6920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
477*6920Ssam 		newsize += sizeof (int);
478*6920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
479*6920Ssam 	}
480*6920Ssam }
481*6920Ssam 
482*6920Ssam /*
483*6920Ssam  * Handle an incoming routing packet.
484*6920Ssam  */
485*6920Ssam rip_input(from, size)
486*6920Ssam 	struct sockaddr *from;
487*6920Ssam 	int size;
488*6920Ssam {
489*6920Ssam 	register struct rip *msg = (struct rip *)packet;
490*6920Ssam 	struct rt_entry *rt;
491*6920Ssam 	struct netinfo *n;
492*6920Ssam 
493*6920Ssam 	if (msg->rip_cmd != RIPCMD_RESPONSE &&
494*6920Ssam 	    msg->rip_cmd != RIPCMD_REQUEST)
495*6920Ssam 		return;
496*6920Ssam 
497*6920Ssam 	/*
498*6920Ssam 	 * The router port is in the lower 1K of the UDP port space,
499*6920Ssam 	 * and so is priviledged.  Hence we can "authenticate" incoming
500*6920Ssam 	 * updates simply by checking the source port.
501*6920Ssam 	 */
502*6920Ssam 	if (msg->rip_cmd == RIPCMD_RESPONSE &&
503*6920Ssam 	    (*afswitch[from->sa_family].af_portmatch)(from) == 0)
504*6920Ssam 		return;
505*6920Ssam 	if (msg->rip_cmd == RIPCMD_REQUEST) {
506*6920Ssam 		rip_respond(from, size);
507*6920Ssam 		return;
508*6920Ssam 	}
509*6920Ssam 
510*6920Ssam 	/*
511*6920Ssam 	 * Process updates.
512*6920Ssam 	 * Extraneous information like Internet ports
513*6920Ssam 	 * must first be purged from the sender's address for
514*6920Ssam 	 * pattern matching below.
515*6920Ssam 	 */
516*6920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
517*6920Ssam 	if (trace)
518*6920Ssam 		printf("input from %x\n", from->sin_addr);
519*6920Ssam 	/*
520*6920Ssam 	 * If response packet is from ourselves, use it only
521*6920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
522*6920Ssam 	 * it as gospel (since it comes from the router) and
523*6920Ssam 	 * unknowingly update the metric to show the outgoing
524*6920Ssam 	 * cost (higher than our real cost).  I guess the protocol
525*6920Ssam 	 * spec doesn't address this because Xerox Ethernets
526*6920Ssam 	 * don't hear their own broadcasts?
527*6920Ssam 	 */
528*6920Ssam 	if (if_ifwithaddr(from)) {
529*6920Ssam 		rt = rtlookup(from);
530*6920Ssam 		if (rt)
531*6920Ssam 			rt->rt_timer = 0;
532*6920Ssam 		return;
533*6920Ssam 	}
534*6920Ssam 	size -= sizeof (int);
535*6920Ssam 	n = msg->rip_nets;
536*6920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
537*6920Ssam 		if (size < sizeof (struct netinfo))
538*6920Ssam 			break;
539*6920Ssam 		if (trace)
540*6920Ssam 			printf("dst %x hc %d...", n->rip_dst.sin_addr,
541*6920Ssam 				n->rip_metric);
542*6920Ssam 		rt = rtlookup(&n->rip_dst);
543*6920Ssam 
544*6920Ssam 		/*
545*6920Ssam 		 * Unknown entry, add it to the tables only if
546*6920Ssam 		 * its interesting.
547*6920Ssam 		 */
548*6920Ssam 		if (rt == 0) {
549*6920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
550*6920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
551*6920Ssam 			if (trace)
552*6920Ssam 				printf("new\n");
553*6920Ssam 			continue;
554*6920Ssam 		}
555*6920Ssam 
556*6920Ssam 		if (trace)
557*6920Ssam 			printf("ours: gate %x hc %d timer %d\n",
558*6920Ssam 			rt->rt_gateway.sin_addr,
559*6920Ssam 			rt->rt_metric, rt->rt_timer);
560*6920Ssam 		/*
561*6920Ssam 		 * Update the entry if one of the following is true:
562*6920Ssam 		 *
563*6920Ssam 		 * (1) The update came directly from the gateway.
564*6920Ssam 		 * (2) A shorter path is provided.
565*6920Ssam 		 * (3) The entry hasn't been updated in a while
566*6920Ssam 		 *     and a path of equivalent cost is offered.
567*6920Ssam 		 */
568*6920Ssam 		if (equal(from, &rt->rt_gateway) ||
569*6920Ssam 		    rt->rt_metric > n->rip_metric ||
570*6920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
571*6920Ssam 		    rt->rt_metric == n->rip_metric)) {
572*6920Ssam 			rtchange(rt, from, n->rip_metric);
573*6920Ssam 			rt->rt_timer = 0;
574*6920Ssam 		}
575*6920Ssam 	}
576*6920Ssam }
577*6920Ssam 
578*6920Ssam /*
579*6920Ssam  * Lookup an entry to the appropriate dstination.
580*6920Ssam  */
581*6920Ssam struct rt_entry *
582*6920Ssam rtlookup(dst)
583*6920Ssam 	struct sockaddr *dst;
584*6920Ssam {
585*6920Ssam 	register struct rt_entry *rt;
586*6920Ssam 	register struct rt_hash *rh;
587*6920Ssam 	register int hash, (*match)();
588*6920Ssam 	struct afhash h;
589*6920Ssam 	int af = dst->sa_family, doinghost = 1;
590*6920Ssam 
591*6920Ssam 	if (af >= AF_MAX)
592*6920Ssam 		return (0);
593*6920Ssam 	(*afswitch[af].af_hash)(dst, &h);
594*6920Ssam 	hash = h.afh_hosthash;
595*6920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
596*6920Ssam 
597*6920Ssam again:
598*6920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
599*6920Ssam 		if (rt->rt_hash != hash)
600*6920Ssam 			continue;
601*6920Ssam 		if (doinghost) {
602*6920Ssam 			if (equal(&rt->rt_dst, dst))
603*6920Ssam 				return (rt);
604*6920Ssam 		} else {
605*6920Ssam 			if (rt->rt_dst.sa_family == af &&
606*6920Ssam 			    (*match)(&rt->rt_dst, dst))
607*6920Ssam 				return (rt);
608*6920Ssam 		}
609*6920Ssam 	}
610*6920Ssam 	if (doinghost) {
611*6920Ssam 		doinghost = 0;
612*6920Ssam 		hash = h.afh_nethash;
613*6920Ssam 		match = afswitch[af].af_netmatch;
614*6920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
615*6920Ssam 		goto again;
616*6920Ssam 	}
617*6920Ssam 	return (0);
618*6920Ssam }
619*6920Ssam 
620*6920Ssam rtinit()
621*6920Ssam {
622*6920Ssam 	register struct rt_hash *rh;
623*6920Ssam 
624*6920Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
625*6920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
626*6920Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
627*6920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
628*6920Ssam }
629*6920Ssam 
630*6920Ssam /*
631*6920Ssam  * Add a new entry.
632*6920Ssam  */
633*6920Ssam rtadd(dst, gate, metric)
634*6920Ssam 	struct sockaddr *dst, *gate;
635*6920Ssam 	short metric;
636*6920Ssam {
637*6920Ssam 	struct afhash h;
638*6920Ssam 	register struct rt_entry *rt;
639*6920Ssam 	struct rt_hash *rh;
640*6920Ssam 	int af = dst->sa_family, flags, hash;
641*6920Ssam 
642*6920Ssam 	if (af >= AF_MAX)
643*6920Ssam 		return;
644*6920Ssam 	(*afswitch[af].af_hash)(dst, &h);
645*6920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
646*6920Ssam 	if (flags & RTF_HOST) {
647*6920Ssam 		hash = h.afh_hosthash;
648*6920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
649*6920Ssam 	} else {
650*6920Ssam 		hash = h.afh_nethash;
651*6920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
652*6920Ssam 	}
653*6920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
654*6920Ssam 	if (rt == 0)
655*6920Ssam 		return;
656*6920Ssam 	rt->rt_hash = hash;
657*6920Ssam 	rt->rt_dst = *dst;
658*6920Ssam 	rt->rt_gateway = *gate;
659*6920Ssam 	rt->rt_metric = metric;
660*6920Ssam 	rt->rt_timer = 0;
661*6920Ssam 	rt->rt_flags = RTF_UP | flags;
662*6920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
663*6920Ssam 	if (metric == 0)
664*6920Ssam 		rt->rt_flags |= RTF_DIRECT;
665*6920Ssam 	insque(rt, rh);
666*6920Ssam 	if (trace)
667*6920Ssam 		log("add", rt);
668*6920Ssam 	if (initializing)
669*6920Ssam 		return;
670*6920Ssam 	if (supplier)
671*6920Ssam 		broadcast(rt);
672*6920Ssam 	if (install) {
673*6920Ssam 		rt->rt_flags |= RTF_ADDRT;
674*6920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
675*6920Ssam 	}
676*6920Ssam }
677*6920Ssam 
678*6920Ssam /*
679*6920Ssam  * Look to see if a change to an existing entry
680*6920Ssam  * is warranted; if so, make it.
681*6920Ssam  */
682*6920Ssam rtchange(rt, gate, metric)
683*6920Ssam 	struct rt_entry *rt;
684*6920Ssam 	struct sockaddr *gate;
685*6920Ssam 	short metric;
686*6920Ssam {
687*6920Ssam 	int change = 0;
688*6920Ssam 
689*6920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
690*6920Ssam 		rt->rt_gateway = *gate;
691*6920Ssam 		change++;
692*6920Ssam 	}
693*6920Ssam 
694*6920Ssam 	/*
695*6920Ssam 	 * If the hop count has changed, adjust
696*6920Ssam 	 * the flags in the routing table entry accordingly.
697*6920Ssam 	 */
698*6920Ssam 	if (metric != rt->rt_metric) {
699*6920Ssam 		if (rt->rt_metric == 0)
700*6920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
701*6920Ssam 		rt->rt_metric = metric;
702*6920Ssam 		if (metric >= HOPCNT_INFINITY)
703*6920Ssam 			rt->rt_flags &= ~RTF_UP;
704*6920Ssam 		else
705*6920Ssam 			rt->rt_flags |= RTF_UP;
706*6920Ssam 		change++;
707*6920Ssam 	}
708*6920Ssam 
709*6920Ssam 	if (!change)
710*6920Ssam 		return;
711*6920Ssam 	if (supplier)
712*6920Ssam 		broadcast(rt);
713*6920Ssam 	if (trace)
714*6920Ssam 		log("change", rt);
715*6920Ssam 	if (install) {
716*6920Ssam 		rt->rt_flags |= RTF_CHGRT;
717*6920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
718*6920Ssam 	}
719*6920Ssam }
720*6920Ssam 
721*6920Ssam /*
722*6920Ssam  * Delete a routing table entry.
723*6920Ssam  */
724*6920Ssam rtdelete(rt)
725*6920Ssam 	struct rt_entry *rt;
726*6920Ssam {
727*6920Ssam 	if (trace)
728*6920Ssam 		log("delete", rt);
729*6920Ssam 	if (install)
730*6920Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_hash) &&
731*6920Ssam 		  errno == EBUSY)
732*6920Ssam 			rt->rt_flags |= RTF_DELRT;
733*6920Ssam 	remque(rt);
734*6920Ssam 	free((char *)rt);
735*6920Ssam }
736*6920Ssam 
737*6920Ssam /*
738*6920Ssam  * Timer routine:
739*6920Ssam  *
740*6920Ssam  * o handle timers on table entries,
741*6920Ssam  * o invalidate entries which haven't been updated in a while,
742*6920Ssam  * o delete entries which are too old,
743*6920Ssam  * o retry ioctl's which weren't successful the first
744*6920Ssam  *   time due to the kernel entry being busy
745*6920Ssam  * o if we're an internetwork router, supply routing updates
746*6920Ssam  *   periodically
747*6920Ssam  */
748*6920Ssam timer()
749*6920Ssam {
750*6920Ssam 	register struct rt_hash *rh;
751*6920Ssam 	register struct rt_entry *rt;
752*6920Ssam 	struct rt_hash *base = hosthash;
753*6920Ssam 	int doinghost = 1;
754*6920Ssam 
755*6920Ssam 	if (trace)
756*6920Ssam 		printf(">>> time %d >>>\n", timeval);
757*6920Ssam again:
758*6920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
759*6920Ssam 		rt = rh->rt_forw;
760*6920Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
761*6920Ssam 
762*6920Ssam 			/*
763*6920Ssam 			 * If the host is indicated to be
764*6920Ssam 			 * "silent" (i.e. it's a logical host,
765*6920Ssam 			 * or one we got from the initialization
766*6920Ssam 			 * file), don't time out it's entry.
767*6920Ssam 			 */
768*6920Ssam 			if (rt->rt_flags & RTF_SILENT)
769*6920Ssam 				continue;
770*6920Ssam 			if (trace)
771*6920Ssam 				log("", rt);
772*6920Ssam 			rt->rt_timer += TIMER_RATE;
773*6920Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
774*6920Ssam 			  (rt->rt_flags & RTF_DELRT)) {
775*6920Ssam 				rt = rt->rt_forw;
776*6920Ssam 				rtdelete(rt->rt_back);
777*6920Ssam 				rt = rt->rt_back;
778*6920Ssam 				continue;
779*6920Ssam 			}
780*6920Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
781*6920Ssam 				rt->rt_metric = HOPCNT_INFINITY;
782*6920Ssam 			if (rt->rt_flags & RTF_CHGRT)
783*6920Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_hash) ||
784*6920Ssam 				  --rt->rt_retry == 0)
785*6920Ssam 					rt->rt_flags &= ~RTF_CHGRT;
786*6920Ssam 			if (rt->rt_flags & RTF_ADDRT)
787*6920Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_hash) ||
788*6920Ssam 				  --rt->rt_retry == 0)
789*6920Ssam 					rt->rt_flags &= ~RTF_ADDRT;
790*6920Ssam 		}
791*6920Ssam 	}
792*6920Ssam 	if (doinghost) {
793*6920Ssam 		doinghost = 0;
794*6920Ssam 		base = nethash;
795*6920Ssam 		goto again;
796*6920Ssam 	}
797*6920Ssam 	timeval += TIMER_RATE;
798*6920Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
799*6920Ssam 		supplyall();
800*6920Ssam 	if (trace)
801*6920Ssam 		printf("<<< time %d <<<\n", timeval);
802*6920Ssam 	alarm(TIMER_RATE);
803*6920Ssam }
804*6920Ssam 
805*6920Ssam log(operation, rt)
806*6920Ssam 	char *operation;
807*6920Ssam 	struct rt_entry *rt;
808*6920Ssam {
809*6920Ssam 	time_t t = time(0);
810*6920Ssam 	struct sockaddr_in *dst, *gate;
811*6920Ssam 	static struct flagbits {
812*6920Ssam 		int	t_bits;
813*6920Ssam 		char	*t_name;
814*6920Ssam 	} bits[] = {
815*6920Ssam 		{ RTF_UP,	"UP" },
816*6920Ssam 		{ RTF_DIRECT,	"DIRECT" },
817*6920Ssam 		{ RTF_HOST,	"HOST" },
818*6920Ssam 		{ RTF_DELRT,	"DELETE" },
819*6920Ssam 		{ RTF_CHGRT,	"CHANGE" },
820*6920Ssam 		{ RTF_SILENT,	"SILENT" },
821*6920Ssam 		{ 0 }
822*6920Ssam 	};
823*6920Ssam 	register struct flagbits *p;
824*6920Ssam 	register int first;
825*6920Ssam 	char *cp;
826*6920Ssam 
827*6920Ssam 	printf("%s ", operation);
828*6920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
829*6920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
830*6920Ssam 	printf("dst %x, router %x, metric %d, flags ",
831*6920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
832*6920Ssam 	cp = "%s";
833*6920Ssam 	for (first = 1, p = bits; p->t_bits > 0; p++) {
834*6920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
835*6920Ssam 			continue;
836*6920Ssam 		printf(cp, p->t_name);
837*6920Ssam 		if (first) {
838*6920Ssam 			cp = "|%s";
839*6920Ssam 			first = 0;
840*6920Ssam 		}
841*6920Ssam 	}
842*6920Ssam 	putchar('\n');
843*6920Ssam }
844