xref: /freebsd/sbin/routed/rdisc.c (revision a8445737e740901f5f2c8d24c12ef7fc8b00134e)
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
36 static char sccsid[] = "@(#)rdisc.c	8.1 (Berkeley) x/y/95";
37 #endif
38 static 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 */
48 struct 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 */
62 struct 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 
69 union ad_u {
70 	struct icmp icmp;
71 	struct icmp_ad ad;
72 	struct icmp_so so;
73 };
74 
75 
76 int	rdisc_sock = -1;		/* router-discovery raw socket */
77 struct interface *rdisc_sock_mcast;	/* current multicast interface */
78 
79 struct timeval rdisc_timer;
80 int rdisc_ok;				/* using solicited route */
81 
82 
83 #define MAX_ADS 5
84 struct 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 
97 static void rdisc_sort(void);
98 
99 
100 /* dump an ICMP Router Discovery Advertisement Message
101  */
102 static void
103 trace_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  */
147 static void
148 get_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  */
162 void
163 set_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  */
247 void
248 set_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  */
291 void
292 rdisc_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  */
354 void
355 if_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  */
372 void
373 if_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  */
388 static void
389 del_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  */
428 static void
429 rdisc_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  */
547 static void
548 parse_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  */
648 static u_short
649 in_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  */
670 static void
671 send_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  */
766 static void
767 send_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  */
796 void
797 rdisc_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  */
836 void
837 rdisc_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 */
877 static struct interface *		/* 0 if bad */
878 ck_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  */
916 void
917 read_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