rdisc.c revision 3173664e967186de0a5f0e61548c25996fa6d37f
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgment:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "defs.h"
#include <netinet/in_systm.h>
#include <fcntl.h>
#include <strings.h>
/*
* The size of the control buffer passed to recvmsg() used to receive
* ancillary data.
*/
#define CONTROL_BUFSIZE 1024
/* router advertisement ICMP packet */
struct icmp_ad {
struct icmp_ad_info {
} icmp_ad_info[1];
};
/* router solicitation ICMP packet */
struct icmp_so {
};
union ad_u {
};
struct timeval rdisc_timer;
#define MAX_ADS 16
int max_ads; /* at least one per interface */
/* accumulated advertisements */
/*
* adjust unsigned preference by interface metric,
* without driving it to infinity
*/
: (p) - ((ifp)->int_metric))
static void rdisc_sort(void);
/* dump an ICMP Router Discovery Advertisement Message */
static void
trace_rdisc(const char *act,
union ad_u *p,
{
int i;
if (!TRACEPACKETS || ftrace == 0)
return;
lastlog();
" from %s to %s via %s life=%d\n",
if (!TRACECONTENTS)
return;
}
} else {
trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x",
}
}
/*
* Prepare Router Discovery socket.
*/
static void
get_rdisc_sock(void)
{
int on = 1;
unsigned char ttl = 1;
struct sockaddr_un laddr;
int len;
if (rdisc_sock < 0) {
if (rdisc_sock < 0)
sizeof (on)))
"rdisc_sock setsockopt(IP_MULTICAST_TTL)");
/*
* On Solaris also open an AF_UNIX socket to
* pass default router information to mib agent
*/
if (rdisc_mib_sock < 0) {
}
len = sizeof (struct sockaddr_un);
(void) unlink(RDISC_SNMP_SOCKET);
}
}
fix_select();
}
}
/*
* Pick multicast group for router-discovery socket
*/
void
int on) /* 0=turn it off */
{
struct ip_mreq m;
if (rdisc_sock < 0) {
/*
* Create the raw socket so that we can hear at least
* broadcast router discovery packets.
*/
!on)
return;
}
/* Can't multicast, so no groups could have been joined. */
return;
}
(void) memset(&m, 0, sizeof (m));
/* stop listening to advertisements */
IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
}
/* start listening to advertisements */
&m, sizeof (m)) < 0) {
LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
} else {
}
}
/* stop listening to solicitations */
IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
}
/* start hearing solicitations */
&m, sizeof (m)) < 0) {
LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
} else {
}
}
}
/*
* start or stop supplying routes to other systems.
*/
void
set_supplier(void)
{
return;
trace_act("%d forwarding interfaces present; becoming %ssupplier",
if (supplystate) {
/* Forget discovered routes. */
}
rdisc_age(0);
/*
* Do not start advertising until we have heard some
* RIP routes.
*/
/* get rid of any redirects */
del_redirects(0, 0);
} else {
/*
* Flush out all those advertisements we had sent by sending
* one with lifetime=0.
*/
}
/*
* Switch router discovery multicast groups from soliciting
* to advertising or back.
*/
continue;
ifp->int_rdisc_cnt = 0;
}
}
/*
* Age discovered routes and find the best one
*/
void
{
/*
* If we are being told about a bad router,
* then age the discovered default route, and if there is
* no alternative, solicit a replacement.
*/
if (bad_gate != 0) {
/*
* Look for the bad discovered default route.
* Age it and note its interface.
*/
continue;
/*
* When we find the bad router, age the route
* to at most SUPPLY_INTERVAL.
* This is contrary to RFC 1256, but defends against
* black holes.
*/
trace_act("age 0.0.0.0 --> %s via %s",
}
break;
}
}
} else if (should_supply(NULL)) {
/*
* If switching from client to server, get rid of old
* default routes.
*/
/*
* If there is a current default router, and the
* there is no rt_spare entry, create one
* for cur_drp to prevent segmentation fault
* at rdisc_sort.
*/
}
rdisc_sort();
}
}
rdisc_sol();
}
}
rdisc_sort();
/*
* Delete old redirected routes to keep the kernel table small,
* and to prevent black holes. Check that the kernel table
* matches the daemon table (i.e. has the default route).
* But only if RIP is not running and we are not dealing with
* a bad gateway, since otherwise age() will be called.
*/
age(0);
}
/*
* Zap all routes discovered via an interface that has gone bad
* This should only be called when !(ifp->int_state & IS_DUP)
* This is called by if_del and if_bad, and the interface pointer
* might not be valid after this.
*/
void
{
continue;
}
/* make a note to re-solicit, turn RIP on or off, etc. */
rdisc_timer.tv_sec = 0;
}
/*
* Rewire all routes discovered via an interface that has gone bad
* This is only called by if_del.
*/
void
{
continue;
}
/* make a note to re-solicit, turn RIP on or off, etc. */
rdisc_timer.tv_sec = 0;
}
/*
* Mark an interface ok for router discovering.
* This is called by if_ok and ifinit.
*/
void
{
ifp->int_rdisc_cnt = 0;
}
/*
* Get rid of a dead discovered router
*/
static void
{
int i;
trace_act("could not find default route in table");
} else {
for (i = 0; i < rt->rt_num_spares; i++) {
break;
}
}
else
trace_act("could not find default route "
}
/* Count the other discovered routers on the interface. */
i = 0;
i++;
}
/*
* If that was the last good discovered router on the interface,
* then solicit a new one.
* This is contrary to RFC 1256, but defends against black holes.
*/
if (i != 0) {
trace_act("discovered router %s via %s"
" is bad--have %d remaining",
trace_act("last discovered router %s via %s"
" is bad--re-solicit",
ifp->int_rdisc_cnt = 0;
rdisc_sol();
} else {
trace_act("last discovered router %s via %s"
" is bad--wait to solicit",
}
}
/* Find the best discovered route, and discard stale routers. */
static void
rdisc_sort(void)
{
int first_rdisc_slot = 0;
int j;
void *ptr;
/*
* If all the rt_spare entries are taken up with with default routes
* learnt from RIP (ie rts_origin = RO_RIP), bail out.
* NOTE:
* We *always* prefer default routes learned via RIP
* (ie RO_RIP) over those learnt via RDISC (ie RO_RDISC).
* The rdisc machinery should not modify, replace or
* remove any existing default routes with RO_RIP set.
*/
for (j = 0; j < rt->rt_num_spares; j++) {
break;
}
}
if (!spares_avail) {
sizeof (struct rt_spare);
j != 0; j--, tmprts++)
} else {
return;
}
}
}
/* Find the best RDISC advertiser */
continue;
/* Get rid of expired discovered routers. */
continue;
}
/*
* Update preference with possibly changed interface
* metric.
*/
/*
* Prefer the current route to prevent thrashing.
* Prefer shorter lifetimes to speed the detection of
* bad routers.
* Avoid sick interfaces.
*/
}
}
/*
* switch to a better RDISC advertiser
*/
/*
* Purge the table of all the default routes that were
* learnt via RDISC, while keeping an eye the first available
* slot for the spare entry of new_drp
*/
int i;
for (i = 0; i < rt->rt_num_spares; i++) {
first_rdisc_slot == 0)
first_rdisc_slot = i;
if (first_rdisc_slot == 0) {
first_rdisc_slot = i;
}
}
}
}
/* Stop using RDISC routes if they are all bad */
trace_act("turn off Router Discovery client");
} else {
trace_act("turn on Router Discovery client"
" using %s via %s",
}
/* Prepare a spare entry for the new_drp */
/*
* If there is no existing default route, add it
* to rts_spare[0].
*/
} else {
/*
* Add the spare entry for the new_drp in
* the first available slot
*/
trace_act("Switching to "
"default router with better "
"preference %s via %s ",
}
}
/*
* Get ready to redo the entire table. The table should
* only include :
* a. empty rt_spare slots
* b. default routes learnt via RIP
* c. default route for the latest best RDISC advertiser
* d. default routes of other RDISC advertisers whose
* dr_pref == best RDISC advertiser->dr_pref
*/
}
/* Redo the entire spare table (without touching RO_RIP entries) */
int i;
/*
* We've either just turned on router discovery,
* or switched to a router with better preference.
* Find all other default routers whose
* pref == cur_drp->dr_pref and add them as spares
*/
int slot = -1;
continue;
continue;
/*
* Either pref matches cur_drp->dr_pref,
* or something has changed in this drp.
* In the former case, we may need to add
* this to rt_spares. In the latter case,
* if the pref has changed, need to take it
* out of rt_spares and the kernel.
*
* First, find an empty slot in rt_spares
* in case we have to add this drp to kernel.
* Also check if it is already there.
*/
for (i = 0; i < rt->rt_num_spares; i++) {
if (slot < 0)
slot = i;
continue;
}
RO_RDISC)) {
/*
* a spare entry for this RDISC
* advertiser already exists. We need
* to check if this entry still belongs
* in the table
*/
break;
}
}
if (dr_done) {
/*
* The rt_spare of this RDISC advertiser
* needs to be removed as it no longer
* belongs in the table because its
* dr_pref is different than the latest
* RDISC advertiser's->dr_pref
*/
}
continue;
}
sizeof (struct rt_spare);
sizeof (struct rt_spare)));
i != 0; i--, tmprts++)
tmprts->rts_metric =
}
}
trace_act("spare default %s via %s",
}
}
}
/* turn RIP on or off */
rip_on(0);
} else {
rip_off();
}
}
/* Handle a single address in an advertisement */
static void
{
void *ptr;
return;
}
/*
* ignore pointers to ourself and routes via unreachable networks
*/
trace_pkt(" discard Router Discovery Ad pointing at us");
return;
}
trace_pkt(" discard Router Discovery Ad"
" toward unreachable net");
return;
}
/*
* Convert preference to an unsigned value
* and later bias it by the metric of the interface.
*/
life = 0;
}
/* accept new info for a familiar entry */
break;
}
if (life == 0)
continue; /* do not worry about dead ads */
/* look for an entry worse than the new one to reuse. */
IS_SICK)))
/* look for the least valuable entry to reuse */
}
}
/* if all of the current entries are better, add more drs[] */
return;
}
/*
* Pointer copy is safe here because if_del
* calls if_bad_rdisc first, so a non-NULL df_ifp
* is always a valid pointer.
*/
/* bias functional preference by metric of the interface */
/* after hearing a good advertisement, stop asking */
}
/*
* Compute the IP checksum. This assumes the packet is less than 32K long.
*/
static uint16_t
{
while (nwords-- != 0)
sum += *p++;
if (len & 1)
/* end-around-carry */
return (~sum);
}
/* Send a router discovery advertisement or solicitation ICMP packet. */
static void
send_rdisc(union ad_u *p,
{
struct sockaddr_in sin;
int flags = 0;
const char *msg;
int ifindex;
/*
* Don't send Rdisc packets on duplicate interfaces, we
* don't want to generate duplicate packets.
*/
return;
switch (type) {
case unicast: /* unicast */
default:
msg = "Send";
break;
case bcast: /* broadcast */
msg = "Send pt-to-pt";
if (ifp->int_dstaddr == 0)
else
} else {
msg = "Send broadcast";
}
break;
case mcast: /* multicast */
msg = "Send multicast";
break;
}
if (rdisc_sock < 0)
if (rdisc_sock_interface != ifp) {
/* select the right interface. */
sizeof (ifindex)) == -1) {
LOGERR("setsockopt(rdisc_sock, IP_XMIT_IF)");
return;
}
/*
* For multicast, we have to choose the source
* address. This is either the local address
* (non-point-to-point) or the remote address.
*/
sizeof (addr)) == -1) {
LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
return;
}
}
}
}
/* Send an advertisement */
static void
{
union ad_u u;
return;
/* Send the configured preference as a network byte order value */
}
/* Advertise as a default router by way of router discovery. */
void
{
return;
continue;
/* skip interfaces we shouldn't use */
continue;
ifp->int_rdisc_cnt++;
}
}
> /* cstyle */))
}
}
/* Solicit for Router Discovery */
void
rdisc_sol(void)
{
union ad_u u;
if (should_supply(NULL))
return;
continue;
/* skip interfaces we shouldn't use */
continue;
sizeof (u.so));
continue;
}
> /* cstyle */))
}
}
/*
* check the IP header of a possible Router Discovery ICMP packet
* Returns 0 if bad
*/
static struct interface *
union ad_u *p,
{
const char *type;
type = "advertisement";
return (NULL); /* Mobile IP */
type = "solicitation";
} else {
return (NULL);
}
trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
return (NULL);
}
trace_pkt("unknown interface for router-discovery %s from %s "
return (ifp);
}
/* Read packets from the router discovery socket */
void
read_d(void)
{
#define PKTLEN 512
struct sockaddr_in from;
struct {
union {
} pkt;
} buf;
union ad_u *p;
for (;;) {
if (cc <= 0) {
LOGERR("recvmsg(rdisc_sock)");
break;
}
continue;
/* LINTED [alignment will be lw aligned] */
/*
* If we could tell the interface on which a packet from
* address 0 arrived, we could deal with such solicitations.
*/
continue;
trace_misc("discard RDISC packet received over %s, %X",
continue;
}
trace_pkt(" "
"discard our own Router Discovery message");
continue;
}
/* The remote address *must* be directly connected. */
trace_misc("discard rdisc message; source %s not on "
continue;
}
case ICMP_ROUTERADVERT:
continue;
sizeof (p->ad.icmp_ad_info[0])) {
"intolerable rdisc address size=%d",
p->ad.icmp_ad_asize);
continue;
}
if (p->ad.icmp_ad_num == 0) {
trace_pkt(" empty?");
continue;
}
sizeof (p->ad.icmp_ad_info) +
(p->ad.icmp_ad_num *
sizeof (p->ad.icmp_ad_info[0])))) {
"rdisc length %d does not match ad_num"
continue;
}
for (n = 0; n < p->ad.icmp_ad_num; n++) {
}
break;
case ICMP_ROUTERSOLICIT:
if (!should_supply(ifp))
continue;
continue;
if (stopint != 0)
continue;
/*
* We should handle messages from address 0,
* but cannot due to kernel limitations.
*/
/* Respond with a point-to-point advertisement */
break;
}
}
if (needsort)
rdisc_sort();
}
void
rdisc_dump(void)
{
}
void
{
msglog("%s \"rdisc_adv\" specified, will not "
} else {
return;
rdisc_timer.tv_sec = 0;
}
}
void
{
return;
rdisc_timer.tv_sec = 0;
}
void
process_d_mib_sock(void)
{
struct sockaddr_un from;
int command;
extern int max_ads;
int num = 0;
trace_misc("Bad command on rdisc_mib_sock");
return;
}
/*
* Count number of good routers
*/
num++;
}
}
fromlen);
}
}
}