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