rdisc.c revision 190711
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 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $FreeBSD: head/sbin/routed/rdisc.c 190711 2009-04-05 14:01:39Z phk $
30 */
31
32#include "defs.h"
33#include <netinet/in_systm.h>
34#include <netinet/ip.h>
35#include <netinet/ip_icmp.h>
36
37#ifdef __NetBSD__
38__RCSID("$NetBSD$");
39#elif defined(__FreeBSD__)
40__RCSID("$FreeBSD: head/sbin/routed/rdisc.c 190711 2009-04-05 14:01:39Z phk $");
41#else
42__RCSID("$Revision: 2.27 $");
43#ident "$Revision: 2.27 $"
44#endif
45
46/* router advertisement ICMP packet */
47struct 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 */
61struct 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
68union ad_u {
69	struct icmp icmp;
70	struct icmp_ad ad;
71	struct icmp_so so;
72};
73
74
75int	rdisc_sock = -1;		/* router-discovery raw socket */
76struct interface *rdisc_sock_mcast;	/* current multicast interface */
77
78struct timeval rdisc_timer;
79int rdisc_ok;				/* using solicited route */
80
81
82#define MAX_ADS 16			/* at least one per interface */
83struct 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 in host byte order */
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/* convert between signed, balanced around zero,
93 * and unsigned zero-based preferences */
94#define SIGN_PREF(p) ((p) ^ MIN_PreferenceLevel)
95#define UNSIGN_PREF(p) SIGN_PREF(p)
96/* adjust unsigned preference by interface metric,
97 * without driving it to infinity */
98#define PREF(p, ifp) ((int)(p) <= ((ifp)->int_metric+(ifp)->int_adj_outmetric)\
99		      ? ((p) != 0 ? 1 : 0)				    \
100		      : (p) - ((ifp)->int_metric+(ifp)->int_adj_outmetric))
101
102static void rdisc_sort(void);
103
104
105/* dump an ICMP Router Discovery Advertisement Message
106 */
107static void
108trace_rdisc(const char	*act,
109	    naddr	from,
110	    naddr	to,
111	    struct interface *ifp,
112	    union ad_u	*p,
113	    u_int	len)
114{
115	int i;
116	n_long *wp, *lim;
117
118
119	if (!TRACEPACKETS || ftrace == 0)
120		return;
121
122	lastlog();
123
124	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
125		(void)fprintf(ftrace, "%s Router Ad"
126			      " from %s to %s via %s life=%d\n",
127			      act, naddr_ntoa(from), naddr_ntoa(to),
128			      ifp ? ifp->int_name : "?",
129			      ntohs(p->ad.icmp_ad_life));
130		if (!TRACECONTENTS)
131			return;
132
133		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
134		lim = &wp[(len - sizeof(p->ad)) / sizeof(*wp)];
135		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
136			(void)fprintf(ftrace, "\t%s preference=%d",
137				      naddr_ntoa(wp[0]), (int)ntohl(wp[1]));
138			wp += p->ad.icmp_ad_asize;
139		}
140		(void)fputc('\n',ftrace);
141
142	} else {
143		trace_act("%s Router Solic. from %s to %s via %s value=%#x",
144			  act, naddr_ntoa(from), naddr_ntoa(to),
145			  ifp ? ifp->int_name : "?",
146			  (int)ntohl(p->so.icmp_so_rsvd));
147	}
148}
149
150/* prepare Router Discovery socket.
151 */
152static void
153get_rdisc_sock(void)
154{
155	if (rdisc_sock < 0) {
156		rdisc_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
157		if (rdisc_sock < 0)
158			BADERR(1,"rdisc_sock = socket()");
159		fix_sock(rdisc_sock,"rdisc_sock");
160		fix_select();
161	}
162}
163
164
165/* Pick multicast group for router-discovery socket
166 */
167void
168set_rdisc_mg(struct interface *ifp,
169	     int on)			/* 0=turn it off */
170{
171	struct group_req gr;
172	struct sockaddr_in *sin;
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	memset(&gr, 0, sizeof(gr));
190	gr.gr_interface = ifp->int_index;
191	sin = (struct sockaddr_in *)&gr.gr_group;
192	sin->sin_family = AF_INET;
193#ifdef _HAVE_SIN_LEN
194	sin->sin_len = sizeof(struct sockaddr_in);
195#endif
196
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			sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
204			if (setsockopt(rdisc_sock, IPPROTO_IP,
205				       MCAST_LEAVE_GROUP,
206				       &gr, sizeof(gr)) < 0)
207				LOGERR("MCAST_LEAVE_GROUP 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		sin->sin_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
215		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
216			       &gr, sizeof(gr)) < 0) {
217			LOGERR("MCAST_JOIN_GROUP 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			sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
230			if (setsockopt(rdisc_sock, IPPROTO_IP,
231				       MCAST_LEAVE_GROUP,
232				       &gr, sizeof(gr)) < 0)
233				LOGERR("MCAST_LEAVE_GROUP 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		sin->sin_addr.s_addr = htonl(INADDR_ALLROUTERS_GROUP);
241		if (setsockopt(rdisc_sock, IPPROTO_IP, MCAST_JOIN_GROUP,
242			       &gr, sizeof(gr)) < 0) {
243			LOGERR("MCAST_JOIN_GROUP 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	LIST_FOREACH(ifp, &ifnet, int_list) {
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,			/* in host byte order */
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 = 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 rsin;
697	int flags;
698	const char *msg;
699
700
701	memset(&rsin, 0, sizeof(rsin));
702	rsin.sin_addr.s_addr = dst;
703	rsin.sin_family = AF_INET;
704#ifdef _HAVE_SIN_LEN
705	rsin.sin_len = sizeof(rsin);
706#endif
707	flags = MSG_DONTROUTE;
708
709	switch (type) {
710	case 0:				/* unicast */
711	default:
712		msg = "Send";
713		break;
714
715	case 1:				/* broadcast */
716		if (ifp->int_if_flags & IFF_POINTOPOINT) {
717			msg = "Send pt-to-pt";
718			rsin.sin_addr.s_addr = ifp->int_dstaddr;
719		} else {
720			msg = "Send broadcast";
721			rsin.sin_addr.s_addr = ifp->int_brdaddr;
722		}
723		break;
724
725	case 2:				/* multicast */
726		msg = "Send multicast";
727		if (ifp->int_state & IS_DUP) {
728			trace_act("abort multicast output via %s"
729				  " with duplicate address",
730				  ifp->int_name);
731			return;
732		}
733		if (rdisc_sock_mcast != ifp) {
734			/* select the right interface. */
735			struct ip_mreqn mreqn;
736
737			memset(&mreqn, 0, sizeof(struct ip_mreqn));
738			mreqn.imr_ifindex = ifp->int_index;
739			if (0 > setsockopt(rdisc_sock,
740					   IPPROTO_IP, IP_MULTICAST_IF,
741					   &mreqn,
742					   sizeof(mreqn))) {
743				LOGERR("setsockopt(rdisc_sock,"
744				       "IP_MULTICAST_IF)");
745				rdisc_sock_mcast = 0;
746				return;
747			}
748			rdisc_sock_mcast = ifp;
749		}
750		flags = 0;
751		break;
752	}
753
754	if (rdisc_sock < 0)
755		get_rdisc_sock();
756
757	trace_rdisc(msg, ifp->int_addr, rsin.sin_addr.s_addr, ifp,
758		    p, p_size);
759
760	if (0 > sendto(rdisc_sock, p, p_size, flags,
761		       (struct sockaddr *)&rsin, sizeof(rsin))) {
762		if (ifp == 0 || !(ifp->int_state & IS_BROKE))
763			msglog("sendto(%s%s%s): %s",
764			       ifp != 0 ? ifp->int_name : "",
765			       ifp != 0 ? ", " : "",
766			       inet_ntoa(rsin.sin_addr),
767			       strerror(errno));
768		if (ifp != 0)
769			if_sick(ifp);
770	}
771}
772
773
774/* Send an advertisement
775 */
776static void
777send_adv(struct interface *ifp,
778	 naddr	dst,			/* 0 or unicast destination */
779	 int	type)			/* 0=unicast, 1=bcast, 2=mcast */
780{
781	union ad_u u;
782	n_long pref;
783
784
785	memset(&u, 0, sizeof(u.ad));
786
787	u.ad.icmp_type = ICMP_ROUTERADVERT;
788	u.ad.icmp_ad_num = 1;
789	u.ad.icmp_ad_asize = sizeof(u.ad.icmp_ad_info[0])/4;
790
791	u.ad.icmp_ad_life = stopint ? 0 : htons(ifp->int_rdisc_int*3);
792
793	/* Convert the configured preference to an unsigned value,
794	 * bias it by the interface metric, and then send it as a
795	 * signed, network byte order value.
796	 */
797	pref = UNSIGN_PREF(ifp->int_rdisc_pref);
798	u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(SIGN_PREF(PREF(pref, ifp)));
799
800	u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
801
802	u.ad.icmp_cksum = in_cksum((u_short*)&u.ad, sizeof(u.ad));
803
804	send_rdisc(&u, sizeof(u.ad), ifp, dst, type);
805}
806
807
808/* Advertise for Router Discovery
809 */
810void
811rdisc_adv(void)
812{
813	struct interface *ifp;
814
815	if (!supplier)
816		return;
817
818	rdisc_timer.tv_sec = now.tv_sec + NEVER;
819
820	LIST_FOREACH(ifp, &ifnet, int_list) {
821		if (0 != (ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)))
822			continue;
823
824		if (!timercmp(&ifp->int_rdisc_timer, &now, >)
825		    || stopint) {
826			send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
827				 (ifp->int_state&IS_BCAST_RDISC) ? 1 : 2);
828			ifp->int_rdisc_cnt++;
829
830			intvl_random(&ifp->int_rdisc_timer,
831				     (ifp->int_rdisc_int*3)/4,
832				     ifp->int_rdisc_int);
833			if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS
834			    && (ifp->int_rdisc_timer.tv_sec
835				> MAX_INITIAL_ADVERT_INTERVAL)) {
836				ifp->int_rdisc_timer.tv_sec
837				= MAX_INITIAL_ADVERT_INTERVAL;
838			}
839			timevaladd(&ifp->int_rdisc_timer, &now);
840		}
841
842		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
843			rdisc_timer = ifp->int_rdisc_timer;
844	}
845}
846
847
848/* Solicit for Router Discovery
849 */
850void
851rdisc_sol(void)
852{
853	struct interface *ifp;
854	union ad_u u;
855
856
857	if (supplier)
858		return;
859
860	rdisc_timer.tv_sec = now.tv_sec + NEVER;
861
862	LIST_FOREACH(ifp, &ifnet, int_list) {
863		if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE))
864		    || ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
865			continue;
866
867		if (!timercmp(&ifp->int_rdisc_timer, &now, >)) {
868			memset(&u, 0, sizeof(u.so));
869			u.so.icmp_type = ICMP_ROUTERSOLICIT;
870			u.so.icmp_cksum = in_cksum((u_short*)&u.so,
871						   sizeof(u.so));
872			send_rdisc(&u, sizeof(u.so), ifp,
873				   htonl(INADDR_ALLROUTERS_GROUP),
874				   ((ifp->int_state&IS_BCAST_RDISC) ? 1 : 2));
875
876			if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
877				continue;
878
879			ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
880			ifp->int_rdisc_timer.tv_usec = 0;
881			timevaladd(&ifp->int_rdisc_timer, &now);
882		}
883
884		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, >))
885			rdisc_timer = ifp->int_rdisc_timer;
886	}
887}
888
889
890/* check the IP header of a possible Router Discovery ICMP packet */
891static struct interface *		/* 0 if bad */
892ck_icmp(const char *act,
893	naddr	from,
894	struct interface *ifp,
895	naddr	to,
896	union ad_u *p,
897	u_int	len)
898{
899	const char *type;
900
901
902	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
903		type = "advertisement";
904	} else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
905		type = "solicitation";
906	} else {
907		return 0;
908	}
909
910	if (p->icmp.icmp_code != 0) {
911		trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
912			  type, p->icmp.icmp_code,
913			  naddr_ntoa(from), naddr_ntoa(to));
914		return 0;
915	}
916
917	trace_rdisc(act, from, to, ifp, p, len);
918
919	if (ifp == 0)
920		trace_pkt("unknown interface for router-discovery %s"
921			  " from %s to %s",
922			  type, naddr_ntoa(from), naddr_ntoa(to));
923
924	return ifp;
925}
926
927
928/* read packets from the router discovery socket
929 */
930void
931read_d(void)
932{
933	static struct msg_limit bad_asize, bad_len;
934#ifdef USE_PASSIFNAME
935	static struct msg_limit  bad_name;
936#endif
937	struct sockaddr_in from;
938	int n, fromlen, cc, hlen;
939	struct {
940#ifdef USE_PASSIFNAME
941		char	ifname[IFNAMSIZ];
942#endif
943		union {
944			struct ip ip;
945			u_short s[512/2];
946			u_char	b[512];
947		} pkt;
948	} buf;
949	union ad_u *p;
950	n_long *wp;
951	struct interface *ifp;
952
953
954	for (;;) {
955		fromlen = sizeof(from);
956		cc = recvfrom(rdisc_sock, &buf, sizeof(buf), 0,
957			      (struct sockaddr*)&from,
958			      &fromlen);
959		if (cc <= 0) {
960			if (cc < 0 && errno != EWOULDBLOCK)
961				LOGERR("recvfrom(rdisc_sock)");
962			break;
963		}
964		if (fromlen != sizeof(struct sockaddr_in))
965			logbad(1,"impossible recvfrom(rdisc_sock) fromlen=%d",
966			       fromlen);
967#ifdef USE_PASSIFNAME
968		if ((cc -= sizeof(buf.ifname)) < 0)
969			logbad(0,"missing USE_PASSIFNAME; only %d bytes",
970			       cc+sizeof(buf.ifname));
971#endif
972
973		hlen = buf.pkt.ip.ip_hl << 2;
974		if (cc < hlen + ICMP_MINLEN)
975			continue;
976		p = (union ad_u *)&buf.pkt.b[hlen];
977		cc -= hlen;
978
979#ifdef USE_PASSIFNAME
980		ifp = ifwithname(buf.ifname, 0);
981		if (ifp == 0)
982			msglim(&bad_name, from.sin_addr.s_addr,
983			       "impossible rdisc if_ name %.*s",
984			       IFNAMSIZ, buf.ifname);
985#else
986		/* If we could tell the interface on which a packet from
987		 * address 0 arrived, we could deal with such solicitations.
988		 */
989		ifp = ((from.sin_addr.s_addr == 0)
990		       ? 0 : iflookup(from.sin_addr.s_addr));
991#endif
992		ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
993			      buf.pkt.ip.ip_dst.s_addr, p, cc);
994		if (ifp == 0)
995			continue;
996		if (ifwithaddr(from.sin_addr.s_addr, 0, 0)) {
997			trace_pkt("    "
998				  "discard our own Router Discovery message");
999			continue;
1000		}
1001
1002		switch (p->icmp.icmp_type) {
1003		case ICMP_ROUTERADVERT:
1004			if (p->ad.icmp_ad_asize*4
1005			    < (int)sizeof(p->ad.icmp_ad_info[0])) {
1006				msglim(&bad_asize, from.sin_addr.s_addr,
1007				       "intolerable rdisc address size=%d",
1008				       p->ad.icmp_ad_asize);
1009				continue;
1010			}
1011			if (p->ad.icmp_ad_num == 0) {
1012				trace_pkt("    empty?");
1013				continue;
1014			}
1015			if (cc != (int)(sizeof(p->ad)
1016					- sizeof(p->ad.icmp_ad_info)
1017					+ (p->ad.icmp_ad_num
1018					   * sizeof(p->ad.icmp_ad_info[0])))) {
1019				msglim(&bad_len, from.sin_addr.s_addr,
1020				       "rdisc length %d does not match ad_num"
1021				       " %d", cc, p->ad.icmp_ad_num);
1022				continue;
1023			}
1024			if (supplier)
1025				continue;
1026			if (ifp->int_state & IS_NO_ADV_IN)
1027				continue;
1028
1029			wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
1030			for (n = 0; n < p->ad.icmp_ad_num; n++) {
1031				parse_ad(from.sin_addr.s_addr,
1032					 wp[0], wp[1],
1033					 ntohs(p->ad.icmp_ad_life),
1034					 ifp);
1035				wp += p->ad.icmp_ad_asize;
1036			}
1037			break;
1038
1039
1040		case ICMP_ROUTERSOLICIT:
1041			if (!supplier)
1042				continue;
1043			if (ifp->int_state & IS_NO_ADV_OUT)
1044				continue;
1045			if (stopint)
1046				continue;
1047
1048			/* XXX
1049			 * We should handle messages from address 0.
1050			 */
1051
1052			/* Respond with a point-to-point advertisement */
1053			send_adv(ifp, from.sin_addr.s_addr, 0);
1054			break;
1055		}
1056	}
1057
1058	rdisc_sort();
1059}
1060