rdisc.c revision 50476
138136Sdfr/*
238136Sdfr * Copyright (c) 1995
338136Sdfr *	The Regents of the University of California.  All rights reserved.
438136Sdfr *
538136Sdfr * Redistribution and use in source and binary forms, with or without
638136Sdfr * modification, are permitted provided that the following conditions
738136Sdfr * are met:
838136Sdfr * 1. Redistributions of source code must retain the above copyright
938136Sdfr *    notice, this list of conditions and the following disclaimer.
1038136Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1138136Sdfr *    notice, this list of conditions and the following disclaimer in the
1238136Sdfr *    documentation and/or other materials provided with the distribution.
1338136Sdfr * 3. All advertising materials mentioning features or use of this software
1438136Sdfr *    must display the following acknowledgment:
1538136Sdfr *	This product includes software developed by the University of
1638136Sdfr *	California, Berkeley and its contributors.
1738136Sdfr * 4. Neither the name of the University nor the names of its contributors
1838136Sdfr *    may be used to endorse or promote products derived from this software
1938136Sdfr *    without specific prior written permission.
2038136Sdfr *
2138136Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2238136Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2338136Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2438136Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2538136Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2650477Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2738136Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2838136Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2947613Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3047613Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3147613Sdfr * SUCH DAMAGE.
3250769Sdfr *
3350769Sdfr * $FreeBSD: head/sbin/routed/rdisc.c 50476 1999-08-28 00:22:10Z peter $
3450769Sdfr */
3550769Sdfr
3647398Sdfr#include "defs.h"
3750769Sdfr#include <netinet/in_systm.h>
3847398Sdfr#include <netinet/ip.h>
3955206Speter#include <netinet/ip_icmp.h>
4047613Sdfr
4147578Sdfr#if !defined(sgi) && !defined(__NetBSD__)
4247578Sdfrstatic char sccsid[] __attribute__((unused)) = "@(#)rdisc.c	8.1 (Berkeley) x/y/95";
4347578Sdfr#elif defined(__NetBSD__)
4447578Sdfr__RCSID"$NetBSD$");
4547578Sdfr#endif
4647578Sdfr#ident "$Revision: 1.3 $"
4747578Sdfr
4847578Sdfr/* router advertisement ICMP packet */
4947578Sdfrstruct icmp_ad {
5047578Sdfr	u_int8_t    icmp_type;		/* type of message */
5147578Sdfr	u_int8_t    icmp_code;		/* type sub code */
5252174Sdfr	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
5352174Sdfr	u_int8_t    icmp_ad_num;	/* # of following router addresses */
5450751Speter	u_int8_t    icmp_ad_asize;	/* 2--words in each advertisement */
5550751Speter	u_int16_t   icmp_ad_life;	/* seconds of validity */
5641181Sdfr	struct icmp_ad_info {
5750769Sdfr	    n_long  icmp_ad_addr;
5850769Sdfr	    n_long  icmp_ad_pref;
5950769Sdfr	} icmp_ad_info[1];
6050769Sdfr};
6150769Sdfr
6250769Sdfr/* router solicitation ICMP packet */
6350769Sdfrstruct icmp_so {
6450769Sdfr	u_int8_t    icmp_type;		/* type of message */
6550769Sdfr	u_int8_t    icmp_code;		/* type sub code */
6650769Sdfr	u_int16_t   icmp_cksum;		/* ones complement cksum of struct */
6750769Sdfr	n_long	    icmp_so_rsvd;
6850769Sdfr};
6950769Sdfr
7050769Sdfrunion ad_u {
7150769Sdfr	struct icmp icmp;
7250769Sdfr	struct icmp_ad ad;
7350769Sdfr	struct icmp_so so;
7450769Sdfr};
7550769Sdfr
7650769Sdfr
7750769Sdfrint	rdisc_sock = -1;		/* router-discovery raw socket */
7850769Sdfrstruct interface *rdisc_sock_mcast;	/* current multicast interface */
7950769Sdfr
8050769Sdfrstruct timeval rdisc_timer;
8150769Sdfrint rdisc_ok;				/* using solicited route */
8250769Sdfr
8350769Sdfr
8450769Sdfr#define MAX_ADS 16			/* at least one per interface */
8550769Sdfrstruct dr {				/* accumulated advertisements */
8650769Sdfr    struct interface *dr_ifp;
8750769Sdfr    naddr   dr_gate;			/* gateway */
8850769Sdfr    time_t  dr_ts;			/* when received */
8950769Sdfr    time_t  dr_life;			/* lifetime */
9038136Sdfr    n_long  dr_recv_pref;		/* received but biased preference */
9141181Sdfr    n_long  dr_pref;			/* preference adjusted by metric */
9241181Sdfr} *cur_drp, drs[MAX_ADS];
9341181Sdfr
9441181Sdfr/* convert between signed, balanced around zero,
9541181Sdfr * and unsigned zero-based preferences */
9641181Sdfr#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
9741181Sdfr#define UNSIGN_PREF(p) SIGN_PREF(p)
9841181Sdfr/* adjust unsigned preference by interface metric,
9941181Sdfr * without driving it to infinity */
10041181Sdfr#define PREF(p, ifp) ((int)(p) <= (ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
10141181Sdfr		      : (p) - ((ifp)->int_metric))
10241181Sdfr
10341181Sdfrstatic void rdisc_sort(void);
10441181Sdfr
10541181Sdfr
10641181Sdfr/* dump an ICMP Router Discovery Advertisement Message
10741181Sdfr */
10847613Sdfrstatic void
10947613Sdfrtrace_rdisc(const char	*act,
11047613Sdfr	    naddr	from,
11147613Sdfr	    naddr	to,
11247613Sdfr	    struct interface *ifp,
11338136Sdfr	    union ad_u	*p,
11438136Sdfr	    u_int	len)
11538136Sdfr{
11638136Sdfr	int i;
11738136Sdfr	n_long *wp, *lim;
11838136Sdfr
11938136Sdfr
12038136Sdfr	if (!TRACEPACKETS || ftrace == 0)
12138136Sdfr		return;
12245720Speter
12338136Sdfr	lastlog();
12438136Sdfr
12538136Sdfr	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
12638136Sdfr		(void)fprintf(ftrace, "%s Router Ad"
12738136Sdfr			      " from %s to %s via %s life=%d\n",
12838136Sdfr			      act, naddr_ntoa(from), naddr_ntoa(to),
12938136Sdfr			      ifp ? ifp->int_name : "?",
13038136Sdfr			      ntohs(p->ad.icmp_ad_life));
13138136Sdfr		if (!TRACECONTENTS)
13238136Sdfr			return;
13338136Sdfr
13438136Sdfr		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
13538136Sdfr		lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
13645720Speter		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
13745720Speter			(void)fprintf(ftrace, "\t%s preference=%d",
13845720Speter				      naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
13947613Sdfr			wp += p->ad.icmp_ad_asize;
14047613Sdfr		}
14147613Sdfr		(void)fputc('\n',ftrace);
14247613Sdfr
14347613Sdfr	} else {
14450769Sdfr		trace_act("%s Router Solic. from %s to %s via %s value=%#x",
14550769Sdfr			  act, naddr_ntoa(from), naddr_ntoa(to),
14650769Sdfr			  ifp ? ifp->int_name : "?",
14750769Sdfr			  (int)ntohl(p->so.icmp_so_rsvd));
14850769Sdfr	}
14950769Sdfr}
15047613Sdfr
15150769Sdfr/* prepare Router Discovery socket.
15250769Sdfr */
15350769Sdfrstatic void
15450769Sdfrget_rdisc_sock(void)
15550769Sdfr{
15650769Sdfr	if (rdisc_sock < 0) {
15750769Sdfr		rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
15850769Sdfr		if (rdisc_sock < 0)
15950769Sdfr			BADERR(1,"rdisc_sock = socket()");
16055206Speter		fix_sock(rdisc_sock,"rdisc_sock");
16147613Sdfr		fix_select();
16247613Sdfr	}
163}
164
165
166/* Pick multicast group for router-discovery socket
167 */
168void
169set_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 */
253void
254set_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 */
297void
298rdisc_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 */
363void
364if_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 */
383void
384if_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 */
399static void
400del_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 */
448static void
449rdisc_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 */
566static void
567parse_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 */
667static u_short
668in_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 */
689static void
690send_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 */
785static void
786send_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 */
819void
820rdisc_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 */
859void
860rdisc_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 */
900static struct interface *		/* 0 if bad */
901ck_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 */
939void
940read_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