xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c (revision 552:6a924c2e2442)
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