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;
81 * Use 255.255.255.255 for broadcasts - not the interface broadcast
85 #define ALLIGN(ptr) (ptr)
87 static int join(int sock, struct sockaddr_in *sin);
88 static void solicitor(struct sockaddr_in *);
90 static void advertise(struct sockaddr_in *, int lft);
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);
109 #define ICMP_ROUTER_ADVERTISEMENT 9
110 #define ICMP_ROUTER_SOLICITATION 10
112 #define ALL_HOSTS_ADDRESS "224.0.0.1"
113 #define ALL_ROUTERS_ADDRESS "224.0.0.2"
117 #if !defined(__GLIBC__) || __GLIBC__ < 2
118 /* For router advertisement */
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 */
132 __u32 ira_preference;
138 /* Router constants */
139 #define MAX_INITIAL_ADVERT_INTERVAL 16
140 #define MAX_INITIAL_ADVERTISEMENTS 3
141 #define MAX_RESPONSE_DELAY 2 /* Not used */
144 #define MAX_SOLICITATIONS 3
145 #define SOLICITATION_INTERVAL 3
146 #define MAX_SOLICITATION_DELAY 1 /* Not used */
148 #define INELIGIBLE_PREF 0x80000000 /* Maximum negative */
150 #define MAX_ADV_INT 600
153 static int num_interfaces;
155 static struct interface *interfaces;
156 static int interfaces_size; /* Number of elements in interfaces */
159 #define MAXPACKET 4096 /* max packet size */
165 "Usage: rdisc [-b] [-d] [-s] [-v] [-f] [-a] [-V] [send_address] [receive_address]\n"
167 " rdisc -r [-b] [-d] [-s] [-v] [-f] [-a] [-V] [-p <preference>] [-T <secs>] \n"
168 " [send_address] [receive_address]\n"
173 int s; /* Socket file descriptor */
174 struct sockaddr_in whereto;/* Address to send to */
176 /* Common variables */
181 int ntransmitted = 0;
183 int forever = 0; /* Never give up on host. If 0 defer fork until
188 /* Router variables */
190 int max_adv_int = MAX_ADV_INT;
193 int initial_advert_interval = MAX_INITIAL_ADVERT_INTERVAL;
194 int initial_advertisements = MAX_INITIAL_ADVERTISEMENTS;
195 int preference = 0; /* Setable with -p option */
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. */
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);
212 static int logging = 0;
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);
222 static __inline__ int isbroadcast(struct sockaddr_in *sin)
224 return (sin->sin_addr.s_addr == INADDR_BROADCAST);
227 static __inline__ int ismulticast(struct sockaddr_in *sin)
229 return IN_CLASSD(ntohl(sin->sin_addr.s_addr));
232 static void prusage(void)
234 (void) fprintf(stderr, "%s", usage);
246 if ((open_max = sysconf(_SC_OPEN_MAX)) == -1) {
248 (void) fprintf(stderr, "OPEN_MAX is not supported\n");
251 (void) fprintf(stderr, "sysconf() error\n");
257 if ((pid=fork()) != 0)
260 for (t = 0; t < open_max; t++)
268 void signal_setup(int signo, void (*handler)(void))
272 memset(&sa, 0, sizeof(sa));
274 sa.sa_handler = (void (*)(int))handler;
276 sa.sa_flags = SA_INTERRUPT;
278 sigaction(signo, &sa, NULL);
284 char *sendaddress, *recvaddress;
286 int main(int argc, char **argv)
288 struct sockaddr_in from;
290 struct sockaddr_in *to = &whereto;
291 struct sockaddr_in joinaddr;
292 sigset_t sset, sset_empty;
296 min_adv_int =( max_adv_int * 3 / 4);
297 lifetime = (3*max_adv_int);
301 while (argc > 0 && *av[0] == '-') {
331 printf("rdisc utility, iputils-ss%s\n", SNAPSHOT);
337 val = strtol(av[0], (char **)NULL, 0);
338 if (val < 4 || val > 1800) {
339 (void) fprintf(stderr,
340 "Bad Max Advertizement Interval\n");
344 min_adv_int =( max_adv_int * 3 / 4);
345 lifetime = (3*max_adv_int);
354 val = strtol(av[0], (char **)NULL, 0);
373 if (support_multicast()) {
374 sendaddress = ALL_ROUTERS_ADDRESS;
377 sendaddress = ALL_HOSTS_ADDRESS;
380 sendaddress = "255.255.255.255";
387 if (support_multicast()) {
388 recvaddress = ALL_HOSTS_ADDRESS;
391 recvaddress = ALL_ROUTERS_ADDRESS;
394 recvaddress = "255.255.255.255";
400 (void) fprintf(stderr, "Extra parameters\n");
406 if (solicit && responder) {
412 if (!(solicit && !forever)) {
415 * Added the next line to stop forking a second time
416 * Fraser Gardiner - Sun Microsystems Australia
421 memset( (char *)&whereto, 0, sizeof(struct sockaddr_in) );
422 to->sin_family = AF_INET;
423 to->sin_addr.s_addr = inet_addr(sendaddress);
425 memset( (char *)&joinaddr, 0, sizeof(struct sockaddr_in) );
426 joinaddr.sin_family = AF_INET;
427 joinaddr.sin_addr.s_addr = inet_addr(recvaddress);
431 srandom((int)gethostid());
434 if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0) {
439 setlinebuf( stdout );
441 signal_setup(SIGINT, finish );
442 signal_setup(SIGTERM, graceful_finish );
443 signal_setup(SIGHUP, initifs );
444 signal_setup(SIGALRM, timer );
447 sigemptyset(&sset_empty);
448 sigaddset(&sset, SIGALRM);
449 sigaddset(&sset, SIGHUP);
450 sigaddset(&sset, SIGTERM);
451 sigaddset(&sset, SIGINT);
454 if (join(s, &joinaddr) < 0) {
455 logerr("Failed joining addresses\n");
459 timer(); /* start things going */
462 u_char packet[MAXPACKET];
463 int len = sizeof (packet);
464 socklen_t fromlen = sizeof (from);
467 cc=recvfrom(s, (char *)packet, len, 0,
468 (struct sockaddr *)&from, &fromlen);
472 logperror("recvfrom");
476 sigprocmask(SIG_SETMASK, &sset, NULL);
477 pr_pack( (char *)packet, cc, &from );
478 sigprocmask(SIG_SETMASK, &sset_empty, NULL);
483 #define TIMER_INTERVAL 3
484 #define GETIFCONF_TIMER 30
486 static int left_until_advertise;
488 /* Called every TIMER_INTERVAL */
492 static int left_until_getifconf;
493 static int left_until_solicit;
496 time += TIMER_INTERVAL;
498 left_until_getifconf -= TIMER_INTERVAL;
499 left_until_advertise -= TIMER_INTERVAL;
500 left_until_solicit -= TIMER_INTERVAL;
502 if (left_until_getifconf < 0) {
504 left_until_getifconf = GETIFCONF_TIMER;
507 if (responder && left_until_advertise <= 0) {
509 advertise(&whereto, lifetime);
510 if (ntransmitted < initial_advertisements)
511 left_until_advertise = initial_advert_interval;
513 left_until_advertise = min_adv_int +
514 ((max_adv_int - min_adv_int) *
515 (random() % 1000)/1000);
518 if (solicit && left_until_solicit <= 0) {
521 if (ntransmitted < max_solicitations)
522 left_until_solicit = solicitation_interval;
525 if (!forever && nreceived == 0)
529 age_table(TIMER_INTERVAL);
530 alarm(TIMER_INTERVAL);
536 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
537 * The IP packet will be added on by the kernel.
540 solicitor(struct sockaddr_in *sin)
542 static u_char outpack[MAXPACKET];
543 struct icmphdr *icp = (struct icmphdr *) ALLIGN(outpack);
547 logtrace("Sending solicitation to %s\n",
548 pr_name(sin->sin_addr));
550 icp->type = ICMP_ROUTER_SOLICITATION;
553 icp->un.gateway = 0; /* Reserved */
556 /* Compute ICMP checksum here */
557 icp->checksum = in_cksum( (u_short *)icp, packetlen );
559 if (isbroadcast(sin))
560 i = sendbcast(s, (char *)outpack, packetlen);
561 else if (ismulticast(sin))
562 i = sendmcast(s, (char *)outpack, packetlen, sin);
564 i = sendto( s, (char *)outpack, packetlen, 0,
565 (struct sockaddr *)sin, sizeof(struct sockaddr));
567 if( i < 0 || i != packetlen ) {
569 logperror("solicitor:sendto");
571 logerr("wrote %s %d chars, ret=%d\n",
572 sendaddress, packetlen, i );
580 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
581 * The IP packet will be added on by the kernel.
584 advertise(struct sockaddr_in *sin, int lft)
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;
592 logtrace("Sending advertisement to %s\n",
593 pr_name(sin->sin_addr));
596 for (i = 0; i < num_interfaces; i++) {
597 rap->icmp_type = ICMP_ROUTER_ADVERTISEMENT;
600 rap->icmp_num_addrs = 0;
602 rap->icmp_lifetime = htons(lft);
606 * TODO handle multiple logical interfaces per
607 * physical interface. (increment with rap->icmp_wpa * 4 for
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++;
616 /* Compute ICMP checksum here */
617 rap->icmp_cksum = in_cksum( (u_short *)rap, packetlen );
619 if (isbroadcast(sin))
620 cc = sendbcastif(s, (char *)outpack, packetlen,
622 else if (ismulticast(sin))
623 cc = sendmcastif( s, (char *)outpack, packetlen, sin,
626 struct interface *ifp = &interfaces[i];
628 * Verify that the interface matches the destination
631 if ((sin->sin_addr.s_addr & ifp->netmask.s_addr) ==
632 (ifp->address.s_addr & ifp->netmask.s_addr)) {
634 logdebug("Unicast to %s ",
635 pr_name(sin->sin_addr));
636 logdebug("on interface %s, %s\n",
638 pr_name(ifp->address));
640 cc = sendto( s, (char *)outpack, packetlen, 0,
641 (struct sockaddr *)sin,
642 sizeof(struct sockaddr));
646 if( cc < 0 || cc != packetlen ) {
650 logerr("wrote %s %d chars, ret=%d\n",
651 sendaddress, packetlen, cc );
661 * Convert an ICMP "type" field to a printable string.
666 static char *ttab[] = {
677 "Router Solicitation",
688 if ( t < 0 || t > 16 )
689 return("OUT-OF-RANGE");
697 * Return a string name for the given IP address.
699 char *pr_name(struct in_addr addr)
704 phe = gethostbyaddr((char *)&addr.s_addr, 4, AF_INET);
706 return( inet_ntoa(addr));
707 snprintf(buf, sizeof(buf), "%s (%s)", phe->h_name, inet_ntoa(addr));
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!).
720 pr_pack(char *buf, int cc, struct sockaddr_in *from)
727 ip = (struct iphdr *) ALLIGN(buf);
731 logtrace("packet too short (%d bytes) from %s\n", cc,
732 pr_name(from->sin_addr));
736 icp = (struct icmphdr *)ALLIGN(buf + hlen);
739 case ICMP_ROUTER_ADVERTISEMENT:
741 struct icmp_ra *rap = (struct icmp_ra *)ALLIGN(icp);
742 struct icmp_ra_addr *ap;
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)) {
753 logtrace("ICMP %s from %s: Bad checksum\n",
754 pr_type((int)rap->icmp_type),
755 pr_name(from->sin_addr));
758 if (rap->icmp_code != 0) {
760 logtrace("ICMP %s from %s: Code = %d\n",
761 pr_type((int)rap->icmp_type),
762 pr_name(from->sin_addr),
766 if (rap->icmp_num_addrs < 1) {
768 logtrace("ICMP %s from %s: No addresses\n",
769 pr_type((int)rap->icmp_type),
770 pr_name(from->sin_addr));
773 if (rap->icmp_wpa < 2) {
775 logtrace("ICMP %s from %s: Words/addr = %d\n",
776 pr_type((int)rap->icmp_type),
777 pr_name(from->sin_addr),
782 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4) {
784 logtrace("ICMP %s from %s: Too short %d, %d\n",
785 pr_type((int)rap->icmp_type),
786 pr_name(from->sin_addr),
788 8 + rap->icmp_num_addrs * rap->icmp_wpa * 4);
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));
798 /* Check that at least one router address is a neighboor
799 * on the arriving link.
801 for (i = 0; (unsigned)i < rap->icmp_num_addrs; i++) {
803 ap = (struct icmp_ra_addr *)
804 ALLIGN(buf + hlen + 8 +
805 i * rap->icmp_wpa * 4);
806 ina.s_addr = ap->ira_addr;
808 logtrace("\taddress %s, preference 0x%x\n",
810 (unsigned int)ntohl(ap->ira_preference));
811 if (is_directly_connected(ina))
813 ntohl(ap->ira_preference),
814 ntohs(rap->icmp_lifetime));
821 * The next line was added so that the alarm is set for the new procces
822 * Fraser Gardiner Sun Microsystems Australia
824 (void) alarm(TIMER_INTERVAL);
830 case ICMP_ROUTER_SOLICITATION:
832 struct sockaddr_in sin;
837 /* TBD verify that the link is multicast or broadcast */
838 /* XXX Find out the link it came in over? */
840 if (in_cksum((u_short *)ALLIGN(buf+hlen), cc)) {
842 logtrace("ICMP %s from %s: Bad checksum\n",
843 pr_type((int)icp->type),
844 pr_name(from->sin_addr));
847 if (icp->code != 0) {
849 logtrace("ICMP %s from %s: Code = %d\n",
850 pr_type((int)icp->type),
851 pr_name(from->sin_addr),
856 if (cc < ICMP_MINLEN) {
858 logtrace("ICMP %s from %s: Too short %d, %d\n",
859 pr_type((int)icp->type),
860 pr_name(from->sin_addr),
867 logtrace("ICMP %s from %s\n",
868 pr_type((int)icp->type),
869 pr_name(from->sin_addr));
871 /* Check that ip_src is either a neighboor
872 * on the arriving link or 0.
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.
879 if (IN_CLASSD(ntohl(ip->daddr)))
880 sin.sin_addr.s_addr = htonl(0xe0000001);
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);
888 sin.sin_addr = ip->saddr;
889 if (!is_directly_connected(sin.sin_addr)) {
891 logtrace("ICMP %s from %s: source not directly connected\n",
892 pr_type((int)icp->type),
893 pr_name(from->sin_addr));
899 advertise(&sin, lifetime);
910 * Checksum routine for Internet Protocol family headers (C Version)
913 u_short in_cksum(u_short *addr, int len)
915 register int nleft = len;
916 register u_short *w = addr;
917 register u_short answer;
918 register int sum = 0;
921 * Our algorithm is simple, using a 32 bit accumulator (sum),
922 * we add sequential 16 bit words to it, and at the end, fold
923 * back all the carry bits from the top 16 bits into the lower
931 /* mop up an odd byte, if necessary */
933 sum += htons(*(u_char *)w<<8);
936 * add back carry outs from top 16 bits to low 16 bits
938 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
939 sum += (sum >> 16); /* add carry */
940 answer = ~sum; /* truncate to 16 bits */
947 * Print out statistics, and give up.
948 * Heavily buffered STDIO is used here, so that all the statistics
949 * will be written with 1 sys-write call. This is nice when more
950 * than one copy of the program is running on a terminal; it prevents
951 * the statistics output from becomming intermingled.
960 /* Send out a packet with a preference so that all
961 * hosts will know that we are dead.
963 * Wrong comment, wrong code.
964 * ttl must be set to 0 instead. --ANK
966 logerr("terminated\n");
968 advertise(&whereto, 0);
971 logtrace("\n----%s rdisc Statistics----\n", sendaddress );
972 logtrace("%d packets transmitted, ", ntransmitted );
973 logtrace("%d packets received, ", nreceived );
975 (void) fflush(stdout);
988 /* From libc/rpc/pmap_rmt.c */
991 sendbcast(int s, char *packet, int packetlen)
995 for (i = 0; i < num_interfaces; i++) {
996 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
998 cc = sendbcastif(s, packet, packetlen, &interfaces[i]);
999 if (cc!= packetlen) {
1007 sendbcastif(int s, char *packet, int packetlen, struct interface *ifp)
1011 struct sockaddr_in baddr;
1013 baddr.sin_family = AF_INET;
1014 baddr.sin_addr = ifp->bcastaddr;
1016 logdebug("Broadcast to %s\n",
1017 pr_name(baddr.sin_addr));
1019 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1020 cc = sendto(s, packet, packetlen, 0,
1021 (struct sockaddr *)&baddr, sizeof (struct sockaddr));
1022 if (cc!= packetlen) {
1023 logperror("sendbcast: sendto");
1024 logerr("Cannot send broadcast packet to %s\n",
1025 pr_name(baddr.sin_addr));
1028 setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&on, sizeof(on));
1033 sendmcast(int s, char *packet, int packetlen, struct sockaddr_in *sin)
1037 for (i = 0; i < num_interfaces; i++) {
1038 if ((interfaces[i].flags & (IFF_BROADCAST|IFF_POINTOPOINT|IFF_MULTICAST)) == 0)
1040 cc = sendmcastif(s, packet, packetlen, sin, &interfaces[i]);
1041 if (cc!= packetlen) {
1049 sendmcastif(int s, char *packet, int packetlen, struct sockaddr_in *sin,
1050 struct interface *ifp)
1053 struct ip_mreqn mreq;
1055 memset(&mreq, 0, sizeof(mreq));
1056 mreq.imr_ifindex = ifp->ifindex;
1057 mreq.imr_address = ifp->localaddr;
1059 logdebug("Multicast to interface %s, %s\n",
1061 pr_name(mreq.imr_address));
1062 if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
1064 sizeof(mreq)) < 0) {
1065 logperror("setsockopt (IP_MULTICAST_IF)");
1066 logerr("Cannot send multicast packet over interface %s, %s\n",
1068 pr_name(mreq.imr_address));
1071 cc = sendto(s, packet, packetlen, 0,
1072 (struct sockaddr *)sin, sizeof (struct sockaddr));
1073 if (cc!= packetlen) {
1074 logperror("sendmcast: sendto");
1075 logerr("Cannot send multicast packet over interface %s, %s\n",
1076 ifp->name, pr_name(mreq.imr_address));
1088 for (i = 0; i < interfaces_size; i++)
1089 interfaces[i].preference = preference;
1099 struct ifreq ifreq, *ifr;
1100 struct sockaddr_in *sin;
1106 sock = socket(AF_INET, SOCK_DGRAM, 0);
1108 logperror("initifs: socket");
1112 if (ioctl(sock, SIOCGIFNUM, (char *)&numifs) < 0) {
1118 bufsize = numifs * sizeof(struct ifreq);
1119 buf = (char *)malloc(bufsize);
1121 logerr("out of memory\n");
1126 interfaces = (struct interface *)ALLIGN(realloc((char *)interfaces,
1127 numifs * sizeof(struct interface)));
1129 interfaces = (struct interface *)ALLIGN(malloc(numifs *
1130 sizeof(struct interface)));
1131 if (interfaces == NULL) {
1132 logerr("out of memory\n");
1137 interfaces_size = numifs;
1139 ifc.ifc_len = bufsize;
1141 if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) {
1142 logperror("initifs: ioctl (get interface configuration)");
1148 for (i = 0, n = ifc.ifc_len/sizeof (struct ifreq); n > 0; n--, ifr++) {
1150 if (strlen(ifreq.ifr_name) >= IFNAMSIZ)
1152 if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
1153 logperror("initifs: ioctl (get interface flags)");
1156 if (ifr->ifr_addr.sa_family != AF_INET)
1158 if ((ifreq.ifr_flags & IFF_UP) == 0)
1160 if (ifreq.ifr_flags & IFF_LOOPBACK)
1162 if ((ifreq.ifr_flags & (IFF_MULTICAST|IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
1164 strncpy(interfaces[i].name, ifr->ifr_name, IFNAMSIZ-1);
1166 sin = (struct sockaddr_in *)ALLIGN(&ifr->ifr_addr);
1167 interfaces[i].localaddr = sin->sin_addr;
1168 interfaces[i].flags = ifreq.ifr_flags;
1169 interfaces[i].netmask.s_addr = (__u32)0xffffffff;
1170 if (ioctl(sock, SIOCGIFINDEX, (char *)&ifreq) < 0) {
1171 logperror("initifs: ioctl (get ifindex)");
1174 interfaces[i].ifindex = ifreq.ifr_ifindex;
1175 if (ifreq.ifr_flags & IFF_POINTOPOINT) {
1176 if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) {
1177 logperror("initifs: ioctl (get destination addr)");
1180 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1181 /* A pt-pt link is identified by the remote address */
1182 interfaces[i].address = sin->sin_addr;
1183 interfaces[i].remoteaddr = sin->sin_addr;
1184 /* Simulate broadcast for pt-pt */
1185 interfaces[i].bcastaddr = sin->sin_addr;
1186 interfaces[i].flags |= IFF_BROADCAST;
1188 /* Non pt-pt links are identified by the local address */
1189 interfaces[i].address = interfaces[i].localaddr;
1190 interfaces[i].remoteaddr = interfaces[i].address;
1191 if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
1192 logperror("initifs: ioctl (get netmask)");
1195 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1196 interfaces[i].netmask = sin->sin_addr;
1197 if (ifreq.ifr_flags & IFF_BROADCAST) {
1198 if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
1199 logperror("initifs: ioctl (get broadcast address)");
1202 sin = (struct sockaddr_in *)ALLIGN(&ifreq.ifr_addr);
1203 interfaces[i].bcastaddr = sin->sin_addr;
1208 logdebug("Found interface %s, flags 0x%x\n",
1209 pr_name(interfaces[i].localaddr),
1210 interfaces[i].flags);
1217 logdebug("Found %d interfaces\n", num_interfaces);
1224 join(int sock, struct sockaddr_in *sin)
1227 struct ip_mreqn mreq;
1228 int joined[num_interfaces];
1230 memset(joined, 0, sizeof(joined));
1232 if (isbroadcast(sin))
1235 mreq.imr_multiaddr = sin->sin_addr;
1236 for (i = 0; i < num_interfaces; i++) {
1237 for (j = 0; j < i; j++) {
1238 if (joined[j] == interfaces[i].ifindex)
1244 mreq.imr_ifindex = interfaces[i].ifindex;
1245 mreq.imr_address.s_addr = 0;
1247 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
1248 (char *)&mreq, sizeof(mreq)) < 0) {
1249 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1253 joined[i] = interfaces[i].ifindex;
1258 int support_multicast()
1263 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1265 logperror("support_multicast: socket");
1269 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL,
1270 (char *)&ttl, sizeof(ttl)) < 0) {
1279 is_directly_connected(struct in_addr in)
1283 for (i = 0; i < num_interfaces; i++) {
1284 /* Check that the subnetwork numbers match */
1286 if ((in.s_addr & interfaces[i].netmask.s_addr ) ==
1287 (interfaces[i].remoteaddr.s_addr & interfaces[i].netmask.s_addr))
1297 struct in_addr router;
1304 struct table *table;
1307 find_router(struct in_addr addr)
1313 if (tp->router.s_addr == addr.s_addr)
1320 int max_preference(void)
1323 int max = (int)INELIGIBLE_PREF;
1327 if (tp->preference > max)
1328 max = tp->preference;
1335 /* Note: this might leave the kernel with no default route for a short time. */
1339 struct table **tpp, *tp;
1340 int recalculate_max = 0;
1341 int max = max_preference();
1344 while (*tpp != NULL) {
1346 tp->remaining_time -= time;
1347 if (tp->remaining_time <= 0) {
1350 del_route(tp->router);
1351 if (best_preference &&
1352 tp->preference == max)
1359 if (recalculate_max) {
1360 int max = max_preference();
1362 if (max != INELIGIBLE_PREF) {
1365 if (tp->preference == max && !tp->in_kernel) {
1366 add_route(tp->router);
1375 void discard_table(void)
1377 struct table **tpp, *tp;
1380 while (*tpp != NULL) {
1384 del_route(tp->router);
1391 record_router(struct in_addr router, int preference, int ttl)
1394 int old_max = max_preference();
1395 int changed_up = 0; /* max preference could have increased */
1396 int changed_down = 0; /* max preference could have decreased */
1399 preference = INELIGIBLE_PREF;
1402 logdebug("Recording %s, ttl %d, preference 0x%x\n",
1406 tp = find_router(router);
1408 if (tp->preference > preference &&
1409 tp->preference == old_max)
1411 else if (preference > tp->preference)
1413 tp->preference = preference;
1414 tp->remaining_time = ttl;
1416 if (preference > old_max)
1418 tp = (struct table *)ALLIGN(malloc(sizeof(struct table)));
1420 logerr("Out of memory\n");
1423 tp->router = router;
1424 tp->preference = preference;
1425 tp->remaining_time = ttl;
1430 if (!tp->in_kernel &&
1431 (!best_preference || tp->preference == max_preference()) &&
1432 tp->preference != INELIGIBLE_PREF) {
1433 add_route(tp->router);
1436 if (tp->preference == INELIGIBLE_PREF && tp->in_kernel) {
1437 del_route(tp->router);
1440 if (best_preference && changed_down) {
1441 /* Check if we should add routes */
1442 int new_max = max_preference();
1443 if (new_max != INELIGIBLE_PREF) {
1446 if (tp->preference == new_max &&
1448 add_route(tp->router);
1455 if (best_preference && (changed_up || changed_down)) {
1456 /* Check if we should remove routes already in the kernel */
1457 int new_max = max_preference();
1460 if (tp->preference < new_max && tp->in_kernel) {
1461 del_route(tp->router);
1470 add_route(struct in_addr addr)
1473 logdebug("Add default route to %s\n", pr_name(addr));
1474 rtioctl(addr, SIOCADDRT);
1478 del_route(struct in_addr addr)
1481 logdebug("Delete default route to %s\n", pr_name(addr));
1482 rtioctl(addr, SIOCDELRT);
1486 rtioctl(struct in_addr addr, int op)
1490 struct sockaddr_in *sin;
1492 memset((char *)&rt, 0, sizeof(struct rtentry));
1493 rt.rt_dst.sa_family = AF_INET;
1494 rt.rt_gateway.sa_family = AF_INET;
1495 rt.rt_genmask.sa_family = AF_INET;
1496 sin = (struct sockaddr_in *)ALLIGN(&rt.rt_gateway);
1497 sin->sin_addr = addr;
1498 rt.rt_flags = RTF_UP | RTF_GATEWAY;
1500 sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
1502 logperror("rtioctl: socket");
1505 if (ioctl(sock, op, (char *)&rt) < 0) {
1506 if (!(op == SIOCADDRT && errno == EEXIST))
1507 logperror("ioctl (add/delete route)");
1519 openlog("in.rdiscd", LOG_PID | LOG_CONS, LOG_DAEMON);
1524 logperror(char *str)
1527 syslog(LOG_ERR, "%s: %m", str);
1529 (void) fprintf(stderr, "%s: %s\n", str, strerror(errno));