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 
66 struct 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 
87 static int join(int sock, struct sockaddr_in *sin);
88 static void solicitor(struct sockaddr_in *);
89 #ifdef RDISC_SERVER
90 static void advertise(struct sockaddr_in *, int lft);
91 #endif
92 static char *pr_name(struct in_addr addr);
93 static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
94 static void age_table(int time);
95 static void record_router(struct in_addr router, int preference, int ttl);
96 static void add_route(struct in_addr addr);
97 static void del_route(struct in_addr addr);
98 static void rtioctl(struct in_addr addr, int op);
99 static int support_multicast(void);
100 static int sendbcast(int s, char *packet, int packetlen);
101 static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
102 static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
103 static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
104 static int is_directly_connected(struct in_addr in);
105 static void initlog(void);
106 static void discard_table(void);
107 static 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 */
119 struct 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 
129 struct 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 */
153 static int num_interfaces;
154 
155 static struct interface *interfaces;
156 static int interfaces_size;			/* Number of elements in interfaces */
157 
158 
159 #define	MAXPACKET	4096	/* max packet size */
160 
161 /* fraser */
162 int debugfile;
163 
164 const 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 
173 int s;			/* Socket file descriptor */
174 struct sockaddr_in whereto;/* Address to send to */
175 
176 /* Common variables */
177 int verbose = 0;
178 int debug = 0;
179 int trace = 0;
180 int solicit = 0;
181 int ntransmitted = 0;
182 int nreceived = 0;
183 int forever = 0;	/* Never give up on host. If 0 defer fork until
184 			 * first response.
185 			 */
186 
187 #ifdef RDISC_SERVER
188 /* Router variables */
189 int responder;
190 int max_adv_int = MAX_ADV_INT;
191 int min_adv_int;
192 int lifetime;
193 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
194 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
195 int preference = 0;		/* Setable with -p option */
196 #endif
197 
198 /* Host variables */
199 int max_solicitations = MAX_SOLICITATIONS;
200 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
201 int 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 
206 static void graceful_finish(void);
207 static void finish(void);
208 static void timer(void);
209 static void initifs(void);
210 static u_short in_cksum(u_short *addr, int len);
211 
212 static 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); })
220 static void logperror(char *str);
221 
isbroadcast(struct sockaddr_in * sin)222 static __inline__ int isbroadcast(struct sockaddr_in *sin)
223 {
224 	return (sin->sin_addr.s_addr == INADDR_BROADCAST);
225 }
226 
ismulticast(struct sockaddr_in * sin)227 static __inline__ int ismulticast(struct sockaddr_in *sin)
228 {
229 	return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
230 }
231 
prusage(void)232 static void prusage(void)
233 {
234 	fputs(usage, stderr);
235 	exit(1);
236 }
237 
do_fork(void)238 void 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 
signal_setup(int signo,void (* handler)(void))268 void 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  */
284 char    *sendaddress, *recvaddress;
285 
main(int argc,char ** argv)286 int 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
368 next:
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 
486 static int left_until_advertise;
487 
488 /* Called every TIMER_INTERVAL */
timer()489 void 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  */
539 void
solicitor(struct sockaddr_in * sin)540 solicitor(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  */
583 void
advertise(struct sockaddr_in * sin,int lft)584 advertise(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  */
663 char *
pr_type(int t)664 pr_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  */
pr_name(struct in_addr addr)699 char *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  */
719 void
pr_pack(char * buf,int cc,struct sockaddr_in * from)720 pr_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 
in_cksum(u_short * addr,int len)921 u_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  */
961 void
finish()962 finish()
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 
985 void
graceful_finish()986 graceful_finish()
987 {
988 	discard_table();
989 	finish();
990 	exit(0);
991 }
992 
993 
994 /* From libc/rpc/pmap_rmt.c */
995 
996 int
sendbcast(int s,char * packet,int packetlen)997 sendbcast(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 
1012 int
sendbcastif(int s,char * packet,int packetlen,struct interface * ifp)1013 sendbcastif(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 
1038 int
sendmcast(int s,char * packet,int packetlen,struct sockaddr_in * sin)1039 sendmcast(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 
1054 int
sendmcastif(int s,char * packet,int packetlen,struct sockaddr_in * sin,struct interface * ifp)1055 sendmcastif(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 
1087 void
init()1088 init()
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 
1100 void
initifs()1101 initifs()
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 
1227 int
join(int sock,struct sockaddr_in * sin)1228 join(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 
support_multicast()1262 int 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 
1282 int
is_directly_connected(struct in_addr in)1283 is_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  */
1300 struct table {
1301 	struct in_addr	router;
1302 	int		preference;
1303 	int		remaining_time;
1304 	int		in_kernel;
1305 	struct table	*next;
1306 };
1307 
1308 struct table *table;
1309 
1310 struct table *
find_router(struct in_addr addr)1311 find_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 
max_preference(void)1324 int 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. */
1340 void
age_table(int time)1341 age_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 
discard_table(void)1379 void 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 
1394 void
record_router(struct in_addr router,int preference,int ttl)1395 record_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 
1473 void
add_route(struct in_addr addr)1474 add_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 
1481 void
del_route(struct in_addr addr)1482 del_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 
1489 void
rtioctl(struct in_addr addr,int op)1490 rtioctl(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 
initlog(void)1520 void initlog(void)
1521 {
1522 	logging++;
1523 	openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1524 }
1525 
1526 
1527 void
logperror(char * str)1528 logperror(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