118334Speter/*
290277Sobrien * Copyright (c) 1995
390277Sobrien *	The Regents of the University of California.  All rights reserved.
418334Speter *
590277Sobrien * Redistribution and use in source and binary forms, with or without
618334Speter * modification, are permitted provided that the following conditions
790277Sobrien * are met:
890277Sobrien * 1. Redistributions of source code must retain the above copyright
990277Sobrien *    notice, this list of conditions and the following disclaimer.
1090277Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer in the
1290277Sobrien *    documentation and/or other materials provided with the distribution.
1390277Sobrien * 4. Neither the name of the University nor the names of its contributors
1490277Sobrien *    may be used to endorse or promote products derived from this software
1590277Sobrien *    without specific prior written permission.
1618334Speter *
1718334Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1890277Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1990277Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2090277Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2118334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2218334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2318334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2418334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2551232Sbde * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2651232Sbde * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2718334Speter * SUCH DAMAGE.
2818334Speter *
2918334Speter * $FreeBSD: releng/10.2/sbin/routed/rdisc.c 190718 2009-04-05 17:33:07Z phk $
3018334Speter */
3118334Speter
3218334Speter#include "defs.h"
3318334Speter#include <netinet/in_systm.h>
3418334Speter#include <netinet/ip.h>
3518334Speter#include <netinet/ip_icmp.h>
3690277Sobrien
3790277Sobrien#ifdef __NetBSD__
3890277Sobrien__RCSID("$NetBSD$");
3990277Sobrien#elif defined(__FreeBSD__)
4090277Sobrien__RCSID("$FreeBSD: releng/10.2/sbin/routed/rdisc.c 190718 2009-04-05 17:33:07Z phk $");
4190277Sobrien#else
4290277Sobrien__RCSID("$Revision: 2.27 $");
4390277Sobrien#ident "$Revision: 2.27 $"
4490277Sobrien#endif
4590277Sobrien
4690277Sobrien/* router advertisement ICMP packet */
4790277Sobrienstruct icmp_ad {
4890277Sobrien	u_int8_t    icmp_type;		/* type of message */
4990277Sobrien	u_int8_t    icmp_code;		/* type sub code */
5090277Sobrien	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
5190277Sobrien	u_int8_t    icmp_ad_num;	/* # of following router addresses */
5290277Sobrien	u_int8_t    icmp_ad_asize;	/* 2--words in each advertisement */
5390277Sobrien	u_int16_t   icmp_ad_life;	/* seconds of validity */
5490277Sobrien	struct icmp_ad_info {
5590277Sobrien	    n_long  icmp_ad_addr;
5690277Sobrien	    n_long  icmp_ad_pref;
5790277Sobrien	} icmp_ad_info[1];
5890277Sobrien};
5990277Sobrien
6090277Sobrien/* router solicitation ICMP packet */
6190277Sobrienstruct icmp_so {
6290277Sobrien	u_int8_t    icmp_type;		/* type of message */
6390277Sobrien	u_int8_t    icmp_code;		/* type sub code */
6490277Sobrien	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
6590277Sobrien	n_long	    icmp_so_rsvd;
6690277Sobrien};
6790277Sobrien
6890277Sobrienunion ad_u {
6990277Sobrien	struct icmp icmp;
7090277Sobrien	struct icmp_ad ad;
7190277Sobrien	struct icmp_so so;
7290277Sobrien};
7390277Sobrien
7490277Sobrien
7550599Sobrienint	rdisc_sock = -1;		/* router-discovery raw socket */
7650599Sobrienstatic const struct interface *rdisc_sock_mcast; /* current multicast interface */
7750599Sobrien
7890277Sobrienstruct timeval rdisc_timer;
7990277Sobrienint rdisc_ok;				/* using solicited route */
8090277Sobrien
8150599Sobrien
8252520Sobrien#define MAX_ADS 16			/* at least one per interface */
8352520Sobrienstruct dr {				/* accumulated advertisements */
8490277Sobrien    struct interface *dr_ifp;
8590277Sobrien    naddr   dr_gate;			/* gateway */
8618334Speter    time_t  dr_ts;			/* when received */
8790277Sobrien    time_t  dr_life;			/* lifetime in host byte order */
8890277Sobrien    n_long  dr_recv_pref;		/* received but biased preference */
8918334Speter    n_long  dr_pref;			/* preference adjusted by metric */
9090277Sobrien};
9190277Sobrienstatic const struct dr *cur_drp;
9290277Sobrienstatic struct dr drs[MAX_ADS];
9318334Speter
9490277Sobrien/* convert between signed, balanced around zero,
9590277Sobrien * and unsigned zero-based preferences */
9690277Sobrien#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
9790277Sobrien#define UNSIGN_PREF(p) SIGN_PREF(p)
9818334Speter/* adjust unsigned preference by interface metric,
9918334Speter * without driving it to infinity */
10090277Sobrien#define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\
10190277Sobrien		      ? ((p) != 0 ? 1 : 0)				    \
10290277Sobrien		      : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric))
10318334Speter
10490277Sobrienstatic void rdisc_sort(void);
10518334Speter
10618334Speter
10790277Sobrien/* dump an ICMP Router Discovery Advertisement Message
10890277Sobrien */
10990277Sobrienstatic void
11090277Sobrientrace_rdisc(const char	*act,
11190277Sobrien	    naddr	from,
11218334Speter	    naddr	to,
11318334Speter	    struct interface *ifp,
11490277Sobrien	    union ad_u	*p,
11590277Sobrien	    u_int	len)
11690277Sobrien{
11790277Sobrien	int i;
11890277Sobrien	n_long *wp, *lim;
11990277Sobrien
12090277Sobrien
12118334Speter	if (!TRACEPACKETS || ftrace == 0)
12290277Sobrien		return;
12352520Sobrien
12490277Sobrien	lastlog();
12590277Sobrien
12690277Sobrien	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
12790277Sobrien		(void)fprintf(ftrace, "%s Router Ad"
12890277Sobrien			      " from %s to %s via %s life=%d\n",
12950599Sobrien			      act, naddr_ntoa(from), naddr_ntoa(to),
13050599Sobrien			      ifp ? ifp->int_name : "?",
13118334Speter			      ntohs(p->ad.icmp_ad_life));
13218334Speter		if (!TRACECONTENTS)
13318334Speter			return;
13418334Speter
13518334Speter		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
13618334Speter		lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
13718334Speter		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
13890277Sobrien			(void)fprintf(ftrace, "\t%s preference=%d",
13990277Sobrien				      naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
14090277Sobrien			wp += p->ad.icmp_ad_asize;
14190277Sobrien		}
14290277Sobrien		(void)fputc('\n',ftrace);
14390277Sobrien
14490277Sobrien	} else {
14590277Sobrien		trace_act("%s Router Solic. from %s to %s via %s value=%#x",
14690277Sobrien			  act, naddr_ntoa(from), naddr_ntoa(to),
14790277Sobrien			  ifp ? ifp->int_name : "?",
14818334Speter			  (int)ntohl(p->so.icmp_so_rsvd));
14918334Speter	}
15018334Speter}
15118334Speter
15218334Speter/* prepare Router Discovery socket.
15318334Speter */
15418334Speterstatic void
15518334Speterget_rdisc_sock(void)
15652520Sobrien{
15718334Speter	if (rdisc_sock < 0) {
15850599Sobrien		rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
15918334Speter		if (rdisc_sock < 0)
16052520Sobrien			BADERR(1,"rdisc_sock = socket()");
16118334Speter		fix_sock(rdisc_sock,"rdisc_sock");
16218334Speter		fix_select();
16318334Speter	}
16418334Speter}
16518334Speter
16618334Speter
167104763Skan/* Pick multicast group for router-discovery socket
168104763Skan */
169104763Skanvoid
170104763Skanset_rdisc_mg(struct interface *ifp,
171104763Skan	     int on)			/* 0=turn it off */
17218334Speter{
17318334Speter	struct group_req gr;
17418334Speter	struct sockaddr_in *sin;
17518334Speter
17618334Speter	assert(ifp != NULL);
17750599Sobrien
17850599Sobrien	if (rdisc_sock < 0) {
17950599Sobrien		/* Create the raw socket so that we can hear at least
18050599Sobrien		 * broadcast router discovery packets.
18150599Sobrien		 */
18218334Speter		if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
18318334Speter		    || !on)
18418334Speter			return;
18518334Speter		get_rdisc_sock();
18690277Sobrien	}
18790277Sobrien
18890277Sobrien	if (!(ifp->int_if_flags & IFF_MULTICAST)) {
18990277Sobrien		ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
19090277Sobrien		return;
19190277Sobrien	}
19290277Sobrien
19390277Sobrien	memset(&gr, 0, sizeof(gr));
19490277Sobrien	gr.gr_interface = ifp->int_index;
19590277Sobrien	sin = (struct sockaddr_in *)&gr.gr_group;
19690277Sobrien	sin->sin_family = AF_INET;
19790277Sobrien#ifdef _HAVE_SIN_LEN
19890277Sobrien	sin->sin_len = sizeof(struct sockaddr_in);
19990277Sobrien#endif
20090277Sobrien
20190277Sobrien	if (supplier
20218334Speter	    || (ifp->int_state & IS_NO_ADV_IN)
20318334Speter	    || !on) {
20418334Speter		/* stop listening to advertisements
20518334Speter		 */
20618334Speter		if (ifp->int_state & IS_ALL_HOSTS) {
20718334Speter			sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
20818334Speter			if (setsockopt(rdisc_sock, IPPROTO_IP,
20990277Sobrien				       MCAST_LEAVE_GROUP,
21018334Speter				       &gr, sizeof(gr)) < 0)
21118334Speter				LOGERR("MCAST_LEAVE_GROUP ALLHOSTS");
21218334Speter			ifp->int_state &= ~IS_ALL_HOSTS;
213117413Skan		}
21418334Speter
21518334Speter	} else if (!(ifp->int_state & IS_ALL_HOSTS)) {
21618334Speter		/* start listening to advertisements
21752520Sobrien		 */
21818334Speter		sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
21918334Speter		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
22018334Speter			       &gr, sizeof(gr)) < 0) {
22118334Speter			LOGERR("MCAST_JOIN_GROUP ALLHOSTS");
22218334Speter		} else {
22390277Sobrien			ifp->int_state |= IS_ALL_HOSTS;
22418334Speter		}
22590277Sobrien	}
22618334Speter
22718334Speter	if (!supplier
22890277Sobrien	    || (ifp->int_state & IS_NO_ADV_OUT)
22990277Sobrien	    || !on) {
23090277Sobrien		/* stop listening to solicitations
23190277Sobrien		 */
23290277Sobrien		if (ifp->int_state & IS_ALL_ROUTERS) {
23390277Sobrien			sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
23490277Sobrien			if (setsockopt(rdisc_sock, IPPROTO_IP,
23590277Sobrien				       MCAST_LEAVE_GROUP,
23690277Sobrien				       &gr, sizeof(gr)) < 0)
23790277Sobrien				LOGERR("MCAST_LEAVE_GROUP ALLROUTERS");
23890277Sobrien			ifp->int_state &= ~IS_ALL_ROUTERS;
23990277Sobrien		}
24090277Sobrien
24190277Sobrien	} else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
242117413Skan		/* start hearing solicitations
24318334Speter		 */
244117413Skan		sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
24518334Speter		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
24618334Speter			       &gr, sizeof(gr)) < 0) {
24790277Sobrien			LOGERR("MCAST_JOIN_GROUP ALLROUTERS");
24890277Sobrien		} else {
24990277Sobrien			ifp->int_state |= IS_ALL_ROUTERS;
25090277Sobrien		}
25118334Speter	}
25218334Speter}
25318334Speter
25418334Speter
25518334Speter/* start supplying routes
25618334Speter */
25718334Spetervoid
25818334Speterset_supplier(void)
25918334Speter{
26018334Speter	struct interface *ifp;
26118334Speter	struct dr *drp;
26290277Sobrien
26390277Sobrien	if (supplier_set)
26490277Sobrien		return;
26590277Sobrien
26690277Sobrien	trace_act("start supplying routes");
26718334Speter
26818334Speter	/* Forget discovered routes.
26918334Speter	 */
27018334Speter	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
27190277Sobrien		drp->dr_recv_pref = 0;
27290277Sobrien		drp->dr_life = 0;
27390277Sobrien	}
27490277Sobrien	rdisc_age(0);
27590277Sobrien
27690277Sobrien	supplier_set = 1;
27790277Sobrien	supplier = 1;
27890277Sobrien
27990277Sobrien	/* Do not start advertising until we have heard some RIP routes */
280104763Skan	LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
281104763Skan
28290277Sobrien	/* Switch router discovery multicast groups from soliciting
283104763Skan	 * to advertising.
28490277Sobrien	 */
28590277Sobrien	LIST_FOREACH(ifp, &ifnet, int_list) {
28690277Sobrien		if (ifp->int_state & IS_BROKE)
28790277Sobrien			continue;
28890277Sobrien		ifp->int_rdisc_cnt = 0;
28990277Sobrien		ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
29090277Sobrien		ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
29190277Sobrien		set_rdisc_mg(ifp, 1);
292117413Skan	}
293117413Skan
294117413Skan	/* get rid of any redirects */
29590277Sobrien	del_redirects(0,0);
29690277Sobrien}
29790277Sobrien
298117413Skan
299117413Skan/* age discovered routes and find the best one
30090277Sobrien */
30190277Sobrienvoid
30290277Sobrienrdisc_age(naddr bad_gate)
30390277Sobrien{
30490277Sobrien	time_t sec;
30590277Sobrien	struct dr *drp;
30690277Sobrien
30790277Sobrien
30890277Sobrien	/* If only advertising, then do only that. */
30990277Sobrien	if (supplier) {
31090277Sobrien		/* If switching from client to server, get rid of old
31152520Sobrien		 * default routes.
31290277Sobrien		 */
31352520Sobrien		if (cur_drp != 0)
31490277Sobrien			rdisc_sort();
31590277Sobrien		rdisc_adv();
31690277Sobrien		return;
31790277Sobrien	}
31890277Sobrien
31990277Sobrien	/* If we are being told about a bad router,
320117413Skan	 * then age the discovered default route, and if there is
32190277Sobrien	 * no alternative, solicit a replacement.
32290277Sobrien	 */
32390277Sobrien	if (bad_gate != 0) {
32490277Sobrien		/* Look for the bad discovered default route.
32590277Sobrien		 * Age it and note its interface.
32690277Sobrien		 */
32790277Sobrien		for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
32890277Sobrien			if (drp->dr_ts == 0)
32990277Sobrien				continue;
33090277Sobrien
331117413Skan			/* When we find the bad router, then age the route
332117413Skan			 * to at most SUPPLY_INTERVAL.
333117413Skan			 * This is contrary to RFC 1256, but defends against
33490277Sobrien			 * black holes.
33590277Sobrien			 */
33618334Speter			if (drp->dr_gate == bad_gate) {
33790277Sobrien				sec = (now.tv_sec - drp->dr_life
33818334Speter				       + SUPPLY_INTERVAL);
33918334Speter				if (drp->dr_ts > sec) {
34018334Speter					trace_act("age 0.0.0.0 --> %s via %s",
34118334Speter						  naddr_ntoa(drp->dr_gate),
34218334Speter						  drp->dr_ifp->int_name);
34318334Speter					drp->dr_ts = sec;
34418334Speter				}
34518334Speter				break;
34618334Speter			}
34718334Speter		}
34818334Speter	}
34918334Speter
35018334Speter	rdisc_sol();
35118334Speter	rdisc_sort();
35218334Speter
35318334Speter	/* Delete old redirected routes to keep the kernel table small,
35418334Speter	 * and to prevent black holes.  Check that the kernel table
35590277Sobrien	 * matches the daemon table (i.e. has the default route).
35650599Sobrien	 * But only if RIP is not running and we are not dealing with
35750599Sobrien	 * a bad gateway, since otherwise age() will be called.
35850599Sobrien	 */
35950599Sobrien	if (rip_sock < 0 && bad_gate == 0)
36050599Sobrien		age(0);
36150599Sobrien}
36250599Sobrien
36390277Sobrien
36490277Sobrien/* Zap all routes discovered via an interface that has gone bad
36590277Sobrien *	This should only be called when !(ifp->int_state & IS_ALIAS)
36690277Sobrien */
36790277Sobrienvoid
36850599Sobrienif_bad_rdisc(struct interface *ifp)
36950599Sobrien{
37050599Sobrien	struct dr *drp;
37150599Sobrien
37250599Sobrien	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
37350599Sobrien		if (drp->dr_ifp != ifp)
37450599Sobrien			continue;
37550599Sobrien		drp->dr_recv_pref = 0;
37650599Sobrien		drp->dr_ts = 0;
37750599Sobrien		drp->dr_life = 0;
37850599Sobrien	}
37950599Sobrien
38090277Sobrien	/* make a note to re-solicit, turn RIP on or off, etc. */
38190277Sobrien	rdisc_timer.tv_sec = 0;
38290277Sobrien}
38390277Sobrien
38490277Sobrien
38590277Sobrien/* mark an interface ok for router discovering.
38690277Sobrien */
38790277Sobrienvoid
38890277Sobrienif_ok_rdisc(struct interface *ifp)
38990277Sobrien{
39018334Speter	set_rdisc_mg(ifp, 1);
39118334Speter
39218334Speter	ifp->int_rdisc_cnt = 0;
39318334Speter	ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
39418334Speter						    ? MIN_WAITTIME
39518334Speter						    : MAX_SOLICITATION_DELAY);
39618334Speter	if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
39718334Speter		rdisc_timer = ifp->int_rdisc_timer;
39818334Speter}
39918334Speter
40018334Speter
40118334Speter/* get rid of a dead discovered router
40218334Speter */
40318334Speterstatic void
40418334Speterdel_rdisc(struct dr *drp)
40518334Speter{
40650599Sobrien	struct interface *ifp;
40790277Sobrien	naddr gate;
40890277Sobrien	int i;
40990277Sobrien
41090277Sobrien
41190277Sobrien	del_redirects(gate = drp->dr_gate, 0);
41290277Sobrien	drp->dr_ts = 0;
41390277Sobrien	drp->dr_life = 0;
41418334Speter
41518334Speter
41618334Speter	/* Count the other discovered routes on the interface.
41718334Speter	 */
41818334Speter	i = 0;
41918334Speter	ifp = drp->dr_ifp;
42018334Speter	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
42118334Speter		if (drp->dr_ts != 0
42218334Speter		    && drp->dr_ifp == ifp)
42318334Speter			i++;
42418334Speter	}
425117413Skan
42618334Speter	/* If that was the last good discovered router on the interface,
42718334Speter	 * then solicit a new one.
42818334Speter	 * This is contrary to RFC 1256, but defends against black holes.
42918334Speter	 */
43018334Speter	if (i != 0) {
43190277Sobrien		trace_act("discovered router %s via %s"
43218334Speter			  " is bad--have %d remaining",
43390277Sobrien			  naddr_ntoa(gate), ifp->int_name, i);
43490277Sobrien	} else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
43590277Sobrien		trace_act("last discovered router %s via %s"
43618334Speter			  " is bad--re-solicit",
43718334Speter			  naddr_ntoa(gate), ifp->int_name);
43818334Speter		ifp->int_rdisc_cnt = 0;
43918334Speter		ifp->int_rdisc_timer.tv_sec = 0;
44050599Sobrien		rdisc_sol();
44118334Speter	} else {
44218334Speter		trace_act("last discovered router %s via %s"
44318334Speter			  " is bad--wait to solicit",
44418334Speter			  naddr_ntoa(gate), ifp->int_name);
44518334Speter	}
44690277Sobrien}
44790277Sobrien
44818334Speter
44918334Speter/* Find the best discovered route,
45090277Sobrien * and discard stale routers.
45118334Speter */
45218334Speterstatic void
45318334Speterrdisc_sort(void)
45418334Speter{
45518334Speter	struct dr *drp, *new_drp;
45618334Speter	struct rt_entry *rt;
457117413Skan	struct rt_spare new;
458117413Skan	struct interface *ifp;
459117413Skan	u_int new_st = 0;
460117413Skan	n_long new_pref = 0;
461117413Skan
462117413Skan
46318334Speter	/* Find the best discovered route.
46418334Speter	 */
46518334Speter	new_drp = 0;
46618334Speter	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
46790277Sobrien		if (drp->dr_ts == 0)
46818334Speter			continue;
46918334Speter		ifp = drp->dr_ifp;
47018334Speter
47150599Sobrien		/* Get rid of expired discovered routers.
47290277Sobrien		 */
47390277Sobrien		if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
47490277Sobrien			del_rdisc(drp);
47590277Sobrien			continue;
47618334Speter		}
47718334Speter
47818334Speter		LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
47918334Speter
48090277Sobrien		/* Update preference with possibly changed interface
48190277Sobrien		 * metric.
48290277Sobrien		 */
48390277Sobrien		drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
48418334Speter
48518334Speter		/* Prefer the current route to prevent thrashing.
48618334Speter		 * Prefer shorter lifetimes to speed the detection of
48718334Speter		 * bad routers.
48818334Speter		 * Avoid sick interfaces.
48918334Speter		 */
49050599Sobrien		if (new_drp == 0
49150599Sobrien		    || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
49218334Speter			&& (new_pref < drp->dr_pref
49318334Speter			    || (new_pref == drp->dr_pref
49418334Speter				&& (drp == cur_drp
49518334Speter				    || (new_drp != cur_drp
49618334Speter					&& new_drp->dr_life > drp->dr_life)))))
49718334Speter		    || ((new_st & IS_SICK)
49818334Speter			&& !(drp->dr_ifp->int_state & IS_SICK))) {
49918334Speter			    new_drp = drp;
50018334Speter			    new_st = drp->dr_ifp->int_state;
50118334Speter			    new_pref = drp->dr_pref;
50218334Speter		}
50318334Speter	}
50418334Speter
50550599Sobrien	/* switch to a better default route
50650599Sobrien	 */
50750599Sobrien	if (new_drp != cur_drp) {
50818334Speter		rt = rtget(RIP_DEFAULT, 0);
50918334Speter
51018334Speter		/* Stop using discovered routes if they are all bad
51118334Speter		 */
51218334Speter		if (new_drp == 0) {
51318334Speter			trace_act("turn off Router Discovery client");
51418334Speter			rdisc_ok = 0;
51518334Speter
51618334Speter			if (rt != 0
51718334Speter			    && (rt->rt_state & RS_RDISC)) {
51818334Speter				new = rt->rt_spares[0];
51918334Speter				new.rts_metric = HOPCNT_INFINITY;
52018334Speter				new.rts_time = now.tv_sec - GARBAGE_TIME;
52196285Sobrien				rtchange(rt, rt->rt_state & ~RS_RDISC,
52218334Speter					 &new, 0);
52318334Speter				rtswitch(rt, 0);
52418334Speter			}
52518334Speter
52618334Speter		} else {
52718334Speter			if (cur_drp == 0) {
52818334Speter				trace_act("turn on Router Discovery client"
52918334Speter					  " using %s via %s",
53018334Speter					  naddr_ntoa(new_drp->dr_gate),
53118334Speter					  new_drp->dr_ifp->int_name);
53218334Speter				rdisc_ok = 1;
53318334Speter
53418334Speter			} else {
53518334Speter				trace_act("switch Router Discovery from"
53618334Speter					  " %s via %s to %s via %s",
53718334Speter					  naddr_ntoa(cur_drp->dr_gate),
53818334Speter					  cur_drp->dr_ifp->int_name,
53918334Speter					  naddr_ntoa(new_drp->dr_gate),
54018334Speter					  new_drp->dr_ifp->int_name);
54118334Speter			}
54218334Speter
54318334Speter			memset(&new, 0, sizeof(new));
54418334Speter			new.rts_ifp = new_drp->dr_ifp;
54518334Speter			new.rts_gate = new_drp->dr_gate;
54618334Speter			new.rts_router = new_drp->dr_gate;
54718334Speter			new.rts_metric = HOPCNT_INFINITY-1;
54818334Speter			new.rts_time = now.tv_sec;
54918334Speter			if (rt != 0) {
55018334Speter				rtchange(rt, rt->rt_state | RS_RDISC, &new, 0);
55118334Speter			} else {
55218334Speter				rtadd(RIP_DEFAULT, 0, RS_RDISC, &new);
55318334Speter			}
55418334Speter		}
55518334Speter
55618334Speter		cur_drp = new_drp;
55718334Speter	}
55818334Speter
55918334Speter	/* turn RIP on or off */
56018334Speter	if (!rdisc_ok || rip_interfaces > 1) {
56118334Speter		rip_on(0);
56218334Speter	} else {
56318334Speter		rip_off();
56418334Speter	}
56518334Speter}
56618334Speter
56718334Speter
56818334Speter/* handle a single address in an advertisement
56950599Sobrien */
57018334Speterstatic void
57150599Sobrienparse_ad(naddr from,
57218334Speter	 naddr gate,
57318334Speter	 n_long pref,			/* signed and in network order */
57418334Speter	 u_short life,			/* in host byte order */
57518334Speter	 struct interface *ifp)
57618334Speter{
57718334Speter	static struct msg_limit bad_gate;
57818334Speter	struct dr *drp, *new_drp;
57918334Speter
58018334Speter
58150599Sobrien	if (gate == RIP_DEFAULT
58250599Sobrien	    || !check_dst(gate)) {
58318334Speter		msglim(&bad_gate, from,"router %s advertising bad gateway %s",
58418334Speter		       naddr_ntoa(from),
58518334Speter		       naddr_ntoa(gate));
58618334Speter		return;
58718334Speter	}
58818334Speter
58918334Speter	/* ignore pointers to ourself and routes via unreachable networks
59018334Speter	 */
59118334Speter	if (ifwithaddr(gate, 1, 0) != 0) {
59250599Sobrien		trace_pkt("    discard Router Discovery Ad pointing at us");
59350599Sobrien		return;
59418334Speter	}
59518334Speter	if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
59690277Sobrien		trace_pkt("    discard Router Discovery Ad"
59790277Sobrien			  " toward unreachable net");
59890277Sobrien		return;
59990277Sobrien	}
60090277Sobrien
60190277Sobrien	/* Convert preference to an unsigned value
60290277Sobrien	 * and later bias it by the metric of the interface.
60390277Sobrien	 */
60490277Sobrien	pref = UNSIGN_PREF(ntohl(pref));
60590277Sobrien
60690277Sobrien	if (pref == 0 || life < MinMaxAdvertiseInterval) {
60790277Sobrien		pref = 0;
60890277Sobrien		life = 0;
60990277Sobrien	}
61090277Sobrien
61190277Sobrien	for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
61290277Sobrien		/* accept new info for a familiar entry
61390277Sobrien		 */
61490277Sobrien		if (drp->dr_gate == gate) {
61590277Sobrien			new_drp = drp;
61690277Sobrien			break;
61718334Speter		}
61890277Sobrien
61990277Sobrien		if (life == 0)
62096285Sobrien			continue;	/* do not worry about dead ads */
62196285Sobrien
62296285Sobrien		if (drp->dr_ts == 0) {
62396285Sobrien			new_drp = drp;	/* use unused entry */
62496285Sobrien
62596285Sobrien		} else if (new_drp == 0) {
62696285Sobrien			/* look for an entry worse than the new one to
62790277Sobrien			 * reuse.
62890277Sobrien			 */
62990277Sobrien			if ((!(ifp->int_state & IS_SICK)
63090277Sobrien			     && (drp->dr_ifp->int_state & IS_SICK))
63190277Sobrien			    || (pref > drp->dr_pref
63290277Sobrien				&& !((ifp->int_state ^ drp->dr_ifp->int_state)
63390277Sobrien				     & IS_SICK)))
63490277Sobrien				new_drp = drp;
63590277Sobrien
63690277Sobrien		} else if (new_drp->dr_ts != 0) {
63790277Sobrien			/* look for the least valuable entry to reuse
63896285Sobrien			 */
63990277Sobrien			if ((!(new_drp->dr_ifp->int_state & IS_SICK)
64090277Sobrien			     && (drp->dr_ifp->int_state & IS_SICK))
64190277Sobrien			    || (new_drp->dr_pref > drp->dr_pref
64290277Sobrien				&& !((new_drp->dr_ifp->int_state
64390277Sobrien				      ^ drp->dr_ifp->int_state)
64490277Sobrien				     & IS_SICK)))
64590277Sobrien				new_drp = drp;
64690277Sobrien		}
64790277Sobrien	}
64890277Sobrien
64990277Sobrien	/* forget it if all of the current entries are better */
65090277Sobrien	if (new_drp == 0)
65190277Sobrien		return;
652117413Skan
653117413Skan	new_drp->dr_ifp = ifp;
654117413Skan	new_drp->dr_gate = gate;
655117413Skan	new_drp->dr_ts = now.tv_sec;
656117413Skan	new_drp->dr_life = life;
65790277Sobrien	new_drp->dr_recv_pref = pref;
65890277Sobrien	/* bias functional preference by metric of the interface */
65990277Sobrien	new_drp->dr_pref = PREF(pref,ifp);
66090277Sobrien
66196285Sobrien	/* after hearing a good advertisement, stop asking
66290277Sobrien	 */
66390277Sobrien	if (!(ifp->int_state & IS_SICK))
66490277Sobrien		ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
66590277Sobrien}
66690277Sobrien
66790277Sobrien
66890277Sobrien/* Compute the IP checksum
66990277Sobrien *	This assumes the packet is less than 32K long.
67090277Sobrien */
67190277Sobrienstatic u_short
67290277Sobrienin_cksum(u_short *p,
673117413Skan	 u_int len)
67490277Sobrien{
67590277Sobrien	u_int sum = 0;
67690277Sobrien	int nwords = len >> 1;
67790277Sobrien
67890277Sobrien	while (nwords-- != 0)
67990277Sobrien		sum += *p++;
68090277Sobrien
681117413Skan	if (len & 1)
682117413Skan		sum += *(u_char *)p;
683117413Skan
68490277Sobrien	/* end-around-carry */
685117413Skan	sum = (sum >> 16) + (sum & 0xffff);
68690277Sobrien	sum += (sum >> 16);
687107594Sobrien	return (~sum);
688107594Sobrien}
689107594Sobrien
69090277Sobrien
69190277Sobrien/* Send a router discovery advertisement or solicitation ICMP packet.
692117413Skan */
693117413Skanstatic void
694107594Sobriensend_rdisc(union ad_u *p,
695107594Sobrien	   int p_size,
696107594Sobrien	   struct interface *ifp,
69796285Sobrien	   naddr dst,			/* 0 or unicast destination */
698117413Skan	   int	type)			/* 0=unicast, 1=bcast, 2=mcast */
69990277Sobrien{
700117413Skan	struct sockaddr_in rsin;
70196285Sobrien	int flags;
70290277Sobrien	const char *msg;
70390277Sobrien
704117413Skan
705117413Skan	memset(&rsin, 0, sizeof(rsin));
706117413Skan	rsin.sin_addr.s_addr = dst;
707117413Skan	rsin.sin_family = AF_INET;
70890277Sobrien#ifdef _HAVE_SIN_LEN
709117413Skan	rsin.sin_len = sizeof(rsin);
710117413Skan#endif
71190277Sobrien	flags = MSG_DONTROUTE;
712117413Skan
713117413Skan	switch (type) {
714117413Skan	case 0:				/* unicast */
715117413Skan	default:
71690277Sobrien		msg = "Send";
71790277Sobrien		break;
71890277Sobrien
71990277Sobrien	case 1:				/* broadcast */
720117413Skan		if (ifp->int_if_flags & IFF_POINTOPOINT) {
721117413Skan			msg = "Send pt-to-pt";
722117413Skan			rsin.sin_addr.s_addr = ifp->int_dstaddr;
72390277Sobrien		} else {
72490277Sobrien			msg = "Send broadcast";
72590277Sobrien			rsin.sin_addr.s_addr = ifp->int_brdaddr;
72690277Sobrien		}
72790277Sobrien		break;
72890277Sobrien
72990277Sobrien	case 2:				/* multicast */
73090277Sobrien		msg = "Send multicast";
73190277Sobrien		if (ifp->int_state & IS_DUP) {
73290277Sobrien			trace_act("abort multicast output via %s"
73390277Sobrien				  " with duplicate address",
73450599Sobrien				  ifp->int_name);
73550599Sobrien			return;
73650599Sobrien		}
73750599Sobrien		if (rdisc_sock_mcast != ifp) {
73890277Sobrien			/* select the right interface. */
73990277Sobrien			struct ip_mreqn mreqn;
74090277Sobrien
74190277Sobrien			memset(&mreqn, 0, sizeof(struct ip_mreqn));
74250599Sobrien			mreqn.imr_ifindex = ifp->int_index;
74350599Sobrien			if (0 > setsockopt(rdisc_sock,
74450599Sobrien					   IPPROTO_IP, IP_MULTICAST_IF,
74550599Sobrien					   &mreqn,
74650599Sobrien					   sizeof(mreqn))) {
74750599Sobrien				LOGERR("setsockopt(rdisc_sock,"
74850599Sobrien				       "IP_MULTICAST_IF)");
74950599Sobrien				rdisc_sock_mcast = 0;
75090277Sobrien				return;
75150599Sobrien			}
752117413Skan			rdisc_sock_mcast = ifp;
753117413Skan		}
754117413Skan		flags = 0;
755117413Skan		break;
756117413Skan	}
757117413Skan
75890277Sobrien	if (rdisc_sock < 0)
75990277Sobrien		get_rdisc_sock();
76050599Sobrien
76152520Sobrien	trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp,
76250599Sobrien		    p, p_size);
76350599Sobrien
76450599Sobrien	if (0 > sendto(rdisc_sock, p, p_size, flags,
76550599Sobrien		       (struct sockaddr *)&rsin, sizeof(rsin))) {
76650599Sobrien		if (ifp == 0 || !(ifp->int_state & IS_BROKE))
76750599Sobrien			msglog("sendto(%s%s%s): %s",
76818334Speter			       ifp != 0 ? ifp->int_name : "",
76918334Speter			       ifp != 0 ? ", " : "",
77018334Speter			       inet_ntoa(rsin.sin_addr),
77118334Speter			       strerror(errno));
77218334Speter		if (ifp != 0)
77318334Speter			if_sick(ifp);
77490277Sobrien	}
77550599Sobrien}
77650599Sobrien
77750599Sobrien
77850599Sobrien/* Send an advertisement
77950599Sobrien */
78050599Sobrienstatic void
78150599Sobriensend_adv(struct interface *ifp,
78250599Sobrien	 naddr	dst,			/* 0 or unicast destination */
78350599Sobrien	 int	type)			/* 0=unicast, 1=bcast, 2=mcast */
78450599Sobrien{
78518334Speter	union ad_u u;
78618334Speter	n_long pref;
78718334Speter
78818334Speter
78952520Sobrien	memset(&u, 0, sizeof(u.ad));
79018334Speter
79118334Speter	u.ad.icmp_type = ICMP_ROUTERADVERT;
79290277Sobrien	u.ad.icmp_ad_num = 1;
79390277Sobrien	u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
79490277Sobrien
79590277Sobrien	u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
79690277Sobrien
79718334Speter	/* Convert the configured preference to an unsigned value,
79818334Speter	 * bias it by the interface metric, and then send it as a
79918334Speter	 * signed, network byte order value.
80018334Speter	 */
80118334Speter	pref = UNSIGN_PREF(ifp->int_rdisc_pref);
80218334Speter	u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp)));
80318334Speter
80418334Speter	u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
80518334Speter
80618334Speter	u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
80718334Speter
80818334Speter	send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
80918334Speter}
81018334Speter
81118334Speter
81218334Speter/* Advertise for Router Discovery
81318334Speter */
81418334Spetervoid
81518334Speterrdisc_adv(void)
81690277Sobrien{
81718334Speter	struct interface *ifp;
81850599Sobrien
81950599Sobrien	if (!supplier)
82050599Sobrien		return;
82150599Sobrien
82290277Sobrien	rdisc_timer.tv_sec = now.tv_sec + NEVER;
82390277Sobrien
82490277Sobrien	LIST_FOREACH(ifp, &ifnet, int_list) {
82590277Sobrien		if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
82690277Sobrien			continue;
82790277Sobrien
82890277Sobrien		if (!timercmp(&ifp->int_rdisc_timer, &now, >)
82990277Sobrien		    || stopint) {
83090277Sobrien			send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
83190277Sobrien				 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
83290277Sobrien			ifp->int_rdisc_cnt++;
83350599Sobrien
83490277Sobrien			intvl_random(&ifp->int_rdisc_timer,
83518334Speter				     (ifp->int_rdisc_int*3)/4,
83690277Sobrien				     ifp->int_rdisc_int);
837117413Skan			if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
838117413Skan			    && (ifp->int_rdisc_timer.tv_sec
83990277Sobrien				> MAX_INITIAL_ADVERT_INTERVAL)) {
840117413Skan				ifp->int_rdisc_timer.tv_sec
841117413Skan				= MAX_INITIAL_ADVERT_INTERVAL;
842117413Skan			}
84396285Sobrien			timevaladd(&ifp->int_rdisc_timer, &now);
84496285Sobrien		}
845117413Skan
846117413Skan		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
84790277Sobrien			rdisc_timer = ifp->int_rdisc_timer;
84818334Speter	}
84990277Sobrien}
850117413Skan
85190277Sobrien
85218334Speter/* Solicit for Router Discovery
85390277Sobrien */
854117413Skanvoid
855117413Skanrdisc_sol(void)
85690277Sobrien{
85718334Speter	struct interface *ifp;
85890277Sobrien	union ad_u u;
85990277Sobrien
86018334Speter
86190277Sobrien	if (supplier)
86290277Sobrien		return;
86318334Speter
86490277Sobrien	rdisc_timer.tv_sec = now.tv_sec + NEVER;
865117413Skan
86690277Sobrien	LIST_FOREACH(ifp, &ifnet, int_list) {
86790277Sobrien		if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
86818334Speter		    || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
86918334Speter			continue;
87090277Sobrien
87118334Speter		if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
87218334Speter			memset(&u, 0, sizeof(u.so));
87318334Speter			u.so.icmp_type = ICMP_ROUTERSOLICIT;
87418334Speter			u.so.icmp_cksum = in_cksum((u_short*)&u.so,
875117413Skan						   sizeof(u.so));
87618334Speter			send_rdisc(&u, sizeof(u.so), ifp,
87718334Speter				   htonl(INADDR_ALLROUTERS_GROUP),
87818334Speter				   ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
87918334Speter
88018334Speter			if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
88118334Speter				continue;
88218334Speter
88318334Speter			ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
88418334Speter			ifp->int_rdisc_timer.tv_usec = 0;
88518334Speter			timevaladd(&ifp->int_rdisc_timer, &now);
88618334Speter		}
88718334Speter
88818334Speter		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
88918334Speter			rdisc_timer = ifp->int_rdisc_timer;
89018334Speter	}
89118334Speter}
89218334Speter
89318334Speter
89418334Speter/* check the IP header of a possible Router Discovery ICMP packet */
89518334Speterstatic struct interface *		/* 0 if bad */
89618334Speterck_icmp(const char *act,
89718334Speter	naddr	from,
89818334Speter	struct interface *ifp,
89918334Speter	naddr	to,
90018334Speter	union ad_u *p,
90190277Sobrien	u_int	len)
90218334Speter{
90390277Sobrien	const char *type;
90418334Speter
90518334Speter
90618334Speter	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
90718334Speter		type = "advertisement";
90818334Speter	} else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
90990277Sobrien		type = "solicitation";
91018334Speter	} else {
91118334Speter		return 0;
91218334Speter	}
91318334Speter
91418334Speter	if (p->icmp.icmp_code != 0) {
91590277Sobrien		trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
91618334Speter			  type, p->icmp.icmp_code,
91718334Speter			  naddr_ntoa(from), naddr_ntoa(to));
91818334Speter		return 0;
91918334Speter	}
92018334Speter
92152520Sobrien	trace_rdisc(act, from, to, ifp, p, len);
92296285Sobrien
92396285Sobrien	if (ifp == 0)
92418334Speter		trace_pkt("unknown interface for router-discovery %s"
925117413Skan			  " from %s to %s",
92618334Speter			  type, naddr_ntoa(from), naddr_ntoa(to));
92718334Speter
92850599Sobrien	return ifp;
92918334Speter}
93018334Speter
93118334Speter
93218334Speter/* read packets from the router discovery socket
93318334Speter */
93418334Spetervoid
93518334Speterread_d(void)
93618334Speter{
93718334Speter	static struct msg_limit bad_asize, bad_len;
93818334Speter#ifdef USE_PASSIFNAME
93918334Speter	static struct msg_limit  bad_name;
94050599Sobrien#endif
94118334Speter	struct sockaddr_in from;
94218334Speter	int n, fromlen, cc, hlen;
94318334Speter	struct {
94418334Speter#ifdef USE_PASSIFNAME
94518334Speter		char	ifname[IFNAMSIZ];
94618334Speter#endif
94718334Speter		union {
94818334Speter			struct ip ip;
94918334Speter			u_char	b[512];
95096285Sobrien		} pkt;
95118334Speter	} buf;
95218334Speter	union ad_u *p;
95318334Speter	n_long *wp;
95418334Speter	struct interface *ifp;
95518334Speter
95618334Speter
95718334Speter	for (;;) {
95852520Sobrien		fromlen = sizeof(from);
95990277Sobrien		cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
96018334Speter			      (struct sockaddr*)&from,
96118334Speter			      &fromlen);
96218334Speter		if (cc <= 0) {
96318334Speter			if (cc < 0 && errno != EWOULDBLOCK)
96418334Speter				LOGERR("recvfrom(rdisc_sock)");
96518334Speter			break;
96618334Speter		}
96718334Speter		if (fromlen != sizeof(struct sockaddr_in))
96818334Speter			logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
96918334Speter			       fromlen);
97018334Speter#ifdef USE_PASSIFNAME
971104763Skan		if ((cc -= sizeof(buf.ifname)) < 0)
97218334Speter			logbad(0,"missing USE_PASSIFNAME; only %d bytes",
97318334Speter			       cc+sizeof(buf.ifname));
97418334Speter#endif
97518334Speter
97696285Sobrien		hlen = buf.pkt.ip.ip_hl << 2;
97718334Speter		if (cc < hlen + ICMP_MINLEN)
97818334Speter			continue;
97918334Speter		p = (union ad_u *)&buf.pkt.b[hlen];
98050599Sobrien		cc -= hlen;
98118334Speter
98252520Sobrien#ifdef USE_PASSIFNAME
98318334Speter		ifp = ifwithname(buf.ifname, 0);
98418334Speter		if (ifp == 0)
98590277Sobrien			msglim(&bad_name, from.sin_addr.s_addr,
98618334Speter			       "impossible rdisc if_ name %.*s",
98718334Speter			       IFNAMSIZ, buf.ifname);
98818334Speter#else
98918334Speter		/* If we could tell the interface on which a packet from
99050599Sobrien		 * address 0 arrived, we could deal with such solicitations.
99118334Speter		 */
99218334Speter		ifp = ((from.sin_addr.s_addr == 0)
99318334Speter		       ? 0 : iflookup(from.sin_addr.s_addr));
99418334Speter#endif
99518334Speter		ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
99618334Speter			      buf.pkt.ip.ip_dst.s_addr, p, cc);
99718334Speter		if (ifp == 0)
99818334Speter			continue;
99918334Speter		if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
100090277Sobrien			trace_pkt("    "
100190277Sobrien				  "discard our own Router Discovery message");
100290277Sobrien			continue;
100390277Sobrien		}
100490277Sobrien
100590277Sobrien		switch (p->icmp.icmp_type) {
100690277Sobrien		case ICMP_ROUTERADVERT:
100790277Sobrien			if (p->ad.icmp_ad_asize*4
100890277Sobrien			    < (int)sizeof(p->ad.icmp_ad_info[0])) {
100990277Sobrien				msglim(&bad_asize, from.sin_addr.s_addr,
101090277Sobrien				       "intolerable rdisc address size=%d",
101190277Sobrien				       p->ad.icmp_ad_asize);
101218334Speter				continue;
101318334Speter			}
101418334Speter			if (p->ad.icmp_ad_num == 0) {
101518334Speter				trace_pkt("    empty?");
101618334Speter				continue;
101718334Speter			}
101818334Speter			if (cc != (int)(sizeof(p->ad)
101990277Sobrien					- sizeof(p->ad.icmp_ad_info)
102018334Speter					+ (p->ad.icmp_ad_num
102152520Sobrien					   * sizeof(p->ad.icmp_ad_info[0])))) {
102218334Speter				msglim(&bad_len, from.sin_addr.s_addr,
102390277Sobrien				       "rdisc length %d does not match ad_num"
102490277Sobrien				       " %d", cc, p->ad.icmp_ad_num);
102552520Sobrien				continue;
102690277Sobrien			}
102718334Speter			if (supplier)
102818334Speter				continue;
102918334Speter			if (ifp->int_state & IS_NO_ADV_IN)
103018334Speter				continue;
103118334Speter
103218334Speter			wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
103318334Speter			for (n = 0; n < p->ad.icmp_ad_num; n++) {
103490277Sobrien				parse_ad(from.sin_addr.s_addr,
103590277Sobrien					 wp[0], wp[1],
103690277Sobrien					 ntohs(p->ad.icmp_ad_life),
103790277Sobrien					 ifp);
103890277Sobrien				wp += p->ad.icmp_ad_asize;
103990277Sobrien			}
104090277Sobrien			break;
104190277Sobrien
104290277Sobrien
104390277Sobrien		case ICMP_ROUTERSOLICIT:
104490277Sobrien			if (!supplier)
104590277Sobrien				continue;
104690277Sobrien			if (ifp->int_state & IS_NO_ADV_OUT)
104790277Sobrien				continue;
104890277Sobrien			if (stopint)
104990277Sobrien				continue;
105090277Sobrien
105190277Sobrien			/* XXX
105290277Sobrien			 * We should handle messages from address 0.
105390277Sobrien			 */
105490277Sobrien
105590277Sobrien			/* Respond with a point-to-point advertisement */
105690277Sobrien			send_adv(ifp, from.sin_addr.s_addr, 0);
105790277Sobrien			break;
105890277Sobrien		}
105990277Sobrien	}
106090277Sobrien
106190277Sobrien	rdisc_sort();
106290277Sobrien}
106390277Sobrien