118316Swollman/*
218316Swollman * Copyright (c) 1995
318316Swollman *	The Regents of the University of California.  All rights reserved.
418316Swollman *
518316Swollman * Redistribution and use in source and binary forms, with or without
618316Swollman * modification, are permitted provided that the following conditions
718316Swollman * are met:
818316Swollman * 1. Redistributions of source code must retain the above copyright
918316Swollman *    notice, this list of conditions and the following disclaimer.
1018316Swollman * 2. Redistributions in binary form must reproduce the above copyright
1118316Swollman *    notice, this list of conditions and the following disclaimer in the
1218316Swollman *    documentation and/or other materials provided with the distribution.
1318316Swollman * 4. Neither the name of the University nor the names of its contributors
1418316Swollman *    may be used to endorse or promote products derived from this software
1518316Swollman *    without specific prior written permission.
1618316Swollman *
1718316Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1818316Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1918316Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2018316Swollman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2118316Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2218316Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2318316Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2418316Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2518316Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2618316Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2718316Swollman * SUCH DAMAGE.
2846303Smarkm *
2950476Speter * $FreeBSD$
3018316Swollman */
3118316Swollman
3218316Swollman#include "defs.h"
3318316Swollman#include <netinet/in_systm.h>
3418316Swollman#include <netinet/ip.h>
3518316Swollman#include <netinet/ip_icmp.h>
3618316Swollman
37126250Sbms#ifdef __NetBSD__
3864483Ssheldonh__RCSID("$NetBSD$");
39126250Sbms#elif defined(__FreeBSD__)
40126250Sbms__RCSID("$FreeBSD$");
41126250Sbms#else
42126250Sbms__RCSID("$Revision: 2.27 $");
43126250Sbms#ident "$Revision: 2.27 $"
4446303Smarkm#endif
4546303Smarkm
4618316Swollman/* router advertisement ICMP packet */
4718316Swollmanstruct icmp_ad {
4818316Swollman	u_int8_t    icmp_type;		/* type of message */
4918316Swollman	u_int8_t    icmp_code;		/* type sub code */
5018316Swollman	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
5118316Swollman	u_int8_t    icmp_ad_num;	/* # of following router addresses */
5218316Swollman	u_int8_t    icmp_ad_asize;	/* 2--words in each advertisement */
5318316Swollman	u_int16_t   icmp_ad_life;	/* seconds of validity */
5418316Swollman	struct icmp_ad_info {
5518316Swollman	    n_long  icmp_ad_addr;
5618316Swollman	    n_long  icmp_ad_pref;
5718316Swollman	} icmp_ad_info[1];
5818316Swollman};
5918316Swollman
6018316Swollman/* router solicitation ICMP packet */
6118316Swollmanstruct icmp_so {
6218316Swollman	u_int8_t    icmp_type;		/* type of message */
6318316Swollman	u_int8_t    icmp_code;		/* type sub code */
6418316Swollman	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
6518316Swollman	n_long	    icmp_so_rsvd;
6618316Swollman};
6718316Swollman
6818316Swollmanunion ad_u {
6918316Swollman	struct icmp icmp;
7018316Swollman	struct icmp_ad ad;
7118316Swollman	struct icmp_so so;
7218316Swollman};
7318316Swollman
7418316Swollman
7518316Swollmanint	rdisc_sock = -1;		/* router-discovery raw socket */
76190716Sphkstatic const struct interface *rdisc_sock_mcast; /* current multicast interface */
7718316Swollman
7818316Swollmanstruct timeval rdisc_timer;
7918316Swollmanint rdisc_ok;				/* using solicited route */
8018316Swollman
8118316Swollman
8246303Smarkm#define MAX_ADS 16			/* at least one per interface */
83190716Sphkstruct dr {				/* accumulated advertisements */
8418316Swollman    struct interface *dr_ifp;
8518316Swollman    naddr   dr_gate;			/* gateway */
8618316Swollman    time_t  dr_ts;			/* when received */
8764131Ssheldonh    time_t  dr_life;			/* lifetime in host byte order */
8818316Swollman    n_long  dr_recv_pref;		/* received but biased preference */
8918316Swollman    n_long  dr_pref;			/* preference adjusted by metric */
90190716Sphk};
91190716Sphkstatic const struct dr *cur_drp;
92190716Sphkstatic struct dr drs[MAX_ADS];
9318316Swollman
9446303Smarkm/* convert between signed, balanced around zero,
9546303Smarkm * and unsigned zero-based preferences */
9646303Smarkm#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
9746303Smarkm#define UNSIGN_PREF(p) SIGN_PREF(p)
9846303Smarkm/* adjust unsigned preference by interface metric,
9946303Smarkm * without driving it to infinity */
100126250Sbms#define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\
101126250Sbms		      ? ((p) != 0 ? 1 : 0)				    \
102126250Sbms		      : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric))
10318316Swollman
10418316Swollmanstatic void rdisc_sort(void);
10518316Swollman
10618316Swollman
10718316Swollman/* dump an ICMP Router Discovery Advertisement Message
10818316Swollman */
10918316Swollmanstatic void
11046303Smarkmtrace_rdisc(const char	*act,
11118316Swollman	    naddr	from,
11218316Swollman	    naddr	to,
11318316Swollman	    struct interface *ifp,
11418316Swollman	    union ad_u	*p,
11518316Swollman	    u_int	len)
11618316Swollman{
11718316Swollman	int i;
11818316Swollman	n_long *wp, *lim;
11918316Swollman
12018316Swollman
12118316Swollman	if (!TRACEPACKETS || ftrace == 0)
12218316Swollman		return;
12318316Swollman
12418316Swollman	lastlog();
12518316Swollman
12618316Swollman	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
12718316Swollman		(void)fprintf(ftrace, "%s Router Ad"
12818316Swollman			      " from %s to %s via %s life=%d\n",
12918316Swollman			      act, naddr_ntoa(from), naddr_ntoa(to),
13018316Swollman			      ifp ? ifp->int_name : "?",
13118316Swollman			      ntohs(p->ad.icmp_ad_life));
13218316Swollman		if (!TRACECONTENTS)
13318316Swollman			return;
13418316Swollman
13518316Swollman		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
13618316Swollman		lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
13718316Swollman		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
13820339Swollman			(void)fprintf(ftrace, "\t%s preference=%d",
13918316Swollman				      naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
14018316Swollman			wp += p->ad.icmp_ad_asize;
14118316Swollman		}
14218316Swollman		(void)fputc('\n',ftrace);
14318316Swollman
14418316Swollman	} else {
14519880Swollman		trace_act("%s Router Solic. from %s to %s via %s value=%#x",
14618316Swollman			  act, naddr_ntoa(from), naddr_ntoa(to),
14718316Swollman			  ifp ? ifp->int_name : "?",
14846303Smarkm			  (int)ntohl(p->so.icmp_so_rsvd));
14918316Swollman	}
15018316Swollman}
15118316Swollman
15218316Swollman/* prepare Router Discovery socket.
15318316Swollman */
15418316Swollmanstatic void
15518316Swollmanget_rdisc_sock(void)
15618316Swollman{
15718316Swollman	if (rdisc_sock < 0) {
15818316Swollman		rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
15918316Swollman		if (rdisc_sock < 0)
16018316Swollman			BADERR(1,"rdisc_sock = socket()");
16118316Swollman		fix_sock(rdisc_sock,"rdisc_sock");
16218316Swollman		fix_select();
16318316Swollman	}
16418316Swollman}
16518316Swollman
16618316Swollman
16718316Swollman/* Pick multicast group for router-discovery socket
16818316Swollman */
16918316Swollmanvoid
17018316Swollmanset_rdisc_mg(struct interface *ifp,
17119880Swollman	     int on)			/* 0=turn it off */
17219880Swollman{
173180993Sphk	struct group_req gr;
174180993Sphk	struct sockaddr_in *sin;
17518316Swollman
176190718Sphk	assert(ifp != NULL);
177190718Sphk
17818316Swollman	if (rdisc_sock < 0) {
17918316Swollman		/* Create the raw socket so that we can hear at least
18018316Swollman		 * broadcast router discovery packets.
18118316Swollman		 */
18218316Swollman		if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
18318316Swollman		    || !on)
18418316Swollman			return;
18518316Swollman		get_rdisc_sock();
18618316Swollman	}
18718316Swollman
18819880Swollman	if (!(ifp->int_if_flags & IFF_MULTICAST)) {
18918316Swollman		ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
19018316Swollman		return;
19118316Swollman	}
19218316Swollman
193180993Sphk	memset(&gr, 0, sizeof(gr));
194180993Sphk	gr.gr_interface = ifp->int_index;
195180993Sphk	sin = (struct sockaddr_in *)&gr.gr_group;
196180993Sphk	sin->sin_family = AF_INET;
197180993Sphk#ifdef _HAVE_SIN_LEN
198180993Sphk	sin->sin_len = sizeof(struct sockaddr_in);
19918316Swollman#endif
200180993Sphk
20118316Swollman	if (supplier
20218316Swollman	    || (ifp->int_state & IS_NO_ADV_IN)
20318316Swollman	    || !on) {
20418316Swollman		/* stop listening to advertisements
20518316Swollman		 */
20618316Swollman		if (ifp->int_state & IS_ALL_HOSTS) {
207180993Sphk			sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
20818316Swollman			if (setsockopt(rdisc_sock, IPPROTO_IP,
209180993Sphk				       MCAST_LEAVE_GROUP,
210180993Sphk				       &gr, sizeof(gr)) < 0)
211180993Sphk				LOGERR("MCAST_LEAVE_GROUP ALLHOSTS");
21218316Swollman			ifp->int_state &= ~IS_ALL_HOSTS;
21318316Swollman		}
21418316Swollman
21518316Swollman	} else if (!(ifp->int_state & IS_ALL_HOSTS)) {
21618316Swollman		/* start listening to advertisements
21718316Swollman		 */
218180993Sphk		sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
219180993Sphk		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
220180993Sphk			       &gr, sizeof(gr)) < 0) {
221180993Sphk			LOGERR("MCAST_JOIN_GROUP ALLHOSTS");
22218316Swollman		} else {
22318316Swollman			ifp->int_state |= IS_ALL_HOSTS;
22418316Swollman		}
22518316Swollman	}
22618316Swollman
22718316Swollman	if (!supplier
22818316Swollman	    || (ifp->int_state & IS_NO_ADV_OUT)
22918316Swollman	    || !on) {
23018316Swollman		/* stop listening to solicitations
23118316Swollman		 */
23218316Swollman		if (ifp->int_state & IS_ALL_ROUTERS) {
233180993Sphk			sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
23418316Swollman			if (setsockopt(rdisc_sock, IPPROTO_IP,
235180993Sphk				       MCAST_LEAVE_GROUP,
236180993Sphk				       &gr, sizeof(gr)) < 0)
237180993Sphk				LOGERR("MCAST_LEAVE_GROUP ALLROUTERS");
23818316Swollman			ifp->int_state &= ~IS_ALL_ROUTERS;
23918316Swollman		}
24018316Swollman
24118316Swollman	} else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
24218316Swollman		/* start hearing solicitations
24318316Swollman		 */
244180993Sphk		sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
245180993Sphk		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
246180993Sphk			       &gr, sizeof(gr)) < 0) {
247180993Sphk			LOGERR("MCAST_JOIN_GROUP ALLROUTERS");
24818316Swollman		} else {
24918316Swollman			ifp->int_state |= IS_ALL_ROUTERS;
25018316Swollman		}
25118316Swollman	}
25218316Swollman}
25318316Swollman
25418316Swollman
25518316Swollman/* start supplying routes
25618316Swollman */
25718316Swollmanvoid
25818316Swollmanset_supplier(void)
25918316Swollman{
26018316Swollman	struct interface *ifp;
26118316Swollman	struct dr *drp;
26218316Swollman
26318316Swollman	if (supplier_set)
26418316Swollman		return;
26518316Swollman
26637908Scharnier	trace_act("start supplying routes");
26718316Swollman
26818316Swollman	/* Forget discovered routes.
26918316Swollman	 */
27018316Swollman	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
27118316Swollman		drp->dr_recv_pref = 0;
27218316Swollman		drp->dr_life = 0;
27318316Swollman	}
27418316Swollman	rdisc_age(0);
27518316Swollman
27618316Swollman	supplier_set = 1;
27718316Swollman	supplier = 1;
27818316Swollman
27918316Swollman	/* Do not start advertising until we have heard some RIP routes */
28018316Swollman	LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
28118316Swollman
28218316Swollman	/* Switch router discovery multicast groups from soliciting
28318316Swollman	 * to advertising.
28418316Swollman	 */
285190711Sphk	LIST_FOREACH(ifp, &ifnet, int_list) {
28618316Swollman		if (ifp->int_state & IS_BROKE)
28718316Swollman			continue;
28818316Swollman		ifp->int_rdisc_cnt = 0;
28918316Swollman		ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
29018316Swollman		ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
29118316Swollman		set_rdisc_mg(ifp, 1);
29218316Swollman	}
29318316Swollman
29418316Swollman	/* get rid of any redirects */
29518316Swollman	del_redirects(0,0);
29618316Swollman}
29718316Swollman
29818316Swollman
29918316Swollman/* age discovered routes and find the best one
30018316Swollman */
30118316Swollmanvoid
30218316Swollmanrdisc_age(naddr bad_gate)
30318316Swollman{
30418316Swollman	time_t sec;
30518316Swollman	struct dr *drp;
30618316Swollman
30718316Swollman
30837908Scharnier	/* If only advertising, then do only that. */
30918316Swollman	if (supplier) {
31046303Smarkm		/* If switching from client to server, get rid of old
31118316Swollman		 * default routes.
31218316Swollman		 */
31318316Swollman		if (cur_drp != 0)
31418316Swollman			rdisc_sort();
31518316Swollman		rdisc_adv();
31618316Swollman		return;
31718316Swollman	}
31818316Swollman
31918316Swollman	/* If we are being told about a bad router,
32018316Swollman	 * then age the discovered default route, and if there is
32137908Scharnier	 * no alternative, solicit a replacement.
32218316Swollman	 */
32318316Swollman	if (bad_gate != 0) {
32418316Swollman		/* Look for the bad discovered default route.
32518316Swollman		 * Age it and note its interface.
32618316Swollman		 */
32718316Swollman		for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
32818316Swollman			if (drp->dr_ts == 0)
32918316Swollman				continue;
33018316Swollman
33118316Swollman			/* When we find the bad router, then age the route
33218316Swollman			 * to at most SUPPLY_INTERVAL.
33318316Swollman			 * This is contrary to RFC 1256, but defends against
33418316Swollman			 * black holes.
33518316Swollman			 */
33618316Swollman			if (drp->dr_gate == bad_gate) {
33718316Swollman				sec = (now.tv_sec - drp->dr_life
33818316Swollman				       + SUPPLY_INTERVAL);
33918316Swollman				if (drp->dr_ts > sec) {
34019880Swollman					trace_act("age 0.0.0.0 --> %s via %s",
34118316Swollman						  naddr_ntoa(drp->dr_gate),
34218316Swollman						  drp->dr_ifp->int_name);
34318316Swollman					drp->dr_ts = sec;
34418316Swollman				}
34518316Swollman				break;
34618316Swollman			}
34718316Swollman		}
34818316Swollman	}
34918316Swollman
35018316Swollman	rdisc_sol();
35146303Smarkm	rdisc_sort();
35218316Swollman
35346303Smarkm	/* Delete old redirected routes to keep the kernel table small,
35446303Smarkm	 * and to prevent black holes.  Check that the kernel table
35546303Smarkm	 * matches the daemon table (i.e. has the default route).
35646303Smarkm	 * But only if RIP is not running and we are not dealing with
35746303Smarkm	 * a bad gateway, since otherwise age() will be called.
35846303Smarkm	 */
35946303Smarkm	if (rip_sock < 0 && bad_gate == 0)
36046303Smarkm		age(0);
36118316Swollman}
36218316Swollman
36318316Swollman
36418316Swollman/* Zap all routes discovered via an interface that has gone bad
36518316Swollman *	This should only be called when !(ifp->int_state & IS_ALIAS)
36618316Swollman */
36718316Swollmanvoid
36818316Swollmanif_bad_rdisc(struct interface *ifp)
36918316Swollman{
37018316Swollman	struct dr *drp;
37118316Swollman
37218316Swollman	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
37318316Swollman		if (drp->dr_ifp != ifp)
37418316Swollman			continue;
37518316Swollman		drp->dr_recv_pref = 0;
37646303Smarkm		drp->dr_ts = 0;
37718316Swollman		drp->dr_life = 0;
37818316Swollman	}
37918316Swollman
38046303Smarkm	/* make a note to re-solicit, turn RIP on or off, etc. */
38146303Smarkm	rdisc_timer.tv_sec = 0;
38218316Swollman}
38318316Swollman
38418316Swollman
38518316Swollman/* mark an interface ok for router discovering.
38618316Swollman */
38718316Swollmanvoid
38818316Swollmanif_ok_rdisc(struct interface *ifp)
38918316Swollman{
39018316Swollman	set_rdisc_mg(ifp, 1);
39118316Swollman
39218316Swollman	ifp->int_rdisc_cnt = 0;
39318316Swollman	ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
39418316Swollman						    ? MIN_WAITTIME
39518316Swollman						    : MAX_SOLICITATION_DELAY);
39618316Swollman	if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
39718316Swollman		rdisc_timer = ifp->int_rdisc_timer;
39818316Swollman}
39918316Swollman
40018316Swollman
40118316Swollman/* get rid of a dead discovered router
40218316Swollman */
40318316Swollmanstatic void
40418316Swollmandel_rdisc(struct dr *drp)
40518316Swollman{
40618316Swollman	struct interface *ifp;
40746303Smarkm	naddr gate;
40818316Swollman	int i;
40918316Swollman
41018316Swollman
41146303Smarkm	del_redirects(gate = drp->dr_gate, 0);
41218316Swollman	drp->dr_ts = 0;
41318316Swollman	drp->dr_life = 0;
41418316Swollman
41518316Swollman
41618316Swollman	/* Count the other discovered routes on the interface.
41718316Swollman	 */
41818316Swollman	i = 0;
41918316Swollman	ifp = drp->dr_ifp;
42018316Swollman	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
42118316Swollman		if (drp->dr_ts != 0
42218316Swollman		    && drp->dr_ifp == ifp)
42318316Swollman			i++;
42418316Swollman	}
42518316Swollman
42618316Swollman	/* If that was the last good discovered router on the interface,
42718316Swollman	 * then solicit a new one.
42818316Swollman	 * This is contrary to RFC 1256, but defends against black holes.
42918316Swollman	 */
43046303Smarkm	if (i != 0) {
43146303Smarkm		trace_act("discovered router %s via %s"
43246303Smarkm			  " is bad--have %d remaining",
43346303Smarkm			  naddr_ntoa(gate), ifp->int_name, i);
43446303Smarkm	} else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
43546303Smarkm		trace_act("last discovered router %s via %s"
43646303Smarkm			  " is bad--re-solicit",
43746303Smarkm			  naddr_ntoa(gate), ifp->int_name);
43818316Swollman		ifp->int_rdisc_cnt = 0;
43918316Swollman		ifp->int_rdisc_timer.tv_sec = 0;
44018316Swollman		rdisc_sol();
44146303Smarkm	} else {
44246303Smarkm		trace_act("last discovered router %s via %s"
44346303Smarkm			  " is bad--wait to solicit",
44446303Smarkm			  naddr_ntoa(gate), ifp->int_name);
44518316Swollman	}
44618316Swollman}
44718316Swollman
44818316Swollman
44918316Swollman/* Find the best discovered route,
45018316Swollman * and discard stale routers.
45118316Swollman */
45218316Swollmanstatic void
45318316Swollmanrdisc_sort(void)
45418316Swollman{
45518316Swollman	struct dr *drp, *new_drp;
45618316Swollman	struct rt_entry *rt;
45746303Smarkm	struct rt_spare new;
45818316Swollman	struct interface *ifp;
45946303Smarkm	u_int new_st = 0;
46046303Smarkm	n_long new_pref = 0;
46118316Swollman
46218316Swollman
46318316Swollman	/* Find the best discovered route.
46418316Swollman	 */
46518316Swollman	new_drp = 0;
46618316Swollman	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
46718316Swollman		if (drp->dr_ts == 0)
46818316Swollman			continue;
46918316Swollman		ifp = drp->dr_ifp;
47018316Swollman
47118316Swollman		/* Get rid of expired discovered routers.
47218316Swollman		 */
47318316Swollman		if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
47418316Swollman			del_rdisc(drp);
47518316Swollman			continue;
47618316Swollman		}
47718316Swollman
47818316Swollman		LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
47918316Swollman
48018316Swollman		/* Update preference with possibly changed interface
48118316Swollman		 * metric.
48218316Swollman		 */
48318316Swollman		drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
48418316Swollman
48518316Swollman		/* Prefer the current route to prevent thrashing.
48618316Swollman		 * Prefer shorter lifetimes to speed the detection of
48718316Swollman		 * bad routers.
48818316Swollman		 * Avoid sick interfaces.
48918316Swollman		 */
49018316Swollman		if (new_drp == 0
49118316Swollman		    || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
49218316Swollman			&& (new_pref < drp->dr_pref
49318316Swollman			    || (new_pref == drp->dr_pref
49418316Swollman				&& (drp == cur_drp
49518316Swollman				    || (new_drp != cur_drp
49618316Swollman					&& new_drp->dr_life > drp->dr_life)))))
49718316Swollman		    || ((new_st & IS_SICK)
49818316Swollman			&& !(drp->dr_ifp->int_state & IS_SICK))) {
49918316Swollman			    new_drp = drp;
50018316Swollman			    new_st = drp->dr_ifp->int_state;
50118316Swollman			    new_pref = drp->dr_pref;
50218316Swollman		}
50318316Swollman	}
50418316Swollman
50518316Swollman	/* switch to a better default route
50618316Swollman	 */
50718316Swollman	if (new_drp != cur_drp) {
50818316Swollman		rt = rtget(RIP_DEFAULT, 0);
50918316Swollman
51018316Swollman		/* Stop using discovered routes if they are all bad
51118316Swollman		 */
51218316Swollman		if (new_drp == 0) {
51319880Swollman			trace_act("turn off Router Discovery client");
51418316Swollman			rdisc_ok = 0;
51518316Swollman
51618316Swollman			if (rt != 0
51718316Swollman			    && (rt->rt_state & RS_RDISC)) {
51846303Smarkm				new = rt->rt_spares[0];
51946303Smarkm				new.rts_metric = HOPCNT_INFINITY;
52046303Smarkm				new.rts_time = now.tv_sec - GARBAGE_TIME;
52118316Swollman				rtchange(rt, rt->rt_state & ~RS_RDISC,
52246303Smarkm					 &new, 0);
52318316Swollman				rtswitch(rt, 0);
52418316Swollman			}
52518316Swollman
52618316Swollman		} else {
52718316Swollman			if (cur_drp == 0) {
52818316Swollman				trace_act("turn on Router Discovery client"
52919880Swollman					  " using %s via %s",
53018316Swollman					  naddr_ntoa(new_drp->dr_gate),
53118316Swollman					  new_drp->dr_ifp->int_name);
53218316Swollman				rdisc_ok = 1;
53318316Swollman
53418316Swollman			} else {
53518316Swollman				trace_act("switch Router Discovery from"
53619880Swollman					  " %s via %s to %s via %s",
53718316Swollman					  naddr_ntoa(cur_drp->dr_gate),
53818316Swollman					  cur_drp->dr_ifp->int_name,
53918316Swollman					  naddr_ntoa(new_drp->dr_gate),
54018316Swollman					  new_drp->dr_ifp->int_name);
54118316Swollman			}
54218316Swollman
54346303Smarkm			memset(&new, 0, sizeof(new));
54446303Smarkm			new.rts_ifp = new_drp->dr_ifp;
54546303Smarkm			new.rts_gate = new_drp->dr_gate;
54646303Smarkm			new.rts_router = new_drp->dr_gate;
54746303Smarkm			new.rts_metric = HOPCNT_INFINITY-1;
54846303Smarkm			new.rts_time = now.tv_sec;
54918316Swollman			if (rt != 0) {
55046303Smarkm				rtchange(rt, rt->rt_state | RS_RDISC, &new, 0);
55118316Swollman			} else {
55246303Smarkm				rtadd(RIP_DEFAULT, 0, RS_RDISC, &new);
55318316Swollman			}
55418316Swollman		}
55518316Swollman
55618316Swollman		cur_drp = new_drp;
55718316Swollman	}
55846303Smarkm
55946303Smarkm	/* turn RIP on or off */
56046303Smarkm	if (!rdisc_ok || rip_interfaces > 1) {
56146303Smarkm		rip_on(0);
56246303Smarkm	} else {
56346303Smarkm		rip_off();
56446303Smarkm	}
56518316Swollman}
56618316Swollman
56718316Swollman
56818316Swollman/* handle a single address in an advertisement
56918316Swollman */
57018316Swollmanstatic void
57118316Swollmanparse_ad(naddr from,
57218316Swollman	 naddr gate,
57346303Smarkm	 n_long pref,			/* signed and in network order */
57464131Ssheldonh	 u_short life,			/* in host byte order */
57518316Swollman	 struct interface *ifp)
57618316Swollman{
57719880Swollman	static struct msg_limit bad_gate;
57818316Swollman	struct dr *drp, *new_drp;
57918316Swollman
58018316Swollman
58118316Swollman	if (gate == RIP_DEFAULT
58218316Swollman	    || !check_dst(gate)) {
58319880Swollman		msglim(&bad_gate, from,"router %s advertising bad gateway %s",
58419880Swollman		       naddr_ntoa(from),
58519880Swollman		       naddr_ntoa(gate));
58618316Swollman		return;
58718316Swollman	}
58818316Swollman
58918316Swollman	/* ignore pointers to ourself and routes via unreachable networks
59018316Swollman	 */
59118316Swollman	if (ifwithaddr(gate, 1, 0) != 0) {
59219880Swollman		trace_pkt("    discard Router Discovery Ad pointing at us");
59318316Swollman		return;
59418316Swollman	}
59518316Swollman	if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
59619880Swollman		trace_pkt("    discard Router Discovery Ad"
59719880Swollman			  " toward unreachable net");
59818316Swollman		return;
59918316Swollman	}
60018316Swollman
60118316Swollman	/* Convert preference to an unsigned value
60218316Swollman	 * and later bias it by the metric of the interface.
60318316Swollman	 */
60446303Smarkm	pref = UNSIGN_PREF(ntohl(pref));
60518316Swollman
60646303Smarkm	if (pref == 0 || life < MinMaxAdvertiseInterval) {
60718316Swollman		pref = 0;
60818316Swollman		life = 0;
60918316Swollman	}
61018316Swollman
61118316Swollman	for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
61218316Swollman		/* accept new info for a familiar entry
61318316Swollman		 */
61418316Swollman		if (drp->dr_gate == gate) {
61518316Swollman			new_drp = drp;
61618316Swollman			break;
61718316Swollman		}
61818316Swollman
61918316Swollman		if (life == 0)
62018316Swollman			continue;	/* do not worry about dead ads */
62118316Swollman
62218316Swollman		if (drp->dr_ts == 0) {
62318316Swollman			new_drp = drp;	/* use unused entry */
62418316Swollman
62518316Swollman		} else if (new_drp == 0) {
62618316Swollman			/* look for an entry worse than the new one to
62718316Swollman			 * reuse.
62818316Swollman			 */
62918316Swollman			if ((!(ifp->int_state & IS_SICK)
63018316Swollman			     && (drp->dr_ifp->int_state & IS_SICK))
63118316Swollman			    || (pref > drp->dr_pref
63218316Swollman				&& !((ifp->int_state ^ drp->dr_ifp->int_state)
63318316Swollman				     & IS_SICK)))
63418316Swollman				new_drp = drp;
63518316Swollman
63618316Swollman		} else if (new_drp->dr_ts != 0) {
63737908Scharnier			/* look for the least valuable entry to reuse
63818316Swollman			 */
63918316Swollman			if ((!(new_drp->dr_ifp->int_state & IS_SICK)
64018316Swollman			     && (drp->dr_ifp->int_state & IS_SICK))
64118316Swollman			    || (new_drp->dr_pref > drp->dr_pref
64218316Swollman				&& !((new_drp->dr_ifp->int_state
64318316Swollman				      ^ drp->dr_ifp->int_state)
64418316Swollman				     & IS_SICK)))
64518316Swollman				new_drp = drp;
64618316Swollman		}
64718316Swollman	}
64818316Swollman
64918316Swollman	/* forget it if all of the current entries are better */
65018316Swollman	if (new_drp == 0)
65118316Swollman		return;
65218316Swollman
65318316Swollman	new_drp->dr_ifp = ifp;
65418316Swollman	new_drp->dr_gate = gate;
65518316Swollman	new_drp->dr_ts = now.tv_sec;
65664131Ssheldonh	new_drp->dr_life = life;
65718316Swollman	new_drp->dr_recv_pref = pref;
65818316Swollman	/* bias functional preference by metric of the interface */
65918316Swollman	new_drp->dr_pref = PREF(pref,ifp);
66018316Swollman
66118316Swollman	/* after hearing a good advertisement, stop asking
66218316Swollman	 */
66318316Swollman	if (!(ifp->int_state & IS_SICK))
66418316Swollman		ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
66518316Swollman}
66618316Swollman
66718316Swollman
66818316Swollman/* Compute the IP checksum
66918316Swollman *	This assumes the packet is less than 32K long.
67018316Swollman */
67118316Swollmanstatic u_short
67218316Swollmanin_cksum(u_short *p,
67318316Swollman	 u_int len)
67418316Swollman{
67518316Swollman	u_int sum = 0;
67618316Swollman	int nwords = len >> 1;
67718316Swollman
67818316Swollman	while (nwords-- != 0)
67918316Swollman		sum += *p++;
68018316Swollman
68118316Swollman	if (len & 1)
68218316Swollman		sum += *(u_char *)p;
68318316Swollman
68418316Swollman	/* end-around-carry */
68518316Swollman	sum = (sum >> 16) + (sum & 0xffff);
68618316Swollman	sum += (sum >> 16);
68718316Swollman	return (~sum);
68818316Swollman}
68918316Swollman
69018316Swollman
69118316Swollman/* Send a router discovery advertisement or solicitation ICMP packet.
69218316Swollman */
69318316Swollmanstatic void
69418316Swollmansend_rdisc(union ad_u *p,
69518316Swollman	   int p_size,
69618316Swollman	   struct interface *ifp,
69746303Smarkm	   naddr dst,			/* 0 or unicast destination */
69818316Swollman	   int	type)			/* 0=unicast, 1=bcast, 2=mcast */
69918316Swollman{
700126250Sbms	struct sockaddr_in rsin;
70118316Swollman	int flags;
70246303Smarkm	const char *msg;
70318316Swollman
70418316Swollman
705126250Sbms	memset(&rsin, 0, sizeof(rsin));
706126250Sbms	rsin.sin_addr.s_addr = dst;
707126250Sbms	rsin.sin_family = AF_INET;
70818316Swollman#ifdef _HAVE_SIN_LEN
709126250Sbms	rsin.sin_len = sizeof(rsin);
71018316Swollman#endif
71118316Swollman	flags = MSG_DONTROUTE;
71218316Swollman
71318316Swollman	switch (type) {
71446303Smarkm	case 0:				/* unicast */
71520339Swollman	default:
71618316Swollman		msg = "Send";
71718316Swollman		break;
71818316Swollman
71918316Swollman	case 1:				/* broadcast */
72018316Swollman		if (ifp->int_if_flags & IFF_POINTOPOINT) {
72118316Swollman			msg = "Send pt-to-pt";
722126250Sbms			rsin.sin_addr.s_addr = ifp->int_dstaddr;
72318316Swollman		} else {
72418316Swollman			msg = "Send broadcast";
725126250Sbms			rsin.sin_addr.s_addr = ifp->int_brdaddr;
72618316Swollman		}
72718316Swollman		break;
72818316Swollman
72918316Swollman	case 2:				/* multicast */
73018316Swollman		msg = "Send multicast";
73118316Swollman		if (ifp->int_state & IS_DUP) {
73218316Swollman			trace_act("abort multicast output via %s"
73319880Swollman				  " with duplicate address",
73418316Swollman				  ifp->int_name);
73518316Swollman			return;
73618316Swollman		}
73718316Swollman		if (rdisc_sock_mcast != ifp) {
73818316Swollman			/* select the right interface. */
739180993Sphk			struct ip_mreqn mreqn;
740180993Sphk
741180993Sphk			memset(&mreqn, 0, sizeof(struct ip_mreqn));
742180993Sphk			mreqn.imr_ifindex = ifp->int_index;
74318316Swollman			if (0 > setsockopt(rdisc_sock,
74418316Swollman					   IPPROTO_IP, IP_MULTICAST_IF,
745180993Sphk					   &mreqn,
746180993Sphk					   sizeof(mreqn))) {
74718316Swollman				LOGERR("setsockopt(rdisc_sock,"
74818316Swollman				       "IP_MULTICAST_IF)");
74918316Swollman				rdisc_sock_mcast = 0;
75018316Swollman				return;
75118316Swollman			}
75218316Swollman			rdisc_sock_mcast = ifp;
75318316Swollman		}
75418316Swollman		flags = 0;
75518316Swollman		break;
75618316Swollman	}
75718316Swollman
75818316Swollman	if (rdisc_sock < 0)
75918316Swollman		get_rdisc_sock();
76018316Swollman
761126250Sbms	trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp,
76218316Swollman		    p, p_size);
76318316Swollman
76418316Swollman	if (0 > sendto(rdisc_sock, p, p_size, flags,
765126250Sbms		       (struct sockaddr *)&rsin, sizeof(rsin))) {
76618316Swollman		if (ifp == 0 || !(ifp->int_state & IS_BROKE))
76718316Swollman			msglog("sendto(%s%s%s): %s",
76818316Swollman			       ifp != 0 ? ifp->int_name : "",
76918316Swollman			       ifp != 0 ? ", " : "",
770126250Sbms			       inet_ntoa(rsin.sin_addr),
77118316Swollman			       strerror(errno));
77218316Swollman		if (ifp != 0)
77318316Swollman			if_sick(ifp);
77418316Swollman	}
77518316Swollman}
77618316Swollman
77718316Swollman
77818316Swollman/* Send an advertisement
77918316Swollman */
78018316Swollmanstatic void
78118316Swollmansend_adv(struct interface *ifp,
78246303Smarkm	 naddr	dst,			/* 0 or unicast destination */
78318316Swollman	 int	type)			/* 0=unicast, 1=bcast, 2=mcast */
78418316Swollman{
78518316Swollman	union ad_u u;
78618316Swollman	n_long pref;
78718316Swollman
78818316Swollman
78946303Smarkm	memset(&u, 0, sizeof(u.ad));
79018316Swollman
79118316Swollman	u.ad.icmp_type = ICMP_ROUTERADVERT;
79218316Swollman	u.ad.icmp_ad_num = 1;
79318316Swollman	u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
79418316Swollman
79518316Swollman	u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
79618316Swollman
79746303Smarkm	/* Convert the configured preference to an unsigned value,
79846303Smarkm	 * bias it by the interface metric, and then send it as a
79946303Smarkm	 * signed, network byte order value.
80046303Smarkm	 */
80146303Smarkm	pref = UNSIGN_PREF(ifp->int_rdisc_pref);
80246303Smarkm	u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp)));
80346303Smarkm
80418316Swollman	u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
80518316Swollman
80618316Swollman	u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
80718316Swollman
80818316Swollman	send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
80918316Swollman}
81018316Swollman
81118316Swollman
81218316Swollman/* Advertise for Router Discovery
81318316Swollman */
81418316Swollmanvoid
81518316Swollmanrdisc_adv(void)
81618316Swollman{
81718316Swollman	struct interface *ifp;
81818316Swollman
81919880Swollman	if (!supplier)
82019880Swollman		return;
82118316Swollman
82218316Swollman	rdisc_timer.tv_sec = now.tv_sec + NEVER;
82318316Swollman
824190711Sphk	LIST_FOREACH(ifp, &ifnet, int_list) {
82519880Swollman		if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
82618316Swollman			continue;
82718316Swollman
82818316Swollman		if (!timercmp(&ifp->int_rdisc_timer, &now, >)
82918316Swollman		    || stopint) {
83018316Swollman			send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
83118316Swollman				 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
83218316Swollman			ifp->int_rdisc_cnt++;
83318316Swollman
83418316Swollman			intvl_random(&ifp->int_rdisc_timer,
83518316Swollman				     (ifp->int_rdisc_int*3)/4,
83618316Swollman				     ifp->int_rdisc_int);
83718316Swollman			if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
83818316Swollman			    && (ifp->int_rdisc_timer.tv_sec
83918316Swollman				> MAX_INITIAL_ADVERT_INTERVAL)) {
84018316Swollman				ifp->int_rdisc_timer.tv_sec
84118316Swollman				= MAX_INITIAL_ADVERT_INTERVAL;
84218316Swollman			}
84318316Swollman			timevaladd(&ifp->int_rdisc_timer, &now);
84418316Swollman		}
84518316Swollman
84618316Swollman		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
84718316Swollman			rdisc_timer = ifp->int_rdisc_timer;
84818316Swollman	}
84918316Swollman}
85018316Swollman
85118316Swollman
85218316Swollman/* Solicit for Router Discovery
85318316Swollman */
85418316Swollmanvoid
85518316Swollmanrdisc_sol(void)
85618316Swollman{
85718316Swollman	struct interface *ifp;
85818316Swollman	union ad_u u;
85918316Swollman
86018316Swollman
86119880Swollman	if (supplier)
86219880Swollman		return;
86319880Swollman
86418316Swollman	rdisc_timer.tv_sec = now.tv_sec + NEVER;
86518316Swollman
866190711Sphk	LIST_FOREACH(ifp, &ifnet, int_list) {
86719880Swollman		if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
86818316Swollman		    || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
86918316Swollman			continue;
87018316Swollman
87118316Swollman		if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
87246303Smarkm			memset(&u, 0, sizeof(u.so));
87318316Swollman			u.so.icmp_type = ICMP_ROUTERSOLICIT;
87418316Swollman			u.so.icmp_cksum = in_cksum((u_short*)&u.so,
87518316Swollman						   sizeof(u.so));
87618316Swollman			send_rdisc(&u, sizeof(u.so), ifp,
87718316Swollman				   htonl(INADDR_ALLROUTERS_GROUP),
87818316Swollman				   ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
87918316Swollman
88018316Swollman			if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
88118316Swollman				continue;
88218316Swollman
88318316Swollman			ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
88418316Swollman			ifp->int_rdisc_timer.tv_usec = 0;
88518316Swollman			timevaladd(&ifp->int_rdisc_timer, &now);
88618316Swollman		}
88718316Swollman
88818316Swollman		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
88918316Swollman			rdisc_timer = ifp->int_rdisc_timer;
89018316Swollman	}
89118316Swollman}
89218316Swollman
89318316Swollman
89418316Swollman/* check the IP header of a possible Router Discovery ICMP packet */
89518316Swollmanstatic struct interface *		/* 0 if bad */
89646303Smarkmck_icmp(const char *act,
89718316Swollman	naddr	from,
89819880Swollman	struct interface *ifp,
89918316Swollman	naddr	to,
90018316Swollman	union ad_u *p,
90118316Swollman	u_int	len)
90218316Swollman{
90346303Smarkm	const char *type;
90418316Swollman
90518316Swollman
90618316Swollman	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
90718316Swollman		type = "advertisement";
90818316Swollman	} else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
90918316Swollman		type = "solicitation";
91018316Swollman	} else {
91118316Swollman		return 0;
91218316Swollman	}
91318316Swollman
91418316Swollman	if (p->icmp.icmp_code != 0) {
91519880Swollman		trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
91618316Swollman			  type, p->icmp.icmp_code,
91718316Swollman			  naddr_ntoa(from), naddr_ntoa(to));
91818316Swollman		return 0;
91918316Swollman	}
92018316Swollman
92118316Swollman	trace_rdisc(act, from, to, ifp, p, len);
92218316Swollman
92318316Swollman	if (ifp == 0)
92418316Swollman		trace_pkt("unknown interface for router-discovery %s"
92518316Swollman			  " from %s to %s",
92618316Swollman			  type, naddr_ntoa(from), naddr_ntoa(to));
92718316Swollman
92818316Swollman	return ifp;
92918316Swollman}
93018316Swollman
93118316Swollman
93218316Swollman/* read packets from the router discovery socket
93318316Swollman */
93418316Swollmanvoid
93518316Swollmanread_d(void)
93618316Swollman{
93719880Swollman	static struct msg_limit bad_asize, bad_len;
93820606Swollman#ifdef USE_PASSIFNAME
93920606Swollman	static struct msg_limit  bad_name;
94020606Swollman#endif
94118316Swollman	struct sockaddr_in from;
94218316Swollman	int n, fromlen, cc, hlen;
94319880Swollman	struct {
94419880Swollman#ifdef USE_PASSIFNAME
94519880Swollman		char	ifname[IFNAMSIZ];
94619880Swollman#endif
94719880Swollman		union {
94819880Swollman			struct ip ip;
94919880Swollman			u_char	b[512];
95019880Swollman		} pkt;
95119880Swollman	} buf;
95218316Swollman	union ad_u *p;
95318316Swollman	n_long *wp;
95418316Swollman	struct interface *ifp;
95518316Swollman
95618316Swollman
95718316Swollman	for (;;) {
95818316Swollman		fromlen = sizeof(from);
95919880Swollman		cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
96018316Swollman			      (struct sockaddr*)&from,
96118316Swollman			      &fromlen);
96218316Swollman		if (cc <= 0) {
96318316Swollman			if (cc < 0 && errno != EWOULDBLOCK)
96418316Swollman				LOGERR("recvfrom(rdisc_sock)");
96518316Swollman			break;
96618316Swollman		}
96718316Swollman		if (fromlen != sizeof(struct sockaddr_in))
96818316Swollman			logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
96918316Swollman			       fromlen);
97019880Swollman#ifdef USE_PASSIFNAME
97119880Swollman		if ((cc -= sizeof(buf.ifname)) < 0)
97219880Swollman			logbad(0,"missing USE_PASSIFNAME; only %d bytes",
97319880Swollman			       cc+sizeof(buf.ifname));
97419880Swollman#endif
97518316Swollman
97619880Swollman		hlen = buf.pkt.ip.ip_hl << 2;
97718316Swollman		if (cc < hlen + ICMP_MINLEN)
97818316Swollman			continue;
97919880Swollman		p = (union ad_u *)&buf.pkt.b[hlen];
98018316Swollman		cc -= hlen;
98118316Swollman
98219880Swollman#ifdef USE_PASSIFNAME
98319880Swollman		ifp = ifwithname(buf.ifname, 0);
98420606Swollman		if (ifp == 0)
98520606Swollman			msglim(&bad_name, from.sin_addr.s_addr,
98620606Swollman			       "impossible rdisc if_ name %.*s",
98720606Swollman			       IFNAMSIZ, buf.ifname);
98819880Swollman#else
98919880Swollman		/* If we could tell the interface on which a packet from
99019880Swollman		 * address 0 arrived, we could deal with such solicitations.
99119880Swollman		 */
99219880Swollman		ifp = ((from.sin_addr.s_addr == 0)
99319880Swollman		       ? 0 : iflookup(from.sin_addr.s_addr));
99419880Swollman#endif
99520606Swollman		ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
99620606Swollman			      buf.pkt.ip.ip_dst.s_addr, p, cc);
99718316Swollman		if (ifp == 0)
99818316Swollman			continue;
99918316Swollman		if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
100020606Swollman			trace_pkt("    "
100120606Swollman				  "discard our own Router Discovery message");
100218316Swollman			continue;
100318316Swollman		}
100418316Swollman
100518316Swollman		switch (p->icmp.icmp_type) {
100618316Swollman		case ICMP_ROUTERADVERT:
100718316Swollman			if (p->ad.icmp_ad_asize*4
100846303Smarkm			    < (int)sizeof(p->ad.icmp_ad_info[0])) {
100919880Swollman				msglim(&bad_asize, from.sin_addr.s_addr,
101019880Swollman				       "intolerable rdisc address size=%d",
101119880Swollman				       p->ad.icmp_ad_asize);
101218316Swollman				continue;
101318316Swollman			}
101418316Swollman			if (p->ad.icmp_ad_num == 0) {
101519880Swollman				trace_pkt("    empty?");
101618316Swollman				continue;
101718316Swollman			}
101846303Smarkm			if (cc != (int)(sizeof(p->ad)
101946303Smarkm					- sizeof(p->ad.icmp_ad_info)
102046303Smarkm					+ (p->ad.icmp_ad_num
102146303Smarkm					   * sizeof(p->ad.icmp_ad_info[0])))) {
102219880Swollman				msglim(&bad_len, from.sin_addr.s_addr,
102319880Swollman				       "rdisc length %d does not match ad_num"
102419880Swollman				       " %d", cc, p->ad.icmp_ad_num);
102518316Swollman				continue;
102618316Swollman			}
102718316Swollman			if (supplier)
102818316Swollman				continue;
102918316Swollman			if (ifp->int_state & IS_NO_ADV_IN)
103018316Swollman				continue;
103118316Swollman
103218316Swollman			wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
103318316Swollman			for (n = 0; n < p->ad.icmp_ad_num; n++) {
103418316Swollman				parse_ad(from.sin_addr.s_addr,
103518316Swollman					 wp[0], wp[1],
103618316Swollman					 ntohs(p->ad.icmp_ad_life),
103718316Swollman					 ifp);
103818316Swollman				wp += p->ad.icmp_ad_asize;
103918316Swollman			}
104018316Swollman			break;
104118316Swollman
104218316Swollman
104318316Swollman		case ICMP_ROUTERSOLICIT:
104418316Swollman			if (!supplier)
104518316Swollman				continue;
104618316Swollman			if (ifp->int_state & IS_NO_ADV_OUT)
104718316Swollman				continue;
104846303Smarkm			if (stopint)
104946303Smarkm				continue;
105018316Swollman
105118316Swollman			/* XXX
105218316Swollman			 * We should handle messages from address 0.
105318316Swollman			 */
105418316Swollman
105518316Swollman			/* Respond with a point-to-point advertisement */
105618316Swollman			send_adv(ifp, from.sin_addr.s_addr, 0);
105718316Swollman			break;
105818316Swollman		}
105918316Swollman	}
106018316Swollman
106118316Swollman	rdisc_sort();
106218316Swollman}
1063