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