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