Home | History | Annotate | Download | only in iputils
      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 
    222 static __inline__ int isbroadcast(struct sockaddr_in *sin)
    223 {
    224 	return (sin->sin_addr.s_addr == INADDR_BROADCAST);
    225 }
    226 
    227 static __inline__ int ismulticast(struct sockaddr_in *sin)
    228 {
    229 	return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
    230 }
    231 
    232 static void prusage(void)
    233 {
    234 	fputs(usage, stderr);
    235 	exit(1);
    236 }
    237 
    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 
    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 
    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 */
    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
    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
    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 *
    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  */
    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
    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 
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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 
   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
   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 *
   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 
   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
   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 
   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
   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
   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
   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
   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 
   1520 void initlog(void)
   1521 {
   1522 	logging++;
   1523 	openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
   1524 }
   1525 
   1526 
   1527 void
   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