xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/in.rdisc/in.rdisc.c (revision 4f364e7c95ee7fd9d5bbeddc1940e92405bb0e72)
1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1987 Regents of the University of California.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that the above copyright notice and this paragraph are
12  * duplicated in all such forms and that any documentation,
13  * advertising materials, and other materials related to such
14  * distribution and use acknowledge that the software was developed
15  * by the University of California, Berkeley. The name of the
16  * University may not be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
21  */
22 
23 #pragma ident	"%Z%%M%	%I%	%E% SMI"
24 
25 #include <stdio.h>
26 #include <errno.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/file.h>
35 
36 #include <sys/ioctl.h>
37 #include <net/if.h>
38 
39 #include <netinet/in_systm.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <netinet/ip_icmp.h>
43 #include <netdb.h>
44 #include <arpa/inet.h>
45 
46 #include <fcntl.h>
47 #include <strings.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 #include <assert.h>
51 
52 #ifdef lint
53 #define	ALIGN(ptr)	(ptr ? 0 : 0)
54 #else
55 #define	ALIGN(ptr)	(ptr)
56 #endif
57 
58 #ifdef SYSV
59 #define	signal(s, f)	sigset(s, (void (*)(int))f)
60 #define	random()	rand()
61 #endif
62 
63 #define	ALL_HOSTS_ADDRESS		"224.0.0.1"
64 #define	ALL_ROUTERS_ADDRESS		"224.0.0.2"
65 
66 #define	MAXIFS 256
67 
68 /* For router advertisement */
69 struct icmp_ra {
70 	uchar_t		icmp_type;	/* type of message, see below */
71 	uchar_t		icmp_code;	/* type sub code */
72 	ushort_t	icmp_cksum;	/* ones complement cksum of struct */
73 	uchar_t		icmp_num_addrs;
74 	uchar_t		icmp_wpa;	/* Words per address */
75 	short		icmp_lifetime;
76 };
77 
78 struct icmp_ra_addr {
79 	ulong_t	addr;
80 	ulong_t preference;
81 };
82 
83 /* Router constants */
84 #define	MAX_INITIAL_ADVERT_INTERVAL	16
85 #define	MAX_INITIAL_ADVERTISEMENTS 	3
86 #define	MAX_RESPONSE_DELAY		2	/* Not used */
87 
88 /* Host constants */
89 #define	MAX_SOLICITATIONS		3
90 #define	SOLICITATION_INTERVAL		3
91 #define	MAX_SOLICITATION_DELAY		1	/* Not used */
92 
93 #define	IGNORE_PREFERENCE	0x80000000	/* Maximum negative */
94 
95 #define	MAX_ADV_INT 600
96 
97 
98 /*
99  * A doubly linked list of all physical interfaces that each contain a
100  * doubly linked list of logical interfaces aka IP addresses.
101  */
102 struct phyint {
103 	char		pi_name[IFNAMSIZ];	/* Used to identify it */
104 	int		pi_state;		/* See below */
105 	struct logint	*pi_logical_first;
106 	struct logint	*pi_logical_last;
107 	struct phyint	*pi_next;
108 	struct phyint	*pi_prev;
109 };
110 
111 struct logint {
112 	char		li_name[IFNAMSIZ];	/* Used to identify it */
113 	int		li_state;		/* See below */
114 	struct in_addr	li_address;	/* Used to identify the interface */
115 	struct in_addr	li_localaddr;	/* Actual address of the interface */
116 	int		li_preference;
117 	int		li_index;	/* interface index (SIOCGLIFINDEX) */
118 	uint64_t	li_flags;
119 	struct in_addr	li_bcastaddr;
120 	struct in_addr	li_remoteaddr;
121 	struct in_addr	li_netmask;
122 	struct logint	*li_next;	/* Next logical for this physical */
123 	struct logint	*li_prev;	/* Prev logical for this physical */
124 	struct phyint	*li_physical;	/* Back pointer */
125 };
126 
127 struct phyint *phyint;
128 int num_usable_interfaces;		/* Num used for sending/receiving */
129 
130 /*
131  * State bits
132  */
133 #define	ST_MARKED	0x01		/* To determine removed interfaces */
134 #define	ST_JOINED	0x02		/* Joined multicast group */
135 #define	ST_DELETED	0x04		/* Interface should be ignored */
136 
137 /* Function prototypes */
138 static void	solicitor(struct sockaddr_in *sin);
139 static void	advertise(struct sockaddr_in *sin);
140 
141 static void	age_table(int time);
142 static void	flush_unreachable_routers(void);
143 static void	record_router(struct in_addr router, long preference, int ttl);
144 
145 static void	add_route(struct in_addr addr);
146 static void	del_route(struct in_addr addr);
147 static void	rtioctl(struct in_addr addr, int op);
148 
149 static int	support_multicast(void);
150 static int	sendbcast(int s, char *packet, int packetlen);
151 static int	sendbcastif(int s, char *packet, int packetlen,
152 		    struct logint *li);
153 static int	sendmcast(int s, char *packet, int packetlen,
154 		    struct sockaddr_in *sin);
155 static int	sendmcastif(int s, char *packet, int packetlen,
156 		    struct sockaddr_in *sin, struct logint *li);
157 
158 static int	ismulticast(struct sockaddr_in *sin);
159 static int	isbroadcast(struct sockaddr_in *sin);
160 int		in_cksum(ushort_t *addr, int len);
161 static struct logint *find_directly_connected_logint(struct in_addr in,
162     struct phyint *pi);
163 static void	force_preference(int preference);
164 
165 static void	timer(void);
166 static void	finish(void);
167 static void	report(void);
168 static void	report_interfaces(void);
169 static void	report_routes(void);
170 static void	reinitifs(void);
171 
172 static struct phyint *find_phyint(char *name);
173 static struct phyint *add_phyint(char *name);
174 static void	free_phyint(struct phyint *pi);
175 static struct logint *find_logint(struct phyint *pi, char *name);
176 static struct logint *add_logint(struct phyint *pi, char *name);
177 static void	free_logint(struct logint *li);
178 
179 static void	deleted_phyint(struct phyint *pi, int s,
180 		    struct sockaddr_in *joinaddr);
181 static void	added_logint(struct logint *li, int s,
182 		    struct sockaddr_in *joinaddr);
183 static void	deleted_logint(struct logint *li, struct logint *newli, int s,
184 		    struct sockaddr_in *joinaddr);
185 
186 static int	initifs(int s, struct sockaddr_in *joinaddr, int preference);
187 static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
188 		    struct ifreq *ifr, struct logint *li);
189 
190 static void	pr_pack(char *buf, int cc, struct sockaddr_in *from);
191 char		*pr_name(struct in_addr addr);
192 char		*pr_type(int t);
193 
194 static void	initlog(void);
195 void		logerr(), logtrace(), logdebug(), logperror();
196 
197 /* Local variables */
198 
199 #define	MAXPACKET	4096	/* max packet size */
200 uchar_t	packet[MAXPACKET];
201 
202 char usage[] =
203 "Usage:	rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
204 "	rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
205 "		[send_address] [receive_address]\n";
206 
207 
208 int s;				/* Socket file descriptor */
209 struct sockaddr_in whereto;	/* Address to send to */
210 struct sockaddr_in g_joinaddr;	/* Address to receive on */
211 char    *sendaddress, *recvaddress;	/* For logging purposes only */
212 
213 /* Common variables */
214 int verbose = 0;
215 int debug = 0;
216 int trace = 0;
217 int start_solicit = 0;	/* -s parameter set */
218 int solicit = 0;	/* Are we currently sending solicitations? */
219 int responder;
220 int ntransmitted = 0;
221 int nreceived = 0;
222 int forever = 0;	/* Never give up on host. If 0 defer fork until */
223 			/* first response.				*/
224 
225 /* Router variables */
226 int max_adv_int = MAX_ADV_INT;
227 int min_adv_int;
228 int lifetime;
229 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
230 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
231 ulong_t g_preference = 0;	/* Setable with -p option */
232 
233 /* Host variables */
234 int max_solicitations = MAX_SOLICITATIONS;
235 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
236 int best_preference = 1; 	/* Set to record only the router(s) with the */
237 				/* best preference in the kernel. Not set   */
238 				/* puts all routes in the kernel.	    */
239 
240 
241 static void
242 prusage()
243 {
244 	(void) fprintf(stderr, usage);
245 	exit(1);
246 }
247 
248 static int	sock = -1;
249 
250 static void
251 do_fork()
252 {
253 	int t;
254 
255 	if (trace)
256 		return;
257 
258 	if (fork())
259 		exit(0);
260 	for (t = 0; t < 20; t++)
261 		if (t != s)
262 			(void) close(t);
263 	sock = -1;
264 	(void) open("/", 0);
265 	(void) dup2(0, 1);
266 	(void) dup2(0, 2);
267 #ifndef SYSV
268 	t = open("/dev/tty", 2);
269 	if (t >= 0) {
270 		(void) ioctl(t, TIOCNOTTY, (char *)0);
271 		(void) close(t);
272 	}
273 #else
274 	(void) setpgrp();
275 #endif
276 	initlog();
277 }
278 
279 /*
280  *			M A I N
281  */
282 int
283 main(int argc, char *argv[])
284 {
285 #ifndef SYSV
286 	struct sigvec sv;
287 #endif
288 	struct sockaddr_in from;
289 	char **av = argv;
290 	struct sockaddr_in *to = &whereto;
291 	ulong_t val;
292 
293 	min_adv_int = (max_adv_int * 3 / 4);
294 	lifetime = (3*max_adv_int);
295 
296 	argc--, av++;
297 	while (argc > 0 && *av[0] == '-') {
298 	    while (*++av[0])
299 		switch (*av[0]) {
300 		case 'd':
301 			debug = 1;
302 			break;
303 		case 't':
304 			trace = 1;
305 			break;
306 		case 'v':
307 			verbose++;
308 			break;
309 		case 's':
310 			start_solicit = solicit = 1;
311 			break;
312 		case 'r':
313 			responder = 1;
314 			break;
315 		case 'a':
316 			best_preference = 0;
317 			break;
318 		case 'b':
319 			best_preference = 1;
320 			break;
321 		case 'f':
322 			forever = 1;
323 			break;
324 		case 'T':
325 			argc--, av++;
326 			if (argc != 0) {
327 				val = strtol(av[0], (char **)NULL, 0);
328 				if (val < 4 || val > 1800) {
329 					(void) fprintf(stderr,
330 					    "Bad Max Advertisement Interval\n");
331 					exit(1);
332 				}
333 				max_adv_int = val;
334 				min_adv_int = (max_adv_int * 3 / 4);
335 				lifetime = (3*max_adv_int);
336 			} else {
337 				prusage();
338 				/* NOTREACHED */
339 			}
340 			goto next;
341 		case 'p':
342 			argc--, av++;
343 			if (argc != 0) {
344 				val = strtoul(av[0], (char **)NULL, 0);
345 				g_preference = val;
346 			} else {
347 				prusage();
348 				/* NOTREACHED */
349 			}
350 			goto next;
351 		default:
352 			prusage();
353 			/* NOTREACHED */
354 		}
355 	next:
356 		argc--, av++;
357 	}
358 	if (argc < 1)  {
359 		if (support_multicast()) {
360 			if (responder)
361 				sendaddress = ALL_HOSTS_ADDRESS;
362 			else
363 				sendaddress = ALL_ROUTERS_ADDRESS;
364 		} else
365 			sendaddress = "255.255.255.255";
366 	} else {
367 		sendaddress = av[0];
368 		argc--;
369 	}
370 	if (argc < 1) {
371 		if (support_multicast()) {
372 			if (responder)
373 				recvaddress = ALL_ROUTERS_ADDRESS;
374 			else
375 				recvaddress = ALL_HOSTS_ADDRESS;
376 		} else
377 			recvaddress = "255.255.255.255";
378 	} else {
379 		recvaddress = av[0];
380 		argc--;
381 	}
382 	if (argc != 0) {
383 		(void) fprintf(stderr, "Extra paramaters\n");
384 		prusage();
385 		/* NOTREACHED */
386 	}
387 
388 	if (solicit && responder) {
389 		prusage();
390 		/* NOTREACHED */
391 	}
392 
393 	if (!(solicit && !forever)) {
394 		do_fork();
395 	}
396 
397 	bzero((char *)&whereto, sizeof (struct sockaddr_in));
398 	to->sin_family = AF_INET;
399 	to->sin_addr.s_addr = inet_addr(sendaddress);
400 	if (to->sin_addr.s_addr == (unsigned long)-1) {
401 		logerr("in.rdisc: bad address %s\n", sendaddress);
402 		exit(1);
403 	}
404 
405 	bzero((char *)&g_joinaddr, sizeof (struct sockaddr_in));
406 	g_joinaddr.sin_family = AF_INET;
407 	g_joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
408 	if (g_joinaddr.sin_addr.s_addr == (unsigned long)-1) {
409 		logerr("in.rdisc: bad address %s\n", recvaddress);
410 		exit(1);
411 	}
412 
413 	if (responder) {
414 #ifdef SYSV
415 		srand((int)gethostid());
416 #else
417 		srandom((int)gethostid());
418 #endif
419 	}
420 
421 	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
422 		logperror("socket");
423 		exit(5);
424 	}
425 
426 #ifdef SYSV
427 	setvbuf(stdout, NULL, _IOLBF, 0);
428 #else
429 	setlinebuf(stdout);
430 #endif
431 
432 	(void) signal(SIGINT, finish);
433 	(void) signal(SIGTERM, finish);
434 	(void) signal(SIGHUP, reinitifs);
435 	(void) signal(SIGUSR1, report);
436 
437 	if (initifs(s, &g_joinaddr, g_preference) < 0) {
438 		logerr("Failed initializing interfaces\n");
439 		exit(2);
440 	}
441 
442 	/*
443 	 * If there are no usable interfaces and we are soliciting
444 	 * waiting for to return an exit code (i.e. forever isn't set)
445 	 * give up immediately.
446 	 */
447 	if (num_usable_interfaces == 0 && solicit && !forever) {
448 		logerr("in.rdisc: No interfaces up\n");
449 		exit(5);
450 	}
451 
452 #ifdef SYSV
453 	(void) signal(SIGALRM, timer);
454 #else
455 	/*
456 	 * Make sure that this signal actually interrupts (rather than
457 	 * restarts) the recvfrom call below.
458 	 */
459 	sv.sv_handler = timer;
460 	sv.sv_mask = 0;
461 	sv.sv_flags = SV_INTERRUPT;
462 	(void) sigvec(SIGALRM, &sv, (struct sigvec *)NULL);
463 #endif
464 	timer();	/* start things going */
465 
466 	for (;;) {
467 		int len = sizeof (packet);
468 		socklen_t fromlen = (socklen_t)sizeof (from);
469 		int cc;
470 		sigset_t newmask, oldmask;
471 
472 		if ((cc = recvfrom(s, (char *)packet, len, 0,
473 		    (struct sockaddr *)&from,
474 		    &fromlen)) < 0) {
475 			if (errno == EINTR)
476 				continue;
477 			logperror("recvfrom");
478 			continue;
479 		}
480 		/* Block all signals while processing */
481 		(void) sigfillset(&newmask);
482 		(void) sigprocmask(SIG_SETMASK, &newmask, &oldmask);
483 		pr_pack((char *)packet, cc, &from);
484 		(void) sigprocmask(SIG_SETMASK, &oldmask, NULL);
485 	}
486 	/* NOTREACHED */
487 }
488 
489 static void
490 report(void)
491 {
492 	report_interfaces();
493 	report_routes();
494 }
495 
496 #define	TIMER_INTERVAL	6
497 #define	GETIFCONF_TIMER	30
498 
499 static int left_until_advertise;
500 
501 /* Called every TIMER_INTERVAL */
502 static void
503 timer(void)
504 {
505 	static int time;
506 	static int left_until_getifconf;
507 	static int left_until_solicit;
508 
509 	time += TIMER_INTERVAL;
510 
511 	left_until_getifconf -= TIMER_INTERVAL;
512 	left_until_advertise -= TIMER_INTERVAL;
513 	left_until_solicit -= TIMER_INTERVAL;
514 
515 	if (left_until_getifconf < 0) {
516 		(void) initifs(s, &g_joinaddr, g_preference);
517 		left_until_getifconf = GETIFCONF_TIMER;
518 	}
519 	if (responder && left_until_advertise <= 0) {
520 		ntransmitted++;
521 		advertise(&whereto);
522 		if (ntransmitted < initial_advertisements)
523 			left_until_advertise = initial_advert_interval;
524 		else
525 			left_until_advertise = min_adv_int +
526 				((max_adv_int - min_adv_int) *
527 				(random() % 1000)/1000);
528 	} else if (solicit && left_until_solicit <= 0) {
529 		if (ntransmitted < max_solicitations) {
530 			ntransmitted++;
531 			solicitor(&whereto);
532 			left_until_solicit = solicitation_interval;
533 		} else {
534 			solicit = 0;
535 			if (!forever && nreceived == 0)
536 				exit(5);
537 		}
538 	}
539 	age_table(TIMER_INTERVAL);
540 	(void) alarm(TIMER_INTERVAL);
541 }
542 
543 /*
544  *			S O L I C I T O R
545  *
546  * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
547  * The IP packet will be added on by the kernel.
548  */
549 static void
550 solicitor(struct sockaddr_in *sin)
551 {
552 	static uchar_t outpack[MAXPACKET];
553 	register struct icmp *icp = (struct icmp *)ALIGN(outpack);
554 	int packetlen, i;
555 
556 	if (verbose) {
557 		logtrace("Sending solicitation to %s\n",
558 			pr_name(sin->sin_addr));
559 	}
560 	icp->icmp_type = ICMP_ROUTERSOLICIT;
561 	icp->icmp_code = 0;
562 	icp->icmp_cksum = 0;
563 	icp->icmp_void = 0; /* Reserved */
564 	packetlen = 8;
565 
566 	/* Compute ICMP checksum here */
567 	icp->icmp_cksum = in_cksum((ushort_t *)icp, packetlen);
568 
569 	if (isbroadcast(sin))
570 		i = sendbcast(s, (char *)outpack, packetlen);
571 	else if (ismulticast(sin))
572 		i = sendmcast(s, (char *)outpack, packetlen, sin);
573 	else {
574 		struct logint *li;
575 
576 		li = find_directly_connected_logint(sin->sin_addr, NULL);
577 		if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
578 			if (verbose) {
579 				logtrace("Suppressing sending %s on %s "
580 				    "(no route exchange on interface)\n",
581 				    pr_type((int)icp->icmp_type), li->li_name);
582 			}
583 			return;
584 		} else {
585 			i = sendto(s, (char *)outpack, packetlen, 0,
586 			    (struct sockaddr *)sin, sizeof (struct sockaddr));
587 		}
588 	}
589 
590 	if (i < 0 || i != packetlen)  {
591 		if (i < 0) {
592 		    logperror("sendto");
593 		}
594 		logerr("wrote %s %d chars, ret=%d\n",
595 			sendaddress, packetlen, i);
596 	}
597 }
598 
599 /*
600  *			A D V E R T I S E
601  *
602  * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
603  * The IP packet will be added on by the kernel.
604  */
605 static void
606 advertise(struct sockaddr_in *sin)
607 {
608 	struct phyint *pi;
609 	struct logint *li, *li_tmp;
610 	static uchar_t outpack[MAXPACKET];
611 	register struct icmp_ra *rap = (struct icmp_ra *)ALIGN(outpack);
612 	struct icmp_ra_addr *ap;
613 	int packetlen, cc;
614 
615 	if (verbose) {
616 		logtrace("Sending advertisement to %s\n",
617 			pr_name(sin->sin_addr));
618 	}
619 
620 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
621 		rap->icmp_type = ICMP_ROUTERADVERT;
622 		rap->icmp_code = 0;
623 		rap->icmp_cksum = 0;
624 		rap->icmp_num_addrs = 0;
625 		rap->icmp_wpa = 2;
626 		rap->icmp_lifetime = htons(lifetime);
627 		packetlen = ICMP_MINLEN;
628 
629 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
630 			if (li->li_state & ST_DELETED)
631 				continue;
632 
633 			/*
634 			 * XXX Just truncate the list of addresses.
635 			 * Should probably send multiple packets.
636 			 */
637 			if (packetlen + rap->icmp_wpa * 4 > sizeof (outpack)) {
638 				if (debug)
639 					logdebug("full packet: %d addresses\n",
640 						rap->icmp_num_addrs);
641 				break;
642 			}
643 			ap = (struct icmp_ra_addr *)ALIGN(outpack + packetlen);
644 			ap->addr = li->li_localaddr.s_addr;
645 			ap->preference = htonl(li->li_preference);
646 			packetlen += rap->icmp_wpa * 4;
647 			rap->icmp_num_addrs++;
648 		}
649 
650 		if (rap->icmp_num_addrs == 0)
651 			continue;
652 
653 		/* Compute ICMP checksum here */
654 		rap->icmp_cksum = in_cksum((ushort_t *)rap, packetlen);
655 
656 		if (isbroadcast(sin))
657 			cc = sendbcastif(s, (char *)outpack, packetlen,
658 			    pi->pi_logical_first);
659 		else if (ismulticast(sin))
660 			cc = sendmcastif(s, (char *)outpack, packetlen, sin,
661 			    pi->pi_logical_first);
662 		else {
663 			/*
664 			 * Verify that the physical interface matches the
665 			 * destination address.
666 			 */
667 			li_tmp = find_directly_connected_logint(sin->sin_addr,
668 			    pi);
669 			if (li_tmp == NULL)
670 				continue;
671 			if (li_tmp->li_flags & IFF_NORTEXCH) {
672 				if (verbose) {
673 					logtrace("Suppressing sending %s on %s "
674 					    "(no route exchange on "
675 					    "interface)\n",
676 					    pr_type((int)rap->icmp_type),
677 					    li_tmp->li_name);
678 				}
679 				continue;
680 			}
681 			if (debug) {
682 				logdebug("Unicast to %s ",
683 				    pr_name(sin->sin_addr));
684 				logdebug("on interface %s\n", pi->pi_name);
685 			}
686 			cc = sendto(s, (char *)outpack, packetlen, 0,
687 			    (struct sockaddr *)sin, sizeof (struct sockaddr));
688 		}
689 		if (cc < 0 || cc != packetlen)  {
690 			if (cc < 0) {
691 				logperror("sendto");
692 			} else {
693 				logerr("wrote %s %d chars, ret=%d\n",
694 					sendaddress, packetlen, cc);
695 			}
696 		}
697 	}
698 }
699 
700 /*
701  *			P R _ T Y P E
702  *
703  * Convert an ICMP "type" field to a printable string.
704  */
705 char *
706 pr_type(int t)
707 {
708 	static char *ttab[] = {
709 		"Echo Reply",
710 		"ICMP 1",
711 		"ICMP 2",
712 		"Dest Unreachable",
713 		"Source Quench",
714 		"Redirect",
715 		"ICMP 6",
716 		"ICMP 7",
717 		"Echo",
718 		"Router Advertise",
719 		"Router Solicitation",
720 		"Time Exceeded",
721 		"Parameter Problem",
722 		"Timestamp",
723 		"Timestamp Reply",
724 		"Info Request",
725 		"Info Reply",
726 		"Netmask Request",
727 		"Netmask Reply"
728 	};
729 
730 	if (t < 0 || t > 16)
731 		return ("OUT-OF-RANGE");
732 
733 	return (ttab[t]);
734 }
735 
736 /*
737  *			P R _ N A M E
738  *
739  * Return a string name for the given IP address.
740  */
741 char *
742 pr_name(struct in_addr addr)
743 {
744 	struct hostent *phe;
745 	static char buf[256];
746 
747 	phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
748 	if (phe == NULL)
749 		return (inet_ntoa(addr));
750 	(void) sprintf(buf, "%s (%s)", phe->h_name, inet_ntoa(addr));
751 	return (buf);
752 }
753 
754 /*
755  *			P R _ P A C K
756  *
757  * Print out the packet, if it came from us.  This logic is necessary
758  * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
759  * which arrive ('tis only fair).  This permits multiple copies of this
760  * program to be run without having intermingled output (or statistics!).
761  */
762 static void
763 pr_pack(char *buf, int cc, struct sockaddr_in *from)
764 {
765 	struct ip *ip;
766 	register struct icmp *icp;
767 	register int i;
768 	int hlen;
769 	struct logint *li;
770 
771 	ip = (struct ip *)ALIGN(buf);
772 	hlen = ip->ip_hl << 2;
773 	if (cc < hlen + ICMP_MINLEN) {
774 		if (verbose)
775 			logtrace("packet too short (%d bytes) from %s\n", cc,
776 				pr_name(from->sin_addr));
777 		return;
778 	}
779 
780 	cc -= hlen;
781 	icp = (struct icmp *)ALIGN(buf + hlen);
782 
783 	/*
784 	 * Let's check if IFF_NORTEXCH flag is set on the interface which
785 	 * recevied this packet.
786 	 * TODO: this code can be re-written using one socket per interface
787 	 * to determine which interface the packet is recevied.
788 	 */
789 	li = find_directly_connected_logint(ip->ip_src, NULL);
790 	if (li != NULL && (li->li_flags & IFF_NORTEXCH)) {
791 		if (verbose) {
792 			logtrace("Ignoring received %s on %s "
793 			    "(no route exchange on interface)",
794 			    pr_type((int)icp->icmp_type), li->li_name);
795 		}
796 		return;
797 	}
798 
799 	if (ip->ip_p == 0) {
800 		/*
801 		 * Assume that we are running on a pre-4.3BSD system
802 		 * such as SunOS before 4.0
803 		 */
804 		icp = (struct icmp *)ALIGN(buf);
805 	}
806 	switch (icp->icmp_type) {
807 	case ICMP_ROUTERADVERT: {
808 		struct icmp_ra *rap = (struct icmp_ra *)ALIGN(icp);
809 		struct icmp_ra_addr *ap;
810 
811 		if (responder)
812 			break;
813 
814 		/* TBD verify that the link is multicast or broadcast */
815 		/* XXX Find out the link it came in over? */
816 #ifdef notdef
817 		if (debug) {
818 			logdebug("ROUTER_ADVERTISEMENT: \n");
819 			pr_hex(buf+hlen, cc);
820 		}
821 #endif /* notdef */
822 		if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
823 			if (verbose)
824 				logtrace("ICMP %s from %s: Bad checksum\n",
825 					pr_type((int)rap->icmp_type),
826 					pr_name(from->sin_addr));
827 			return;
828 		}
829 		if (rap->icmp_code != 0) {
830 			if (verbose)
831 				logtrace("ICMP %s from %s: Code = %d\n",
832 					pr_type((int)rap->icmp_type),
833 					pr_name(from->sin_addr),
834 					rap->icmp_code);
835 			return;
836 		}
837 		if (rap->icmp_num_addrs < 1) {
838 			if (verbose)
839 				logtrace("ICMP %s from %s: No addresses\n",
840 					pr_type((int)rap->icmp_type),
841 					pr_name(from->sin_addr));
842 			return;
843 		}
844 		if (rap->icmp_wpa < 2) {
845 			if (verbose)
846 				logtrace("ICMP %s from %s: Words/addr = %d\n",
847 					pr_type((int)rap->icmp_type),
848 					pr_name(from->sin_addr),
849 					rap->icmp_wpa);
850 			return;
851 		}
852 		if ((unsigned)cc <
853 		    ICMP_MINLEN + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
854 			if (verbose)
855 				logtrace("ICMP %s from %s: Too short %d, %d\n",
856 					pr_type((int)rap->icmp_type),
857 					pr_name(from->sin_addr),
858 					cc,
859 					ICMP_MINLEN +
860 					rap->icmp_num_addrs *
861 					rap->icmp_wpa * 4);
862 			return;
863 		}
864 		rap->icmp_lifetime = ntohs(rap->icmp_lifetime);
865 		if ((rap->icmp_lifetime < 4 && rap->icmp_lifetime != 0) ||
866 		    rap->icmp_lifetime > 9000) {
867 			if (verbose)
868 			    logtrace("ICMP %s from %s: Invalid lifetime %d\n",
869 					pr_type((int)rap->icmp_type),
870 					pr_name(from->sin_addr),
871 					rap->icmp_lifetime);
872 			return;
873 		}
874 		if (verbose)
875 			logtrace("ICMP %s from %s, lifetime %d\n",
876 				pr_type((int)rap->icmp_type),
877 				pr_name(from->sin_addr),
878 				rap->icmp_lifetime);
879 
880 		/*
881 		 * Check that at least one router address is a neighbor
882 		 * on the arriving link.
883 		 */
884 		for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
885 			struct in_addr ina;
886 			ap = (struct icmp_ra_addr *)
887 				ALIGN(buf + hlen + ICMP_MINLEN +
888 					i * rap->icmp_wpa * 4);
889 			ap->preference = ntohl(ap->preference);
890 			ina.s_addr = ap->addr;
891 			if (verbose)
892 				logtrace("\taddress %s, preference 0x%x\n",
893 					pr_name(ina),
894 					ap->preference);
895 			if (!responder) {
896 				if (find_directly_connected_logint(ina, NULL) !=
897 				    NULL) {
898 					record_router(ina,
899 					    (long)ap->preference,
900 					    rap->icmp_lifetime);
901 				}
902 			}
903 		}
904 		nreceived++;
905 		if (!forever) {
906 			(void) alarm(0);
907 			do_fork();
908 			forever = 1;
909 			(void) alarm(TIMER_INTERVAL);
910 		}
911 		break;
912 	}
913 
914 	case ICMP_ROUTERSOLICIT: {
915 		struct sockaddr_in sin;
916 
917 		if (!responder)
918 			break;
919 
920 		/* TBD verify that the link is multicast or broadcast */
921 		/* XXX Find out the link it came in over? */
922 #ifdef notdef
923 		if (debug) {
924 			logdebug("ROUTER_SOLICITATION: \n");
925 			pr_hex(buf+hlen, cc);
926 		}
927 #endif /* notdef */
928 		if (in_cksum((ushort_t *)ALIGN(buf+hlen), cc)) {
929 			if (verbose)
930 				logtrace("ICMP %s from %s: Bad checksum\n",
931 					pr_type((int)icp->icmp_type),
932 					pr_name(from->sin_addr));
933 			return;
934 		}
935 		if (icp->icmp_code != 0) {
936 			if (verbose)
937 				logtrace("ICMP %s from %s: Code = %d\n",
938 					pr_type((int)icp->icmp_type),
939 					pr_name(from->sin_addr),
940 					icp->icmp_code);
941 			return;
942 		}
943 
944 		if (cc < ICMP_MINLEN) {
945 			if (verbose)
946 				logtrace("ICMP %s from %s: Too short %d, %d\n",
947 					pr_type((int)icp->icmp_type),
948 					pr_name(from->sin_addr),
949 					cc,
950 					ICMP_MINLEN);
951 			return;
952 		}
953 
954 		if (verbose)
955 			logtrace("ICMP %s from %s\n",
956 				pr_type((int)icp->icmp_type),
957 				pr_name(from->sin_addr));
958 
959 		if (!responder)
960 			break;
961 
962 		/*
963 		 * Check that ip_src is either a neighbor
964 		 * on the arriving link or 0.
965 		 */
966 		sin.sin_family = AF_INET;
967 		if (ip->ip_src.s_addr == 0) {
968 			/*
969 			 * If it was sent to the broadcast address we respond
970 			 * to the broadcast address.
971 			 */
972 			if (IN_CLASSD(ntohl(ip->ip_dst.s_addr))) {
973 				sin.sin_addr.s_addr =
974 				    htonl(INADDR_ALLHOSTS_GROUP);
975 			} else
976 				sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
977 			/* Restart the timer when we broadcast */
978 			left_until_advertise = min_adv_int +
979 				((max_adv_int - min_adv_int)
980 				 * (random() % 1000)/1000);
981 		} else {
982 			if (li == NULL) {
983 				if (verbose)
984 					logtrace("ICMP %s from %s: %s\n",
985 						pr_type((int)icp->icmp_type),
986 						pr_name(from->sin_addr),
987 					"source not directly connected");
988 				break;
989 			}
990 			sin.sin_addr.s_addr = ip->ip_src.s_addr;
991 		}
992 		nreceived++;
993 		ntransmitted++;
994 		advertise(&sin);
995 		break;
996 	}
997 	}
998 }
999 
1000 
1001 /*
1002  *			I N _ C K S U M
1003  *
1004  * Checksum routine for Internet Protocol family headers (C Version)
1005  *
1006  */
1007 int
1008 in_cksum(ushort_t *addr, int len)
1009 {
1010 	register int nleft = len;
1011 	register ushort_t *w = addr;
1012 	register ushort_t answer;
1013 	ushort_t odd_byte = 0;
1014 	register int sum = 0;
1015 
1016 	/*
1017 	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
1018 	 *  we add sequential 16 bit words to it, and at the end, fold
1019 	 *  back all the carry bits from the top 16 bits into the lower
1020 	 *  16 bits.
1021 	 */
1022 	while (nleft > 1)  {
1023 		sum += *w++;
1024 		nleft -= 2;
1025 	}
1026 
1027 	/* mop up an odd byte, if necessary */
1028 	if (nleft == 1) {
1029 		*(uchar_t *)(&odd_byte) = *(uchar_t *)w;
1030 		sum += odd_byte;
1031 	}
1032 
1033 	/*
1034 	 * add back carry outs from top 16 bits to low 16 bits
1035 	 */
1036 	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
1037 	sum += (sum >> 16);			/* add carry */
1038 	answer = ~sum;				/* truncate to 16 bits */
1039 	return (answer);
1040 }
1041 
1042 /*
1043  *			F I N I S H
1044  *
1045  * Print out statistics, and give up.
1046  * Heavily buffered stdio is used here, so that all the statistics
1047  * will be written with 1 sys-write call.  This is nice when more
1048  * than one copy of the program is running on a terminal;  it prevents
1049  * the statistics output from becoming intermingled.
1050  */
1051 static void
1052 finish(void)
1053 {
1054 	if (responder) {
1055 		/*
1056 		 * Send out a packet with a preference so that all
1057 		 * hosts will know that we are dead.
1058 		 */
1059 		logerr("terminated\n");
1060 		force_preference(IGNORE_PREFERENCE);
1061 		ntransmitted++;
1062 		advertise(&whereto);
1063 	}
1064 	if (verbose) {
1065 		logtrace("\n----%s rdisc Statistics----\n", sendaddress);
1066 		logtrace("%d packets transmitted, ", ntransmitted);
1067 		logtrace("%d packets received, ", nreceived);
1068 		logtrace("\n");
1069 	}
1070 	(void) fflush(stdout);
1071 	exit(0);
1072 }
1073 
1074 #include <ctype.h>
1075 
1076 #ifdef notdef
1077 int
1078 pr_hex(unsigned char *data, int len)
1079 {
1080 	FILE *out;
1081 
1082 	out = stdout;
1083 
1084 	while (len) {
1085 		register int i;
1086 		char charstring[17];
1087 
1088 		(void) strcpy(charstring, "                "); /* 16 spaces */
1089 		for (i = 0; i < 16; i++) {
1090 			/*
1091 			 * output the bytes one at a time,
1092 			 * not going past "len" bytes
1093 			 */
1094 			if (len) {
1095 				char ch = *data & 0x7f; /* strip parity */
1096 				if (!isprint((uchar_t)ch))
1097 					ch = ' '; /* ensure printable */
1098 				charstring[i] = ch;
1099 				(void) fprintf(out, "%02x ", *data++);
1100 				len--;
1101 			} else
1102 				(void) fprintf(out, "   ");
1103 			if (i == 7)
1104 				(void) fprintf(out, "   ");
1105 		}
1106 
1107 		(void) fprintf(out, "    *%s*\n", charstring);
1108 	}
1109 }
1110 #endif /* notdef */
1111 
1112 static int
1113 isbroadcast(struct sockaddr_in *sin)
1114 {
1115 	return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
1116 }
1117 
1118 static int
1119 ismulticast(struct sockaddr_in *sin)
1120 {
1121 	return (IN_CLASSD(ntohl(sin->sin_addr.s_addr)));
1122 }
1123 
1124 /* From libc/rpc/pmap_rmt.c */
1125 
1126 
1127 /* Only send once per physical interface */
1128 static int
1129 sendbcast(int s, char *packet, int packetlen)
1130 {
1131 	struct phyint *pi;
1132 	struct logint *li;
1133 	boolean_t bcast;
1134 	int cc;
1135 
1136 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1137 		bcast = B_FALSE;
1138 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1139 			if (li->li_state & ST_DELETED)
1140 				continue;
1141 
1142 			if (li->li_flags & IFF_BROADCAST) {
1143 				bcast = B_TRUE;
1144 				break;
1145 			}
1146 		}
1147 		if (!bcast)
1148 			continue;
1149 		cc = sendbcastif(s, packet, packetlen, li);
1150 		if (cc != packetlen) {
1151 			return (cc);
1152 		}
1153 	}
1154 	return (packetlen);
1155 }
1156 
1157 static int
1158 sendbcastif(int s, char *packet, int packetlen, struct logint *li)
1159 {
1160 	int cc;
1161 	struct sockaddr_in baddr;
1162 	struct icmp *icp = (struct icmp *)ALIGN(packet);
1163 
1164 	baddr.sin_family = AF_INET;
1165 
1166 	if ((li->li_flags & IFF_BROADCAST) == 0) {
1167 		if (verbose) {
1168 			logtrace("Suppressing sending %s on %s "
1169 			    "(interface is not broadcast capable)\n",
1170 			    pr_type((int)icp->icmp_type), li->li_name);
1171 		}
1172 		return (packetlen);
1173 	}
1174 	if (li->li_flags & IFF_NORTEXCH) {
1175 		if (verbose) {
1176 			logtrace("Suppressing sending %s on %s "
1177 			    "(no route exchange on interface)\n",
1178 			    pr_type((int)icp->icmp_type), li->li_name);
1179 		}
1180 		return (packetlen);
1181 	}
1182 
1183 	baddr.sin_addr = li->li_bcastaddr;
1184 	if (debug)
1185 		logdebug("Broadcast to %s\n",
1186 			pr_name(baddr.sin_addr));
1187 	cc = sendto(s, packet, packetlen, 0,
1188 	    (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1189 	if (cc != packetlen) {
1190 		logperror("sendbcast: sendto");
1191 		logerr("Cannot send broadcast packet to %s\n",
1192 			pr_name(baddr.sin_addr));
1193 	}
1194 	return (cc);
1195 }
1196 
1197 static int
1198 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1199 {
1200 	struct phyint *pi;
1201 	struct logint *li;
1202 	boolean_t mcast;
1203 	int cc;
1204 
1205 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1206 		mcast = B_FALSE;
1207 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1208 			if (li->li_state & ST_DELETED)
1209 				continue;
1210 
1211 			if (li->li_flags & IFF_MULTICAST) {
1212 				mcast = B_TRUE;
1213 				break;
1214 			}
1215 		}
1216 		if (!mcast)
1217 			continue;
1218 		cc = sendmcastif(s, packet, packetlen, sin, li);
1219 		if (cc != packetlen) {
1220 			return (cc);
1221 		}
1222 	}
1223 	return (packetlen);
1224 }
1225 
1226 static int
1227 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1228     struct logint *li)
1229 {
1230 	int cc;
1231 	struct sockaddr_in ifaddr;
1232 	struct icmp *icp = (struct icmp *)ALIGN(packet);
1233 
1234 	ifaddr.sin_family = AF_INET;
1235 
1236 	if ((li->li_flags & IFF_MULTICAST) == 0) {
1237 		if (verbose) {
1238 			logtrace("Suppressing sending %s on %s "
1239 			    "(interface is not multicast capable)\n",
1240 			    pr_type((int)icp->icmp_type), li->li_name);
1241 		}
1242 		return (packetlen);
1243 	}
1244 	if (li->li_flags & IFF_NORTEXCH) {
1245 		if (verbose) {
1246 			logtrace("Suppressing sending %s on %s "
1247 			    "(no route exchange on interface)\n",
1248 			    pr_type((int)icp->icmp_type), li->li_name);
1249 		}
1250 		return (packetlen);
1251 	}
1252 
1253 	ifaddr.sin_addr = li->li_address;
1254 	if (debug)
1255 		logdebug("Multicast to interface %s\n",
1256 			pr_name(ifaddr.sin_addr));
1257 	if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1258 	    (char *)&ifaddr.sin_addr,
1259 	    sizeof (ifaddr.sin_addr)) < 0) {
1260 		logperror("setsockopt (IP_MULTICAST_IF)");
1261 		logerr("Cannot send multicast packet over interface %s\n",
1262 			pr_name(ifaddr.sin_addr));
1263 		return (-1);
1264 	}
1265 	cc = sendto(s, packet, packetlen, 0,
1266 	    (struct sockaddr *)sin, sizeof (struct sockaddr));
1267 	if (cc != packetlen) {
1268 		logperror("sendmcast: sendto");
1269 		logerr("Cannot send multicast packet over interface %s\n",
1270 			pr_name(ifaddr.sin_addr));
1271 	}
1272 	return (cc);
1273 }
1274 
1275 static void
1276 reinitifs(void)
1277 {
1278 	(void) initifs(s, &g_joinaddr, g_preference);
1279 }
1280 
1281 static void
1282 force_preference(int preference)
1283 {
1284 	struct phyint *pi;
1285 	struct logint *li;
1286 
1287 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1288 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1289 			if (li->li_state & ST_DELETED)
1290 				continue;
1291 
1292 			li->li_preference = preference;
1293 		}
1294 	}
1295 }
1296 
1297 /*
1298  * Returns -1 on failure.
1299  */
1300 static int
1301 initifs(int s, struct sockaddr_in *joinaddr, int preference)
1302 {
1303 	struct ifconf ifc;
1304 	struct ifreq ifreq, *ifr;
1305 	struct lifreq lifreq;
1306 	int n;
1307 	char *buf;
1308 	int numifs;
1309 	unsigned bufsize;
1310 	struct phyint *pi;
1311 	struct logint *li;
1312 	int num_deletions;
1313 	char phyintname[IFNAMSIZ];
1314 	char *cp;
1315 	int old_num_usable_interfaces = num_usable_interfaces;
1316 
1317 	/*
1318 	 * Mark all interfaces so that we can determine which ones
1319 	 * have gone away.
1320 	 */
1321 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1322 		pi->pi_state |= ST_MARKED;
1323 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1324 			li->li_state |= ST_MARKED;
1325 		}
1326 	}
1327 
1328 	if (sock < 0) {
1329 		sock = socket(AF_INET, SOCK_DGRAM, 0);
1330 		if (sock < 0) {
1331 			logperror("initifs: socket");
1332 			return (-1);
1333 		}
1334 	}
1335 #ifdef SIOCGIFNUM
1336 	if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1337 		logperror("initifs: SIOCGIFNUM");
1338 		return (-1);
1339 	}
1340 #else
1341 	numifs = MAXIFS;
1342 #endif
1343 	bufsize = numifs * sizeof (struct ifreq);
1344 	buf = (char *)malloc(bufsize);
1345 	if (buf == NULL) {
1346 		logerr("out of memory\n");
1347 		(void) close(sock);
1348 		sock = -1;
1349 		return (-1);
1350 	}
1351 	ifc.ifc_len = bufsize;
1352 	ifc.ifc_buf = buf;
1353 	if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1354 		logperror("initifs: ioctl (get interface configuration)");
1355 		(void) close(sock);
1356 		sock = -1;
1357 		(void) free(buf);
1358 		return (-1);
1359 	}
1360 	ifr = ifc.ifc_req;
1361 	for (n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1362 		ifreq = *ifr;
1363 		/*
1364 		 * We need to use new interface ioctls to get 64-bit flags.
1365 		 */
1366 		(void) strncpy(lifreq.lifr_name, ifr->ifr_name,
1367 		    sizeof (ifr->ifr_name));
1368 		if (ioctl(sock, SIOCGLIFFLAGS, (char *)&lifreq) < 0) {
1369 			logperror("initifs: ioctl (get interface flags)");
1370 			continue;
1371 		}
1372 		if (ifr->ifr_addr.sa_family != AF_INET)
1373 			continue;
1374 		if ((lifreq.lifr_flags & IFF_UP) == 0)
1375 			continue;
1376 		if (lifreq.lifr_flags & IFF_LOOPBACK)
1377 			continue;
1378 		if ((lifreq.lifr_flags & (IFF_MULTICAST | IFF_BROADCAST)) == 0)
1379 			continue;
1380 
1381 		/* Create the physical name by truncating at the ':' */
1382 		strncpy(phyintname, ifreq.ifr_name, sizeof (phyintname));
1383 		if ((cp = strchr(phyintname, ':')) != NULL)
1384 			*cp = '\0';
1385 
1386 		pi = find_phyint(phyintname);
1387 		if (pi == NULL) {
1388 			pi = add_phyint(phyintname);
1389 			if (pi == NULL) {
1390 				logerr("out of memory\n");
1391 				(void) close(sock);
1392 				sock = -1;
1393 				(void) free(buf);
1394 				return (-1);
1395 			}
1396 		}
1397 		pi->pi_state &= ~ST_MARKED;
1398 
1399 		li = find_logint(pi, ifreq.ifr_name);
1400 		if (li != NULL) {
1401 			/*
1402 			 * Detect significant changes.
1403 			 * We treat netmask changes as insignificant but all
1404 			 * other changes cause a delete plus add of the
1405 			 * logical interface.
1406 			 * Note: if the flags and localaddr are unchanged
1407 			 * then nothing but the netmask and the broadcast
1408 			 * address could have changed since the other addresses
1409 			 * are derived from the flags and the localaddr.
1410 			 */
1411 			struct logint newli;
1412 
1413 			if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1414 			    &ifreq, &newli)) {
1415 				free_logint(li);
1416 				continue;
1417 			}
1418 
1419 			if (newli.li_flags != li->li_flags ||
1420 			    newli.li_localaddr.s_addr !=
1421 			    li->li_localaddr.s_addr || newli.li_index !=
1422 			    li->li_index) {
1423 				/* Treat as an interface deletion + addition */
1424 				li->li_state |= ST_DELETED;
1425 				deleted_logint(li, &newli, s, joinaddr);
1426 				free_logint(li);
1427 				li = NULL;	/* li recreated below */
1428 			} else {
1429 				/*
1430 				 * No significant changes.
1431 				 * Just update the netmask, and broadcast.
1432 				 */
1433 				li->li_netmask = newli.li_netmask;
1434 				li->li_bcastaddr = newli.li_bcastaddr;
1435 			}
1436 		}
1437 		if (li == NULL) {
1438 			li = add_logint(pi, ifreq.ifr_name);
1439 			if (li == NULL) {
1440 				logerr("out of memory\n");
1441 				(void) close(sock);
1442 				sock = -1;
1443 				(void) free(buf);
1444 				return (-1);
1445 			}
1446 
1447 			/* init li */
1448 			if (!getconfig(sock, lifreq.lifr_flags, &ifr->ifr_addr,
1449 			    &ifreq, li)) {
1450 				free_logint(li);
1451 				continue;
1452 			}
1453 			li->li_preference = preference;
1454 			added_logint(li, s, joinaddr);
1455 		}
1456 		li->li_state &= ~ST_MARKED;
1457 	}
1458 	(void) free(buf);
1459 
1460 	/*
1461 	 * Determine which interfaces have gone away.
1462 	 * The deletion is done in three phases:
1463 	 * 1. Mark ST_DELETED
1464 	 * 2. Inform using the deleted_* function.
1465 	 * 3. Unlink and free the actual memory.
1466 	 * Note that for #3 the physical interface must be deleted after
1467 	 * the logical ones.
1468 	 * Also count the number of physical interfaces.
1469 	 */
1470 	num_usable_interfaces = 0;
1471 	num_deletions = 0;
1472 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1473 		if (pi->pi_state & ST_MARKED) {
1474 			num_deletions++;
1475 			pi->pi_state |= ST_DELETED;
1476 		}
1477 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1478 			if (li->li_state & ST_MARKED) {
1479 				num_deletions++;
1480 				li->li_state |= ST_DELETED;
1481 			}
1482 		}
1483 		if (!(pi->pi_state & ST_DELETED))
1484 			num_usable_interfaces++;
1485 	}
1486 	if (num_deletions != 0) {
1487 		struct phyint *nextpi;
1488 		struct logint *nextli;
1489 
1490 		for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1491 			if (pi->pi_state & ST_DELETED) {
1492 				/*
1493 				 * By deleting the physical interface pi, all of
1494 				 * the corresponding logical interfaces will
1495 				 * also be deleted so there is no need to delete
1496 				 * them individually.
1497 				 */
1498 				deleted_phyint(pi, s, joinaddr);
1499 			} else {
1500 				for (li = pi->pi_logical_first; li != NULL;
1501 				    li = li->li_next) {
1502 					if (li->li_state & ST_DELETED) {
1503 						deleted_logint(li, NULL, s,
1504 						    joinaddr);
1505 					}
1506 				}
1507 			}
1508 		}
1509 		/* Do the actual linked list update + free */
1510 		for (pi = phyint; pi != NULL; pi = nextpi) {
1511 			nextpi = pi->pi_next;
1512 			for (li = pi->pi_logical_first; li != NULL;
1513 			    li = nextli) {
1514 				nextli = li->li_next;
1515 				if (li->li_state & ST_DELETED)
1516 					free_logint(li);
1517 			}
1518 			if (pi->pi_state & ST_DELETED)
1519 				free_phyint(pi);
1520 		}
1521 	}
1522 	/*
1523 	 * When the set of available interfaces goes from zero to
1524 	 * non-zero we restart solicitations if '-s' was specified.
1525 	 */
1526 	if (old_num_usable_interfaces == 0 && num_usable_interfaces > 0 &&
1527 	    start_solicit && !solicit) {
1528 		if (debug)
1529 			logdebug("switching to solicitations: num if %d\n",
1530 			    num_usable_interfaces);
1531 		solicit = start_solicit;
1532 		ntransmitted = 0;
1533 		ntransmitted++;
1534 		solicitor(&whereto);
1535 	}
1536 	return (0);
1537 }
1538 
1539 static boolean_t
1540 getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
1541     struct ifreq *ifr, struct logint *li)
1542 {
1543 	struct ifreq ifreq;
1544 	struct sockaddr_in *sin;
1545 	struct lifreq lifreq;
1546 
1547 	ifreq = *ifr;	/* Copy name etc */
1548 
1549 	li->li_flags = if_flags;
1550 	sin = (struct sockaddr_in *)ALIGN(addr);
1551 	li->li_localaddr = sin->sin_addr;
1552 
1553 	(void) strlcpy(lifreq.lifr_name, ifr->ifr_name,
1554 	    sizeof (lifreq.lifr_name));
1555 	if (ioctl(sock, SIOCGLIFINDEX, &lifreq) < 0) {
1556 		logperror("initifs: ioctl (get if index)");
1557 		/* Continue with 0; a safe value never used for interfaces */
1558 		li->li_index = 0;
1559 	} else {
1560 		li->li_index = lifreq.lifr_index;
1561 	}
1562 
1563 	if (if_flags & IFF_POINTOPOINT) {
1564 		li->li_netmask.s_addr = (unsigned long)0xffffffff;
1565 		if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1566 			logperror("initifs: ioctl (get dest addr)");
1567 			return (B_FALSE);
1568 		}
1569 		/* A pt-pt link is identified by the remote address */
1570 		sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1571 		li->li_address = sin->sin_addr;
1572 		li->li_remoteaddr = sin->sin_addr;
1573 		/* Simulate broadcast for pt-pt */
1574 		li->li_bcastaddr = sin->sin_addr;
1575 		li->li_flags |= IFF_BROADCAST;
1576 	} else {
1577 		/*
1578 		 * Non pt-pt links are identified by the local
1579 		 * address
1580 		 */
1581 		li->li_address = li->li_localaddr;
1582 		li->li_remoteaddr = li->li_address;
1583 		if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1584 			logperror("initifs: ioctl (get netmask)");
1585 			return (B_FALSE);
1586 		}
1587 		sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1588 		li->li_netmask = sin->sin_addr;
1589 		if (if_flags & IFF_BROADCAST) {
1590 			if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1591 				logperror(
1592 				    "initifs: ioctl (get broadcast address)");
1593 				return (B_FALSE);
1594 			}
1595 			sin = (struct sockaddr_in *)ALIGN(&ifreq.ifr_addr);
1596 			li->li_bcastaddr = sin->sin_addr;
1597 		}
1598 	}
1599 	return (B_TRUE);
1600 }
1601 
1602 
1603 static int
1604 support_multicast(void)
1605 {
1606 	int sock;
1607 	uchar_t ttl = 1;
1608 
1609 	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1610 	if (sock < 0) {
1611 		logperror("support_multicast: socket");
1612 		return (0);
1613 	}
1614 
1615 	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1616 	    (char *)&ttl, sizeof (ttl)) < 0) {
1617 		(void) close(sock);
1618 		return (0);
1619 	}
1620 	(void) close(sock);
1621 	return (1);
1622 }
1623 
1624 /*
1625  * For a given destination address, find the logical interface to use.
1626  * If opi is NULL check all interfaces. Otherwise just match against
1627  * the specified physical interface.
1628  * Return logical interface if there's a match, NULL otherwise.
1629  */
1630 static struct logint *
1631 find_directly_connected_logint(struct in_addr in, struct phyint *opi)
1632 {
1633 	struct phyint *pi;
1634 	struct logint *li;
1635 
1636 	if (opi == NULL)
1637 		pi = phyint;
1638 	else
1639 		pi = opi;
1640 
1641 	for (; pi != NULL; pi = pi->pi_next) {
1642 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1643 			if (li->li_state & ST_DELETED)
1644 				continue;
1645 
1646 			/* Check that the subnetwork numbers match */
1647 			if ((in.s_addr & li->li_netmask.s_addr) ==
1648 			    (li->li_remoteaddr.s_addr &
1649 			    li->li_netmask.s_addr))
1650 				return (li);
1651 		}
1652 		if (opi != NULL)
1653 			break;
1654 	}
1655 	return (NULL);
1656 }
1657 
1658 /*
1659  * INTERFACES - physical and logical identified by name
1660  */
1661 
1662 
1663 static void
1664 report_interfaces(void)
1665 {
1666 	struct phyint *pi;
1667 	struct logint *li;
1668 
1669 	logdebug("\nInterfaces:\n\n");
1670 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1671 		logdebug("Phyint %s state 0x%x\n",
1672 		    pi->pi_name, pi->pi_state);
1673 		for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1674 			logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
1675 			    li->li_name, li->li_state, li->li_flags,
1676 			    pr_name(li->li_address));
1677 			logdebug("\tlocal %s pref 0x%x ",
1678 			    pr_name(li->li_localaddr), li->li_preference);
1679 			logdebug("bcast %s\n",
1680 			    pr_name(li->li_bcastaddr));
1681 			logdebug("\tremote %s ",
1682 			    pr_name(li->li_remoteaddr));
1683 			logdebug("netmask %s\n",
1684 			    pr_name(li->li_netmask));
1685 		}
1686 	}
1687 }
1688 
1689 static struct phyint *
1690 find_phyint(char *name)
1691 {
1692 	struct phyint *pi;
1693 
1694 	for (pi = phyint; pi != NULL; pi = pi->pi_next) {
1695 		if (strcmp(pi->pi_name, name) == 0)
1696 			return (pi);
1697 	}
1698 	return (NULL);
1699 }
1700 
1701 /* Assumes that the entry does not exist - caller must use find_* */
1702 static struct phyint *
1703 add_phyint(char *name)
1704 {
1705 	struct phyint *pi;
1706 
1707 	pi = malloc(sizeof (*pi));
1708 	if (pi == NULL)
1709 		return (NULL);
1710 	bzero((char *)pi, sizeof (*pi));
1711 
1712 	strncpy(pi->pi_name, name, sizeof (pi->pi_name));
1713 	/* Link into list */
1714 	pi->pi_next = phyint;
1715 	pi->pi_prev = NULL;
1716 	if (phyint != NULL)
1717 		phyint->pi_prev = pi;
1718 	phyint = pi;
1719 	return (pi);
1720 }
1721 
1722 static void
1723 free_phyint(struct phyint *pi)
1724 {
1725 	assert(pi->pi_logical_first == NULL);
1726 	assert(pi->pi_logical_last == NULL);
1727 
1728 	if (pi->pi_prev == NULL) {
1729 		/* Delete first */
1730 		assert(phyint == pi);
1731 		phyint = pi->pi_next;
1732 	} else {
1733 		assert(pi->pi_prev->pi_next == pi);
1734 		pi->pi_prev->pi_next = pi->pi_next;
1735 	}
1736 	if (pi->pi_next != NULL) {
1737 		assert(pi->pi_next->pi_prev == pi);
1738 		pi->pi_next->pi_prev = pi->pi_prev;
1739 	}
1740 	free(pi);
1741 }
1742 
1743 static struct logint *
1744 find_logint(struct phyint *pi, char *name)
1745 {
1746 	struct logint *li;
1747 
1748 	for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1749 		if (strcmp(li->li_name, name) == 0)
1750 			return (li);
1751 	}
1752 	return (NULL);
1753 }
1754 
1755 /*
1756  * Assumes that the entry does not exist - caller must use find_*
1757  * Tail insertion.
1758  */
1759 static struct logint *
1760 add_logint(struct phyint *pi, char *name)
1761 {
1762 	struct logint *li;
1763 
1764 	li = malloc(sizeof (*li));
1765 	if (li == NULL)
1766 		return (NULL);
1767 	bzero((char *)li, sizeof (*li));
1768 
1769 	strncpy(li->li_name, name, sizeof (li->li_name));
1770 	/* Link into list */
1771 	li->li_prev = pi->pi_logical_last;
1772 	if (pi->pi_logical_last == NULL) {
1773 		/* First one */
1774 		assert(pi->pi_logical_first == NULL);
1775 		pi->pi_logical_first = li;
1776 	} else {
1777 		pi->pi_logical_last->li_next = li;
1778 	}
1779 	li->li_next = NULL;
1780 	li->li_physical = pi;
1781 	pi->pi_logical_last = li;
1782 	return (li);
1783 
1784 }
1785 
1786 static void
1787 free_logint(struct logint *li)
1788 {
1789 	struct phyint *pi;
1790 
1791 	pi = li->li_physical;
1792 	if (li->li_prev == NULL) {
1793 		/* Delete first */
1794 		assert(pi->pi_logical_first == li);
1795 		pi->pi_logical_first = li->li_next;
1796 	} else {
1797 		assert(li->li_prev->li_next == li);
1798 		li->li_prev->li_next = li->li_next;
1799 	}
1800 	if (li->li_next == NULL) {
1801 		/* Delete last */
1802 		assert(pi->pi_logical_last == li);
1803 		pi->pi_logical_last = li->li_prev;
1804 	} else {
1805 		assert(li->li_next->li_prev == li);
1806 		li->li_next->li_prev = li->li_prev;
1807 	}
1808 	free(li);
1809 }
1810 
1811 
1812 /* Tell all the logical interfaces that they are going away */
1813 static void
1814 deleted_phyint(struct phyint *pi, int s,
1815     struct sockaddr_in *joinaddr)
1816 {
1817 	struct logint *li;
1818 
1819 	if (debug)
1820 		logdebug("Deleting physical interface %s\n", pi->pi_name);
1821 
1822 	for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1823 		li->li_state |= ST_DELETED;
1824 	}
1825 	for (li = pi->pi_logical_first; li != NULL; li = li->li_next) {
1826 		deleted_logint(li, NULL, s, joinaddr);
1827 	}
1828 }
1829 
1830 /*
1831  * Join the multicast address if no other logical interface has done
1832  * so for this physical interface.
1833  */
1834 static void
1835 added_logint(struct logint *li, int s,
1836     struct sockaddr_in *joinaddr)
1837 {
1838 	if (debug)
1839 		logdebug("Adding logical interface %s\n", li->li_name);
1840 
1841 	if ((!(li->li_physical->pi_state & ST_JOINED)) &&
1842 	    (!isbroadcast(joinaddr))) {
1843 		struct ip_mreq mreq;
1844 
1845 		mreq.imr_multiaddr = joinaddr->sin_addr;
1846 		mreq.imr_interface = li->li_address;
1847 
1848 		if (debug)
1849 			logdebug("Joining MC on interface %s\n", li->li_name);
1850 
1851 		if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1852 		    (char *)&mreq, sizeof (mreq)) < 0) {
1853 			logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1854 		} else {
1855 			li->li_physical->pi_state |= ST_JOINED;
1856 			li->li_state |= ST_JOINED;
1857 		}
1858 	}
1859 }
1860 
1861 /*
1862  * Leave the multicast address if this logical interface joined it.
1863  * Look for a replacement logical interface for the same physical interface.
1864  * Remove any routes which are no longer reachable.
1865  *
1866  * If newli is non-NULL, then it is likely that the address of a logical
1867  * interface has changed.  In this case, the membership should be dropped using
1868  * the new address of the interface in question.
1869  *
1870  * XXX When a physical interface is being deleted by deleted_phyint(), this
1871  * routine will be called for each logical interface associated with the
1872  * physical one.  This should be made more efficient as there is no point in
1873  * searching for an alternate logical interface to add group membership to as
1874  * they all are marked ST_DELETED.
1875  */
1876 static void
1877 deleted_logint(struct logint *li, struct logint *newli, int s,
1878     struct sockaddr_in *joinaddr)
1879 {
1880 	struct phyint *pi;
1881 	struct logint *oli;
1882 
1883 	if (debug)
1884 		logdebug("Deleting logical interface %s\n", li->li_name);
1885 
1886 	assert(li->li_state & ST_DELETED);
1887 
1888 	if (li->li_state & ST_JOINED) {
1889 		struct ip_mreq mreq;
1890 
1891 		pi = li->li_physical;
1892 		assert(pi->pi_state & ST_JOINED);
1893 		assert(!isbroadcast(joinaddr));
1894 
1895 		mreq.imr_multiaddr = joinaddr->sin_addr;
1896 		if (newli != NULL)
1897 			mreq.imr_interface = newli->li_address;
1898 		else
1899 			mreq.imr_interface = li->li_address;
1900 
1901 		if (debug)
1902 			logdebug("Leaving MC on interface %s\n", li->li_name);
1903 
1904 		if (setsockopt(s, IPPROTO_IP, IP_DROP_MEMBERSHIP,
1905 		    (char *)&mreq, sizeof (mreq)) < 0) {
1906 			/*
1907 			 * EADDRNOTAVAIL will be returned if the interface has
1908 			 * been unplumbed or if the interface no longer has
1909 			 * IFF_MULTICAST set.  The former is the common case
1910 			 * while the latter is rare so don't log the error
1911 			 * unless some other error was returned or if debug is
1912 			 * set.
1913 			 */
1914 			if (errno != EADDRNOTAVAIL) {
1915 				logperror("setsockopt (IP_DROP_MEMBERSHIP)");
1916 			} else if (debug) {
1917 				logdebug("%s: %s\n",
1918 				    "setsockopt (IP_DROP_MEMBERSHIP)",
1919 				    strerror(errno));
1920 			}
1921 		}
1922 		li->li_physical->pi_state &= ~ST_JOINED;
1923 		li->li_state &= ~ST_JOINED;
1924 
1925 		/* Is there another interface that can join? */
1926 		for (oli = pi->pi_logical_first; oli != NULL;
1927 		    oli = oli->li_next) {
1928 			if (oli->li_state & ST_DELETED)
1929 				continue;
1930 
1931 			mreq.imr_multiaddr = joinaddr->sin_addr;
1932 			mreq.imr_interface = oli->li_address;
1933 
1934 			if (debug)
1935 				logdebug("Joining MC on interface %s\n",
1936 				    oli->li_name);
1937 
1938 			if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1939 				(char *)&mreq, sizeof (mreq)) < 0) {
1940 				logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1941 			} else {
1942 				pi->pi_state |= ST_JOINED;
1943 				oli->li_state |= ST_JOINED;
1944 				break;
1945 			}
1946 		}
1947 	}
1948 
1949 	flush_unreachable_routers();
1950 }
1951 
1952 
1953 
1954 /*
1955  * TABLES
1956  */
1957 struct table {
1958 	struct in_addr	router;
1959 	int		preference;
1960 	int		remaining_time;
1961 	int		in_kernel;
1962 	struct table	*next;
1963 };
1964 
1965 struct table *table;
1966 
1967 static void
1968 report_routes(void)
1969 {
1970 	struct table *tp;
1971 
1972 	logdebug("\nRoutes:\n\n");
1973 	tp = table;
1974 	while (tp) {
1975 		logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
1976 		    pr_name(tp->router), tp->preference,
1977 		    tp->remaining_time,
1978 		    (tp->in_kernel ? "in" : "not in"));
1979 		tp = tp->next;
1980 	}
1981 }
1982 
1983 static struct table *
1984 find_router(struct in_addr addr)
1985 {
1986 	struct table *tp;
1987 
1988 	tp = table;
1989 	while (tp) {
1990 		if (tp->router.s_addr == addr.s_addr)
1991 			return (tp);
1992 		tp = tp->next;
1993 	}
1994 	return (NULL);
1995 }
1996 
1997 static int
1998 max_preference(void)
1999 {
2000 	struct table *tp;
2001 	int max = (int)IGNORE_PREFERENCE;
2002 
2003 	tp = table;
2004 	while (tp) {
2005 		if (tp->preference > max)
2006 			max = tp->preference;
2007 		tp = tp->next;
2008 	}
2009 	return (max);
2010 }
2011 
2012 
2013 /* Note: this might leave the kernel with no default route for a short time. */
2014 static void
2015 age_table(int time)
2016 {
2017 	struct table **tpp, *tp;
2018 	int recalculate_max = 0;
2019 	int max = max_preference();
2020 
2021 	tpp = &table;
2022 	while (*tpp != NULL) {
2023 		tp = *tpp;
2024 		tp->remaining_time -= time;
2025 		if (tp->remaining_time <= 0) {
2026 			*tpp = tp->next;
2027 			if (debug) {
2028 				logdebug("Timed out router %s\n",
2029 				    pr_name(tp->router));
2030 			}
2031 			if (tp->in_kernel)
2032 				del_route(tp->router);
2033 			if (best_preference &&
2034 			    tp->preference == max)
2035 				recalculate_max++;
2036 			free((char *)tp);
2037 		} else {
2038 			tpp = &tp->next;
2039 		}
2040 	}
2041 	if (recalculate_max) {
2042 		int max = max_preference();
2043 
2044 		if (max != IGNORE_PREFERENCE) {
2045 			tp = table;
2046 			while (tp) {
2047 				if (tp->preference == max && !tp->in_kernel) {
2048 					add_route(tp->router);
2049 					tp->in_kernel++;
2050 				}
2051 				tp = tp->next;
2052 			}
2053 		}
2054 	}
2055 }
2056 
2057 /*
2058  * Remove any routes which are no longer directly connected.
2059  */
2060 static void
2061 flush_unreachable_routers(void)
2062 {
2063 	struct table **tpp, *tp;
2064 	int recalculate_max = 0;
2065 	int max = max_preference();
2066 
2067 	tpp = &table;
2068 	while (*tpp != NULL) {
2069 		tp = *tpp;
2070 		if (find_directly_connected_logint(tp->router, NULL) == NULL) {
2071 			*tpp = tp->next;
2072 			if (debug) {
2073 				logdebug("Unreachable router %s\n",
2074 				    pr_name(tp->router));
2075 			}
2076 			if (tp->in_kernel)
2077 				del_route(tp->router);
2078 			if (best_preference &&
2079 			    tp->preference == max)
2080 				recalculate_max++;
2081 			free((char *)tp);
2082 		} else {
2083 			tpp = &tp->next;
2084 		}
2085 	}
2086 	if (recalculate_max) {
2087 		int max = max_preference();
2088 
2089 		if (max != IGNORE_PREFERENCE) {
2090 			tp = table;
2091 			while (tp) {
2092 				if (tp->preference == max && !tp->in_kernel) {
2093 					add_route(tp->router);
2094 					tp->in_kernel++;
2095 				}
2096 				tp = tp->next;
2097 			}
2098 		}
2099 	}
2100 }
2101 
2102 static void
2103 record_router(struct in_addr router, long preference, int ttl)
2104 {
2105 	struct table *tp;
2106 	int old_max = max_preference();
2107 	int changed_up = 0;	/* max preference could have increased */
2108 	int changed_down = 0;	/* max preference could have decreased */
2109 
2110 	if (debug)
2111 		logdebug("Recording %s, preference 0x%x\n",
2112 			pr_name(router),
2113 			preference);
2114 	tp = find_router(router);
2115 	if (tp) {
2116 		if (tp->preference > preference &&
2117 		    tp->preference == old_max)
2118 			changed_down++;
2119 		else if (preference > tp->preference)
2120 			changed_up++;
2121 		tp->preference = preference;
2122 		tp->remaining_time = ttl;
2123 	} else {
2124 		if (preference > old_max)
2125 			changed_up++;
2126 		tp = (struct table *)ALIGN(malloc(sizeof (struct table)));
2127 		if (tp == NULL) {
2128 			logerr("Out of memory\n");
2129 			return;
2130 		}
2131 		tp->router = router;
2132 		tp->preference = preference;
2133 		tp->remaining_time = ttl;
2134 		tp->in_kernel = 0;
2135 		tp->next = table;
2136 		table = tp;
2137 	}
2138 	if (!tp->in_kernel &&
2139 	    (!best_preference || tp->preference == max_preference()) &&
2140 	    tp->preference != IGNORE_PREFERENCE) {
2141 		add_route(tp->router);
2142 		tp->in_kernel++;
2143 	}
2144 	if (tp->preference == IGNORE_PREFERENCE && tp->in_kernel) {
2145 		del_route(tp->router);
2146 		tp->in_kernel = 0;
2147 	}
2148 	if (best_preference && changed_down) {
2149 		/* Check if we should add routes */
2150 		int new_max = max_preference();
2151 		if (new_max != IGNORE_PREFERENCE) {
2152 			tp = table;
2153 			while (tp) {
2154 				if (tp->preference == new_max &&
2155 				    !tp->in_kernel) {
2156 					add_route(tp->router);
2157 					tp->in_kernel++;
2158 				}
2159 				tp = tp->next;
2160 			}
2161 		}
2162 	}
2163 	if (best_preference && (changed_up || changed_down)) {
2164 		/* Check if we should remove routes already in the kernel */
2165 		int new_max = max_preference();
2166 		tp = table;
2167 		while (tp) {
2168 			if (tp->preference < new_max && tp->in_kernel) {
2169 				del_route(tp->router);
2170 				tp->in_kernel = 0;
2171 			}
2172 			tp = tp->next;
2173 		}
2174 	}
2175 }
2176 
2177 
2178 #include <net/route.h>
2179 
2180 static void
2181 add_route(struct in_addr addr)
2182 {
2183 	if (debug)
2184 		logdebug("Add default route to %s\n", pr_name(addr));
2185 	rtioctl(addr, SIOCADDRT);
2186 }
2187 
2188 static void
2189 del_route(struct in_addr addr)
2190 {
2191 	if (debug)
2192 		logdebug("Delete default route to %s\n", pr_name(addr));
2193 	rtioctl(addr, SIOCDELRT);
2194 }
2195 
2196 static void
2197 rtioctl(struct in_addr addr, int op)
2198 {
2199 	int sock;
2200 	struct rtentry rt;
2201 	struct sockaddr_in *sin;
2202 	bzero((char *)&rt, sizeof (struct rtentry));
2203 	rt.rt_dst.sa_family = AF_INET;
2204 	rt.rt_gateway.sa_family = AF_INET;
2205 	sin = (struct sockaddr_in *)ALIGN(&rt.rt_gateway);
2206 	sin->sin_addr = addr;
2207 	rt.rt_flags = RTF_UP | RTF_GATEWAY;
2208 
2209 	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
2210 	if (sock < 0) {
2211 		logperror("rtioctl: socket");
2212 		return;
2213 	}
2214 	if (ioctl(sock, op, (char *)&rt) < 0) {
2215 		if (!(op == SIOCADDRT && errno == EEXIST))
2216 			logperror("ioctl (add/delete route)");
2217 	}
2218 	(void) close(sock);
2219 }
2220 
2221 
2222 
2223 /*
2224  * LOGGER
2225  */
2226 
2227 #include <syslog.h>
2228 
2229 static int logging = 0;
2230 
2231 static void
2232 initlog(void)
2233 {
2234 	logging++;
2235 	openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
2236 }
2237 
2238 /* VARARGS1 */
2239 void
2240 logerr(fmt, a, b, c, d, e, f, g, h)
2241 	char *fmt;
2242 {
2243 	if (logging)
2244 		syslog(LOG_ERR, fmt, a, b, c, d, e, f, g, h);
2245 	else
2246 		(void) fprintf(stderr, fmt, a, b, c, d, e, f, g, h);
2247 }
2248 
2249 /* VARARGS1 */
2250 void
2251 logtrace(fmt, a, b, c, d, e, f, g, h)
2252 	char *fmt;
2253 {
2254 	if (logging)
2255 		syslog(LOG_INFO, fmt, a, b, c, d, e, f, g, h);
2256 	else
2257 		(void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2258 }
2259 
2260 /* VARARGS1 */
2261 void
2262 logdebug(fmt, a, b, c, d, e, f, g, h)
2263 	char *fmt;
2264 {
2265 	if (logging)
2266 		syslog(LOG_DEBUG, fmt, a, b, c, d, e, f, g, h);
2267 	else
2268 		(void) fprintf(stdout, fmt, a, b, c, d, e, f, g, h);
2269 }
2270 
2271 void
2272 logperror(str)
2273 	char *str;
2274 {
2275 	if (logging)
2276 		syslog(LOG_ERR, "%s: %s\n", str, strerror(errno));
2277 	else
2278 		(void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
2279 }
2280