xref: /csrg-svn/sbin/routed/routed.c (revision 6929)
16920Ssam #ifndef lint
2*6929Ssam static char sccsid[] = "@(#)routed.c	4.3 05/24/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)
26*6929Ssam #define	min(a,b)	((a)>(b)?(b):(a))
276920Ssam 
286920Ssam struct nlist nl[] = {
296920Ssam #define	N_IFNET		0
306920Ssam 	{ "_ifnet" },
316920Ssam 	0,
326920Ssam };
336920Ssam 
346920Ssam struct	sockaddr_in myaddr = { AF_INET, IPPORT_ROUTESERVER };
356920Ssam 
366920Ssam int	s;
37*6929Ssam int	kmem = -1;
386920Ssam int	supplier;		/* process should supply updates */
396920Ssam int	initializing;		/* stem off broadcast() calls */
406920Ssam int	install = 0;		/* if 1 call kernel */
41*6929Ssam int	lookforinterfaces = 1;
42*6929Ssam int	performnlist = 1;
436920Ssam int	timeval;
446920Ssam int	timer();
456920Ssam int	cleanup();
466920Ssam int	trace = 0;
476920Ssam 
486920Ssam char	packet[MAXPACKETSIZE];
496920Ssam 
506920Ssam extern char *malloc();
516922Ssam extern int errno, exit();
526920Ssam 
536920Ssam main(argc, argv)
546920Ssam 	int argc;
556920Ssam 	char *argv[];
566920Ssam {
576920Ssam 	int cc;
586920Ssam 	struct sockaddr from;
596920Ssam 
60*6929Ssam #ifdef notdef
616920Ssam 	{   int t = open("/dev/tty", 2);
626920Ssam 	    if (t >= 0) {
636920Ssam 		ioctl(t, TIOCNOTTY, 0);
646920Ssam 		close(t);
656920Ssam 	    }
666920Ssam 	}
67*6929Ssam #endif
686920Ssam 	if (trace) {
69*6929Ssam 		(void) freopen("/etc/routerlog", "a", stdout);
70*6929Ssam 		(void) dup2(fileno(stdout), 2);
716920Ssam 		setbuf(stdout, NULL);
726920Ssam 	}
736920Ssam #ifdef vax
746920Ssam 	myaddr.sin_port = htons(myaddr.sin_port);
756920Ssam #endif
766920Ssam again:
776920Ssam 	s = socket(SOCK_DGRAM, 0, &myaddr, 0);
786920Ssam 	if (s < 0) {
796920Ssam 		perror("socket");
806920Ssam 		sleep(30);
816920Ssam 		goto again;
826920Ssam 	}
836920Ssam 	rtinit();
846920Ssam 	getothers();
85*6929Ssam 	initializing = 1;
866920Ssam 	getinterfaces();
87*6929Ssam 	initializing = 0;
886920Ssam 	request();
896920Ssam 
906920Ssam 	argv++, argc--;
916920Ssam 	while (argc > 0) {
926920Ssam 		if (strcmp(*argv, "-s") == 0)
93*6929Ssam 			supplier = 1;
946920Ssam 		else if (strcmp(*argv, "-q") == 0)
956920Ssam 			supplier = 0;
966920Ssam 		argv++, argc--;
976920Ssam 	}
986920Ssam 	sigset(SIGALRM, timer);
99*6929Ssam 	timer();
1006920Ssam 
1016920Ssam 	/*
1026920Ssam 	 * Listen for routing packets
1036920Ssam 	 */
1046920Ssam 	for (;;) {
1056920Ssam 		cc = receive(s, &from, packet, sizeof (packet));
1066920Ssam 		if (cc <= 0) {
1076920Ssam 			if (cc < 0 && errno != EINTR)
1086920Ssam 				perror("receive");
1096920Ssam 			continue;
1106920Ssam 		}
1116920Ssam 		sighold(SIGALRM);
1126920Ssam 		rip_input(&from, cc);
1136920Ssam 		sigrelse(SIGALRM);
1146920Ssam 	}
1156920Ssam }
1166920Ssam 
1176920Ssam /*
1186920Ssam  * Look in a file for any gateways we should configure
1196920Ssam  * outside the directly connected ones.  This is a kludge,
1206920Ssam  * but until we can find out about gateways on the "other side"
1216920Ssam  * of the ARPANET using GGP, it's a must.
1226920Ssam  *
1236920Ssam  * We don't really know the distance to the gateway, so we
1246920Ssam  * assume it's a neighbor.
1256920Ssam  */
1266920Ssam getothers()
1276920Ssam {
1286920Ssam 	struct sockaddr_in dst, gate;
1296920Ssam 	FILE *fp = fopen("/etc/gateways", "r");
1306920Ssam 	struct rt_entry *rt;
1316920Ssam 
1326920Ssam 	if (fp == NULL)
1336920Ssam 		return;
1346920Ssam 	bzero((char *)&dst, sizeof (dst));
1356920Ssam 	bzero((char *)&gate, sizeof (gate));
1366920Ssam 	dst.sin_family = AF_INET;
1376920Ssam 	gate.sin_family = AF_INET;
1386920Ssam 	while (fscanf(fp, "%x %x", &dst.sin_addr.s_addr,
1396920Ssam 	   &gate.sin_addr.s_addr) != EOF) {
1406920Ssam 		rtadd((struct sockaddr *)&dst, (struct sockaddr *)&gate, 1);
1416920Ssam 		rt = rtlookup((struct sockaddr *)&dst);
1426920Ssam 		if (rt)
1436920Ssam 			rt->rt_flags |= RTF_SILENT;
1446920Ssam 	}
1456920Ssam 	fclose(fp);
1466920Ssam }
1476920Ssam 
148*6929Ssam /*
149*6929Ssam  * Timer routine:
150*6929Ssam  *
151*6929Ssam  * o handle timers on table entries,
152*6929Ssam  * o invalidate entries which haven't been updated in a while,
153*6929Ssam  * o delete entries which are too old,
154*6929Ssam  * o retry ioctl's which weren't successful the first
155*6929Ssam  *   time due to the kernel entry being busy
156*6929Ssam  * o if we're an internetwork router, supply routing updates
157*6929Ssam  *   periodically
158*6929Ssam  */
159*6929Ssam timer()
1606920Ssam {
161*6929Ssam 	register struct rt_hash *rh;
162*6929Ssam 	register struct rt_entry *rt;
163*6929Ssam 	struct rt_hash *base = hosthash;
164*6929Ssam 	int doinghost = 1;
1656920Ssam 
166*6929Ssam 	if (trace)
167*6929Ssam 		printf(">>> time %d >>>\n", timeval);
168*6929Ssam again:
169*6929Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
170*6929Ssam 		rt = rh->rt_forw;
171*6929Ssam 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
1726920Ssam 
173*6929Ssam 			/*
174*6929Ssam 			 * If the host is indicated to be
175*6929Ssam 			 * "silent" (i.e. it's one we got
176*6929Ssam 			 * from the initialization file),
177*6929Ssam 			 * don't time out it's entry.
178*6929Ssam 			 */
179*6929Ssam 			if (rt->rt_flags & RTF_SILENT)
180*6929Ssam 				continue;
181*6929Ssam 			log("", rt);
182*6929Ssam 			rt->rt_timer += TIMER_RATE;
1836920Ssam 
184*6929Ssam 			/*
185*6929Ssam 			 * If the entry should be deleted
186*6929Ssam 			 * attempt to do so and reclaim space.
187*6929Ssam 			 */
188*6929Ssam 			if (rt->rt_timer >= GARBAGE_TIME ||
189*6929Ssam 			  (rt->rt_flags & RTF_DELRT)) {
190*6929Ssam 				rt = rt->rt_forw;
191*6929Ssam 				rtdelete(rt->rt_back);
192*6929Ssam 				rt = rt->rt_back;
193*6929Ssam 				continue;
194*6929Ssam 			}
1956920Ssam 
196*6929Ssam 			/*
197*6929Ssam 			 * If we haven't heard from the router
198*6929Ssam 			 * in a long time indicate the route is
199*6929Ssam 			 * hard to reach.
200*6929Ssam 			 */
201*6929Ssam 			if (rt->rt_timer >= EXPIRE_TIME)
202*6929Ssam 				rt->rt_metric = HOPCNT_INFINITY;
203*6929Ssam 			if (rt->rt_flags & RTF_CHGRT)
204*6929Ssam 				if (!ioctl(s, SIOCCHGRT,(char *)&rt->rt_hash) ||
205*6929Ssam 				  --rt->rt_retry == 0)
206*6929Ssam 					rt->rt_flags &= ~RTF_CHGRT;
2076920Ssam 
208*6929Ssam 			/*
209*6929Ssam 			 * Try to add the route to the kernel tables.
210*6929Ssam 			 * If this fails because the entry already exists
211*6929Ssam 			 * (perhaps because someone manually added it)
212*6929Ssam 			 * change the add to a change.  If the operation
213*6929Ssam 			 * fails otherwise (likely because the entry is
214*6929Ssam 			 * in use), retry the operation a few more times.
215*6929Ssam 			 */
216*6929Ssam 			if (rt->rt_flags & RTF_ADDRT) {
217*6929Ssam 				if (!ioctl(s, SIOCADDRT,(char *)&rt->rt_hash)) {
218*6929Ssam 					if (errno == EEXIST) {
219*6929Ssam 						rt->rt_flags &= ~RTF_ADDRT;
220*6929Ssam 						rt->rt_flags |= RTF_CHGRT;
221*6929Ssam 						rt->rt_retry =
222*6929Ssam 						    (EXPIRE_TIME/TIMER_RATE);
223*6929Ssam 						continue;
224*6929Ssam 					}
225*6929Ssam 					if (--rt->rt_retry)
226*6929Ssam 						continue;
227*6929Ssam 				}
228*6929Ssam 				rt->rt_flags &= ~RTF_ADDRT;
229*6929Ssam 			}
230*6929Ssam 		}
231*6929Ssam 	}
232*6929Ssam 	if (doinghost) {
233*6929Ssam 		doinghost = 0;
234*6929Ssam 		base = nethash;
235*6929Ssam 		goto again;
236*6929Ssam 	}
237*6929Ssam 	timeval += TIMER_RATE;
238*6929Ssam 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
239*6929Ssam 		getinterfaces();
240*6929Ssam 	if (supplier && (timeval % SUPPLY_INTERVAL) == 0)
241*6929Ssam 		supplyall();
242*6929Ssam 	if (trace)
243*6929Ssam 		printf("<<< time %d <<<\n", timeval);
244*6929Ssam 	alarm(TIMER_RATE);
2456920Ssam }
2466920Ssam 
2476920Ssam /*
2486920Ssam  * Find the network interfaces attached to this machine.
249*6929Ssam  * The info is used to:
2506920Ssam  *
2516920Ssam  * (1) initialize the routing tables, as done by the kernel.
2526920Ssam  * (2) ignore incoming packets we send.
2536920Ssam  * (3) figure out broadcast capability and addresses.
2546920Ssam  * (4) figure out if we're an internetwork gateway.
2556920Ssam  *
2566920Ssam  * We don't handle anything but Internet addresses.
2576920Ssam  */
2586920Ssam getinterfaces()
2596920Ssam {
260*6929Ssam 	struct ifnet *ifp;
261*6929Ssam 	struct ifnet ifstruct, *next;
2626920Ssam 	struct sockaddr_in net;
263*6929Ssam 	register struct sockaddr *dst;
2646920Ssam 	int nets;
2656920Ssam 
266*6929Ssam 	if (performnlist) {
267*6929Ssam 		nlist("/vmunix", nl);
268*6929Ssam 		if (nl[N_IFNET].n_value == 0) {
269*6929Ssam 			performnlist++;
270*6929Ssam 			printf("ifnet: not in namelist\n");
271*6929Ssam 			return;
272*6929Ssam 		}
273*6929Ssam 		performnlist = 0;
2746920Ssam 	}
2756920Ssam 	if (kmem < 0) {
276*6929Ssam 		kmem = open("/dev/kmem", 0);
277*6929Ssam 		if (kmem < 0) {
278*6929Ssam 			perror("/dev/kmem");
279*6929Ssam 			return;
280*6929Ssam 		}
2816920Ssam 	}
282*6929Ssam 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
283*6929Ssam 	    read(kmem, (char *)&ifp, sizeof (ifp)) != sizeof (ifp)) {
284*6929Ssam 		performnlist = 1;
285*6929Ssam 		return;
286*6929Ssam 	}
2876920Ssam 	bzero((char *)&net, sizeof (net));
2886920Ssam 	net.sin_family = AF_INET;
2896920Ssam 	nets = 0;
290*6929Ssam 	while (ifp) {
291*6929Ssam 		if (lseek(kmem, (long)ifp, 0) == -1 ||
292*6929Ssam 		  read(kmem, (char *)&ifstruct, sizeof (ifstruct)) !=
293*6929Ssam 		  sizeof (ifstruct)) {
2946920Ssam 			perror("read");
295*6929Ssam 			performnlist = 1;
2966920Ssam 			break;
2976920Ssam 		}
298*6929Ssam 		ifp = &ifstruct;
299*6929Ssam 		if ((ifp->if_flags & IFF_UP) == 0) {
300*6929Ssam 			lookforinterfaces++;
301*6929Ssam 	skip:
302*6929Ssam 			ifp = ifp->if_next;
303*6929Ssam 			continue;
304*6929Ssam 		}
305*6929Ssam 		if (ifp->if_addr.sa_family != AF_INET)
306*6929Ssam 			goto skip;
307*6929Ssam 		/* ignore software loopback networks. */
3086920Ssam 		if (ifp->if_net == LOOPBACKNET)
3096920Ssam 			goto skip;
310*6929Ssam 		/* check if we already know about this one */
311*6929Ssam 		if (if_ifwithaddr(&ifstruct.if_addr)) {
312*6929Ssam 			nets++;
3136920Ssam 			goto skip;
3146920Ssam 		}
315*6929Ssam 		ifp = (struct ifnet *)malloc(sizeof (struct ifnet));
316*6929Ssam 		if (ifp == 0) {
317*6929Ssam 			printf("routed: out of memory\n");
318*6929Ssam 			break;
319*6929Ssam 		}
320*6929Ssam 		bcopy((char *)&ifstruct, (char *)ifp, sizeof (struct ifnet));
3216920Ssam 
3226920Ssam 		/*
323*6929Ssam 		 * Count the # of directly connected networks
324*6929Ssam 		 * and point to point links which aren't looped
325*6929Ssam 		 * back to ourself.  This is used below to
326*6929Ssam 		 * decide if we should be a routing "supplier".
3276920Ssam 		 */
328*6929Ssam 		if ((ifp->if_flags & IFF_POINTOPOINT) == 0 ||
329*6929Ssam 		    if_ifwithaddr(&ifp->if_dstaddr) == 0)
330*6929Ssam 			nets++;
3316920Ssam 
332*6929Ssam 		if (ifp->if_flags & IFF_POINTOPOINT)
333*6929Ssam 			dst = &ifp->if_dstaddr;
334*6929Ssam 		else {
335*6929Ssam 			net.sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
336*6929Ssam 			dst = (struct sockaddr *)&net;
337*6929Ssam 		}
338*6929Ssam 		next = ifp->if_next;
339*6929Ssam 		ifp->if_next = ifnet;
340*6929Ssam 		ifnet = ifp;
341*6929Ssam 		if (rtlookup(dst) == 0)
342*6929Ssam 			rtadd(dst, &ifp->if_addr, 0);
343*6929Ssam 		ifp = next;
3446920Ssam 	}
3456920Ssam 	supplier = nets > 1;
3466920Ssam }
3476920Ssam 
3486920Ssam /*
3496920Ssam  * Send a request message to all directly
3506920Ssam  * connected hosts and networks.
3516920Ssam  */
3526920Ssam request()
3536920Ssam {
354*6929Ssam 	register struct rip *msg = (struct rip *)packet;
3556920Ssam 
356*6929Ssam 	msg->rip_cmd = RIPCMD_REQUEST;
357*6929Ssam 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
358*6929Ssam 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
359*6929Ssam 	sendall();
3606920Ssam }
3616920Ssam 
3626920Ssam /*
3636920Ssam  * Broadcast a new, or modified, routing table entry
3646920Ssam  * to all directly connected hosts and networks.
3656920Ssam  */
3666920Ssam broadcast(entry)
3676920Ssam 	struct rt_entry *entry;
3686920Ssam {
369*6929Ssam 	struct rip *msg = (struct rip *)packet;
370*6929Ssam 
371*6929Ssam 	log("broadcast", entry);
372*6929Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
373*6929Ssam 	msg->rip_nets[0].rip_dst = entry->rt_dst;
374*6929Ssam 	msg->rip_nets[0].rip_metric = min(entry->rt_metric+1, HOPCNT_INFINITY);
375*6929Ssam 	sendall();
376*6929Ssam }
377*6929Ssam 
378*6929Ssam sendall()
379*6929Ssam {
3806920Ssam 	register struct rt_hash *rh;
3816920Ssam 	register struct rt_entry *rt;
3826920Ssam 	register struct sockaddr *dst;
3836920Ssam 	struct rt_hash *base = hosthash;
3846920Ssam 	int doinghost = 1;
3856920Ssam 
3866920Ssam again:
3876920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
3886920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
3896920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
3906920Ssam 			continue;
3916920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
3926920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
3936920Ssam 		else
3946920Ssam 			dst = &rt->rt_gateway;
3956920Ssam 		(*afswitch[dst->sa_family].af_output)(dst, sizeof (struct rip));
3966920Ssam 	}
3976920Ssam 	if (doinghost) {
398*6929Ssam 		base = nethash;
3996920Ssam 		doinghost = 0;
4006920Ssam 		goto again;
4016920Ssam 	}
4026920Ssam }
4036920Ssam 
4046920Ssam /*
4056920Ssam  * Supply all directly connected neighbors with the
4066920Ssam  * current state of the routing tables.
4076920Ssam  */
4086920Ssam supplyall()
4096920Ssam {
4106920Ssam 	register struct rt_entry *rt;
4116920Ssam 	register struct rt_hash *rh;
4126920Ssam 	register struct sockaddr *dst;
4136920Ssam 	struct rt_hash *base = hosthash;
4146920Ssam 	int doinghost = 1;
4156920Ssam 
4166920Ssam again:
4176920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4186920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4196920Ssam 		if ((rt->rt_flags & RTF_SILENT) || rt->rt_metric > 0)
4206920Ssam 			continue;
4216920Ssam 		if (rt->rt_ifp && (rt->rt_ifp->if_flags & IFF_BROADCAST))
4226920Ssam 			dst = &rt->rt_ifp->if_broadaddr;
4236920Ssam 		else
4246920Ssam 			dst = &rt->rt_gateway;
425*6929Ssam 		log("supply", rt);
4266920Ssam 		supply(dst);
4276920Ssam 	}
4286920Ssam 	if (doinghost) {
4296920Ssam 		base = nethash;
4306920Ssam 		doinghost = 0;
4316920Ssam 		goto again;
4326920Ssam 	}
4336920Ssam }
4346920Ssam 
4356920Ssam /*
4366920Ssam  * Supply routing information to target "sa".
4376920Ssam  */
4386920Ssam supply(sa)
4396920Ssam 	struct sockaddr *sa;
4406920Ssam {
4416920Ssam 	struct rip *msg = (struct rip *)packet;
4426920Ssam 	struct netinfo *n = msg->rip_nets;
4436920Ssam 	register struct rt_hash *rh;
4446920Ssam 	register struct rt_entry *rt;
4456920Ssam 	struct rt_hash *base = hosthash;
446*6929Ssam 	int doinghost = 1, size;
4476920Ssam 	int (*output)() = afswitch[sa->sa_family].af_output;
4486920Ssam 
4496920Ssam 	msg->rip_cmd = RIPCMD_RESPONSE;
4506920Ssam again:
4516920Ssam 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
4526920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
4536920Ssam 
4546920Ssam 		/*
4556920Ssam 		 * Flush packet out if not enough room for
4566920Ssam 		 * another routing table entry.
4576920Ssam 		 */
458*6929Ssam 		size = (char *)n - packet;
459*6929Ssam 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
460*6929Ssam 			(*output)(sa, size);
4616920Ssam 			n = msg->rip_nets;
4626920Ssam 		}
4636920Ssam 		n->rip_dst = rt->rt_dst;
464*6929Ssam 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
465*6929Ssam 		n++;
4666920Ssam 	}
4676920Ssam 	if (doinghost) {
4686920Ssam 		doinghost = 0;
4696920Ssam 		base = nethash;
4706920Ssam 		goto again;
4716920Ssam 	}
472*6929Ssam 	if (n != msg->rip_nets)
473*6929Ssam 		(*output)(sa, (char *)n - packet);
4746920Ssam }
4756920Ssam 
4766920Ssam /*
4776920Ssam  * Respond to a routing info request.
4786920Ssam  */
4796920Ssam rip_respond(from, size)
4806920Ssam 	struct sockaddr *from;
4816920Ssam 	int size;
4826920Ssam {
4836920Ssam 	register struct rip *msg = (struct rip *)packet;
4846920Ssam 	struct netinfo *np = msg->rip_nets;
4856920Ssam 	struct rt_entry *rt;
4866920Ssam 	int newsize = 0;
4876920Ssam 
488*6929Ssam 	size -= 4 * sizeof (char);
4896920Ssam 	while (size > 0) {
4906920Ssam 		if (size < sizeof (struct netinfo))
4916920Ssam 			break;
4926920Ssam 		size -= sizeof (struct netinfo);
4936920Ssam 		if (np->rip_dst.sa_family == AF_UNSPEC &&
4946920Ssam 		    np->rip_metric == HOPCNT_INFINITY && size == 0) {
4956920Ssam 			supply(from);
4966920Ssam 			return;
4976920Ssam 		}
4986920Ssam 		rt = rtlookup(&np->rip_dst);
499*6929Ssam 		np->rip_metric = rt == 0 ?
500*6929Ssam 			HOPCNT_INFINITY : min(rt->rt_metric+1, HOPCNT_INFINITY);
5016920Ssam 		np++, newsize += sizeof (struct netinfo);
5026920Ssam 	}
5036920Ssam 	if (newsize > 0) {
5046920Ssam 		msg->rip_cmd = RIPCMD_RESPONSE;
5056920Ssam 		newsize += sizeof (int);
5066920Ssam 		(*afswitch[from->sa_family].af_output)(from, newsize);
5076920Ssam 	}
5086920Ssam }
5096920Ssam 
5106920Ssam /*
5116920Ssam  * Handle an incoming routing packet.
5126920Ssam  */
5136920Ssam rip_input(from, size)
5146920Ssam 	struct sockaddr *from;
5156920Ssam 	int size;
5166920Ssam {
5176920Ssam 	register struct rip *msg = (struct rip *)packet;
5186920Ssam 	struct rt_entry *rt;
5196920Ssam 	struct netinfo *n;
5206920Ssam 
5216920Ssam 	if (msg->rip_cmd != RIPCMD_RESPONSE &&
5226920Ssam 	    msg->rip_cmd != RIPCMD_REQUEST)
5236920Ssam 		return;
5246920Ssam 
5256920Ssam 	if (msg->rip_cmd == RIPCMD_RESPONSE &&
5266920Ssam 	    (*afswitch[from->sa_family].af_portmatch)(from) == 0)
5276920Ssam 		return;
5286920Ssam 	if (msg->rip_cmd == RIPCMD_REQUEST) {
5296920Ssam 		rip_respond(from, size);
5306920Ssam 		return;
5316920Ssam 	}
5326920Ssam 
5336920Ssam 	/*
5346920Ssam 	 * Process updates.
5356920Ssam 	 * Extraneous information like Internet ports
5366920Ssam 	 * must first be purged from the sender's address for
5376920Ssam 	 * pattern matching below.
5386920Ssam 	 */
5396920Ssam 	(*afswitch[from->sa_family].af_canon)(from);
5406920Ssam 	if (trace)
5416920Ssam 		printf("input from %x\n", from->sin_addr);
5426920Ssam 	/*
5436920Ssam 	 * If response packet is from ourselves, use it only
5446920Ssam 	 * to reset timer on entry.  Otherwise, we'd believe
5456920Ssam 	 * it as gospel (since it comes from the router) and
5466920Ssam 	 * unknowingly update the metric to show the outgoing
5476920Ssam 	 * cost (higher than our real cost).  I guess the protocol
5486920Ssam 	 * spec doesn't address this because Xerox Ethernets
5496920Ssam 	 * don't hear their own broadcasts?
5506920Ssam 	 */
5516920Ssam 	if (if_ifwithaddr(from)) {
5526920Ssam 		rt = rtlookup(from);
5536920Ssam 		if (rt)
5546920Ssam 			rt->rt_timer = 0;
5556920Ssam 		return;
5566920Ssam 	}
557*6929Ssam 	size -= 4 * sizeof (char);
5586920Ssam 	n = msg->rip_nets;
5596920Ssam 	for (; size > 0; size -= sizeof (struct netinfo), n++) {
5606920Ssam 		if (size < sizeof (struct netinfo))
5616920Ssam 			break;
5626920Ssam 		if (trace)
5636920Ssam 			printf("dst %x hc %d...", n->rip_dst.sin_addr,
5646920Ssam 				n->rip_metric);
5656920Ssam 		rt = rtlookup(&n->rip_dst);
5666920Ssam 
5676920Ssam 		/*
5686920Ssam 		 * Unknown entry, add it to the tables only if
5696920Ssam 		 * its interesting.
5706920Ssam 		 */
5716920Ssam 		if (rt == 0) {
5726920Ssam 			if (n->rip_metric < HOPCNT_INFINITY)
5736920Ssam 				rtadd(&n->rip_dst, from, n->rip_metric);
5746920Ssam 			if (trace)
5756920Ssam 				printf("new\n");
5766920Ssam 			continue;
5776920Ssam 		}
5786920Ssam 
5796920Ssam 		if (trace)
5806920Ssam 			printf("ours: gate %x hc %d timer %d\n",
5816920Ssam 			rt->rt_gateway.sin_addr,
5826920Ssam 			rt->rt_metric, rt->rt_timer);
5836920Ssam 		/*
5846920Ssam 		 * Update the entry if one of the following is true:
5856920Ssam 		 *
5866920Ssam 		 * (1) The update came directly from the gateway.
5876920Ssam 		 * (2) A shorter path is provided.
5886920Ssam 		 * (3) The entry hasn't been updated in a while
589*6929Ssam 		 *     and a path of equivalent cost is offered
590*6929Ssam 		 *     (with the cost finite).
5916920Ssam 		 */
5926920Ssam 		if (equal(from, &rt->rt_gateway) ||
5936920Ssam 		    rt->rt_metric > n->rip_metric ||
5946920Ssam 		    (rt->rt_timer > (EXPIRE_TIME/2) &&
595*6929Ssam 		    rt->rt_metric == n->rip_metric &&
596*6929Ssam 		    rt->rt_metric < HOPCNT_INFINITY)) {
5976920Ssam 			rtchange(rt, from, n->rip_metric);
5986920Ssam 			rt->rt_timer = 0;
5996920Ssam 		}
6006920Ssam 	}
6016920Ssam }
6026920Ssam 
6036920Ssam /*
6046920Ssam  * Lookup an entry to the appropriate dstination.
6056920Ssam  */
6066920Ssam struct rt_entry *
6076920Ssam rtlookup(dst)
6086920Ssam 	struct sockaddr *dst;
6096920Ssam {
6106920Ssam 	register struct rt_entry *rt;
6116920Ssam 	register struct rt_hash *rh;
6126920Ssam 	register int hash, (*match)();
6136920Ssam 	struct afhash h;
6146920Ssam 	int af = dst->sa_family, doinghost = 1;
6156920Ssam 
6166920Ssam 	if (af >= AF_MAX)
6176920Ssam 		return (0);
6186920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6196920Ssam 	hash = h.afh_hosthash;
6206920Ssam 	rh = &hosthash[hash % ROUTEHASHSIZ];
6216920Ssam 
6226920Ssam again:
6236920Ssam 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
6246920Ssam 		if (rt->rt_hash != hash)
6256920Ssam 			continue;
6266920Ssam 		if (doinghost) {
6276920Ssam 			if (equal(&rt->rt_dst, dst))
6286920Ssam 				return (rt);
6296920Ssam 		} else {
6306920Ssam 			if (rt->rt_dst.sa_family == af &&
6316920Ssam 			    (*match)(&rt->rt_dst, dst))
6326920Ssam 				return (rt);
6336920Ssam 		}
6346920Ssam 	}
6356920Ssam 	if (doinghost) {
6366920Ssam 		doinghost = 0;
6376920Ssam 		hash = h.afh_nethash;
6386920Ssam 		match = afswitch[af].af_netmatch;
6396920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6406920Ssam 		goto again;
6416920Ssam 	}
6426920Ssam 	return (0);
6436920Ssam }
6446920Ssam 
6456920Ssam rtinit()
6466920Ssam {
6476920Ssam 	register struct rt_hash *rh;
6486920Ssam 
6496920Ssam 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
6506920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6516920Ssam 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
6526920Ssam 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
6536920Ssam }
6546920Ssam 
6556920Ssam /*
6566920Ssam  * Add a new entry.
6576920Ssam  */
6586920Ssam rtadd(dst, gate, metric)
6596920Ssam 	struct sockaddr *dst, *gate;
6606920Ssam 	short metric;
6616920Ssam {
6626920Ssam 	struct afhash h;
6636920Ssam 	register struct rt_entry *rt;
6646920Ssam 	struct rt_hash *rh;
6656920Ssam 	int af = dst->sa_family, flags, hash;
6666920Ssam 
6676920Ssam 	if (af >= AF_MAX)
6686920Ssam 		return;
6696920Ssam 	(*afswitch[af].af_hash)(dst, &h);
6706920Ssam 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
6716920Ssam 	if (flags & RTF_HOST) {
6726920Ssam 		hash = h.afh_hosthash;
6736920Ssam 		rh = &hosthash[hash % ROUTEHASHSIZ];
6746920Ssam 	} else {
6756920Ssam 		hash = h.afh_nethash;
6766920Ssam 		rh = &nethash[hash % ROUTEHASHSIZ];
6776920Ssam 	}
6786920Ssam 	rt = (struct rt_entry *)malloc(sizeof (*rt));
6796920Ssam 	if (rt == 0)
6806920Ssam 		return;
6816920Ssam 	rt->rt_hash = hash;
6826920Ssam 	rt->rt_dst = *dst;
6836920Ssam 	rt->rt_gateway = *gate;
6846920Ssam 	rt->rt_metric = metric;
6856920Ssam 	rt->rt_timer = 0;
6866920Ssam 	rt->rt_flags = RTF_UP | flags;
6876920Ssam 	rt->rt_ifp = if_ifwithnet(&rt->rt_gateway);
6886920Ssam 	if (metric == 0)
6896920Ssam 		rt->rt_flags |= RTF_DIRECT;
6906920Ssam 	insque(rt, rh);
691*6929Ssam 	log("add", rt);
6926920Ssam 	if (initializing)
6936920Ssam 		return;
6946920Ssam 	if (supplier)
6956920Ssam 		broadcast(rt);
6966920Ssam 	if (install) {
6976920Ssam 		rt->rt_flags |= RTF_ADDRT;
6986920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
6996920Ssam 	}
7006920Ssam }
7016920Ssam 
7026920Ssam /*
7036920Ssam  * Look to see if a change to an existing entry
7046920Ssam  * is warranted; if so, make it.
7056920Ssam  */
7066920Ssam rtchange(rt, gate, metric)
7076920Ssam 	struct rt_entry *rt;
7086920Ssam 	struct sockaddr *gate;
7096920Ssam 	short metric;
7106920Ssam {
7116920Ssam 	int change = 0;
7126920Ssam 
7136920Ssam 	if (!equal(&rt->rt_gateway, gate)) {
7146920Ssam 		rt->rt_gateway = *gate;
7156920Ssam 		change++;
7166920Ssam 	}
7176920Ssam 
7186920Ssam 	/*
7196920Ssam 	 * If the hop count has changed, adjust
7206920Ssam 	 * the flags in the routing table entry accordingly.
7216920Ssam 	 */
7226920Ssam 	if (metric != rt->rt_metric) {
7236920Ssam 		if (rt->rt_metric == 0)
7246920Ssam 			rt->rt_flags &= ~RTF_DIRECT;
7256920Ssam 		rt->rt_metric = metric;
7266920Ssam 		if (metric >= HOPCNT_INFINITY)
7276920Ssam 			rt->rt_flags &= ~RTF_UP;
7286920Ssam 		else
7296920Ssam 			rt->rt_flags |= RTF_UP;
7306920Ssam 		change++;
7316920Ssam 	}
7326920Ssam 
7336920Ssam 	if (!change)
7346920Ssam 		return;
7356920Ssam 	if (supplier)
7366920Ssam 		broadcast(rt);
737*6929Ssam 	log("change", rt);
7386920Ssam 	if (install) {
7396920Ssam 		rt->rt_flags |= RTF_CHGRT;
7406920Ssam 		rt->rt_retry = EXPIRE_TIME/TIMER_RATE;
7416920Ssam 	}
7426920Ssam }
7436920Ssam 
7446920Ssam /*
7456920Ssam  * Delete a routing table entry.
7466920Ssam  */
7476920Ssam rtdelete(rt)
7486920Ssam 	struct rt_entry *rt;
7496920Ssam {
750*6929Ssam 	log("delete", rt);
7516920Ssam 	if (install)
7526920Ssam 		if (ioctl(s, SIOCDELRT, (char *)&rt->rt_hash) &&
7536920Ssam 		  errno == EBUSY)
7546920Ssam 			rt->rt_flags |= RTF_DELRT;
7556920Ssam 	remque(rt);
7566920Ssam 	free((char *)rt);
7576920Ssam }
7586920Ssam 
7596920Ssam log(operation, rt)
7606920Ssam 	char *operation;
7616920Ssam 	struct rt_entry *rt;
7626920Ssam {
7636920Ssam 	time_t t = time(0);
7646920Ssam 	struct sockaddr_in *dst, *gate;
7656920Ssam 	static struct flagbits {
7666920Ssam 		int	t_bits;
7676920Ssam 		char	*t_name;
7686920Ssam 	} bits[] = {
7696920Ssam 		{ RTF_UP,	"UP" },
7706920Ssam 		{ RTF_DIRECT,	"DIRECT" },
7716920Ssam 		{ RTF_HOST,	"HOST" },
7726920Ssam 		{ RTF_DELRT,	"DELETE" },
7736920Ssam 		{ RTF_CHGRT,	"CHANGE" },
7746920Ssam 		{ RTF_SILENT,	"SILENT" },
7756920Ssam 		{ 0 }
7766920Ssam 	};
7776920Ssam 	register struct flagbits *p;
7786920Ssam 	register int first;
7796920Ssam 	char *cp;
7806920Ssam 
781*6929Ssam 	if (trace == 0)
782*6929Ssam 		return;
7836920Ssam 	printf("%s ", operation);
7846920Ssam 	dst = (struct sockaddr_in *)&rt->rt_dst;
7856920Ssam 	gate = (struct sockaddr_in *)&rt->rt_gateway;
7866920Ssam 	printf("dst %x, router %x, metric %d, flags ",
7876920Ssam 		dst->sin_addr, gate->sin_addr, rt->rt_metric);
7886920Ssam 	cp = "%s";
7896920Ssam 	for (first = 1, p = bits; p->t_bits > 0; p++) {
7906920Ssam 		if ((rt->rt_flags & p->t_bits) == 0)
7916920Ssam 			continue;
7926920Ssam 		printf(cp, p->t_name);
7936920Ssam 		if (first) {
7946920Ssam 			cp = "|%s";
7956920Ssam 			first = 0;
7966920Ssam 		}
7976920Ssam 	}
7986920Ssam 	putchar('\n');
7996920Ssam }
800*6929Ssam 
801*6929Ssam struct ifnet *
802*6929Ssam if_ifwithaddr(addr)
803*6929Ssam 	struct sockaddr *addr;
804*6929Ssam {
805*6929Ssam 	register struct ifnet *ifp;
806*6929Ssam 
807*6929Ssam #define	same(a1, a2) \
808*6929Ssam 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
809*6929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
810*6929Ssam 		if (ifp->if_addr.sa_family != addr->sa_family)
811*6929Ssam 			continue;
812*6929Ssam 		if (same(&ifp->if_addr, addr))
813*6929Ssam 			break;
814*6929Ssam 		if ((ifp->if_flags & IFF_BROADCAST) &&
815*6929Ssam 		    same(&ifp->if_broadaddr, addr))
816*6929Ssam 			break;
817*6929Ssam 	}
818*6929Ssam 	return (ifp);
819*6929Ssam #undef same
820*6929Ssam }
821*6929Ssam 
822*6929Ssam struct ifnet *
823*6929Ssam if_ifwithnet(addr)
824*6929Ssam 	register struct sockaddr *addr;
825*6929Ssam {
826*6929Ssam 	register struct ifnet *ifp;
827*6929Ssam 	register int af = addr->sa_family;
828*6929Ssam 	register int (*netmatch)();
829*6929Ssam 
830*6929Ssam 	if (af >= AF_MAX)
831*6929Ssam 		return (0);
832*6929Ssam 	netmatch = afswitch[af].af_netmatch;
833*6929Ssam 	for (ifp = ifnet; ifp; ifp = ifp->if_next) {
834*6929Ssam 		if (af != ifp->if_addr.sa_family)
835*6929Ssam 			continue;
836*6929Ssam 		if ((*netmatch)(addr, &ifp->if_addr))
837*6929Ssam 			break;
838*6929Ssam 	}
839*6929Ssam 	return (ifp);
840*6929Ssam }
841*6929Ssam 
842*6929Ssam struct in_addr
843*6929Ssam if_makeaddr(net, host)
844*6929Ssam 	int net, host;
845*6929Ssam {
846*6929Ssam 	u_long addr;
847*6929Ssam 
848*6929Ssam 	if (net < 128)
849*6929Ssam 		addr = (net << 24) | host;
850*6929Ssam 	else if (net < 65536)
851*6929Ssam 		addr = (net << 16) | host;
852*6929Ssam 	else
853*6929Ssam 		addr = (net << 8) | host;
854*6929Ssam #ifdef vax
855*6929Ssam 	addr = htonl(addr);
856*6929Ssam #endif
857*6929Ssam 	return (*(struct in_addr *)&addr);
858*6929Ssam }
859