rdisc.c revision 313379eb6b9da55f7371adef39a92153a0707d4a
1/*
2 * Rdisc (this program) was developed by Sun Microsystems, Inc. and is
3 * provided for unrestricted use provided that this legend is included on
4 * all tape media and as a part of the software program in whole or part.
5 * Users may copy or modify Rdisc without charge, and they may freely
6 * distribute it.
7 *
8 * RDISC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
9 * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
10 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
11 *
12 * Rdisc is provided with no support and without any obligation on the
13 * part of Sun Microsystems, Inc. to assist in its use, correction,
14 * modification or enhancement.
15 *
16 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
17 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY RDISC
18 * OR ANY PART THEREOF.
19 *
20 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
21 * or profits or other special, indirect and consequential damages, even if
22 * Sun has been advised of the possibility of such damages.
23 *
24 * Sun Microsystems, Inc.
25 * 2550 Garcia Avenue
26 * Mountain View, California  94043
27 */
28#include <stdio.h>
29#include <errno.h>
30#include <signal.h>
31#include <unistd.h>
32#include <stdlib.h>
33#include <sys/types.h>
34#include <sys/time.h>
35/* Do not use "improved" glibc version! */
36#include <linux/limits.h>
37
38#include <sys/param.h>
39#include <sys/socket.h>
40#include <sys/file.h>
41#include <malloc.h>
42
43#include <sys/ioctl.h>
44#include <linux/if.h>
45#include <linux/route.h>
46
47#include <netinet/in.h>
48#include <netinet/ip.h>
49#include <netinet/ip_icmp.h>
50
51/*
52 * The next include contains all defs and structures for multicast
53 * that are not in SunOS 4.1.x. On a SunOS 4.1.x system none of this code
54 * is ever used because it does not support multicast
55 * Fraser Gardiner - Sun Microsystems Australia
56 */
57
58#include <netdb.h>
59#include <arpa/inet.h>
60
61#include <string.h>
62#include <syslog.h>
63
64#include "SNAPSHOT.h"
65
66struct interface
67{
68	struct in_addr 	address;	/* Used to identify the interface */
69	struct in_addr	localaddr;	/* Actual address if the interface */
70	int 		preference;
71	int		flags;
72	struct in_addr	bcastaddr;
73	struct in_addr	remoteaddr;
74	struct in_addr	netmask;
75	int		ifindex;
76	char		name[IFNAMSIZ];
77};
78
79/*
80 * TBD
81 *	Use 255.255.255.255 for broadcasts - not the interface broadcast
82 *	address.
83 */
84
85#define ALLIGN(ptr)	(ptr)
86
87static int join(int sock, struct sockaddr_in *sin);
88static void solicitor(struct sockaddr_in *);
89#ifdef RDISC_SERVER
90static void advertise(struct sockaddr_in *, int lft);
91#endif
92static char *pr_name(struct in_addr addr);
93static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
94static void age_table(int time);
95static void record_router(struct in_addr router, int preference, int ttl);
96static void add_route(struct in_addr addr);
97static void del_route(struct in_addr addr);
98static void rtioctl(struct in_addr addr, int op);
99static int support_multicast(void);
100static int sendbcast(int s, char *packet, int packetlen);
101static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
102static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
103static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
104static int is_directly_connected(struct in_addr in);
105static void initlog(void);
106static void discard_table(void);
107static void init(void);
108
109#define ICMP_ROUTER_ADVERTISEMENT	9
110#define ICMP_ROUTER_SOLICITATION	10
111
112#define ALL_HOSTS_ADDRESS		"224.0.0.1"
113#define ALL_ROUTERS_ADDRESS		"224.0.0.2"
114
115#define MAXIFS 32
116
117#if !defined(__GLIBC__) || __GLIBC__ < 2
118/* For router advertisement */
119struct icmp_ra
120{
121	u_char	icmp_type;		/* type of message, see below */
122	u_char	icmp_code;		/* type sub code */
123	u_short	icmp_cksum;		/* ones complement cksum of struct */
124	u_char	icmp_num_addrs;
125	u_char	icmp_wpa;		/* Words per address */
126	short 	icmp_lifetime;
127};
128
129struct icmp_ra_addr
130{
131	__u32	ira_addr;
132	__u32	ira_preference;
133};
134#else
135#define icmp_ra icmp
136#endif
137
138/* Router constants */
139#define	MAX_INITIAL_ADVERT_INTERVAL	16
140#define	MAX_INITIAL_ADVERTISEMENTS  	3
141#define	MAX_RESPONSE_DELAY		2	/* Not used */
142
143/* Host constants */
144#define MAX_SOLICITATIONS 		3
145#define SOLICITATION_INTERVAL 		3
146#define MAX_SOLICITATION_DELAY		1	/* Not used */
147
148#define INELIGIBLE_PREF			0x80000000	/* Maximum negative */
149
150#define MAX_ADV_INT 600
151
152/* Statics */
153static int num_interfaces;
154
155static struct interface *interfaces;
156static int interfaces_size;			/* Number of elements in interfaces */
157
158
159#define	MAXPACKET	4096	/* max packet size */
160
161/* fraser */
162int debugfile;
163
164const char usage[] =
165"Usage:	rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
166#ifdef RDISC_SERVER
167"       rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>]\n"
168"		 [send_address] [receive_address]\n"
169#endif
170;
171
172
173int s;			/* Socket file descriptor */
174struct sockaddr_in whereto;/* Address to send to */
175
176/* Common variables */
177int verbose = 0;
178int debug = 0;
179int trace = 0;
180int solicit = 0;
181int ntransmitted = 0;
182int nreceived = 0;
183int forever = 0;	/* Never give up on host. If 0 defer fork until
184			 * first response.
185			 */
186
187#ifdef RDISC_SERVER
188/* Router variables */
189int responder;
190int max_adv_int = MAX_ADV_INT;
191int min_adv_int;
192int lifetime;
193int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
194int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
195int preference = 0;		/* Setable with -p option */
196#endif
197
198/* Host variables */
199int max_solicitations = MAX_SOLICITATIONS;
200unsigned int solicitation_interval = SOLICITATION_INTERVAL;
201int best_preference = 1;  	/* Set to record only the router(s) with the
202				   best preference in the kernel. Not set
203				   puts all routes in the kernel. */
204
205
206static void graceful_finish(void);
207static void finish(void);
208static void timer(void);
209static void initifs(void);
210static u_short in_cksum(u_short *addr, int len);
211
212static int logging = 0;
213
214#define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \
215			  else fprintf(stderr, fmt); })
216#define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \
217			  else fprintf(stderr, fmt); })
218#define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \
219			  else fprintf(stderr, fmt); })
220static void logperror(char *str);
221
222static __inline__ int isbroadcast(struct sockaddr_in *sin)
223{
224	return (sin->sin_addr.s_addr == INADDR_BROADCAST);
225}
226
227static __inline__ int ismulticast(struct sockaddr_in *sin)
228{
229	return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
230}
231
232static void prusage(void)
233{
234	fputs(usage, stderr);
235	exit(1);
236}
237
238void do_fork(void)
239{
240	int t;
241	pid_t pid;
242	long open_max;
243
244	if (trace)
245		return;
246	if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) {
247		if (errno == 0) {
248			(void) fprintf(stderr, "OPEN_MAX is not supported\n");
249		}
250		else {
251			(void) fprintf(stderr, "sysconf() error\n");
252		}
253		exit(1);
254	}
255
256
257	if ((pid=fork()) != 0)
258		exit(0);
259
260	for (t = 0; t < open_max; t++)
261		if (t != s)
262			close(t);
263
264	setsid();
265	initlog();
266}
267
268void signal_setup(int signo, void (*handler)(void))
269{
270	struct sigaction sa;
271
272	memset(&sa, 0, sizeof(sa));
273
274	sa.sa_handler = (void (*)(int))handler;
275#ifdef SA_INTERRUPT
276	sa.sa_flags = SA_INTERRUPT;
277#endif
278	sigaction(signo, &sa, NULL);
279}
280
281/*
282 * 			M A I N
283 */
284char    *sendaddress, *recvaddress;
285
286int main(int argc, char **argv)
287{
288	struct sockaddr_in from;
289	char **av = argv;
290	struct sockaddr_in *to = &whereto;
291	struct sockaddr_in joinaddr;
292	sigset_t sset, sset_empty;
293#ifdef RDISC_SERVER
294	int val;
295
296	min_adv_int =( max_adv_int * 3 / 4);
297	lifetime = (3*max_adv_int);
298#endif
299
300	argc--, av++;
301	while (argc > 0 && *av[0] == '-') {
302		while (*++av[0]) {
303			switch (*av[0]) {
304			case 'd':
305				debug = 1;
306				break;
307			case 't':
308				trace = 1;
309				break;
310			case 'v':
311				verbose++;
312				break;
313			case 's':
314				solicit = 1;
315				break;
316#ifdef RDISC_SERVER
317			case 'r':
318				responder = 1;
319				break;
320#endif
321			case 'a':
322				best_preference = 0;
323				break;
324			case 'b':
325				best_preference = 1;
326				break;
327			case 'f':
328				forever = 1;
329				break;
330			case 'V':
331				printf("rdisc utility, iputils-%s\n", SNAPSHOT);
332				exit(0);
333#ifdef RDISC_SERVER
334			case 'T':
335				argc--, av++;
336				if (argc != 0) {
337					val = strtol(av[0], (char **)NULL, 0);
338					if (val < 4 || val > 1800) {
339						(void) fprintf(stderr,
340							       "Bad Max Advertizement Interval\n");
341						exit(1);
342					}
343					max_adv_int = val;
344					min_adv_int =( max_adv_int * 3 / 4);
345					lifetime = (3*max_adv_int);
346				} else {
347					prusage();
348					/* NOTREACHED*/
349				}
350				goto next;
351			case 'p':
352				argc--, av++;
353				if (argc != 0) {
354					val = strtol(av[0], (char **)NULL, 0);
355					preference = val;
356				} else {
357					prusage();
358					/* NOTREACHED*/
359				}
360				goto next;
361#endif
362			default:
363				prusage();
364				/* NOTREACHED*/
365			}
366		}
367#ifdef RDISC_SERVER
368next:
369#endif
370		argc--, av++;
371	}
372	if( argc < 1)  {
373		if (support_multicast()) {
374			sendaddress = ALL_ROUTERS_ADDRESS;
375#ifdef RDISC_SERVER
376			if (responder)
377				sendaddress = ALL_HOSTS_ADDRESS;
378#endif
379		} else
380			sendaddress = "255.255.255.255";
381	} else {
382		sendaddress = av[0];
383		argc--;
384	}
385
386	if (argc < 1) {
387		if (support_multicast()) {
388			recvaddress = ALL_HOSTS_ADDRESS;
389#ifdef RDISC_SERVER
390			if (responder)
391				recvaddress = ALL_ROUTERS_ADDRESS;
392#endif
393		} else
394			recvaddress = "255.255.255.255";
395	} else {
396		recvaddress = av[0];
397		argc--;
398	}
399	if (argc != 0) {
400		(void) fprintf(stderr, "Extra parameters\n");
401		prusage();
402		/* NOTREACHED */
403	}
404
405#ifdef RDISC_SERVER
406	if (solicit && responder) {
407		prusage();
408		/* NOTREACHED */
409	}
410#endif
411
412	if (!(solicit && !forever)) {
413		do_fork();
414/*
415 * Added the next line to stop forking a second time
416 * Fraser Gardiner - Sun Microsystems Australia
417 */
418		forever = 1;
419	}
420
421	memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) );
422	to->sin_family = AF_INET;
423	to->sin_addr.s_addr = inet_addr(sendaddress);
424
425	memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) );
426	joinaddr.sin_family = AF_INET;
427	joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
428
429#ifdef RDISC_SERVER
430	if (responder)
431		srandom((int)gethostid());
432#endif
433
434	if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
435		logperror("socket");
436		exit(5);
437	}
438
439	setlinebuf( stdout );
440
441	signal_setup(SIGINT, finish );
442	signal_setup(SIGTERM, graceful_finish );
443	signal_setup(SIGHUP, initifs );
444	signal_setup(SIGALRM, timer );
445
446	sigemptyset(&sset);
447	sigemptyset(&sset_empty);
448	sigaddset(&sset, SIGALRM);
449	sigaddset(&sset, SIGHUP);
450	sigaddset(&sset, SIGTERM);
451	sigaddset(&sset, SIGINT);
452
453	init();
454	if (join(s, &joinaddr) < 0) {
455		logerr("Failed joining addresses\n");
456		exit (2);
457	}
458
459	timer();	/* start things going */
460
461	for (;;) {
462		u_char	packet[MAXPACKET];
463		int len = sizeof (packet);
464		socklen_t fromlen = sizeof (from);
465		int cc;
466
467		cc=recvfrom(s, (char *)packet, len, 0,
468			    (struct sockaddr *)&from, &fromlen);
469		if (cc<0) {
470			if (errno == EINTR)
471				continue;
472			logperror("recvfrom");
473			continue;
474		}
475
476		sigprocmask(SIG_SETMASK, &sset, NULL);
477		pr_pack( (char *)packet, cc, &from );
478		sigprocmask(SIG_SETMASK, &sset_empty, NULL);
479	}
480	/*NOTREACHED*/
481}
482
483#define TIMER_INTERVAL 	3
484#define GETIFCONF_TIMER	30
485
486static int left_until_advertise;
487
488/* Called every TIMER_INTERVAL */
489void timer()
490{
491	static int time;
492	static int left_until_getifconf;
493	static int left_until_solicit;
494
495
496	time += TIMER_INTERVAL;
497
498	left_until_getifconf -= TIMER_INTERVAL;
499	left_until_advertise -= TIMER_INTERVAL;
500	left_until_solicit -= TIMER_INTERVAL;
501
502	if (left_until_getifconf < 0) {
503		initifs();
504		left_until_getifconf = GETIFCONF_TIMER;
505	}
506#ifdef RDISC_SERVER
507	if (responder && left_until_advertise <= 0) {
508		ntransmitted++;
509		advertise(&whereto, lifetime);
510		if (ntransmitted < initial_advertisements)
511			left_until_advertise = initial_advert_interval;
512		else
513			left_until_advertise = min_adv_int +
514				((max_adv_int - min_adv_int) *
515				 (random() % 1000)/1000);
516	} else
517#endif
518	if (solicit && left_until_solicit <= 0) {
519		ntransmitted++;
520		solicitor(&whereto);
521		if (ntransmitted < max_solicitations)
522			left_until_solicit = solicitation_interval;
523		else {
524			solicit = 0;
525			if (!forever && nreceived == 0)
526				exit(5);
527		}
528	}
529	age_table(TIMER_INTERVAL);
530	alarm(TIMER_INTERVAL);
531}
532
533/*
534 * 			S O L I C I T O R
535 *
536 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
537 * The IP packet will be added on by the kernel.
538 */
539void
540solicitor(struct sockaddr_in *sin)
541{
542	static u_char outpack[MAXPACKET];
543	struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
544	int packetlen, i;
545
546	if (verbose) {
547		logtrace("Sending solicitation to %s\n",
548			 pr_name(sin->sin_addr));
549	}
550	icp->type = ICMP_ROUTER_SOLICITATION;
551	icp->code = 0;
552	icp->checksum = 0;
553	icp->un.gateway = 0; /* Reserved */
554	packetlen = 8;
555
556	/* Compute ICMP checksum here */
557	icp->checksum = in_cksum( (u_short *)icp, packetlen );
558
559	if (isbroadcast(sin))
560		i = sendbcast(s, (char *)outpack, packetlen);
561	else if (ismulticast(sin))
562		i = sendmcast(s, (char *)outpack, packetlen, sin);
563	else
564		i = sendto( s, (char *)outpack, packetlen, 0,
565			   (struct sockaddr *)sin, sizeof(struct sockaddr));
566
567	if( i < 0 || i != packetlen )  {
568		if( i<0 ) {
569		    logperror("solicitor:sendto");
570		}
571		logerr("wrote %s %d chars, ret=%d\n",
572			sendaddress, packetlen, i );
573	}
574}
575
576#ifdef RDISC_SERVER
577/*
578 * 			A V E R T I S E
579 *
580 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
581 * The IP packet will be added on by the kernel.
582 */
583void
584advertise(struct sockaddr_in *sin, int lft)
585{
586	static u_char outpack[MAXPACKET];
587	struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack);
588	struct icmp_ra_addr *ap;
589	int packetlen, i, cc;
590
591	if (verbose) {
592		logtrace("Sending advertisement to %s\n",
593			 pr_name(sin->sin_addr));
594	}
595
596	for (i = 0; i < num_interfaces; i++) {
597		rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
598		rap->icmp_code = 0;
599		rap->icmp_cksum = 0;
600		rap->icmp_num_addrs = 0;
601		rap->icmp_wpa = 2;
602		rap->icmp_lifetime = htons(lft);
603		packetlen = 8;
604
605		/*
606		 * TODO handle multiple logical interfaces per
607		 * physical interface. (increment with rap->icmp_wpa * 4 for
608		 * each address.)
609		 */
610		ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN);
611		ap->ira_addr = interfaces[i].localaddr.s_addr;
612		ap->ira_preference = htonl(interfaces[i].preference);
613		packetlen += rap->icmp_wpa * 4;
614		rap->icmp_num_addrs++;
615
616		/* Compute ICMP checksum here */
617		rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
618
619		if (isbroadcast(sin))
620			cc = sendbcastif(s, (char *)outpack, packetlen,
621					&interfaces[i]);
622		else if (ismulticast(sin))
623			cc = sendmcastif( s, (char *)outpack, packetlen, sin,
624					&interfaces[i]);
625		else {
626			struct interface *ifp = &interfaces[i];
627			/*
628			 * Verify that the interface matches the destination
629			 * address.
630			 */
631			if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
632			    (ifp->address.s_addr & ifp->netmask.s_addr)) {
633				if (debug) {
634					logdebug("Unicast to %s ",
635						 pr_name(sin->sin_addr));
636					logdebug("on interface %s, %s\n",
637						 ifp->name,
638						 pr_name(ifp->address));
639				}
640				cc = sendto( s, (char *)outpack, packetlen, 0,
641					    (struct sockaddr *)sin,
642					    sizeof(struct sockaddr));
643			} else
644				cc = packetlen;
645		}
646		if( cc < 0 || cc != packetlen )  {
647			if (cc < 0) {
648				logperror("sendto");
649			} else {
650				logerr("wrote %s %d chars, ret=%d\n",
651				       sendaddress, packetlen, cc );
652			}
653		}
654	}
655}
656#endif
657
658/*
659 * 			P R _ T Y P E
660 *
661 * Convert an ICMP "type" field to a printable string.
662 */
663char *
664pr_type(int t)
665{
666	static char *ttab[] = {
667		"Echo Reply",
668		"ICMP 1",
669		"ICMP 2",
670		"Dest Unreachable",
671		"Source Quench",
672		"Redirect",
673		"ICMP 6",
674		"ICMP 7",
675		"Echo",
676		"Router Advertise",
677		"Router Solicitation",
678		"Time Exceeded",
679		"Parameter Problem",
680		"Timestamp",
681		"Timestamp Reply",
682		"Info Request",
683		"Info Reply",
684		"Netmask Request",
685		"Netmask Reply"
686	};
687
688	if ( t < 0 || t > 16 )
689		return("OUT-OF-RANGE");
690
691	return(ttab[t]);
692}
693
694/*
695 *			P R _ N A M E
696 *
697 * Return a string name for the given IP address.
698 */
699char *pr_name(struct in_addr addr)
700{
701	struct hostent *phe;
702	static char buf[80];
703
704	phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
705	if (phe == NULL)
706		return( inet_ntoa(addr));
707	snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr));
708	return(buf);
709}
710
711/*
712 *			P R _ P A C K
713 *
714 * Print out the packet, if it came from us.  This logic is necessary
715 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
716 * which arrive ('tis only fair).  This permits multiple copies of this
717 * program to be run without having intermingled output (or statistics!).
718 */
719void
720pr_pack(char *buf, int cc, struct sockaddr_in *from)
721{
722	struct iphdr *ip;
723	struct icmphdr *icp;
724	int i;
725	int hlen;
726
727	ip = (struct iphdr *) ALLIGN(buf);
728	hlen = ip->ihl << 2;
729	if (cc < hlen + 8) {
730		if (verbose)
731			logtrace("packet too short (%d bytes) from %s\n", cc,
732				 pr_name(from->sin_addr));
733		return;
734	}
735	cc -= hlen;
736	icp = (struct icmphdr *)ALLIGN(buf + hlen);
737
738	switch (icp->type) {
739	case ICMP_ROUTER_ADVERTISEMENT:
740	{
741		struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
742		struct icmp_ra_addr *ap;
743
744#ifdef RDISC_SERVER
745		if (responder)
746			break;
747#endif
748
749		/* TBD verify that the link is multicast or broadcast */
750		/* XXX Find out the link it came in over? */
751		if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
752			if (verbose)
753				logtrace("ICMP %s from %s: Bad checksum\n",
754					 pr_type((int)rap->icmp_type),
755					 pr_name(from->sin_addr));
756			return;
757		}
758		if (rap->icmp_code != 0) {
759			if (verbose)
760				logtrace("ICMP %s from %s: Code = %d\n",
761					 pr_type((int)rap->icmp_type),
762					 pr_name(from->sin_addr),
763					 rap->icmp_code);
764			return;
765		}
766		if (rap->icmp_num_addrs < 1) {
767			if (verbose)
768				logtrace("ICMP %s from %s: No addresses\n",
769					 pr_type((int)rap->icmp_type),
770					 pr_name(from->sin_addr));
771			return;
772		}
773		if (rap->icmp_wpa < 2) {
774			if (verbose)
775				logtrace("ICMP %s from %s: Words/addr = %d\n",
776					 pr_type((int)rap->icmp_type),
777					 pr_name(from->sin_addr),
778					 rap->icmp_wpa);
779			return;
780		}
781		if ((unsigned)cc <
782		    8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
783			if (verbose)
784				logtrace("ICMP %s from %s: Too short %d, %d\n",
785					      pr_type((int)rap->icmp_type),
786					      pr_name(from->sin_addr),
787					      cc,
788					      8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
789			return;
790		}
791
792		if (verbose)
793			logtrace("ICMP %s from %s, lifetime %d\n",
794				      pr_type((int)rap->icmp_type),
795				      pr_name(from->sin_addr),
796				      ntohs(rap->icmp_lifetime));
797
798		/* Check that at least one router address is a neighboor
799		 * on the arriving link.
800		 */
801		for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
802			struct in_addr ina;
803			ap = (struct icmp_ra_addr *)
804				ALLIGN(buf + hlen + 8 +
805				       i * rap->icmp_wpa * 4);
806			ina.s_addr = ap->ira_addr;
807			if (verbose)
808				logtrace("\taddress %s, preference 0x%x\n",
809					      pr_name(ina),
810					      (unsigned int)ntohl(ap->ira_preference));
811			if (is_directly_connected(ina))
812				record_router(ina,
813					      ntohl(ap->ira_preference),
814					      ntohs(rap->icmp_lifetime));
815		}
816		nreceived++;
817		if (!forever) {
818			do_fork();
819			forever = 1;
820/*
821 * The next line was added so that the alarm is set for the new procces
822 * Fraser Gardiner Sun Microsystems Australia
823 */
824			(void) alarm(TIMER_INTERVAL);
825		}
826		break;
827	}
828
829#ifdef RDISC_SERVER
830	case ICMP_ROUTER_SOLICITATION:
831	{
832		struct sockaddr_in sin;
833
834		if (!responder)
835			break;
836
837		/* TBD verify that the link is multicast or broadcast */
838		/* XXX Find out the link it came in over? */
839
840		if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
841			if (verbose)
842				logtrace("ICMP %s from %s: Bad checksum\n",
843					      pr_type((int)icp->type),
844					      pr_name(from->sin_addr));
845			return;
846		}
847		if (icp->code != 0) {
848			if (verbose)
849				logtrace("ICMP %s from %s: Code = %d\n",
850					      pr_type((int)icp->type),
851					      pr_name(from->sin_addr),
852					      icp->code);
853			return;
854		}
855
856		if (cc < ICMP_MINLEN) {
857			if (verbose)
858				logtrace("ICMP %s from %s: Too short %d, %d\n",
859					      pr_type((int)icp->type),
860					      pr_name(from->sin_addr),
861					      cc,
862					      ICMP_MINLEN);
863			return;
864		}
865
866		if (verbose)
867			logtrace("ICMP %s from %s\n",
868				      pr_type((int)icp->type),
869				      pr_name(from->sin_addr));
870
871		/* Check that ip_src is either a neighboor
872		 * on the arriving link or 0.
873		 */
874		sin.sin_family = AF_INET;
875		if (ip->saddr == 0) {
876			/* If it was sent to the broadcast address we respond
877			 * to the broadcast address.
878			 */
879			if (IN_CLASSD(ntohl(ip->daddr)))
880				sin.sin_addr.s_addr = htonl(0xe0000001);
881			else
882				sin.sin_addr.s_addr = INADDR_BROADCAST;
883			/* Restart the timer when we broadcast */
884			left_until_advertise = min_adv_int +
885				((max_adv_int - min_adv_int)
886				 * (random() % 1000)/1000);
887		} else {
888			sin.sin_addr.s_addr = ip->saddr;
889			if (!is_directly_connected(sin.sin_addr)) {
890				if (verbose)
891					logtrace("ICMP %s from %s: source not directly connected\n",
892						      pr_type((int)icp->type),
893						      pr_name(from->sin_addr));
894				break;
895			}
896		}
897		nreceived++;
898		ntransmitted++;
899		advertise(&sin, lifetime);
900		break;
901	}
902#endif
903	}
904}
905
906
907/*
908 *			I N _ C K S U M
909 *
910 * Checksum routine for Internet Protocol family headers (C Version)
911 *
912 */
913#if BYTE_ORDER == LITTLE_ENDIAN
914# define ODDBYTE(v)	(v)
915#elif BYTE_ORDER == BIG_ENDIAN
916# define ODDBYTE(v)	((u_short)(v) << 8)
917#else
918# define ODDBYTE(v)	htons((u_short)(v) << 8)
919#endif
920
921u_short in_cksum(u_short *addr, int len)
922{
923	register int nleft = len;
924	register u_short *w = addr;
925	register u_short answer;
926	register int sum = 0;
927
928	/*
929	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
930	 *  we add sequential 16 bit words to it, and at the end, fold
931	 *  back all the carry bits from the top 16 bits into the lower
932	 *  16 bits.
933	 */
934	while( nleft > 1 )  {
935		sum += *w++;
936		nleft -= 2;
937	}
938
939	/* mop up an odd byte, if necessary */
940	if( nleft == 1 )
941		sum += ODDBYTE(*(u_char *)w);	/* le16toh() may be unavailable on old systems */
942
943	/*
944	 * add back carry outs from top 16 bits to low 16 bits
945	 */
946	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
947	sum += (sum >> 16);			/* add carry */
948	answer = ~sum;				/* truncate to 16 bits */
949	return (answer);
950}
951
952/*
953 *			F I N I S H
954 *
955 * Print out statistics, and give up.
956 * Heavily buffered STDIO is used here, so that all the statistics
957 * will be written with 1 sys-write call.  This is nice when more
958 * than one copy of the program is running on a terminal;  it prevents
959 * the statistics output from becomming intermingled.
960 */
961void
962finish()
963{
964#ifdef RDISC_SERVER
965	if (responder) {
966		/* Send out a packet with a preference so that all
967		 * hosts will know that we are dead.
968		 *
969		 * Wrong comment, wrong code.
970		 *	ttl must be set to 0 instead. --ANK
971		 */
972		logerr("terminated\n");
973		ntransmitted++;
974		advertise(&whereto, 0);
975	}
976#endif
977	logtrace("\n----%s rdisc Statistics----\n", sendaddress );
978	logtrace("%d packets transmitted, ", ntransmitted );
979	logtrace("%d packets received, ", nreceived );
980	logtrace("\n");
981	(void) fflush(stdout);
982	exit(0);
983}
984
985void
986graceful_finish()
987{
988	discard_table();
989	finish();
990	exit(0);
991}
992
993
994/* From libc/rpc/pmap_rmt.c */
995
996int
997sendbcast(int s, char *packet, int packetlen)
998{
999	int i, cc;
1000
1001	for (i = 0; i < num_interfaces; i++) {
1002		if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1003			continue;
1004		cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
1005		if (cc!= packetlen) {
1006			return (cc);
1007		}
1008	}
1009	return (packetlen);
1010}
1011
1012int
1013sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
1014{
1015	int on;
1016	int cc;
1017	struct sockaddr_in baddr;
1018
1019	baddr.sin_family = AF_INET;
1020	baddr.sin_addr = ifp->bcastaddr;
1021	if (debug)
1022		logdebug("Broadcast to %s\n",
1023			 pr_name(baddr.sin_addr));
1024	on = 1;
1025	setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1026	cc = sendto(s, packet, packetlen, 0,
1027		    (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1028	if (cc!= packetlen) {
1029		logperror("sendbcast: sendto");
1030		logerr("Cannot send broadcast packet to %s\n",
1031		       pr_name(baddr.sin_addr));
1032	}
1033	on = 0;
1034	setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1035	return (cc);
1036}
1037
1038int
1039sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1040{
1041	int i, cc;
1042
1043	for (i = 0; i < num_interfaces; i++) {
1044		if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
1045			continue;
1046		cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
1047		if (cc!= packetlen) {
1048			return (cc);
1049		}
1050	}
1051	return (packetlen);
1052}
1053
1054int
1055sendmcastif(int s, char *packet, int packetlen,	struct sockaddr_in *sin,
1056	    struct interface *ifp)
1057{
1058	int cc;
1059	struct ip_mreqn mreq;
1060
1061	memset(&mreq, 0, sizeof(mreq));
1062	mreq.imr_ifindex = ifp->ifindex;
1063	mreq.imr_address = ifp->localaddr;
1064	if (debug)
1065		logdebug("Multicast to interface %s, %s\n",
1066			 ifp->name,
1067			 pr_name(mreq.imr_address));
1068	if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1069		       (char *)&mreq,
1070		       sizeof(mreq)) < 0) {
1071		logperror("setsockopt (IP_MULTICAST_IF)");
1072		logerr("Cannot send multicast packet over interface %s, %s\n",
1073		       ifp->name,
1074		       pr_name(mreq.imr_address));
1075		return (-1);
1076	}
1077	cc = sendto(s, packet, packetlen, 0,
1078		    (struct sockaddr *)sin, sizeof (struct sockaddr));
1079	if (cc!= packetlen) {
1080		logperror("sendmcast: sendto");
1081		logerr("Cannot send multicast packet over interface %s, %s\n",
1082		       ifp->name, pr_name(mreq.imr_address));
1083	}
1084	return (cc);
1085}
1086
1087void
1088init()
1089{
1090	initifs();
1091#ifdef RDISC_SERVER
1092	{
1093		int i;
1094		for (i = 0; i < interfaces_size; i++)
1095			interfaces[i].preference = preference;
1096	}
1097#endif
1098}
1099
1100void
1101initifs()
1102{
1103	int	sock;
1104	struct ifconf ifc;
1105	struct ifreq ifreq, *ifr;
1106	struct sockaddr_in *sin;
1107	int n, i;
1108	char *buf;
1109	int numifs;
1110	unsigned bufsize;
1111
1112	sock = socket(AF_INET, SOCK_DGRAM, 0);
1113	if (sock < 0) {
1114		logperror("initifs: socket");
1115		return;
1116	}
1117#ifdef SIOCGIFNUM
1118	if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1119		numifs = MAXIFS;
1120	}
1121#else
1122	numifs = MAXIFS;
1123#endif
1124	bufsize = numifs * sizeof(struct ifreq);
1125	buf = (char *)malloc(bufsize);
1126	if (buf == NULL) {
1127		logerr("out of memory\n");
1128		(void) close(sock);
1129		return;
1130	}
1131	if (interfaces != NULL)
1132		(void) free(interfaces);
1133	interfaces = (struct interface *)ALLIGN(malloc(numifs *
1134					sizeof(struct interface)));
1135	if (interfaces == NULL) {
1136		logerr("out of memory\n");
1137		(void) close(sock);
1138		(void) free(buf);
1139		return;
1140	}
1141	interfaces_size = numifs;
1142
1143	ifc.ifc_len = bufsize;
1144	ifc.ifc_buf = buf;
1145	if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1146		logperror("initifs: ioctl (get interface configuration)");
1147		(void) close(sock);
1148		(void) free(buf);
1149		return;
1150	}
1151	ifr = ifc.ifc_req;
1152	for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1153		ifreq = *ifr;
1154		if (strlen(ifreq.ifr_name) >= IFNAMSIZ)
1155			continue;
1156		if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
1157			logperror("initifs: ioctl (get interface flags)");
1158			continue;
1159		}
1160		if (ifr->ifr_addr.sa_family != AF_INET)
1161			continue;
1162		if ((ifreq.ifr_flags & IFF_UP) == 0)
1163			continue;
1164		if (ifreq.ifr_flags & IFF_LOOPBACK)
1165			continue;
1166		if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1167			continue;
1168		strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
1169
1170		sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
1171		interfaces[i].localaddr = sin->sin_addr;
1172		interfaces[i].flags = ifreq.ifr_flags;
1173		interfaces[i].netmask.s_addr = (__u32)0xffffffff;
1174		if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
1175			logperror("initifs: ioctl (get ifindex)");
1176			continue;
1177		}
1178		interfaces[i].ifindex = ifreq.ifr_ifindex;
1179		if (ifreq.ifr_flags & IFF_POINTOPOINT) {
1180			if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1181				logperror("initifs: ioctl (get destination addr)");
1182				continue;
1183			}
1184			sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1185			/* A pt-pt link is identified by the remote address */
1186			interfaces[i].address = sin->sin_addr;
1187			interfaces[i].remoteaddr = sin->sin_addr;
1188			/* Simulate broadcast for pt-pt */
1189			interfaces[i].bcastaddr = sin->sin_addr;
1190			interfaces[i].flags |= IFF_BROADCAST;
1191		} else {
1192			/* Non pt-pt links are identified by the local address */
1193			interfaces[i].address = interfaces[i].localaddr;
1194			interfaces[i].remoteaddr = interfaces[i].address;
1195			if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1196				logperror("initifs: ioctl (get netmask)");
1197				continue;
1198			}
1199			sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1200			interfaces[i].netmask = sin->sin_addr;
1201			if (ifreq.ifr_flags & IFF_BROADCAST) {
1202				if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1203					logperror("initifs: ioctl (get broadcast address)");
1204					continue;
1205				}
1206				sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1207				interfaces[i].bcastaddr = sin->sin_addr;
1208			}
1209		}
1210#ifdef notdef
1211		if (debug)
1212			logdebug("Found interface %s, flags 0x%x\n",
1213				 pr_name(interfaces[i].localaddr),
1214				 interfaces[i].flags);
1215#endif
1216		i++;
1217	}
1218	num_interfaces = i;
1219#ifdef notdef
1220	if (debug)
1221		logdebug("Found %d interfaces\n", num_interfaces);
1222#endif
1223	(void) close(sock);
1224	(void) free(buf);
1225}
1226
1227int
1228join(int sock, struct sockaddr_in *sin)
1229{
1230	int i, j;
1231	struct ip_mreqn mreq;
1232	int joined[num_interfaces];
1233
1234	memset(joined, 0, sizeof(joined));
1235
1236	if (isbroadcast(sin))
1237		return (0);
1238
1239	mreq.imr_multiaddr = sin->sin_addr;
1240	for (i = 0; i < num_interfaces; i++) {
1241		for (j = 0; j < i; j++) {
1242			if (joined[j] == interfaces[i].ifindex)
1243				break;
1244		}
1245		if (j != i)
1246			continue;
1247
1248		mreq.imr_ifindex = interfaces[i].ifindex;
1249		mreq.imr_address.s_addr = 0;
1250
1251		if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1252			       (char *)&mreq, sizeof(mreq)) < 0) {
1253			logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1254			return (-1);
1255		}
1256
1257		joined[i] = interfaces[i].ifindex;
1258	}
1259	return (0);
1260}
1261
1262int support_multicast()
1263{
1264	int sock;
1265	u_char ttl = 1;
1266
1267	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1268	if (sock < 0) {
1269		logperror("support_multicast: socket");
1270		return (0);
1271	}
1272
1273	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1274		       (char *)&ttl, sizeof(ttl)) < 0) {
1275		(void) close(sock);
1276		return (0);
1277	}
1278	(void) close(sock);
1279	return (1);
1280}
1281
1282int
1283is_directly_connected(struct in_addr in)
1284{
1285	int i;
1286
1287	for (i = 0; i < num_interfaces; i++) {
1288		/* Check that the subnetwork numbers match */
1289
1290		if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
1291		    (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
1292			return (1);
1293	}
1294	return (0);
1295}
1296
1297/*
1298 * TABLES
1299 */
1300struct table {
1301	struct in_addr	router;
1302	int		preference;
1303	int		remaining_time;
1304	int		in_kernel;
1305	struct table	*next;
1306};
1307
1308struct table *table;
1309
1310struct table *
1311find_router(struct in_addr addr)
1312{
1313	struct table *tp;
1314
1315	tp = table;
1316	while (tp) {
1317		if (tp->router.s_addr == addr.s_addr)
1318			return (tp);
1319		tp = tp->next;
1320	}
1321	return (NULL);
1322}
1323
1324int max_preference(void)
1325{
1326	struct table *tp;
1327	int max = (int)INELIGIBLE_PREF;
1328
1329	tp = table;
1330	while (tp) {
1331		if (tp->preference > max)
1332			max = tp->preference;
1333		tp = tp->next;
1334	}
1335	return (max);
1336}
1337
1338
1339/* Note: this might leave the kernel with no default route for a short time. */
1340void
1341age_table(int time)
1342{
1343	struct table **tpp, *tp;
1344	int recalculate_max = 0;
1345	int max = max_preference();
1346
1347	tpp = &table;
1348	while (*tpp != NULL) {
1349		tp = *tpp;
1350		tp->remaining_time -= time;
1351		if (tp->remaining_time <= 0) {
1352			*tpp = tp->next;
1353			if (tp->in_kernel)
1354				del_route(tp->router);
1355			if (best_preference &&
1356			    tp->preference == max)
1357				recalculate_max++;
1358			free((char *)tp);
1359		} else {
1360			tpp = &tp->next;
1361		}
1362	}
1363	if (recalculate_max) {
1364		int max = max_preference();
1365
1366		if (max != INELIGIBLE_PREF) {
1367			tp = table;
1368			while (tp) {
1369				if (tp->preference == max && !tp->in_kernel) {
1370					add_route(tp->router);
1371					tp->in_kernel++;
1372				}
1373				tp = tp->next;
1374			}
1375		}
1376	}
1377}
1378
1379void discard_table(void)
1380{
1381	struct table **tpp, *tp;
1382
1383	tpp = &table;
1384	while (*tpp != NULL) {
1385		tp = *tpp;
1386		*tpp = tp->next;
1387		if (tp->in_kernel)
1388			del_route(tp->router);
1389		free((char *)tp);
1390	}
1391}
1392
1393
1394void
1395record_router(struct in_addr router, int preference, int ttl)
1396{
1397	struct table *tp;
1398	int old_max = max_preference();
1399	int changed_up = 0;	/* max preference could have increased */
1400	int changed_down = 0;	/* max preference could have decreased */
1401
1402	if (ttl < 4)
1403		preference = INELIGIBLE_PREF;
1404
1405	if (debug)
1406		logdebug("Recording %s, ttl %d, preference 0x%x\n",
1407			 pr_name(router),
1408			 ttl,
1409			 preference);
1410	tp = find_router(router);
1411	if (tp) {
1412		if (tp->preference > preference &&
1413		    tp->preference == old_max)
1414			changed_down++;
1415		else if (preference > tp->preference)
1416			changed_up++;
1417		tp->preference = preference;
1418		tp->remaining_time = ttl;
1419	} else {
1420		if (preference > old_max)
1421			changed_up++;
1422		tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
1423		if (tp == NULL) {
1424			logerr("Out of memory\n");
1425			return;
1426		}
1427		tp->router = router;
1428		tp->preference = preference;
1429		tp->remaining_time = ttl;
1430		tp->in_kernel = 0;
1431		tp->next = table;
1432		table = tp;
1433	}
1434	if (!tp->in_kernel &&
1435	    (!best_preference || tp->preference == max_preference()) &&
1436	    tp->preference != INELIGIBLE_PREF) {
1437		add_route(tp->router);
1438		tp->in_kernel++;
1439	}
1440	if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) {
1441		del_route(tp->router);
1442		tp->in_kernel = 0;
1443	}
1444	if (best_preference && changed_down) {
1445		/* Check if we should add routes */
1446		int new_max = max_preference();
1447		if (new_max != INELIGIBLE_PREF) {
1448			tp = table;
1449			while (tp) {
1450				if (tp->preference == new_max &&
1451				    !tp->in_kernel) {
1452					add_route(tp->router);
1453					tp->in_kernel++;
1454				}
1455				tp = tp->next;
1456			}
1457		}
1458	}
1459	if (best_preference && (changed_up || changed_down)) {
1460		/* Check if we should remove routes already in the kernel */
1461		int new_max = max_preference();
1462		tp = table;
1463		while (tp) {
1464			if (tp->preference < new_max && tp->in_kernel) {
1465				del_route(tp->router);
1466				tp->in_kernel = 0;
1467			}
1468			tp = tp->next;
1469		}
1470	}
1471}
1472
1473void
1474add_route(struct in_addr addr)
1475{
1476	if (debug)
1477		logdebug("Add default route to %s\n", pr_name(addr));
1478	rtioctl(addr, SIOCADDRT);
1479}
1480
1481void
1482del_route(struct in_addr addr)
1483{
1484	if (debug)
1485		logdebug("Delete default route to %s\n", pr_name(addr));
1486	rtioctl(addr, SIOCDELRT);
1487}
1488
1489void
1490rtioctl(struct in_addr addr, int op)
1491{
1492	int sock;
1493	struct rtentry rt;
1494	struct sockaddr_in *sin;
1495
1496	memset((char *)&rt, 0, sizeof(struct rtentry));
1497	rt.rt_dst.sa_family = AF_INET;
1498	rt.rt_gateway.sa_family = AF_INET;
1499	rt.rt_genmask.sa_family = AF_INET;
1500	sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
1501	sin->sin_addr = addr;
1502	rt.rt_flags = RTF_UP | RTF_GATEWAY;
1503
1504	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1505	if (sock < 0) {
1506		logperror("rtioctl: socket");
1507		return;
1508	}
1509	if (ioctl(sock, op, (char *)&rt) < 0) {
1510		if (!(op == SIOCADDRT && errno == EEXIST))
1511			logperror("ioctl (add/delete route)");
1512	}
1513	(void) close(sock);
1514}
1515
1516/*
1517 * LOGGER
1518 */
1519
1520void initlog(void)
1521{
1522	logging++;
1523	openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1524}
1525
1526
1527void
1528logperror(char *str)
1529{
1530	if (logging)
1531		syslog(LOG_ERR, "%s: %m", str);
1532	else
1533		(void) fprintf(stderr, "%s: %s\n", str, strerror(errno));
1534}
1535