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
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.
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.
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.
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.
24 * Sun Microsystems, Inc.
26 * Mountain View, California 94043
33 #include <sys/types.h>
35 /* Do not use "improved" glibc version! */
36 #include <linux/limits.h>
38 #include <sys/param.h>
39 #include <sys/socket.h>
43 #include <sys/ioctl.h>
45 #include <linux/route.h>
47 #include <netinet/in.h>
48 #include <netinet/ip.h>
49 #include <netinet/ip_icmp.h>
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
59 #include <arpa/inet.h>
68 struct in_addr address; /* Used to identify the interface */
69 struct in_addr localaddr; /* Actual address if the interface */
72 struct in_addr bcastaddr;
73 struct in_addr remoteaddr;
74 struct in_addr netmask;
82 * Use 255.255.255.255 for broadcasts - not the interface broadcast
86 #define ALLIGN(ptr) (ptr)
88 static int join(int sock, struct sockaddr_in *sin);
89 static void solicitor(struct sockaddr_in *);
91 static void advertise(struct sockaddr_in *, int lft);
93 static char *pr_name(struct in_addr addr);
94 static void pr_pack(char *buf, int cc, struct sockaddr_in *from);
95 static void age_table(int time);
96 static void record_router(struct in_addr router, int preference, int ttl);
97 static void add_route(struct in_addr addr);
98 static void del_route(struct in_addr addr);
99 static void rtioctl(struct in_addr addr, int op);
100 static int support_multicast(void);
101 static int sendbcast(int s, char *packet, int packetlen);
102 static int sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *);
103 static int sendbcastif(int s, char *packet, int packetlen, struct interface *ifp);
104 static int sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin, struct interface *ifp);
105 static int is_directly_connected(struct in_addr in);
106 static void initlog(void);
107 static void discard_table(void);
108 static void init(void);
110 #define ICMP_ROUTER_ADVERTISEMENT 9
111 #define ICMP_ROUTER_SOLICITATION 10
113 #define ALL_HOSTS_ADDRESS "224.0.0.1"
114 #define ALL_ROUTERS_ADDRESS "224.0.0.2"
118 #if !defined(__GLIBC__) || __GLIBC__ < 2
119 /* For router advertisement */
122 u_char icmp_type; /* type of message, see below */
123 u_char icmp_code; /* type sub code */
124 u_short icmp_cksum; /* ones complement cksum of struct */
125 u_char icmp_num_addrs;
126 u_char icmp_wpa; /* Words per address */
133 __u32 ira_preference;
139 /* Router constants */
140 #define MAX_INITIAL_ADVERT_INTERVAL 16
141 #define MAX_INITIAL_ADVERTISEMENTS 3
142 #define MAX_RESPONSE_DELAY 2 /* Not used */
145 #define MAX_SOLICITATIONS 3
146 #define SOLICITATION_INTERVAL 3
147 #define MAX_SOLICITATION_DELAY 1 /* Not used */
149 #define INELIGIBLE_PREF 0x80000000 /* Maximum negative */
151 #define MAX_ADV_INT 600
154 static int num_interfaces;
156 static struct interface *interfaces;
157 static int interfaces_size; /* Number of elements in interfaces */
160 #define MAXPACKET 4096 /* max packet size */
166 "Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
168 " rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>] \n"
169 " [send_address] [receive_address]\n"
174 int s; /* Socket file descriptor */
175 struct sockaddr_in whereto;/* Address to send to */
177 /* Common variables */
182 int ntransmitted = 0;
184 int forever = 0; /* Never give up on host. If 0 defer fork until
189 /* Router variables */
191 int max_adv_int = MAX_ADV_INT;
194 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
195 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
196 int preference = 0; /* Setable with -p option */
200 int max_solicitations = MAX_SOLICITATIONS;
201 unsigned int solicitation_interval = SOLICITATION_INTERVAL;
202 int best_preference = 1; /* Set to record only the router(s) with the
203 best preference in the kernel. Not set
204 puts all routes in the kernel. */
207 static void graceful_finish(void);
208 static void finish(void);
209 static void timer(void);
210 static void initifs(void);
211 static u_short in_cksum(u_short *addr, int len);
213 static int logging = 0;
215 #define logerr(fmt...) ({ if (logging) syslog(LOG_ERR, fmt); \
216 else fprintf(stderr, fmt); })
217 #define logtrace(fmt...) ({ if (logging) syslog(LOG_INFO, fmt); \
218 else fprintf(stderr, fmt); })
219 #define logdebug(fmt...) ({ if (logging) syslog(LOG_DEBUG, fmt); \
220 else fprintf(stderr, fmt); })
221 static void logperror(char *str);
223 static __inline__ int isbroadcast(struct sockaddr_in *sin)
225 return (sin->sin_addr.s_addr == INADDR_BROADCAST);
228 static __inline__ int ismulticast(struct sockaddr_in *sin)
230 return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
233 static void prusage(void)
235 (void) fprintf(stderr, usage);
247 if ((pid=fork()) != 0)
250 for (t = 0; t < OPEN_MAX; t++)
258 void signal_setup(int signo, void (*handler)(void))
262 memset(&sa, 0, sizeof(sa));
264 sa.sa_handler = (void (*)(int))handler;
266 sa.sa_flags = SA_INTERRUPT;
268 sigaction(signo, &sa, NULL);
274 char *sendaddress, *recvaddress;
276 int main(int argc, char **argv)
278 struct sockaddr_in from;
280 struct sockaddr_in *to = &whereto;
281 struct sockaddr_in joinaddr;
282 sigset_t sset, sset_empty;
286 min_adv_int =( max_adv_int * 3 / 4);
287 lifetime = (3*max_adv_int);
291 while (argc > 0 && *av[0] == '-') {
321 printf("rdisc utility, iputils-ss%s\n", SNAPSHOT);
327 val = strtol(av[0], (char **)NULL, 0);
328 if (val < 4 || val > 1800) {
329 (void) fprintf(stderr,
330 "Bad Max Advertizement Interval\n");
334 min_adv_int =( max_adv_int * 3 / 4);
335 lifetime = (3*max_adv_int);
344 val = strtol(av[0], (char **)NULL, 0);
363 if (support_multicast()) {
364 sendaddress = ALL_ROUTERS_ADDRESS;
367 sendaddress = ALL_HOSTS_ADDRESS;
370 sendaddress = "255.255.255.255";
377 if (support_multicast()) {
378 recvaddress = ALL_HOSTS_ADDRESS;
381 recvaddress = ALL_ROUTERS_ADDRESS;
384 recvaddress = "255.255.255.255";
390 (void) fprintf(stderr, "Extra paramaters\n");
396 if (solicit && responder) {
402 if (!(solicit && !forever)) {
405 * Added the next line to stop forking a second time
406 * Fraser Gardiner - Sun Microsystems Australia
411 bzero( (char *)&whereto, sizeof(struct sockaddr_in) );
412 to->sin_family = AF_INET;
413 to->sin_addr.s_addr = inet_addr(sendaddress);
415 bzero( (char *)&joinaddr, sizeof(struct sockaddr_in) );
416 joinaddr.sin_family = AF_INET;
417 joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
421 srandom((int)gethostid());
424 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
429 setlinebuf( stdout );
431 signal_setup(SIGINT, finish );
432 signal_setup(SIGTERM, graceful_finish );
433 signal_setup(SIGHUP, initifs );
434 signal_setup(SIGALRM, timer );
437 sigemptyset(&sset_empty);
438 sigaddset(&sset, SIGALRM);
439 sigaddset(&sset, SIGHUP);
440 sigaddset(&sset, SIGTERM);
441 sigaddset(&sset, SIGINT);
444 if (join(s, &joinaddr) < 0) {
445 logerr("Failed joining addresses\n");
449 timer(); /* start things going */
452 u_char packet[MAXPACKET];
453 int len = sizeof (packet);
454 socklen_t fromlen = sizeof (from);
457 cc=recvfrom(s, (char *)packet, len, 0,
458 (struct sockaddr *)&from, &fromlen);
462 logperror("recvfrom");
466 sigprocmask(SIG_SETMASK, &sset, NULL);
467 pr_pack( (char *)packet, cc, &from );
468 sigprocmask(SIG_SETMASK, &sset_empty, NULL);
473 #define TIMER_INTERVAL 3
474 #define GETIFCONF_TIMER 30
476 static int left_until_advertise;
478 /* Called every TIMER_INTERVAL */
482 static int left_until_getifconf;
483 static int left_until_solicit;
486 time += TIMER_INTERVAL;
488 left_until_getifconf -= TIMER_INTERVAL;
489 left_until_advertise -= TIMER_INTERVAL;
490 left_until_solicit -= TIMER_INTERVAL;
492 if (left_until_getifconf < 0) {
494 left_until_getifconf = GETIFCONF_TIMER;
497 if (responder && left_until_advertise <= 0) {
499 advertise(&whereto, lifetime);
500 if (ntransmitted < initial_advertisements)
501 left_until_advertise = initial_advert_interval;
503 left_until_advertise = min_adv_int +
504 ((max_adv_int - min_adv_int) *
505 (random() % 1000)/1000);
508 if (solicit && left_until_solicit <= 0) {
511 if (ntransmitted < max_solicitations)
512 left_until_solicit = solicitation_interval;
515 if (!forever && nreceived == 0)
519 age_table(TIMER_INTERVAL);
520 alarm(TIMER_INTERVAL);
526 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
527 * The IP packet will be added on by the kernel.
530 solicitor(struct sockaddr_in *sin)
532 static u_char outpack[MAXPACKET];
533 struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
537 logtrace("Sending solicitation to %s\n",
538 pr_name(sin->sin_addr));
540 icp->type = ICMP_ROUTER_SOLICITATION;
543 icp->un.gateway = 0; /* Reserved */
546 /* Compute ICMP checksum here */
547 icp->checksum = in_cksum( (u_short *)icp, packetlen );
549 if (isbroadcast(sin))
550 i = sendbcast(s, (char *)outpack, packetlen);
551 else if (ismulticast(sin))
552 i = sendmcast(s, (char *)outpack, packetlen, sin);
554 i = sendto( s, (char *)outpack, packetlen, 0,
555 (struct sockaddr *)sin, sizeof(struct sockaddr));
557 if( i < 0 || i != packetlen ) {
559 logperror("solicitor:sendto");
561 logerr("wrote %s %d chars, ret=%d\n",
562 sendaddress, packetlen, i );
570 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
571 * The IP packet will be added on by the kernel.
574 advertise(struct sockaddr_in *sin, int lft)
576 static u_char outpack[MAXPACKET];
577 struct icmp_ra *rap = (struct icmp_ra *) ALLIGN(outpack);
578 struct icmp_ra_addr *ap;
579 int packetlen, i, cc;
582 logtrace("Sending advertisement to %s\n",
583 pr_name(sin->sin_addr));
586 for (i = 0; i < num_interfaces; i++) {
587 rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
590 rap->icmp_num_addrs = 0;
592 rap->icmp_lifetime = htons(lft);
596 * TODO handle multiple logical interfaces per
597 * physical interface. (increment with rap->icmp_wpa * 4 for
600 ap = (struct icmp_ra_addr *)ALLIGN(outpack + ICMP_MINLEN);
601 ap->ira_addr = interfaces[i].localaddr.s_addr;
602 ap->ira_preference = htonl(interfaces[i].preference);
603 packetlen += rap->icmp_wpa * 4;
604 rap->icmp_num_addrs++;
606 /* Compute ICMP checksum here */
607 rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
609 if (isbroadcast(sin))
610 cc = sendbcastif(s, (char *)outpack, packetlen,
612 else if (ismulticast(sin))
613 cc = sendmcastif( s, (char *)outpack, packetlen, sin,
616 struct interface *ifp = &interfaces[i];
618 * Verify that the interface matches the destination
621 if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
622 (ifp->address.s_addr & ifp->netmask.s_addr)) {
624 logdebug("Unicast to %s ",
625 pr_name(sin->sin_addr));
626 logdebug("on interface %s, %s\n",
628 pr_name(ifp->address));
630 cc = sendto( s, (char *)outpack, packetlen, 0,
631 (struct sockaddr *)sin,
632 sizeof(struct sockaddr));
636 if( cc < 0 || cc != packetlen ) {
640 logerr("wrote %s %d chars, ret=%d\n",
641 sendaddress, packetlen, cc );
651 * Convert an ICMP "type" field to a printable string.
656 static char *ttab[] = {
667 "Router Solicitation",
678 if ( t < 0 || t > 16 )
679 return("OUT-OF-RANGE");
687 * Return a string name for the given IP address.
689 char *pr_name(struct in_addr addr)
694 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
696 return( inet_ntoa(addr));
697 snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr));
704 * Print out the packet, if it came from us. This logic is necessary
705 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
706 * which arrive ('tis only fair). This permits multiple copies of this
707 * program to be run without having intermingled output (or statistics!).
710 pr_pack(char *buf, int cc, struct sockaddr_in *from)
717 ip = (struct iphdr *) ALLIGN(buf);
721 logtrace("packet too short (%d bytes) from %s\n", cc,
722 pr_name(from->sin_addr));
726 icp = (struct icmphdr *)ALLIGN(buf + hlen);
729 case ICMP_ROUTER_ADVERTISEMENT:
731 struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
732 struct icmp_ra_addr *ap;
739 /* TBD verify that the link is multicast or broadcast */
740 /* XXX Find out the link it came in over? */
741 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
743 logtrace("ICMP %s from %s: Bad checksum\n",
744 pr_type((int)rap->icmp_type),
745 pr_name(from->sin_addr));
748 if (rap->icmp_code != 0) {
750 logtrace("ICMP %s from %s: Code = %d\n",
751 pr_type((int)rap->icmp_type),
752 pr_name(from->sin_addr),
756 if (rap->icmp_num_addrs < 1) {
758 logtrace("ICMP %s from %s: No addresses\n",
759 pr_type((int)rap->icmp_type),
760 pr_name(from->sin_addr));
763 if (rap->icmp_wpa < 2) {
765 logtrace("ICMP %s from %s: Words/addr = %d\n",
766 pr_type((int)rap->icmp_type),
767 pr_name(from->sin_addr),
772 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
774 logtrace("ICMP %s from %s: Too short %d, %d\n",
775 pr_type((int)rap->icmp_type),
776 pr_name(from->sin_addr),
778 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
783 logtrace("ICMP %s from %s, lifetime %d\n",
784 pr_type((int)rap->icmp_type),
785 pr_name(from->sin_addr),
786 ntohs(rap->icmp_lifetime));
788 /* Check that at least one router address is a neighboor
789 * on the arriving link.
791 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
793 ap = (struct icmp_ra_addr *)
794 ALLIGN(buf + hlen + 8 +
795 i * rap->icmp_wpa * 4);
796 ina.s_addr = ap->ira_addr;
798 logtrace("\taddress %s, preference 0x%x\n",
800 (unsigned int)ntohl(ap->ira_preference));
801 if (is_directly_connected(ina))
803 ntohl(ap->ira_preference),
804 ntohs(rap->icmp_lifetime));
811 * The next line was added so that the alarm is set for the new procces
812 * Fraser Gardiner Sun Microsystems Australia
814 (void) alarm(TIMER_INTERVAL);
820 case ICMP_ROUTER_SOLICITATION:
822 struct sockaddr_in sin;
827 /* TBD verify that the link is multicast or broadcast */
828 /* XXX Find out the link it came in over? */
830 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
832 logtrace("ICMP %s from %s: Bad checksum\n",
833 pr_type((int)icp->type),
834 pr_name(from->sin_addr));
837 if (icp->code != 0) {
839 logtrace("ICMP %s from %s: Code = %d\n",
840 pr_type((int)icp->type),
841 pr_name(from->sin_addr),
846 if (cc < ICMP_MINLEN) {
848 logtrace("ICMP %s from %s: Too short %d, %d\n",
849 pr_type((int)icp->type),
850 pr_name(from->sin_addr),
857 logtrace("ICMP %s from %s\n",
858 pr_type((int)icp->type),
859 pr_name(from->sin_addr));
861 /* Check that ip_src is either a neighboor
862 * on the arriving link or 0.
864 sin.sin_family = AF_INET;
865 if (ip->saddr == 0) {
866 /* If it was sent to the broadcast address we respond
867 * to the broadcast address.
869 if (IN_CLASSD(ntohl(ip->daddr)))
870 sin.sin_addr.s_addr = htonl(0xe0000001);
872 sin.sin_addr.s_addr = INADDR_BROADCAST;
873 /* Restart the timer when we broadcast */
874 left_until_advertise = min_adv_int +
875 ((max_adv_int - min_adv_int)
876 * (random() % 1000)/1000);
878 sin.sin_addr = ip->saddr;
879 if (!is_directly_connected(sin.sin_addr)) {
881 logtrace("ICMP %s from %s: source not directly connected\n",
882 pr_type((int)icp->type),
883 pr_name(from->sin_addr));
889 advertise(&sin, lifetime);
900 * Checksum routine for Internet Protocol family headers (C Version)
903 u_short in_cksum(u_short *addr, int len)
905 register int nleft = len;
906 register u_short *w = addr;
907 register u_short answer;
908 register int sum = 0;
911 * Our algorithm is simple, using a 32 bit accumulator (sum),
912 * we add sequential 16 bit words to it, and at the end, fold
913 * back all the carry bits from the top 16 bits into the lower
921 /* mop up an odd byte, if necessary */
923 sum += htons(*(u_char *)w<<8);
926 * add back carry outs from top 16 bits to low 16 bits
928 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
929 sum += (sum >> 16); /* add carry */
930 answer = ~sum; /* truncate to 16 bits */
937 * Print out statistics, and give up.
938 * Heavily buffered STDIO is used here, so that all the statistics
939 * will be written with 1 sys-write call. This is nice when more
940 * than one copy of the program is running on a terminal; it prevents
941 * the statistics output from becomming intermingled.
950 /* Send out a packet with a preference so that all
951 * hosts will know that we are dead.
953 * Wrong comment, wrong code.
954 * ttl must be set to 0 instead. --ANK
956 logerr("terminated\n");
958 advertise(&whereto, 0);
961 logtrace("\n----%s rdisc Statistics----\n", sendaddress );
962 logtrace("%d packets transmitted, ", ntransmitted );
963 logtrace("%d packets received, ", nreceived );
965 (void) fflush(stdout);
978 /* From libc/rpc/pmap_rmt.c */
981 sendbcast(int s, char *packet, int packetlen)
985 for (i = 0; i < num_interfaces; i++) {
986 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
988 cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
989 if (cc!= packetlen) {
997 sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
1001 struct sockaddr_in baddr;
1003 baddr.sin_family = AF_INET;
1004 baddr.sin_addr = ifp->bcastaddr;
1006 logdebug("Broadcast to %s\n",
1007 pr_name(baddr.sin_addr));
1009 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1010 cc = sendto(s, packet, packetlen, 0,
1011 (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1012 if (cc!= packetlen) {
1013 logperror("sendbcast: sendto");
1014 logerr("Cannot send broadcast packet to %s\n",
1015 pr_name(baddr.sin_addr));
1018 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1023 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1027 for (i = 0; i < num_interfaces; i++) {
1028 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
1030 cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
1031 if (cc!= packetlen) {
1039 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1040 struct interface *ifp)
1043 struct ip_mreqn mreq;
1045 memset(&mreq, 0, sizeof(mreq));
1046 mreq.imr_ifindex = ifp->ifindex;
1047 mreq.imr_address = ifp->localaddr;
1049 logdebug("Multicast to interface %s, %s\n",
1051 pr_name(mreq.imr_address));
1052 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1054 sizeof(mreq)) < 0) {
1055 logperror("setsockopt (IP_MULTICAST_IF)");
1056 logerr("Cannot send multicast packet over interface %s, %s\n",
1058 pr_name(mreq.imr_address));
1061 cc = sendto(s, packet, packetlen, 0,
1062 (struct sockaddr *)sin, sizeof (struct sockaddr));
1063 if (cc!= packetlen) {
1064 logperror("sendmcast: sendto");
1065 logerr("Cannot send multicast packet over interface %s, %s\n",
1066 ifp->name, pr_name(mreq.imr_address));
1078 for (i = 0; i < interfaces_size; i++)
1079 interfaces[i].preference = preference;
1089 struct ifreq ifreq, *ifr;
1090 struct sockaddr_in *sin;
1096 sock = socket(AF_INET, SOCK_DGRAM, 0);
1098 logperror("initifs: socket");
1102 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1108 bufsize = numifs * sizeof(struct ifreq);
1109 buf = (char *)malloc(bufsize);
1111 logerr("out of memory\n");
1116 interfaces = (struct interface *)ALLIGN(realloc((char *)interfaces,
1117 numifs * sizeof(struct interface)));
1119 interfaces = (struct interface *)ALLIGN(malloc(numifs *
1120 sizeof(struct interface)));
1121 if (interfaces == NULL) {
1122 logerr("out of memory\n");
1127 interfaces_size = numifs;
1129 ifc.ifc_len = bufsize;
1131 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1132 logperror("initifs: ioctl (get interface configuration)");
1138 for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1140 if (strlen(ifreq.ifr_name) >= IFNAMSIZ)
1142 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
1143 logperror("initifs: ioctl (get interface flags)");
1146 if (ifr->ifr_addr.sa_family != AF_INET)
1148 if ((ifreq.ifr_flags & IFF_UP) == 0)
1150 if (ifreq.ifr_flags & IFF_LOOPBACK)
1152 if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1154 strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
1156 sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
1157 interfaces[i].localaddr = sin->sin_addr;
1158 interfaces[i].flags = ifreq.ifr_flags;
1159 interfaces[i].netmask.s_addr = (__u32)0xffffffff;
1160 if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
1161 logperror("initifs: ioctl (get ifindex)");
1164 interfaces[i].ifindex = ifreq.ifr_ifindex;
1165 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
1166 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1167 logperror("initifs: ioctl (get destination addr)");
1170 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1171 /* A pt-pt link is identified by the remote address */
1172 interfaces[i].address = sin->sin_addr;
1173 interfaces[i].remoteaddr = sin->sin_addr;
1174 /* Simulate broadcast for pt-pt */
1175 interfaces[i].bcastaddr = sin->sin_addr;
1176 interfaces[i].flags |= IFF_BROADCAST;
1178 /* Non pt-pt links are identified by the local address */
1179 interfaces[i].address = interfaces[i].localaddr;
1180 interfaces[i].remoteaddr = interfaces[i].address;
1181 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1182 logperror("initifs: ioctl (get netmask)");
1185 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1186 interfaces[i].netmask = sin->sin_addr;
1187 if (ifreq.ifr_flags & IFF_BROADCAST) {
1188 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1189 logperror("initifs: ioctl (get broadcast address)");
1192 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1193 interfaces[i].bcastaddr = sin->sin_addr;
1198 logdebug("Found interface %s, flags 0x%x\n",
1199 pr_name(interfaces[i].localaddr),
1200 interfaces[i].flags);
1207 logdebug("Found %d interfaces\n", num_interfaces);
1214 join(int sock, struct sockaddr_in *sin)
1217 struct ip_mreqn mreq;
1219 if (isbroadcast(sin))
1222 mreq.imr_multiaddr = sin->sin_addr;
1223 for (i = 0; i < num_interfaces; i++) {
1224 mreq.imr_ifindex = interfaces[i].ifindex;
1225 mreq.imr_address.s_addr = 0;
1227 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1228 (char *)&mreq, sizeof(mreq)) < 0) {
1229 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1236 int support_multicast()
1241 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1243 logperror("support_multicast: socket");
1247 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1248 (char *)&ttl, sizeof(ttl)) < 0) {
1257 is_directly_connected(struct in_addr in)
1261 for (i = 0; i < num_interfaces; i++) {
1262 /* Check that the subnetwork numbers match */
1264 if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
1265 (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
1275 struct in_addr router;
1282 struct table *table;
1285 find_router(struct in_addr addr)
1291 if (tp->router.s_addr == addr.s_addr)
1298 int max_preference(void)
1301 int max = (int)INELIGIBLE_PREF;
1305 if (tp->preference > max)
1306 max = tp->preference;
1313 /* Note: this might leave the kernel with no default route for a short time. */
1317 struct table **tpp, *tp;
1318 int recalculate_max = 0;
1319 int max = max_preference();
1322 while (*tpp != NULL) {
1324 tp->remaining_time -= time;
1325 if (tp->remaining_time <= 0) {
1328 del_route(tp->router);
1329 if (best_preference &&
1330 tp->preference == max)
1337 if (recalculate_max) {
1338 int max = max_preference();
1340 if (max != INELIGIBLE_PREF) {
1343 if (tp->preference == max && !tp->in_kernel) {
1344 add_route(tp->router);
1353 void discard_table(void)
1355 struct table **tpp, *tp;
1358 while (*tpp != NULL) {
1362 del_route(tp->router);
1369 record_router(struct in_addr router, int preference, int ttl)
1372 int old_max = max_preference();
1373 int changed_up = 0; /* max preference could have increased */
1374 int changed_down = 0; /* max preference could have decreased */
1377 preference = INELIGIBLE_PREF;
1380 logdebug("Recording %s, ttl %d, preference 0x%x\n",
1384 tp = find_router(router);
1386 if (tp->preference > preference &&
1387 tp->preference == old_max)
1389 else if (preference > tp->preference)
1391 tp->preference = preference;
1392 tp->remaining_time = ttl;
1394 if (preference > old_max)
1396 tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
1398 logerr("Out of memory\n");
1401 tp->router = router;
1402 tp->preference = preference;
1403 tp->remaining_time = ttl;
1408 if (!tp->in_kernel &&
1409 (!best_preference || tp->preference == max_preference()) &&
1410 tp->preference != INELIGIBLE_PREF) {
1411 add_route(tp->router);
1414 if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) {
1415 del_route(tp->router);
1418 if (best_preference && changed_down) {
1419 /* Check if we should add routes */
1420 int new_max = max_preference();
1421 if (new_max != INELIGIBLE_PREF) {
1424 if (tp->preference == new_max &&
1426 add_route(tp->router);
1433 if (best_preference && (changed_up || changed_down)) {
1434 /* Check if we should remove routes already in the kernel */
1435 int new_max = max_preference();
1438 if (tp->preference < new_max && tp->in_kernel) {
1439 del_route(tp->router);
1448 add_route(struct in_addr addr)
1451 logdebug("Add default route to %s\n", pr_name(addr));
1452 rtioctl(addr, SIOCADDRT);
1456 del_route(struct in_addr addr)
1459 logdebug("Delete default route to %s\n", pr_name(addr));
1460 rtioctl(addr, SIOCDELRT);
1464 rtioctl(struct in_addr addr, int op)
1468 struct sockaddr_in *sin;
1470 bzero((char *)&rt, sizeof(struct rtentry));
1471 rt.rt_dst.sa_family = AF_INET;
1472 rt.rt_gateway.sa_family = AF_INET;
1473 rt.rt_genmask.sa_family = AF_INET;
1474 sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
1475 sin->sin_addr = addr;
1476 rt.rt_flags = RTF_UP | RTF_GATEWAY;
1478 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1480 logperror("rtioctl: socket");
1483 if (ioctl(sock, op, (char *)&rt) < 0) {
1484 if (!(op == SIOCADDRT && errno == EEXIST))
1485 logperror("ioctl (add/delete route)");
1497 openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1502 logperror(char *str)
1505 syslog(LOG_ERR, "%s: %m", str);
1507 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));