1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 4*7c478bd9Sstevel@tonic-gate * 5*7c478bd9Sstevel@tonic-gate * Copyright (c) 1995 6*7c478bd9Sstevel@tonic-gate * The Regents of the University of California. All rights reserved. 7*7c478bd9Sstevel@tonic-gate * 8*7c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without 9*7c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions 10*7c478bd9Sstevel@tonic-gate * are met: 11*7c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright 12*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer. 13*7c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright 14*7c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the 15*7c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution. 16*7c478bd9Sstevel@tonic-gate * 3. All advertising materials mentioning features or use of this software 17*7c478bd9Sstevel@tonic-gate * must display the following acknowledgment: 18*7c478bd9Sstevel@tonic-gate * This product includes software developed by the University of 19*7c478bd9Sstevel@tonic-gate * California, Berkeley and its contributors. 20*7c478bd9Sstevel@tonic-gate * 4. Neither the name of the University nor the names of its contributors 21*7c478bd9Sstevel@tonic-gate * may be used to endorse or promote products derived from this software 22*7c478bd9Sstevel@tonic-gate * without specific prior written permission. 23*7c478bd9Sstevel@tonic-gate * 24*7c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25*7c478bd9Sstevel@tonic-gate * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26*7c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27*7c478bd9Sstevel@tonic-gate * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28*7c478bd9Sstevel@tonic-gate * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29*7c478bd9Sstevel@tonic-gate * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30*7c478bd9Sstevel@tonic-gate * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31*7c478bd9Sstevel@tonic-gate * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32*7c478bd9Sstevel@tonic-gate * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33*7c478bd9Sstevel@tonic-gate * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34*7c478bd9Sstevel@tonic-gate * SUCH DAMAGE. 35*7c478bd9Sstevel@tonic-gate * 36*7c478bd9Sstevel@tonic-gate * $FreeBSD: src/sbin/routed/rdisc.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $ 37*7c478bd9Sstevel@tonic-gate */ 38*7c478bd9Sstevel@tonic-gate 39*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 40*7c478bd9Sstevel@tonic-gate 41*7c478bd9Sstevel@tonic-gate #include "defs.h" 42*7c478bd9Sstevel@tonic-gate #include <netinet/in_systm.h> 43*7c478bd9Sstevel@tonic-gate #include <netinet/ip.h> 44*7c478bd9Sstevel@tonic-gate #include <netinet/ip_icmp.h> 45*7c478bd9Sstevel@tonic-gate 46*7c478bd9Sstevel@tonic-gate /* 47*7c478bd9Sstevel@tonic-gate * The size of the control buffer passed to recvmsg() used to receive 48*7c478bd9Sstevel@tonic-gate * ancillary data. 49*7c478bd9Sstevel@tonic-gate */ 50*7c478bd9Sstevel@tonic-gate #define CONTROL_BUFSIZE 1024 51*7c478bd9Sstevel@tonic-gate 52*7c478bd9Sstevel@tonic-gate /* router advertisement ICMP packet */ 53*7c478bd9Sstevel@tonic-gate struct icmp_ad { 54*7c478bd9Sstevel@tonic-gate uint8_t icmp_type; /* type of message */ 55*7c478bd9Sstevel@tonic-gate uint8_t icmp_code; /* type sub code */ 56*7c478bd9Sstevel@tonic-gate uint16_t icmp_cksum; /* ones complement cksum of struct */ 57*7c478bd9Sstevel@tonic-gate uint8_t icmp_ad_num; /* # of following router addresses */ 58*7c478bd9Sstevel@tonic-gate uint8_t icmp_ad_asize; /* 2--words in each advertisement */ 59*7c478bd9Sstevel@tonic-gate uint16_t icmp_ad_life; /* seconds of validity */ 60*7c478bd9Sstevel@tonic-gate struct icmp_ad_info { 61*7c478bd9Sstevel@tonic-gate in_addr_t icmp_ad_addr; 62*7c478bd9Sstevel@tonic-gate uint32_t icmp_ad_pref; 63*7c478bd9Sstevel@tonic-gate } icmp_ad_info[1]; 64*7c478bd9Sstevel@tonic-gate }; 65*7c478bd9Sstevel@tonic-gate 66*7c478bd9Sstevel@tonic-gate /* router solicitation ICMP packet */ 67*7c478bd9Sstevel@tonic-gate struct icmp_so { 68*7c478bd9Sstevel@tonic-gate uint8_t icmp_type; /* type of message */ 69*7c478bd9Sstevel@tonic-gate uint8_t icmp_code; /* type sub code */ 70*7c478bd9Sstevel@tonic-gate uint16_t icmp_cksum; /* ones complement cksum of struct */ 71*7c478bd9Sstevel@tonic-gate uint32_t icmp_so_rsvd; 72*7c478bd9Sstevel@tonic-gate }; 73*7c478bd9Sstevel@tonic-gate 74*7c478bd9Sstevel@tonic-gate union ad_u { 75*7c478bd9Sstevel@tonic-gate struct icmp icmp; 76*7c478bd9Sstevel@tonic-gate struct icmp_ad ad; 77*7c478bd9Sstevel@tonic-gate struct icmp_so so; 78*7c478bd9Sstevel@tonic-gate }; 79*7c478bd9Sstevel@tonic-gate 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate int rdisc_sock = -1; /* router-discovery raw socket */ 82*7c478bd9Sstevel@tonic-gate static struct interface *rdisc_sock_interface; /* current rdisc interface */ 83*7c478bd9Sstevel@tonic-gate 84*7c478bd9Sstevel@tonic-gate struct timeval rdisc_timer; 85*7c478bd9Sstevel@tonic-gate boolean_t rdisc_ok; /* using solicited route */ 86*7c478bd9Sstevel@tonic-gate 87*7c478bd9Sstevel@tonic-gate #define MAX_ADS 16 88*7c478bd9Sstevel@tonic-gate int max_ads; /* at least one per interface */ 89*7c478bd9Sstevel@tonic-gate /* accumulated advertisements */ 90*7c478bd9Sstevel@tonic-gate static struct dr *cur_drp, *drs; 91*7c478bd9Sstevel@tonic-gate 92*7c478bd9Sstevel@tonic-gate /* 93*7c478bd9Sstevel@tonic-gate * adjust unsigned preference by interface metric, 94*7c478bd9Sstevel@tonic-gate * without driving it to infinity 95*7c478bd9Sstevel@tonic-gate */ 96*7c478bd9Sstevel@tonic-gate #define PREF(p, ifp) ((p) <= (uint32_t)(ifp)->int_metric ? ((p) != 0 ? 1 : 0) \ 97*7c478bd9Sstevel@tonic-gate : (p) - ((ifp)->int_metric)) 98*7c478bd9Sstevel@tonic-gate 99*7c478bd9Sstevel@tonic-gate static void rdisc_sort(void); 100*7c478bd9Sstevel@tonic-gate 101*7c478bd9Sstevel@tonic-gate typedef enum { unicast, bcast, mcast } dstaddr_t; 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate /* dump an ICMP Router Discovery Advertisement Message */ 104*7c478bd9Sstevel@tonic-gate static void 105*7c478bd9Sstevel@tonic-gate trace_rdisc(const char *act, 106*7c478bd9Sstevel@tonic-gate uint32_t from, 107*7c478bd9Sstevel@tonic-gate uint32_t to, 108*7c478bd9Sstevel@tonic-gate struct interface *ifp, 109*7c478bd9Sstevel@tonic-gate union ad_u *p, 110*7c478bd9Sstevel@tonic-gate uint_t len) 111*7c478bd9Sstevel@tonic-gate { 112*7c478bd9Sstevel@tonic-gate int i; 113*7c478bd9Sstevel@tonic-gate n_long *wp, *lim; 114*7c478bd9Sstevel@tonic-gate 115*7c478bd9Sstevel@tonic-gate 116*7c478bd9Sstevel@tonic-gate if (!TRACEPACKETS || ftrace == 0) 117*7c478bd9Sstevel@tonic-gate return; 118*7c478bd9Sstevel@tonic-gate 119*7c478bd9Sstevel@tonic-gate lastlog(); 120*7c478bd9Sstevel@tonic-gate 121*7c478bd9Sstevel@tonic-gate if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 122*7c478bd9Sstevel@tonic-gate (void) fprintf(ftrace, "%s Router Ad" 123*7c478bd9Sstevel@tonic-gate " from %s to %s via %s life=%d\n", 124*7c478bd9Sstevel@tonic-gate act, naddr_ntoa(from), naddr_ntoa(to), 125*7c478bd9Sstevel@tonic-gate ifp ? ifp->int_name : "?", 126*7c478bd9Sstevel@tonic-gate ntohs(p->ad.icmp_ad_life)); 127*7c478bd9Sstevel@tonic-gate if (!TRACECONTENTS) 128*7c478bd9Sstevel@tonic-gate return; 129*7c478bd9Sstevel@tonic-gate 130*7c478bd9Sstevel@tonic-gate wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 131*7c478bd9Sstevel@tonic-gate lim = &wp[(len - sizeof (p->ad)) / sizeof (*wp)]; 132*7c478bd9Sstevel@tonic-gate for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) { 133*7c478bd9Sstevel@tonic-gate (void) fprintf(ftrace, "\t%s preference=%ld", 134*7c478bd9Sstevel@tonic-gate naddr_ntoa(wp[0]), (long)ntohl(wp[1])); 135*7c478bd9Sstevel@tonic-gate wp += p->ad.icmp_ad_asize; 136*7c478bd9Sstevel@tonic-gate } 137*7c478bd9Sstevel@tonic-gate (void) fputc('\n', ftrace); 138*7c478bd9Sstevel@tonic-gate 139*7c478bd9Sstevel@tonic-gate } else { 140*7c478bd9Sstevel@tonic-gate trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x", 141*7c478bd9Sstevel@tonic-gate act, naddr_ntoa(from), naddr_ntoa(to), 142*7c478bd9Sstevel@tonic-gate ifp ? ifp->int_name : "?", 143*7c478bd9Sstevel@tonic-gate ntohl(p->so.icmp_so_rsvd)); 144*7c478bd9Sstevel@tonic-gate } 145*7c478bd9Sstevel@tonic-gate } 146*7c478bd9Sstevel@tonic-gate 147*7c478bd9Sstevel@tonic-gate /* 148*7c478bd9Sstevel@tonic-gate * Prepare Router Discovery socket. 149*7c478bd9Sstevel@tonic-gate */ 150*7c478bd9Sstevel@tonic-gate static void 151*7c478bd9Sstevel@tonic-gate get_rdisc_sock(void) 152*7c478bd9Sstevel@tonic-gate { 153*7c478bd9Sstevel@tonic-gate int on = 1; 154*7c478bd9Sstevel@tonic-gate unsigned char ttl = 1; 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate if (rdisc_sock < 0) { 157*7c478bd9Sstevel@tonic-gate max_ads = MAX_ADS; 158*7c478bd9Sstevel@tonic-gate drs = rtmalloc(max_ads * sizeof (struct dr), "get_rdisc_sock"); 159*7c478bd9Sstevel@tonic-gate (void) memset(drs, 0, max_ads * sizeof (struct dr)); 160*7c478bd9Sstevel@tonic-gate rdisc_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); 161*7c478bd9Sstevel@tonic-gate if (rdisc_sock < 0) 162*7c478bd9Sstevel@tonic-gate BADERR(_B_TRUE, "rdisc_sock = socket()"); 163*7c478bd9Sstevel@tonic-gate fix_sock(rdisc_sock, "rdisc_sock"); 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_RECVIF, &on, 166*7c478bd9Sstevel@tonic-gate sizeof (on))) 167*7c478bd9Sstevel@tonic-gate BADERR(_B_FALSE, "setsockopt(IP_RECVIF)"); 168*7c478bd9Sstevel@tonic-gate 169*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_TTL, 170*7c478bd9Sstevel@tonic-gate &ttl, sizeof (ttl)) < 0) 171*7c478bd9Sstevel@tonic-gate DBGERR(_B_TRUE, 172*7c478bd9Sstevel@tonic-gate "rdisc_sock setsockopt(IP_MULTICAST_TTL)"); 173*7c478bd9Sstevel@tonic-gate 174*7c478bd9Sstevel@tonic-gate fix_select(); 175*7c478bd9Sstevel@tonic-gate } 176*7c478bd9Sstevel@tonic-gate } 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate 179*7c478bd9Sstevel@tonic-gate /* 180*7c478bd9Sstevel@tonic-gate * Pick multicast group for router-discovery socket 181*7c478bd9Sstevel@tonic-gate */ 182*7c478bd9Sstevel@tonic-gate void 183*7c478bd9Sstevel@tonic-gate set_rdisc_mg(struct interface *ifp, 184*7c478bd9Sstevel@tonic-gate int on) /* 0=turn it off */ 185*7c478bd9Sstevel@tonic-gate { 186*7c478bd9Sstevel@tonic-gate struct ip_mreq m; 187*7c478bd9Sstevel@tonic-gate boolean_t dosupply; 188*7c478bd9Sstevel@tonic-gate 189*7c478bd9Sstevel@tonic-gate if (rdisc_sock < 0) { 190*7c478bd9Sstevel@tonic-gate /* 191*7c478bd9Sstevel@tonic-gate * Create the raw socket so that we can hear at least 192*7c478bd9Sstevel@tonic-gate * broadcast router discovery packets. 193*7c478bd9Sstevel@tonic-gate */ 194*7c478bd9Sstevel@tonic-gate if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC || 195*7c478bd9Sstevel@tonic-gate !on) 196*7c478bd9Sstevel@tonic-gate return; 197*7c478bd9Sstevel@tonic-gate get_rdisc_sock(); 198*7c478bd9Sstevel@tonic-gate } 199*7c478bd9Sstevel@tonic-gate 200*7c478bd9Sstevel@tonic-gate if (!(ifp->int_if_flags & IFF_MULTICAST)) { 201*7c478bd9Sstevel@tonic-gate /* Can't multicast, so no groups could have been joined. */ 202*7c478bd9Sstevel@tonic-gate ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS); 203*7c478bd9Sstevel@tonic-gate return; 204*7c478bd9Sstevel@tonic-gate } 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate dosupply = should_supply(ifp); 207*7c478bd9Sstevel@tonic-gate 208*7c478bd9Sstevel@tonic-gate (void) memset(&m, 0, sizeof (m)); 209*7c478bd9Sstevel@tonic-gate m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) && 210*7c478bd9Sstevel@tonic-gate (ifp->int_dstaddr != 0) ? ifp->int_dstaddr : ifp->int_addr); 211*7c478bd9Sstevel@tonic-gate if (dosupply || (ifp->int_state & IS_NO_ADV_IN) || !on) { 212*7c478bd9Sstevel@tonic-gate /* stop listening to advertisements */ 213*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_ALL_HOSTS) { 214*7c478bd9Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 215*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, 216*7c478bd9Sstevel@tonic-gate IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 && 217*7c478bd9Sstevel@tonic-gate errno != EADDRNOTAVAIL && errno != ENOENT) 218*7c478bd9Sstevel@tonic-gate LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS"); 219*7c478bd9Sstevel@tonic-gate ifp->int_state &= ~IS_ALL_HOSTS; 220*7c478bd9Sstevel@tonic-gate } 221*7c478bd9Sstevel@tonic-gate 222*7c478bd9Sstevel@tonic-gate } else if (!(ifp->int_state & IS_ALL_HOSTS)) { 223*7c478bd9Sstevel@tonic-gate /* start listening to advertisements */ 224*7c478bd9Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP); 225*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 226*7c478bd9Sstevel@tonic-gate &m, sizeof (m)) < 0) { 227*7c478bd9Sstevel@tonic-gate LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS"); 228*7c478bd9Sstevel@tonic-gate } else { 229*7c478bd9Sstevel@tonic-gate ifp->int_state |= IS_ALL_HOSTS; 230*7c478bd9Sstevel@tonic-gate } 231*7c478bd9Sstevel@tonic-gate } 232*7c478bd9Sstevel@tonic-gate 233*7c478bd9Sstevel@tonic-gate if (!dosupply || (ifp->int_state & IS_NO_ADV_OUT) || 234*7c478bd9Sstevel@tonic-gate !IS_IFF_ROUTING(ifp->int_if_flags) || !on) { 235*7c478bd9Sstevel@tonic-gate /* stop listening to solicitations */ 236*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_ALL_ROUTERS) { 237*7c478bd9Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP); 238*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, 239*7c478bd9Sstevel@tonic-gate IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 && 240*7c478bd9Sstevel@tonic-gate errno != EADDRNOTAVAIL && errno != ENOENT) 241*7c478bd9Sstevel@tonic-gate LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS"); 242*7c478bd9Sstevel@tonic-gate ifp->int_state &= ~IS_ALL_ROUTERS; 243*7c478bd9Sstevel@tonic-gate } 244*7c478bd9Sstevel@tonic-gate 245*7c478bd9Sstevel@tonic-gate } else if (!(ifp->int_state & IS_ALL_ROUTERS)) { 246*7c478bd9Sstevel@tonic-gate /* start hearing solicitations */ 247*7c478bd9Sstevel@tonic-gate m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP); 248*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 249*7c478bd9Sstevel@tonic-gate &m, sizeof (m)) < 0) { 250*7c478bd9Sstevel@tonic-gate LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS"); 251*7c478bd9Sstevel@tonic-gate } else { 252*7c478bd9Sstevel@tonic-gate ifp->int_state |= IS_ALL_ROUTERS; 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate } 255*7c478bd9Sstevel@tonic-gate } 256*7c478bd9Sstevel@tonic-gate 257*7c478bd9Sstevel@tonic-gate 258*7c478bd9Sstevel@tonic-gate /* 259*7c478bd9Sstevel@tonic-gate * start or stop supplying routes to other systems. 260*7c478bd9Sstevel@tonic-gate */ 261*7c478bd9Sstevel@tonic-gate void 262*7c478bd9Sstevel@tonic-gate set_supplier(void) 263*7c478bd9Sstevel@tonic-gate { 264*7c478bd9Sstevel@tonic-gate struct interface *ifp; 265*7c478bd9Sstevel@tonic-gate struct dr *drp; 266*7c478bd9Sstevel@tonic-gate static boolean_t supplystate = _B_FALSE; 267*7c478bd9Sstevel@tonic-gate 268*7c478bd9Sstevel@tonic-gate if (supplystate == (fwd_interfaces > 1)) 269*7c478bd9Sstevel@tonic-gate return; 270*7c478bd9Sstevel@tonic-gate supplystate = fwd_interfaces > 1; 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate trace_act("%d forwarding interfaces present; becoming %ssupplier", 273*7c478bd9Sstevel@tonic-gate fwd_interfaces, supplystate ? "" : "non-"); 274*7c478bd9Sstevel@tonic-gate 275*7c478bd9Sstevel@tonic-gate if (supplystate) { 276*7c478bd9Sstevel@tonic-gate /* Forget discovered routes. */ 277*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 278*7c478bd9Sstevel@tonic-gate drp->dr_recv_pref = DEF_PREFERENCELEVEL; 279*7c478bd9Sstevel@tonic-gate drp->dr_life = 0; 280*7c478bd9Sstevel@tonic-gate } 281*7c478bd9Sstevel@tonic-gate rdisc_age(0); 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate /* 284*7c478bd9Sstevel@tonic-gate * Do not start advertising until we have heard some 285*7c478bd9Sstevel@tonic-gate * RIP routes. 286*7c478bd9Sstevel@tonic-gate */ 287*7c478bd9Sstevel@tonic-gate LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME); 288*7c478bd9Sstevel@tonic-gate 289*7c478bd9Sstevel@tonic-gate /* get rid of any redirects */ 290*7c478bd9Sstevel@tonic-gate del_redirects(0, 0); 291*7c478bd9Sstevel@tonic-gate } else { 292*7c478bd9Sstevel@tonic-gate /* 293*7c478bd9Sstevel@tonic-gate * Flush out all those advertisements we had sent by sending 294*7c478bd9Sstevel@tonic-gate * one with lifetime=0. 295*7c478bd9Sstevel@tonic-gate */ 296*7c478bd9Sstevel@tonic-gate rdisc_adv(_B_TRUE); 297*7c478bd9Sstevel@tonic-gate } 298*7c478bd9Sstevel@tonic-gate 299*7c478bd9Sstevel@tonic-gate /* 300*7c478bd9Sstevel@tonic-gate * Switch router discovery multicast groups from soliciting 301*7c478bd9Sstevel@tonic-gate * to advertising or back. 302*7c478bd9Sstevel@tonic-gate */ 303*7c478bd9Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) { 304*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_BROKE) 305*7c478bd9Sstevel@tonic-gate continue; 306*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt = 0; 307*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec; 308*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME; 309*7c478bd9Sstevel@tonic-gate set_rdisc_mg(ifp, 1); 310*7c478bd9Sstevel@tonic-gate } 311*7c478bd9Sstevel@tonic-gate } 312*7c478bd9Sstevel@tonic-gate 313*7c478bd9Sstevel@tonic-gate 314*7c478bd9Sstevel@tonic-gate /* 315*7c478bd9Sstevel@tonic-gate * Age discovered routes and find the best one 316*7c478bd9Sstevel@tonic-gate */ 317*7c478bd9Sstevel@tonic-gate void 318*7c478bd9Sstevel@tonic-gate rdisc_age(in_addr_t bad_gate) 319*7c478bd9Sstevel@tonic-gate { 320*7c478bd9Sstevel@tonic-gate time_t sec; 321*7c478bd9Sstevel@tonic-gate struct dr *drp; 322*7c478bd9Sstevel@tonic-gate struct rt_spare new; 323*7c478bd9Sstevel@tonic-gate struct rt_entry *rt; 324*7c478bd9Sstevel@tonic-gate 325*7c478bd9Sstevel@tonic-gate /* 326*7c478bd9Sstevel@tonic-gate * If we are being told about a bad router, 327*7c478bd9Sstevel@tonic-gate * then age the discovered default route, and if there is 328*7c478bd9Sstevel@tonic-gate * no alternative, solicit a replacement. 329*7c478bd9Sstevel@tonic-gate */ 330*7c478bd9Sstevel@tonic-gate if (bad_gate != 0) { 331*7c478bd9Sstevel@tonic-gate /* 332*7c478bd9Sstevel@tonic-gate * Look for the bad discovered default route. 333*7c478bd9Sstevel@tonic-gate * Age it and note its interface. 334*7c478bd9Sstevel@tonic-gate */ 335*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 336*7c478bd9Sstevel@tonic-gate if (drp->dr_ts == 0) 337*7c478bd9Sstevel@tonic-gate continue; 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate /* 340*7c478bd9Sstevel@tonic-gate * When we find the bad router, age the route 341*7c478bd9Sstevel@tonic-gate * to at most SUPPLY_INTERVAL. 342*7c478bd9Sstevel@tonic-gate * This is contrary to RFC 1256, but defends against 343*7c478bd9Sstevel@tonic-gate * black holes. 344*7c478bd9Sstevel@tonic-gate */ 345*7c478bd9Sstevel@tonic-gate if (drp->dr_gate == bad_gate) { 346*7c478bd9Sstevel@tonic-gate sec = (now.tv_sec - drp->dr_life + 347*7c478bd9Sstevel@tonic-gate SUPPLY_INTERVAL); 348*7c478bd9Sstevel@tonic-gate if (drp->dr_ts > sec) { 349*7c478bd9Sstevel@tonic-gate trace_act("age 0.0.0.0 --> %s via %s", 350*7c478bd9Sstevel@tonic-gate naddr_ntoa(drp->dr_gate), 351*7c478bd9Sstevel@tonic-gate drp->dr_ifp->int_name); 352*7c478bd9Sstevel@tonic-gate drp->dr_ts = sec; 353*7c478bd9Sstevel@tonic-gate } 354*7c478bd9Sstevel@tonic-gate break; 355*7c478bd9Sstevel@tonic-gate } 356*7c478bd9Sstevel@tonic-gate } 357*7c478bd9Sstevel@tonic-gate } else if (should_supply(NULL)) { 358*7c478bd9Sstevel@tonic-gate /* 359*7c478bd9Sstevel@tonic-gate * If switching from client to server, get rid of old 360*7c478bd9Sstevel@tonic-gate * default routes. 361*7c478bd9Sstevel@tonic-gate */ 362*7c478bd9Sstevel@tonic-gate if (cur_drp != NULL) { 363*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 364*7c478bd9Sstevel@tonic-gate /* 365*7c478bd9Sstevel@tonic-gate * If there is a current default router, and the 366*7c478bd9Sstevel@tonic-gate * there is no rt_spare entry, create one 367*7c478bd9Sstevel@tonic-gate * for cur_drp to prevent segmentation fault 368*7c478bd9Sstevel@tonic-gate * at rdisc_sort. 369*7c478bd9Sstevel@tonic-gate */ 370*7c478bd9Sstevel@tonic-gate if (rt == NULL) { 371*7c478bd9Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new)); 372*7c478bd9Sstevel@tonic-gate new.rts_ifp = cur_drp->dr_ifp; 373*7c478bd9Sstevel@tonic-gate new.rts_gate = cur_drp->dr_gate; 374*7c478bd9Sstevel@tonic-gate new.rts_router = cur_drp->dr_gate; 375*7c478bd9Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1; 376*7c478bd9Sstevel@tonic-gate new.rts_time = now.tv_sec; 377*7c478bd9Sstevel@tonic-gate new.rts_origin = RO_RDISC; 378*7c478bd9Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new); 379*7c478bd9Sstevel@tonic-gate } 380*7c478bd9Sstevel@tonic-gate 381*7c478bd9Sstevel@tonic-gate rdisc_sort(); 382*7c478bd9Sstevel@tonic-gate } 383*7c478bd9Sstevel@tonic-gate rdisc_adv(_B_FALSE); 384*7c478bd9Sstevel@tonic-gate } 385*7c478bd9Sstevel@tonic-gate 386*7c478bd9Sstevel@tonic-gate rdisc_sol(); 387*7c478bd9Sstevel@tonic-gate if (cur_drp != NULL) { 388*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 389*7c478bd9Sstevel@tonic-gate if (rt == NULL) { 390*7c478bd9Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new)); 391*7c478bd9Sstevel@tonic-gate new.rts_ifp = cur_drp->dr_ifp; 392*7c478bd9Sstevel@tonic-gate new.rts_gate = cur_drp->dr_gate; 393*7c478bd9Sstevel@tonic-gate new.rts_router = cur_drp->dr_gate; 394*7c478bd9Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1; 395*7c478bd9Sstevel@tonic-gate new.rts_time = now.tv_sec; 396*7c478bd9Sstevel@tonic-gate new.rts_origin = RO_RDISC; 397*7c478bd9Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new); 398*7c478bd9Sstevel@tonic-gate } 399*7c478bd9Sstevel@tonic-gate } 400*7c478bd9Sstevel@tonic-gate rdisc_sort(); 401*7c478bd9Sstevel@tonic-gate 402*7c478bd9Sstevel@tonic-gate /* 403*7c478bd9Sstevel@tonic-gate * Delete old redirected routes to keep the kernel table small, 404*7c478bd9Sstevel@tonic-gate * and to prevent black holes. Check that the kernel table 405*7c478bd9Sstevel@tonic-gate * matches the daemon table (i.e. has the default route). 406*7c478bd9Sstevel@tonic-gate * But only if RIP is not running and we are not dealing with 407*7c478bd9Sstevel@tonic-gate * a bad gateway, since otherwise age() will be called. 408*7c478bd9Sstevel@tonic-gate */ 409*7c478bd9Sstevel@tonic-gate if (rip_sock < 0 && bad_gate == 0) 410*7c478bd9Sstevel@tonic-gate age(0); 411*7c478bd9Sstevel@tonic-gate } 412*7c478bd9Sstevel@tonic-gate 413*7c478bd9Sstevel@tonic-gate 414*7c478bd9Sstevel@tonic-gate /* 415*7c478bd9Sstevel@tonic-gate * Zap all routes discovered via an interface that has gone bad 416*7c478bd9Sstevel@tonic-gate * This should only be called when !(ifp->int_state & IS_DUP) 417*7c478bd9Sstevel@tonic-gate * This is called by if_del and if_bad, and the interface pointer 418*7c478bd9Sstevel@tonic-gate * might not be valid after this. 419*7c478bd9Sstevel@tonic-gate */ 420*7c478bd9Sstevel@tonic-gate void 421*7c478bd9Sstevel@tonic-gate if_bad_rdisc(struct interface *ifp) 422*7c478bd9Sstevel@tonic-gate { 423*7c478bd9Sstevel@tonic-gate struct dr *drp; 424*7c478bd9Sstevel@tonic-gate 425*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 426*7c478bd9Sstevel@tonic-gate if (drp->dr_ifp != ifp) 427*7c478bd9Sstevel@tonic-gate continue; 428*7c478bd9Sstevel@tonic-gate (void) memset(drp, 0, sizeof (*drp)); 429*7c478bd9Sstevel@tonic-gate } 430*7c478bd9Sstevel@tonic-gate 431*7c478bd9Sstevel@tonic-gate /* make a note to re-solicit, turn RIP on or off, etc. */ 432*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = 0; 433*7c478bd9Sstevel@tonic-gate } 434*7c478bd9Sstevel@tonic-gate 435*7c478bd9Sstevel@tonic-gate /* 436*7c478bd9Sstevel@tonic-gate * Rewire all routes discovered via an interface that has gone bad 437*7c478bd9Sstevel@tonic-gate * This is only called by if_del. 438*7c478bd9Sstevel@tonic-gate */ 439*7c478bd9Sstevel@tonic-gate void 440*7c478bd9Sstevel@tonic-gate if_rewire_rdisc(struct interface *oldifp, struct interface *newifp) 441*7c478bd9Sstevel@tonic-gate { 442*7c478bd9Sstevel@tonic-gate struct dr *drp; 443*7c478bd9Sstevel@tonic-gate 444*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 445*7c478bd9Sstevel@tonic-gate if (drp->dr_ifp != oldifp) 446*7c478bd9Sstevel@tonic-gate continue; 447*7c478bd9Sstevel@tonic-gate drp->dr_ifp = newifp; 448*7c478bd9Sstevel@tonic-gate drp->dr_pref += (newifp->int_metric - oldifp->int_metric); 449*7c478bd9Sstevel@tonic-gate drp->dr_flags |= DR_CHANGED; 450*7c478bd9Sstevel@tonic-gate } 451*7c478bd9Sstevel@tonic-gate 452*7c478bd9Sstevel@tonic-gate /* make a note to re-solicit, turn RIP on or off, etc. */ 453*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = 0; 454*7c478bd9Sstevel@tonic-gate } 455*7c478bd9Sstevel@tonic-gate 456*7c478bd9Sstevel@tonic-gate /* 457*7c478bd9Sstevel@tonic-gate * Mark an interface ok for router discovering. 458*7c478bd9Sstevel@tonic-gate * This is called by if_ok and ifinit. 459*7c478bd9Sstevel@tonic-gate */ 460*7c478bd9Sstevel@tonic-gate void 461*7c478bd9Sstevel@tonic-gate if_ok_rdisc(struct interface *ifp) 462*7c478bd9Sstevel@tonic-gate { 463*7c478bd9Sstevel@tonic-gate set_rdisc_mg(ifp, 1); 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt = 0; 466*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = now.tv_sec + 467*7c478bd9Sstevel@tonic-gate ((ifp->int_state & IS_NO_ADV_OUT) ? 468*7c478bd9Sstevel@tonic-gate MAX_SOLICITATION_DELAY : MIN_WAITTIME); 469*7c478bd9Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, > /* cstyle */)) 470*7c478bd9Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer; 471*7c478bd9Sstevel@tonic-gate } 472*7c478bd9Sstevel@tonic-gate 473*7c478bd9Sstevel@tonic-gate /* 474*7c478bd9Sstevel@tonic-gate * Get rid of a dead discovered router 475*7c478bd9Sstevel@tonic-gate */ 476*7c478bd9Sstevel@tonic-gate static void 477*7c478bd9Sstevel@tonic-gate del_rdisc(struct dr *drp) 478*7c478bd9Sstevel@tonic-gate { 479*7c478bd9Sstevel@tonic-gate struct interface *ifp; 480*7c478bd9Sstevel@tonic-gate uint32_t gate; 481*7c478bd9Sstevel@tonic-gate int i; 482*7c478bd9Sstevel@tonic-gate struct rt_entry *rt; 483*7c478bd9Sstevel@tonic-gate struct rt_spare *rts = NULL; 484*7c478bd9Sstevel@tonic-gate 485*7c478bd9Sstevel@tonic-gate del_redirects(gate = drp->dr_gate, 0); 486*7c478bd9Sstevel@tonic-gate drp->dr_ts = 0; 487*7c478bd9Sstevel@tonic-gate drp->dr_life = 0; 488*7c478bd9Sstevel@tonic-gate 489*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 490*7c478bd9Sstevel@tonic-gate if (rt == NULL) { 491*7c478bd9Sstevel@tonic-gate trace_act("could not find default route in table"); 492*7c478bd9Sstevel@tonic-gate } else { 493*7c478bd9Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) { 494*7c478bd9Sstevel@tonic-gate if ((rt->rt_spares[i].rts_gate == drp->dr_gate) && 495*7c478bd9Sstevel@tonic-gate (rt->rt_spares[i].rts_origin == RO_RDISC)) { 496*7c478bd9Sstevel@tonic-gate rts = &rt->rt_spares[i]; 497*7c478bd9Sstevel@tonic-gate break; 498*7c478bd9Sstevel@tonic-gate } 499*7c478bd9Sstevel@tonic-gate } 500*7c478bd9Sstevel@tonic-gate if (rts != NULL) 501*7c478bd9Sstevel@tonic-gate rts_delete(rt, rts); 502*7c478bd9Sstevel@tonic-gate else 503*7c478bd9Sstevel@tonic-gate trace_act("could not find default route " 504*7c478bd9Sstevel@tonic-gate "through %s in table", naddr_ntoa(drp->dr_gate)); 505*7c478bd9Sstevel@tonic-gate } 506*7c478bd9Sstevel@tonic-gate 507*7c478bd9Sstevel@tonic-gate /* Count the other discovered routers on the interface. */ 508*7c478bd9Sstevel@tonic-gate i = 0; 509*7c478bd9Sstevel@tonic-gate ifp = drp->dr_ifp; 510*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 511*7c478bd9Sstevel@tonic-gate if (drp->dr_ts != 0 && drp->dr_ifp == ifp) 512*7c478bd9Sstevel@tonic-gate i++; 513*7c478bd9Sstevel@tonic-gate } 514*7c478bd9Sstevel@tonic-gate 515*7c478bd9Sstevel@tonic-gate /* 516*7c478bd9Sstevel@tonic-gate * If that was the last good discovered router on the interface, 517*7c478bd9Sstevel@tonic-gate * then solicit a new one. 518*7c478bd9Sstevel@tonic-gate * This is contrary to RFC 1256, but defends against black holes. 519*7c478bd9Sstevel@tonic-gate */ 520*7c478bd9Sstevel@tonic-gate if (i != 0) { 521*7c478bd9Sstevel@tonic-gate trace_act("discovered router %s via %s" 522*7c478bd9Sstevel@tonic-gate " is bad--have %d remaining", 523*7c478bd9Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name, i); 524*7c478bd9Sstevel@tonic-gate } else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) { 525*7c478bd9Sstevel@tonic-gate trace_act("last discovered router %s via %s" 526*7c478bd9Sstevel@tonic-gate " is bad--re-solicit", 527*7c478bd9Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name); 528*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt = 0; 529*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = 0; 530*7c478bd9Sstevel@tonic-gate rdisc_sol(); 531*7c478bd9Sstevel@tonic-gate } else { 532*7c478bd9Sstevel@tonic-gate trace_act("last discovered router %s via %s" 533*7c478bd9Sstevel@tonic-gate " is bad--wait to solicit", 534*7c478bd9Sstevel@tonic-gate naddr_ntoa(gate), ifp->int_name); 535*7c478bd9Sstevel@tonic-gate } 536*7c478bd9Sstevel@tonic-gate } 537*7c478bd9Sstevel@tonic-gate 538*7c478bd9Sstevel@tonic-gate 539*7c478bd9Sstevel@tonic-gate /* Find the best discovered route, and discard stale routers. */ 540*7c478bd9Sstevel@tonic-gate static void 541*7c478bd9Sstevel@tonic-gate rdisc_sort(void) 542*7c478bd9Sstevel@tonic-gate { 543*7c478bd9Sstevel@tonic-gate struct dr *drp, *new_drp; 544*7c478bd9Sstevel@tonic-gate struct rt_entry *rt; 545*7c478bd9Sstevel@tonic-gate struct rt_spare new, *rts; 546*7c478bd9Sstevel@tonic-gate struct interface *ifp; 547*7c478bd9Sstevel@tonic-gate uint_t new_st = 0; 548*7c478bd9Sstevel@tonic-gate uint32_t new_pref = DEF_PREFERENCELEVEL; 549*7c478bd9Sstevel@tonic-gate int first_rdisc_slot = 0; 550*7c478bd9Sstevel@tonic-gate int j; 551*7c478bd9Sstevel@tonic-gate boolean_t spares_avail; 552*7c478bd9Sstevel@tonic-gate void *ptr; 553*7c478bd9Sstevel@tonic-gate size_t ptrsize; 554*7c478bd9Sstevel@tonic-gate 555*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate /* 558*7c478bd9Sstevel@tonic-gate * If all the rt_spare entries are taken up with with default routes 559*7c478bd9Sstevel@tonic-gate * learnt from RIP (ie rts_origin = RO_RIP), bail out. 560*7c478bd9Sstevel@tonic-gate * NOTE: 561*7c478bd9Sstevel@tonic-gate * We *always* prefer default routes learned via RIP 562*7c478bd9Sstevel@tonic-gate * (ie RO_RIP) over those learnt via RDISC (ie RO_RDISC). 563*7c478bd9Sstevel@tonic-gate * The rdisc machinery should not modify, replace or 564*7c478bd9Sstevel@tonic-gate * remove any existing default routes with RO_RIP set. 565*7c478bd9Sstevel@tonic-gate */ 566*7c478bd9Sstevel@tonic-gate if (rt != NULL) { 567*7c478bd9Sstevel@tonic-gate spares_avail = _B_FALSE; 568*7c478bd9Sstevel@tonic-gate for (j = 0; j < rt->rt_num_spares; j++) { 569*7c478bd9Sstevel@tonic-gate rts = &rt->rt_spares[j]; 570*7c478bd9Sstevel@tonic-gate if (rts->rts_gate == 0 || rts->rts_origin != RO_RIP) { 571*7c478bd9Sstevel@tonic-gate spares_avail = _B_TRUE; 572*7c478bd9Sstevel@tonic-gate break; 573*7c478bd9Sstevel@tonic-gate } 574*7c478bd9Sstevel@tonic-gate } 575*7c478bd9Sstevel@tonic-gate if (!spares_avail) { 576*7c478bd9Sstevel@tonic-gate ptrsize = (rt->rt_num_spares + SPARE_INC) * 577*7c478bd9Sstevel@tonic-gate sizeof (struct rt_spare); 578*7c478bd9Sstevel@tonic-gate ptr = realloc(rt->rt_spares, ptrsize); 579*7c478bd9Sstevel@tonic-gate if (ptr != NULL) { 580*7c478bd9Sstevel@tonic-gate struct rt_spare *tmprts; 581*7c478bd9Sstevel@tonic-gate 582*7c478bd9Sstevel@tonic-gate rt->rt_spares = ptr; 583*7c478bd9Sstevel@tonic-gate rts = &rt->rt_spares[rt->rt_num_spares]; 584*7c478bd9Sstevel@tonic-gate (void) memset(rts, 0, 585*7c478bd9Sstevel@tonic-gate (SPARE_INC * sizeof (struct rt_spare))); 586*7c478bd9Sstevel@tonic-gate rt->rt_num_spares += SPARE_INC; 587*7c478bd9Sstevel@tonic-gate for (tmprts = rts, j = SPARE_INC; 588*7c478bd9Sstevel@tonic-gate j != 0; j--, tmprts++) 589*7c478bd9Sstevel@tonic-gate tmprts->rts_metric = HOPCNT_INFINITY; 590*7c478bd9Sstevel@tonic-gate spares_avail = _B_TRUE; 591*7c478bd9Sstevel@tonic-gate } else { 592*7c478bd9Sstevel@tonic-gate return; 593*7c478bd9Sstevel@tonic-gate } 594*7c478bd9Sstevel@tonic-gate } 595*7c478bd9Sstevel@tonic-gate } 596*7c478bd9Sstevel@tonic-gate /* Find the best RDISC advertiser */ 597*7c478bd9Sstevel@tonic-gate rt = NULL; 598*7c478bd9Sstevel@tonic-gate new_drp = NULL; 599*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 600*7c478bd9Sstevel@tonic-gate if (drp->dr_ts == 0) 601*7c478bd9Sstevel@tonic-gate continue; 602*7c478bd9Sstevel@tonic-gate ifp = drp->dr_ifp; 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate /* Get rid of expired discovered routers. */ 605*7c478bd9Sstevel@tonic-gate if (drp->dr_ts + drp->dr_life <= now.tv_sec) { 606*7c478bd9Sstevel@tonic-gate del_rdisc(drp); 607*7c478bd9Sstevel@tonic-gate continue; 608*7c478bd9Sstevel@tonic-gate } 609*7c478bd9Sstevel@tonic-gate 610*7c478bd9Sstevel@tonic-gate LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life); 611*7c478bd9Sstevel@tonic-gate 612*7c478bd9Sstevel@tonic-gate /* 613*7c478bd9Sstevel@tonic-gate * Update preference with possibly changed interface 614*7c478bd9Sstevel@tonic-gate * metric. 615*7c478bd9Sstevel@tonic-gate */ 616*7c478bd9Sstevel@tonic-gate drp->dr_pref = PREF(drp->dr_recv_pref, ifp); 617*7c478bd9Sstevel@tonic-gate 618*7c478bd9Sstevel@tonic-gate /* 619*7c478bd9Sstevel@tonic-gate * Prefer the current route to prevent thrashing. 620*7c478bd9Sstevel@tonic-gate * Prefer shorter lifetimes to speed the detection of 621*7c478bd9Sstevel@tonic-gate * bad routers. 622*7c478bd9Sstevel@tonic-gate * Avoid sick interfaces. 623*7c478bd9Sstevel@tonic-gate */ 624*7c478bd9Sstevel@tonic-gate if (new_drp == NULL || 625*7c478bd9Sstevel@tonic-gate (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) && 626*7c478bd9Sstevel@tonic-gate (new_pref < drp->dr_pref || 627*7c478bd9Sstevel@tonic-gate (new_pref == drp->dr_pref && (drp == cur_drp || 628*7c478bd9Sstevel@tonic-gate (new_drp != cur_drp && 629*7c478bd9Sstevel@tonic-gate new_drp->dr_life > drp->dr_life))))) || 630*7c478bd9Sstevel@tonic-gate ((new_st & IS_SICK) && 631*7c478bd9Sstevel@tonic-gate !(drp->dr_ifp->int_state & IS_SICK))) { 632*7c478bd9Sstevel@tonic-gate new_drp = drp; 633*7c478bd9Sstevel@tonic-gate new_st = drp->dr_ifp->int_state; 634*7c478bd9Sstevel@tonic-gate new_pref = drp->dr_pref; 635*7c478bd9Sstevel@tonic-gate } 636*7c478bd9Sstevel@tonic-gate } 637*7c478bd9Sstevel@tonic-gate 638*7c478bd9Sstevel@tonic-gate /* 639*7c478bd9Sstevel@tonic-gate * switch to a better RDISC advertiser 640*7c478bd9Sstevel@tonic-gate */ 641*7c478bd9Sstevel@tonic-gate if ((new_drp != cur_drp) || (rt == NULL)) { 642*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 643*7c478bd9Sstevel@tonic-gate 644*7c478bd9Sstevel@tonic-gate /* 645*7c478bd9Sstevel@tonic-gate * Purge the table of all the default routes that were 646*7c478bd9Sstevel@tonic-gate * learnt via RDISC, while keeping an eye the first available 647*7c478bd9Sstevel@tonic-gate * slot for the spare entry of new_drp 648*7c478bd9Sstevel@tonic-gate */ 649*7c478bd9Sstevel@tonic-gate if (rt != NULL) { 650*7c478bd9Sstevel@tonic-gate int i; 651*7c478bd9Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) { 652*7c478bd9Sstevel@tonic-gate rts = &rt->rt_spares[i]; 653*7c478bd9Sstevel@tonic-gate if (rts->rts_gate == 0 && first_rdisc_slot == 0) 654*7c478bd9Sstevel@tonic-gate first_rdisc_slot = i; 655*7c478bd9Sstevel@tonic-gate if (rts->rts_origin == RO_RDISC) { 656*7c478bd9Sstevel@tonic-gate rts_delete(rt, rts); 657*7c478bd9Sstevel@tonic-gate if (first_rdisc_slot == 0) { 658*7c478bd9Sstevel@tonic-gate first_rdisc_slot = i; 659*7c478bd9Sstevel@tonic-gate } 660*7c478bd9Sstevel@tonic-gate } 661*7c478bd9Sstevel@tonic-gate } 662*7c478bd9Sstevel@tonic-gate } 663*7c478bd9Sstevel@tonic-gate 664*7c478bd9Sstevel@tonic-gate /* Stop using RDISC routes if they are all bad */ 665*7c478bd9Sstevel@tonic-gate if (new_drp == NULL) { 666*7c478bd9Sstevel@tonic-gate trace_act("turn off Router Discovery client"); 667*7c478bd9Sstevel@tonic-gate rdisc_ok = _B_FALSE; 668*7c478bd9Sstevel@tonic-gate 669*7c478bd9Sstevel@tonic-gate } else { 670*7c478bd9Sstevel@tonic-gate if (cur_drp == NULL) { 671*7c478bd9Sstevel@tonic-gate trace_act("turn on Router Discovery client" 672*7c478bd9Sstevel@tonic-gate " using %s via %s", 673*7c478bd9Sstevel@tonic-gate naddr_ntoa(new_drp->dr_gate), 674*7c478bd9Sstevel@tonic-gate new_drp->dr_ifp->int_name); 675*7c478bd9Sstevel@tonic-gate rdisc_ok = _B_TRUE; 676*7c478bd9Sstevel@tonic-gate } 677*7c478bd9Sstevel@tonic-gate 678*7c478bd9Sstevel@tonic-gate /* Prepare a spare entry for the new_drp */ 679*7c478bd9Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new)); 680*7c478bd9Sstevel@tonic-gate new.rts_ifp = new_drp->dr_ifp; 681*7c478bd9Sstevel@tonic-gate new.rts_gate = new_drp->dr_gate; 682*7c478bd9Sstevel@tonic-gate new.rts_router = new_drp->dr_gate; 683*7c478bd9Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1; 684*7c478bd9Sstevel@tonic-gate new.rts_time = now.tv_sec; 685*7c478bd9Sstevel@tonic-gate new.rts_origin = RO_RDISC; 686*7c478bd9Sstevel@tonic-gate /* 687*7c478bd9Sstevel@tonic-gate * If there is no existing default route, add it 688*7c478bd9Sstevel@tonic-gate * to rts_spare[0]. 689*7c478bd9Sstevel@tonic-gate */ 690*7c478bd9Sstevel@tonic-gate if (rt == NULL) { 691*7c478bd9Sstevel@tonic-gate rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new); 692*7c478bd9Sstevel@tonic-gate } else { 693*7c478bd9Sstevel@tonic-gate 694*7c478bd9Sstevel@tonic-gate /* 695*7c478bd9Sstevel@tonic-gate * Add the spare entry for the new_drp in 696*7c478bd9Sstevel@tonic-gate * the first available slot 697*7c478bd9Sstevel@tonic-gate */ 698*7c478bd9Sstevel@tonic-gate trace_act("Switching to " 699*7c478bd9Sstevel@tonic-gate "default router with better " 700*7c478bd9Sstevel@tonic-gate "preference %s via %s ", 701*7c478bd9Sstevel@tonic-gate naddr_ntoa(new_drp->dr_gate), 702*7c478bd9Sstevel@tonic-gate new_drp->dr_ifp->int_name); 703*7c478bd9Sstevel@tonic-gate rt->rt_spares[first_rdisc_slot] = new; 704*7c478bd9Sstevel@tonic-gate rt = NULL; /* redo rt_spares */ 705*7c478bd9Sstevel@tonic-gate } 706*7c478bd9Sstevel@tonic-gate } 707*7c478bd9Sstevel@tonic-gate 708*7c478bd9Sstevel@tonic-gate /* 709*7c478bd9Sstevel@tonic-gate * Get ready to redo the entire table. The table should 710*7c478bd9Sstevel@tonic-gate * only include : 711*7c478bd9Sstevel@tonic-gate * a. empty rt_spare slots 712*7c478bd9Sstevel@tonic-gate * b. default routes learnt via RIP 713*7c478bd9Sstevel@tonic-gate * c. default route for the latest best RDISC advertiser 714*7c478bd9Sstevel@tonic-gate * d. default routes of other RDISC advertisers whose 715*7c478bd9Sstevel@tonic-gate * dr_pref == best RDISC advertiser->dr_pref 716*7c478bd9Sstevel@tonic-gate */ 717*7c478bd9Sstevel@tonic-gate cur_drp = new_drp; 718*7c478bd9Sstevel@tonic-gate } 719*7c478bd9Sstevel@tonic-gate 720*7c478bd9Sstevel@tonic-gate /* Redo the entire spare table (without touching RO_RIP entries) */ 721*7c478bd9Sstevel@tonic-gate if (rdisc_ok && rt == NULL) { 722*7c478bd9Sstevel@tonic-gate int i; 723*7c478bd9Sstevel@tonic-gate /* 724*7c478bd9Sstevel@tonic-gate * We've either just turned on router discovery, 725*7c478bd9Sstevel@tonic-gate * or switched to a router with better preference. 726*7c478bd9Sstevel@tonic-gate * Find all other default routers whose 727*7c478bd9Sstevel@tonic-gate * pref == cur_drp->dr_pref and add them as spares 728*7c478bd9Sstevel@tonic-gate */ 729*7c478bd9Sstevel@tonic-gate 730*7c478bd9Sstevel@tonic-gate rt = rtget(RIP_DEFAULT, 0); 731*7c478bd9Sstevel@tonic-gate 732*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) { 733*7c478bd9Sstevel@tonic-gate boolean_t dr_done = _B_FALSE; 734*7c478bd9Sstevel@tonic-gate int slot = -1; 735*7c478bd9Sstevel@tonic-gate 736*7c478bd9Sstevel@tonic-gate if (drp->dr_ts == 0) 737*7c478bd9Sstevel@tonic-gate continue; 738*7c478bd9Sstevel@tonic-gate 739*7c478bd9Sstevel@tonic-gate if (drp->dr_pref != cur_drp->dr_pref && 740*7c478bd9Sstevel@tonic-gate ((drp->dr_flags & DR_CHANGED) == 0)) 741*7c478bd9Sstevel@tonic-gate continue; 742*7c478bd9Sstevel@tonic-gate 743*7c478bd9Sstevel@tonic-gate /* 744*7c478bd9Sstevel@tonic-gate * Either pref matches cur_drp->dr_pref, 745*7c478bd9Sstevel@tonic-gate * or something has changed in this drp. 746*7c478bd9Sstevel@tonic-gate * In the former case, we may need to add 747*7c478bd9Sstevel@tonic-gate * this to rt_spares. In the latter case, 748*7c478bd9Sstevel@tonic-gate * if the pref has changed, need to take it 749*7c478bd9Sstevel@tonic-gate * out of rt_spares and the kernel. 750*7c478bd9Sstevel@tonic-gate * 751*7c478bd9Sstevel@tonic-gate * First, find an empty slot in rt_spares 752*7c478bd9Sstevel@tonic-gate * in case we have to add this drp to kernel. 753*7c478bd9Sstevel@tonic-gate * Also check if it is already there. 754*7c478bd9Sstevel@tonic-gate */ 755*7c478bd9Sstevel@tonic-gate for (i = 0; i < rt->rt_num_spares; i++) { 756*7c478bd9Sstevel@tonic-gate if (rt->rt_spares[i].rts_gate == 0) { 757*7c478bd9Sstevel@tonic-gate if (slot < 0) 758*7c478bd9Sstevel@tonic-gate slot = i; 759*7c478bd9Sstevel@tonic-gate continue; 760*7c478bd9Sstevel@tonic-gate } 761*7c478bd9Sstevel@tonic-gate if ((rt->rt_spares[i].rts_gate == 762*7c478bd9Sstevel@tonic-gate drp->dr_gate) && 763*7c478bd9Sstevel@tonic-gate (rt->rt_spares[i].rts_origin == 764*7c478bd9Sstevel@tonic-gate RO_RDISC)) { 765*7c478bd9Sstevel@tonic-gate /* 766*7c478bd9Sstevel@tonic-gate * a spare entry for this RDISC 767*7c478bd9Sstevel@tonic-gate * advertiser already exists. We need 768*7c478bd9Sstevel@tonic-gate * to check if this entry still belongs 769*7c478bd9Sstevel@tonic-gate * in the table 770*7c478bd9Sstevel@tonic-gate */ 771*7c478bd9Sstevel@tonic-gate dr_done = _B_TRUE; 772*7c478bd9Sstevel@tonic-gate break; 773*7c478bd9Sstevel@tonic-gate } 774*7c478bd9Sstevel@tonic-gate } 775*7c478bd9Sstevel@tonic-gate 776*7c478bd9Sstevel@tonic-gate drp->dr_flags &= ~DR_CHANGED; 777*7c478bd9Sstevel@tonic-gate 778*7c478bd9Sstevel@tonic-gate if (drp->dr_pref != cur_drp->dr_pref) { 779*7c478bd9Sstevel@tonic-gate if (dr_done) { 780*7c478bd9Sstevel@tonic-gate /* 781*7c478bd9Sstevel@tonic-gate * The rt_spare of this RDISC advertiser 782*7c478bd9Sstevel@tonic-gate * needs to be removed as it no longer 783*7c478bd9Sstevel@tonic-gate * belongs in the table because its 784*7c478bd9Sstevel@tonic-gate * dr_pref is different than the latest 785*7c478bd9Sstevel@tonic-gate * RDISC advertiser's->dr_pref 786*7c478bd9Sstevel@tonic-gate */ 787*7c478bd9Sstevel@tonic-gate rts_delete(rt, &rt->rt_spares[i]); 788*7c478bd9Sstevel@tonic-gate } 789*7c478bd9Sstevel@tonic-gate continue; 790*7c478bd9Sstevel@tonic-gate } 791*7c478bd9Sstevel@tonic-gate 792*7c478bd9Sstevel@tonic-gate if (slot < 0) { 793*7c478bd9Sstevel@tonic-gate ptrsize = (rt->rt_num_spares + SPARE_INC) * 794*7c478bd9Sstevel@tonic-gate sizeof (struct rt_spare); 795*7c478bd9Sstevel@tonic-gate ptr = realloc(rt->rt_spares, ptrsize); 796*7c478bd9Sstevel@tonic-gate if (ptr != NULL) { 797*7c478bd9Sstevel@tonic-gate struct rt_spare *tmprts; 798*7c478bd9Sstevel@tonic-gate 799*7c478bd9Sstevel@tonic-gate rt->rt_spares = ptr; 800*7c478bd9Sstevel@tonic-gate slot = rt->rt_num_spares; 801*7c478bd9Sstevel@tonic-gate rts = &rt->rt_spares[rt->rt_num_spares]; 802*7c478bd9Sstevel@tonic-gate (void) memset(rts, 0, (SPARE_INC * 803*7c478bd9Sstevel@tonic-gate sizeof (struct rt_spare))); 804*7c478bd9Sstevel@tonic-gate rt->rt_num_spares += SPARE_INC; 805*7c478bd9Sstevel@tonic-gate for (tmprts = rts, i = SPARE_INC; 806*7c478bd9Sstevel@tonic-gate i != 0; i--, tmprts++) 807*7c478bd9Sstevel@tonic-gate tmprts->rts_metric = 808*7c478bd9Sstevel@tonic-gate HOPCNT_INFINITY; 809*7c478bd9Sstevel@tonic-gate } 810*7c478bd9Sstevel@tonic-gate } 811*7c478bd9Sstevel@tonic-gate 812*7c478bd9Sstevel@tonic-gate if (slot >= 0 && (dr_done != _B_TRUE)) { 813*7c478bd9Sstevel@tonic-gate (void) memset(&new, 0, sizeof (new)); 814*7c478bd9Sstevel@tonic-gate new.rts_ifp = drp->dr_ifp; 815*7c478bd9Sstevel@tonic-gate new.rts_gate = drp->dr_gate; 816*7c478bd9Sstevel@tonic-gate new.rts_router = drp->dr_gate; 817*7c478bd9Sstevel@tonic-gate new.rts_metric = HOPCNT_INFINITY-1; 818*7c478bd9Sstevel@tonic-gate new.rts_time = now.tv_sec; 819*7c478bd9Sstevel@tonic-gate new.rts_origin = RO_RDISC; 820*7c478bd9Sstevel@tonic-gate rt->rt_spares[slot] = new; 821*7c478bd9Sstevel@tonic-gate trace_act("spare default %s via %s", 822*7c478bd9Sstevel@tonic-gate naddr_ntoa(drp->dr_gate), 823*7c478bd9Sstevel@tonic-gate drp->dr_ifp->int_name); 824*7c478bd9Sstevel@tonic-gate } 825*7c478bd9Sstevel@tonic-gate } 826*7c478bd9Sstevel@tonic-gate } 827*7c478bd9Sstevel@tonic-gate 828*7c478bd9Sstevel@tonic-gate /* turn RIP on or off */ 829*7c478bd9Sstevel@tonic-gate if (!rdisc_ok || rip_interfaces > 1) { 830*7c478bd9Sstevel@tonic-gate rip_on(0); 831*7c478bd9Sstevel@tonic-gate } else { 832*7c478bd9Sstevel@tonic-gate rip_off(); 833*7c478bd9Sstevel@tonic-gate } 834*7c478bd9Sstevel@tonic-gate } 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate 837*7c478bd9Sstevel@tonic-gate /* Handle a single address in an advertisement */ 838*7c478bd9Sstevel@tonic-gate static void 839*7c478bd9Sstevel@tonic-gate parse_ad(uint32_t from, 840*7c478bd9Sstevel@tonic-gate in_addr_t gate, 841*7c478bd9Sstevel@tonic-gate uint32_t pref, /* signed and in network order */ 842*7c478bd9Sstevel@tonic-gate ushort_t life, /* in host byte order */ 843*7c478bd9Sstevel@tonic-gate struct interface *ifp) 844*7c478bd9Sstevel@tonic-gate { 845*7c478bd9Sstevel@tonic-gate static struct msg_limit bad_gate; 846*7c478bd9Sstevel@tonic-gate struct dr *drp, *new_drp; 847*7c478bd9Sstevel@tonic-gate void *ptr; 848*7c478bd9Sstevel@tonic-gate size_t ptrsize; 849*7c478bd9Sstevel@tonic-gate 850*7c478bd9Sstevel@tonic-gate if (gate == RIP_DEFAULT || !check_dst(gate)) { 851*7c478bd9Sstevel@tonic-gate msglim(&bad_gate, from, "router %s advertising bad gateway %s", 852*7c478bd9Sstevel@tonic-gate naddr_ntoa(from), naddr_ntoa(gate)); 853*7c478bd9Sstevel@tonic-gate return; 854*7c478bd9Sstevel@tonic-gate } 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate /* 857*7c478bd9Sstevel@tonic-gate * ignore pointers to ourself and routes via unreachable networks 858*7c478bd9Sstevel@tonic-gate */ 859*7c478bd9Sstevel@tonic-gate if (ifwithaddr(gate, _B_TRUE, _B_FALSE) != 0) { 860*7c478bd9Sstevel@tonic-gate trace_pkt(" discard Router Discovery Ad pointing at us"); 861*7c478bd9Sstevel@tonic-gate return; 862*7c478bd9Sstevel@tonic-gate } 863*7c478bd9Sstevel@tonic-gate if (!on_net(gate, ifp->int_net, ifp->int_mask)) { 864*7c478bd9Sstevel@tonic-gate trace_pkt(" discard Router Discovery Ad" 865*7c478bd9Sstevel@tonic-gate " toward unreachable net"); 866*7c478bd9Sstevel@tonic-gate return; 867*7c478bd9Sstevel@tonic-gate } 868*7c478bd9Sstevel@tonic-gate /* 869*7c478bd9Sstevel@tonic-gate * Convert preference to an unsigned value 870*7c478bd9Sstevel@tonic-gate * and later bias it by the metric of the interface. 871*7c478bd9Sstevel@tonic-gate */ 872*7c478bd9Sstevel@tonic-gate pref = UNSIGN_PREF(ntohl(pref)); 873*7c478bd9Sstevel@tonic-gate 874*7c478bd9Sstevel@tonic-gate if (pref == DEF_PREFERENCELEVEL || life < MIN_MAXADVERTISEINTERVAL) { 875*7c478bd9Sstevel@tonic-gate pref = DEF_PREFERENCELEVEL; 876*7c478bd9Sstevel@tonic-gate life = 0; 877*7c478bd9Sstevel@tonic-gate } 878*7c478bd9Sstevel@tonic-gate 879*7c478bd9Sstevel@tonic-gate for (new_drp = NULL, drp = drs; drp < &drs[max_ads]; drp++) { 880*7c478bd9Sstevel@tonic-gate /* accept new info for a familiar entry */ 881*7c478bd9Sstevel@tonic-gate if ((drp->dr_gate == gate) && (drp->dr_ifp == ifp)) { 882*7c478bd9Sstevel@tonic-gate new_drp = drp; 883*7c478bd9Sstevel@tonic-gate drp->dr_flags |= DR_CHANGED; 884*7c478bd9Sstevel@tonic-gate break; 885*7c478bd9Sstevel@tonic-gate } 886*7c478bd9Sstevel@tonic-gate 887*7c478bd9Sstevel@tonic-gate if (life == 0) 888*7c478bd9Sstevel@tonic-gate continue; /* do not worry about dead ads */ 889*7c478bd9Sstevel@tonic-gate 890*7c478bd9Sstevel@tonic-gate if (drp->dr_ts == 0) { 891*7c478bd9Sstevel@tonic-gate new_drp = drp; /* use unused entry */ 892*7c478bd9Sstevel@tonic-gate 893*7c478bd9Sstevel@tonic-gate } else if (new_drp == NULL) { 894*7c478bd9Sstevel@tonic-gate /* look for an entry worse than the new one to reuse. */ 895*7c478bd9Sstevel@tonic-gate if ((!(ifp->int_state & IS_SICK) && 896*7c478bd9Sstevel@tonic-gate (drp->dr_ifp->int_state & IS_SICK)) || 897*7c478bd9Sstevel@tonic-gate (pref > drp->dr_pref && 898*7c478bd9Sstevel@tonic-gate !((ifp->int_state ^ drp->dr_ifp->int_state) & 899*7c478bd9Sstevel@tonic-gate IS_SICK))) 900*7c478bd9Sstevel@tonic-gate new_drp = drp; 901*7c478bd9Sstevel@tonic-gate 902*7c478bd9Sstevel@tonic-gate } else if (new_drp->dr_ts != 0) { 903*7c478bd9Sstevel@tonic-gate /* look for the least valuable entry to reuse */ 904*7c478bd9Sstevel@tonic-gate if ((!(new_drp->dr_ifp->int_state & IS_SICK) && 905*7c478bd9Sstevel@tonic-gate (drp->dr_ifp->int_state & IS_SICK)) || 906*7c478bd9Sstevel@tonic-gate (new_drp->dr_pref > drp->dr_pref && 907*7c478bd9Sstevel@tonic-gate !((new_drp->dr_ifp->int_state ^ 908*7c478bd9Sstevel@tonic-gate drp->dr_ifp->int_state) & IS_SICK))) 909*7c478bd9Sstevel@tonic-gate new_drp = drp; 910*7c478bd9Sstevel@tonic-gate } 911*7c478bd9Sstevel@tonic-gate } 912*7c478bd9Sstevel@tonic-gate 913*7c478bd9Sstevel@tonic-gate /* if all of the current entries are better, add more drs[] */ 914*7c478bd9Sstevel@tonic-gate if (new_drp == NULL) { 915*7c478bd9Sstevel@tonic-gate ptrsize = (max_ads + MAX_ADS) * sizeof (struct dr); 916*7c478bd9Sstevel@tonic-gate ptr = realloc(drs, ptrsize); 917*7c478bd9Sstevel@tonic-gate if (ptr == NULL) 918*7c478bd9Sstevel@tonic-gate return; 919*7c478bd9Sstevel@tonic-gate drs = ptr; 920*7c478bd9Sstevel@tonic-gate (void) memset(&drs[max_ads], 0, MAX_ADS * sizeof (struct dr)); 921*7c478bd9Sstevel@tonic-gate new_drp = &drs[max_ads]; 922*7c478bd9Sstevel@tonic-gate max_ads += MAX_ADS; 923*7c478bd9Sstevel@tonic-gate } 924*7c478bd9Sstevel@tonic-gate 925*7c478bd9Sstevel@tonic-gate /* 926*7c478bd9Sstevel@tonic-gate * Pointer copy is safe here because if_del 927*7c478bd9Sstevel@tonic-gate * calls if_bad_rdisc first, so a non-NULL df_ifp 928*7c478bd9Sstevel@tonic-gate * is always a valid pointer. 929*7c478bd9Sstevel@tonic-gate */ 930*7c478bd9Sstevel@tonic-gate new_drp->dr_ifp = ifp; 931*7c478bd9Sstevel@tonic-gate new_drp->dr_gate = gate; 932*7c478bd9Sstevel@tonic-gate new_drp->dr_ts = now.tv_sec; 933*7c478bd9Sstevel@tonic-gate new_drp->dr_life = life; 934*7c478bd9Sstevel@tonic-gate new_drp->dr_recv_pref = pref; 935*7c478bd9Sstevel@tonic-gate /* bias functional preference by metric of the interface */ 936*7c478bd9Sstevel@tonic-gate new_drp->dr_pref = PREF(pref, ifp); 937*7c478bd9Sstevel@tonic-gate 938*7c478bd9Sstevel@tonic-gate /* after hearing a good advertisement, stop asking */ 939*7c478bd9Sstevel@tonic-gate if (!(ifp->int_state & IS_SICK)) 940*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt = MAX_SOLICITATIONS; 941*7c478bd9Sstevel@tonic-gate } 942*7c478bd9Sstevel@tonic-gate 943*7c478bd9Sstevel@tonic-gate 944*7c478bd9Sstevel@tonic-gate /* 945*7c478bd9Sstevel@tonic-gate * Compute the IP checksum. This assumes the packet is less than 32K long. 946*7c478bd9Sstevel@tonic-gate */ 947*7c478bd9Sstevel@tonic-gate static uint16_t 948*7c478bd9Sstevel@tonic-gate in_cksum(uint16_t *p, uint_t len) 949*7c478bd9Sstevel@tonic-gate { 950*7c478bd9Sstevel@tonic-gate uint32_t sum = 0; 951*7c478bd9Sstevel@tonic-gate int nwords = len >> 1; 952*7c478bd9Sstevel@tonic-gate 953*7c478bd9Sstevel@tonic-gate while (nwords-- != 0) 954*7c478bd9Sstevel@tonic-gate sum += *p++; 955*7c478bd9Sstevel@tonic-gate 956*7c478bd9Sstevel@tonic-gate if (len & 1) 957*7c478bd9Sstevel@tonic-gate sum += *(uchar_t *)p; 958*7c478bd9Sstevel@tonic-gate 959*7c478bd9Sstevel@tonic-gate /* end-around-carry */ 960*7c478bd9Sstevel@tonic-gate sum = (sum >> 16) + (sum & 0xffff); 961*7c478bd9Sstevel@tonic-gate sum += (sum >> 16); 962*7c478bd9Sstevel@tonic-gate return (~sum); 963*7c478bd9Sstevel@tonic-gate } 964*7c478bd9Sstevel@tonic-gate 965*7c478bd9Sstevel@tonic-gate 966*7c478bd9Sstevel@tonic-gate /* Send a router discovery advertisement or solicitation ICMP packet. */ 967*7c478bd9Sstevel@tonic-gate static void 968*7c478bd9Sstevel@tonic-gate send_rdisc(union ad_u *p, 969*7c478bd9Sstevel@tonic-gate uint_t p_size, 970*7c478bd9Sstevel@tonic-gate struct interface *ifp, 971*7c478bd9Sstevel@tonic-gate in_addr_t dst, /* 0 or unicast destination */ 972*7c478bd9Sstevel@tonic-gate dstaddr_t type) 973*7c478bd9Sstevel@tonic-gate { 974*7c478bd9Sstevel@tonic-gate struct sockaddr_in sin; 975*7c478bd9Sstevel@tonic-gate int flags = 0; 976*7c478bd9Sstevel@tonic-gate const char *msg; 977*7c478bd9Sstevel@tonic-gate int ifindex; 978*7c478bd9Sstevel@tonic-gate struct in_addr addr; 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate /* 981*7c478bd9Sstevel@tonic-gate * Don't send Rdisc packets on duplicate interfaces, we 982*7c478bd9Sstevel@tonic-gate * don't want to generate duplicate packets. 983*7c478bd9Sstevel@tonic-gate */ 984*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_DUP) 985*7c478bd9Sstevel@tonic-gate return; 986*7c478bd9Sstevel@tonic-gate 987*7c478bd9Sstevel@tonic-gate (void) memset(&sin, 0, sizeof (sin)); 988*7c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = dst; 989*7c478bd9Sstevel@tonic-gate sin.sin_family = AF_INET; 990*7c478bd9Sstevel@tonic-gate 991*7c478bd9Sstevel@tonic-gate switch (type) { 992*7c478bd9Sstevel@tonic-gate case unicast: /* unicast */ 993*7c478bd9Sstevel@tonic-gate default: 994*7c478bd9Sstevel@tonic-gate flags = MSG_DONTROUTE; 995*7c478bd9Sstevel@tonic-gate msg = "Send"; 996*7c478bd9Sstevel@tonic-gate break; 997*7c478bd9Sstevel@tonic-gate 998*7c478bd9Sstevel@tonic-gate case bcast: /* broadcast */ 999*7c478bd9Sstevel@tonic-gate if (ifp->int_if_flags & IFF_POINTOPOINT) { 1000*7c478bd9Sstevel@tonic-gate msg = "Send pt-to-pt"; 1001*7c478bd9Sstevel@tonic-gate if (ifp->int_dstaddr == 0) 1002*7c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = htonl(INADDR_BROADCAST); 1003*7c478bd9Sstevel@tonic-gate else 1004*7c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = ifp->int_dstaddr; 1005*7c478bd9Sstevel@tonic-gate } else { 1006*7c478bd9Sstevel@tonic-gate msg = "Send broadcast"; 1007*7c478bd9Sstevel@tonic-gate sin.sin_addr.s_addr = ifp->int_brdaddr; 1008*7c478bd9Sstevel@tonic-gate } 1009*7c478bd9Sstevel@tonic-gate break; 1010*7c478bd9Sstevel@tonic-gate 1011*7c478bd9Sstevel@tonic-gate case mcast: /* multicast */ 1012*7c478bd9Sstevel@tonic-gate msg = "Send multicast"; 1013*7c478bd9Sstevel@tonic-gate break; 1014*7c478bd9Sstevel@tonic-gate } 1015*7c478bd9Sstevel@tonic-gate 1016*7c478bd9Sstevel@tonic-gate if (rdisc_sock < 0) 1017*7c478bd9Sstevel@tonic-gate get_rdisc_sock(); 1018*7c478bd9Sstevel@tonic-gate 1019*7c478bd9Sstevel@tonic-gate if (rdisc_sock_interface != ifp) { 1020*7c478bd9Sstevel@tonic-gate /* select the right interface. */ 1021*7c478bd9Sstevel@tonic-gate ifindex = (type != mcast && ifp->int_phys != NULL) ? 1022*7c478bd9Sstevel@tonic-gate ifp->int_phys->phyi_index : 0; 1023*7c478bd9Sstevel@tonic-gate if (setsockopt(rdisc_sock, IPPROTO_IP, IP_XMIT_IF, &ifindex, 1024*7c478bd9Sstevel@tonic-gate sizeof (ifindex)) == -1) { 1025*7c478bd9Sstevel@tonic-gate LOGERR("setsockopt(rdisc_sock, IP_XMIT_IF)"); 1026*7c478bd9Sstevel@tonic-gate return; 1027*7c478bd9Sstevel@tonic-gate } 1028*7c478bd9Sstevel@tonic-gate /* 1029*7c478bd9Sstevel@tonic-gate * For multicast, we have to choose the source 1030*7c478bd9Sstevel@tonic-gate * address. This is either the local address 1031*7c478bd9Sstevel@tonic-gate * (non-point-to-point) or the remote address. 1032*7c478bd9Sstevel@tonic-gate */ 1033*7c478bd9Sstevel@tonic-gate addr.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ? 1034*7c478bd9Sstevel@tonic-gate ifp->int_dstaddr : ifp->int_addr; 1035*7c478bd9Sstevel@tonic-gate if (type == mcast && 1036*7c478bd9Sstevel@tonic-gate setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr, 1037*7c478bd9Sstevel@tonic-gate sizeof (addr)) == -1) { 1038*7c478bd9Sstevel@tonic-gate LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)"); 1039*7c478bd9Sstevel@tonic-gate return; 1040*7c478bd9Sstevel@tonic-gate } 1041*7c478bd9Sstevel@tonic-gate rdisc_sock_interface = ifp; 1042*7c478bd9Sstevel@tonic-gate } 1043*7c478bd9Sstevel@tonic-gate 1044*7c478bd9Sstevel@tonic-gate trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size); 1045*7c478bd9Sstevel@tonic-gate 1046*7c478bd9Sstevel@tonic-gate if (0 > sendto(rdisc_sock, p, p_size, flags, 1047*7c478bd9Sstevel@tonic-gate (struct sockaddr *)&sin, sizeof (sin))) { 1048*7c478bd9Sstevel@tonic-gate if (!(ifp->int_state & IS_BROKE)) 1049*7c478bd9Sstevel@tonic-gate writelog(LOG_WARNING, "sendto(%s%s%s): %s", 1050*7c478bd9Sstevel@tonic-gate ifp->int_name, ", ", 1051*7c478bd9Sstevel@tonic-gate inet_ntoa(sin.sin_addr), 1052*7c478bd9Sstevel@tonic-gate rip_strerror(errno)); 1053*7c478bd9Sstevel@tonic-gate if (ifp != NULL) 1054*7c478bd9Sstevel@tonic-gate if_sick(ifp, _B_FALSE); 1055*7c478bd9Sstevel@tonic-gate } 1056*7c478bd9Sstevel@tonic-gate } 1057*7c478bd9Sstevel@tonic-gate 1058*7c478bd9Sstevel@tonic-gate 1059*7c478bd9Sstevel@tonic-gate /* Send an advertisement */ 1060*7c478bd9Sstevel@tonic-gate static void 1061*7c478bd9Sstevel@tonic-gate send_adv(struct interface *ifp, 1062*7c478bd9Sstevel@tonic-gate in_addr_t dst, 1063*7c478bd9Sstevel@tonic-gate dstaddr_t type) 1064*7c478bd9Sstevel@tonic-gate { 1065*7c478bd9Sstevel@tonic-gate union ad_u u; 1066*7c478bd9Sstevel@tonic-gate 1067*7c478bd9Sstevel@tonic-gate if ((ifp->int_state & (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC)) == 1068*7c478bd9Sstevel@tonic-gate IS_SUPPRESS_RDISC) 1069*7c478bd9Sstevel@tonic-gate return; 1070*7c478bd9Sstevel@tonic-gate 1071*7c478bd9Sstevel@tonic-gate (void) memset(&u, 0, sizeof (u.ad)); 1072*7c478bd9Sstevel@tonic-gate 1073*7c478bd9Sstevel@tonic-gate u.ad.icmp_type = ICMP_ROUTERADVERT; 1074*7c478bd9Sstevel@tonic-gate u.ad.icmp_code = ICMP_ROUTERADVERT_COMMON; 1075*7c478bd9Sstevel@tonic-gate u.ad.icmp_ad_num = 1; 1076*7c478bd9Sstevel@tonic-gate u.ad.icmp_ad_asize = sizeof (u.ad.icmp_ad_info[0])/4; 1077*7c478bd9Sstevel@tonic-gate 1078*7c478bd9Sstevel@tonic-gate u.ad.icmp_ad_life = (stopint || !should_supply(ifp) || 1079*7c478bd9Sstevel@tonic-gate (ifp->int_state & IS_SUPPRESS_RDISC)) ? 0 : 1080*7c478bd9Sstevel@tonic-gate htons(ifp->int_rdisc_int*3); 1081*7c478bd9Sstevel@tonic-gate 1082*7c478bd9Sstevel@tonic-gate /* Send the configured preference as a network byte order value */ 1083*7c478bd9Sstevel@tonic-gate u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(ifp->int_rdisc_pref); 1084*7c478bd9Sstevel@tonic-gate 1085*7c478bd9Sstevel@tonic-gate u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr; 1086*7c478bd9Sstevel@tonic-gate 1087*7c478bd9Sstevel@tonic-gate u.ad.icmp_cksum = in_cksum((uint16_t *)&u.ad, sizeof (u.ad)); 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate send_rdisc(&u, sizeof (u.ad), ifp, dst, type); 1090*7c478bd9Sstevel@tonic-gate 1091*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_SUPPRESS_RDISC) 1092*7c478bd9Sstevel@tonic-gate ifp->int_state &= ~IS_FLUSH_RDISC; 1093*7c478bd9Sstevel@tonic-gate } 1094*7c478bd9Sstevel@tonic-gate 1095*7c478bd9Sstevel@tonic-gate 1096*7c478bd9Sstevel@tonic-gate /* Advertise as a default router by way of router discovery. */ 1097*7c478bd9Sstevel@tonic-gate void 1098*7c478bd9Sstevel@tonic-gate rdisc_adv(boolean_t forceadv) 1099*7c478bd9Sstevel@tonic-gate { 1100*7c478bd9Sstevel@tonic-gate struct interface *ifp; 1101*7c478bd9Sstevel@tonic-gate 1102*7c478bd9Sstevel@tonic-gate if (!forceadv && !should_supply(NULL)) 1103*7c478bd9Sstevel@tonic-gate return; 1104*7c478bd9Sstevel@tonic-gate 1105*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = now.tv_sec + NEVER; 1106*7c478bd9Sstevel@tonic-gate 1107*7c478bd9Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) { 1108*7c478bd9Sstevel@tonic-gate if ((ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)) || 1109*7c478bd9Sstevel@tonic-gate (!forceadv && !IS_IFF_ROUTING(ifp->int_if_flags))) 1110*7c478bd9Sstevel@tonic-gate continue; 1111*7c478bd9Sstevel@tonic-gate 1112*7c478bd9Sstevel@tonic-gate /* skip interfaces we shouldn't use */ 1113*7c478bd9Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags)) 1114*7c478bd9Sstevel@tonic-gate continue; 1115*7c478bd9Sstevel@tonic-gate 1116*7c478bd9Sstevel@tonic-gate if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */) || 1117*7c478bd9Sstevel@tonic-gate stopint != 0 || forceadv) { 1118*7c478bd9Sstevel@tonic-gate send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP), 1119*7c478bd9Sstevel@tonic-gate (ifp->int_state & IS_BCAST_RDISC) ? 1 : 2); 1120*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt++; 1121*7c478bd9Sstevel@tonic-gate 1122*7c478bd9Sstevel@tonic-gate intvl_random(&ifp->int_rdisc_timer, 1123*7c478bd9Sstevel@tonic-gate (ifp->int_rdisc_int*3)/4, ifp->int_rdisc_int); 1124*7c478bd9Sstevel@tonic-gate if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS && 1125*7c478bd9Sstevel@tonic-gate (ifp->int_rdisc_timer.tv_sec > 1126*7c478bd9Sstevel@tonic-gate MAX_INITIAL_ADVERT_INTERVAL)) { 1127*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = 1128*7c478bd9Sstevel@tonic-gate MAX_INITIAL_ADVERT_INTERVAL; 1129*7c478bd9Sstevel@tonic-gate } 1130*7c478bd9Sstevel@tonic-gate timevaladd(&ifp->int_rdisc_timer, &now); 1131*7c478bd9Sstevel@tonic-gate } 1132*7c478bd9Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, 1133*7c478bd9Sstevel@tonic-gate > /* cstyle */)) 1134*7c478bd9Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer; 1135*7c478bd9Sstevel@tonic-gate } 1136*7c478bd9Sstevel@tonic-gate } 1137*7c478bd9Sstevel@tonic-gate 1138*7c478bd9Sstevel@tonic-gate 1139*7c478bd9Sstevel@tonic-gate /* Solicit for Router Discovery */ 1140*7c478bd9Sstevel@tonic-gate void 1141*7c478bd9Sstevel@tonic-gate rdisc_sol(void) 1142*7c478bd9Sstevel@tonic-gate { 1143*7c478bd9Sstevel@tonic-gate struct interface *ifp; 1144*7c478bd9Sstevel@tonic-gate union ad_u u; 1145*7c478bd9Sstevel@tonic-gate 1146*7c478bd9Sstevel@tonic-gate if (should_supply(NULL)) 1147*7c478bd9Sstevel@tonic-gate return; 1148*7c478bd9Sstevel@tonic-gate 1149*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = now.tv_sec + NEVER; 1150*7c478bd9Sstevel@tonic-gate 1151*7c478bd9Sstevel@tonic-gate for (ifp = ifnet; ifp; ifp = ifp->int_next) { 1152*7c478bd9Sstevel@tonic-gate if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) || 1153*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 1154*7c478bd9Sstevel@tonic-gate continue; 1155*7c478bd9Sstevel@tonic-gate 1156*7c478bd9Sstevel@tonic-gate /* skip interfaces we shouldn't use */ 1157*7c478bd9Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags)) 1158*7c478bd9Sstevel@tonic-gate continue; 1159*7c478bd9Sstevel@tonic-gate 1160*7c478bd9Sstevel@tonic-gate if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */)) { 1161*7c478bd9Sstevel@tonic-gate (void) memset(&u, 0, sizeof (u.so)); 1162*7c478bd9Sstevel@tonic-gate u.so.icmp_type = ICMP_ROUTERSOLICIT; 1163*7c478bd9Sstevel@tonic-gate u.so.icmp_cksum = in_cksum((uint16_t *)&u.so, 1164*7c478bd9Sstevel@tonic-gate sizeof (u.so)); 1165*7c478bd9Sstevel@tonic-gate send_rdisc(&u, sizeof (u.so), ifp, 1166*7c478bd9Sstevel@tonic-gate htonl(INADDR_ALLRTRS_GROUP), 1167*7c478bd9Sstevel@tonic-gate ((ifp->int_state&IS_BCAST_RDISC) ? bcast : mcast)); 1168*7c478bd9Sstevel@tonic-gate 1169*7c478bd9Sstevel@tonic-gate if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) 1170*7c478bd9Sstevel@tonic-gate continue; 1171*7c478bd9Sstevel@tonic-gate 1172*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL; 1173*7c478bd9Sstevel@tonic-gate ifp->int_rdisc_timer.tv_usec = 0; 1174*7c478bd9Sstevel@tonic-gate timevaladd(&ifp->int_rdisc_timer, &now); 1175*7c478bd9Sstevel@tonic-gate } 1176*7c478bd9Sstevel@tonic-gate 1177*7c478bd9Sstevel@tonic-gate if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, 1178*7c478bd9Sstevel@tonic-gate > /* cstyle */)) 1179*7c478bd9Sstevel@tonic-gate rdisc_timer = ifp->int_rdisc_timer; 1180*7c478bd9Sstevel@tonic-gate } 1181*7c478bd9Sstevel@tonic-gate } 1182*7c478bd9Sstevel@tonic-gate 1183*7c478bd9Sstevel@tonic-gate 1184*7c478bd9Sstevel@tonic-gate /* 1185*7c478bd9Sstevel@tonic-gate * check the IP header of a possible Router Discovery ICMP packet 1186*7c478bd9Sstevel@tonic-gate * Returns 0 if bad 1187*7c478bd9Sstevel@tonic-gate */ 1188*7c478bd9Sstevel@tonic-gate static struct interface * 1189*7c478bd9Sstevel@tonic-gate ck_icmp(const char *act, 1190*7c478bd9Sstevel@tonic-gate in_addr_t from, 1191*7c478bd9Sstevel@tonic-gate struct interface *ifp, 1192*7c478bd9Sstevel@tonic-gate in_addr_t to, 1193*7c478bd9Sstevel@tonic-gate union ad_u *p, 1194*7c478bd9Sstevel@tonic-gate uint_t len) 1195*7c478bd9Sstevel@tonic-gate { 1196*7c478bd9Sstevel@tonic-gate const char *type; 1197*7c478bd9Sstevel@tonic-gate 1198*7c478bd9Sstevel@tonic-gate 1199*7c478bd9Sstevel@tonic-gate if (p->icmp.icmp_type == ICMP_ROUTERADVERT) { 1200*7c478bd9Sstevel@tonic-gate type = "advertisement"; 1201*7c478bd9Sstevel@tonic-gate if (p->icmp.icmp_code == ICMP_ROUTERADVERT_NOCOMMON) 1202*7c478bd9Sstevel@tonic-gate return (NULL); /* Mobile IP */ 1203*7c478bd9Sstevel@tonic-gate } else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) { 1204*7c478bd9Sstevel@tonic-gate type = "solicitation"; 1205*7c478bd9Sstevel@tonic-gate } else { 1206*7c478bd9Sstevel@tonic-gate return (NULL); 1207*7c478bd9Sstevel@tonic-gate } 1208*7c478bd9Sstevel@tonic-gate 1209*7c478bd9Sstevel@tonic-gate if (p->icmp.icmp_code != ICMP_ROUTERADVERT_COMMON) { 1210*7c478bd9Sstevel@tonic-gate trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s", 1211*7c478bd9Sstevel@tonic-gate type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to)); 1212*7c478bd9Sstevel@tonic-gate return (NULL); 1213*7c478bd9Sstevel@tonic-gate } 1214*7c478bd9Sstevel@tonic-gate 1215*7c478bd9Sstevel@tonic-gate trace_rdisc(act, from, to, ifp, p, len); 1216*7c478bd9Sstevel@tonic-gate 1217*7c478bd9Sstevel@tonic-gate if (ifp == NULL) 1218*7c478bd9Sstevel@tonic-gate trace_pkt("unknown interface for router-discovery %s from %s " 1219*7c478bd9Sstevel@tonic-gate "to %s", type, naddr_ntoa(from), naddr_ntoa(to)); 1220*7c478bd9Sstevel@tonic-gate 1221*7c478bd9Sstevel@tonic-gate return (ifp); 1222*7c478bd9Sstevel@tonic-gate } 1223*7c478bd9Sstevel@tonic-gate 1224*7c478bd9Sstevel@tonic-gate 1225*7c478bd9Sstevel@tonic-gate /* Read packets from the router discovery socket */ 1226*7c478bd9Sstevel@tonic-gate void 1227*7c478bd9Sstevel@tonic-gate read_d(void) 1228*7c478bd9Sstevel@tonic-gate { 1229*7c478bd9Sstevel@tonic-gate #define PKTLEN 512 1230*7c478bd9Sstevel@tonic-gate static struct msg_limit bad_asize, bad_len; 1231*7c478bd9Sstevel@tonic-gate struct sockaddr_in from; 1232*7c478bd9Sstevel@tonic-gate int n, cc, hlen; 1233*7c478bd9Sstevel@tonic-gate struct { 1234*7c478bd9Sstevel@tonic-gate union { 1235*7c478bd9Sstevel@tonic-gate struct ip ip; 1236*7c478bd9Sstevel@tonic-gate uint16_t s[PKTLEN/sizeof (uint16_t)]; 1237*7c478bd9Sstevel@tonic-gate uint8_t b[PKTLEN/sizeof (uint8_t)]; 1238*7c478bd9Sstevel@tonic-gate } pkt; 1239*7c478bd9Sstevel@tonic-gate } buf; 1240*7c478bd9Sstevel@tonic-gate union ad_u *p; 1241*7c478bd9Sstevel@tonic-gate n_long *wp; 1242*7c478bd9Sstevel@tonic-gate struct interface *ifp; 1243*7c478bd9Sstevel@tonic-gate boolean_t needsort = _B_FALSE; 1244*7c478bd9Sstevel@tonic-gate struct msghdr msg; 1245*7c478bd9Sstevel@tonic-gate struct iovec iov; 1246*7c478bd9Sstevel@tonic-gate uint8_t ancillary_data[CONTROL_BUFSIZE]; 1247*7c478bd9Sstevel@tonic-gate 1248*7c478bd9Sstevel@tonic-gate iov.iov_base = &buf; 1249*7c478bd9Sstevel@tonic-gate iov.iov_len = sizeof (buf); 1250*7c478bd9Sstevel@tonic-gate msg.msg_iov = &iov; 1251*7c478bd9Sstevel@tonic-gate msg.msg_iovlen = 1; 1252*7c478bd9Sstevel@tonic-gate msg.msg_name = &from; 1253*7c478bd9Sstevel@tonic-gate msg.msg_control = &ancillary_data; 1254*7c478bd9Sstevel@tonic-gate 1255*7c478bd9Sstevel@tonic-gate for (;;) { 1256*7c478bd9Sstevel@tonic-gate msg.msg_namelen = sizeof (from); 1257*7c478bd9Sstevel@tonic-gate msg.msg_controllen = sizeof (ancillary_data); 1258*7c478bd9Sstevel@tonic-gate cc = recvmsg(rdisc_sock, &msg, 0); 1259*7c478bd9Sstevel@tonic-gate if (cc <= 0) { 1260*7c478bd9Sstevel@tonic-gate if (cc < 0 && errno != EWOULDBLOCK) 1261*7c478bd9Sstevel@tonic-gate LOGERR("recvmsg(rdisc_sock)"); 1262*7c478bd9Sstevel@tonic-gate break; 1263*7c478bd9Sstevel@tonic-gate } 1264*7c478bd9Sstevel@tonic-gate 1265*7c478bd9Sstevel@tonic-gate hlen = buf.pkt.ip.ip_hl << 2; 1266*7c478bd9Sstevel@tonic-gate if (cc < hlen + ICMP_MINLEN) 1267*7c478bd9Sstevel@tonic-gate continue; 1268*7c478bd9Sstevel@tonic-gate /* LINTED [alignment will be lw aligned] */ 1269*7c478bd9Sstevel@tonic-gate p = (union ad_u *)&buf.pkt.b[hlen]; 1270*7c478bd9Sstevel@tonic-gate cc -= hlen; 1271*7c478bd9Sstevel@tonic-gate 1272*7c478bd9Sstevel@tonic-gate /* 1273*7c478bd9Sstevel@tonic-gate * If we could tell the interface on which a packet from 1274*7c478bd9Sstevel@tonic-gate * address 0 arrived, we could deal with such solicitations. 1275*7c478bd9Sstevel@tonic-gate */ 1276*7c478bd9Sstevel@tonic-gate ifp = receiving_interface(&msg, _B_FALSE); 1277*7c478bd9Sstevel@tonic-gate ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp, 1278*7c478bd9Sstevel@tonic-gate buf.pkt.ip.ip_dst.s_addr, p, cc); 1279*7c478bd9Sstevel@tonic-gate if (ifp == NULL) 1280*7c478bd9Sstevel@tonic-gate continue; 1281*7c478bd9Sstevel@tonic-gate 1282*7c478bd9Sstevel@tonic-gate if (IS_IFF_QUIET(ifp->int_if_flags)) { 1283*7c478bd9Sstevel@tonic-gate trace_misc("discard RDISC packet received over %s, %X", 1284*7c478bd9Sstevel@tonic-gate ifp->int_name, ifp->int_if_flags); 1285*7c478bd9Sstevel@tonic-gate continue; 1286*7c478bd9Sstevel@tonic-gate } 1287*7c478bd9Sstevel@tonic-gate 1288*7c478bd9Sstevel@tonic-gate if (from.sin_addr.s_addr != 0 && 1289*7c478bd9Sstevel@tonic-gate ifwithaddr(from.sin_addr.s_addr, _B_FALSE, _B_FALSE)) { 1290*7c478bd9Sstevel@tonic-gate trace_pkt(" " 1291*7c478bd9Sstevel@tonic-gate "discard our own Router Discovery message"); 1292*7c478bd9Sstevel@tonic-gate continue; 1293*7c478bd9Sstevel@tonic-gate } 1294*7c478bd9Sstevel@tonic-gate 1295*7c478bd9Sstevel@tonic-gate /* The remote address *must* be directly connected. */ 1296*7c478bd9Sstevel@tonic-gate if (!remote_address_ok(ifp, from.sin_addr.s_addr)) { 1297*7c478bd9Sstevel@tonic-gate trace_misc("discard rdisc message; source %s not on " 1298*7c478bd9Sstevel@tonic-gate "interface %s", naddr_ntoa(from.sin_addr.s_addr), 1299*7c478bd9Sstevel@tonic-gate ifp->int_name); 1300*7c478bd9Sstevel@tonic-gate continue; 1301*7c478bd9Sstevel@tonic-gate } 1302*7c478bd9Sstevel@tonic-gate 1303*7c478bd9Sstevel@tonic-gate switch (p->icmp.icmp_type) { 1304*7c478bd9Sstevel@tonic-gate case ICMP_ROUTERADVERT: 1305*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_NO_ADV_IN) 1306*7c478bd9Sstevel@tonic-gate continue; 1307*7c478bd9Sstevel@tonic-gate 1308*7c478bd9Sstevel@tonic-gate if (p->ad.icmp_ad_asize*2*sizeof (wp[0]) < 1309*7c478bd9Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info[0])) { 1310*7c478bd9Sstevel@tonic-gate msglim(&bad_asize, from.sin_addr.s_addr, 1311*7c478bd9Sstevel@tonic-gate "intolerable rdisc address size=%d", 1312*7c478bd9Sstevel@tonic-gate p->ad.icmp_ad_asize); 1313*7c478bd9Sstevel@tonic-gate continue; 1314*7c478bd9Sstevel@tonic-gate } 1315*7c478bd9Sstevel@tonic-gate if (p->ad.icmp_ad_num == 0) { 1316*7c478bd9Sstevel@tonic-gate trace_pkt(" empty?"); 1317*7c478bd9Sstevel@tonic-gate continue; 1318*7c478bd9Sstevel@tonic-gate } 1319*7c478bd9Sstevel@tonic-gate if (cc < (sizeof (p->ad) - 1320*7c478bd9Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info) + 1321*7c478bd9Sstevel@tonic-gate (p->ad.icmp_ad_num * 1322*7c478bd9Sstevel@tonic-gate sizeof (p->ad.icmp_ad_info[0])))) { 1323*7c478bd9Sstevel@tonic-gate msglim(&bad_len, from.sin_addr.s_addr, 1324*7c478bd9Sstevel@tonic-gate "rdisc length %d does not match ad_num" 1325*7c478bd9Sstevel@tonic-gate " %d", cc, p->ad.icmp_ad_num); 1326*7c478bd9Sstevel@tonic-gate continue; 1327*7c478bd9Sstevel@tonic-gate } 1328*7c478bd9Sstevel@tonic-gate 1329*7c478bd9Sstevel@tonic-gate needsort = _B_TRUE; 1330*7c478bd9Sstevel@tonic-gate wp = &p->ad.icmp_ad_info[0].icmp_ad_addr; 1331*7c478bd9Sstevel@tonic-gate for (n = 0; n < p->ad.icmp_ad_num; n++) { 1332*7c478bd9Sstevel@tonic-gate parse_ad(from.sin_addr.s_addr, 1333*7c478bd9Sstevel@tonic-gate wp[0], wp[1], 1334*7c478bd9Sstevel@tonic-gate ntohs(p->ad.icmp_ad_life), ifp); 1335*7c478bd9Sstevel@tonic-gate wp += p->ad.icmp_ad_asize; 1336*7c478bd9Sstevel@tonic-gate } 1337*7c478bd9Sstevel@tonic-gate break; 1338*7c478bd9Sstevel@tonic-gate 1339*7c478bd9Sstevel@tonic-gate 1340*7c478bd9Sstevel@tonic-gate case ICMP_ROUTERSOLICIT: 1341*7c478bd9Sstevel@tonic-gate if (!should_supply(ifp)) 1342*7c478bd9Sstevel@tonic-gate continue; 1343*7c478bd9Sstevel@tonic-gate if ((ifp->int_state & IS_NO_ADV_OUT) || 1344*7c478bd9Sstevel@tonic-gate !IS_IFF_ROUTING(ifp->int_if_flags)) 1345*7c478bd9Sstevel@tonic-gate continue; 1346*7c478bd9Sstevel@tonic-gate if (stopint != 0) 1347*7c478bd9Sstevel@tonic-gate continue; 1348*7c478bd9Sstevel@tonic-gate 1349*7c478bd9Sstevel@tonic-gate /* 1350*7c478bd9Sstevel@tonic-gate * We should handle messages from address 0, 1351*7c478bd9Sstevel@tonic-gate * but cannot due to kernel limitations. 1352*7c478bd9Sstevel@tonic-gate */ 1353*7c478bd9Sstevel@tonic-gate 1354*7c478bd9Sstevel@tonic-gate /* Respond with a point-to-point advertisement */ 1355*7c478bd9Sstevel@tonic-gate send_adv(ifp, from.sin_addr.s_addr, 0); 1356*7c478bd9Sstevel@tonic-gate break; 1357*7c478bd9Sstevel@tonic-gate } 1358*7c478bd9Sstevel@tonic-gate } 1359*7c478bd9Sstevel@tonic-gate 1360*7c478bd9Sstevel@tonic-gate if (needsort) 1361*7c478bd9Sstevel@tonic-gate rdisc_sort(); 1362*7c478bd9Sstevel@tonic-gate } 1363*7c478bd9Sstevel@tonic-gate 1364*7c478bd9Sstevel@tonic-gate void 1365*7c478bd9Sstevel@tonic-gate rdisc_dump(void) 1366*7c478bd9Sstevel@tonic-gate { 1367*7c478bd9Sstevel@tonic-gate struct dr *drp; 1368*7c478bd9Sstevel@tonic-gate 1369*7c478bd9Sstevel@tonic-gate for (drp = drs; drp < &drs[max_ads]; drp++) 1370*7c478bd9Sstevel@tonic-gate if (drp->dr_ts != 0) 1371*7c478bd9Sstevel@tonic-gate trace_dr(drp); 1372*7c478bd9Sstevel@tonic-gate } 1373*7c478bd9Sstevel@tonic-gate 1374*7c478bd9Sstevel@tonic-gate void 1375*7c478bd9Sstevel@tonic-gate rdisc_suppress(struct interface *ifp) 1376*7c478bd9Sstevel@tonic-gate { 1377*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_ADV_OUT) { 1378*7c478bd9Sstevel@tonic-gate msglog("%s \"rdisc_adv\" specified, will not " 1379*7c478bd9Sstevel@tonic-gate "suppress rdisc adv", ifp->int_name); 1380*7c478bd9Sstevel@tonic-gate } else { 1381*7c478bd9Sstevel@tonic-gate if (ifp->int_state & IS_SUPPRESS_RDISC) 1382*7c478bd9Sstevel@tonic-gate return; 1383*7c478bd9Sstevel@tonic-gate ifp->int_state |= (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC); 1384*7c478bd9Sstevel@tonic-gate trace_misc("suppress rdisc adv on %s", ifp->int_name); 1385*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = 0; 1386*7c478bd9Sstevel@tonic-gate } 1387*7c478bd9Sstevel@tonic-gate } 1388*7c478bd9Sstevel@tonic-gate 1389*7c478bd9Sstevel@tonic-gate void 1390*7c478bd9Sstevel@tonic-gate rdisc_restore(struct interface *ifp) 1391*7c478bd9Sstevel@tonic-gate { 1392*7c478bd9Sstevel@tonic-gate if ((ifp->int_state & IS_SUPPRESS_RDISC) == 0) 1393*7c478bd9Sstevel@tonic-gate return; 1394*7c478bd9Sstevel@tonic-gate ifp->int_state &= ~(IS_SUPPRESS_RDISC|IS_FLUSH_RDISC); 1395*7c478bd9Sstevel@tonic-gate trace_misc("restoring rdisc adv on %s", ifp->int_name); 1396*7c478bd9Sstevel@tonic-gate rdisc_timer.tv_sec = 0; 1397*7c478bd9Sstevel@tonic-gate } 1398