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