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