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 */
69struct 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
78struct 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 */
102struct 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
111struct 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
127struct phyint *phyint;
128int 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 */
138static void	solicitor(struct sockaddr_in *sin);
139static void	advertise(struct sockaddr_in *sin);
140
141static void	age_table(int time);
142static void	flush_unreachable_routers(void);
143static void	record_router(struct in_addr router, long preference, int ttl);
144
145static void	add_route(struct in_addr addr);
146static void	del_route(struct in_addr addr);
147static void	rtioctl(struct in_addr addr, int op);
148
149static int	support_multicast(void);
150static int	sendbcast(int s, char *packet, int packetlen);
151static int	sendbcastif(int s, char *packet, int packetlen,
152		    struct logint *li);
153static int	sendmcast(int s, char *packet, int packetlen,
154		    struct sockaddr_in *sin);
155static int	sendmcastif(int s, char *packet, int packetlen,
156		    struct sockaddr_in *sin, struct logint *li);
157
158static int	ismulticast(struct sockaddr_in *sin);
159static int	isbroadcast(struct sockaddr_in *sin);
160int		in_cksum(ushort_t *addr, int len);
161static struct logint *find_directly_connected_logint(struct in_addr in,
162    struct phyint *pi);
163static void	force_preference(int preference);
164
165static void	timer(void);
166static void	finish(void);
167static void	report(void);
168static void	report_interfaces(void);
169static void	report_routes(void);
170static void	reinitifs(void);
171
172static struct phyint *find_phyint(char *name);
173static struct phyint *add_phyint(char *name);
174static void	free_phyint(struct phyint *pi);
175static struct logint *find_logint(struct phyint *pi, char *name);
176static struct logint *add_logint(struct phyint *pi, char *name);
177static void	free_logint(struct logint *li);
178
179static void	deleted_phyint(struct phyint *pi, int s,
180		    struct sockaddr_in *joinaddr);
181static void	added_logint(struct logint *li, int s,
182		    struct sockaddr_in *joinaddr);
183static void	deleted_logint(struct logint *li, struct logint *newli, int s,
184		    struct sockaddr_in *joinaddr);
185
186static int	initifs(int s, struct sockaddr_in *joinaddr, int preference);
187static boolean_t getconfig(int sock, uint64_t if_flags, struct sockaddr *addr,
188		    struct ifreq *ifr, struct logint *li);
189
190static void	pr_pack(char *buf, int cc, struct sockaddr_in *from);
191char		*pr_name(struct in_addr addr);
192char		*pr_type(int t);
193
194static void	initlog(void);
195void		logerr(), logtrace(), logdebug(), logperror();
196
197/* Local variables */
198
199#define	MAXPACKET	4096	/* max packet size */
200uchar_t	packet[MAXPACKET];
201
202char 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
208int s;				/* Socket file descriptor */
209struct sockaddr_in whereto;	/* Address to send to */
210struct sockaddr_in g_joinaddr;	/* Address to receive on */
211char    *sendaddress, *recvaddress;	/* For logging purposes only */
212
213/* Common variables */
214int verbose = 0;
215int debug = 0;
216int trace = 0;
217int start_solicit = 0;	/* -s parameter set */
218int solicit = 0;	/* Are we currently sending solicitations? */
219int responder;
220int ntransmitted = 0;
221int nreceived = 0;
222int forever = 0;	/* Never give up on host. If 0 defer fork until */
223			/* first response.				*/
224
225/* Router variables */
226int max_adv_int = MAX_ADV_INT;
227int min_adv_int;
228int lifetime;
229int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
230int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
231ulong_t g_preference = 0;	/* Setable with -p option */
232
233/* Host variables */
234int max_solicitations = MAX_SOLICITATIONS;
235unsigned int solicitation_interval = SOLICITATION_INTERVAL;
236int 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
241static void
242prusage()
243{
244	(void) fprintf(stderr, usage);
245	exit(1);
246}
247
248static int	sock = -1;
249
250static void
251do_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 */
282int
283main(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
489static void
490report(void)
491{
492	report_interfaces();
493	report_routes();
494}
495
496#define	TIMER_INTERVAL	6
497#define	GETIFCONF_TIMER	30
498
499static int left_until_advertise;
500
501/* Called every TIMER_INTERVAL */
502static void
503timer(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 */
549static void
550solicitor(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 */
605static void
606advertise(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 */
705char *
706pr_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 */
741char *
742pr_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 */
762static void
763pr_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 */
1007int
1008in_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 */
1051static void
1052finish(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
1077int
1078pr_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
1112static int
1113isbroadcast(struct sockaddr_in *sin)
1114{
1115	return (sin->sin_addr.s_addr == htonl(INADDR_BROADCAST));
1116}
1117
1118static int
1119ismulticast(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 */
1128static int
1129sendbcast(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
1157static int
1158sendbcastif(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
1197static int
1198sendmcast(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
1226static int
1227sendmcastif(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
1275static void
1276reinitifs(void)
1277{
1278	(void) initifs(s, &g_joinaddr, g_preference);
1279}
1280
1281static void
1282force_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 */
1300static int
1301initifs(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
1539static boolean_t
1540getconfig(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
1603static int
1604support_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 */
1630static struct logint *
1631find_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
1663static void
1664report_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
1689static struct phyint *
1690find_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_* */
1702static struct phyint *
1703add_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
1722static void
1723free_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
1743static struct logint *
1744find_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 */
1759static struct logint *
1760add_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
1786static void
1787free_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 */
1813static void
1814deleted_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 */
1834static void
1835added_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 */
1876static void
1877deleted_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 */
1957struct table {
1958	struct in_addr	router;
1959	int		preference;
1960	int		remaining_time;
1961	int		in_kernel;
1962	struct table	*next;
1963};
1964
1965struct table *table;
1966
1967static void
1968report_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
1983static struct table *
1984find_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
1997static int
1998max_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. */
2014static void
2015age_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 */
2060static void
2061flush_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
2102static void
2103record_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
2180static void
2181add_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
2188static void
2189del_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
2196static void
2197rtioctl(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
2229static int logging = 0;
2230
2231static void
2232initlog(void)
2233{
2234	logging++;
2235	openlog("in.rdisc", LOG_PID | LOG_CONS, LOG_DAEMON);
2236}
2237
2238/* VARARGS1 */
2239void
2240logerr(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 */
2250void
2251logtrace(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 */
2261void
2262logdebug(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
2271void
2272logperror(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