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