10Sstevel@tonic-gate /*
2*5381Smeem * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate *
50Sstevel@tonic-gate * Copyright (c) 1995
60Sstevel@tonic-gate * The Regents of the University of California. All rights reserved.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
90Sstevel@tonic-gate * modification, are permitted provided that the following conditions
100Sstevel@tonic-gate * are met:
110Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
120Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
130Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
140Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
150Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
160Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software
170Sstevel@tonic-gate * must display the following acknowledgment:
180Sstevel@tonic-gate * This product includes software developed by the University of
190Sstevel@tonic-gate * California, Berkeley and its contributors.
200Sstevel@tonic-gate * 4. Neither the name of the University nor the names of its contributors
210Sstevel@tonic-gate * may be used to endorse or promote products derived from this software
220Sstevel@tonic-gate * without specific prior written permission.
230Sstevel@tonic-gate *
240Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
250Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
260Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
270Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
280Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
290Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
300Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
310Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
320Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
330Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
340Sstevel@tonic-gate * SUCH DAMAGE.
350Sstevel@tonic-gate *
360Sstevel@tonic-gate * $FreeBSD: src/sbin/routed/rdisc.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $
370Sstevel@tonic-gate */
380Sstevel@tonic-gate
390Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
400Sstevel@tonic-gate
410Sstevel@tonic-gate #include "defs.h"
420Sstevel@tonic-gate #include <netinet/in_systm.h>
430Sstevel@tonic-gate #include <netinet/ip.h>
440Sstevel@tonic-gate #include <netinet/ip_icmp.h>
453284Sapersson #include <fcntl.h>
463284Sapersson #include <strings.h>
470Sstevel@tonic-gate
480Sstevel@tonic-gate /*
490Sstevel@tonic-gate * The size of the control buffer passed to recvmsg() used to receive
500Sstevel@tonic-gate * ancillary data.
510Sstevel@tonic-gate */
520Sstevel@tonic-gate #define CONTROL_BUFSIZE 1024
530Sstevel@tonic-gate
540Sstevel@tonic-gate /* router advertisement ICMP packet */
550Sstevel@tonic-gate struct icmp_ad {
560Sstevel@tonic-gate uint8_t icmp_type; /* type of message */
570Sstevel@tonic-gate uint8_t icmp_code; /* type sub code */
580Sstevel@tonic-gate uint16_t icmp_cksum; /* ones complement cksum of struct */
590Sstevel@tonic-gate uint8_t icmp_ad_num; /* # of following router addresses */
600Sstevel@tonic-gate uint8_t icmp_ad_asize; /* 2--words in each advertisement */
610Sstevel@tonic-gate uint16_t icmp_ad_life; /* seconds of validity */
620Sstevel@tonic-gate struct icmp_ad_info {
630Sstevel@tonic-gate in_addr_t icmp_ad_addr;
640Sstevel@tonic-gate uint32_t icmp_ad_pref;
650Sstevel@tonic-gate } icmp_ad_info[1];
660Sstevel@tonic-gate };
670Sstevel@tonic-gate
680Sstevel@tonic-gate /* router solicitation ICMP packet */
690Sstevel@tonic-gate struct icmp_so {
700Sstevel@tonic-gate uint8_t icmp_type; /* type of message */
710Sstevel@tonic-gate uint8_t icmp_code; /* type sub code */
720Sstevel@tonic-gate uint16_t icmp_cksum; /* ones complement cksum of struct */
730Sstevel@tonic-gate uint32_t icmp_so_rsvd;
740Sstevel@tonic-gate };
750Sstevel@tonic-gate
760Sstevel@tonic-gate union ad_u {
770Sstevel@tonic-gate struct icmp icmp;
780Sstevel@tonic-gate struct icmp_ad ad;
790Sstevel@tonic-gate struct icmp_so so;
800Sstevel@tonic-gate };
810Sstevel@tonic-gate
820Sstevel@tonic-gate
830Sstevel@tonic-gate int rdisc_sock = -1; /* router-discovery raw socket */
843284Sapersson int rdisc_mib_sock = -1; /* AF_UNIX mib info socket */
850Sstevel@tonic-gate static struct interface *rdisc_sock_interface; /* current rdisc interface */
860Sstevel@tonic-gate
870Sstevel@tonic-gate struct timeval rdisc_timer;
880Sstevel@tonic-gate boolean_t rdisc_ok; /* using solicited route */
890Sstevel@tonic-gate
900Sstevel@tonic-gate #define MAX_ADS 16
910Sstevel@tonic-gate int max_ads; /* at least one per interface */
920Sstevel@tonic-gate /* accumulated advertisements */
933284Sapersson static struct dr *cur_drp;
943284Sapersson struct dr *drs;
950Sstevel@tonic-gate
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate * adjust unsigned preference by interface metric,
980Sstevel@tonic-gate * without driving it to infinity
990Sstevel@tonic-gate */
1000Sstevel@tonic-gate #define PREF(p, ifp) ((p) <= (uint32_t)(ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
1010Sstevel@tonic-gate : (p) - ((ifp)->int_metric))
1020Sstevel@tonic-gate
1030Sstevel@tonic-gate static void rdisc_sort(void);
1040Sstevel@tonic-gate
1050Sstevel@tonic-gate typedef enum { unicast, bcast, mcast } dstaddr_t;
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate /* dump an ICMP Router Discovery Advertisement Message */
1080Sstevel@tonic-gate static void
trace_rdisc(const char * act,uint32_t from,uint32_t to,struct interface * ifp,union ad_u * p,uint_t len)1090Sstevel@tonic-gate trace_rdisc(const char *act,
1100Sstevel@tonic-gate uint32_t from,
1110Sstevel@tonic-gate uint32_t to,
1120Sstevel@tonic-gate struct interface *ifp,
1130Sstevel@tonic-gate union ad_u *p,
1140Sstevel@tonic-gate uint_t len)
1150Sstevel@tonic-gate {
1160Sstevel@tonic-gate int i;
1170Sstevel@tonic-gate n_long *wp, *lim;
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate if (!TRACEPACKETS || ftrace == 0)
1210Sstevel@tonic-gate return;
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate lastlog();
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
1260Sstevel@tonic-gate (void) fprintf(ftrace, "%s Router Ad"
1270Sstevel@tonic-gate " from %s to %s via %s life=%d\n",
1280Sstevel@tonic-gate act, naddr_ntoa(from), naddr_ntoa(to),
1290Sstevel@tonic-gate ifp ? ifp->int_name : "?",
1300Sstevel@tonic-gate ntohs(p->ad.icmp_ad_life));
1310Sstevel@tonic-gate if (!TRACECONTENTS)
1320Sstevel@tonic-gate return;
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
1350Sstevel@tonic-gate lim = &wp[(len - sizeof (p->ad)) / sizeof (*wp)];
1360Sstevel@tonic-gate for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
1370Sstevel@tonic-gate (void) fprintf(ftrace, "\t%s preference=%ld",
1380Sstevel@tonic-gate naddr_ntoa(wp[0]), (long)ntohl(wp[1]));
1390Sstevel@tonic-gate wp += p->ad.icmp_ad_asize;
1400Sstevel@tonic-gate }
1410Sstevel@tonic-gate (void) fputc('\n', ftrace);
1420Sstevel@tonic-gate
1430Sstevel@tonic-gate } else {
1440Sstevel@tonic-gate trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x",
1450Sstevel@tonic-gate act, naddr_ntoa(from), naddr_ntoa(to),
1460Sstevel@tonic-gate ifp ? ifp->int_name : "?",
1470Sstevel@tonic-gate ntohl(p->so.icmp_so_rsvd));
1480Sstevel@tonic-gate }
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate /*
1520Sstevel@tonic-gate * Prepare Router Discovery socket.
1530Sstevel@tonic-gate */
1540Sstevel@tonic-gate static void
get_rdisc_sock(void)1550Sstevel@tonic-gate get_rdisc_sock(void)
1560Sstevel@tonic-gate {
1570Sstevel@tonic-gate int on = 1;
1580Sstevel@tonic-gate unsigned char ttl = 1;
1593284Sapersson struct sockaddr_un laddr;
1603284Sapersson int len;
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate if (rdisc_sock < 0) {
1630Sstevel@tonic-gate max_ads = MAX_ADS;
1640Sstevel@tonic-gate drs = rtmalloc(max_ads * sizeof (struct dr), "get_rdisc_sock");
1650Sstevel@tonic-gate (void) memset(drs, 0, max_ads * sizeof (struct dr));
1660Sstevel@tonic-gate rdisc_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
1670Sstevel@tonic-gate if (rdisc_sock < 0)
1680Sstevel@tonic-gate BADERR(_B_TRUE, "rdisc_sock = socket()");
1690Sstevel@tonic-gate fix_sock(rdisc_sock, "rdisc_sock");
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_RECVIF, &on,
1720Sstevel@tonic-gate sizeof (on)))
1730Sstevel@tonic-gate BADERR(_B_FALSE, "setsockopt(IP_RECVIF)");
1740Sstevel@tonic-gate
1750Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_TTL,
1760Sstevel@tonic-gate &ttl, sizeof (ttl)) < 0)
1770Sstevel@tonic-gate DBGERR(_B_TRUE,
1780Sstevel@tonic-gate "rdisc_sock setsockopt(IP_MULTICAST_TTL)");
1790Sstevel@tonic-gate
1803284Sapersson /*
1813284Sapersson * On Solaris also open an AF_UNIX socket to
1823284Sapersson * pass default router information to mib agent
1833284Sapersson */
1843284Sapersson
1853284Sapersson rdisc_mib_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
1863284Sapersson if (rdisc_mib_sock < 0) {
1873284Sapersson BADERR(_B_TRUE, "rdisc_mib_sock = socket()");
1883284Sapersson }
1893284Sapersson
1903284Sapersson bzero(&laddr, sizeof (laddr));
1913284Sapersson laddr.sun_family = AF_UNIX;
1923284Sapersson
1933284Sapersson (void) strncpy(laddr.sun_path, RDISC_SNMP_SOCKET,
1943284Sapersson sizeof (laddr.sun_path));
1953284Sapersson len = sizeof (struct sockaddr_un);
1963284Sapersson
1973284Sapersson (void) unlink(RDISC_SNMP_SOCKET);
1983284Sapersson
1993284Sapersson if (bind(rdisc_mib_sock, (struct sockaddr *)&laddr, len) < 0) {
2003284Sapersson BADERR(_B_TRUE, "bind(rdisc_mib_sock)");
2013284Sapersson }
2023284Sapersson
2033284Sapersson if (fcntl(rdisc_mib_sock, F_SETFL, O_NONBLOCK) < 0) {
2043284Sapersson BADERR(_B_TRUE, "rdisc_mib_sock fcntl O_NONBLOCK");
2053284Sapersson }
2063284Sapersson
2070Sstevel@tonic-gate fix_select();
2080Sstevel@tonic-gate }
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate
2120Sstevel@tonic-gate /*
2130Sstevel@tonic-gate * Pick multicast group for router-discovery socket
2140Sstevel@tonic-gate */
2150Sstevel@tonic-gate void
set_rdisc_mg(struct interface * ifp,int on)2160Sstevel@tonic-gate set_rdisc_mg(struct interface *ifp,
2170Sstevel@tonic-gate int on) /* 0=turn it off */
2180Sstevel@tonic-gate {
2190Sstevel@tonic-gate struct ip_mreq m;
2200Sstevel@tonic-gate boolean_t dosupply;
2210Sstevel@tonic-gate
2220Sstevel@tonic-gate if (rdisc_sock < 0) {
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate * Create the raw socket so that we can hear at least
2250Sstevel@tonic-gate * broadcast router discovery packets.
2260Sstevel@tonic-gate */
2270Sstevel@tonic-gate if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC ||
2280Sstevel@tonic-gate !on)
2290Sstevel@tonic-gate return;
2300Sstevel@tonic-gate get_rdisc_sock();
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate
2330Sstevel@tonic-gate if (!(ifp->int_if_flags & IFF_MULTICAST)) {
2340Sstevel@tonic-gate /* Can't multicast, so no groups could have been joined. */
2350Sstevel@tonic-gate ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
2360Sstevel@tonic-gate return;
2370Sstevel@tonic-gate }
2380Sstevel@tonic-gate
2390Sstevel@tonic-gate dosupply = should_supply(ifp);
2400Sstevel@tonic-gate
2410Sstevel@tonic-gate (void) memset(&m, 0, sizeof (m));
2420Sstevel@tonic-gate m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) &&
2430Sstevel@tonic-gate (ifp->int_dstaddr != 0) ? ifp->int_dstaddr : ifp->int_addr);
2440Sstevel@tonic-gate if (dosupply || (ifp->int_state & IS_NO_ADV_IN) || !on) {
2450Sstevel@tonic-gate /* stop listening to advertisements */
2460Sstevel@tonic-gate if (ifp->int_state & IS_ALL_HOSTS) {
2470Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
2480Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP,
2490Sstevel@tonic-gate IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
2500Sstevel@tonic-gate errno != EADDRNOTAVAIL && errno != ENOENT)
2510Sstevel@tonic-gate LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
2520Sstevel@tonic-gate ifp->int_state &= ~IS_ALL_HOSTS;
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate } else if (!(ifp->int_state & IS_ALL_HOSTS)) {
2560Sstevel@tonic-gate /* start listening to advertisements */
2570Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
2580Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
2590Sstevel@tonic-gate &m, sizeof (m)) < 0) {
2600Sstevel@tonic-gate LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
2610Sstevel@tonic-gate } else {
2620Sstevel@tonic-gate ifp->int_state |= IS_ALL_HOSTS;
2630Sstevel@tonic-gate }
2640Sstevel@tonic-gate }
2650Sstevel@tonic-gate
2660Sstevel@tonic-gate if (!dosupply || (ifp->int_state & IS_NO_ADV_OUT) ||
2670Sstevel@tonic-gate !IS_IFF_ROUTING(ifp->int_if_flags) || !on) {
2680Sstevel@tonic-gate /* stop listening to solicitations */
2690Sstevel@tonic-gate if (ifp->int_state & IS_ALL_ROUTERS) {
2700Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
2710Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP,
2720Sstevel@tonic-gate IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
2730Sstevel@tonic-gate errno != EADDRNOTAVAIL && errno != ENOENT)
2740Sstevel@tonic-gate LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
2750Sstevel@tonic-gate ifp->int_state &= ~IS_ALL_ROUTERS;
2760Sstevel@tonic-gate }
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate } else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
2790Sstevel@tonic-gate /* start hearing solicitations */
2800Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
2810Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
2820Sstevel@tonic-gate &m, sizeof (m)) < 0) {
2830Sstevel@tonic-gate LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
2840Sstevel@tonic-gate } else {
2850Sstevel@tonic-gate ifp->int_state |= IS_ALL_ROUTERS;
2860Sstevel@tonic-gate }
2870Sstevel@tonic-gate }
2880Sstevel@tonic-gate }
2890Sstevel@tonic-gate
2900Sstevel@tonic-gate
2910Sstevel@tonic-gate /*
2920Sstevel@tonic-gate * start or stop supplying routes to other systems.
2930Sstevel@tonic-gate */
2940Sstevel@tonic-gate void
set_supplier(void)2950Sstevel@tonic-gate set_supplier(void)
2960Sstevel@tonic-gate {
2970Sstevel@tonic-gate struct interface *ifp;
2980Sstevel@tonic-gate struct dr *drp;
2990Sstevel@tonic-gate static boolean_t supplystate = _B_FALSE;
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate if (supplystate == (fwd_interfaces > 1))
3020Sstevel@tonic-gate return;
3030Sstevel@tonic-gate supplystate = fwd_interfaces > 1;
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate trace_act("%d forwarding interfaces present; becoming %ssupplier",
3060Sstevel@tonic-gate fwd_interfaces, supplystate ? "" : "non-");
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate if (supplystate) {
3090Sstevel@tonic-gate /* Forget discovered routes. */
3100Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
3110Sstevel@tonic-gate drp->dr_recv_pref = DEF_PREFERENCELEVEL;
3120Sstevel@tonic-gate drp->dr_life = 0;
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate rdisc_age(0);
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate /*
3170Sstevel@tonic-gate * Do not start advertising until we have heard some
3180Sstevel@tonic-gate * RIP routes.
3190Sstevel@tonic-gate */
3200Sstevel@tonic-gate LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
3210Sstevel@tonic-gate
3220Sstevel@tonic-gate /* get rid of any redirects */
3230Sstevel@tonic-gate del_redirects(0, 0);
3240Sstevel@tonic-gate } else {
3250Sstevel@tonic-gate /*
3260Sstevel@tonic-gate * Flush out all those advertisements we had sent by sending
3270Sstevel@tonic-gate * one with lifetime=0.
3280Sstevel@tonic-gate */
3290Sstevel@tonic-gate rdisc_adv(_B_TRUE);
3300Sstevel@tonic-gate }
3310Sstevel@tonic-gate
3320Sstevel@tonic-gate /*
3330Sstevel@tonic-gate * Switch router discovery multicast groups from soliciting
3340Sstevel@tonic-gate * to advertising or back.
3350Sstevel@tonic-gate */
3360Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) {
3370Sstevel@tonic-gate if (ifp->int_state & IS_BROKE)
3380Sstevel@tonic-gate continue;
3390Sstevel@tonic-gate ifp->int_rdisc_cnt = 0;
3400Sstevel@tonic-gate ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
3410Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
3420Sstevel@tonic-gate set_rdisc_mg(ifp, 1);
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate
3460Sstevel@tonic-gate
3470Sstevel@tonic-gate /*
3480Sstevel@tonic-gate * Age discovered routes and find the best one
3490Sstevel@tonic-gate */
3500Sstevel@tonic-gate void
rdisc_age(in_addr_t bad_gate)3510Sstevel@tonic-gate rdisc_age(in_addr_t bad_gate)
3520Sstevel@tonic-gate {
3530Sstevel@tonic-gate time_t sec;
3540Sstevel@tonic-gate struct dr *drp;
3550Sstevel@tonic-gate struct rt_spare new;
3560Sstevel@tonic-gate struct rt_entry *rt;
3570Sstevel@tonic-gate
3580Sstevel@tonic-gate /*
3590Sstevel@tonic-gate * If we are being told about a bad router,
3600Sstevel@tonic-gate * then age the discovered default route, and if there is
3610Sstevel@tonic-gate * no alternative, solicit a replacement.
3620Sstevel@tonic-gate */
3630Sstevel@tonic-gate if (bad_gate != 0) {
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate * Look for the bad discovered default route.
3660Sstevel@tonic-gate * Age it and note its interface.
3670Sstevel@tonic-gate */
3680Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
3690Sstevel@tonic-gate if (drp->dr_ts == 0)
3700Sstevel@tonic-gate continue;
3710Sstevel@tonic-gate
3720Sstevel@tonic-gate /*
3730Sstevel@tonic-gate * When we find the bad router, age the route
3740Sstevel@tonic-gate * to at most SUPPLY_INTERVAL.
3750Sstevel@tonic-gate * This is contrary to RFC 1256, but defends against
3760Sstevel@tonic-gate * black holes.
3770Sstevel@tonic-gate */
3780Sstevel@tonic-gate if (drp->dr_gate == bad_gate) {
3790Sstevel@tonic-gate sec = (now.tv_sec - drp->dr_life +
3800Sstevel@tonic-gate SUPPLY_INTERVAL);
3810Sstevel@tonic-gate if (drp->dr_ts > sec) {
3820Sstevel@tonic-gate trace_act("age 0.0.0.0 --> %s via %s",
3830Sstevel@tonic-gate naddr_ntoa(drp->dr_gate),
3840Sstevel@tonic-gate drp->dr_ifp->int_name);
3850Sstevel@tonic-gate drp->dr_ts = sec;
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate break;
3880Sstevel@tonic-gate }
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate } else if (should_supply(NULL)) {
3910Sstevel@tonic-gate /*
3920Sstevel@tonic-gate * If switching from client to server, get rid of old
3930Sstevel@tonic-gate * default routes.
3940Sstevel@tonic-gate */
3950Sstevel@tonic-gate if (cur_drp != NULL) {
3960Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
3970Sstevel@tonic-gate /*
3980Sstevel@tonic-gate * If there is a current default router, and the
3990Sstevel@tonic-gate * there is no rt_spare entry, create one
4000Sstevel@tonic-gate * for cur_drp to prevent segmentation fault
4010Sstevel@tonic-gate * at rdisc_sort.
4020Sstevel@tonic-gate */
4030Sstevel@tonic-gate if (rt == NULL) {
4040Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new));
4050Sstevel@tonic-gate new.rts_ifp = cur_drp->dr_ifp;
4060Sstevel@tonic-gate new.rts_gate = cur_drp->dr_gate;
4070Sstevel@tonic-gate new.rts_router = cur_drp->dr_gate;
4080Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1;
4090Sstevel@tonic-gate new.rts_time = now.tv_sec;
4100Sstevel@tonic-gate new.rts_origin = RO_RDISC;
4110Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate rdisc_sort();
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate rdisc_adv(_B_FALSE);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate
4190Sstevel@tonic-gate rdisc_sol();
4200Sstevel@tonic-gate if (cur_drp != NULL) {
4210Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
4220Sstevel@tonic-gate if (rt == NULL) {
4230Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new));
4240Sstevel@tonic-gate new.rts_ifp = cur_drp->dr_ifp;
4250Sstevel@tonic-gate new.rts_gate = cur_drp->dr_gate;
4260Sstevel@tonic-gate new.rts_router = cur_drp->dr_gate;
4270Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1;
4280Sstevel@tonic-gate new.rts_time = now.tv_sec;
4290Sstevel@tonic-gate new.rts_origin = RO_RDISC;
4300Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
4310Sstevel@tonic-gate }
4320Sstevel@tonic-gate }
4330Sstevel@tonic-gate rdisc_sort();
4340Sstevel@tonic-gate
4350Sstevel@tonic-gate /*
4360Sstevel@tonic-gate * Delete old redirected routes to keep the kernel table small,
4370Sstevel@tonic-gate * and to prevent black holes. Check that the kernel table
4380Sstevel@tonic-gate * matches the daemon table (i.e. has the default route).
4390Sstevel@tonic-gate * But only if RIP is not running and we are not dealing with
4400Sstevel@tonic-gate * a bad gateway, since otherwise age() will be called.
4410Sstevel@tonic-gate */
4420Sstevel@tonic-gate if (rip_sock < 0 && bad_gate == 0)
4430Sstevel@tonic-gate age(0);
4440Sstevel@tonic-gate }
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate
4470Sstevel@tonic-gate /*
4480Sstevel@tonic-gate * Zap all routes discovered via an interface that has gone bad
4490Sstevel@tonic-gate * This should only be called when !(ifp->int_state & IS_DUP)
4500Sstevel@tonic-gate * This is called by if_del and if_bad, and the interface pointer
4510Sstevel@tonic-gate * might not be valid after this.
4520Sstevel@tonic-gate */
4530Sstevel@tonic-gate void
if_bad_rdisc(struct interface * ifp)4540Sstevel@tonic-gate if_bad_rdisc(struct interface *ifp)
4550Sstevel@tonic-gate {
4560Sstevel@tonic-gate struct dr *drp;
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
4590Sstevel@tonic-gate if (drp->dr_ifp != ifp)
4600Sstevel@tonic-gate continue;
4610Sstevel@tonic-gate (void) memset(drp, 0, sizeof (*drp));
4620Sstevel@tonic-gate }
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate /* make a note to re-solicit, turn RIP on or off, etc. */
4650Sstevel@tonic-gate rdisc_timer.tv_sec = 0;
4660Sstevel@tonic-gate }
4670Sstevel@tonic-gate
4680Sstevel@tonic-gate /*
4690Sstevel@tonic-gate * Rewire all routes discovered via an interface that has gone bad
4700Sstevel@tonic-gate * This is only called by if_del.
4710Sstevel@tonic-gate */
4720Sstevel@tonic-gate void
if_rewire_rdisc(struct interface * oldifp,struct interface * newifp)4730Sstevel@tonic-gate if_rewire_rdisc(struct interface *oldifp, struct interface *newifp)
4740Sstevel@tonic-gate {
4750Sstevel@tonic-gate struct dr *drp;
4760Sstevel@tonic-gate
4770Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
4780Sstevel@tonic-gate if (drp->dr_ifp != oldifp)
4790Sstevel@tonic-gate continue;
4800Sstevel@tonic-gate drp->dr_ifp = newifp;
4810Sstevel@tonic-gate drp->dr_pref += (newifp->int_metric - oldifp->int_metric);
4820Sstevel@tonic-gate drp->dr_flags |= DR_CHANGED;
4830Sstevel@tonic-gate }
4840Sstevel@tonic-gate
4850Sstevel@tonic-gate /* make a note to re-solicit, turn RIP on or off, etc. */
4860Sstevel@tonic-gate rdisc_timer.tv_sec = 0;
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate /*
4900Sstevel@tonic-gate * Mark an interface ok for router discovering.
4910Sstevel@tonic-gate * This is called by if_ok and ifinit.
4920Sstevel@tonic-gate */
4930Sstevel@tonic-gate void
if_ok_rdisc(struct interface * ifp)4940Sstevel@tonic-gate if_ok_rdisc(struct interface *ifp)
4950Sstevel@tonic-gate {
4960Sstevel@tonic-gate set_rdisc_mg(ifp, 1);
4970Sstevel@tonic-gate
4980Sstevel@tonic-gate ifp->int_rdisc_cnt = 0;
4990Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = now.tv_sec +
5000Sstevel@tonic-gate ((ifp->int_state & IS_NO_ADV_OUT) ?
5010Sstevel@tonic-gate MAX_SOLICITATION_DELAY : MIN_WAITTIME);
5020Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, > /* cstyle */))
5030Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer;
5040Sstevel@tonic-gate }
5050Sstevel@tonic-gate
5060Sstevel@tonic-gate /*
5070Sstevel@tonic-gate * Get rid of a dead discovered router
5080Sstevel@tonic-gate */
5090Sstevel@tonic-gate static void
del_rdisc(struct dr * drp)5100Sstevel@tonic-gate del_rdisc(struct dr *drp)
5110Sstevel@tonic-gate {
5120Sstevel@tonic-gate struct interface *ifp;
5130Sstevel@tonic-gate uint32_t gate;
5140Sstevel@tonic-gate int i;
5150Sstevel@tonic-gate struct rt_entry *rt;
5160Sstevel@tonic-gate struct rt_spare *rts = NULL;
5170Sstevel@tonic-gate
5180Sstevel@tonic-gate del_redirects(gate = drp->dr_gate, 0);
5190Sstevel@tonic-gate drp->dr_ts = 0;
5200Sstevel@tonic-gate drp->dr_life = 0;
5210Sstevel@tonic-gate
5220Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
5230Sstevel@tonic-gate if (rt == NULL) {
5240Sstevel@tonic-gate trace_act("could not find default route in table");
5250Sstevel@tonic-gate } else {
5260Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) {
5270Sstevel@tonic-gate if ((rt->rt_spares[i].rts_gate == drp->dr_gate) &&
5280Sstevel@tonic-gate (rt->rt_spares[i].rts_origin == RO_RDISC)) {
5290Sstevel@tonic-gate rts = &rt->rt_spares[i];
5300Sstevel@tonic-gate break;
5310Sstevel@tonic-gate }
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate if (rts != NULL)
5340Sstevel@tonic-gate rts_delete(rt, rts);
5350Sstevel@tonic-gate else
5360Sstevel@tonic-gate trace_act("could not find default route "
5370Sstevel@tonic-gate "through %s in table", naddr_ntoa(drp->dr_gate));
5380Sstevel@tonic-gate }
5390Sstevel@tonic-gate
5400Sstevel@tonic-gate /* Count the other discovered routers on the interface. */
5410Sstevel@tonic-gate i = 0;
5420Sstevel@tonic-gate ifp = drp->dr_ifp;
5430Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
5440Sstevel@tonic-gate if (drp->dr_ts != 0 && drp->dr_ifp == ifp)
5450Sstevel@tonic-gate i++;
5460Sstevel@tonic-gate }
5470Sstevel@tonic-gate
5480Sstevel@tonic-gate /*
5490Sstevel@tonic-gate * If that was the last good discovered router on the interface,
5500Sstevel@tonic-gate * then solicit a new one.
5510Sstevel@tonic-gate * This is contrary to RFC 1256, but defends against black holes.
5520Sstevel@tonic-gate */
5530Sstevel@tonic-gate if (i != 0) {
5540Sstevel@tonic-gate trace_act("discovered router %s via %s"
5550Sstevel@tonic-gate " is bad--have %d remaining",
5560Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name, i);
5570Sstevel@tonic-gate } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
5580Sstevel@tonic-gate trace_act("last discovered router %s via %s"
5590Sstevel@tonic-gate " is bad--re-solicit",
5600Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name);
5610Sstevel@tonic-gate ifp->int_rdisc_cnt = 0;
5620Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = 0;
5630Sstevel@tonic-gate rdisc_sol();
5640Sstevel@tonic-gate } else {
5650Sstevel@tonic-gate trace_act("last discovered router %s via %s"
5660Sstevel@tonic-gate " is bad--wait to solicit",
5670Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name);
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate }
5700Sstevel@tonic-gate
5710Sstevel@tonic-gate
5720Sstevel@tonic-gate /* Find the best discovered route, and discard stale routers. */
5730Sstevel@tonic-gate static void
rdisc_sort(void)5740Sstevel@tonic-gate rdisc_sort(void)
5750Sstevel@tonic-gate {
5760Sstevel@tonic-gate struct dr *drp, *new_drp;
5770Sstevel@tonic-gate struct rt_entry *rt;
5780Sstevel@tonic-gate struct rt_spare new, *rts;
5790Sstevel@tonic-gate struct interface *ifp;
5800Sstevel@tonic-gate uint_t new_st = 0;
5810Sstevel@tonic-gate uint32_t new_pref = DEF_PREFERENCELEVEL;
5820Sstevel@tonic-gate int first_rdisc_slot = 0;
5830Sstevel@tonic-gate int j;
5840Sstevel@tonic-gate boolean_t spares_avail;
5850Sstevel@tonic-gate void *ptr;
5860Sstevel@tonic-gate size_t ptrsize;
5870Sstevel@tonic-gate
5880Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
5890Sstevel@tonic-gate
5900Sstevel@tonic-gate /*
5910Sstevel@tonic-gate * If all the rt_spare entries are taken up with with default routes
5920Sstevel@tonic-gate * learnt from RIP (ie rts_origin = RO_RIP), bail out.
5930Sstevel@tonic-gate * NOTE:
5940Sstevel@tonic-gate * We *always* prefer default routes learned via RIP
5950Sstevel@tonic-gate * (ie RO_RIP) over those learnt via RDISC (ie RO_RDISC).
5960Sstevel@tonic-gate * The rdisc machinery should not modify, replace or
5970Sstevel@tonic-gate * remove any existing default routes with RO_RIP set.
5980Sstevel@tonic-gate */
5990Sstevel@tonic-gate if (rt != NULL) {
6000Sstevel@tonic-gate spares_avail = _B_FALSE;
6010Sstevel@tonic-gate for (j = 0; j < rt->rt_num_spares; j++) {
6020Sstevel@tonic-gate rts = &rt->rt_spares[j];
603552Ssowmini if (rts->rts_gate == 0 || rts->rts_origin != RO_RIP ||
604552Ssowmini rts->rts_ifp == &dummy_ifp) {
6050Sstevel@tonic-gate spares_avail = _B_TRUE;
6060Sstevel@tonic-gate break;
6070Sstevel@tonic-gate }
6080Sstevel@tonic-gate }
6090Sstevel@tonic-gate if (!spares_avail) {
6100Sstevel@tonic-gate ptrsize = (rt->rt_num_spares + SPARE_INC) *
6110Sstevel@tonic-gate sizeof (struct rt_spare);
6120Sstevel@tonic-gate ptr = realloc(rt->rt_spares, ptrsize);
6130Sstevel@tonic-gate if (ptr != NULL) {
6140Sstevel@tonic-gate struct rt_spare *tmprts;
6150Sstevel@tonic-gate
6160Sstevel@tonic-gate rt->rt_spares = ptr;
6170Sstevel@tonic-gate rts = &rt->rt_spares[rt->rt_num_spares];
6180Sstevel@tonic-gate (void) memset(rts, 0,
6190Sstevel@tonic-gate (SPARE_INC * sizeof (struct rt_spare)));
6200Sstevel@tonic-gate rt->rt_num_spares += SPARE_INC;
6210Sstevel@tonic-gate for (tmprts = rts, j = SPARE_INC;
6220Sstevel@tonic-gate j != 0; j--, tmprts++)
6230Sstevel@tonic-gate tmprts->rts_metric = HOPCNT_INFINITY;
6240Sstevel@tonic-gate spares_avail = _B_TRUE;
6250Sstevel@tonic-gate } else {
6260Sstevel@tonic-gate return;
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate }
6290Sstevel@tonic-gate }
6300Sstevel@tonic-gate /* Find the best RDISC advertiser */
6310Sstevel@tonic-gate rt = NULL;
6320Sstevel@tonic-gate new_drp = NULL;
6330Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
6340Sstevel@tonic-gate if (drp->dr_ts == 0)
6350Sstevel@tonic-gate continue;
6360Sstevel@tonic-gate ifp = drp->dr_ifp;
6370Sstevel@tonic-gate
6380Sstevel@tonic-gate /* Get rid of expired discovered routers. */
6390Sstevel@tonic-gate if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
6400Sstevel@tonic-gate del_rdisc(drp);
6410Sstevel@tonic-gate continue;
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate
6440Sstevel@tonic-gate LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life);
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate /*
6470Sstevel@tonic-gate * Update preference with possibly changed interface
6480Sstevel@tonic-gate * metric.
6490Sstevel@tonic-gate */
6500Sstevel@tonic-gate drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
6510Sstevel@tonic-gate
6520Sstevel@tonic-gate /*
6530Sstevel@tonic-gate * Prefer the current route to prevent thrashing.
6540Sstevel@tonic-gate * Prefer shorter lifetimes to speed the detection of
6550Sstevel@tonic-gate * bad routers.
6560Sstevel@tonic-gate * Avoid sick interfaces.
6570Sstevel@tonic-gate */
6580Sstevel@tonic-gate if (new_drp == NULL ||
6590Sstevel@tonic-gate (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) &&
6600Sstevel@tonic-gate (new_pref < drp->dr_pref ||
6610Sstevel@tonic-gate (new_pref == drp->dr_pref && (drp == cur_drp ||
6620Sstevel@tonic-gate (new_drp != cur_drp &&
6630Sstevel@tonic-gate new_drp->dr_life > drp->dr_life))))) ||
6640Sstevel@tonic-gate ((new_st & IS_SICK) &&
6650Sstevel@tonic-gate !(drp->dr_ifp->int_state & IS_SICK))) {
666*5381Smeem new_drp = drp;
667*5381Smeem new_st = drp->dr_ifp->int_state;
668*5381Smeem new_pref = drp->dr_pref;
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate
6720Sstevel@tonic-gate /*
6730Sstevel@tonic-gate * switch to a better RDISC advertiser
6740Sstevel@tonic-gate */
6750Sstevel@tonic-gate if ((new_drp != cur_drp) || (rt == NULL)) {
6760Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
6770Sstevel@tonic-gate
6780Sstevel@tonic-gate /*
6790Sstevel@tonic-gate * Purge the table of all the default routes that were
6800Sstevel@tonic-gate * learnt via RDISC, while keeping an eye the first available
6810Sstevel@tonic-gate * slot for the spare entry of new_drp
6820Sstevel@tonic-gate */
6830Sstevel@tonic-gate if (rt != NULL) {
6840Sstevel@tonic-gate int i;
6850Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) {
6860Sstevel@tonic-gate rts = &rt->rt_spares[i];
687552Ssowmini if ((rts->rts_gate == 0 ||
688552Ssowmini rts->rts_ifp == &dummy_ifp) &&
689552Ssowmini first_rdisc_slot == 0)
6900Sstevel@tonic-gate first_rdisc_slot = i;
6910Sstevel@tonic-gate if (rts->rts_origin == RO_RDISC) {
6920Sstevel@tonic-gate rts_delete(rt, rts);
6930Sstevel@tonic-gate if (first_rdisc_slot == 0) {
6940Sstevel@tonic-gate first_rdisc_slot = i;
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate }
6980Sstevel@tonic-gate }
6990Sstevel@tonic-gate
7000Sstevel@tonic-gate /* Stop using RDISC routes if they are all bad */
7010Sstevel@tonic-gate if (new_drp == NULL) {
7020Sstevel@tonic-gate trace_act("turn off Router Discovery client");
7030Sstevel@tonic-gate rdisc_ok = _B_FALSE;
7040Sstevel@tonic-gate
7050Sstevel@tonic-gate } else {
7060Sstevel@tonic-gate if (cur_drp == NULL) {
7070Sstevel@tonic-gate trace_act("turn on Router Discovery client"
7080Sstevel@tonic-gate " using %s via %s",
7090Sstevel@tonic-gate naddr_ntoa(new_drp->dr_gate),
7100Sstevel@tonic-gate new_drp->dr_ifp->int_name);
7110Sstevel@tonic-gate rdisc_ok = _B_TRUE;
7120Sstevel@tonic-gate }
7130Sstevel@tonic-gate
7140Sstevel@tonic-gate /* Prepare a spare entry for the new_drp */
7150Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new));
7160Sstevel@tonic-gate new.rts_ifp = new_drp->dr_ifp;
7170Sstevel@tonic-gate new.rts_gate = new_drp->dr_gate;
7180Sstevel@tonic-gate new.rts_router = new_drp->dr_gate;
7190Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1;
7200Sstevel@tonic-gate new.rts_time = now.tv_sec;
7210Sstevel@tonic-gate new.rts_origin = RO_RDISC;
7220Sstevel@tonic-gate /*
7230Sstevel@tonic-gate * If there is no existing default route, add it
7240Sstevel@tonic-gate * to rts_spare[0].
7250Sstevel@tonic-gate */
7260Sstevel@tonic-gate if (rt == NULL) {
7270Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
7280Sstevel@tonic-gate } else {
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate /*
7310Sstevel@tonic-gate * Add the spare entry for the new_drp in
7320Sstevel@tonic-gate * the first available slot
7330Sstevel@tonic-gate */
7340Sstevel@tonic-gate trace_act("Switching to "
7350Sstevel@tonic-gate "default router with better "
7360Sstevel@tonic-gate "preference %s via %s ",
7370Sstevel@tonic-gate naddr_ntoa(new_drp->dr_gate),
7380Sstevel@tonic-gate new_drp->dr_ifp->int_name);
7390Sstevel@tonic-gate rt->rt_spares[first_rdisc_slot] = new;
7400Sstevel@tonic-gate rt = NULL; /* redo rt_spares */
7410Sstevel@tonic-gate }
7420Sstevel@tonic-gate }
7430Sstevel@tonic-gate
7440Sstevel@tonic-gate /*
7450Sstevel@tonic-gate * Get ready to redo the entire table. The table should
7460Sstevel@tonic-gate * only include :
7470Sstevel@tonic-gate * a. empty rt_spare slots
7480Sstevel@tonic-gate * b. default routes learnt via RIP
7490Sstevel@tonic-gate * c. default route for the latest best RDISC advertiser
7500Sstevel@tonic-gate * d. default routes of other RDISC advertisers whose
7510Sstevel@tonic-gate * dr_pref == best RDISC advertiser->dr_pref
7520Sstevel@tonic-gate */
7530Sstevel@tonic-gate cur_drp = new_drp;
7540Sstevel@tonic-gate }
7550Sstevel@tonic-gate
7560Sstevel@tonic-gate /* Redo the entire spare table (without touching RO_RIP entries) */
7570Sstevel@tonic-gate if (rdisc_ok && rt == NULL) {
7580Sstevel@tonic-gate int i;
7590Sstevel@tonic-gate /*
7600Sstevel@tonic-gate * We've either just turned on router discovery,
7610Sstevel@tonic-gate * or switched to a router with better preference.
7620Sstevel@tonic-gate * Find all other default routers whose
7630Sstevel@tonic-gate * pref == cur_drp->dr_pref and add them as spares
7640Sstevel@tonic-gate */
7650Sstevel@tonic-gate
7660Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0);
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) {
7690Sstevel@tonic-gate boolean_t dr_done = _B_FALSE;
7700Sstevel@tonic-gate int slot = -1;
7710Sstevel@tonic-gate
7720Sstevel@tonic-gate if (drp->dr_ts == 0)
7730Sstevel@tonic-gate continue;
7740Sstevel@tonic-gate
7750Sstevel@tonic-gate if (drp->dr_pref != cur_drp->dr_pref &&
7760Sstevel@tonic-gate ((drp->dr_flags & DR_CHANGED) == 0))
7770Sstevel@tonic-gate continue;
7780Sstevel@tonic-gate
7790Sstevel@tonic-gate /*
7800Sstevel@tonic-gate * Either pref matches cur_drp->dr_pref,
7810Sstevel@tonic-gate * or something has changed in this drp.
7820Sstevel@tonic-gate * In the former case, we may need to add
7830Sstevel@tonic-gate * this to rt_spares. In the latter case,
7840Sstevel@tonic-gate * if the pref has changed, need to take it
7850Sstevel@tonic-gate * out of rt_spares and the kernel.
7860Sstevel@tonic-gate *
7870Sstevel@tonic-gate * First, find an empty slot in rt_spares
7880Sstevel@tonic-gate * in case we have to add this drp to kernel.
7890Sstevel@tonic-gate * Also check if it is already there.
7900Sstevel@tonic-gate */
7910Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) {
7920Sstevel@tonic-gate if (rt->rt_spares[i].rts_gate == 0) {
7930Sstevel@tonic-gate if (slot < 0)
7940Sstevel@tonic-gate slot = i;
7950Sstevel@tonic-gate continue;
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate if ((rt->rt_spares[i].rts_gate ==
7980Sstevel@tonic-gate drp->dr_gate) &&
7990Sstevel@tonic-gate (rt->rt_spares[i].rts_origin ==
8000Sstevel@tonic-gate RO_RDISC)) {
8010Sstevel@tonic-gate /*
8020Sstevel@tonic-gate * a spare entry for this RDISC
8030Sstevel@tonic-gate * advertiser already exists. We need
8040Sstevel@tonic-gate * to check if this entry still belongs
8050Sstevel@tonic-gate * in the table
8060Sstevel@tonic-gate */
8070Sstevel@tonic-gate dr_done = _B_TRUE;
8080Sstevel@tonic-gate break;
8090Sstevel@tonic-gate }
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate drp->dr_flags &= ~DR_CHANGED;
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate if (drp->dr_pref != cur_drp->dr_pref) {
8150Sstevel@tonic-gate if (dr_done) {
8160Sstevel@tonic-gate /*
8170Sstevel@tonic-gate * The rt_spare of this RDISC advertiser
8180Sstevel@tonic-gate * needs to be removed as it no longer
8190Sstevel@tonic-gate * belongs in the table because its
8200Sstevel@tonic-gate * dr_pref is different than the latest
8210Sstevel@tonic-gate * RDISC advertiser's->dr_pref
8220Sstevel@tonic-gate */
8230Sstevel@tonic-gate rts_delete(rt, &rt->rt_spares[i]);
8240Sstevel@tonic-gate }
8250Sstevel@tonic-gate continue;
8260Sstevel@tonic-gate }
8270Sstevel@tonic-gate
828552Ssowmini if (slot < 0 && !dr_done) {
8290Sstevel@tonic-gate ptrsize = (rt->rt_num_spares + SPARE_INC) *
8300Sstevel@tonic-gate sizeof (struct rt_spare);
8310Sstevel@tonic-gate ptr = realloc(rt->rt_spares, ptrsize);
8320Sstevel@tonic-gate if (ptr != NULL) {
8330Sstevel@tonic-gate struct rt_spare *tmprts;
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate rt->rt_spares = ptr;
8360Sstevel@tonic-gate slot = rt->rt_num_spares;
8370Sstevel@tonic-gate rts = &rt->rt_spares[rt->rt_num_spares];
8380Sstevel@tonic-gate (void) memset(rts, 0, (SPARE_INC *
8390Sstevel@tonic-gate sizeof (struct rt_spare)));
8400Sstevel@tonic-gate rt->rt_num_spares += SPARE_INC;
8410Sstevel@tonic-gate for (tmprts = rts, i = SPARE_INC;
8420Sstevel@tonic-gate i != 0; i--, tmprts++)
8430Sstevel@tonic-gate tmprts->rts_metric =
8440Sstevel@tonic-gate HOPCNT_INFINITY;
8450Sstevel@tonic-gate }
8460Sstevel@tonic-gate }
8470Sstevel@tonic-gate
8480Sstevel@tonic-gate if (slot >= 0 && (dr_done != _B_TRUE)) {
8490Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new));
8500Sstevel@tonic-gate new.rts_ifp = drp->dr_ifp;
8510Sstevel@tonic-gate new.rts_gate = drp->dr_gate;
8520Sstevel@tonic-gate new.rts_router = drp->dr_gate;
8530Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1;
8540Sstevel@tonic-gate new.rts_time = now.tv_sec;
8550Sstevel@tonic-gate new.rts_origin = RO_RDISC;
8560Sstevel@tonic-gate rt->rt_spares[slot] = new;
8570Sstevel@tonic-gate trace_act("spare default %s via %s",
8580Sstevel@tonic-gate naddr_ntoa(drp->dr_gate),
8590Sstevel@tonic-gate drp->dr_ifp->int_name);
8600Sstevel@tonic-gate }
8610Sstevel@tonic-gate }
8620Sstevel@tonic-gate }
8630Sstevel@tonic-gate
8640Sstevel@tonic-gate /* turn RIP on or off */
8650Sstevel@tonic-gate if (!rdisc_ok || rip_interfaces > 1) {
8660Sstevel@tonic-gate rip_on(0);
8670Sstevel@tonic-gate } else {
8680Sstevel@tonic-gate rip_off();
8690Sstevel@tonic-gate }
8700Sstevel@tonic-gate }
8710Sstevel@tonic-gate
8720Sstevel@tonic-gate
8730Sstevel@tonic-gate /* Handle a single address in an advertisement */
8740Sstevel@tonic-gate static void
parse_ad(uint32_t from,in_addr_t gate,uint32_t pref,ushort_t life,struct interface * ifp)8750Sstevel@tonic-gate parse_ad(uint32_t from,
8760Sstevel@tonic-gate in_addr_t gate,
8770Sstevel@tonic-gate uint32_t pref, /* signed and in network order */
8780Sstevel@tonic-gate ushort_t life, /* in host byte order */
8790Sstevel@tonic-gate struct interface *ifp)
8800Sstevel@tonic-gate {
8810Sstevel@tonic-gate static struct msg_limit bad_gate;
8820Sstevel@tonic-gate struct dr *drp, *new_drp;
8830Sstevel@tonic-gate void *ptr;
8840Sstevel@tonic-gate size_t ptrsize;
8850Sstevel@tonic-gate
8860Sstevel@tonic-gate if (gate == RIP_DEFAULT || !check_dst(gate)) {
8870Sstevel@tonic-gate msglim(&bad_gate, from, "router %s advertising bad gateway %s",
8880Sstevel@tonic-gate naddr_ntoa(from), naddr_ntoa(gate));
8890Sstevel@tonic-gate return;
8900Sstevel@tonic-gate }
8910Sstevel@tonic-gate
8920Sstevel@tonic-gate /*
8930Sstevel@tonic-gate * ignore pointers to ourself and routes via unreachable networks
8940Sstevel@tonic-gate */
8950Sstevel@tonic-gate if (ifwithaddr(gate, _B_TRUE, _B_FALSE) != 0) {
8960Sstevel@tonic-gate trace_pkt(" discard Router Discovery Ad pointing at us");
8970Sstevel@tonic-gate return;
8980Sstevel@tonic-gate }
8990Sstevel@tonic-gate if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
9000Sstevel@tonic-gate trace_pkt(" discard Router Discovery Ad"
9010Sstevel@tonic-gate " toward unreachable net");
9020Sstevel@tonic-gate return;
9030Sstevel@tonic-gate }
9040Sstevel@tonic-gate /*
9050Sstevel@tonic-gate * Convert preference to an unsigned value
9060Sstevel@tonic-gate * and later bias it by the metric of the interface.
9070Sstevel@tonic-gate */
9080Sstevel@tonic-gate pref = UNSIGN_PREF(ntohl(pref));
9090Sstevel@tonic-gate
9100Sstevel@tonic-gate if (pref == DEF_PREFERENCELEVEL || life < MIN_MAXADVERTISEINTERVAL) {
9110Sstevel@tonic-gate pref = DEF_PREFERENCELEVEL;
9120Sstevel@tonic-gate life = 0;
9130Sstevel@tonic-gate }
9140Sstevel@tonic-gate
9150Sstevel@tonic-gate for (new_drp = NULL, drp = drs; drp < &drs[max_ads]; drp++) {
9160Sstevel@tonic-gate /* accept new info for a familiar entry */
9170Sstevel@tonic-gate if ((drp->dr_gate == gate) && (drp->dr_ifp == ifp)) {
9180Sstevel@tonic-gate new_drp = drp;
9190Sstevel@tonic-gate drp->dr_flags |= DR_CHANGED;
9200Sstevel@tonic-gate break;
9210Sstevel@tonic-gate }
9220Sstevel@tonic-gate
9230Sstevel@tonic-gate if (life == 0)
9240Sstevel@tonic-gate continue; /* do not worry about dead ads */
9250Sstevel@tonic-gate
9260Sstevel@tonic-gate if (drp->dr_ts == 0) {
9270Sstevel@tonic-gate new_drp = drp; /* use unused entry */
9280Sstevel@tonic-gate
9290Sstevel@tonic-gate } else if (new_drp == NULL) {
9300Sstevel@tonic-gate /* look for an entry worse than the new one to reuse. */
9310Sstevel@tonic-gate if ((!(ifp->int_state & IS_SICK) &&
9320Sstevel@tonic-gate (drp->dr_ifp->int_state & IS_SICK)) ||
9330Sstevel@tonic-gate (pref > drp->dr_pref &&
9340Sstevel@tonic-gate !((ifp->int_state ^ drp->dr_ifp->int_state) &
9350Sstevel@tonic-gate IS_SICK)))
9360Sstevel@tonic-gate new_drp = drp;
9370Sstevel@tonic-gate
9380Sstevel@tonic-gate } else if (new_drp->dr_ts != 0) {
9390Sstevel@tonic-gate /* look for the least valuable entry to reuse */
9400Sstevel@tonic-gate if ((!(new_drp->dr_ifp->int_state & IS_SICK) &&
9410Sstevel@tonic-gate (drp->dr_ifp->int_state & IS_SICK)) ||
9420Sstevel@tonic-gate (new_drp->dr_pref > drp->dr_pref &&
9430Sstevel@tonic-gate !((new_drp->dr_ifp->int_state ^
9440Sstevel@tonic-gate drp->dr_ifp->int_state) & IS_SICK)))
9450Sstevel@tonic-gate new_drp = drp;
9460Sstevel@tonic-gate }
9470Sstevel@tonic-gate }
9480Sstevel@tonic-gate
9490Sstevel@tonic-gate /* if all of the current entries are better, add more drs[] */
9500Sstevel@tonic-gate if (new_drp == NULL) {
9510Sstevel@tonic-gate ptrsize = (max_ads + MAX_ADS) * sizeof (struct dr);
9520Sstevel@tonic-gate ptr = realloc(drs, ptrsize);
9530Sstevel@tonic-gate if (ptr == NULL)
9540Sstevel@tonic-gate return;
9550Sstevel@tonic-gate drs = ptr;
9560Sstevel@tonic-gate (void) memset(&drs[max_ads], 0, MAX_ADS * sizeof (struct dr));
9570Sstevel@tonic-gate new_drp = &drs[max_ads];
9580Sstevel@tonic-gate max_ads += MAX_ADS;
9590Sstevel@tonic-gate }
9600Sstevel@tonic-gate
9610Sstevel@tonic-gate /*
9620Sstevel@tonic-gate * Pointer copy is safe here because if_del
9630Sstevel@tonic-gate * calls if_bad_rdisc first, so a non-NULL df_ifp
9640Sstevel@tonic-gate * is always a valid pointer.
9650Sstevel@tonic-gate */
9660Sstevel@tonic-gate new_drp->dr_ifp = ifp;
9670Sstevel@tonic-gate new_drp->dr_gate = gate;
9680Sstevel@tonic-gate new_drp->dr_ts = now.tv_sec;
9690Sstevel@tonic-gate new_drp->dr_life = life;
9700Sstevel@tonic-gate new_drp->dr_recv_pref = pref;
9710Sstevel@tonic-gate /* bias functional preference by metric of the interface */
9720Sstevel@tonic-gate new_drp->dr_pref = PREF(pref, ifp);
9730Sstevel@tonic-gate
9740Sstevel@tonic-gate /* after hearing a good advertisement, stop asking */
9750Sstevel@tonic-gate if (!(ifp->int_state & IS_SICK))
9760Sstevel@tonic-gate ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
9770Sstevel@tonic-gate }
9780Sstevel@tonic-gate
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate /*
9810Sstevel@tonic-gate * Compute the IP checksum. This assumes the packet is less than 32K long.
9820Sstevel@tonic-gate */
9830Sstevel@tonic-gate static uint16_t
in_cksum(uint16_t * p,uint_t len)9840Sstevel@tonic-gate in_cksum(uint16_t *p, uint_t len)
9850Sstevel@tonic-gate {
9860Sstevel@tonic-gate uint32_t sum = 0;
9870Sstevel@tonic-gate int nwords = len >> 1;
9880Sstevel@tonic-gate
9890Sstevel@tonic-gate while (nwords-- != 0)
9900Sstevel@tonic-gate sum += *p++;
9910Sstevel@tonic-gate
9920Sstevel@tonic-gate if (len & 1)
9930Sstevel@tonic-gate sum += *(uchar_t *)p;
9940Sstevel@tonic-gate
9950Sstevel@tonic-gate /* end-around-carry */
9960Sstevel@tonic-gate sum = (sum >> 16) + (sum & 0xffff);
9970Sstevel@tonic-gate sum += (sum >> 16);
9980Sstevel@tonic-gate return (~sum);
9990Sstevel@tonic-gate }
10000Sstevel@tonic-gate
10010Sstevel@tonic-gate
10020Sstevel@tonic-gate /* Send a router discovery advertisement or solicitation ICMP packet. */
10030Sstevel@tonic-gate static void
send_rdisc(union ad_u * p,uint_t p_size,struct interface * ifp,in_addr_t dst,dstaddr_t type)10040Sstevel@tonic-gate send_rdisc(union ad_u *p,
10050Sstevel@tonic-gate uint_t p_size,
10060Sstevel@tonic-gate struct interface *ifp,
10070Sstevel@tonic-gate in_addr_t dst, /* 0 or unicast destination */
10080Sstevel@tonic-gate dstaddr_t type)
10090Sstevel@tonic-gate {
10100Sstevel@tonic-gate struct sockaddr_in sin;
10110Sstevel@tonic-gate int flags = 0;
10120Sstevel@tonic-gate const char *msg;
1013*5381Smeem int ifindex = 0;
10140Sstevel@tonic-gate struct in_addr addr;
10150Sstevel@tonic-gate
10160Sstevel@tonic-gate /*
10170Sstevel@tonic-gate * Don't send Rdisc packets on duplicate interfaces, we
10180Sstevel@tonic-gate * don't want to generate duplicate packets.
10190Sstevel@tonic-gate */
10200Sstevel@tonic-gate if (ifp->int_state & IS_DUP)
10210Sstevel@tonic-gate return;
10220Sstevel@tonic-gate
10230Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (sin));
10240Sstevel@tonic-gate sin.sin_addr.s_addr = dst;
10250Sstevel@tonic-gate sin.sin_family = AF_INET;
10260Sstevel@tonic-gate
10270Sstevel@tonic-gate switch (type) {
10280Sstevel@tonic-gate case unicast: /* unicast */
10290Sstevel@tonic-gate default:
10300Sstevel@tonic-gate flags = MSG_DONTROUTE;
10310Sstevel@tonic-gate msg = "Send";
10320Sstevel@tonic-gate break;
10330Sstevel@tonic-gate
10340Sstevel@tonic-gate case bcast: /* broadcast */
10350Sstevel@tonic-gate if (ifp->int_if_flags & IFF_POINTOPOINT) {
10360Sstevel@tonic-gate msg = "Send pt-to-pt";
10370Sstevel@tonic-gate if (ifp->int_dstaddr == 0)
10380Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
10390Sstevel@tonic-gate else
10400Sstevel@tonic-gate sin.sin_addr.s_addr = ifp->int_dstaddr;
10410Sstevel@tonic-gate } else {
10420Sstevel@tonic-gate msg = "Send broadcast";
10430Sstevel@tonic-gate sin.sin_addr.s_addr = ifp->int_brdaddr;
10440Sstevel@tonic-gate }
10450Sstevel@tonic-gate break;
10460Sstevel@tonic-gate
10470Sstevel@tonic-gate case mcast: /* multicast */
10480Sstevel@tonic-gate msg = "Send multicast";
10490Sstevel@tonic-gate break;
10500Sstevel@tonic-gate }
10510Sstevel@tonic-gate
10520Sstevel@tonic-gate if (rdisc_sock < 0)
10530Sstevel@tonic-gate get_rdisc_sock();
10540Sstevel@tonic-gate
1055*5381Smeem /* select the right interface. */
1056*5381Smeem ifindex = (type != mcast && ifp->int_phys != NULL) ?
1057*5381Smeem ifp->int_phys->phyi_index : 0;
1058*5381Smeem
10590Sstevel@tonic-gate if (rdisc_sock_interface != ifp) {
10600Sstevel@tonic-gate /*
10610Sstevel@tonic-gate * For multicast, we have to choose the source
10620Sstevel@tonic-gate * address. This is either the local address
10630Sstevel@tonic-gate * (non-point-to-point) or the remote address.
10640Sstevel@tonic-gate */
10650Sstevel@tonic-gate addr.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
10660Sstevel@tonic-gate ifp->int_dstaddr : ifp->int_addr;
10670Sstevel@tonic-gate if (type == mcast &&
10680Sstevel@tonic-gate setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr,
1069*5381Smeem sizeof (addr)) == -1) {
10700Sstevel@tonic-gate LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
10710Sstevel@tonic-gate return;
10720Sstevel@tonic-gate }
10730Sstevel@tonic-gate rdisc_sock_interface = ifp;
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate
10760Sstevel@tonic-gate trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size);
10770Sstevel@tonic-gate
1078*5381Smeem if (0 > sendtoif(rdisc_sock, p, p_size, flags, &sin, ifindex)) {
10790Sstevel@tonic-gate if (!(ifp->int_state & IS_BROKE))
10800Sstevel@tonic-gate writelog(LOG_WARNING, "sendto(%s%s%s): %s",
10810Sstevel@tonic-gate ifp->int_name, ", ",
10820Sstevel@tonic-gate inet_ntoa(sin.sin_addr),
10830Sstevel@tonic-gate rip_strerror(errno));
10840Sstevel@tonic-gate if (ifp != NULL)
10850Sstevel@tonic-gate if_sick(ifp, _B_FALSE);
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate
10890Sstevel@tonic-gate
10900Sstevel@tonic-gate /* Send an advertisement */
10910Sstevel@tonic-gate static void
send_adv(struct interface * ifp,in_addr_t dst,dstaddr_t type)10920Sstevel@tonic-gate send_adv(struct interface *ifp,
10930Sstevel@tonic-gate in_addr_t dst,
10940Sstevel@tonic-gate dstaddr_t type)
10950Sstevel@tonic-gate {
10960Sstevel@tonic-gate union ad_u u;
10970Sstevel@tonic-gate
10980Sstevel@tonic-gate if ((ifp->int_state & (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC)) ==
10990Sstevel@tonic-gate IS_SUPPRESS_RDISC)
11000Sstevel@tonic-gate return;
11010Sstevel@tonic-gate
11020Sstevel@tonic-gate (void) memset(&u, 0, sizeof (u.ad));
11030Sstevel@tonic-gate
11040Sstevel@tonic-gate u.ad.icmp_type = ICMP_ROUTERADVERT;
11050Sstevel@tonic-gate u.ad.icmp_code = ICMP_ROUTERADVERT_COMMON;
11060Sstevel@tonic-gate u.ad.icmp_ad_num = 1;
11070Sstevel@tonic-gate u.ad.icmp_ad_asize = sizeof (u.ad.icmp_ad_info[0])/4;
11080Sstevel@tonic-gate
11090Sstevel@tonic-gate u.ad.icmp_ad_life = (stopint || !should_supply(ifp) ||
11100Sstevel@tonic-gate (ifp->int_state & IS_SUPPRESS_RDISC)) ? 0 :
11110Sstevel@tonic-gate htons(ifp->int_rdisc_int*3);
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate /* Send the configured preference as a network byte order value */
11140Sstevel@tonic-gate u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(ifp->int_rdisc_pref);
11150Sstevel@tonic-gate
11160Sstevel@tonic-gate u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
11170Sstevel@tonic-gate
11180Sstevel@tonic-gate u.ad.icmp_cksum = in_cksum((uint16_t *)&u.ad, sizeof (u.ad));
11190Sstevel@tonic-gate
11200Sstevel@tonic-gate send_rdisc(&u, sizeof (u.ad), ifp, dst, type);
11210Sstevel@tonic-gate
11220Sstevel@tonic-gate if (ifp->int_state & IS_SUPPRESS_RDISC)
11230Sstevel@tonic-gate ifp->int_state &= ~IS_FLUSH_RDISC;
11240Sstevel@tonic-gate }
11250Sstevel@tonic-gate
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate /* Advertise as a default router by way of router discovery. */
11280Sstevel@tonic-gate void
rdisc_adv(boolean_t forceadv)11290Sstevel@tonic-gate rdisc_adv(boolean_t forceadv)
11300Sstevel@tonic-gate {
11310Sstevel@tonic-gate struct interface *ifp;
11320Sstevel@tonic-gate
11330Sstevel@tonic-gate if (!forceadv && !should_supply(NULL))
11340Sstevel@tonic-gate return;
11350Sstevel@tonic-gate
11360Sstevel@tonic-gate rdisc_timer.tv_sec = now.tv_sec + NEVER;
11370Sstevel@tonic-gate
11380Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) {
11390Sstevel@tonic-gate if ((ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)) ||
11400Sstevel@tonic-gate (!forceadv && !IS_IFF_ROUTING(ifp->int_if_flags)))
11410Sstevel@tonic-gate continue;
11420Sstevel@tonic-gate
11430Sstevel@tonic-gate /* skip interfaces we shouldn't use */
11440Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags))
11450Sstevel@tonic-gate continue;
11460Sstevel@tonic-gate
11470Sstevel@tonic-gate if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */) ||
11480Sstevel@tonic-gate stopint != 0 || forceadv) {
11490Sstevel@tonic-gate send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
11500Sstevel@tonic-gate (ifp->int_state & IS_BCAST_RDISC) ? 1 : 2);
11510Sstevel@tonic-gate ifp->int_rdisc_cnt++;
11520Sstevel@tonic-gate
11530Sstevel@tonic-gate intvl_random(&ifp->int_rdisc_timer,
11540Sstevel@tonic-gate (ifp->int_rdisc_int*3)/4, ifp->int_rdisc_int);
11550Sstevel@tonic-gate if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS &&
11560Sstevel@tonic-gate (ifp->int_rdisc_timer.tv_sec >
11570Sstevel@tonic-gate MAX_INITIAL_ADVERT_INTERVAL)) {
11580Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec =
11590Sstevel@tonic-gate MAX_INITIAL_ADVERT_INTERVAL;
11600Sstevel@tonic-gate }
11610Sstevel@tonic-gate timevaladd(&ifp->int_rdisc_timer, &now);
11620Sstevel@tonic-gate }
11630Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
11640Sstevel@tonic-gate > /* cstyle */))
11650Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer;
11660Sstevel@tonic-gate }
11670Sstevel@tonic-gate }
11680Sstevel@tonic-gate
11690Sstevel@tonic-gate
11700Sstevel@tonic-gate /* Solicit for Router Discovery */
11710Sstevel@tonic-gate void
rdisc_sol(void)11720Sstevel@tonic-gate rdisc_sol(void)
11730Sstevel@tonic-gate {
11740Sstevel@tonic-gate struct interface *ifp;
11750Sstevel@tonic-gate union ad_u u;
11760Sstevel@tonic-gate
11770Sstevel@tonic-gate if (should_supply(NULL))
11780Sstevel@tonic-gate return;
11790Sstevel@tonic-gate
11800Sstevel@tonic-gate rdisc_timer.tv_sec = now.tv_sec + NEVER;
11810Sstevel@tonic-gate
11820Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) {
11830Sstevel@tonic-gate if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) ||
11840Sstevel@tonic-gate ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
11850Sstevel@tonic-gate continue;
11860Sstevel@tonic-gate
11870Sstevel@tonic-gate /* skip interfaces we shouldn't use */
11880Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags))
11890Sstevel@tonic-gate continue;
11900Sstevel@tonic-gate
11910Sstevel@tonic-gate if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */)) {
11920Sstevel@tonic-gate (void) memset(&u, 0, sizeof (u.so));
11930Sstevel@tonic-gate u.so.icmp_type = ICMP_ROUTERSOLICIT;
11940Sstevel@tonic-gate u.so.icmp_cksum = in_cksum((uint16_t *)&u.so,
11950Sstevel@tonic-gate sizeof (u.so));
11960Sstevel@tonic-gate send_rdisc(&u, sizeof (u.so), ifp,
11970Sstevel@tonic-gate htonl(INADDR_ALLRTRS_GROUP),
11980Sstevel@tonic-gate ((ifp->int_state&IS_BCAST_RDISC) ? bcast : mcast));
11990Sstevel@tonic-gate
12000Sstevel@tonic-gate if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
12010Sstevel@tonic-gate continue;
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
12040Sstevel@tonic-gate ifp->int_rdisc_timer.tv_usec = 0;
12050Sstevel@tonic-gate timevaladd(&ifp->int_rdisc_timer, &now);
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate
12080Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
12090Sstevel@tonic-gate > /* cstyle */))
12100Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer;
12110Sstevel@tonic-gate }
12120Sstevel@tonic-gate }
12130Sstevel@tonic-gate
12140Sstevel@tonic-gate
12150Sstevel@tonic-gate /*
12160Sstevel@tonic-gate * check the IP header of a possible Router Discovery ICMP packet
12170Sstevel@tonic-gate * Returns 0 if bad
12180Sstevel@tonic-gate */
12190Sstevel@tonic-gate static struct interface *
ck_icmp(const char * act,in_addr_t from,struct interface * ifp,in_addr_t to,union ad_u * p,uint_t len)12200Sstevel@tonic-gate ck_icmp(const char *act,
12210Sstevel@tonic-gate in_addr_t from,
12220Sstevel@tonic-gate struct interface *ifp,
12230Sstevel@tonic-gate in_addr_t to,
12240Sstevel@tonic-gate union ad_u *p,
12250Sstevel@tonic-gate uint_t len)
12260Sstevel@tonic-gate {
12270Sstevel@tonic-gate const char *type;
12280Sstevel@tonic-gate
12290Sstevel@tonic-gate
12300Sstevel@tonic-gate if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
12310Sstevel@tonic-gate type = "advertisement";
12320Sstevel@tonic-gate if (p->icmp.icmp_code == ICMP_ROUTERADVERT_NOCOMMON)
12330Sstevel@tonic-gate return (NULL); /* Mobile IP */
12340Sstevel@tonic-gate } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
12350Sstevel@tonic-gate type = "solicitation";
12360Sstevel@tonic-gate } else {
12370Sstevel@tonic-gate return (NULL);
12380Sstevel@tonic-gate }
12390Sstevel@tonic-gate
12400Sstevel@tonic-gate if (p->icmp.icmp_code != ICMP_ROUTERADVERT_COMMON) {
12410Sstevel@tonic-gate trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
12420Sstevel@tonic-gate type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to));
12430Sstevel@tonic-gate return (NULL);
12440Sstevel@tonic-gate }
12450Sstevel@tonic-gate
12460Sstevel@tonic-gate trace_rdisc(act, from, to, ifp, p, len);
12470Sstevel@tonic-gate
12480Sstevel@tonic-gate if (ifp == NULL)
12490Sstevel@tonic-gate trace_pkt("unknown interface for router-discovery %s from %s "
12500Sstevel@tonic-gate "to %s", type, naddr_ntoa(from), naddr_ntoa(to));
12510Sstevel@tonic-gate
12520Sstevel@tonic-gate return (ifp);
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate
12550Sstevel@tonic-gate
12560Sstevel@tonic-gate /* Read packets from the router discovery socket */
12570Sstevel@tonic-gate void
read_d(void)12580Sstevel@tonic-gate read_d(void)
12590Sstevel@tonic-gate {
12600Sstevel@tonic-gate #define PKTLEN 512
12610Sstevel@tonic-gate static struct msg_limit bad_asize, bad_len;
12620Sstevel@tonic-gate struct sockaddr_in from;
12630Sstevel@tonic-gate int n, cc, hlen;
12640Sstevel@tonic-gate struct {
12650Sstevel@tonic-gate union {
12660Sstevel@tonic-gate struct ip ip;
12670Sstevel@tonic-gate uint16_t s[PKTLEN/sizeof (uint16_t)];
12680Sstevel@tonic-gate uint8_t b[PKTLEN/sizeof (uint8_t)];
12690Sstevel@tonic-gate } pkt;
12700Sstevel@tonic-gate } buf;
12710Sstevel@tonic-gate union ad_u *p;
12720Sstevel@tonic-gate n_long *wp;
12730Sstevel@tonic-gate struct interface *ifp;
12740Sstevel@tonic-gate boolean_t needsort = _B_FALSE;
12750Sstevel@tonic-gate struct msghdr msg;
12760Sstevel@tonic-gate struct iovec iov;
12770Sstevel@tonic-gate uint8_t ancillary_data[CONTROL_BUFSIZE];
12780Sstevel@tonic-gate
12790Sstevel@tonic-gate iov.iov_base = &buf;
12800Sstevel@tonic-gate iov.iov_len = sizeof (buf);
12810Sstevel@tonic-gate msg.msg_iov = &iov;
12820Sstevel@tonic-gate msg.msg_iovlen = 1;
12830Sstevel@tonic-gate msg.msg_name = &from;
12840Sstevel@tonic-gate msg.msg_control = &ancillary_data;
12850Sstevel@tonic-gate
12860Sstevel@tonic-gate for (;;) {
12870Sstevel@tonic-gate msg.msg_namelen = sizeof (from);
12880Sstevel@tonic-gate msg.msg_controllen = sizeof (ancillary_data);
12890Sstevel@tonic-gate cc = recvmsg(rdisc_sock, &msg, 0);
12900Sstevel@tonic-gate if (cc <= 0) {
12910Sstevel@tonic-gate if (cc < 0 && errno != EWOULDBLOCK)
1292*5381Smeem LOGERR("recvmsg(rdisc_sock)");
12930Sstevel@tonic-gate break;
12940Sstevel@tonic-gate }
12950Sstevel@tonic-gate
12960Sstevel@tonic-gate hlen = buf.pkt.ip.ip_hl << 2;
12970Sstevel@tonic-gate if (cc < hlen + ICMP_MINLEN)
12980Sstevel@tonic-gate continue;
12990Sstevel@tonic-gate /* LINTED [alignment will be lw aligned] */
13000Sstevel@tonic-gate p = (union ad_u *)&buf.pkt.b[hlen];
13010Sstevel@tonic-gate cc -= hlen;
13020Sstevel@tonic-gate
13030Sstevel@tonic-gate /*
13040Sstevel@tonic-gate * If we could tell the interface on which a packet from
13050Sstevel@tonic-gate * address 0 arrived, we could deal with such solicitations.
13060Sstevel@tonic-gate */
13070Sstevel@tonic-gate ifp = receiving_interface(&msg, _B_FALSE);
13080Sstevel@tonic-gate ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
13090Sstevel@tonic-gate buf.pkt.ip.ip_dst.s_addr, p, cc);
13100Sstevel@tonic-gate if (ifp == NULL)
13110Sstevel@tonic-gate continue;
13120Sstevel@tonic-gate
13130Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags)) {
13140Sstevel@tonic-gate trace_misc("discard RDISC packet received over %s, %X",
13150Sstevel@tonic-gate ifp->int_name, ifp->int_if_flags);
13160Sstevel@tonic-gate continue;
13170Sstevel@tonic-gate }
13180Sstevel@tonic-gate
13190Sstevel@tonic-gate if (from.sin_addr.s_addr != 0 &&
13200Sstevel@tonic-gate ifwithaddr(from.sin_addr.s_addr, _B_FALSE, _B_FALSE)) {
13210Sstevel@tonic-gate trace_pkt(" "
13220Sstevel@tonic-gate "discard our own Router Discovery message");
13230Sstevel@tonic-gate continue;
13240Sstevel@tonic-gate }
13250Sstevel@tonic-gate
13260Sstevel@tonic-gate /* The remote address *must* be directly connected. */
13270Sstevel@tonic-gate if (!remote_address_ok(ifp, from.sin_addr.s_addr)) {
13280Sstevel@tonic-gate trace_misc("discard rdisc message; source %s not on "
13290Sstevel@tonic-gate "interface %s", naddr_ntoa(from.sin_addr.s_addr),
13300Sstevel@tonic-gate ifp->int_name);
13310Sstevel@tonic-gate continue;
13320Sstevel@tonic-gate }
13330Sstevel@tonic-gate
13340Sstevel@tonic-gate switch (p->icmp.icmp_type) {
13350Sstevel@tonic-gate case ICMP_ROUTERADVERT:
13360Sstevel@tonic-gate if (ifp->int_state & IS_NO_ADV_IN)
13370Sstevel@tonic-gate continue;
13380Sstevel@tonic-gate
13390Sstevel@tonic-gate if (p->ad.icmp_ad_asize*2*sizeof (wp[0]) <
13400Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info[0])) {
13410Sstevel@tonic-gate msglim(&bad_asize, from.sin_addr.s_addr,
13420Sstevel@tonic-gate "intolerable rdisc address size=%d",
13430Sstevel@tonic-gate p->ad.icmp_ad_asize);
13440Sstevel@tonic-gate continue;
13450Sstevel@tonic-gate }
13460Sstevel@tonic-gate if (p->ad.icmp_ad_num == 0) {
13470Sstevel@tonic-gate trace_pkt(" empty?");
13480Sstevel@tonic-gate continue;
13490Sstevel@tonic-gate }
13500Sstevel@tonic-gate if (cc < (sizeof (p->ad) -
13510Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info) +
13520Sstevel@tonic-gate (p->ad.icmp_ad_num *
13530Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info[0])))) {
13540Sstevel@tonic-gate msglim(&bad_len, from.sin_addr.s_addr,
13550Sstevel@tonic-gate "rdisc length %d does not match ad_num"
13560Sstevel@tonic-gate " %d", cc, p->ad.icmp_ad_num);
13570Sstevel@tonic-gate continue;
13580Sstevel@tonic-gate }
13590Sstevel@tonic-gate
13600Sstevel@tonic-gate needsort = _B_TRUE;
13610Sstevel@tonic-gate wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
13620Sstevel@tonic-gate for (n = 0; n < p->ad.icmp_ad_num; n++) {
13630Sstevel@tonic-gate parse_ad(from.sin_addr.s_addr,
13640Sstevel@tonic-gate wp[0], wp[1],
13650Sstevel@tonic-gate ntohs(p->ad.icmp_ad_life), ifp);
13660Sstevel@tonic-gate wp += p->ad.icmp_ad_asize;
13670Sstevel@tonic-gate }
13680Sstevel@tonic-gate break;
13690Sstevel@tonic-gate
13700Sstevel@tonic-gate
13710Sstevel@tonic-gate case ICMP_ROUTERSOLICIT:
13720Sstevel@tonic-gate if (!should_supply(ifp))
13730Sstevel@tonic-gate continue;
13740Sstevel@tonic-gate if ((ifp->int_state & IS_NO_ADV_OUT) ||
13750Sstevel@tonic-gate !IS_IFF_ROUTING(ifp->int_if_flags))
13760Sstevel@tonic-gate continue;
13770Sstevel@tonic-gate if (stopint != 0)
13780Sstevel@tonic-gate continue;
13790Sstevel@tonic-gate
13800Sstevel@tonic-gate /*
13810Sstevel@tonic-gate * We should handle messages from address 0,
13820Sstevel@tonic-gate * but cannot due to kernel limitations.
13830Sstevel@tonic-gate */
13840Sstevel@tonic-gate
13850Sstevel@tonic-gate /* Respond with a point-to-point advertisement */
13860Sstevel@tonic-gate send_adv(ifp, from.sin_addr.s_addr, 0);
13870Sstevel@tonic-gate break;
13880Sstevel@tonic-gate }
13890Sstevel@tonic-gate }
13900Sstevel@tonic-gate
13910Sstevel@tonic-gate if (needsort)
13920Sstevel@tonic-gate rdisc_sort();
13930Sstevel@tonic-gate }
13940Sstevel@tonic-gate
13950Sstevel@tonic-gate void
rdisc_dump(void)13960Sstevel@tonic-gate rdisc_dump(void)
13970Sstevel@tonic-gate {
13980Sstevel@tonic-gate struct dr *drp;
13990Sstevel@tonic-gate
14000Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++)
14010Sstevel@tonic-gate if (drp->dr_ts != 0)
14020Sstevel@tonic-gate trace_dr(drp);
14030Sstevel@tonic-gate }
14040Sstevel@tonic-gate
14050Sstevel@tonic-gate void
rdisc_suppress(struct interface * ifp)14060Sstevel@tonic-gate rdisc_suppress(struct interface *ifp)
14070Sstevel@tonic-gate {
14080Sstevel@tonic-gate if (ifp->int_state & IS_ADV_OUT) {
14090Sstevel@tonic-gate msglog("%s \"rdisc_adv\" specified, will not "
14100Sstevel@tonic-gate "suppress rdisc adv", ifp->int_name);
14110Sstevel@tonic-gate } else {
14120Sstevel@tonic-gate if (ifp->int_state & IS_SUPPRESS_RDISC)
14130Sstevel@tonic-gate return;
14140Sstevel@tonic-gate ifp->int_state |= (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
14150Sstevel@tonic-gate trace_misc("suppress rdisc adv on %s", ifp->int_name);
14160Sstevel@tonic-gate rdisc_timer.tv_sec = 0;
14170Sstevel@tonic-gate }
14180Sstevel@tonic-gate }
14190Sstevel@tonic-gate
14200Sstevel@tonic-gate void
rdisc_restore(struct interface * ifp)14210Sstevel@tonic-gate rdisc_restore(struct interface *ifp)
14220Sstevel@tonic-gate {
14230Sstevel@tonic-gate if ((ifp->int_state & IS_SUPPRESS_RDISC) == 0)
14240Sstevel@tonic-gate return;
14250Sstevel@tonic-gate ifp->int_state &= ~(IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
14260Sstevel@tonic-gate trace_misc("restoring rdisc adv on %s", ifp->int_name);
14270Sstevel@tonic-gate rdisc_timer.tv_sec = 0;
14280Sstevel@tonic-gate }
14293284Sapersson
14303284Sapersson void
process_d_mib_sock(void)14313284Sapersson process_d_mib_sock(void)
14323284Sapersson {
14333284Sapersson
14343284Sapersson socklen_t fromlen;
14353284Sapersson struct sockaddr_un from;
14363284Sapersson ssize_t len;
14373284Sapersson int command;
14383284Sapersson struct dr *drp;
14393284Sapersson rdisc_info_t rdisc_info;
14403284Sapersson defr_t def_router;
14413284Sapersson extern int max_ads;
14423284Sapersson int num = 0;
14433284Sapersson
14443284Sapersson fromlen = (socklen_t)sizeof (from);
14453284Sapersson len = recvfrom(rdisc_mib_sock, &command, sizeof (int), 0,
14463284Sapersson (struct sockaddr *)&from, &fromlen);
14473284Sapersson
14483284Sapersson if (len < sizeof (int) || command != RDISC_SNMP_INFO_REQ) {
14493284Sapersson trace_misc("Bad command on rdisc_mib_sock");
14503284Sapersson return;
14513284Sapersson }
14523284Sapersson
14533284Sapersson /*
14543284Sapersson * Count number of good routers
14553284Sapersson */
14563284Sapersson for (drp = drs; drp < &drs[max_ads]; drp++) {
14573284Sapersson if (drp->dr_ts != 0) {
14583284Sapersson num++;
14593284Sapersson }
14603284Sapersson }
14613284Sapersson
14623284Sapersson rdisc_info.info_type = RDISC_SNMP_INFO_RESPONSE;
14633284Sapersson rdisc_info.info_version = RDISC_SNMP_INFO_VER;
14643284Sapersson rdisc_info.info_num_of_routers = num;
14653284Sapersson
14663284Sapersson (void) sendto(rdisc_mib_sock, &rdisc_info, sizeof (rdisc_info_t), 0,
14673284Sapersson (struct sockaddr *)&from, fromlen);
14683284Sapersson
14693284Sapersson for (drp = drs; drp < &drs[max_ads]; drp++) {
14703284Sapersson if (drp->dr_ts != 0) {
14713284Sapersson def_router.defr_info_type = RDISC_DEF_ROUTER_INFO;
14723284Sapersson def_router.defr_version = RDISC_DEF_ROUTER_VER;
14733284Sapersson def_router.defr_index =
14743284Sapersson drp->dr_ifp->int_phys->phyi_index;
14753284Sapersson def_router.defr_life = drp->dr_life;
14763284Sapersson def_router.defr_addr.s_addr = drp->dr_gate;
14773284Sapersson def_router.defr_pref = drp->dr_pref;
14783284Sapersson (void) sendto(rdisc_mib_sock, &def_router,
14793284Sapersson sizeof (defr_t), 0, (struct sockaddr *)&from,
14803284Sapersson fromlen);
14813284Sapersson }
14823284Sapersson }
14833284Sapersson }
1484