2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgment:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * $FreeBSD: src/sbin/routed/rdisc.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $
39 #pragma ident "%Z%%M% %I% %E% SMI"
42 #include <netinet/in_systm.h>
43 #include <netinet/ip.h>
44 #include <netinet/ip_icmp.h>
49 * The size of the control buffer passed to recvmsg() used to receive
52 #define CONTROL_BUFSIZE 1024
54 /* router advertisement ICMP packet */
56 uint8_t icmp_type
; /* type of message */
57 uint8_t icmp_code
; /* type sub code */
58 uint16_t icmp_cksum
; /* ones complement cksum of struct */
59 uint8_t icmp_ad_num
; /* # of following router addresses */
60 uint8_t icmp_ad_asize
; /* 2--words in each advertisement */
61 uint16_t icmp_ad_life
; /* seconds of validity */
63 in_addr_t icmp_ad_addr
;
64 uint32_t icmp_ad_pref
;
68 /* router solicitation ICMP packet */
70 uint8_t icmp_type
; /* type of message */
71 uint8_t icmp_code
; /* type sub code */
72 uint16_t icmp_cksum
; /* ones complement cksum of struct */
73 uint32_t icmp_so_rsvd
;
83 int rdisc_sock
= -1; /* router-discovery raw socket */
84 int rdisc_mib_sock
= -1; /* AF_UNIX mib info socket */
85 static struct interface
*rdisc_sock_interface
; /* current rdisc interface */
87 struct timeval rdisc_timer
;
88 boolean_t rdisc_ok
; /* using solicited route */
91 int max_ads
; /* at least one per interface */
92 /* accumulated advertisements */
93 static struct dr
*cur_drp
;
97 * adjust unsigned preference by interface metric,
98 * without driving it to infinity
100 #define PREF(p, ifp) ((p) <= (uint32_t)(ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
101 : (p) - ((ifp)->int_metric))
103 static void rdisc_sort(void);
105 typedef enum { unicast
, bcast
, mcast
} dstaddr_t
;
107 /* dump an ICMP Router Discovery Advertisement Message */
109 trace_rdisc(const char *act
,
112 struct interface
*ifp
,
120 if (!TRACEPACKETS
|| ftrace
== 0)
125 if (p
->icmp
.icmp_type
== ICMP_ROUTERADVERT
) {
126 (void) fprintf(ftrace
, "%s Router Ad"
127 " from %s to %s via %s life=%d\n",
128 act
, naddr_ntoa(from
), naddr_ntoa(to
),
129 ifp
? ifp
->int_name
: "?",
130 ntohs(p
->ad
.icmp_ad_life
));
134 wp
= &p
->ad
.icmp_ad_info
[0].icmp_ad_addr
;
135 lim
= &wp
[(len
- sizeof (p
->ad
)) / sizeof (*wp
)];
136 for (i
= 0; i
< p
->ad
.icmp_ad_num
&& wp
<= lim
; i
++) {
137 (void) fprintf(ftrace
, "\t%s preference=%ld",
138 naddr_ntoa(wp
[0]), (long)ntohl(wp
[1]));
139 wp
+= p
->ad
.icmp_ad_asize
;
141 (void) fputc('\n', ftrace
);
144 trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x",
145 act
, naddr_ntoa(from
), naddr_ntoa(to
),
146 ifp
? ifp
->int_name
: "?",
147 ntohl(p
->so
.icmp_so_rsvd
));
152 * Prepare Router Discovery socket.
158 unsigned char ttl
= 1;
159 struct sockaddr_un laddr
;
162 if (rdisc_sock
< 0) {
164 drs
= rtmalloc(max_ads
* sizeof (struct dr
), "get_rdisc_sock");
165 (void) memset(drs
, 0, max_ads
* sizeof (struct dr
));
166 rdisc_sock
= socket(PF_INET
, SOCK_RAW
, IPPROTO_ICMP
);
168 BADERR(_B_TRUE
, "rdisc_sock = socket()");
169 fix_sock(rdisc_sock
, "rdisc_sock");
171 if (setsockopt(rdisc_sock
, IPPROTO_IP
, IP_RECVIF
, &on
,
173 BADERR(_B_FALSE
, "setsockopt(IP_RECVIF)");
175 if (setsockopt(rdisc_sock
, IPPROTO_IP
, IP_MULTICAST_TTL
,
176 &ttl
, sizeof (ttl
)) < 0)
178 "rdisc_sock setsockopt(IP_MULTICAST_TTL)");
181 * On Solaris also open an AF_UNIX socket to
182 * pass default router information to mib agent
185 rdisc_mib_sock
= socket(AF_UNIX
, SOCK_DGRAM
, 0);
186 if (rdisc_mib_sock
< 0) {
187 BADERR(_B_TRUE
, "rdisc_mib_sock = socket()");
190 bzero(&laddr
, sizeof (laddr
));
191 laddr
.sun_family
= AF_UNIX
;
193 (void) strncpy(laddr
.sun_path
, RDISC_SNMP_SOCKET
,
194 sizeof (laddr
.sun_path
));
195 len
= sizeof (struct sockaddr_un
);
197 (void) unlink(RDISC_SNMP_SOCKET
);
199 if (bind(rdisc_mib_sock
, (struct sockaddr
*)&laddr
, len
) < 0) {
200 BADERR(_B_TRUE
, "bind(rdisc_mib_sock)");
203 if (fcntl(rdisc_mib_sock
, F_SETFL
, O_NONBLOCK
) < 0) {
204 BADERR(_B_TRUE
, "rdisc_mib_sock fcntl O_NONBLOCK");
213 * Pick multicast group for router-discovery socket
216 set_rdisc_mg(struct interface
*ifp
,
217 int on
) /* 0=turn it off */
222 if (rdisc_sock
< 0) {
224 * Create the raw socket so that we can hear at least
225 * broadcast router discovery packets.
227 if ((ifp
->int_state
& IS_NO_RDISC
) == IS_NO_RDISC
||
233 if (!(ifp
->int_if_flags
& IFF_MULTICAST
)) {
234 /* Can't multicast, so no groups could have been joined. */
235 ifp
->int_state
&= ~(IS_ALL_HOSTS
| IS_ALL_ROUTERS
);
239 dosupply
= should_supply(ifp
);
241 (void) memset(&m
, 0, sizeof (m
));
242 m
.imr_interface
.s_addr
= ((ifp
->int_if_flags
& IFF_POINTOPOINT
) &&
243 (ifp
->int_dstaddr
!= 0) ? ifp
->int_dstaddr
: ifp
->int_addr
);
244 if (dosupply
|| (ifp
->int_state
& IS_NO_ADV_IN
) || !on
) {
245 /* stop listening to advertisements */
246 if (ifp
->int_state
& IS_ALL_HOSTS
) {
247 m
.imr_multiaddr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
248 if (setsockopt(rdisc_sock
, IPPROTO_IP
,
249 IP_DROP_MEMBERSHIP
, &m
, sizeof (m
)) < 0 &&
250 errno
!= EADDRNOTAVAIL
&& errno
!= ENOENT
)
251 LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
252 ifp
->int_state
&= ~IS_ALL_HOSTS
;
255 } else if (!(ifp
->int_state
& IS_ALL_HOSTS
)) {
256 /* start listening to advertisements */
257 m
.imr_multiaddr
.s_addr
= htonl(INADDR_ALLHOSTS_GROUP
);
258 if (setsockopt(rdisc_sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
259 &m
, sizeof (m
)) < 0) {
260 LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
262 ifp
->int_state
|= IS_ALL_HOSTS
;
266 if (!dosupply
|| (ifp
->int_state
& IS_NO_ADV_OUT
) ||
267 !IS_IFF_ROUTING(ifp
->int_if_flags
) || !on
) {
268 /* stop listening to solicitations */
269 if (ifp
->int_state
& IS_ALL_ROUTERS
) {
270 m
.imr_multiaddr
.s_addr
= htonl(INADDR_ALLRTRS_GROUP
);
271 if (setsockopt(rdisc_sock
, IPPROTO_IP
,
272 IP_DROP_MEMBERSHIP
, &m
, sizeof (m
)) < 0 &&
273 errno
!= EADDRNOTAVAIL
&& errno
!= ENOENT
)
274 LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
275 ifp
->int_state
&= ~IS_ALL_ROUTERS
;
278 } else if (!(ifp
->int_state
& IS_ALL_ROUTERS
)) {
279 /* start hearing solicitations */
280 m
.imr_multiaddr
.s_addr
= htonl(INADDR_ALLRTRS_GROUP
);
281 if (setsockopt(rdisc_sock
, IPPROTO_IP
, IP_ADD_MEMBERSHIP
,
282 &m
, sizeof (m
)) < 0) {
283 LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
285 ifp
->int_state
|= IS_ALL_ROUTERS
;
292 * start or stop supplying routes to other systems.
297 struct interface
*ifp
;
299 static boolean_t supplystate
= _B_FALSE
;
301 if (supplystate
== (fwd_interfaces
> 1))
303 supplystate
= fwd_interfaces
> 1;
305 trace_act("%d forwarding interfaces present; becoming %ssupplier",
306 fwd_interfaces
, supplystate
? "" : "non-");
309 /* Forget discovered routes. */
310 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
311 drp
->dr_recv_pref
= DEF_PREFERENCELEVEL
;
317 * Do not start advertising until we have heard some
320 LIM_SEC(rdisc_timer
, now
.tv_sec
+MIN_WAITTIME
);
322 /* get rid of any redirects */
326 * Flush out all those advertisements we had sent by sending
327 * one with lifetime=0.
333 * Switch router discovery multicast groups from soliciting
334 * to advertising or back.
336 for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
337 if (ifp
->int_state
& IS_BROKE
)
339 ifp
->int_rdisc_cnt
= 0;
340 ifp
->int_rdisc_timer
.tv_usec
= rdisc_timer
.tv_usec
;
341 ifp
->int_rdisc_timer
.tv_sec
= now
.tv_sec
+MIN_WAITTIME
;
342 set_rdisc_mg(ifp
, 1);
348 * Age discovered routes and find the best one
351 rdisc_age(in_addr_t bad_gate
)
359 * If we are being told about a bad router,
360 * then age the discovered default route, and if there is
361 * no alternative, solicit a replacement.
365 * Look for the bad discovered default route.
366 * Age it and note its interface.
368 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
373 * When we find the bad router, age the route
374 * to at most SUPPLY_INTERVAL.
375 * This is contrary to RFC 1256, but defends against
378 if (drp
->dr_gate
== bad_gate
) {
379 sec
= (now
.tv_sec
- drp
->dr_life
+
381 if (drp
->dr_ts
> sec
) {
382 trace_act("age 0.0.0.0 --> %s via %s",
383 naddr_ntoa(drp
->dr_gate
),
384 drp
->dr_ifp
->int_name
);
390 } else if (should_supply(NULL
)) {
392 * If switching from client to server, get rid of old
395 if (cur_drp
!= NULL
) {
396 rt
= rtget(RIP_DEFAULT
, 0);
398 * If there is a current default router, and the
399 * there is no rt_spare entry, create one
400 * for cur_drp to prevent segmentation fault
404 (void) memset(&new, 0, sizeof (new));
405 new.rts_ifp
= cur_drp
->dr_ifp
;
406 new.rts_gate
= cur_drp
->dr_gate
;
407 new.rts_router
= cur_drp
->dr_gate
;
408 new.rts_metric
= HOPCNT_INFINITY
-1;
409 new.rts_time
= now
.tv_sec
;
410 new.rts_origin
= RO_RDISC
;
411 rtadd(RIP_DEFAULT
, 0, RS_NOPROPAGATE
, &new);
420 if (cur_drp
!= NULL
) {
421 rt
= rtget(RIP_DEFAULT
, 0);
423 (void) memset(&new, 0, sizeof (new));
424 new.rts_ifp
= cur_drp
->dr_ifp
;
425 new.rts_gate
= cur_drp
->dr_gate
;
426 new.rts_router
= cur_drp
->dr_gate
;
427 new.rts_metric
= HOPCNT_INFINITY
-1;
428 new.rts_time
= now
.tv_sec
;
429 new.rts_origin
= RO_RDISC
;
430 rtadd(RIP_DEFAULT
, 0, RS_NOPROPAGATE
, &new);
436 * Delete old redirected routes to keep the kernel table small,
437 * and to prevent black holes. Check that the kernel table
438 * matches the daemon table (i.e. has the default route).
439 * But only if RIP is not running and we are not dealing with
440 * a bad gateway, since otherwise age() will be called.
442 if (rip_sock
< 0 && bad_gate
== 0)
448 * Zap all routes discovered via an interface that has gone bad
449 * This should only be called when !(ifp->int_state & IS_DUP)
450 * This is called by if_del and if_bad, and the interface pointer
451 * might not be valid after this.
454 if_bad_rdisc(struct interface
*ifp
)
458 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
459 if (drp
->dr_ifp
!= ifp
)
461 (void) memset(drp
, 0, sizeof (*drp
));
464 /* make a note to re-solicit, turn RIP on or off, etc. */
465 rdisc_timer
.tv_sec
= 0;
469 * Rewire all routes discovered via an interface that has gone bad
470 * This is only called by if_del.
473 if_rewire_rdisc(struct interface
*oldifp
, struct interface
*newifp
)
477 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
478 if (drp
->dr_ifp
!= oldifp
)
480 drp
->dr_ifp
= newifp
;
481 drp
->dr_pref
+= (newifp
->int_metric
- oldifp
->int_metric
);
482 drp
->dr_flags
|= DR_CHANGED
;
485 /* make a note to re-solicit, turn RIP on or off, etc. */
486 rdisc_timer
.tv_sec
= 0;
490 * Mark an interface ok for router discovering.
491 * This is called by if_ok and ifinit.
494 if_ok_rdisc(struct interface
*ifp
)
496 set_rdisc_mg(ifp
, 1);
498 ifp
->int_rdisc_cnt
= 0;
499 ifp
->int_rdisc_timer
.tv_sec
= now
.tv_sec
+
500 ((ifp
->int_state
& IS_NO_ADV_OUT
) ?
501 MAX_SOLICITATION_DELAY
: MIN_WAITTIME
);
502 if (timercmp(&rdisc_timer
, &ifp
->int_rdisc_timer
, > /* cstyle */))
503 rdisc_timer
= ifp
->int_rdisc_timer
;
507 * Get rid of a dead discovered router
510 del_rdisc(struct dr
*drp
)
512 struct interface
*ifp
;
516 struct rt_spare
*rts
= NULL
;
518 del_redirects(gate
= drp
->dr_gate
, 0);
522 rt
= rtget(RIP_DEFAULT
, 0);
524 trace_act("could not find default route in table");
526 for (i
= 0; i
< rt
->rt_num_spares
; i
++) {
527 if ((rt
->rt_spares
[i
].rts_gate
== drp
->dr_gate
) &&
528 (rt
->rt_spares
[i
].rts_origin
== RO_RDISC
)) {
529 rts
= &rt
->rt_spares
[i
];
536 trace_act("could not find default route "
537 "through %s in table", naddr_ntoa(drp
->dr_gate
));
540 /* Count the other discovered routers on the interface. */
543 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
544 if (drp
->dr_ts
!= 0 && drp
->dr_ifp
== ifp
)
549 * If that was the last good discovered router on the interface,
550 * then solicit a new one.
551 * This is contrary to RFC 1256, but defends against black holes.
554 trace_act("discovered router %s via %s"
555 " is bad--have %d remaining",
556 naddr_ntoa(gate
), ifp
->int_name
, i
);
557 } else if (ifp
->int_rdisc_cnt
>= MAX_SOLICITATIONS
) {
558 trace_act("last discovered router %s via %s"
559 " is bad--re-solicit",
560 naddr_ntoa(gate
), ifp
->int_name
);
561 ifp
->int_rdisc_cnt
= 0;
562 ifp
->int_rdisc_timer
.tv_sec
= 0;
565 trace_act("last discovered router %s via %s"
566 " is bad--wait to solicit",
567 naddr_ntoa(gate
), ifp
->int_name
);
572 /* Find the best discovered route, and discard stale routers. */
576 struct dr
*drp
, *new_drp
;
578 struct rt_spare
new, *rts
;
579 struct interface
*ifp
;
581 uint32_t new_pref
= DEF_PREFERENCELEVEL
;
582 int first_rdisc_slot
= 0;
584 boolean_t spares_avail
;
588 rt
= rtget(RIP_DEFAULT
, 0);
591 * If all the rt_spare entries are taken up with with default routes
592 * learnt from RIP (ie rts_origin = RO_RIP), bail out.
594 * We *always* prefer default routes learned via RIP
595 * (ie RO_RIP) over those learnt via RDISC (ie RO_RDISC).
596 * The rdisc machinery should not modify, replace or
597 * remove any existing default routes with RO_RIP set.
600 spares_avail
= _B_FALSE
;
601 for (j
= 0; j
< rt
->rt_num_spares
; j
++) {
602 rts
= &rt
->rt_spares
[j
];
603 if (rts
->rts_gate
== 0 || rts
->rts_origin
!= RO_RIP
||
604 rts
->rts_ifp
== &dummy_ifp
) {
605 spares_avail
= _B_TRUE
;
610 ptrsize
= (rt
->rt_num_spares
+ SPARE_INC
) *
611 sizeof (struct rt_spare
);
612 ptr
= realloc(rt
->rt_spares
, ptrsize
);
614 struct rt_spare
*tmprts
;
617 rts
= &rt
->rt_spares
[rt
->rt_num_spares
];
618 (void) memset(rts
, 0,
619 (SPARE_INC
* sizeof (struct rt_spare
)));
620 rt
->rt_num_spares
+= SPARE_INC
;
621 for (tmprts
= rts
, j
= SPARE_INC
;
622 j
!= 0; j
--, tmprts
++)
623 tmprts
->rts_metric
= HOPCNT_INFINITY
;
624 spares_avail
= _B_TRUE
;
630 /* Find the best RDISC advertiser */
633 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
638 /* Get rid of expired discovered routers. */
639 if (drp
->dr_ts
+ drp
->dr_life
<= now
.tv_sec
) {
644 LIM_SEC(rdisc_timer
, drp
->dr_ts
+drp
->dr_life
);
647 * Update preference with possibly changed interface
650 drp
->dr_pref
= PREF(drp
->dr_recv_pref
, ifp
);
653 * Prefer the current route to prevent thrashing.
654 * Prefer shorter lifetimes to speed the detection of
656 * Avoid sick interfaces.
658 if (new_drp
== NULL
||
659 (!((new_st
^ drp
->dr_ifp
->int_state
) & IS_SICK
) &&
660 (new_pref
< drp
->dr_pref
||
661 (new_pref
== drp
->dr_pref
&& (drp
== cur_drp
||
662 (new_drp
!= cur_drp
&&
663 new_drp
->dr_life
> drp
->dr_life
))))) ||
664 ((new_st
& IS_SICK
) &&
665 !(drp
->dr_ifp
->int_state
& IS_SICK
))) {
667 new_st
= drp
->dr_ifp
->int_state
;
668 new_pref
= drp
->dr_pref
;
673 * switch to a better RDISC advertiser
675 if ((new_drp
!= cur_drp
) || (rt
== NULL
)) {
676 rt
= rtget(RIP_DEFAULT
, 0);
679 * Purge the table of all the default routes that were
680 * learnt via RDISC, while keeping an eye the first available
681 * slot for the spare entry of new_drp
685 for (i
= 0; i
< rt
->rt_num_spares
; i
++) {
686 rts
= &rt
->rt_spares
[i
];
687 if ((rts
->rts_gate
== 0 ||
688 rts
->rts_ifp
== &dummy_ifp
) &&
689 first_rdisc_slot
== 0)
690 first_rdisc_slot
= i
;
691 if (rts
->rts_origin
== RO_RDISC
) {
693 if (first_rdisc_slot
== 0) {
694 first_rdisc_slot
= i
;
700 /* Stop using RDISC routes if they are all bad */
701 if (new_drp
== NULL
) {
702 trace_act("turn off Router Discovery client");
706 if (cur_drp
== NULL
) {
707 trace_act("turn on Router Discovery client"
709 naddr_ntoa(new_drp
->dr_gate
),
710 new_drp
->dr_ifp
->int_name
);
714 /* Prepare a spare entry for the new_drp */
715 (void) memset(&new, 0, sizeof (new));
716 new.rts_ifp
= new_drp
->dr_ifp
;
717 new.rts_gate
= new_drp
->dr_gate
;
718 new.rts_router
= new_drp
->dr_gate
;
719 new.rts_metric
= HOPCNT_INFINITY
-1;
720 new.rts_time
= now
.tv_sec
;
721 new.rts_origin
= RO_RDISC
;
723 * If there is no existing default route, add it
727 rtadd(RIP_DEFAULT
, 0, RS_NOPROPAGATE
, &new);
731 * Add the spare entry for the new_drp in
732 * the first available slot
734 trace_act("Switching to "
735 "default router with better "
736 "preference %s via %s ",
737 naddr_ntoa(new_drp
->dr_gate
),
738 new_drp
->dr_ifp
->int_name
);
739 rt
->rt_spares
[first_rdisc_slot
] = new;
740 rt
= NULL
; /* redo rt_spares */
745 * Get ready to redo the entire table. The table should
747 * a. empty rt_spare slots
748 * b. default routes learnt via RIP
749 * c. default route for the latest best RDISC advertiser
750 * d. default routes of other RDISC advertisers whose
751 * dr_pref == best RDISC advertiser->dr_pref
756 /* Redo the entire spare table (without touching RO_RIP entries) */
757 if (rdisc_ok
&& rt
== NULL
) {
760 * We've either just turned on router discovery,
761 * or switched to a router with better preference.
762 * Find all other default routers whose
763 * pref == cur_drp->dr_pref and add them as spares
766 rt
= rtget(RIP_DEFAULT
, 0);
768 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
769 boolean_t dr_done
= _B_FALSE
;
775 if (drp
->dr_pref
!= cur_drp
->dr_pref
&&
776 ((drp
->dr_flags
& DR_CHANGED
) == 0))
780 * Either pref matches cur_drp->dr_pref,
781 * or something has changed in this drp.
782 * In the former case, we may need to add
783 * this to rt_spares. In the latter case,
784 * if the pref has changed, need to take it
785 * out of rt_spares and the kernel.
787 * First, find an empty slot in rt_spares
788 * in case we have to add this drp to kernel.
789 * Also check if it is already there.
791 for (i
= 0; i
< rt
->rt_num_spares
; i
++) {
792 if (rt
->rt_spares
[i
].rts_gate
== 0) {
797 if ((rt
->rt_spares
[i
].rts_gate
==
799 (rt
->rt_spares
[i
].rts_origin
==
802 * a spare entry for this RDISC
803 * advertiser already exists. We need
804 * to check if this entry still belongs
812 drp
->dr_flags
&= ~DR_CHANGED
;
814 if (drp
->dr_pref
!= cur_drp
->dr_pref
) {
817 * The rt_spare of this RDISC advertiser
818 * needs to be removed as it no longer
819 * belongs in the table because its
820 * dr_pref is different than the latest
821 * RDISC advertiser's->dr_pref
823 rts_delete(rt
, &rt
->rt_spares
[i
]);
828 if (slot
< 0 && !dr_done
) {
829 ptrsize
= (rt
->rt_num_spares
+ SPARE_INC
) *
830 sizeof (struct rt_spare
);
831 ptr
= realloc(rt
->rt_spares
, ptrsize
);
833 struct rt_spare
*tmprts
;
836 slot
= rt
->rt_num_spares
;
837 rts
= &rt
->rt_spares
[rt
->rt_num_spares
];
838 (void) memset(rts
, 0, (SPARE_INC
*
839 sizeof (struct rt_spare
)));
840 rt
->rt_num_spares
+= SPARE_INC
;
841 for (tmprts
= rts
, i
= SPARE_INC
;
842 i
!= 0; i
--, tmprts
++)
848 if (slot
>= 0 && (dr_done
!= _B_TRUE
)) {
849 (void) memset(&new, 0, sizeof (new));
850 new.rts_ifp
= drp
->dr_ifp
;
851 new.rts_gate
= drp
->dr_gate
;
852 new.rts_router
= drp
->dr_gate
;
853 new.rts_metric
= HOPCNT_INFINITY
-1;
854 new.rts_time
= now
.tv_sec
;
855 new.rts_origin
= RO_RDISC
;
856 rt
->rt_spares
[slot
] = new;
857 trace_act("spare default %s via %s",
858 naddr_ntoa(drp
->dr_gate
),
859 drp
->dr_ifp
->int_name
);
864 /* turn RIP on or off */
865 if (!rdisc_ok
|| rip_interfaces
> 1) {
873 /* Handle a single address in an advertisement */
875 parse_ad(uint32_t from
,
877 uint32_t pref
, /* signed and in network order */
878 ushort_t life
, /* in host byte order */
879 struct interface
*ifp
)
881 static struct msg_limit bad_gate
;
882 struct dr
*drp
, *new_drp
;
886 if (gate
== RIP_DEFAULT
|| !check_dst(gate
)) {
887 msglim(&bad_gate
, from
, "router %s advertising bad gateway %s",
888 naddr_ntoa(from
), naddr_ntoa(gate
));
893 * ignore pointers to ourself and routes via unreachable networks
895 if (ifwithaddr(gate
, _B_TRUE
, _B_FALSE
) != 0) {
896 trace_pkt(" discard Router Discovery Ad pointing at us");
899 if (!on_net(gate
, ifp
->int_net
, ifp
->int_mask
)) {
900 trace_pkt(" discard Router Discovery Ad"
901 " toward unreachable net");
905 * Convert preference to an unsigned value
906 * and later bias it by the metric of the interface.
908 pref
= UNSIGN_PREF(ntohl(pref
));
910 if (pref
== DEF_PREFERENCELEVEL
|| life
< MIN_MAXADVERTISEINTERVAL
) {
911 pref
= DEF_PREFERENCELEVEL
;
915 for (new_drp
= NULL
, drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
916 /* accept new info for a familiar entry */
917 if ((drp
->dr_gate
== gate
) && (drp
->dr_ifp
== ifp
)) {
919 drp
->dr_flags
|= DR_CHANGED
;
924 continue; /* do not worry about dead ads */
926 if (drp
->dr_ts
== 0) {
927 new_drp
= drp
; /* use unused entry */
929 } else if (new_drp
== NULL
) {
930 /* look for an entry worse than the new one to reuse. */
931 if ((!(ifp
->int_state
& IS_SICK
) &&
932 (drp
->dr_ifp
->int_state
& IS_SICK
)) ||
933 (pref
> drp
->dr_pref
&&
934 !((ifp
->int_state
^ drp
->dr_ifp
->int_state
) &
938 } else if (new_drp
->dr_ts
!= 0) {
939 /* look for the least valuable entry to reuse */
940 if ((!(new_drp
->dr_ifp
->int_state
& IS_SICK
) &&
941 (drp
->dr_ifp
->int_state
& IS_SICK
)) ||
942 (new_drp
->dr_pref
> drp
->dr_pref
&&
943 !((new_drp
->dr_ifp
->int_state
^
944 drp
->dr_ifp
->int_state
) & IS_SICK
)))
949 /* if all of the current entries are better, add more drs[] */
950 if (new_drp
== NULL
) {
951 ptrsize
= (max_ads
+ MAX_ADS
) * sizeof (struct dr
);
952 ptr
= realloc(drs
, ptrsize
);
956 (void) memset(&drs
[max_ads
], 0, MAX_ADS
* sizeof (struct dr
));
957 new_drp
= &drs
[max_ads
];
962 * Pointer copy is safe here because if_del
963 * calls if_bad_rdisc first, so a non-NULL df_ifp
964 * is always a valid pointer.
966 new_drp
->dr_ifp
= ifp
;
967 new_drp
->dr_gate
= gate
;
968 new_drp
->dr_ts
= now
.tv_sec
;
969 new_drp
->dr_life
= life
;
970 new_drp
->dr_recv_pref
= pref
;
971 /* bias functional preference by metric of the interface */
972 new_drp
->dr_pref
= PREF(pref
, ifp
);
974 /* after hearing a good advertisement, stop asking */
975 if (!(ifp
->int_state
& IS_SICK
))
976 ifp
->int_rdisc_cnt
= MAX_SOLICITATIONS
;
981 * Compute the IP checksum. This assumes the packet is less than 32K long.
984 in_cksum(uint16_t *p
, uint_t len
)
987 int nwords
= len
>> 1;
989 while (nwords
-- != 0)
993 sum
+= *(uchar_t
*)p
;
995 /* end-around-carry */
996 sum
= (sum
>> 16) + (sum
& 0xffff);
1002 /* Send a router discovery advertisement or solicitation ICMP packet. */
1004 send_rdisc(union ad_u
*p
,
1006 struct interface
*ifp
,
1007 in_addr_t dst
, /* 0 or unicast destination */
1010 struct sockaddr_in sin
;
1014 struct in_addr addr
;
1017 * Don't send Rdisc packets on duplicate interfaces, we
1018 * don't want to generate duplicate packets.
1020 if (ifp
->int_state
& IS_DUP
)
1023 (void) memset(&sin
, 0, sizeof (sin
));
1024 sin
.sin_addr
.s_addr
= dst
;
1025 sin
.sin_family
= AF_INET
;
1028 case unicast
: /* unicast */
1030 flags
= MSG_DONTROUTE
;
1034 case bcast
: /* broadcast */
1035 if (ifp
->int_if_flags
& IFF_POINTOPOINT
) {
1036 msg
= "Send pt-to-pt";
1037 if (ifp
->int_dstaddr
== 0)
1038 sin
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
1040 sin
.sin_addr
.s_addr
= ifp
->int_dstaddr
;
1042 msg
= "Send broadcast";
1043 sin
.sin_addr
.s_addr
= ifp
->int_brdaddr
;
1047 case mcast
: /* multicast */
1048 msg
= "Send multicast";
1055 /* select the right interface. */
1056 ifindex
= (type
!= mcast
&& ifp
->int_phys
!= NULL
) ?
1057 ifp
->int_phys
->phyi_index
: 0;
1059 if (rdisc_sock_interface
!= ifp
) {
1061 * For multicast, we have to choose the source
1062 * address. This is either the local address
1063 * (non-point-to-point) or the remote address.
1065 addr
.s_addr
= (ifp
->int_if_flags
& IFF_POINTOPOINT
) ?
1066 ifp
->int_dstaddr
: ifp
->int_addr
;
1067 if (type
== mcast
&&
1068 setsockopt(rdisc_sock
, IPPROTO_IP
, IP_MULTICAST_IF
, &addr
,
1069 sizeof (addr
)) == -1) {
1070 LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
1073 rdisc_sock_interface
= ifp
;
1076 trace_rdisc(msg
, ifp
->int_addr
, sin
.sin_addr
.s_addr
, ifp
, p
, p_size
);
1078 if (0 > sendtoif(rdisc_sock
, p
, p_size
, flags
, &sin
, ifindex
)) {
1079 if (!(ifp
->int_state
& IS_BROKE
))
1080 writelog(LOG_WARNING
, "sendto(%s%s%s): %s",
1081 ifp
->int_name
, ", ",
1082 inet_ntoa(sin
.sin_addr
),
1083 rip_strerror(errno
));
1085 if_sick(ifp
, _B_FALSE
);
1090 /* Send an advertisement */
1092 send_adv(struct interface
*ifp
,
1098 if ((ifp
->int_state
& (IS_SUPPRESS_RDISC
|IS_FLUSH_RDISC
)) ==
1102 (void) memset(&u
, 0, sizeof (u
.ad
));
1104 u
.ad
.icmp_type
= ICMP_ROUTERADVERT
;
1105 u
.ad
.icmp_code
= ICMP_ROUTERADVERT_COMMON
;
1106 u
.ad
.icmp_ad_num
= 1;
1107 u
.ad
.icmp_ad_asize
= sizeof (u
.ad
.icmp_ad_info
[0])/4;
1109 u
.ad
.icmp_ad_life
= (stopint
|| !should_supply(ifp
) ||
1110 (ifp
->int_state
& IS_SUPPRESS_RDISC
)) ? 0 :
1111 htons(ifp
->int_rdisc_int
*3);
1113 /* Send the configured preference as a network byte order value */
1114 u
.ad
.icmp_ad_info
[0].icmp_ad_pref
= htonl(ifp
->int_rdisc_pref
);
1116 u
.ad
.icmp_ad_info
[0].icmp_ad_addr
= ifp
->int_addr
;
1118 u
.ad
.icmp_cksum
= in_cksum((uint16_t *)&u
.ad
, sizeof (u
.ad
));
1120 send_rdisc(&u
, sizeof (u
.ad
), ifp
, dst
, type
);
1122 if (ifp
->int_state
& IS_SUPPRESS_RDISC
)
1123 ifp
->int_state
&= ~IS_FLUSH_RDISC
;
1127 /* Advertise as a default router by way of router discovery. */
1129 rdisc_adv(boolean_t forceadv
)
1131 struct interface
*ifp
;
1133 if (!forceadv
&& !should_supply(NULL
))
1136 rdisc_timer
.tv_sec
= now
.tv_sec
+ NEVER
;
1138 for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
1139 if ((ifp
->int_state
& (IS_NO_ADV_OUT
| IS_BROKE
)) ||
1140 (!forceadv
&& !IS_IFF_ROUTING(ifp
->int_if_flags
)))
1143 /* skip interfaces we shouldn't use */
1144 if (IS_IFF_QUIET(ifp
->int_if_flags
))
1147 if (!timercmp(&ifp
->int_rdisc_timer
, &now
, > /* cstyle */) ||
1148 stopint
!= 0 || forceadv
) {
1149 send_adv(ifp
, htonl(INADDR_ALLHOSTS_GROUP
),
1150 (ifp
->int_state
& IS_BCAST_RDISC
) ? 1 : 2);
1151 ifp
->int_rdisc_cnt
++;
1153 intvl_random(&ifp
->int_rdisc_timer
,
1154 (ifp
->int_rdisc_int
*3)/4, ifp
->int_rdisc_int
);
1155 if (ifp
->int_rdisc_cnt
< MAX_INITIAL_ADVERTS
&&
1156 (ifp
->int_rdisc_timer
.tv_sec
>
1157 MAX_INITIAL_ADVERT_INTERVAL
)) {
1158 ifp
->int_rdisc_timer
.tv_sec
=
1159 MAX_INITIAL_ADVERT_INTERVAL
;
1161 timevaladd(&ifp
->int_rdisc_timer
, &now
);
1163 if (timercmp(&rdisc_timer
, &ifp
->int_rdisc_timer
,
1165 rdisc_timer
= ifp
->int_rdisc_timer
;
1170 /* Solicit for Router Discovery */
1174 struct interface
*ifp
;
1177 if (should_supply(NULL
))
1180 rdisc_timer
.tv_sec
= now
.tv_sec
+ NEVER
;
1182 for (ifp
= ifnet
; ifp
; ifp
= ifp
->int_next
) {
1183 if (0 != (ifp
->int_state
& (IS_NO_SOL_OUT
| IS_BROKE
)) ||
1184 ifp
->int_rdisc_cnt
>= MAX_SOLICITATIONS
)
1187 /* skip interfaces we shouldn't use */
1188 if (IS_IFF_QUIET(ifp
->int_if_flags
))
1191 if (!timercmp(&ifp
->int_rdisc_timer
, &now
, > /* cstyle */)) {
1192 (void) memset(&u
, 0, sizeof (u
.so
));
1193 u
.so
.icmp_type
= ICMP_ROUTERSOLICIT
;
1194 u
.so
.icmp_cksum
= in_cksum((uint16_t *)&u
.so
,
1196 send_rdisc(&u
, sizeof (u
.so
), ifp
,
1197 htonl(INADDR_ALLRTRS_GROUP
),
1198 ((ifp
->int_state
&IS_BCAST_RDISC
) ? bcast
: mcast
));
1200 if (++ifp
->int_rdisc_cnt
>= MAX_SOLICITATIONS
)
1203 ifp
->int_rdisc_timer
.tv_sec
= SOLICITATION_INTERVAL
;
1204 ifp
->int_rdisc_timer
.tv_usec
= 0;
1205 timevaladd(&ifp
->int_rdisc_timer
, &now
);
1208 if (timercmp(&rdisc_timer
, &ifp
->int_rdisc_timer
,
1210 rdisc_timer
= ifp
->int_rdisc_timer
;
1216 * check the IP header of a possible Router Discovery ICMP packet
1219 static struct interface
*
1220 ck_icmp(const char *act
,
1222 struct interface
*ifp
,
1230 if (p
->icmp
.icmp_type
== ICMP_ROUTERADVERT
) {
1231 type
= "advertisement";
1232 if (p
->icmp
.icmp_code
== ICMP_ROUTERADVERT_NOCOMMON
)
1233 return (NULL
); /* Mobile IP */
1234 } else if (p
->icmp
.icmp_type
== ICMP_ROUTERSOLICIT
) {
1235 type
= "solicitation";
1240 if (p
->icmp
.icmp_code
!= ICMP_ROUTERADVERT_COMMON
) {
1241 trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
1242 type
, p
->icmp
.icmp_code
, naddr_ntoa(from
), naddr_ntoa(to
));
1246 trace_rdisc(act
, from
, to
, ifp
, p
, len
);
1249 trace_pkt("unknown interface for router-discovery %s from %s "
1250 "to %s", type
, naddr_ntoa(from
), naddr_ntoa(to
));
1256 /* Read packets from the router discovery socket */
1261 static struct msg_limit bad_asize
, bad_len
;
1262 struct sockaddr_in from
;
1267 uint16_t s
[PKTLEN
/sizeof (uint16_t)];
1268 uint8_t b
[PKTLEN
/sizeof (uint8_t)];
1273 struct interface
*ifp
;
1274 boolean_t needsort
= _B_FALSE
;
1277 uint8_t ancillary_data
[CONTROL_BUFSIZE
];
1279 iov
.iov_base
= &buf
;
1280 iov
.iov_len
= sizeof (buf
);
1283 msg
.msg_name
= &from
;
1284 msg
.msg_control
= &ancillary_data
;
1287 msg
.msg_namelen
= sizeof (from
);
1288 msg
.msg_controllen
= sizeof (ancillary_data
);
1289 cc
= recvmsg(rdisc_sock
, &msg
, 0);
1291 if (cc
< 0 && errno
!= EWOULDBLOCK
)
1292 LOGERR("recvmsg(rdisc_sock)");
1296 hlen
= buf
.pkt
.ip
.ip_hl
<< 2;
1297 if (cc
< hlen
+ ICMP_MINLEN
)
1299 /* LINTED [alignment will be lw aligned] */
1300 p
= (union ad_u
*)&buf
.pkt
.b
[hlen
];
1304 * If we could tell the interface on which a packet from
1305 * address 0 arrived, we could deal with such solicitations.
1307 ifp
= receiving_interface(&msg
, _B_FALSE
);
1308 ifp
= ck_icmp("Recv", from
.sin_addr
.s_addr
, ifp
,
1309 buf
.pkt
.ip
.ip_dst
.s_addr
, p
, cc
);
1313 if (IS_IFF_QUIET(ifp
->int_if_flags
)) {
1314 trace_misc("discard RDISC packet received over %s, %X",
1315 ifp
->int_name
, ifp
->int_if_flags
);
1319 if (from
.sin_addr
.s_addr
!= 0 &&
1320 ifwithaddr(from
.sin_addr
.s_addr
, _B_FALSE
, _B_FALSE
)) {
1322 "discard our own Router Discovery message");
1326 /* The remote address *must* be directly connected. */
1327 if (!remote_address_ok(ifp
, from
.sin_addr
.s_addr
)) {
1328 trace_misc("discard rdisc message; source %s not on "
1329 "interface %s", naddr_ntoa(from
.sin_addr
.s_addr
),
1334 switch (p
->icmp
.icmp_type
) {
1335 case ICMP_ROUTERADVERT
:
1336 if (ifp
->int_state
& IS_NO_ADV_IN
)
1339 if (p
->ad
.icmp_ad_asize
*2*sizeof (wp
[0]) <
1340 sizeof (p
->ad
.icmp_ad_info
[0])) {
1341 msglim(&bad_asize
, from
.sin_addr
.s_addr
,
1342 "intolerable rdisc address size=%d",
1343 p
->ad
.icmp_ad_asize
);
1346 if (p
->ad
.icmp_ad_num
== 0) {
1347 trace_pkt(" empty?");
1350 if (cc
< (sizeof (p
->ad
) -
1351 sizeof (p
->ad
.icmp_ad_info
) +
1352 (p
->ad
.icmp_ad_num
*
1353 sizeof (p
->ad
.icmp_ad_info
[0])))) {
1354 msglim(&bad_len
, from
.sin_addr
.s_addr
,
1355 "rdisc length %d does not match ad_num"
1356 " %d", cc
, p
->ad
.icmp_ad_num
);
1361 wp
= &p
->ad
.icmp_ad_info
[0].icmp_ad_addr
;
1362 for (n
= 0; n
< p
->ad
.icmp_ad_num
; n
++) {
1363 parse_ad(from
.sin_addr
.s_addr
,
1365 ntohs(p
->ad
.icmp_ad_life
), ifp
);
1366 wp
+= p
->ad
.icmp_ad_asize
;
1371 case ICMP_ROUTERSOLICIT
:
1372 if (!should_supply(ifp
))
1374 if ((ifp
->int_state
& IS_NO_ADV_OUT
) ||
1375 !IS_IFF_ROUTING(ifp
->int_if_flags
))
1381 * We should handle messages from address 0,
1382 * but cannot due to kernel limitations.
1385 /* Respond with a point-to-point advertisement */
1386 send_adv(ifp
, from
.sin_addr
.s_addr
, 0);
1400 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++)
1401 if (drp
->dr_ts
!= 0)
1406 rdisc_suppress(struct interface
*ifp
)
1408 if (ifp
->int_state
& IS_ADV_OUT
) {
1409 msglog("%s \"rdisc_adv\" specified, will not "
1410 "suppress rdisc adv", ifp
->int_name
);
1412 if (ifp
->int_state
& IS_SUPPRESS_RDISC
)
1414 ifp
->int_state
|= (IS_SUPPRESS_RDISC
|IS_FLUSH_RDISC
);
1415 trace_misc("suppress rdisc adv on %s", ifp
->int_name
);
1416 rdisc_timer
.tv_sec
= 0;
1421 rdisc_restore(struct interface
*ifp
)
1423 if ((ifp
->int_state
& IS_SUPPRESS_RDISC
) == 0)
1425 ifp
->int_state
&= ~(IS_SUPPRESS_RDISC
|IS_FLUSH_RDISC
);
1426 trace_misc("restoring rdisc adv on %s", ifp
->int_name
);
1427 rdisc_timer
.tv_sec
= 0;
1431 process_d_mib_sock(void)
1435 struct sockaddr_un from
;
1439 rdisc_info_t rdisc_info
;
1444 fromlen
= (socklen_t
)sizeof (from
);
1445 len
= recvfrom(rdisc_mib_sock
, &command
, sizeof (int), 0,
1446 (struct sockaddr
*)&from
, &fromlen
);
1448 if (len
< sizeof (int) || command
!= RDISC_SNMP_INFO_REQ
) {
1449 trace_misc("Bad command on rdisc_mib_sock");
1454 * Count number of good routers
1456 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
1457 if (drp
->dr_ts
!= 0) {
1462 rdisc_info
.info_type
= RDISC_SNMP_INFO_RESPONSE
;
1463 rdisc_info
.info_version
= RDISC_SNMP_INFO_VER
;
1464 rdisc_info
.info_num_of_routers
= num
;
1466 (void) sendto(rdisc_mib_sock
, &rdisc_info
, sizeof (rdisc_info_t
), 0,
1467 (struct sockaddr
*)&from
, fromlen
);
1469 for (drp
= drs
; drp
< &drs
[max_ads
]; drp
++) {
1470 if (drp
->dr_ts
!= 0) {
1471 def_router
.defr_info_type
= RDISC_DEF_ROUTER_INFO
;
1472 def_router
.defr_version
= RDISC_DEF_ROUTER_VER
;
1473 def_router
.defr_index
=
1474 drp
->dr_ifp
->int_phys
->phyi_index
;
1475 def_router
.defr_life
= drp
->dr_life
;
1476 def_router
.defr_addr
.s_addr
= drp
->dr_gate
;
1477 def_router
.defr_pref
= drp
->dr_pref
;
1478 (void) sendto(rdisc_mib_sock
, &def_router
,
1479 sizeof (defr_t
), 0, (struct sockaddr
*)&from
,