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