Home | History | Annotate | Line # | Download | only in routed
rdisc.c revision 1.10
      1 /*	$NetBSD: rdisc.c,v 1.10 2000/03/02 21:00:41 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1995
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgment:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #if !defined(lint) && !defined(sgi) && !defined(__NetBSD__)
     37 static char sccsid[] __attribute__((unused)) = "@(#)rdisc.c	8.1 (Berkeley) x/y/95";
     38 #elif defined(__NetBSD__)
     39 #include <sys/cdefs.h>
     40 __RCSID("$NetBSD: rdisc.c,v 1.10 2000/03/02 21:00:41 christos Exp $");
     41 #endif
     42 
     43 #include "defs.h"
     44 #include <netinet/in_systm.h>
     45 #include <netinet/ip.h>
     46 #include <netinet/ip_icmp.h>
     47 
     48 /* router advertisement ICMP packet */
     49 struct icmp_ad {
     50 	u_int8_t    icmp_type;		/* type of message */
     51 	u_int8_t    icmp_code;		/* type sub code */
     52 	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
     53 	u_int8_t    icmp_ad_num;	/* # of following router addresses */
     54 	u_int8_t    icmp_ad_asize;	/* 2--words in each advertisement */
     55 	u_int16_t   icmp_ad_life;	/* seconds of validity */
     56 	struct icmp_ad_info {
     57 	    n_long  icmp_ad_addr;
     58 	    n_long  icmp_ad_pref;
     59 	} icmp_ad_info[1];
     60 };
     61 
     62 /* router solicitation ICMP packet */
     63 struct icmp_so {
     64 	u_int8_t    icmp_type;		/* type of message */
     65 	u_int8_t    icmp_code;		/* type sub code */
     66 	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
     67 	n_long	    icmp_so_rsvd;
     68 };
     69 
     70 union ad_u {
     71 	struct icmp icmp;
     72 	struct icmp_ad ad;
     73 	struct icmp_so so;
     74 };
     75 
     76 
     77 int	rdisc_sock = -1;		/* router-discovery raw socket */
     78 struct interface *rdisc_sock_mcast;	/* current multicast interface */
     79 
     80 struct timeval rdisc_timer;
     81 int rdisc_ok;				/* using solicited route */
     82 
     83 
     84 #define MAX_ADS 16			/* at least one per interface */
     85 struct dr {				/* accumulated advertisements */
     86     struct interface *dr_ifp;
     87     naddr   dr_gate;			/* gateway */
     88     time_t  dr_ts;			/* when received */
     89     time_t  dr_life;			/* lifetime */
     90     n_long  dr_recv_pref;		/* received but biased preference */
     91     n_long  dr_pref;			/* preference adjusted by metric */
     92 } *cur_drp, drs[MAX_ADS];
     93 
     94 /* convert between signed, balanced around zero,
     95  * and unsigned zero-based preferences */
     96 #define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
     97 #define UNSIGN_PREF(p) SIGN_PREF(p)
     98 /* adjust unsigned preference by interface metric,
     99  * without driving it to infinity */
    100 #define PREF(p, ifp) ((int)(p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
    101 		      : (p) - ((ifp)->int_metric))
    102 
    103 static void rdisc_sort(void);
    104 
    105 
    106 /* dump an ICMP Router Discovery Advertisement Message
    107  */
    108 static void
    109 trace_rdisc(const char	*act,
    110 	    naddr	from,
    111 	    naddr	to,
    112 	    struct interface *ifp,
    113 	    union ad_u	*p,
    114 	    u_int	len)
    115 {
    116 	int i;
    117 	n_long *wp, *lim;
    118 
    119 
    120 	if (!TRACEPACKETS || ftrace == 0)
    121 		return;
    122 
    123 	lastlog();
    124 
    125 	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
    126 		(void)fprintf(ftrace, "%s Router Ad"
    127 			      " from %s to %s via %s life=%d\n",
    128 			      act, naddr_ntoa(from), naddr_ntoa(to),
    129 			      ifp ? ifp->int_name : "?",
    130 			      ntohs(p->ad.icmp_ad_life));
    131 		if (!TRACECONTENTS)
    132 			return;
    133 
    134 		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
    135 		lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
    136 		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
    137 			(void)fprintf(ftrace, "\t%s preference=%d",
    138 				      naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
    139 			wp += p->ad.icmp_ad_asize;
    140 		}
    141 		(void)fputc('\n',ftrace);
    142 
    143 	} else {
    144 		trace_act("%s Router Solic. from %s to %s via %s value=%#x",
    145 			  act, naddr_ntoa(from), naddr_ntoa(to),
    146 			  ifp ? ifp->int_name : "?",
    147 			  (int)ntohl(p->so.icmp_so_rsvd));
    148 	}
    149 }
    150 
    151 /* prepare Router Discovery socket.
    152  */
    153 static void
    154 get_rdisc_sock(void)
    155 {
    156 	if (rdisc_sock < 0) {
    157 		rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    158 		if (rdisc_sock < 0)
    159 			BADERR(1,"rdisc_sock = socket()");
    160 		fix_sock(rdisc_sock,"rdisc_sock");
    161 		fix_select();
    162 	}
    163 }
    164 
    165 
    166 /* Pick multicast group for router-discovery socket
    167  */
    168 void
    169 set_rdisc_mg(struct interface *ifp,
    170 	     int on)			/* 0=turn it off */
    171 {
    172 	struct ip_mreq m;
    173 
    174 	if (rdisc_sock < 0) {
    175 		/* Create the raw socket so that we can hear at least
    176 		 * broadcast router discovery packets.
    177 		 */
    178 		if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC
    179 		    || !on)
    180 			return;
    181 		get_rdisc_sock();
    182 	}
    183 
    184 	if (!(ifp->int_if_flags & IFF_MULTICAST)) {
    185 		ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
    186 		return;
    187 	}
    188 
    189 #ifdef MCAST_PPP_BUG
    190 	if (ifp->int_if_flags & IFF_POINTOPOINT)
    191 		return;
    192 #endif
    193 	memset(&m, 0, sizeof(m));
    194 	m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT)
    195 				  ? ifp->int_dstaddr
    196 				  : ifp->int_addr);
    197 	if (supplier
    198 	    || (ifp->int_state & IS_NO_ADV_IN)
    199 	    || !on) {
    200 		/* stop listening to advertisements
    201 		 */
    202 		if (ifp->int_state & IS_ALL_HOSTS) {
    203 			m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
    204 			if (setsockopt(rdisc_sock, IPPROTO_IP,
    205 				       IP_DROP_MEMBERSHIP,
    206 				       &m, sizeof(m)) < 0)
    207 				LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
    208 			ifp->int_state &= ~IS_ALL_HOSTS;
    209 		}
    210 
    211 	} else if (!(ifp->int_state & IS_ALL_HOSTS)) {
    212 		/* start listening to advertisements
    213 		 */
    214 		m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
    215 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
    216 			       &m, sizeof(m)) < 0) {
    217 			LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
    218 		} else {
    219 			ifp->int_state |= IS_ALL_HOSTS;
    220 		}
    221 	}
    222 
    223 	if (!supplier
    224 	    || (ifp->int_state & IS_NO_ADV_OUT)
    225 	    || !on) {
    226 		/* stop listening to solicitations
    227 		 */
    228 		if (ifp->int_state & IS_ALL_ROUTERS) {
    229 			m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
    230 			if (setsockopt(rdisc_sock, IPPROTO_IP,
    231 				       IP_DROP_MEMBERSHIP,
    232 				       &m, sizeof(m)) < 0)
    233 				LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
    234 			ifp->int_state &= ~IS_ALL_ROUTERS;
    235 		}
    236 
    237 	} else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
    238 		/* start hearing solicitations
    239 		 */
    240 		m.imr_multiaddr.s_addr=htonl(INADDR_ALLROUTERS_GROUP);
    241 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
    242 			       &m, sizeof(m)) < 0) {
    243 			LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
    244 		} else {
    245 			ifp->int_state |= IS_ALL_ROUTERS;
    246 		}
    247 	}
    248 }
    249 
    250 
    251 /* start supplying routes
    252  */
    253 void
    254 set_supplier(void)
    255 {
    256 	struct interface *ifp;
    257 	struct dr *drp;
    258 
    259 	if (supplier_set)
    260 		return;
    261 
    262 	trace_act("start supplying routes");
    263 
    264 	/* Forget discovered routes.
    265 	 */
    266 	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
    267 		drp->dr_recv_pref = 0;
    268 		drp->dr_life = 0;
    269 	}
    270 	rdisc_age(0);
    271 
    272 	supplier_set = 1;
    273 	supplier = 1;
    274 
    275 	/* Do not start advertising until we have heard some RIP routes */
    276 	LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
    277 
    278 	/* Switch router discovery multicast groups from soliciting
    279 	 * to advertising.
    280 	 */
    281 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
    282 		if (ifp->int_state & IS_BROKE)
    283 			continue;
    284 		ifp->int_rdisc_cnt = 0;
    285 		ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
    286 		ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
    287 		set_rdisc_mg(ifp, 1);
    288 	}
    289 
    290 	/* get rid of any redirects */
    291 	del_redirects(0,0);
    292 }
    293 
    294 
    295 /* age discovered routes and find the best one
    296  */
    297 void
    298 rdisc_age(naddr bad_gate)
    299 {
    300 	time_t sec;
    301 	struct dr *drp;
    302 
    303 
    304 	/* If only advertising, then do only that. */
    305 	if (supplier) {
    306 		/* If switching from client to server, get rid of old
    307 		 * default routes.
    308 		 */
    309 		if (cur_drp != 0)
    310 			rdisc_sort();
    311 		rdisc_adv();
    312 		return;
    313 	}
    314 
    315 	/* If we are being told about a bad router,
    316 	 * then age the discovered default route, and if there is
    317 	 * no alternative, solicit a replacement.
    318 	 */
    319 	if (bad_gate != 0) {
    320 		/* Look for the bad discovered default route.
    321 		 * Age it and note its interface.
    322 		 */
    323 		for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
    324 			if (drp->dr_ts == 0)
    325 				continue;
    326 
    327 			/* When we find the bad router, then age the route
    328 			 * to at most SUPPLY_INTERVAL.
    329 			 * This is contrary to RFC 1256, but defends against
    330 			 * black holes.
    331 			 */
    332 			if (drp->dr_gate == bad_gate) {
    333 				sec = (now.tv_sec - drp->dr_life
    334 				       + SUPPLY_INTERVAL);
    335 				if (drp->dr_ts > sec) {
    336 					trace_act("age 0.0.0.0 --> %s via %s",
    337 						  naddr_ntoa(drp->dr_gate),
    338 						  drp->dr_ifp->int_name);
    339 					drp->dr_ts = sec;
    340 				}
    341 				break;
    342 			}
    343 		}
    344 	}
    345 
    346 	rdisc_sol();
    347 	rdisc_sort();
    348 
    349 	/* Delete old redirected routes to keep the kernel table small,
    350 	 * and to prevent black holes.  Check that the kernel table
    351 	 * matches the daemon table (i.e. has the default route).
    352 	 * But only if RIP is not running and we are not dealing with
    353 	 * a bad gateway, since otherwise age() will be called.
    354 	 */
    355 	if (rip_sock < 0 && bad_gate == 0)
    356 		age(0);
    357 }
    358 
    359 
    360 /* Zap all routes discovered via an interface that has gone bad
    361  *	This should only be called when !(ifp->int_state & IS_ALIAS)
    362  */
    363 void
    364 if_bad_rdisc(struct interface *ifp)
    365 {
    366 	struct dr *drp;
    367 
    368 	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
    369 		if (drp->dr_ifp != ifp)
    370 			continue;
    371 		drp->dr_recv_pref = 0;
    372 		drp->dr_ts = 0;
    373 		drp->dr_life = 0;
    374 	}
    375 
    376 	/* make a note to re-solicit, turn RIP on or off, etc. */
    377 	rdisc_timer.tv_sec = 0;
    378 }
    379 
    380 
    381 /* mark an interface ok for router discovering.
    382  */
    383 void
    384 if_ok_rdisc(struct interface *ifp)
    385 {
    386 	set_rdisc_mg(ifp, 1);
    387 
    388 	ifp->int_rdisc_cnt = 0;
    389 	ifp->int_rdisc_timer.tv_sec = now.tv_sec + (supplier
    390 						    ? MIN_WAITTIME
    391 						    : MAX_SOLICITATION_DELAY);
    392 	if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
    393 		rdisc_timer = ifp->int_rdisc_timer;
    394 }
    395 
    396 
    397 /* get rid of a dead discovered router
    398  */
    399 static void
    400 del_rdisc(struct dr *drp)
    401 {
    402 	struct interface *ifp;
    403 	naddr gate;
    404 	int i;
    405 
    406 
    407 	del_redirects(gate = drp->dr_gate, 0);
    408 	drp->dr_ts = 0;
    409 	drp->dr_life = 0;
    410 
    411 
    412 	/* Count the other discovered routes on the interface.
    413 	 */
    414 	i = 0;
    415 	ifp = drp->dr_ifp;
    416 	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
    417 		if (drp->dr_ts != 0
    418 		    && drp->dr_ifp == ifp)
    419 			i++;
    420 	}
    421 
    422 	/* If that was the last good discovered router on the interface,
    423 	 * then solicit a new one.
    424 	 * This is contrary to RFC 1256, but defends against black holes.
    425 	 */
    426 	if (i != 0) {
    427 		trace_act("discovered router %s via %s"
    428 			  " is bad--have %d remaining",
    429 			  naddr_ntoa(gate), ifp->int_name, i);
    430 	} else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
    431 		trace_act("last discovered router %s via %s"
    432 			  " is bad--re-solicit",
    433 			  naddr_ntoa(gate), ifp->int_name);
    434 		ifp->int_rdisc_cnt = 0;
    435 		ifp->int_rdisc_timer.tv_sec = 0;
    436 		rdisc_sol();
    437 	} else {
    438 		trace_act("last discovered router %s via %s"
    439 			  " is bad--wait to solicit",
    440 			  naddr_ntoa(gate), ifp->int_name);
    441 	}
    442 }
    443 
    444 
    445 /* Find the best discovered route,
    446  * and discard stale routers.
    447  */
    448 static void
    449 rdisc_sort(void)
    450 {
    451 	struct dr *drp, *new_drp;
    452 	struct rt_entry *rt;
    453 	struct rt_spare new;
    454 	struct interface *ifp;
    455 	u_int new_st = 0;
    456 	n_long new_pref = 0;
    457 
    458 
    459 	/* Find the best discovered route.
    460 	 */
    461 	new_drp = 0;
    462 	for (drp = drs; drp < &drs[MAX_ADS]; drp++) {
    463 		if (drp->dr_ts == 0)
    464 			continue;
    465 		ifp = drp->dr_ifp;
    466 
    467 		/* Get rid of expired discovered routers.
    468 		 */
    469 		if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
    470 			del_rdisc(drp);
    471 			continue;
    472 		}
    473 
    474 		LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life+1);
    475 
    476 		/* Update preference with possibly changed interface
    477 		 * metric.
    478 		 */
    479 		drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
    480 
    481 		/* Prefer the current route to prevent thrashing.
    482 		 * Prefer shorter lifetimes to speed the detection of
    483 		 * bad routers.
    484 		 * Avoid sick interfaces.
    485 		 */
    486 		if (new_drp == 0
    487 		    || (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK)
    488 			&& (new_pref < drp->dr_pref
    489 			    || (new_pref == drp->dr_pref
    490 				&& (drp == cur_drp
    491 				    || (new_drp != cur_drp
    492 					&& new_drp->dr_life > drp->dr_life)))))
    493 		    || ((new_st & IS_SICK)
    494 			&& !(drp->dr_ifp->int_state & IS_SICK))) {
    495 			    new_drp = drp;
    496 			    new_st = drp->dr_ifp->int_state;
    497 			    new_pref = drp->dr_pref;
    498 		}
    499 	}
    500 
    501 	/* switch to a better default route
    502 	 */
    503 	if (new_drp != cur_drp) {
    504 		rt = rtget(RIP_DEFAULT, 0);
    505 
    506 		/* Stop using discovered routes if they are all bad
    507 		 */
    508 		if (new_drp == 0) {
    509 			trace_act("turn off Router Discovery client");
    510 			rdisc_ok = 0;
    511 
    512 			if (rt != 0
    513 			    && (rt->rt_state & RS_RDISC)) {
    514 				new = rt->rt_spares[0];
    515 				new.rts_metric = HOPCNT_INFINITY;
    516 				new.rts_time = now.tv_sec - GARBAGE_TIME;
    517 				rtchange(rt, rt->rt_state & ~RS_RDISC,
    518 					 &new, 0);
    519 				rtswitch(rt, 0);
    520 			}
    521 
    522 		} else {
    523 			if (cur_drp == 0) {
    524 				trace_act("turn on Router Discovery client"
    525 					  " using %s via %s",
    526 					  naddr_ntoa(new_drp->dr_gate),
    527 					  new_drp->dr_ifp->int_name);
    528 				rdisc_ok = 1;
    529 
    530 			} else {
    531 				trace_act("switch Router Discovery from"
    532 					  " %s via %s to %s via %s",
    533 					  naddr_ntoa(cur_drp->dr_gate),
    534 					  cur_drp->dr_ifp->int_name,
    535 					  naddr_ntoa(new_drp->dr_gate),
    536 					  new_drp->dr_ifp->int_name);
    537 			}
    538 
    539 			memset(&new, 0, sizeof(new));
    540 			new.rts_ifp = new_drp->dr_ifp;
    541 			new.rts_gate = new_drp->dr_gate;
    542 			new.rts_router = new_drp->dr_gate;
    543 			new.rts_metric = HOPCNT_INFINITY-1;
    544 			new.rts_time = now.tv_sec;
    545 			if (rt != 0) {
    546 				rtchange(rt, rt->rt_state | RS_RDISC, &new, 0);
    547 			} else {
    548 				rtadd(RIP_DEFAULT, 0, RS_RDISC, &new);
    549 			}
    550 		}
    551 
    552 		cur_drp = new_drp;
    553 	}
    554 
    555 	/* turn RIP on or off */
    556 	if (!rdisc_ok || rip_interfaces > 1) {
    557 		rip_on(0);
    558 	} else {
    559 		rip_off();
    560 	}
    561 }
    562 
    563 
    564 /* handle a single address in an advertisement
    565  */
    566 static void
    567 parse_ad(naddr from,
    568 	 naddr gate,
    569 	 n_long pref,			/* signed and in network order */
    570 	 u_short life,
    571 	 struct interface *ifp)
    572 {
    573 	static struct msg_limit bad_gate;
    574 	struct dr *drp, *new_drp;
    575 
    576 
    577 	if (gate == RIP_DEFAULT
    578 	    || !check_dst(gate)) {
    579 		msglim(&bad_gate, from,"router %s advertising bad gateway %s",
    580 		       naddr_ntoa(from),
    581 		       naddr_ntoa(gate));
    582 		return;
    583 	}
    584 
    585 	/* ignore pointers to ourself and routes via unreachable networks
    586 	 */
    587 	if (ifwithaddr(gate, 1, 0) != 0) {
    588 		trace_pkt("    discard Router Discovery Ad pointing at us");
    589 		return;
    590 	}
    591 	if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
    592 		trace_pkt("    discard Router Discovery Ad"
    593 			  " toward unreachable net");
    594 		return;
    595 	}
    596 
    597 	/* Convert preference to an unsigned value
    598 	 * and later bias it by the metric of the interface.
    599 	 */
    600 	pref = UNSIGN_PREF(ntohl(pref));
    601 
    602 	if (pref == 0 || life < MinMaxAdvertiseInterval) {
    603 		pref = 0;
    604 		life = 0;
    605 	}
    606 
    607 	for (new_drp = 0, drp = drs; drp < &drs[MAX_ADS]; drp++) {
    608 		/* accept new info for a familiar entry
    609 		 */
    610 		if (drp->dr_gate == gate) {
    611 			new_drp = drp;
    612 			break;
    613 		}
    614 
    615 		if (life == 0)
    616 			continue;	/* do not worry about dead ads */
    617 
    618 		if (drp->dr_ts == 0) {
    619 			new_drp = drp;	/* use unused entry */
    620 
    621 		} else if (new_drp == 0) {
    622 			/* look for an entry worse than the new one to
    623 			 * reuse.
    624 			 */
    625 			if ((!(ifp->int_state & IS_SICK)
    626 			     && (drp->dr_ifp->int_state & IS_SICK))
    627 			    || (pref > drp->dr_pref
    628 				&& !((ifp->int_state ^ drp->dr_ifp->int_state)
    629 				     & IS_SICK)))
    630 				new_drp = drp;
    631 
    632 		} else if (new_drp->dr_ts != 0) {
    633 			/* look for the least valuable entry to reuse
    634 			 */
    635 			if ((!(new_drp->dr_ifp->int_state & IS_SICK)
    636 			     && (drp->dr_ifp->int_state & IS_SICK))
    637 			    || (new_drp->dr_pref > drp->dr_pref
    638 				&& !((new_drp->dr_ifp->int_state
    639 				      ^ drp->dr_ifp->int_state)
    640 				     & IS_SICK)))
    641 				new_drp = drp;
    642 		}
    643 	}
    644 
    645 	/* forget it if all of the current entries are better */
    646 	if (new_drp == 0)
    647 		return;
    648 
    649 	new_drp->dr_ifp = ifp;
    650 	new_drp->dr_gate = gate;
    651 	new_drp->dr_ts = now.tv_sec;
    652 	new_drp->dr_life = ntohs(life);
    653 	new_drp->dr_recv_pref = pref;
    654 	/* bias functional preference by metric of the interface */
    655 	new_drp->dr_pref = PREF(pref,ifp);
    656 
    657 	/* after hearing a good advertisement, stop asking
    658 	 */
    659 	if (!(ifp->int_state & IS_SICK))
    660 		ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
    661 }
    662 
    663 
    664 /* Compute the IP checksum
    665  *	This assumes the packet is less than 32K long.
    666  */
    667 static u_short
    668 in_cksum(u_short *p,
    669 	 u_int len)
    670 {
    671 	u_int sum = 0;
    672 	int nwords = len >> 1;
    673 
    674 	while (nwords-- != 0)
    675 		sum += *p++;
    676 
    677 	if (len & 1)
    678 		sum += *(u_char *)p;
    679 
    680 	/* end-around-carry */
    681 	sum = (sum >> 16) + (sum & 0xffff);
    682 	sum += (sum >> 16);
    683 	return (~sum);
    684 }
    685 
    686 
    687 /* Send a router discovery advertisement or solicitation ICMP packet.
    688  */
    689 static void
    690 send_rdisc(union ad_u *p,
    691 	   int p_size,
    692 	   struct interface *ifp,
    693 	   naddr dst,			/* 0 or unicast destination */
    694 	   int	type)			/* 0=unicast, 1=bcast, 2=mcast */
    695 {
    696 	struct sockaddr_in sin;
    697 	int flags;
    698 	const char *msg;
    699 	naddr tgt_mcast;
    700 
    701 
    702 	memset(&sin, 0, sizeof(sin));
    703 	sin.sin_addr.s_addr = dst;
    704 	sin.sin_family = AF_INET;
    705 #ifdef _HAVE_SIN_LEN
    706 	sin.sin_len = sizeof(sin);
    707 #endif
    708 	flags = MSG_DONTROUTE;
    709 
    710 	switch (type) {
    711 	case 0:				/* unicast */
    712 	default:
    713 		msg = "Send";
    714 		break;
    715 
    716 	case 1:				/* broadcast */
    717 		if (ifp->int_if_flags & IFF_POINTOPOINT) {
    718 			msg = "Send pt-to-pt";
    719 			sin.sin_addr.s_addr = ifp->int_dstaddr;
    720 		} else {
    721 			msg = "Send broadcast";
    722 			sin.sin_addr.s_addr = ifp->int_brdaddr;
    723 		}
    724 		break;
    725 
    726 	case 2:				/* multicast */
    727 		msg = "Send multicast";
    728 		if (ifp->int_state & IS_DUP) {
    729 			trace_act("abort multicast output via %s"
    730 				  " with duplicate address",
    731 				  ifp->int_name);
    732 			return;
    733 		}
    734 		if (rdisc_sock_mcast != ifp) {
    735 			/* select the right interface. */
    736 #ifdef MCAST_PPP_BUG
    737 			/* Do not specify the primary interface explicitly
    738 			 * if we have the multicast point-to-point kernel
    739 			 * bug, since the kernel will do the wrong thing
    740 			 * if the local address of a point-to-point link
    741 			 * is the same as the address of an ordinary
    742 			 * interface.
    743 			 */
    744 			if (ifp->int_addr == myaddr) {
    745 				tgt_mcast = 0;
    746 			} else
    747 #endif
    748 			tgt_mcast = ifp->int_addr;
    749 			if (0 > setsockopt(rdisc_sock,
    750 					   IPPROTO_IP, IP_MULTICAST_IF,
    751 					   &tgt_mcast, sizeof(tgt_mcast))) {
    752 				LOGERR("setsockopt(rdisc_sock,"
    753 				       "IP_MULTICAST_IF)");
    754 				rdisc_sock_mcast = 0;
    755 				return;
    756 			}
    757 			rdisc_sock_mcast = ifp;
    758 		}
    759 		flags = 0;
    760 		break;
    761 	}
    762 
    763 	if (rdisc_sock < 0)
    764 		get_rdisc_sock();
    765 
    766 	trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp,
    767 		    p, p_size);
    768 
    769 	if (0 > sendto(rdisc_sock, p, p_size, flags,
    770 		       (struct sockaddr *)&sin, sizeof(sin))) {
    771 		if (ifp == 0 || !(ifp->int_state & IS_BROKE))
    772 			msglog("sendto(%s%s%s): %s",
    773 			       ifp != 0 ? ifp->int_name : "",
    774 			       ifp != 0 ? ", " : "",
    775 			       inet_ntoa(sin.sin_addr),
    776 			       strerror(errno));
    777 		if (ifp != 0)
    778 			if_sick(ifp);
    779 	}
    780 }
    781 
    782 
    783 /* Send an advertisement
    784  */
    785 static void
    786 send_adv(struct interface *ifp,
    787 	 naddr	dst,			/* 0 or unicast destination */
    788 	 int	type)			/* 0=unicast, 1=bcast, 2=mcast */
    789 {
    790 	union ad_u u;
    791 	n_long pref;
    792 
    793 
    794 	memset(&u, 0, sizeof(u.ad));
    795 
    796 	u.ad.icmp_type = ICMP_ROUTERADVERT;
    797 	u.ad.icmp_ad_num = 1;
    798 	u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
    799 
    800 	u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
    801 
    802 	/* Convert the configured preference to an unsigned value,
    803 	 * bias it by the interface metric, and then send it as a
    804 	 * signed, network byte order value.
    805 	 */
    806 	pref = UNSIGN_PREF(ifp->int_rdisc_pref);
    807 	u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp)));
    808 
    809 	u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
    810 
    811 	u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
    812 
    813 	send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
    814 }
    815 
    816 
    817 /* Advertise for Router Discovery
    818  */
    819 void
    820 rdisc_adv(void)
    821 {
    822 	struct interface *ifp;
    823 
    824 	if (!supplier)
    825 		return;
    826 
    827 	rdisc_timer.tv_sec = now.tv_sec + NEVER;
    828 
    829 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
    830 		if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
    831 			continue;
    832 
    833 		if (!timercmp(&ifp->int_rdisc_timer, &now, >)
    834 		    || stopint) {
    835 			send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
    836 				 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
    837 			ifp->int_rdisc_cnt++;
    838 
    839 			intvl_random(&ifp->int_rdisc_timer,
    840 				     (ifp->int_rdisc_int*3)/4,
    841 				     ifp->int_rdisc_int);
    842 			if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
    843 			    && (ifp->int_rdisc_timer.tv_sec
    844 				> MAX_INITIAL_ADVERT_INTERVAL)) {
    845 				ifp->int_rdisc_timer.tv_sec
    846 				= MAX_INITIAL_ADVERT_INTERVAL;
    847 			}
    848 			timevaladd(&ifp->int_rdisc_timer, &now);
    849 		}
    850 
    851 		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
    852 			rdisc_timer = ifp->int_rdisc_timer;
    853 	}
    854 }
    855 
    856 
    857 /* Solicit for Router Discovery
    858  */
    859 void
    860 rdisc_sol(void)
    861 {
    862 	struct interface *ifp;
    863 	union ad_u u;
    864 
    865 
    866 	if (supplier)
    867 		return;
    868 
    869 	rdisc_timer.tv_sec = now.tv_sec + NEVER;
    870 
    871 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
    872 		if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
    873 		    || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
    874 			continue;
    875 
    876 		if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
    877 			memset(&u, 0, sizeof(u.so));
    878 			u.so.icmp_type = ICMP_ROUTERSOLICIT;
    879 			u.so.icmp_cksum = in_cksum((u_short*)&u.so,
    880 						   sizeof(u.so));
    881 			send_rdisc(&u, sizeof(u.so), ifp,
    882 				   htonl(INADDR_ALLROUTERS_GROUP),
    883 				   ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
    884 
    885 			if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
    886 				continue;
    887 
    888 			ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
    889 			ifp->int_rdisc_timer.tv_usec = 0;
    890 			timevaladd(&ifp->int_rdisc_timer, &now);
    891 		}
    892 
    893 		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
    894 			rdisc_timer = ifp->int_rdisc_timer;
    895 	}
    896 }
    897 
    898 
    899 /* check the IP header of a possible Router Discovery ICMP packet */
    900 static struct interface *		/* 0 if bad */
    901 ck_icmp(const char *act,
    902 	naddr	from,
    903 	struct interface *ifp,
    904 	naddr	to,
    905 	union ad_u *p,
    906 	u_int	len)
    907 {
    908 	const char *type;
    909 
    910 
    911 	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
    912 		type = "advertisement";
    913 	} else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
    914 		type = "solicitation";
    915 	} else {
    916 		return 0;
    917 	}
    918 
    919 	if (p->icmp.icmp_code != 0) {
    920 		trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
    921 			  type, p->icmp.icmp_code,
    922 			  naddr_ntoa(from), naddr_ntoa(to));
    923 		return 0;
    924 	}
    925 
    926 	trace_rdisc(act, from, to, ifp, p, len);
    927 
    928 	if (ifp == 0)
    929 		trace_pkt("unknown interface for router-discovery %s"
    930 			  " from %s to %s",
    931 			  type, naddr_ntoa(from), naddr_ntoa(to));
    932 
    933 	return ifp;
    934 }
    935 
    936 
    937 /* read packets from the router discovery socket
    938  */
    939 void
    940 read_d(void)
    941 {
    942 	static struct msg_limit bad_asize, bad_len;
    943 #ifdef USE_PASSIFNAME
    944 	static struct msg_limit  bad_name;
    945 #endif
    946 	struct sockaddr_in from;
    947 	int n, fromlen, cc, hlen;
    948 	struct {
    949 #ifdef USE_PASSIFNAME
    950 		char	ifname[IFNAMSIZ];
    951 #endif
    952 		union {
    953 			struct ip ip;
    954 			u_short s[512/2];
    955 			u_char	b[512];
    956 		} pkt;
    957 	} buf;
    958 	union ad_u *p;
    959 	n_long *wp;
    960 	struct interface *ifp;
    961 
    962 
    963 	for (;;) {
    964 		fromlen = sizeof(from);
    965 		cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
    966 			      (struct sockaddr*)&from,
    967 			      &fromlen);
    968 		if (cc <= 0) {
    969 			if (cc < 0 && errno != EWOULDBLOCK)
    970 				LOGERR("recvfrom(rdisc_sock)");
    971 			break;
    972 		}
    973 		if (fromlen != sizeof(struct sockaddr_in))
    974 			logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
    975 			       fromlen);
    976 #ifdef USE_PASSIFNAME
    977 		if ((cc -= sizeof(buf.ifname)) < 0)
    978 			logbad(0,"missing USE_PASSIFNAME; only %d bytes",
    979 			       cc+sizeof(buf.ifname));
    980 #endif
    981 
    982 		hlen = buf.pkt.ip.ip_hl << 2;
    983 		if (cc < hlen + ICMP_MINLEN)
    984 			continue;
    985 		p = (union ad_u *)&buf.pkt.b[hlen];
    986 		cc -= hlen;
    987 
    988 #ifdef USE_PASSIFNAME
    989 		ifp = ifwithname(buf.ifname, 0);
    990 		if (ifp == 0)
    991 			msglim(&bad_name, from.sin_addr.s_addr,
    992 			       "impossible rdisc if_ name %.*s",
    993 			       IFNAMSIZ, buf.ifname);
    994 #else
    995 		/* If we could tell the interface on which a packet from
    996 		 * address 0 arrived, we could deal with such solicitations.
    997 		 */
    998 		ifp = ((from.sin_addr.s_addr == 0)
    999 		       ? 0 : iflookup(from.sin_addr.s_addr));
   1000 #endif
   1001 		ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
   1002 			      buf.pkt.ip.ip_dst.s_addr, p, cc);
   1003 		if (ifp == 0)
   1004 			continue;
   1005 		if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
   1006 			trace_pkt("    "
   1007 				  "discard our own Router Discovery message");
   1008 			continue;
   1009 		}
   1010 
   1011 		switch (p->icmp.icmp_type) {
   1012 		case ICMP_ROUTERADVERT:
   1013 			if (p->ad.icmp_ad_asize*4
   1014 			    < (int)sizeof(p->ad.icmp_ad_info[0])) {
   1015 				msglim(&bad_asize, from.sin_addr.s_addr,
   1016 				       "intolerable rdisc address size=%d",
   1017 				       p->ad.icmp_ad_asize);
   1018 				continue;
   1019 			}
   1020 			if (p->ad.icmp_ad_num == 0) {
   1021 				trace_pkt("    empty?");
   1022 				continue;
   1023 			}
   1024 			if (cc != (int)(sizeof(p->ad)
   1025 					- sizeof(p->ad.icmp_ad_info)
   1026 					+ (p->ad.icmp_ad_num
   1027 					   * sizeof(p->ad.icmp_ad_info[0])))) {
   1028 				msglim(&bad_len, from.sin_addr.s_addr,
   1029 				       "rdisc length %d does not match ad_num"
   1030 				       " %d", cc, p->ad.icmp_ad_num);
   1031 				continue;
   1032 			}
   1033 			if (supplier)
   1034 				continue;
   1035 			if (ifp->int_state & IS_NO_ADV_IN)
   1036 				continue;
   1037 
   1038 			wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
   1039 			for (n = 0; n < p->ad.icmp_ad_num; n++) {
   1040 				parse_ad(from.sin_addr.s_addr,
   1041 					 wp[0], wp[1],
   1042 					 ntohs(p->ad.icmp_ad_life),
   1043 					 ifp);
   1044 				wp += p->ad.icmp_ad_asize;
   1045 			}
   1046 			break;
   1047 
   1048 
   1049 		case ICMP_ROUTERSOLICIT:
   1050 			if (!supplier)
   1051 				continue;
   1052 			if (ifp->int_state & IS_NO_ADV_OUT)
   1053 				continue;
   1054 			if (stopint)
   1055 				continue;
   1056 
   1057 			/* XXX
   1058 			 * We should handle messages from address 0.
   1059 			 */
   1060 
   1061 			/* Respond with a point-to-point advertisement */
   1062 			send_adv(ifp, from.sin_addr.s_addr, 0);
   1063 			break;
   1064 		}
   1065 	}
   1066 
   1067 	rdisc_sort();
   1068 }
   1069