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