xref: /csrg-svn/sbin/routed/routed.c (revision 8339)
1 #ifndef lint
2 static char sccsid[] = "@(#)routed.c	4.23 10/06/82";
3 #endif
4 
5 /*
6  * Routing Table Management Daemon
7  */
8 #include <sys/types.h>
9 #include <sys/ioctl.h>
10 #include <sys/socket.h>
11 #include <net/in.h>
12 #include <net/if.h>
13 #include <errno.h>
14 #include <stdio.h>
15 #include <nlist.h>
16 #include <signal.h>
17 #include <time.h>
18 #include <netdb.h>
19 #define	RIPCMDS
20 #include "rip.h"
21 #include "router.h"
22 
23 #define	LOOPBACKNET	0177
24 /* casts to keep lint happy */
25 #define	insque(q,p)	_insque((caddr_t)q,(caddr_t)p)
26 #define	remque(q)	_remque((caddr_t)q)
27 #define equal(a1, a2) \
28 	(bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
29 #define	min(a,b)	((a)>(b)?(b):(a))
30 
31 struct nlist nl[] = {
32 #define	N_IFNET		0
33 	{ "_ifnet" },
34 	0,
35 };
36 
37 struct	sockaddr_in routingaddr = { AF_INET, IPPORT_ROUTESERVER };
38 struct	sockaddr_in noroutingaddr = { AF_INET, IPPORT_ROUTESERVER+1 };
39 
40 int	s;
41 int	snoroute;		/* socket with no routing */
42 int	kmem = -1;
43 int	supplier = -1;		/* process should supply updates */
44 int	install = 1;		/* if 1 call kernel */
45 int	lookforinterfaces = 1;
46 int	performnlist = 1;
47 int	externalinterfaces = 0;	/* # of remote and local interfaces */
48 int	timeval = -TIMER_RATE;
49 int	timer();
50 int	cleanup();
51 
52 #define tprintf if (trace) printf
53 int	trace = 0;
54 FILE	*ftrace;
55 
56 char	packet[MAXPACKETSIZE+1];
57 struct	rip *msg = (struct rip *)packet;
58 
59 struct in_addr if_makeaddr();
60 struct interface *if_ifwithaddr(), *if_ifwithnet();
61 extern char *malloc(), *sys_errlist[];
62 extern int errno, exit();
63 char	**argv0;
64 
65 int	sendmsg(), supply();
66 
67 main(argc, argv)
68 	int argc;
69 	char *argv[];
70 {
71 	int cc;
72 	struct sockaddr from;
73 
74 	argv0 = argv;
75 #ifndef DEBUG
76 	if (fork())
77 		exit(0);
78 	for (cc = 0; cc < 10; cc++)
79 		(void) close(cc);
80 	(void) open("/", 0);
81 	(void) dup2(0, 1);
82 	(void) dup2(0, 2);
83 	{ int t = open("/dev/tty", 2);
84 	  if (t >= 0) {
85 		ioctl(t, TIOCNOTTY, (char *)0);
86 		(void) close(t);
87 	  }
88 	}
89 #endif
90 	if (trace) {
91 		ftrace = fopen("/etc/routerlog", "w");
92 		dup2(fileno(ftrace), 1);
93 		dup2(fileno(ftrace), 2);
94 	}
95 
96 	/*
97 	 * We use two sockets.  One for which outgoing
98 	 * packets are routed and for which they're not.
99 	 * The latter allows us to delete routing table
100 	 * entries in the kernel for network interfaces
101 	 * attached to our host which we believe are down
102 	 * while still polling it to see when/if it comes
103 	 * back up.  With the new ipc interface we'll be
104 	 * able to specify ``don't route'' as an option
105 	 * to send, but until then we utilize a second port.
106 	 */
107 #ifdef vax || pdp11
108 	routingaddr.sin_port = htons(routingaddr.sin_port);
109 	noroutingaddr.sin_port = htons(noroutingaddr.sin_port);
110 #endif
111 again:
112 	s = socket(SOCK_DGRAM, 0, &routingaddr, 0);
113 	if (s < 0) {
114 		perror("socket");
115 		sleep(30);
116 		goto again;
117 	}
118 again2:
119 	snoroute = socket(SOCK_DGRAM, 0, &noroutingaddr, SO_DONTROUTE);
120 	if (snoroute < 0) {
121 		perror("socket");
122 		sleep(30);
123 		goto again2;
124 	}
125 	argv++, argc--;
126 	while (argc > 0 && **argv == '-') {
127 		if (!strcmp(*argv, "-s") == 0) {
128 			supplier = 1;
129 			argv++, argc--;
130 			continue;
131 		}
132 		if (!strcmp(*argv, "-q") == 0) {
133 			supplier = 0;
134 			argv++, argc--;
135 			continue;
136 		}
137 		goto usage;
138 	}
139 	if (argc > 0) {
140 usage:
141 		fprintf(stderr, "usage: routed [ -sq ]\n");
142 		exit(1);
143 	}
144 	/*
145 	 * Collect an initial view of the world by
146 	 * snooping in the kernel and the gateway kludge
147 	 * file.  Then, send a request packet on all
148 	 * directly connected networks to find out what
149 	 * everyone else thinks.
150 	 */
151 	rtinit();
152 	gwkludge();
153 	ifinit();
154 	if (supplier < 0)
155 		supplier = 0;
156 	msg->rip_cmd = RIPCMD_REQUEST;
157 	msg->rip_nets[0].rip_dst.sa_family = AF_UNSPEC;
158 	msg->rip_nets[0].rip_metric = HOPCNT_INFINITY;
159 	toall(sendmsg);
160 	sigset(SIGALRM, timer);
161 	timer();
162 
163 	for (;;) {
164 		cc = receive(s, &from, packet, sizeof (packet));
165 		if (cc <= 0) {
166 			if (cc < 0 && errno != EINTR)
167 				perror("receive");
168 			continue;
169 		}
170 		sighold(SIGALRM);
171 		rip_input(&from, cc);
172 		sigrelse(SIGALRM);
173 	}
174 }
175 
176 rtinit()
177 {
178 	register struct rthash *rh;
179 
180 	for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
181 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
182 	for (rh = hosthash; rh < &hosthash[ROUTEHASHSIZ]; rh++)
183 		rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
184 }
185 
186 struct	interface *ifnet;
187 
188 /*
189  * Probe the kernel through /dev/kmem to find the network
190  * interfaces which have configured themselves.  If the
191  * interface is present but not yet up (for example an
192  * ARPANET IMP), set the lookforinterfaces flag so we'll
193  * come back later and look again.
194  */
195 ifinit()
196 {
197 	struct interface *ifp;
198 	struct ifnet ifs, *next;
199 
200 	if (performnlist) {
201 		nlist("/vmunix", nl);
202 		if (nl[N_IFNET].n_value == 0) {
203 			printf("ifnet: not in namelist\n");
204 			goto bad;
205 		}
206 		performnlist = 0;
207 	}
208 	if (kmem < 0) {
209 		kmem = open("/dev/kmem", 0);
210 		if (kmem < 0) {
211 			perror("/dev/kmem");
212 			goto bad;
213 		}
214 	}
215 	if (lseek(kmem, (long)nl[N_IFNET].n_value, 0) == -1 ||
216 	    read(kmem, (char *)&next, sizeof (next)) != sizeof (next)) {
217 		printf("ifnet: error reading kmem\n");
218 		goto bad;
219 	}
220 	lookforinterfaces = 0;
221 	while (next) {
222 		if (lseek(kmem, (long)next, 0) == -1 ||
223 		    read(kmem, (char *)&ifs, sizeof (ifs)) != sizeof (ifs)) {
224 			perror("read");
225 			goto bad;
226 		}
227 		next = ifs.if_next;
228 		if ((ifs.if_flags & IFF_UP) == 0) {
229 			lookforinterfaces = 1;
230 			continue;
231 		}
232 		/* already known to us? */
233 		if (if_ifwithaddr(&ifs.if_addr))
234 			continue;
235 		/* argh, this'll have to change sometime */
236 		if (ifs.if_addr.sa_family != AF_INET)
237 			continue;
238 		/* no one cares about software loopback interfaces */
239 		if (ifs.if_net == LOOPBACKNET)
240 			continue;
241 		ifp = (struct interface *)malloc(sizeof (struct interface));
242 		if (ifp == 0) {
243 			printf("routed: out of memory\n");
244 			break;
245 		}
246 		/*
247 		 * Count the # of directly connected networks
248 		 * and point to point links which aren't looped
249 		 * back to ourself.  This is used below to
250 		 * decide if we should be a routing ``supplier''.
251 		 */
252 		if ((ifs.if_flags & IFF_POINTOPOINT) == 0 ||
253 		    if_ifwithaddr(&ifs.if_dstaddr) == 0)
254 			externalinterfaces++;
255 		ifp->int_addr = ifs.if_addr;
256 		ifp->int_flags = ifs.if_flags | IFF_INTERFACE;
257 		/* this works because broadaddr overlaps dstaddr */
258 		ifp->int_broadaddr = ifs.if_broadaddr;
259 		ifp->int_net = ifs.if_net;
260 		ifp->int_metric = 0;
261 		ifp->int_next = ifnet;
262 		ifnet = ifp;
263 		addrouteforif(ifp);
264 	}
265 	if (externalinterfaces > 1 && supplier < 0)
266 		supplier = 1;
267 	return;
268 bad:
269 	sleep(60);
270 	close(kmem), close(s), close(snoroute);
271 	execv("/etc/routed", argv0);
272 	_exit(0177);
273 }
274 
275 addrouteforif(ifp)
276 	struct interface *ifp;
277 {
278 	struct sockaddr_in net;
279 	struct sockaddr *dst;
280 	int state, metric;
281 	struct rt_entry *rt;
282 
283 	if (ifp->int_flags & IFF_POINTOPOINT)
284 		dst = &ifp->int_dstaddr;
285 	else {
286 		bzero((char *)&net, sizeof (net));
287 		net.sin_family = AF_INET;
288 		net.sin_addr = if_makeaddr(ifp->int_net, INADDR_ANY);
289 		dst = (struct sockaddr *)&net;
290 	}
291 	rt = rtlookup(dst);
292 	rtadd(dst, &ifp->int_addr, ifp->int_metric,
293 		ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
294 	if (rt)
295 		rtdelete(rt);
296 }
297 
298 /*
299  * As a concession to the ARPANET we read a list of gateways
300  * from /etc/gateways and add them to our tables.  This file
301  * exists at each ARPANET gateway and indicates a set of ``remote''
302  * gateways (i.e. a gateway which we can't immediately determine
303  * if it's present or not as we can do for those directly connected
304  * at the hardware level).  If a gateway is marked ``passive''
305  * in the file, then we assume it doesn't have a routing process
306  * of our design and simply assume it's always present.  Those
307  * not marked passive are treated as if they were directly
308  * connected -- they're added into the interface list so we'll
309  * send them routing updates.
310  */
311 gwkludge()
312 {
313 	struct sockaddr_in dst, gate;
314 	FILE *fp;
315 	char *dname, *gname, *qual, buf[BUFSIZ];
316 	struct interface *ifp;
317 	int metric;
318 
319 	fp = fopen("/etc/gateways", "r");
320 	if (fp == NULL)
321 		return;
322 	qual = buf; dname = buf + 64; gname = buf + ((BUFSIZ - 64) / 2);
323 	bzero((char *)&dst, sizeof (dst));
324 	bzero((char *)&gate, sizeof (gate));
325 	dst.sin_family = gate.sin_family = AF_INET;
326 	/* format: dst XX gateway XX metric DD [passive]\n */
327 #define	readentry(fp) \
328 	fscanf((fp), "dst %s gateway %s metric %d %s\n", \
329 		dname, gname, &metric, qual)
330 	for (;;) {
331 		struct hostent *host;
332 
333 		if (readentry(fp) == EOF)
334 			break;
335 		host = gethostbyname(dname);
336 		if (host == 0)
337 			continue;
338 		bcopy(host->h_addr, &dst.sin_addr, host->h_length);
339 		host = gethostbyname(gname);
340 		if (host == 0)
341 			continue;
342 		bcopy(host->h_addr, &gate.sin_addr, host->h_length);
343 		ifp = (struct interface *)malloc(sizeof (*ifp));
344 		bzero((char *)ifp, sizeof (*ifp));
345 		ifp->int_flags = IFF_REMOTE;
346 		/* can't identify broadcast capability */
347 		ifp->int_net = in_netof(dst.sin_addr);
348 		if ((*afswitch[dst.sin_family].af_checkhost)(&dst)) {
349 			ifp->int_flags |= IFF_POINTOPOINT;
350 			ifp->int_dstaddr = *((struct sockaddr *)&dst);
351 		}
352 		if (strcmp(qual, "passive") == 0)
353 			ifp->int_flags |= IFF_PASSIVE;
354 		else
355 			/* assume no duplicate entries */
356 			externalinterfaces++;
357 		ifp->int_addr = *((struct sockaddr *)&gate);
358 		ifp->int_metric = metric;
359 		ifp->int_next = ifnet;
360 		ifnet = ifp;
361 		addrouteforif(ifp);
362 	}
363 	fclose(fp);
364 }
365 
366 /*
367  * Timer routine.  Performs routing information supply
368  * duties and manages timers on routing table entries.
369  */
370 timer()
371 {
372 	register struct rthash *rh;
373 	register struct rt_entry *rt;
374 	struct rthash *base = hosthash;
375 	int doinghost = 1, timetobroadcast;
376 
377 	timeval += TIMER_RATE;
378 	if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
379 		ifinit();
380 	timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
381 	tprintf(">>> time %d >>>\n", timeval);
382 again:
383 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++) {
384 		rt = rh->rt_forw;
385 		for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
386 			/*
387 			 * We don't advance time on a routing entry for
388 			 * a passive gateway or that for our only interface.
389 			 * The latter is excused because we don't act as
390 			 * a routing information supplier and hence would
391 			 * time it out.  This is fair as if it's down
392 			 * we're cut off from the world anyway and it's
393 			 * not likely we'll grow any new hardware in
394 			 * the mean time.
395 			 */
396 			if (!(rt->rt_state & RTS_PASSIVE) &&
397 			    (supplier || !(rt->rt_state & RTS_INTERFACE)))
398 				rt->rt_timer += TIMER_RATE;
399 			if (rt->rt_timer >= EXPIRE_TIME)
400 				rt->rt_metric = HOPCNT_INFINITY;
401 			log("", rt);
402 			if (rt->rt_timer >= GARBAGE_TIME) {
403 				rt = rt->rt_back;
404 				rtdelete(rt->rt_forw);
405 				continue;
406 			}
407 			if (rt->rt_state & RTS_CHANGED) {
408 				rt->rt_state &= ~RTS_CHANGED;
409 				/* don't send extraneous packets */
410 				if (!supplier || timetobroadcast)
411 					continue;
412 				log("broadcast", rt);
413 				msg->rip_cmd = RIPCMD_RESPONSE;
414 				msg->rip_nets[0].rip_dst = rt->rt_dst;
415 				msg->rip_nets[0].rip_metric =
416 				    min(rt->rt_metric+1, HOPCNT_INFINITY);
417 				toall(sendmsg);
418 			}
419 		}
420 	}
421 	if (doinghost) {
422 		doinghost = 0;
423 		base = nethash;
424 		goto again;
425 	}
426 	if (timetobroadcast)
427 		toall(supply);
428 	tprintf("<<< time %d <<<\n", timeval);
429 	alarm(TIMER_RATE);
430 }
431 
432 toall(f)
433 	int (*f)();
434 {
435 	register struct interface *ifp;
436 	register struct sockaddr *dst;
437 
438 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
439 		if (ifp->int_flags & IFF_PASSIVE)
440 			continue;
441 		dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
442 		      ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
443 		      &ifp->int_addr;
444 		(*f)(dst, ifp->int_flags & IFF_INTERFACE);
445 	}
446 }
447 
448 /*ARGSUSED*/
449 sendmsg(dst, dontroute)
450 	struct sockaddr *dst;
451 	int dontroute;
452 {
453 	(*afswitch[dst->sa_family].af_output)(s, dst, sizeof (struct rip));
454 }
455 
456 /*
457  * Supply dst with the contents of the routing tables.
458  * If this won't fit in one packet, chop it up into several.
459  */
460 supply(dst, dontroute)
461 	struct sockaddr *dst;
462 	int dontroute;
463 {
464 	register struct rt_entry *rt;
465 	struct netinfo *n = msg->rip_nets;
466 	register struct rthash *rh;
467 	struct rthash *base = hosthash;
468 	int doinghost = 1, size;
469 	int (*output)() = afswitch[dst->sa_family].af_output;
470 	int sto = dontroute ? snoroute : s;
471 
472 	msg->rip_cmd = RIPCMD_RESPONSE;
473 again:
474 	for (rh = base; rh < &base[ROUTEHASHSIZ]; rh++)
475 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
476 		size = (char *)n - packet;
477 		if (size > MAXPACKETSIZE - sizeof (struct netinfo)) {
478 			(*output)(sto, dst, size);
479 			n = msg->rip_nets;
480 		}
481 		n->rip_dst = rt->rt_dst;
482 		n->rip_metric = min(rt->rt_metric + 1, HOPCNT_INFINITY);
483 		n++;
484 	}
485 	if (doinghost) {
486 		doinghost = 0;
487 		base = nethash;
488 		goto again;
489 	}
490 	if (n != msg->rip_nets)
491 		(*output)(sto, dst, (char *)n - packet);
492 }
493 
494 /*
495  * Handle an incoming routing packet.
496  */
497 rip_input(from, size)
498 	struct sockaddr *from;
499 	int size;
500 {
501 	struct rt_entry *rt;
502 	struct netinfo *n;
503 	struct interface *ifp;
504 	time_t t;
505 	int newsize;
506 	struct afswitch *afp;
507 
508 	if (trace) {
509 		if (msg->rip_cmd < RIPCMD_MAX)
510 			printf("%s from %x\n", ripcmds[msg->rip_cmd],
511 			    ((struct sockaddr_in *)from)->sin_addr);
512 		else
513 			printf("%x from %x\n", msg->rip_cmd,
514 			    ((struct sockaddr_in *)from)->sin_addr);
515 	}
516 	if (from->sa_family >= AF_MAX)
517 		return;
518 	afp = &afswitch[from->sa_family];
519 	switch (msg->rip_cmd) {
520 
521 	case RIPCMD_REQUEST:
522 		newsize = 0;
523 		size -= 4 * sizeof (char);
524 		n = msg->rip_nets;
525 		while (size > 0) {
526 			if (size < sizeof (struct netinfo))
527 				break;
528 			size -= sizeof (struct netinfo);
529 
530 			/*
531 			 * A single entry with sa_family == AF_UNSPEC and
532 			 * metric ``infinity'' means ``all routes''.
533 			 */
534 			if (n->rip_dst.sa_family == AF_UNSPEC &&
535 			    n->rip_metric == HOPCNT_INFINITY && size == 0) {
536 				supply(from, 0);
537 				return;
538 			}
539 			rt = rtlookup(&n->rip_dst);
540 			n->rip_metric = rt == 0 ? HOPCNT_INFINITY :
541 				min(rt->rt_metric+1, HOPCNT_INFINITY);
542 			n++, newsize += sizeof (struct netinfo);
543 		}
544 		if (newsize > 0) {
545 			msg->rip_cmd = RIPCMD_RESPONSE;
546 			newsize += sizeof (int);
547 			(*afp->af_output)(s, from, newsize);
548 		}
549 		return;
550 
551 	case RIPCMD_TRACEON:
552 		if ((*afp->af_portcheck)(from) == 0)
553 			return;
554 		if (trace)
555 			return;
556 		packet[size] = '\0';
557 		ftrace = fopen(msg->rip_tracefile, "a");
558 		if (ftrace == NULL)
559 			return;
560 		(void) dup2(fileno(ftrace), 1);
561 		(void) dup2(fileno(ftrace), 2);
562 		trace = 1;
563 		t = time(0);
564 		printf("*** Tracing turned on at %.24s ***\n", ctime(&t));
565 		return;
566 
567 	case RIPCMD_TRACEOFF:
568 		/* verify message came from a priviledged port */
569 		if ((*afp->af_portcheck)(from) == 0)
570 			return;
571 		if (!trace)
572 			return;
573 		t = time(0);
574 		printf("*** Tracing turned off at %.24s ***\n", ctime(&t));
575 		fflush(stdout), fflush(stderr);
576 		if (ftrace)
577 			fclose(ftrace);
578 		(void) close(1), (void) close(2);
579 		trace = 0;
580 		return;
581 
582 	case RIPCMD_RESPONSE:
583 		/* verify message came from a router */
584 		if ((*afp->af_portmatch)(from) == 0)
585 			return;
586 		(*afp->af_canon)(from);
587 		/* are we talking to ourselves? */
588 		ifp = if_ifwithaddr(from);
589 		if (ifp) {
590 			rt = rtfind(from);
591 			if (rt == 0)
592 				addrouteforif(ifp);
593 			else
594 				rt->rt_timer = 0;
595 			return;
596 		}
597 		size -= 4 * sizeof (char);
598 		n = msg->rip_nets;
599 		for (; size > 0; size -= sizeof (struct netinfo), n++) {
600 			if (size < sizeof (struct netinfo))
601 				break;
602 			if (n->rip_metric >= HOPCNT_INFINITY)
603 				continue;
604 			tprintf("dst %x hc %d...",
605 			    ((struct sockaddr_in *)&n->rip_dst)->sin_addr,
606 			    n->rip_metric);
607 			rt = rtlookup(&n->rip_dst);
608 			if (rt == 0) {
609 				rtadd(&n->rip_dst, from, n->rip_metric, 0);
610 				continue;
611 			}
612 			tprintf("ours: gate %x hc %d timer %d\n",
613 			  ((struct sockaddr_in *)&rt->rt_router)->sin_addr,
614 			  rt->rt_metric, rt->rt_timer);
615 
616 			/*
617 			 * Update if from gateway, shorter, or getting
618 			 * stale and equivalent.
619 			 */
620 			if (equal(from, &rt->rt_router) ||
621 			    n->rip_metric < rt->rt_metric ||
622 			    (rt->rt_timer > (EXPIRE_TIME/2) &&
623 			    rt->rt_metric == n->rip_metric)) {
624 				rtchange(rt, from, n->rip_metric);
625 				rt->rt_timer = 0;
626 			}
627 		}
628 		return;
629 	}
630 	tprintf("bad packet, cmd=%x\n", msg->rip_cmd);
631 }
632 
633 /*
634  * Lookup dst in the tables for an exact match.
635  */
636 struct rt_entry *
637 rtlookup(dst)
638 	struct sockaddr *dst;
639 {
640 	register struct rt_entry *rt;
641 	register struct rthash *rh;
642 	register int hash;
643 	struct afhash h;
644 	int doinghost = 1;
645 
646 	if (dst->sa_family >= AF_MAX)
647 		return (0);
648 	(*afswitch[dst->sa_family].af_hash)(dst, &h);
649 	hash = h.afh_hosthash;
650 	rh = &hosthash[hash % ROUTEHASHSIZ];
651 again:
652 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
653 		if (rt->rt_hash != hash)
654 			continue;
655 		if (equal(&rt->rt_dst, dst))
656 			return (rt);
657 	}
658 	if (doinghost) {
659 		doinghost = 0;
660 		hash = h.afh_nethash;
661 		rh = &nethash[hash % ROUTEHASHSIZ];
662 		goto again;
663 	}
664 	return (0);
665 }
666 
667 /*
668  * Find a route to dst as the kernel would.
669  */
670 struct rt_entry *
671 rtfind(dst)
672 	struct sockaddr *dst;
673 {
674 	register struct rt_entry *rt;
675 	register struct rthash *rh;
676 	register int hash;
677 	struct afhash h;
678 	int af = dst->sa_family;
679 	int doinghost = 1, (*match)();
680 
681 	if (af >= AF_MAX)
682 		return (0);
683 	(*afswitch[af].af_hash)(dst, &h);
684 	hash = h.afh_hosthash;
685 	rh = &hosthash[hash % ROUTEHASHSIZ];
686 
687 again:
688 	for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
689 		if (rt->rt_hash != hash)
690 			continue;
691 		if (doinghost) {
692 			if (equal(&rt->rt_dst, dst))
693 				return (rt);
694 		} else {
695 			if (rt->rt_dst.sa_family == af &&
696 			    (*match)(&rt->rt_dst, dst))
697 				return (rt);
698 		}
699 	}
700 	if (doinghost) {
701 		doinghost = 0;
702 		hash = h.afh_nethash;
703 		rh = &nethash[hash % ROUTEHASHSIZ];
704 		match = afswitch[af].af_netmatch;
705 		goto again;
706 	}
707 	return (0);
708 }
709 
710 rtadd(dst, gate, metric, state)
711 	struct sockaddr *dst, *gate;
712 	int metric, state;
713 {
714 	struct afhash h;
715 	register struct rt_entry *rt;
716 	struct rthash *rh;
717 	int af = dst->sa_family, flags, hash;
718 
719 	if (af >= AF_MAX)
720 		return;
721 	(*afswitch[af].af_hash)(dst, &h);
722 	flags = (*afswitch[af].af_checkhost)(dst) ? RTF_HOST : 0;
723 	if (flags & RTF_HOST) {
724 		hash = h.afh_hosthash;
725 		rh = &hosthash[hash % ROUTEHASHSIZ];
726 	} else {
727 		hash = h.afh_nethash;
728 		rh = &nethash[hash % ROUTEHASHSIZ];
729 	}
730 	rt = (struct rt_entry *)malloc(sizeof (*rt));
731 	if (rt == 0)
732 		return;
733 	rt->rt_hash = hash;
734 	rt->rt_dst = *dst;
735 	rt->rt_router = *gate;
736 	rt->rt_metric = metric;
737 	rt->rt_timer = 0;
738 	rt->rt_flags = RTF_UP | flags;
739 	rt->rt_state = state | RTS_CHANGED;
740 	rt->rt_ifp = if_ifwithnet(&rt->rt_router);
741 	if (metric)
742 		rt->rt_flags |= RTF_GATEWAY;
743 	insque(rt, rh);
744 	log("add", rt);
745 	if (install && ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
746 		tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
747 }
748 
749 rtchange(rt, gate, metric)
750 	struct rt_entry *rt;
751 	struct sockaddr *gate;
752 	short metric;
753 {
754 	int doioctl = 0, metricchanged = 0;
755 	struct rtentry oldroute;
756 
757 	if (!equal(&rt->rt_router, gate))
758 		doioctl++;
759 	if (metric != rt->rt_metric) {
760 		metricchanged++;
761 		rt->rt_metric = metric;
762 	}
763 	if (doioctl || metricchanged) {
764 		log("change", rt);
765 		rt->rt_state |= RTS_CHANGED;
766 	}
767 	if (doioctl) {
768 		oldroute = rt->rt_rt;
769 		rt->rt_router = *gate;
770 		if (install) {
771 			if (ioctl(s, SIOCADDRT, (char *)&rt->rt_rt) < 0)
772 				tprintf("SIOCADDRT: %s\n", sys_errlist[errno]);
773 			if (ioctl(s, SIOCDELRT, (char *)&oldroute) < 0)
774 				tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
775 		}
776 	}
777 }
778 
779 rtdelete(rt)
780 	struct rt_entry *rt;
781 {
782 	log("delete", rt);
783 	if (install && ioctl(s, SIOCDELRT, (char *)&rt->rt_rt))
784 		tprintf("SIOCDELRT: %s\n", sys_errlist[errno]);
785 	remque(rt);
786 	free((char *)rt);
787 }
788 
789 log(operation, rt)
790 	char *operation;
791 	struct rt_entry *rt;
792 {
793 	struct sockaddr_in *dst, *gate;
794 	static struct bits {
795 		int	t_bits;
796 		char	*t_name;
797 	} flagbits[] = {
798 		{ RTF_UP,	"UP" },
799 		{ RTF_GATEWAY,	"GATEWAY" },
800 		{ RTF_HOST,	"HOST" },
801 		{ 0 }
802 	}, statebits[] = {
803 		{ RTS_PASSIVE,	"PASSIVE" },
804 		{ RTS_REMOTE,	"REMOTE" },
805 		{ RTS_INTERFACE,"INTERFACE" },
806 		{ RTS_CHANGED,	"CHANGED" },
807 		{ 0 }
808 	};
809 	register struct bits *p;
810 	register int first;
811 	char *cp;
812 
813 	if (trace == 0)
814 		return;
815 	printf("%s ", operation);
816 	dst = (struct sockaddr_in *)&rt->rt_dst;
817 	gate = (struct sockaddr_in *)&rt->rt_router;
818 	printf("dst %x, router %x, metric %d, flags", dst->sin_addr,
819 		gate->sin_addr, rt->rt_metric);
820 	cp = " %s";
821 	for (first = 1, p = flagbits; p->t_bits > 0; p++) {
822 		if ((rt->rt_flags & p->t_bits) == 0)
823 			continue;
824 		printf(cp, p->t_name);
825 		if (first) {
826 			cp = "|%s";
827 			first = 0;
828 		}
829 	}
830 	printf(" state");
831 	cp = " %s";
832 	for (first = 1, p = statebits; p->t_bits > 0; p++) {
833 		if ((rt->rt_state & p->t_bits) == 0)
834 			continue;
835 		printf(cp, p->t_name);
836 		if (first) {
837 			cp = "|%s";
838 			first = 0;
839 		}
840 	}
841 	putchar('\n');
842 }
843 
844 struct interface *
845 if_ifwithaddr(addr)
846 	struct sockaddr *addr;
847 {
848 	register struct interface *ifp;
849 
850 #define	same(a1, a2) \
851 	(bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 14) == 0)
852 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
853 		if (ifp->int_flags & IFF_REMOTE)
854 			continue;
855 		if (ifp->int_addr.sa_family != addr->sa_family)
856 			continue;
857 		if (same(&ifp->int_addr, addr))
858 			break;
859 		if ((ifp->int_flags & IFF_BROADCAST) &&
860 		    same(&ifp->int_broadaddr, addr))
861 			break;
862 	}
863 	return (ifp);
864 #undef same
865 }
866 
867 struct interface *
868 if_ifwithnet(addr)
869 	register struct sockaddr *addr;
870 {
871 	register struct interface *ifp;
872 	register int af = addr->sa_family;
873 	register int (*netmatch)();
874 
875 	if (af >= AF_MAX)
876 		return (0);
877 	netmatch = afswitch[af].af_netmatch;
878 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
879 		if (ifp->int_flags & IFF_REMOTE)
880 			continue;
881 		if (af != ifp->int_addr.sa_family)
882 			continue;
883 		if ((*netmatch)(addr, &ifp->int_addr))
884 			break;
885 	}
886 	return (ifp);
887 }
888 
889 struct in_addr
890 if_makeaddr(net, host)
891 	int net, host;
892 {
893 	u_long addr;
894 
895 	if (net < 128)
896 		addr = (net << 24) | host;
897 	else if (net < 65536)
898 		addr = (net << 16) | host;
899 	else
900 		addr = (net << 8) | host;
901 #if vax || pdp11
902 	addr = htonl(addr);
903 #endif
904 	return (*(struct in_addr *)&addr);
905 }
906 
907 /*
908  * Return the network number from an internet
909  * address; handles class a/b/c network #'s.
910  */
911 in_netof(in)
912 	struct in_addr in;
913 {
914 #if vax || pdp11
915 	register u_long net;
916 
917 	if ((in.s_addr&IN_CLASSA) == 0)
918 		return (in.s_addr & IN_CLASSA_NET);
919 	if ((in.s_addr&IN_CLASSB) == 0)
920 		return ((int)htons((u_short)(in.s_addr & IN_CLASSB_NET)));
921 	net = htonl((u_long)(in.s_addr & IN_CLASSC_NET));
922 	net >>= 8;
923 	return ((int)net);
924 #else
925 	return (IN_NETOF(in));
926 #endif
927 }
928 
929 /*
930  * Return the local network address portion of an
931  * internet address; handles class a/b/c network
932  * number formats.
933  */
934 in_lnaof(in)
935 	struct in_addr in;
936 {
937 #if vax || pdp11
938 #define	IN_LNAOF(in) \
939 	(((in).s_addr&IN_CLASSA) == 0 ? (in).s_addr&IN_CLASSA_LNA : \
940 		((in).s_addr&IN_CLASSB) == 0 ? (in).s_addr&IN_CLASSB_LNA : \
941 			(in).s_addr&IN_CLASSC_LNA)
942 	return ((int)htonl((u_long)IN_LNAOF(in)));
943 #else
944 	return (IN_LNAOF(in));
945 #endif
946 }
947