2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
7 * Copyright (c) 1987 Regents of the University of California.
10 * Redistribution and use in source and binary forms are permitted
11 * provided that the above copyright notice and this paragraph are
12 * duplicated in all such forms and that any documentation,
13 * advertising materials, and other materials related to such
14 * distribution and use acknowledge that the software was developed
15 * by the University of California, Berkeley. The name of the
16 * University may not be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
20 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/socket.h>
36 #include <sys/ioctl.h>
39 #include <netinet/in_systm.h>
40 #include <netinet/in.h>
41 #include <netinet/ip.h>
42 #include <netinet/ip_icmp.h>
44 #include <arpa/inet.h>
53 #define ALIGN(ptr) (ptr ? 0 : 0)
55 #define ALIGN(ptr) (ptr)
59 #define signal(s, f) sigset(s, (void (*)(int))f)
60 #define random() rand()
63 #define ALL_HOSTS_ADDRESS "224.0.0.1"
64 #define ALL_ROUTERS_ADDRESS "224.0.0.2"
68 /* For router advertisement */
70 uchar_t icmp_type
; /* type of message, see below */
71 uchar_t icmp_code
; /* type sub code */
72 ushort_t icmp_cksum
; /* ones complement cksum of struct */
73 uchar_t icmp_num_addrs
;
74 uchar_t icmp_wpa
; /* Words per address */
83 /* Router constants */
84 #define MAX_INITIAL_ADVERT_INTERVAL 16
85 #define MAX_INITIAL_ADVERTISEMENTS 3
86 #define MAX_RESPONSE_DELAY 2 /* Not used */
89 #define MAX_SOLICITATIONS 3
90 #define SOLICITATION_INTERVAL 3
91 #define MAX_SOLICITATION_DELAY 1 /* Not used */
93 #define IGNORE_PREFERENCE 0x80000000 /* Maximum negative */
95 #define MAX_ADV_INT 600
99 * A doubly linked list of all physical interfaces that each contain a
100 * doubly linked list of logical interfaces aka IP addresses.
103 char pi_name
[IFNAMSIZ
]; /* Used to identify it */
104 int pi_state
; /* See below */
105 struct logint
*pi_logical_first
;
106 struct logint
*pi_logical_last
;
107 struct phyint
*pi_next
;
108 struct phyint
*pi_prev
;
112 char li_name
[IFNAMSIZ
]; /* Used to identify it */
113 int li_state
; /* See below */
114 struct in_addr li_address
; /* Used to identify the interface */
115 struct in_addr li_localaddr
; /* Actual address of the interface */
117 int li_index
; /* interface index (SIOCGLIFINDEX) */
119 struct in_addr li_bcastaddr
;
120 struct in_addr li_remoteaddr
;
121 struct in_addr li_netmask
;
122 struct logint
*li_next
; /* Next logical for this physical */
123 struct logint
*li_prev
; /* Prev logical for this physical */
124 struct phyint
*li_physical
; /* Back pointer */
127 struct phyint
*phyint
;
128 int num_usable_interfaces
; /* Num used for sending/receiving */
133 #define ST_MARKED 0x01 /* To determine removed interfaces */
134 #define ST_JOINED 0x02 /* Joined multicast group */
135 #define ST_DELETED 0x04 /* Interface should be ignored */
137 /* Function prototypes */
138 static void solicitor(struct sockaddr_in
*sin
);
139 static void advertise(struct sockaddr_in
*sin
);
141 static void age_table(int time
);
142 static void flush_unreachable_routers(void);
143 static void record_router(struct in_addr router
, long preference
, int ttl
);
145 static void add_route(struct in_addr addr
);
146 static void del_route(struct in_addr addr
);
147 static void rtioctl(struct in_addr addr
, int op
);
149 static int support_multicast(void);
150 static int sendbcast(int s
, char *packet
, int packetlen
);
151 static int sendbcastif(int s
, char *packet
, int packetlen
,
153 static int sendmcast(int s
, char *packet
, int packetlen
,
154 struct sockaddr_in
*sin
);
155 static int sendmcastif(int s
, char *packet
, int packetlen
,
156 struct sockaddr_in
*sin
, struct logint
*li
);
158 static int ismulticast(struct sockaddr_in
*sin
);
159 static int isbroadcast(struct sockaddr_in
*sin
);
160 int in_cksum(ushort_t
*addr
, int len
);
161 static struct logint
*find_directly_connected_logint(struct in_addr in
,
163 static void force_preference(int preference
);
165 static void timer(void);
166 static void finish(void);
167 static void report(void);
168 static void report_interfaces(void);
169 static void report_routes(void);
170 static void reinitifs(void);
172 static struct phyint
*find_phyint(char *name
);
173 static struct phyint
*add_phyint(char *name
);
174 static void free_phyint(struct phyint
*pi
);
175 static struct logint
*find_logint(struct phyint
*pi
, char *name
);
176 static struct logint
*add_logint(struct phyint
*pi
, char *name
);
177 static void free_logint(struct logint
*li
);
179 static void deleted_phyint(struct phyint
*pi
, int s
,
180 struct sockaddr_in
*joinaddr
);
181 static void added_logint(struct logint
*li
, int s
,
182 struct sockaddr_in
*joinaddr
);
183 static void deleted_logint(struct logint
*li
, struct logint
*newli
, int s
,
184 struct sockaddr_in
*joinaddr
);
186 static int initifs(int s
, struct sockaddr_in
*joinaddr
, int preference
);
187 static boolean_t
getconfig(int sock
, uint64_t if_flags
, struct sockaddr
*addr
,
188 struct ifreq
*ifr
, struct logint
*li
);
190 static void pr_pack(char *buf
, int cc
, struct sockaddr_in
*from
);
191 char *pr_name(struct in_addr addr
);
192 char *pr_type(int t
);
194 static void initlog(void);
195 void logerr(), logtrace(), logdebug(), logperror();
197 /* Local variables */
199 #define MAXPACKET 4096 /* max packet size */
200 uchar_t packet
[MAXPACKET
];
203 "Usage: rdisc [-s] [-v] [-f] [-a] [send_address] [receive_address]\n"
204 " rdisc -r [-v] [-p <preference>] [-T <secs>] \n"
205 " [send_address] [receive_address]\n";
208 int s
; /* Socket file descriptor */
209 struct sockaddr_in whereto
; /* Address to send to */
210 struct sockaddr_in g_joinaddr
; /* Address to receive on */
211 char *sendaddress
, *recvaddress
; /* For logging purposes only */
213 /* Common variables */
217 int start_solicit
= 0; /* -s parameter set */
218 int solicit
= 0; /* Are we currently sending solicitations? */
220 int ntransmitted
= 0;
222 int forever
= 0; /* Never give up on host. If 0 defer fork until */
223 /* first response. */
225 /* Router variables */
226 int max_adv_int
= MAX_ADV_INT
;
229 int initial_advert_interval
= MAX_INITIAL_ADVERT_INTERVAL
;
230 int initial_advertisements
= MAX_INITIAL_ADVERTISEMENTS
;
231 ulong_t g_preference
= 0; /* Setable with -p option */
234 int max_solicitations
= MAX_SOLICITATIONS
;
235 unsigned int solicitation_interval
= SOLICITATION_INTERVAL
;
236 int best_preference
= 1; /* Set to record only the router(s) with the */
237 /* best preference in the kernel. Not set */
238 /* puts all routes in the kernel. */
244 (void) fprintf(stderr
, usage
);
248 static int sock
= -1;
260 for (t
= 0; t
< 20; t
++)
268 t
= open("/dev/tty", 2);
270 (void) ioctl(t
, TIOCNOTTY
, (char *)0);
283 main(int argc
, char *argv
[])
288 struct sockaddr_in from
;
290 struct sockaddr_in
*to
= &whereto
;
293 min_adv_int
= (max_adv_int
* 3 / 4);
294 lifetime
= (3*max_adv_int
);
297 while (argc
> 0 && *av
[0] == '-') {
310 start_solicit
= solicit
= 1;
327 val
= strtol(av
[0], (char **)NULL
, 0);
328 if (val
< 4 || val
> 1800) {
329 (void) fprintf(stderr
,
330 "Bad Max Advertisement Interval\n");
334 min_adv_int
= (max_adv_int
* 3 / 4);
335 lifetime
= (3*max_adv_int
);
344 val
= strtoul(av
[0], (char **)NULL
, 0);
359 if (support_multicast()) {
361 sendaddress
= ALL_HOSTS_ADDRESS
;
363 sendaddress
= ALL_ROUTERS_ADDRESS
;
365 sendaddress
= "255.255.255.255";
371 if (support_multicast()) {
373 recvaddress
= ALL_ROUTERS_ADDRESS
;
375 recvaddress
= ALL_HOSTS_ADDRESS
;
377 recvaddress
= "255.255.255.255";
383 (void) fprintf(stderr
, "Extra paramaters\n");
388 if (solicit
&& responder
) {
393 if (!(solicit
&& !forever
)) {
397 bzero((char *)&whereto
, sizeof (struct sockaddr_in
));
398 to
->sin_family
= AF_INET
;
399 to
->sin_addr
.s_addr
= inet_addr(sendaddress
);
400 if (to
->sin_addr
.s_addr
== (unsigned long)-1) {
401 logerr("in.rdisc: bad address %s\n", sendaddress
);
405 bzero((char *)&g_joinaddr
, sizeof (struct sockaddr_in
));
406 g_joinaddr
.sin_family
= AF_INET
;
407 g_joinaddr
.sin_addr
.s_addr
= inet_addr(recvaddress
);
408 if (g_joinaddr
.sin_addr
.s_addr
== (unsigned long)-1) {
409 logerr("in.rdisc: bad address %s\n", recvaddress
);
415 srand((int)gethostid());
417 srandom((int)gethostid());
421 if ((s
= socket(AF_INET
, SOCK_RAW
, IPPROTO_ICMP
)) < 0) {
427 setvbuf(stdout
, NULL
, _IOLBF
, 0);
432 (void) signal(SIGINT
, finish
);
433 (void) signal(SIGTERM
, finish
);
434 (void) signal(SIGHUP
, reinitifs
);
435 (void) signal(SIGUSR1
, report
);
437 if (initifs(s
, &g_joinaddr
, g_preference
) < 0) {
438 logerr("Failed initializing interfaces\n");
443 * If there are no usable interfaces and we are soliciting
444 * waiting for to return an exit code (i.e. forever isn't set)
445 * give up immediately.
447 if (num_usable_interfaces
== 0 && solicit
&& !forever
) {
448 logerr("in.rdisc: No interfaces up\n");
453 (void) signal(SIGALRM
, timer
);
456 * Make sure that this signal actually interrupts (rather than
457 * restarts) the recvfrom call below.
459 sv
.sv_handler
= timer
;
461 sv
.sv_flags
= SV_INTERRUPT
;
462 (void) sigvec(SIGALRM
, &sv
, (struct sigvec
*)NULL
);
464 timer(); /* start things going */
467 int len
= sizeof (packet
);
468 socklen_t fromlen
= (socklen_t
)sizeof (from
);
470 sigset_t newmask
, oldmask
;
472 if ((cc
= recvfrom(s
, (char *)packet
, len
, 0,
473 (struct sockaddr
*)&from
,
477 logperror("recvfrom");
480 /* Block all signals while processing */
481 (void) sigfillset(&newmask
);
482 (void) sigprocmask(SIG_SETMASK
, &newmask
, &oldmask
);
483 pr_pack((char *)packet
, cc
, &from
);
484 (void) sigprocmask(SIG_SETMASK
, &oldmask
, NULL
);
496 #define TIMER_INTERVAL 6
497 #define GETIFCONF_TIMER 30
499 static int left_until_advertise
;
501 /* Called every TIMER_INTERVAL */
506 static int left_until_getifconf
;
507 static int left_until_solicit
;
509 time
+= TIMER_INTERVAL
;
511 left_until_getifconf
-= TIMER_INTERVAL
;
512 left_until_advertise
-= TIMER_INTERVAL
;
513 left_until_solicit
-= TIMER_INTERVAL
;
515 if (left_until_getifconf
< 0) {
516 (void) initifs(s
, &g_joinaddr
, g_preference
);
517 left_until_getifconf
= GETIFCONF_TIMER
;
519 if (responder
&& left_until_advertise
<= 0) {
522 if (ntransmitted
< initial_advertisements
)
523 left_until_advertise
= initial_advert_interval
;
525 left_until_advertise
= min_adv_int
+
526 ((max_adv_int
- min_adv_int
) *
527 (random() % 1000)/1000);
528 } else if (solicit
&& left_until_solicit
<= 0) {
529 if (ntransmitted
< max_solicitations
) {
532 left_until_solicit
= solicitation_interval
;
535 if (!forever
&& nreceived
== 0)
539 age_table(TIMER_INTERVAL
);
540 (void) alarm(TIMER_INTERVAL
);
546 * Compose and transmit an ICMP ROUTER SOLICITATION REQUEST packet.
547 * The IP packet will be added on by the kernel.
550 solicitor(struct sockaddr_in
*sin
)
552 static uchar_t outpack
[MAXPACKET
];
553 register struct icmp
*icp
= (struct icmp
*)ALIGN(outpack
);
557 logtrace("Sending solicitation to %s\n",
558 pr_name(sin
->sin_addr
));
560 icp
->icmp_type
= ICMP_ROUTERSOLICIT
;
563 icp
->icmp_void
= 0; /* Reserved */
566 /* Compute ICMP checksum here */
567 icp
->icmp_cksum
= in_cksum((ushort_t
*)icp
, packetlen
);
569 if (isbroadcast(sin
))
570 i
= sendbcast(s
, (char *)outpack
, packetlen
);
571 else if (ismulticast(sin
))
572 i
= sendmcast(s
, (char *)outpack
, packetlen
, sin
);
576 li
= find_directly_connected_logint(sin
->sin_addr
, NULL
);
577 if (li
!= NULL
&& (li
->li_flags
& IFF_NORTEXCH
)) {
579 logtrace("Suppressing sending %s on %s "
580 "(no route exchange on interface)\n",
581 pr_type((int)icp
->icmp_type
), li
->li_name
);
585 i
= sendto(s
, (char *)outpack
, packetlen
, 0,
586 (struct sockaddr
*)sin
, sizeof (struct sockaddr
));
590 if (i
< 0 || i
!= packetlen
) {
594 logerr("wrote %s %d chars, ret=%d\n",
595 sendaddress
, packetlen
, i
);
602 * Compose and transmit an ICMP ROUTER ADVERTISEMENT packet.
603 * The IP packet will be added on by the kernel.
606 advertise(struct sockaddr_in
*sin
)
609 struct logint
*li
, *li_tmp
;
610 static uchar_t outpack
[MAXPACKET
];
611 register struct icmp_ra
*rap
= (struct icmp_ra
*)ALIGN(outpack
);
612 struct icmp_ra_addr
*ap
;
616 logtrace("Sending advertisement to %s\n",
617 pr_name(sin
->sin_addr
));
620 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
621 rap
->icmp_type
= ICMP_ROUTERADVERT
;
624 rap
->icmp_num_addrs
= 0;
626 rap
->icmp_lifetime
= htons(lifetime
);
627 packetlen
= ICMP_MINLEN
;
629 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
630 if (li
->li_state
& ST_DELETED
)
634 * XXX Just truncate the list of addresses.
635 * Should probably send multiple packets.
637 if (packetlen
+ rap
->icmp_wpa
* 4 > sizeof (outpack
)) {
639 logdebug("full packet: %d addresses\n",
640 rap
->icmp_num_addrs
);
643 ap
= (struct icmp_ra_addr
*)ALIGN(outpack
+ packetlen
);
644 ap
->addr
= li
->li_localaddr
.s_addr
;
645 ap
->preference
= htonl(li
->li_preference
);
646 packetlen
+= rap
->icmp_wpa
* 4;
647 rap
->icmp_num_addrs
++;
650 if (rap
->icmp_num_addrs
== 0)
653 /* Compute ICMP checksum here */
654 rap
->icmp_cksum
= in_cksum((ushort_t
*)rap
, packetlen
);
656 if (isbroadcast(sin
))
657 cc
= sendbcastif(s
, (char *)outpack
, packetlen
,
658 pi
->pi_logical_first
);
659 else if (ismulticast(sin
))
660 cc
= sendmcastif(s
, (char *)outpack
, packetlen
, sin
,
661 pi
->pi_logical_first
);
664 * Verify that the physical interface matches the
665 * destination address.
667 li_tmp
= find_directly_connected_logint(sin
->sin_addr
,
671 if (li_tmp
->li_flags
& IFF_NORTEXCH
) {
673 logtrace("Suppressing sending %s on %s "
674 "(no route exchange on "
676 pr_type((int)rap
->icmp_type
),
682 logdebug("Unicast to %s ",
683 pr_name(sin
->sin_addr
));
684 logdebug("on interface %s\n", pi
->pi_name
);
686 cc
= sendto(s
, (char *)outpack
, packetlen
, 0,
687 (struct sockaddr
*)sin
, sizeof (struct sockaddr
));
689 if (cc
< 0 || cc
!= packetlen
) {
693 logerr("wrote %s %d chars, ret=%d\n",
694 sendaddress
, packetlen
, cc
);
703 * Convert an ICMP "type" field to a printable string.
708 static char *ttab
[] = {
719 "Router Solicitation",
731 return ("OUT-OF-RANGE");
739 * Return a string name for the given IP address.
742 pr_name(struct in_addr addr
)
745 static char buf
[256];
747 phe
= gethostbyaddr((char *)&addr
.s_addr
, 4, AF_INET
);
749 return (inet_ntoa(addr
));
750 (void) sprintf(buf
, "%s (%s)", phe
->h_name
, inet_ntoa(addr
));
757 * Print out the packet, if it came from us. This logic is necessary
758 * because ALL readers of the ICMP socket get a copy of ALL ICMP packets
759 * which arrive ('tis only fair). This permits multiple copies of this
760 * program to be run without having intermingled output (or statistics!).
763 pr_pack(char *buf
, int cc
, struct sockaddr_in
*from
)
766 register struct icmp
*icp
;
771 ip
= (struct ip
*)ALIGN(buf
);
772 hlen
= ip
->ip_hl
<< 2;
773 if (cc
< hlen
+ ICMP_MINLEN
) {
775 logtrace("packet too short (%d bytes) from %s\n", cc
,
776 pr_name(from
->sin_addr
));
781 icp
= (struct icmp
*)ALIGN(buf
+ hlen
);
784 * Let's check if IFF_NORTEXCH flag is set on the interface which
785 * recevied this packet.
786 * TODO: this code can be re-written using one socket per interface
787 * to determine which interface the packet is recevied.
789 li
= find_directly_connected_logint(ip
->ip_src
, NULL
);
790 if (li
!= NULL
&& (li
->li_flags
& IFF_NORTEXCH
)) {
792 logtrace("Ignoring received %s on %s "
793 "(no route exchange on interface)",
794 pr_type((int)icp
->icmp_type
), li
->li_name
);
801 * Assume that we are running on a pre-4.3BSD system
802 * such as SunOS before 4.0
804 icp
= (struct icmp
*)ALIGN(buf
);
806 switch (icp
->icmp_type
) {
807 case ICMP_ROUTERADVERT
: {
808 struct icmp_ra
*rap
= (struct icmp_ra
*)ALIGN(icp
);
809 struct icmp_ra_addr
*ap
;
814 /* TBD verify that the link is multicast or broadcast */
815 /* XXX Find out the link it came in over? */
818 logdebug("ROUTER_ADVERTISEMENT: \n");
819 pr_hex(buf
+hlen
, cc
);
822 if (in_cksum((ushort_t
*)ALIGN(buf
+hlen
), cc
)) {
824 logtrace("ICMP %s from %s: Bad checksum\n",
825 pr_type((int)rap
->icmp_type
),
826 pr_name(from
->sin_addr
));
829 if (rap
->icmp_code
!= 0) {
831 logtrace("ICMP %s from %s: Code = %d\n",
832 pr_type((int)rap
->icmp_type
),
833 pr_name(from
->sin_addr
),
837 if (rap
->icmp_num_addrs
< 1) {
839 logtrace("ICMP %s from %s: No addresses\n",
840 pr_type((int)rap
->icmp_type
),
841 pr_name(from
->sin_addr
));
844 if (rap
->icmp_wpa
< 2) {
846 logtrace("ICMP %s from %s: Words/addr = %d\n",
847 pr_type((int)rap
->icmp_type
),
848 pr_name(from
->sin_addr
),
853 ICMP_MINLEN
+ rap
->icmp_num_addrs
* rap
->icmp_wpa
* 4) {
855 logtrace("ICMP %s from %s: Too short %d, %d\n",
856 pr_type((int)rap
->icmp_type
),
857 pr_name(from
->sin_addr
),
860 rap
->icmp_num_addrs
*
864 rap
->icmp_lifetime
= ntohs(rap
->icmp_lifetime
);
865 if ((rap
->icmp_lifetime
< 4 && rap
->icmp_lifetime
!= 0) ||
866 rap
->icmp_lifetime
> 9000) {
868 logtrace("ICMP %s from %s: Invalid lifetime %d\n",
869 pr_type((int)rap
->icmp_type
),
870 pr_name(from
->sin_addr
),
875 logtrace("ICMP %s from %s, lifetime %d\n",
876 pr_type((int)rap
->icmp_type
),
877 pr_name(from
->sin_addr
),
881 * Check that at least one router address is a neighbor
882 * on the arriving link.
884 for (i
= 0; (unsigned)i
< rap
->icmp_num_addrs
; i
++) {
886 ap
= (struct icmp_ra_addr
*)
887 ALIGN(buf
+ hlen
+ ICMP_MINLEN
+
888 i
* rap
->icmp_wpa
* 4);
889 ap
->preference
= ntohl(ap
->preference
);
890 ina
.s_addr
= ap
->addr
;
892 logtrace("\taddress %s, preference 0x%x\n",
896 if (find_directly_connected_logint(ina
, NULL
) !=
899 (long)ap
->preference
,
909 (void) alarm(TIMER_INTERVAL
);
914 case ICMP_ROUTERSOLICIT
: {
915 struct sockaddr_in sin
;
920 /* TBD verify that the link is multicast or broadcast */
921 /* XXX Find out the link it came in over? */
924 logdebug("ROUTER_SOLICITATION: \n");
925 pr_hex(buf
+hlen
, cc
);
928 if (in_cksum((ushort_t
*)ALIGN(buf
+hlen
), cc
)) {
930 logtrace("ICMP %s from %s: Bad checksum\n",
931 pr_type((int)icp
->icmp_type
),
932 pr_name(from
->sin_addr
));
935 if (icp
->icmp_code
!= 0) {
937 logtrace("ICMP %s from %s: Code = %d\n",
938 pr_type((int)icp
->icmp_type
),
939 pr_name(from
->sin_addr
),
944 if (cc
< ICMP_MINLEN
) {
946 logtrace("ICMP %s from %s: Too short %d, %d\n",
947 pr_type((int)icp
->icmp_type
),
948 pr_name(from
->sin_addr
),
955 logtrace("ICMP %s from %s\n",
956 pr_type((int)icp
->icmp_type
),
957 pr_name(from
->sin_addr
));
963 * Check that ip_src is either a neighbor
964 * on the arriving link or 0.
966 sin
.sin_family
= AF_INET
;
967 if (ip
->ip_src
.s_addr
== 0) {
969 * If it was sent to the broadcast address we respond
970 * to the broadcast address.
972 if (IN_CLASSD(ntohl(ip
->ip_dst
.s_addr
))) {
973 sin
.sin_addr
.s_addr
=
974 htonl(INADDR_ALLHOSTS_GROUP
);
976 sin
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
977 /* Restart the timer when we broadcast */
978 left_until_advertise
= min_adv_int
+
979 ((max_adv_int
- min_adv_int
)
980 * (random() % 1000)/1000);
984 logtrace("ICMP %s from %s: %s\n",
985 pr_type((int)icp
->icmp_type
),
986 pr_name(from
->sin_addr
),
987 "source not directly connected");
990 sin
.sin_addr
.s_addr
= ip
->ip_src
.s_addr
;
1004 * Checksum routine for Internet Protocol family headers (C Version)
1008 in_cksum(ushort_t
*addr
, int len
)
1010 register int nleft
= len
;
1011 register ushort_t
*w
= addr
;
1012 register ushort_t answer
;
1013 ushort_t odd_byte
= 0;
1014 register int sum
= 0;
1017 * Our algorithm is simple, using a 32 bit accumulator (sum),
1018 * we add sequential 16 bit words to it, and at the end, fold
1019 * back all the carry bits from the top 16 bits into the lower
1027 /* mop up an odd byte, if necessary */
1029 *(uchar_t
*)(&odd_byte
) = *(uchar_t
*)w
;
1034 * add back carry outs from top 16 bits to low 16 bits
1036 sum
= (sum
>> 16) + (sum
& 0xffff); /* add hi 16 to low 16 */
1037 sum
+= (sum
>> 16); /* add carry */
1038 answer
= ~sum
; /* truncate to 16 bits */
1045 * Print out statistics, and give up.
1046 * Heavily buffered stdio is used here, so that all the statistics
1047 * will be written with 1 sys-write call. This is nice when more
1048 * than one copy of the program is running on a terminal; it prevents
1049 * the statistics output from becoming intermingled.
1056 * Send out a packet with a preference so that all
1057 * hosts will know that we are dead.
1059 logerr("terminated\n");
1060 force_preference(IGNORE_PREFERENCE
);
1062 advertise(&whereto
);
1065 logtrace("\n----%s rdisc Statistics----\n", sendaddress
);
1066 logtrace("%d packets transmitted, ", ntransmitted
);
1067 logtrace("%d packets received, ", nreceived
);
1070 (void) fflush(stdout
);
1078 pr_hex(unsigned char *data
, int len
)
1086 char charstring
[17];
1088 (void) strcpy(charstring
, " "); /* 16 spaces */
1089 for (i
= 0; i
< 16; i
++) {
1091 * output the bytes one at a time,
1092 * not going past "len" bytes
1095 char ch
= *data
& 0x7f; /* strip parity */
1096 if (!isprint((uchar_t
)ch
))
1097 ch
= ' '; /* ensure printable */
1099 (void) fprintf(out
, "%02x ", *data
++);
1102 (void) fprintf(out
, " ");
1104 (void) fprintf(out
, " ");
1107 (void) fprintf(out
, " *%s*\n", charstring
);
1113 isbroadcast(struct sockaddr_in
*sin
)
1115 return (sin
->sin_addr
.s_addr
== htonl(INADDR_BROADCAST
));
1119 ismulticast(struct sockaddr_in
*sin
)
1121 return (IN_CLASSD(ntohl(sin
->sin_addr
.s_addr
)));
1124 /* From libc/rpc/pmap_rmt.c */
1127 /* Only send once per physical interface */
1129 sendbcast(int s
, char *packet
, int packetlen
)
1136 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1138 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1139 if (li
->li_state
& ST_DELETED
)
1142 if (li
->li_flags
& IFF_BROADCAST
) {
1149 cc
= sendbcastif(s
, packet
, packetlen
, li
);
1150 if (cc
!= packetlen
) {
1158 sendbcastif(int s
, char *packet
, int packetlen
, struct logint
*li
)
1161 struct sockaddr_in baddr
;
1162 struct icmp
*icp
= (struct icmp
*)ALIGN(packet
);
1164 baddr
.sin_family
= AF_INET
;
1166 if ((li
->li_flags
& IFF_BROADCAST
) == 0) {
1168 logtrace("Suppressing sending %s on %s "
1169 "(interface is not broadcast capable)\n",
1170 pr_type((int)icp
->icmp_type
), li
->li_name
);
1174 if (li
->li_flags
& IFF_NORTEXCH
) {
1176 logtrace("Suppressing sending %s on %s "
1177 "(no route exchange on interface)\n",
1178 pr_type((int)icp
->icmp_type
), li
->li_name
);
1183 baddr
.sin_addr
= li
->li_bcastaddr
;
1185 logdebug("Broadcast to %s\n",
1186 pr_name(baddr
.sin_addr
));
1187 cc
= sendto(s
, packet
, packetlen
, 0,
1188 (struct sockaddr
*)&baddr
, sizeof (struct sockaddr
));
1189 if (cc
!= packetlen
) {
1190 logperror("sendbcast: sendto");
1191 logerr("Cannot send broadcast packet to %s\n",
1192 pr_name(baddr
.sin_addr
));
1198 sendmcast(int s
, char *packet
, int packetlen
, struct sockaddr_in
*sin
)
1205 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1207 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1208 if (li
->li_state
& ST_DELETED
)
1211 if (li
->li_flags
& IFF_MULTICAST
) {
1218 cc
= sendmcastif(s
, packet
, packetlen
, sin
, li
);
1219 if (cc
!= packetlen
) {
1227 sendmcastif(int s
, char *packet
, int packetlen
, struct sockaddr_in
*sin
,
1231 struct sockaddr_in ifaddr
;
1232 struct icmp
*icp
= (struct icmp
*)ALIGN(packet
);
1234 ifaddr
.sin_family
= AF_INET
;
1236 if ((li
->li_flags
& IFF_MULTICAST
) == 0) {
1238 logtrace("Suppressing sending %s on %s "
1239 "(interface is not multicast capable)\n",
1240 pr_type((int)icp
->icmp_type
), li
->li_name
);
1244 if (li
->li_flags
& IFF_NORTEXCH
) {
1246 logtrace("Suppressing sending %s on %s "
1247 "(no route exchange on interface)\n",
1248 pr_type((int)icp
->icmp_type
), li
->li_name
);
1253 ifaddr
.sin_addr
= li
->li_address
;
1255 logdebug("Multicast to interface %s\n",
1256 pr_name(ifaddr
.sin_addr
));
1257 if (setsockopt(s
, IPPROTO_IP
, IP_MULTICAST_IF
,
1258 (char *)&ifaddr
.sin_addr
,
1259 sizeof (ifaddr
.sin_addr
)) < 0) {
1260 logperror("setsockopt (IP_MULTICAST_IF)");
1261 logerr("Cannot send multicast packet over interface %s\n",
1262 pr_name(ifaddr
.sin_addr
));
1265 cc
= sendto(s
, packet
, packetlen
, 0,
1266 (struct sockaddr
*)sin
, sizeof (struct sockaddr
));
1267 if (cc
!= packetlen
) {
1268 logperror("sendmcast: sendto");
1269 logerr("Cannot send multicast packet over interface %s\n",
1270 pr_name(ifaddr
.sin_addr
));
1278 (void) initifs(s
, &g_joinaddr
, g_preference
);
1282 force_preference(int preference
)
1287 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1288 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1289 if (li
->li_state
& ST_DELETED
)
1292 li
->li_preference
= preference
;
1298 * Returns -1 on failure.
1301 initifs(int s
, struct sockaddr_in
*joinaddr
, int preference
)
1304 struct ifreq ifreq
, *ifr
;
1305 struct lifreq lifreq
;
1313 char phyintname
[IFNAMSIZ
];
1315 int old_num_usable_interfaces
= num_usable_interfaces
;
1318 * Mark all interfaces so that we can determine which ones
1321 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1322 pi
->pi_state
|= ST_MARKED
;
1323 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1324 li
->li_state
|= ST_MARKED
;
1329 sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
1331 logperror("initifs: socket");
1336 if (ioctl(sock
, SIOCGIFNUM
, (char *)&numifs
) < 0) {
1337 logperror("initifs: SIOCGIFNUM");
1343 bufsize
= numifs
* sizeof (struct ifreq
);
1344 buf
= (char *)malloc(bufsize
);
1346 logerr("out of memory\n");
1351 ifc
.ifc_len
= bufsize
;
1353 if (ioctl(sock
, SIOCGIFCONF
, (char *)&ifc
) < 0) {
1354 logperror("initifs: ioctl (get interface configuration)");
1361 for (n
= ifc
.ifc_len
/sizeof (struct ifreq
); n
> 0; n
--, ifr
++) {
1364 * We need to use new interface ioctls to get 64-bit flags.
1366 (void) strncpy(lifreq
.lifr_name
, ifr
->ifr_name
,
1367 sizeof (ifr
->ifr_name
));
1368 if (ioctl(sock
, SIOCGLIFFLAGS
, (char *)&lifreq
) < 0) {
1369 logperror("initifs: ioctl (get interface flags)");
1372 if (ifr
->ifr_addr
.sa_family
!= AF_INET
)
1374 if ((lifreq
.lifr_flags
& IFF_UP
) == 0)
1376 if (lifreq
.lifr_flags
& IFF_LOOPBACK
)
1378 if ((lifreq
.lifr_flags
& (IFF_MULTICAST
| IFF_BROADCAST
)) == 0)
1381 /* Create the physical name by truncating at the ':' */
1382 strncpy(phyintname
, ifreq
.ifr_name
, sizeof (phyintname
));
1383 if ((cp
= strchr(phyintname
, ':')) != NULL
)
1386 pi
= find_phyint(phyintname
);
1388 pi
= add_phyint(phyintname
);
1390 logerr("out of memory\n");
1397 pi
->pi_state
&= ~ST_MARKED
;
1399 li
= find_logint(pi
, ifreq
.ifr_name
);
1402 * Detect significant changes.
1403 * We treat netmask changes as insignificant but all
1404 * other changes cause a delete plus add of the
1405 * logical interface.
1406 * Note: if the flags and localaddr are unchanged
1407 * then nothing but the netmask and the broadcast
1408 * address could have changed since the other addresses
1409 * are derived from the flags and the localaddr.
1411 struct logint newli
;
1413 if (!getconfig(sock
, lifreq
.lifr_flags
, &ifr
->ifr_addr
,
1419 if (newli
.li_flags
!= li
->li_flags
||
1420 newli
.li_localaddr
.s_addr
!=
1421 li
->li_localaddr
.s_addr
|| newli
.li_index
!=
1423 /* Treat as an interface deletion + addition */
1424 li
->li_state
|= ST_DELETED
;
1425 deleted_logint(li
, &newli
, s
, joinaddr
);
1427 li
= NULL
; /* li recreated below */
1430 * No significant changes.
1431 * Just update the netmask, and broadcast.
1433 li
->li_netmask
= newli
.li_netmask
;
1434 li
->li_bcastaddr
= newli
.li_bcastaddr
;
1438 li
= add_logint(pi
, ifreq
.ifr_name
);
1440 logerr("out of memory\n");
1448 if (!getconfig(sock
, lifreq
.lifr_flags
, &ifr
->ifr_addr
,
1453 li
->li_preference
= preference
;
1454 added_logint(li
, s
, joinaddr
);
1456 li
->li_state
&= ~ST_MARKED
;
1461 * Determine which interfaces have gone away.
1462 * The deletion is done in three phases:
1463 * 1. Mark ST_DELETED
1464 * 2. Inform using the deleted_* function.
1465 * 3. Unlink and free the actual memory.
1466 * Note that for #3 the physical interface must be deleted after
1468 * Also count the number of physical interfaces.
1470 num_usable_interfaces
= 0;
1472 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1473 if (pi
->pi_state
& ST_MARKED
) {
1475 pi
->pi_state
|= ST_DELETED
;
1477 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1478 if (li
->li_state
& ST_MARKED
) {
1480 li
->li_state
|= ST_DELETED
;
1483 if (!(pi
->pi_state
& ST_DELETED
))
1484 num_usable_interfaces
++;
1486 if (num_deletions
!= 0) {
1487 struct phyint
*nextpi
;
1488 struct logint
*nextli
;
1490 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1491 if (pi
->pi_state
& ST_DELETED
) {
1493 * By deleting the physical interface pi, all of
1494 * the corresponding logical interfaces will
1495 * also be deleted so there is no need to delete
1496 * them individually.
1498 deleted_phyint(pi
, s
, joinaddr
);
1500 for (li
= pi
->pi_logical_first
; li
!= NULL
;
1502 if (li
->li_state
& ST_DELETED
) {
1503 deleted_logint(li
, NULL
, s
,
1509 /* Do the actual linked list update + free */
1510 for (pi
= phyint
; pi
!= NULL
; pi
= nextpi
) {
1511 nextpi
= pi
->pi_next
;
1512 for (li
= pi
->pi_logical_first
; li
!= NULL
;
1514 nextli
= li
->li_next
;
1515 if (li
->li_state
& ST_DELETED
)
1518 if (pi
->pi_state
& ST_DELETED
)
1523 * When the set of available interfaces goes from zero to
1524 * non-zero we restart solicitations if '-s' was specified.
1526 if (old_num_usable_interfaces
== 0 && num_usable_interfaces
> 0 &&
1527 start_solicit
&& !solicit
) {
1529 logdebug("switching to solicitations: num if %d\n",
1530 num_usable_interfaces
);
1531 solicit
= start_solicit
;
1534 solicitor(&whereto
);
1540 getconfig(int sock
, uint64_t if_flags
, struct sockaddr
*addr
,
1541 struct ifreq
*ifr
, struct logint
*li
)
1544 struct sockaddr_in
*sin
;
1545 struct lifreq lifreq
;
1547 ifreq
= *ifr
; /* Copy name etc */
1549 li
->li_flags
= if_flags
;
1550 sin
= (struct sockaddr_in
*)ALIGN(addr
);
1551 li
->li_localaddr
= sin
->sin_addr
;
1553 (void) strlcpy(lifreq
.lifr_name
, ifr
->ifr_name
,
1554 sizeof (lifreq
.lifr_name
));
1555 if (ioctl(sock
, SIOCGLIFINDEX
, &lifreq
) < 0) {
1556 logperror("initifs: ioctl (get if index)");
1557 /* Continue with 0; a safe value never used for interfaces */
1560 li
->li_index
= lifreq
.lifr_index
;
1563 if (if_flags
& IFF_POINTOPOINT
) {
1564 li
->li_netmask
.s_addr
= (unsigned long)0xffffffff;
1565 if (ioctl(sock
, SIOCGIFDSTADDR
, (char *)&ifreq
) < 0) {
1566 logperror("initifs: ioctl (get dest addr)");
1569 /* A pt-pt link is identified by the remote address */
1570 sin
= (struct sockaddr_in
*)ALIGN(&ifreq
.ifr_addr
);
1571 li
->li_address
= sin
->sin_addr
;
1572 li
->li_remoteaddr
= sin
->sin_addr
;
1573 /* Simulate broadcast for pt-pt */
1574 li
->li_bcastaddr
= sin
->sin_addr
;
1575 li
->li_flags
|= IFF_BROADCAST
;
1578 * Non pt-pt links are identified by the local
1581 li
->li_address
= li
->li_localaddr
;
1582 li
->li_remoteaddr
= li
->li_address
;
1583 if (ioctl(sock
, SIOCGIFNETMASK
, (char *)&ifreq
) < 0) {
1584 logperror("initifs: ioctl (get netmask)");
1587 sin
= (struct sockaddr_in
*)ALIGN(&ifreq
.ifr_addr
);
1588 li
->li_netmask
= sin
->sin_addr
;
1589 if (if_flags
& IFF_BROADCAST
) {
1590 if (ioctl(sock
, SIOCGIFBRDADDR
, (char *)&ifreq
) < 0) {
1592 "initifs: ioctl (get broadcast address)");
1595 sin
= (struct sockaddr_in
*)ALIGN(&ifreq
.ifr_addr
);
1596 li
->li_bcastaddr
= sin
->sin_addr
;
1604 support_multicast(void)
1609 sock
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
1611 logperror("support_multicast: socket");
1615 if (setsockopt(sock
, IPPROTO_IP
, IP_MULTICAST_TTL
,
1616 (char *)&ttl
, sizeof (ttl
)) < 0) {
1625 * For a given destination address, find the logical interface to use.
1626 * If opi is NULL check all interfaces. Otherwise just match against
1627 * the specified physical interface.
1628 * Return logical interface if there's a match, NULL otherwise.
1630 static struct logint
*
1631 find_directly_connected_logint(struct in_addr in
, struct phyint
*opi
)
1641 for (; pi
!= NULL
; pi
= pi
->pi_next
) {
1642 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1643 if (li
->li_state
& ST_DELETED
)
1646 /* Check that the subnetwork numbers match */
1647 if ((in
.s_addr
& li
->li_netmask
.s_addr
) ==
1648 (li
->li_remoteaddr
.s_addr
&
1649 li
->li_netmask
.s_addr
))
1659 * INTERFACES - physical and logical identified by name
1664 report_interfaces(void)
1669 logdebug("\nInterfaces:\n\n");
1670 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1671 logdebug("Phyint %s state 0x%x\n",
1672 pi
->pi_name
, pi
->pi_state
);
1673 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1674 logdebug("IF %s state 0x%x, flags 0x%x, addr %s\n",
1675 li
->li_name
, li
->li_state
, li
->li_flags
,
1676 pr_name(li
->li_address
));
1677 logdebug("\tlocal %s pref 0x%x ",
1678 pr_name(li
->li_localaddr
), li
->li_preference
);
1679 logdebug("bcast %s\n",
1680 pr_name(li
->li_bcastaddr
));
1681 logdebug("\tremote %s ",
1682 pr_name(li
->li_remoteaddr
));
1683 logdebug("netmask %s\n",
1684 pr_name(li
->li_netmask
));
1689 static struct phyint
*
1690 find_phyint(char *name
)
1694 for (pi
= phyint
; pi
!= NULL
; pi
= pi
->pi_next
) {
1695 if (strcmp(pi
->pi_name
, name
) == 0)
1701 /* Assumes that the entry does not exist - caller must use find_* */
1702 static struct phyint
*
1703 add_phyint(char *name
)
1707 pi
= malloc(sizeof (*pi
));
1710 bzero((char *)pi
, sizeof (*pi
));
1712 strncpy(pi
->pi_name
, name
, sizeof (pi
->pi_name
));
1713 /* Link into list */
1714 pi
->pi_next
= phyint
;
1717 phyint
->pi_prev
= pi
;
1723 free_phyint(struct phyint
*pi
)
1725 assert(pi
->pi_logical_first
== NULL
);
1726 assert(pi
->pi_logical_last
== NULL
);
1728 if (pi
->pi_prev
== NULL
) {
1730 assert(phyint
== pi
);
1731 phyint
= pi
->pi_next
;
1733 assert(pi
->pi_prev
->pi_next
== pi
);
1734 pi
->pi_prev
->pi_next
= pi
->pi_next
;
1736 if (pi
->pi_next
!= NULL
) {
1737 assert(pi
->pi_next
->pi_prev
== pi
);
1738 pi
->pi_next
->pi_prev
= pi
->pi_prev
;
1743 static struct logint
*
1744 find_logint(struct phyint
*pi
, char *name
)
1748 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1749 if (strcmp(li
->li_name
, name
) == 0)
1756 * Assumes that the entry does not exist - caller must use find_*
1759 static struct logint
*
1760 add_logint(struct phyint
*pi
, char *name
)
1764 li
= malloc(sizeof (*li
));
1767 bzero((char *)li
, sizeof (*li
));
1769 strncpy(li
->li_name
, name
, sizeof (li
->li_name
));
1770 /* Link into list */
1771 li
->li_prev
= pi
->pi_logical_last
;
1772 if (pi
->pi_logical_last
== NULL
) {
1774 assert(pi
->pi_logical_first
== NULL
);
1775 pi
->pi_logical_first
= li
;
1777 pi
->pi_logical_last
->li_next
= li
;
1780 li
->li_physical
= pi
;
1781 pi
->pi_logical_last
= li
;
1787 free_logint(struct logint
*li
)
1791 pi
= li
->li_physical
;
1792 if (li
->li_prev
== NULL
) {
1794 assert(pi
->pi_logical_first
== li
);
1795 pi
->pi_logical_first
= li
->li_next
;
1797 assert(li
->li_prev
->li_next
== li
);
1798 li
->li_prev
->li_next
= li
->li_next
;
1800 if (li
->li_next
== NULL
) {
1802 assert(pi
->pi_logical_last
== li
);
1803 pi
->pi_logical_last
= li
->li_prev
;
1805 assert(li
->li_next
->li_prev
== li
);
1806 li
->li_next
->li_prev
= li
->li_prev
;
1812 /* Tell all the logical interfaces that they are going away */
1814 deleted_phyint(struct phyint
*pi
, int s
,
1815 struct sockaddr_in
*joinaddr
)
1820 logdebug("Deleting physical interface %s\n", pi
->pi_name
);
1822 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1823 li
->li_state
|= ST_DELETED
;
1825 for (li
= pi
->pi_logical_first
; li
!= NULL
; li
= li
->li_next
) {
1826 deleted_logint(li
, NULL
, s
, joinaddr
);
1831 * Join the multicast address if no other logical interface has done
1832 * so for this physical interface.
1835 added_logint(struct logint
*li
, int s
,
1836 struct sockaddr_in
*joinaddr
)
1839 logdebug("Adding logical interface %s\n", li
->li_name
);
1841 if ((!(li
->li_physical
->pi_state
& ST_JOINED
)) &&
1842 (!isbroadcast(joinaddr
))) {
1843 struct ip_mreq mreq
;
1845 mreq
.imr_multiaddr
= joinaddr
->sin_addr
;
1846 mreq
.imr_interface
= li
->li_address
;
1849 logdebug("Joining MC on interface %s\n", li
->li_name
);
1851 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
1852 (char *)&mreq
, sizeof (mreq
)) < 0) {
1853 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1855 li
->li_physical
->pi_state
|= ST_JOINED
;
1856 li
->li_state
|= ST_JOINED
;
1862 * Leave the multicast address if this logical interface joined it.
1863 * Look for a replacement logical interface for the same physical interface.
1864 * Remove any routes which are no longer reachable.
1866 * If newli is non-NULL, then it is likely that the address of a logical
1867 * interface has changed. In this case, the membership should be dropped using
1868 * the new address of the interface in question.
1870 * XXX When a physical interface is being deleted by deleted_phyint(), this
1871 * routine will be called for each logical interface associated with the
1872 * physical one. This should be made more efficient as there is no point in
1873 * searching for an alternate logical interface to add group membership to as
1874 * they all are marked ST_DELETED.
1877 deleted_logint(struct logint
*li
, struct logint
*newli
, int s
,
1878 struct sockaddr_in
*joinaddr
)
1884 logdebug("Deleting logical interface %s\n", li
->li_name
);
1886 assert(li
->li_state
& ST_DELETED
);
1888 if (li
->li_state
& ST_JOINED
) {
1889 struct ip_mreq mreq
;
1891 pi
= li
->li_physical
;
1892 assert(pi
->pi_state
& ST_JOINED
);
1893 assert(!isbroadcast(joinaddr
));
1895 mreq
.imr_multiaddr
= joinaddr
->sin_addr
;
1897 mreq
.imr_interface
= newli
->li_address
;
1899 mreq
.imr_interface
= li
->li_address
;
1902 logdebug("Leaving MC on interface %s\n", li
->li_name
);
1904 if (setsockopt(s
, IPPROTO_IP
, IP_DROP_MEMBERSHIP
,
1905 (char *)&mreq
, sizeof (mreq
)) < 0) {
1907 * EADDRNOTAVAIL will be returned if the interface has
1908 * been unplumbed or if the interface no longer has
1909 * IFF_MULTICAST set. The former is the common case
1910 * while the latter is rare so don't log the error
1911 * unless some other error was returned or if debug is
1914 if (errno
!= EADDRNOTAVAIL
) {
1915 logperror("setsockopt (IP_DROP_MEMBERSHIP)");
1917 logdebug("%s: %s\n",
1918 "setsockopt (IP_DROP_MEMBERSHIP)",
1922 li
->li_physical
->pi_state
&= ~ST_JOINED
;
1923 li
->li_state
&= ~ST_JOINED
;
1925 /* Is there another interface that can join? */
1926 for (oli
= pi
->pi_logical_first
; oli
!= NULL
;
1927 oli
= oli
->li_next
) {
1928 if (oli
->li_state
& ST_DELETED
)
1931 mreq
.imr_multiaddr
= joinaddr
->sin_addr
;
1932 mreq
.imr_interface
= oli
->li_address
;
1935 logdebug("Joining MC on interface %s\n",
1938 if (setsockopt(s
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
1939 (char *)&mreq
, sizeof (mreq
)) < 0) {
1940 logperror("setsockopt (IP_ADD_MEMBERSHIP)");
1942 pi
->pi_state
|= ST_JOINED
;
1943 oli
->li_state
|= ST_JOINED
;
1949 flush_unreachable_routers();
1958 struct in_addr router
;
1965 struct table
*table
;
1972 logdebug("\nRoutes:\n\n");
1975 logdebug("Router %s, pref 0x%x, time %d, %s kernel\n",
1976 pr_name(tp
->router
), tp
->preference
,
1978 (tp
->in_kernel
? "in" : "not in"));
1983 static struct table
*
1984 find_router(struct in_addr addr
)
1990 if (tp
->router
.s_addr
== addr
.s_addr
)
1998 max_preference(void)
2001 int max
= (int)IGNORE_PREFERENCE
;
2005 if (tp
->preference
> max
)
2006 max
= tp
->preference
;
2013 /* Note: this might leave the kernel with no default route for a short time. */
2017 struct table
**tpp
, *tp
;
2018 int recalculate_max
= 0;
2019 int max
= max_preference();
2022 while (*tpp
!= NULL
) {
2024 tp
->remaining_time
-= time
;
2025 if (tp
->remaining_time
<= 0) {
2028 logdebug("Timed out router %s\n",
2029 pr_name(tp
->router
));
2032 del_route(tp
->router
);
2033 if (best_preference
&&
2034 tp
->preference
== max
)
2041 if (recalculate_max
) {
2042 int max
= max_preference();
2044 if (max
!= IGNORE_PREFERENCE
) {
2047 if (tp
->preference
== max
&& !tp
->in_kernel
) {
2048 add_route(tp
->router
);
2058 * Remove any routes which are no longer directly connected.
2061 flush_unreachable_routers(void)
2063 struct table
**tpp
, *tp
;
2064 int recalculate_max
= 0;
2065 int max
= max_preference();
2068 while (*tpp
!= NULL
) {
2070 if (find_directly_connected_logint(tp
->router
, NULL
) == NULL
) {
2073 logdebug("Unreachable router %s\n",
2074 pr_name(tp
->router
));
2077 del_route(tp
->router
);
2078 if (best_preference
&&
2079 tp
->preference
== max
)
2086 if (recalculate_max
) {
2087 int max
= max_preference();
2089 if (max
!= IGNORE_PREFERENCE
) {
2092 if (tp
->preference
== max
&& !tp
->in_kernel
) {
2093 add_route(tp
->router
);
2103 record_router(struct in_addr router
, long preference
, int ttl
)
2106 int old_max
= max_preference();
2107 int changed_up
= 0; /* max preference could have increased */
2108 int changed_down
= 0; /* max preference could have decreased */
2111 logdebug("Recording %s, preference 0x%x\n",
2114 tp
= find_router(router
);
2116 if (tp
->preference
> preference
&&
2117 tp
->preference
== old_max
)
2119 else if (preference
> tp
->preference
)
2121 tp
->preference
= preference
;
2122 tp
->remaining_time
= ttl
;
2124 if (preference
> old_max
)
2126 tp
= (struct table
*)ALIGN(malloc(sizeof (struct table
)));
2128 logerr("Out of memory\n");
2131 tp
->router
= router
;
2132 tp
->preference
= preference
;
2133 tp
->remaining_time
= ttl
;
2138 if (!tp
->in_kernel
&&
2139 (!best_preference
|| tp
->preference
== max_preference()) &&
2140 tp
->preference
!= IGNORE_PREFERENCE
) {
2141 add_route(tp
->router
);
2144 if (tp
->preference
== IGNORE_PREFERENCE
&& tp
->in_kernel
) {
2145 del_route(tp
->router
);
2148 if (best_preference
&& changed_down
) {
2149 /* Check if we should add routes */
2150 int new_max
= max_preference();
2151 if (new_max
!= IGNORE_PREFERENCE
) {
2154 if (tp
->preference
== new_max
&&
2156 add_route(tp
->router
);
2163 if (best_preference
&& (changed_up
|| changed_down
)) {
2164 /* Check if we should remove routes already in the kernel */
2165 int new_max
= max_preference();
2168 if (tp
->preference
< new_max
&& tp
->in_kernel
) {
2169 del_route(tp
->router
);
2178 #include <net/route.h>
2181 add_route(struct in_addr addr
)
2184 logdebug("Add default route to %s\n", pr_name(addr
));
2185 rtioctl(addr
, SIOCADDRT
);
2189 del_route(struct in_addr addr
)
2192 logdebug("Delete default route to %s\n", pr_name(addr
));
2193 rtioctl(addr
, SIOCDELRT
);
2197 rtioctl(struct in_addr addr
, int op
)
2201 struct sockaddr_in
*sin
;
2202 bzero((char *)&rt
, sizeof (struct rtentry
));
2203 rt
.rt_dst
.sa_family
= AF_INET
;
2204 rt
.rt_gateway
.sa_family
= AF_INET
;
2205 sin
= (struct sockaddr_in
*)ALIGN(&rt
.rt_gateway
);
2206 sin
->sin_addr
= addr
;
2207 rt
.rt_flags
= RTF_UP
| RTF_GATEWAY
;
2209 sock
= socket(AF_INET
, SOCK_DGRAM
, IPPROTO_UDP
);
2211 logperror("rtioctl: socket");
2214 if (ioctl(sock
, op
, (char *)&rt
) < 0) {
2215 if (!(op
== SIOCADDRT
&& errno
== EEXIST
))
2216 logperror("ioctl (add/delete route)");
2229 static int logging
= 0;
2235 openlog("in.rdisc", LOG_PID
| LOG_CONS
, LOG_DAEMON
);
2240 logerr(fmt
, a
, b
, c
, d
, e
, f
, g
, h
)
2244 syslog(LOG_ERR
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2246 (void) fprintf(stderr
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2251 logtrace(fmt
, a
, b
, c
, d
, e
, f
, g
, h
)
2255 syslog(LOG_INFO
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2257 (void) fprintf(stdout
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2262 logdebug(fmt
, a
, b
, c
, d
, e
, f
, g
, h
)
2266 syslog(LOG_DEBUG
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2268 (void) fprintf(stdout
, fmt
, a
, b
, c
, d
, e
, f
, g
, h
);
2276 syslog(LOG_ERR
, "%s: %s\n", str
, strerror(errno
));
2278 (void) fprintf(stderr
, "%s: %s\n", str
, strerror(errno
));