xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/in.routed/rdisc.c (revision 924db11bd45e384ed350190cc9fc0f9ea85e21dd)
1 /*
2  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  *
5  * Copyright (c) 1995
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgment:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  * $FreeBSD: src/sbin/routed/rdisc.c,v 1.8 2000/08/11 08:24:38 sheldonh Exp $
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 #include "defs.h"
42 #include <netinet/in_systm.h>
43 #include <netinet/ip.h>
44 #include <netinet/ip_icmp.h>
45 #include <fcntl.h>
46 #include <strings.h>
47 
48 /*
49  * The size of the control buffer passed to recvmsg() used to receive
50  * ancillary data.
51  */
52 #define	CONTROL_BUFSIZE	1024
53 
54 /* router advertisement ICMP packet */
55 struct icmp_ad {
56 	uint8_t    icmp_type;		/* type of message */
57 	uint8_t    icmp_code;		/* type sub code */
58 	uint16_t   icmp_cksum;		/* ones complement cksum of struct */
59 	uint8_t    icmp_ad_num;	/* # of following router addresses */
60 	uint8_t    icmp_ad_asize;	/* 2--words in each advertisement */
61 	uint16_t   icmp_ad_life;	/* seconds of validity */
62 	struct icmp_ad_info {
63 	    in_addr_t  icmp_ad_addr;
64 	    uint32_t  icmp_ad_pref;
65 	} icmp_ad_info[1];
66 };
67 
68 /* router solicitation ICMP packet */
69 struct icmp_so {
70 	uint8_t    icmp_type;		/* type of message */
71 	uint8_t    icmp_code;		/* type sub code */
72 	uint16_t   icmp_cksum;		/* ones complement cksum of struct */
73 	uint32_t   icmp_so_rsvd;
74 };
75 
76 union ad_u {
77 	struct icmp icmp;
78 	struct icmp_ad ad;
79 	struct icmp_so so;
80 };
81 
82 
83 int	rdisc_sock = -1;		/* router-discovery raw socket */
84 int	rdisc_mib_sock = -1;		/* AF_UNIX mib info socket */
85 static struct interface *rdisc_sock_interface; /* current rdisc interface */
86 
87 struct timeval rdisc_timer;
88 boolean_t rdisc_ok;				/* using solicited route */
89 
90 #define	MAX_ADS		16
91 int max_ads; /* at least one per interface */
92 /* accumulated advertisements */
93 static struct dr *cur_drp;
94 struct dr *drs;
95 
96 /*
97  * adjust unsigned preference by interface metric,
98  * without driving it to infinity
99  */
100 #define	PREF(p, ifp) ((p) <= (uint32_t)(ifp)->int_metric ? ((p) != 0 ? 1 : 0) \
101 	: (p) - ((ifp)->int_metric))
102 
103 static void rdisc_sort(void);
104 
105 typedef enum { unicast, bcast, mcast } dstaddr_t;
106 
107 /* dump an ICMP Router Discovery Advertisement Message */
108 static void
109 trace_rdisc(const char	*act,
110     uint32_t from,
111     uint32_t to,
112     struct interface *ifp,
113     union ad_u	*p,
114     uint_t len)
115 {
116 	int i;
117 	n_long *wp, *lim;
118 
119 
120 	if (!TRACEPACKETS || ftrace == 0)
121 		return;
122 
123 	lastlog();
124 
125 	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
126 		(void) fprintf(ftrace, "%s Router Ad"
127 		    " from %s to %s via %s life=%d\n",
128 		    act, naddr_ntoa(from), naddr_ntoa(to),
129 		    ifp ? ifp->int_name : "?",
130 		    ntohs(p->ad.icmp_ad_life));
131 		if (!TRACECONTENTS)
132 			return;
133 
134 		wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
135 		lim = &wp[(len - sizeof (p->ad)) / sizeof (*wp)];
136 		for (i = 0; i < p->ad.icmp_ad_num && wp <= lim; i++) {
137 			(void) fprintf(ftrace, "\t%s preference=%ld",
138 			    naddr_ntoa(wp[0]), (long)ntohl(wp[1]));
139 			wp += p->ad.icmp_ad_asize;
140 		}
141 		(void) fputc('\n', ftrace);
142 
143 	} else {
144 		trace_act("%s Router Solic. from %s to %s via %s rsvd=%#x",
145 		    act, naddr_ntoa(from), naddr_ntoa(to),
146 		    ifp ? ifp->int_name : "?",
147 		    ntohl(p->so.icmp_so_rsvd));
148 	}
149 }
150 
151 /*
152  * Prepare Router Discovery socket.
153  */
154 static void
155 get_rdisc_sock(void)
156 {
157 	int on = 1;
158 	unsigned char ttl = 1;
159 	struct sockaddr_un laddr;
160 	int len;
161 
162 	if (rdisc_sock < 0) {
163 		max_ads = MAX_ADS;
164 		drs = rtmalloc(max_ads * sizeof (struct dr), "get_rdisc_sock");
165 		(void) memset(drs, 0, max_ads * sizeof (struct dr));
166 		rdisc_sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
167 		if (rdisc_sock < 0)
168 			BADERR(_B_TRUE, "rdisc_sock = socket()");
169 		fix_sock(rdisc_sock, "rdisc_sock");
170 
171 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_RECVIF, &on,
172 		    sizeof (on)))
173 			BADERR(_B_FALSE, "setsockopt(IP_RECVIF)");
174 
175 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_TTL,
176 		    &ttl, sizeof (ttl)) < 0)
177 			DBGERR(_B_TRUE,
178 			    "rdisc_sock setsockopt(IP_MULTICAST_TTL)");
179 
180 		/*
181 		 * On Solaris also open an AF_UNIX socket to
182 		 * pass default router information to mib agent
183 		 */
184 
185 		rdisc_mib_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
186 		if (rdisc_mib_sock < 0) {
187 			BADERR(_B_TRUE, "rdisc_mib_sock = socket()");
188 		}
189 
190 		bzero(&laddr, sizeof (laddr));
191 		laddr.sun_family = AF_UNIX;
192 
193 		(void) strncpy(laddr.sun_path, RDISC_SNMP_SOCKET,
194 		    sizeof (laddr.sun_path));
195 		len = sizeof (struct sockaddr_un);
196 
197 		(void) unlink(RDISC_SNMP_SOCKET);
198 
199 		if (bind(rdisc_mib_sock, (struct sockaddr *)&laddr, len) < 0) {
200 			BADERR(_B_TRUE, "bind(rdisc_mib_sock)");
201 		}
202 
203 		if (fcntl(rdisc_mib_sock, F_SETFL, O_NONBLOCK) < 0) {
204 			BADERR(_B_TRUE, "rdisc_mib_sock fcntl O_NONBLOCK");
205 		}
206 
207 		fix_select();
208 	}
209 }
210 
211 
212 /*
213  * Pick multicast group for router-discovery socket
214  */
215 void
216 set_rdisc_mg(struct interface *ifp,
217     int on)	/* 0=turn it off */
218 {
219 	struct ip_mreq m;
220 	boolean_t dosupply;
221 
222 	if (rdisc_sock < 0) {
223 		/*
224 		 * Create the raw socket so that we can hear at least
225 		 * broadcast router discovery packets.
226 		 */
227 		if ((ifp->int_state & IS_NO_RDISC) == IS_NO_RDISC ||
228 		    !on)
229 			return;
230 		get_rdisc_sock();
231 	}
232 
233 	if (!(ifp->int_if_flags & IFF_MULTICAST)) {
234 		/* Can't multicast, so no groups could have been joined. */
235 		ifp->int_state &= ~(IS_ALL_HOSTS | IS_ALL_ROUTERS);
236 		return;
237 	}
238 
239 	dosupply = should_supply(ifp);
240 
241 	(void) memset(&m, 0, sizeof (m));
242 	m.imr_interface.s_addr = ((ifp->int_if_flags & IFF_POINTOPOINT) &&
243 	    (ifp->int_dstaddr != 0) ? ifp->int_dstaddr : ifp->int_addr);
244 	if (dosupply || (ifp->int_state & IS_NO_ADV_IN) || !on) {
245 		/* stop listening to advertisements */
246 		if (ifp->int_state & IS_ALL_HOSTS) {
247 			m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
248 			if (setsockopt(rdisc_sock, IPPROTO_IP,
249 			    IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
250 			    errno != EADDRNOTAVAIL && errno != ENOENT)
251 				LOGERR("IP_DROP_MEMBERSHIP ALLHOSTS");
252 			ifp->int_state &= ~IS_ALL_HOSTS;
253 		}
254 
255 	} else if (!(ifp->int_state & IS_ALL_HOSTS)) {
256 		/* start listening to advertisements */
257 		m.imr_multiaddr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
258 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
259 		    &m, sizeof (m)) < 0) {
260 			LOGERR("IP_ADD_MEMBERSHIP ALLHOSTS");
261 		} else {
262 			ifp->int_state |= IS_ALL_HOSTS;
263 		}
264 	}
265 
266 	if (!dosupply || (ifp->int_state & IS_NO_ADV_OUT) ||
267 	    !IS_IFF_ROUTING(ifp->int_if_flags) || !on) {
268 		/* stop listening to solicitations */
269 		if (ifp->int_state & IS_ALL_ROUTERS) {
270 			m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
271 			if (setsockopt(rdisc_sock, IPPROTO_IP,
272 			    IP_DROP_MEMBERSHIP, &m, sizeof (m)) < 0 &&
273 			    errno != EADDRNOTAVAIL && errno != ENOENT)
274 				LOGERR("IP_DROP_MEMBERSHIP ALLROUTERS");
275 			ifp->int_state &= ~IS_ALL_ROUTERS;
276 		}
277 
278 	} else if (!(ifp->int_state & IS_ALL_ROUTERS)) {
279 		/* start hearing solicitations */
280 		m.imr_multiaddr.s_addr = htonl(INADDR_ALLRTRS_GROUP);
281 		if (setsockopt(rdisc_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
282 		    &m, sizeof (m)) < 0) {
283 			LOGERR("IP_ADD_MEMBERSHIP ALLROUTERS");
284 		} else {
285 			ifp->int_state |= IS_ALL_ROUTERS;
286 		}
287 	}
288 }
289 
290 
291 /*
292  * start or stop supplying routes to other systems.
293  */
294 void
295 set_supplier(void)
296 {
297 	struct interface *ifp;
298 	struct dr *drp;
299 	static boolean_t supplystate = _B_FALSE;
300 
301 	if (supplystate == (fwd_interfaces > 1))
302 		return;
303 	supplystate = fwd_interfaces > 1;
304 
305 	trace_act("%d forwarding interfaces present; becoming %ssupplier",
306 	    fwd_interfaces, supplystate ? "" : "non-");
307 
308 	if (supplystate) {
309 		/* Forget discovered routes. */
310 		for (drp = drs; drp < &drs[max_ads]; drp++) {
311 			drp->dr_recv_pref = DEF_PREFERENCELEVEL;
312 			drp->dr_life = 0;
313 		}
314 		rdisc_age(0);
315 
316 		/*
317 		 * Do not start advertising until we have heard some
318 		 * RIP routes.
319 		 */
320 		LIM_SEC(rdisc_timer, now.tv_sec+MIN_WAITTIME);
321 
322 		/* get rid of any redirects */
323 		del_redirects(0, 0);
324 	} else {
325 		/*
326 		 * Flush out all those advertisements we had sent by sending
327 		 * one with lifetime=0.
328 		 */
329 		rdisc_adv(_B_TRUE);
330 	}
331 
332 	/*
333 	 * Switch router discovery multicast groups from soliciting
334 	 * to advertising or back.
335 	 */
336 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
337 		if (ifp->int_state & IS_BROKE)
338 			continue;
339 		ifp->int_rdisc_cnt = 0;
340 		ifp->int_rdisc_timer.tv_usec = rdisc_timer.tv_usec;
341 		ifp->int_rdisc_timer.tv_sec = now.tv_sec+MIN_WAITTIME;
342 		set_rdisc_mg(ifp, 1);
343 	}
344 }
345 
346 
347 /*
348  * Age discovered routes and find the best one
349  */
350 void
351 rdisc_age(in_addr_t bad_gate)
352 {
353 	time_t sec;
354 	struct dr *drp;
355 	struct rt_spare new;
356 	struct rt_entry *rt;
357 
358 	/*
359 	 * If we are being told about a bad router,
360 	 * then age the discovered default route, and if there is
361 	 * no alternative, solicit a replacement.
362 	 */
363 	if (bad_gate != 0) {
364 		/*
365 		 * Look for the bad discovered default route.
366 		 * Age it and note its interface.
367 		 */
368 		for (drp = drs; drp < &drs[max_ads]; drp++) {
369 			if (drp->dr_ts == 0)
370 				continue;
371 
372 			/*
373 			 * When we find the bad router, age the route
374 			 * to at most SUPPLY_INTERVAL.
375 			 * This is contrary to RFC 1256, but defends against
376 			 * black holes.
377 			 */
378 			if (drp->dr_gate == bad_gate) {
379 				sec = (now.tv_sec - drp->dr_life +
380 				    SUPPLY_INTERVAL);
381 				if (drp->dr_ts > sec) {
382 					trace_act("age 0.0.0.0 --> %s via %s",
383 					    naddr_ntoa(drp->dr_gate),
384 					    drp->dr_ifp->int_name);
385 					drp->dr_ts = sec;
386 				}
387 				break;
388 			}
389 		}
390 	} else if (should_supply(NULL)) {
391 		/*
392 		 * If switching from client to server, get rid of old
393 		 * default routes.
394 		 */
395 		if (cur_drp != NULL) {
396 			rt = rtget(RIP_DEFAULT, 0);
397 			/*
398 			 * If there is a current default router, and the
399 			 * there is no rt_spare entry, create one
400 			 * for cur_drp to prevent segmentation fault
401 			 * at rdisc_sort.
402 			 */
403 			if (rt == NULL) {
404 				(void) memset(&new, 0, sizeof (new));
405 				new.rts_ifp = cur_drp->dr_ifp;
406 				new.rts_gate = cur_drp->dr_gate;
407 				new.rts_router = cur_drp->dr_gate;
408 				new.rts_metric = HOPCNT_INFINITY-1;
409 				new.rts_time = now.tv_sec;
410 				new.rts_origin = RO_RDISC;
411 				rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
412 			}
413 
414 			rdisc_sort();
415 		}
416 		rdisc_adv(_B_FALSE);
417 	}
418 
419 	rdisc_sol();
420 	if (cur_drp != NULL) {
421 		rt = rtget(RIP_DEFAULT, 0);
422 		if (rt == NULL) {
423 			(void) memset(&new, 0, sizeof (new));
424 			new.rts_ifp = cur_drp->dr_ifp;
425 			new.rts_gate = cur_drp->dr_gate;
426 			new.rts_router = cur_drp->dr_gate;
427 			new.rts_metric = HOPCNT_INFINITY-1;
428 			new.rts_time = now.tv_sec;
429 			new.rts_origin = RO_RDISC;
430 			rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
431 		}
432 	}
433 	rdisc_sort();
434 
435 	/*
436 	 * Delete old redirected routes to keep the kernel table small,
437 	 * and to prevent black holes.  Check that the kernel table
438 	 * matches the daemon table (i.e. has the default route).
439 	 * But only if RIP is not running and we are not dealing with
440 	 * a bad gateway, since otherwise age() will be called.
441 	 */
442 	if (rip_sock < 0 && bad_gate == 0)
443 		age(0);
444 }
445 
446 
447 /*
448  * Zap all routes discovered via an interface that has gone bad
449  * This should only be called when !(ifp->int_state & IS_DUP)
450  * This is called by if_del and if_bad, and the interface pointer
451  * might not be valid after this.
452  */
453 void
454 if_bad_rdisc(struct interface *ifp)
455 {
456 	struct dr *drp;
457 
458 	for (drp = drs; drp < &drs[max_ads]; drp++) {
459 		if (drp->dr_ifp != ifp)
460 			continue;
461 		(void) memset(drp, 0, sizeof (*drp));
462 	}
463 
464 	/* make a note to re-solicit, turn RIP on or off, etc. */
465 	rdisc_timer.tv_sec = 0;
466 }
467 
468 /*
469  * Rewire all routes discovered via an interface that has gone bad
470  * This is only called by if_del.
471  */
472 void
473 if_rewire_rdisc(struct interface *oldifp, struct interface *newifp)
474 {
475 	struct dr *drp;
476 
477 	for (drp = drs; drp < &drs[max_ads]; drp++) {
478 		if (drp->dr_ifp != oldifp)
479 			continue;
480 		drp->dr_ifp = newifp;
481 		drp->dr_pref += (newifp->int_metric - oldifp->int_metric);
482 		drp->dr_flags |= DR_CHANGED;
483 	}
484 
485 	/* make a note to re-solicit, turn RIP on or off, etc. */
486 	rdisc_timer.tv_sec = 0;
487 }
488 
489 /*
490  * Mark an interface ok for router discovering.
491  * This is called by if_ok and ifinit.
492  */
493 void
494 if_ok_rdisc(struct interface *ifp)
495 {
496 	set_rdisc_mg(ifp, 1);
497 
498 	ifp->int_rdisc_cnt = 0;
499 	ifp->int_rdisc_timer.tv_sec = now.tv_sec +
500 	    ((ifp->int_state & IS_NO_ADV_OUT) ?
501 	    MAX_SOLICITATION_DELAY : MIN_WAITTIME);
502 	if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer, > /* cstyle */))
503 		rdisc_timer = ifp->int_rdisc_timer;
504 }
505 
506 /*
507  * Get rid of a dead discovered router
508  */
509 static void
510 del_rdisc(struct dr *drp)
511 {
512 	struct interface *ifp;
513 	uint32_t gate;
514 	int i;
515 	struct rt_entry *rt;
516 	struct rt_spare *rts = NULL;
517 
518 	del_redirects(gate = drp->dr_gate, 0);
519 	drp->dr_ts = 0;
520 	drp->dr_life = 0;
521 
522 	rt = rtget(RIP_DEFAULT, 0);
523 	if (rt == NULL) {
524 		trace_act("could not find default route in table");
525 	} else {
526 		for (i = 0; i < rt->rt_num_spares; i++) {
527 			if ((rt->rt_spares[i].rts_gate == drp->dr_gate) &&
528 			    (rt->rt_spares[i].rts_origin == RO_RDISC)) {
529 				rts = &rt->rt_spares[i];
530 				break;
531 			}
532 		}
533 		if (rts != NULL)
534 			rts_delete(rt, rts);
535 		else
536 			trace_act("could not find default route "
537 			    "through %s in table", naddr_ntoa(drp->dr_gate));
538 	}
539 
540 	/* Count the other discovered routers on the interface.  */
541 	i = 0;
542 	ifp = drp->dr_ifp;
543 	for (drp = drs; drp < &drs[max_ads]; drp++) {
544 		if (drp->dr_ts != 0 && drp->dr_ifp == ifp)
545 			i++;
546 	}
547 
548 	/*
549 	 * If that was the last good discovered router on the interface,
550 	 * then solicit a new one.
551 	 * This is contrary to RFC 1256, but defends against black holes.
552 	 */
553 	if (i != 0) {
554 		trace_act("discovered router %s via %s"
555 		    " is bad--have %d remaining",
556 		    naddr_ntoa(gate), ifp->int_name, i);
557 	} else if (ifp->int_rdisc_cnt >= MAX_SOLICITATIONS) {
558 		trace_act("last discovered router %s via %s"
559 		    " is bad--re-solicit",
560 		    naddr_ntoa(gate), ifp->int_name);
561 		ifp->int_rdisc_cnt = 0;
562 		ifp->int_rdisc_timer.tv_sec = 0;
563 		rdisc_sol();
564 	} else {
565 		trace_act("last discovered router %s via %s"
566 		    " is bad--wait to solicit",
567 		    naddr_ntoa(gate), ifp->int_name);
568 	}
569 }
570 
571 
572 /* Find the best discovered route, and discard stale routers. */
573 static void
574 rdisc_sort(void)
575 {
576 	struct dr *drp, *new_drp;
577 	struct rt_entry *rt;
578 	struct rt_spare new, *rts;
579 	struct interface *ifp;
580 	uint_t new_st = 0;
581 	uint32_t new_pref = DEF_PREFERENCELEVEL;
582 	int first_rdisc_slot = 0;
583 	int j;
584 	boolean_t spares_avail;
585 	void *ptr;
586 	size_t ptrsize;
587 
588 	rt = rtget(RIP_DEFAULT, 0);
589 
590 	/*
591 	 * If all the rt_spare entries are taken up with with default routes
592 	 * learnt from RIP (ie rts_origin = RO_RIP), bail out.
593 	 * NOTE:
594 	 *	We *always* prefer default routes learned via RIP
595 	 *	(ie RO_RIP) over those learnt via RDISC (ie RO_RDISC).
596 	 *	The rdisc machinery should not modify, replace or
597 	 *	remove any existing default routes with RO_RIP set.
598 	 */
599 	if (rt != NULL) {
600 		spares_avail = _B_FALSE;
601 		for (j = 0; j < rt->rt_num_spares; j++)  {
602 			rts = &rt->rt_spares[j];
603 			if (rts->rts_gate == 0 || rts->rts_origin != RO_RIP ||
604 			    rts->rts_ifp == &dummy_ifp) {
605 				spares_avail = _B_TRUE;
606 				break;
607 			}
608 		}
609 		if (!spares_avail) {
610 			ptrsize = (rt->rt_num_spares + SPARE_INC) *
611 			    sizeof (struct rt_spare);
612 			ptr = realloc(rt->rt_spares, ptrsize);
613 			if (ptr != NULL) {
614 				struct rt_spare *tmprts;
615 
616 				rt->rt_spares = ptr;
617 				rts = &rt->rt_spares[rt->rt_num_spares];
618 				(void) memset(rts, 0,
619 				    (SPARE_INC * sizeof (struct rt_spare)));
620 				rt->rt_num_spares += SPARE_INC;
621 				for (tmprts = rts, j = SPARE_INC;
622 				    j != 0; j--, tmprts++)
623 					tmprts->rts_metric = HOPCNT_INFINITY;
624 				spares_avail = _B_TRUE;
625 			} else {
626 				return;
627 			}
628 		}
629 	}
630 	/* Find the best RDISC advertiser */
631 	rt = NULL;
632 	new_drp = NULL;
633 	for (drp = drs; drp < &drs[max_ads]; drp++) {
634 		if (drp->dr_ts == 0)
635 			continue;
636 		ifp = drp->dr_ifp;
637 
638 		/* Get rid of expired discovered routers. */
639 		if (drp->dr_ts + drp->dr_life <= now.tv_sec) {
640 			del_rdisc(drp);
641 			continue;
642 		}
643 
644 		LIM_SEC(rdisc_timer, drp->dr_ts+drp->dr_life);
645 
646 		/*
647 		 * Update preference with possibly changed interface
648 		 * metric.
649 		 */
650 		drp->dr_pref = PREF(drp->dr_recv_pref, ifp);
651 
652 		/*
653 		 * Prefer the current route to prevent thrashing.
654 		 * Prefer shorter lifetimes to speed the detection of
655 		 * bad routers.
656 		 * Avoid sick interfaces.
657 		 */
658 		if (new_drp == NULL ||
659 		    (!((new_st ^ drp->dr_ifp->int_state) & IS_SICK) &&
660 		    (new_pref < drp->dr_pref ||
661 		    (new_pref == drp->dr_pref && (drp == cur_drp ||
662 		    (new_drp != cur_drp &&
663 		    new_drp->dr_life > drp->dr_life))))) ||
664 		    ((new_st & IS_SICK) &&
665 		    !(drp->dr_ifp->int_state & IS_SICK))) {
666 			new_drp = drp;
667 			new_st = drp->dr_ifp->int_state;
668 			new_pref = drp->dr_pref;
669 		}
670 	}
671 
672 	/*
673 	 * switch to a better RDISC advertiser
674 	 */
675 	if ((new_drp != cur_drp) || (rt == NULL))  {
676 		rt = rtget(RIP_DEFAULT, 0);
677 
678 		/*
679 		 * Purge the table of all the default routes that were
680 		 * learnt via RDISC, while keeping an eye the first available
681 		 * slot for the spare entry of new_drp
682 		 */
683 		if (rt != NULL) {
684 			int i;
685 			for (i = 0; i < rt->rt_num_spares; i++)  {
686 				rts = &rt->rt_spares[i];
687 				if ((rts->rts_gate == 0 ||
688 				    rts->rts_ifp == &dummy_ifp) &&
689 				    first_rdisc_slot == 0)
690 					first_rdisc_slot = i;
691 				if (rts->rts_origin == RO_RDISC) {
692 					rts_delete(rt, rts);
693 					if (first_rdisc_slot == 0) {
694 						first_rdisc_slot = i;
695 					}
696 				}
697 			}
698 		}
699 
700 		/* Stop using RDISC routes if they are all bad */
701 		if (new_drp == NULL) {
702 			trace_act("turn off Router Discovery client");
703 			rdisc_ok = _B_FALSE;
704 
705 		} else {
706 			if (cur_drp == NULL) {
707 				trace_act("turn on Router Discovery client"
708 				    " using %s via %s",
709 				    naddr_ntoa(new_drp->dr_gate),
710 				    new_drp->dr_ifp->int_name);
711 				rdisc_ok = _B_TRUE;
712 			}
713 
714 			/* Prepare a spare entry for the new_drp */
715 			(void) memset(&new, 0, sizeof (new));
716 			new.rts_ifp = new_drp->dr_ifp;
717 			new.rts_gate = new_drp->dr_gate;
718 			new.rts_router = new_drp->dr_gate;
719 			new.rts_metric = HOPCNT_INFINITY-1;
720 			new.rts_time = now.tv_sec;
721 			new.rts_origin = RO_RDISC;
722 			/*
723 			 * If there is no existing default route, add it
724 			 * to rts_spare[0].
725 			 */
726 			if (rt == NULL) {
727 				rtadd(RIP_DEFAULT, 0, RS_NOPROPAGATE, &new);
728 			} else {
729 
730 				/*
731 				 * Add the spare entry for the new_drp in
732 				 * the first available slot
733 				 */
734 				trace_act("Switching to "
735 				    "default router with better "
736 				    "preference %s via %s ",
737 				    naddr_ntoa(new_drp->dr_gate),
738 				    new_drp->dr_ifp->int_name);
739 				rt->rt_spares[first_rdisc_slot] = new;
740 				rt = NULL; /* redo rt_spares */
741 			}
742 		}
743 
744 		/*
745 		 * Get ready to redo the entire table. The table should
746 		 * only include :
747 		 * 	a. empty rt_spare slots
748 		 * 	b. default routes learnt via RIP
749 		 * 	c. default route for the latest best RDISC advertiser
750 		 * 	d. default routes of other RDISC advertisers whose
751 		 *	dr_pref == best RDISC advertiser->dr_pref
752 		 */
753 		cur_drp = new_drp;
754 	}
755 
756 	/* Redo the entire spare table (without touching RO_RIP entries) */
757 	if (rdisc_ok && rt == NULL) {
758 		int i;
759 		/*
760 		 * We've either just turned on router discovery,
761 		 * or switched to a router with better preference.
762 		 * Find all other default routers whose
763 		 * pref == cur_drp->dr_pref and add them as spares
764 		 */
765 
766 		rt = rtget(RIP_DEFAULT, 0);
767 
768 		for (drp = drs; drp < &drs[max_ads]; drp++) {
769 			boolean_t dr_done = _B_FALSE;
770 			int slot = -1;
771 
772 			if (drp->dr_ts == 0)
773 				continue;
774 
775 			if (drp->dr_pref != cur_drp->dr_pref &&
776 			    ((drp->dr_flags & DR_CHANGED) == 0))
777 				continue;
778 
779 			/*
780 			 * Either pref matches cur_drp->dr_pref,
781 			 * or something has changed in this drp.
782 			 * In the former case, we may need to add
783 			 * this to rt_spares. In the latter case,
784 			 * if the pref has changed, need to take it
785 			 * out of rt_spares and the kernel.
786 			 *
787 			 * First, find an empty slot in rt_spares
788 			 * in case we have to add this drp to kernel.
789 			 * Also check if it is already there.
790 			 */
791 			for (i = 0; i < rt->rt_num_spares; i++) {
792 				if (rt->rt_spares[i].rts_gate == 0) {
793 					if (slot < 0)
794 						slot = i;
795 					continue;
796 				}
797 				if ((rt->rt_spares[i].rts_gate ==
798 				    drp->dr_gate) &&
799 				    (rt->rt_spares[i].rts_origin ==
800 				    RO_RDISC)) {
801 					/*
802 					 * a spare entry for this RDISC
803 					 * advertiser already exists. We need
804 					 * to check if this entry still belongs
805 					 * in the table
806 					 */
807 					dr_done = _B_TRUE;
808 					break;
809 				}
810 			}
811 
812 			drp->dr_flags &= ~DR_CHANGED;
813 
814 			if (drp->dr_pref != cur_drp->dr_pref) {
815 				if (dr_done) {
816 					/*
817 					 * The rt_spare of this RDISC advertiser
818 					 * needs to be removed as it no longer
819 					 * belongs in the table because its
820 					 * dr_pref is different than the latest
821 					 * RDISC advertiser's->dr_pref
822 					 */
823 					rts_delete(rt, &rt->rt_spares[i]);
824 				}
825 				continue;
826 			}
827 
828 			if (slot < 0 && !dr_done)  {
829 				ptrsize = (rt->rt_num_spares + SPARE_INC) *
830 				    sizeof (struct rt_spare);
831 				ptr = realloc(rt->rt_spares, ptrsize);
832 				if (ptr != NULL) {
833 					struct rt_spare *tmprts;
834 
835 					rt->rt_spares = ptr;
836 					slot = rt->rt_num_spares;
837 					rts = &rt->rt_spares[rt->rt_num_spares];
838 					(void) memset(rts, 0, (SPARE_INC *
839 					    sizeof (struct rt_spare)));
840 					rt->rt_num_spares += SPARE_INC;
841 					for (tmprts = rts, i = SPARE_INC;
842 					    i != 0; i--, tmprts++)
843 						tmprts->rts_metric =
844 						    HOPCNT_INFINITY;
845 				}
846 			}
847 
848 			if (slot >= 0 && (dr_done != _B_TRUE)) {
849 				(void) memset(&new, 0, sizeof (new));
850 				new.rts_ifp = drp->dr_ifp;
851 				new.rts_gate = drp->dr_gate;
852 				new.rts_router = drp->dr_gate;
853 				new.rts_metric = HOPCNT_INFINITY-1;
854 				new.rts_time = now.tv_sec;
855 				new.rts_origin = RO_RDISC;
856 				rt->rt_spares[slot] = new;
857 				trace_act("spare default %s via %s",
858 				    naddr_ntoa(drp->dr_gate),
859 				    drp->dr_ifp->int_name);
860 			}
861 		}
862 	}
863 
864 	/* turn RIP on or off */
865 	if (!rdisc_ok || rip_interfaces > 1) {
866 		rip_on(0);
867 	} else {
868 		rip_off();
869 	}
870 }
871 
872 
873 /* Handle a single address in an advertisement */
874 static void
875 parse_ad(uint32_t from,
876     in_addr_t gate,
877     uint32_t pref,		/* signed and in network order */
878     ushort_t life,		/* in host byte order */
879     struct interface *ifp)
880 {
881 	static struct msg_limit bad_gate;
882 	struct dr *drp, *new_drp;
883 	void *ptr;
884 	size_t ptrsize;
885 
886 	if (gate == RIP_DEFAULT || !check_dst(gate)) {
887 		msglim(&bad_gate, from, "router %s advertising bad gateway %s",
888 		    naddr_ntoa(from), naddr_ntoa(gate));
889 		return;
890 	}
891 
892 	/*
893 	 * ignore pointers to ourself and routes via unreachable networks
894 	 */
895 	if (ifwithaddr(gate, _B_TRUE, _B_FALSE) != 0) {
896 		trace_pkt("    discard Router Discovery Ad pointing at us");
897 		return;
898 	}
899 	if (!on_net(gate, ifp->int_net, ifp->int_mask)) {
900 		trace_pkt("    discard Router Discovery Ad"
901 		    " toward unreachable net");
902 		return;
903 	}
904 	/*
905 	 * Convert preference to an unsigned value
906 	 * and later bias it by the metric of the interface.
907 	 */
908 	pref = UNSIGN_PREF(ntohl(pref));
909 
910 	if (pref == DEF_PREFERENCELEVEL || life < MIN_MAXADVERTISEINTERVAL) {
911 		pref = DEF_PREFERENCELEVEL;
912 		life = 0;
913 	}
914 
915 	for (new_drp = NULL, drp = drs; drp < &drs[max_ads]; drp++) {
916 		/* accept new info for a familiar entry */
917 		if ((drp->dr_gate == gate) && (drp->dr_ifp == ifp)) {
918 			new_drp = drp;
919 			drp->dr_flags |= DR_CHANGED;
920 			break;
921 		}
922 
923 		if (life == 0)
924 			continue;	/* do not worry about dead ads */
925 
926 		if (drp->dr_ts == 0) {
927 			new_drp = drp;	/* use unused entry */
928 
929 		} else if (new_drp == NULL) {
930 			/* look for an entry worse than the new one to reuse. */
931 			if ((!(ifp->int_state & IS_SICK) &&
932 			    (drp->dr_ifp->int_state & IS_SICK)) ||
933 			    (pref > drp->dr_pref &&
934 			    !((ifp->int_state ^ drp->dr_ifp->int_state) &
935 			    IS_SICK)))
936 				new_drp = drp;
937 
938 		} else if (new_drp->dr_ts != 0) {
939 			/* look for the least valuable entry to reuse */
940 			if ((!(new_drp->dr_ifp->int_state & IS_SICK) &&
941 			    (drp->dr_ifp->int_state & IS_SICK)) ||
942 			    (new_drp->dr_pref > drp->dr_pref &&
943 			    !((new_drp->dr_ifp->int_state ^
944 			    drp->dr_ifp->int_state) & IS_SICK)))
945 				new_drp = drp;
946 		}
947 	}
948 
949 	/* if all of the current entries are better, add more drs[] */
950 	if (new_drp == NULL) {
951 		ptrsize = (max_ads + MAX_ADS) * sizeof (struct dr);
952 		ptr = realloc(drs, ptrsize);
953 		if (ptr == NULL)
954 			return;
955 		drs = ptr;
956 		(void) memset(&drs[max_ads], 0, MAX_ADS * sizeof (struct dr));
957 		new_drp = &drs[max_ads];
958 		max_ads += MAX_ADS;
959 	}
960 
961 	/*
962 	 * Pointer copy is safe here because if_del
963 	 * calls if_bad_rdisc first, so a non-NULL df_ifp
964 	 * is always a valid pointer.
965 	 */
966 	new_drp->dr_ifp = ifp;
967 	new_drp->dr_gate = gate;
968 	new_drp->dr_ts = now.tv_sec;
969 	new_drp->dr_life = life;
970 	new_drp->dr_recv_pref = pref;
971 	/* bias functional preference by metric of the interface */
972 	new_drp->dr_pref = PREF(pref, ifp);
973 
974 	/* after hearing a good advertisement, stop asking */
975 	if (!(ifp->int_state & IS_SICK))
976 		ifp->int_rdisc_cnt = MAX_SOLICITATIONS;
977 }
978 
979 
980 /*
981  * Compute the IP checksum. This assumes the packet is less than 32K long.
982  */
983 static uint16_t
984 in_cksum(uint16_t *p, uint_t len)
985 {
986 	uint32_t sum = 0;
987 	int nwords = len >> 1;
988 
989 	while (nwords-- != 0)
990 		sum += *p++;
991 
992 	if (len & 1)
993 		sum += *(uchar_t *)p;
994 
995 	/* end-around-carry */
996 	sum = (sum >> 16) + (sum & 0xffff);
997 	sum += (sum >> 16);
998 	return (~sum);
999 }
1000 
1001 
1002 /* Send a router discovery advertisement or solicitation ICMP packet. */
1003 static void
1004 send_rdisc(union ad_u *p,
1005     uint_t p_size,
1006     struct interface *ifp,
1007     in_addr_t dst,		/* 0 or unicast destination */
1008     dstaddr_t type)
1009 {
1010 	struct sockaddr_in sin;
1011 	int flags = 0;
1012 	const char *msg;
1013 	int ifindex = 0;
1014 	struct in_addr addr;
1015 
1016 	/*
1017 	 * Don't send Rdisc packets on duplicate interfaces, we
1018 	 * don't want to generate duplicate packets.
1019 	 */
1020 	if (ifp->int_state & IS_DUP)
1021 		return;
1022 
1023 	(void) memset(&sin, 0, sizeof (sin));
1024 	sin.sin_addr.s_addr = dst;
1025 	sin.sin_family = AF_INET;
1026 
1027 	switch (type) {
1028 	case unicast:				/* unicast */
1029 	default:
1030 		flags = MSG_DONTROUTE;
1031 		msg = "Send";
1032 		break;
1033 
1034 	case bcast:				/* broadcast */
1035 		if (ifp->int_if_flags & IFF_POINTOPOINT) {
1036 			msg = "Send pt-to-pt";
1037 			if (ifp->int_dstaddr == 0)
1038 				sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
1039 			else
1040 				sin.sin_addr.s_addr = ifp->int_dstaddr;
1041 		} else {
1042 			msg = "Send broadcast";
1043 			sin.sin_addr.s_addr = ifp->int_brdaddr;
1044 		}
1045 		break;
1046 
1047 	case mcast:				/* multicast */
1048 		msg = "Send multicast";
1049 		break;
1050 	}
1051 
1052 	if (rdisc_sock < 0)
1053 		get_rdisc_sock();
1054 
1055 	/* select the right interface. */
1056 	ifindex = (type != mcast && ifp->int_phys != NULL) ?
1057 	    ifp->int_phys->phyi_index : 0;
1058 
1059 	if (rdisc_sock_interface != ifp) {
1060 		/*
1061 		 * For multicast, we have to choose the source
1062 		 * address.  This is either the local address
1063 		 * (non-point-to-point) or the remote address.
1064 		 */
1065 		addr.s_addr = (ifp->int_if_flags & IFF_POINTOPOINT) ?
1066 		    ifp->int_dstaddr : ifp->int_addr;
1067 		if (type == mcast &&
1068 		    setsockopt(rdisc_sock, IPPROTO_IP, IP_MULTICAST_IF, &addr,
1069 		    sizeof (addr)) == -1) {
1070 			LOGERR("setsockopt(rdisc_sock, IP_MULTICAST_IF)");
1071 			return;
1072 		}
1073 		rdisc_sock_interface = ifp;
1074 	}
1075 
1076 	trace_rdisc(msg, ifp->int_addr, sin.sin_addr.s_addr, ifp, p, p_size);
1077 
1078 	if (0 > sendtoif(rdisc_sock, p, p_size, flags, &sin, ifindex)) {
1079 		if (!(ifp->int_state & IS_BROKE))
1080 			writelog(LOG_WARNING, "sendto(%s%s%s): %s",
1081 			    ifp->int_name, ", ",
1082 			    inet_ntoa(sin.sin_addr),
1083 			    rip_strerror(errno));
1084 		if (ifp != NULL)
1085 			if_sick(ifp, _B_FALSE);
1086 	}
1087 }
1088 
1089 
1090 /* Send an advertisement */
1091 static void
1092 send_adv(struct interface *ifp,
1093     in_addr_t dst,
1094     dstaddr_t type)
1095 {
1096 	union ad_u u;
1097 
1098 	if ((ifp->int_state & (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC)) ==
1099 	    IS_SUPPRESS_RDISC)
1100 		return;
1101 
1102 	(void) memset(&u, 0, sizeof (u.ad));
1103 
1104 	u.ad.icmp_type = ICMP_ROUTERADVERT;
1105 	u.ad.icmp_code = ICMP_ROUTERADVERT_COMMON;
1106 	u.ad.icmp_ad_num = 1;
1107 	u.ad.icmp_ad_asize = sizeof (u.ad.icmp_ad_info[0])/4;
1108 
1109 	u.ad.icmp_ad_life = (stopint || !should_supply(ifp) ||
1110 	    (ifp->int_state & IS_SUPPRESS_RDISC)) ? 0 :
1111 	    htons(ifp->int_rdisc_int*3);
1112 
1113 	/* Send the configured preference as a network byte order value */
1114 	u.ad.icmp_ad_info[0].icmp_ad_pref = htonl(ifp->int_rdisc_pref);
1115 
1116 	u.ad.icmp_ad_info[0].icmp_ad_addr = ifp->int_addr;
1117 
1118 	u.ad.icmp_cksum = in_cksum((uint16_t *)&u.ad, sizeof (u.ad));
1119 
1120 	send_rdisc(&u, sizeof (u.ad), ifp, dst, type);
1121 
1122 	if (ifp->int_state & IS_SUPPRESS_RDISC)
1123 		ifp->int_state &= ~IS_FLUSH_RDISC;
1124 }
1125 
1126 
1127 /* Advertise as a default router by way of router discovery. */
1128 void
1129 rdisc_adv(boolean_t forceadv)
1130 {
1131 	struct interface *ifp;
1132 
1133 	if (!forceadv && !should_supply(NULL))
1134 		return;
1135 
1136 	rdisc_timer.tv_sec = now.tv_sec + NEVER;
1137 
1138 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
1139 		if ((ifp->int_state & (IS_NO_ADV_OUT | IS_BROKE)) ||
1140 		    (!forceadv && !IS_IFF_ROUTING(ifp->int_if_flags)))
1141 			continue;
1142 
1143 		/* skip interfaces we shouldn't use */
1144 		if (IS_IFF_QUIET(ifp->int_if_flags))
1145 			continue;
1146 
1147 		if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */) ||
1148 		    stopint != 0 || forceadv) {
1149 			send_adv(ifp, htonl(INADDR_ALLHOSTS_GROUP),
1150 			    (ifp->int_state & IS_BCAST_RDISC) ? 1 : 2);
1151 			ifp->int_rdisc_cnt++;
1152 
1153 			intvl_random(&ifp->int_rdisc_timer,
1154 			    (ifp->int_rdisc_int*3)/4, ifp->int_rdisc_int);
1155 			if (ifp->int_rdisc_cnt < MAX_INITIAL_ADVERTS &&
1156 			    (ifp->int_rdisc_timer.tv_sec >
1157 			    MAX_INITIAL_ADVERT_INTERVAL)) {
1158 				ifp->int_rdisc_timer.tv_sec =
1159 				    MAX_INITIAL_ADVERT_INTERVAL;
1160 			}
1161 			timevaladd(&ifp->int_rdisc_timer, &now);
1162 		}
1163 		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
1164 		    > /* cstyle */))
1165 			rdisc_timer = ifp->int_rdisc_timer;
1166 	}
1167 }
1168 
1169 
1170 /* Solicit for Router Discovery */
1171 void
1172 rdisc_sol(void)
1173 {
1174 	struct interface *ifp;
1175 	union ad_u u;
1176 
1177 	if (should_supply(NULL))
1178 		return;
1179 
1180 	rdisc_timer.tv_sec = now.tv_sec + NEVER;
1181 
1182 	for (ifp = ifnet; ifp; ifp = ifp->int_next) {
1183 		if (0 != (ifp->int_state & (IS_NO_SOL_OUT | IS_BROKE)) ||
1184 		    ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
1185 			continue;
1186 
1187 		/* skip interfaces we shouldn't use */
1188 		if (IS_IFF_QUIET(ifp->int_if_flags))
1189 			continue;
1190 
1191 		if (!timercmp(&ifp->int_rdisc_timer, &now, > /* cstyle */)) {
1192 			(void) memset(&u, 0, sizeof (u.so));
1193 			u.so.icmp_type = ICMP_ROUTERSOLICIT;
1194 			u.so.icmp_cksum = in_cksum((uint16_t *)&u.so,
1195 			    sizeof (u.so));
1196 			send_rdisc(&u, sizeof (u.so), ifp,
1197 			    htonl(INADDR_ALLRTRS_GROUP),
1198 			    ((ifp->int_state&IS_BCAST_RDISC) ? bcast : mcast));
1199 
1200 			if (++ifp->int_rdisc_cnt >= MAX_SOLICITATIONS)
1201 				continue;
1202 
1203 			ifp->int_rdisc_timer.tv_sec = SOLICITATION_INTERVAL;
1204 			ifp->int_rdisc_timer.tv_usec = 0;
1205 			timevaladd(&ifp->int_rdisc_timer, &now);
1206 		}
1207 
1208 		if (timercmp(&rdisc_timer, &ifp->int_rdisc_timer,
1209 		    > /* cstyle */))
1210 			rdisc_timer = ifp->int_rdisc_timer;
1211 	}
1212 }
1213 
1214 
1215 /*
1216  * check the IP header of a possible Router Discovery ICMP packet
1217  * Returns 0 if bad
1218  */
1219 static struct interface *
1220 ck_icmp(const char *act,
1221     in_addr_t	from,
1222     struct interface *ifp,
1223     in_addr_t	to,
1224     union ad_u *p,
1225     uint_t	len)
1226 {
1227 	const char *type;
1228 
1229 
1230 	if (p->icmp.icmp_type == ICMP_ROUTERADVERT) {
1231 		type = "advertisement";
1232 		if (p->icmp.icmp_code == ICMP_ROUTERADVERT_NOCOMMON)
1233 			return (NULL); /* Mobile IP */
1234 	} else if (p->icmp.icmp_type == ICMP_ROUTERSOLICIT) {
1235 		type = "solicitation";
1236 	} else {
1237 		return (NULL);
1238 	}
1239 
1240 	if (p->icmp.icmp_code != ICMP_ROUTERADVERT_COMMON) {
1241 		trace_pkt("unrecognized ICMP Router %s code=%d from %s to %s",
1242 		    type, p->icmp.icmp_code, naddr_ntoa(from), naddr_ntoa(to));
1243 		return (NULL);
1244 	}
1245 
1246 	trace_rdisc(act, from, to, ifp, p, len);
1247 
1248 	if (ifp == NULL)
1249 		trace_pkt("unknown interface for router-discovery %s from %s "
1250 		    "to %s", type, naddr_ntoa(from), naddr_ntoa(to));
1251 
1252 	return (ifp);
1253 }
1254 
1255 
1256 /* Read packets from the router discovery socket */
1257 void
1258 read_d(void)
1259 {
1260 #define	PKTLEN	512
1261 	static struct msg_limit bad_asize, bad_len;
1262 	struct sockaddr_in from;
1263 	int n, cc, hlen;
1264 	struct {
1265 		union {
1266 			struct ip ip;
1267 			uint16_t s[PKTLEN/sizeof (uint16_t)];
1268 			uint8_t	b[PKTLEN/sizeof (uint8_t)];
1269 		} pkt;
1270 	} buf;
1271 	union ad_u *p;
1272 	n_long *wp;
1273 	struct interface *ifp;
1274 	boolean_t needsort = _B_FALSE;
1275 	struct msghdr msg;
1276 	struct iovec iov;
1277 	uint8_t ancillary_data[CONTROL_BUFSIZE];
1278 
1279 	iov.iov_base = &buf;
1280 	iov.iov_len = sizeof (buf);
1281 	msg.msg_iov = &iov;
1282 	msg.msg_iovlen = 1;
1283 	msg.msg_name = &from;
1284 	msg.msg_control = &ancillary_data;
1285 
1286 	for (;;) {
1287 		msg.msg_namelen = sizeof (from);
1288 		msg.msg_controllen = sizeof (ancillary_data);
1289 		cc = recvmsg(rdisc_sock, &msg, 0);
1290 		if (cc <= 0) {
1291 			if (cc < 0 && errno != EWOULDBLOCK)
1292 				LOGERR("recvmsg(rdisc_sock)");
1293 			break;
1294 		}
1295 
1296 		hlen = buf.pkt.ip.ip_hl << 2;
1297 		if (cc < hlen + ICMP_MINLEN)
1298 			continue;
1299 		/* LINTED [alignment will be lw aligned] */
1300 		p = (union ad_u *)&buf.pkt.b[hlen];
1301 		cc -= hlen;
1302 
1303 		/*
1304 		 * If we could tell the interface on which a packet from
1305 		 * address 0 arrived, we could deal with such solicitations.
1306 		 */
1307 		ifp = receiving_interface(&msg, _B_FALSE);
1308 		ifp = ck_icmp("Recv", from.sin_addr.s_addr, ifp,
1309 		    buf.pkt.ip.ip_dst.s_addr, p, cc);
1310 		if (ifp == NULL)
1311 			continue;
1312 
1313 		if (IS_IFF_QUIET(ifp->int_if_flags)) {
1314 			trace_misc("discard RDISC packet received over %s, %X",
1315 			    ifp->int_name, ifp->int_if_flags);
1316 			continue;
1317 		}
1318 
1319 		if (from.sin_addr.s_addr != 0 &&
1320 		    ifwithaddr(from.sin_addr.s_addr, _B_FALSE, _B_FALSE)) {
1321 			trace_pkt("    "
1322 			    "discard our own Router Discovery message");
1323 			continue;
1324 		}
1325 
1326 		/* The remote address *must* be directly connected. */
1327 		if (!remote_address_ok(ifp, from.sin_addr.s_addr)) {
1328 			trace_misc("discard rdisc message; source %s not on "
1329 			    "interface %s", naddr_ntoa(from.sin_addr.s_addr),
1330 			    ifp->int_name);
1331 			continue;
1332 		}
1333 
1334 		switch (p->icmp.icmp_type) {
1335 		case ICMP_ROUTERADVERT:
1336 			if (ifp->int_state & IS_NO_ADV_IN)
1337 				continue;
1338 
1339 			if (p->ad.icmp_ad_asize*2*sizeof (wp[0]) <
1340 			    sizeof (p->ad.icmp_ad_info[0])) {
1341 				msglim(&bad_asize, from.sin_addr.s_addr,
1342 				    "intolerable rdisc address size=%d",
1343 				    p->ad.icmp_ad_asize);
1344 				continue;
1345 			}
1346 			if (p->ad.icmp_ad_num == 0) {
1347 				trace_pkt("    empty?");
1348 				continue;
1349 			}
1350 			if (cc < (sizeof (p->ad) -
1351 			    sizeof (p->ad.icmp_ad_info) +
1352 			    (p->ad.icmp_ad_num *
1353 			    sizeof (p->ad.icmp_ad_info[0])))) {
1354 				msglim(&bad_len, from.sin_addr.s_addr,
1355 				    "rdisc length %d does not match ad_num"
1356 				    " %d", cc, p->ad.icmp_ad_num);
1357 				continue;
1358 			}
1359 
1360 			needsort = _B_TRUE;
1361 			wp = &p->ad.icmp_ad_info[0].icmp_ad_addr;
1362 			for (n = 0; n < p->ad.icmp_ad_num; n++) {
1363 				parse_ad(from.sin_addr.s_addr,
1364 				    wp[0], wp[1],
1365 				    ntohs(p->ad.icmp_ad_life), ifp);
1366 				wp += p->ad.icmp_ad_asize;
1367 			}
1368 			break;
1369 
1370 
1371 		case ICMP_ROUTERSOLICIT:
1372 			if (!should_supply(ifp))
1373 				continue;
1374 			if ((ifp->int_state & IS_NO_ADV_OUT) ||
1375 			    !IS_IFF_ROUTING(ifp->int_if_flags))
1376 				continue;
1377 			if (stopint != 0)
1378 				continue;
1379 
1380 			/*
1381 			 * We should handle messages from address 0,
1382 			 * but cannot due to kernel limitations.
1383 			 */
1384 
1385 			/* Respond with a point-to-point advertisement */
1386 			send_adv(ifp, from.sin_addr.s_addr, 0);
1387 			break;
1388 		}
1389 	}
1390 
1391 	if (needsort)
1392 		rdisc_sort();
1393 }
1394 
1395 void
1396 rdisc_dump(void)
1397 {
1398 	struct dr *drp;
1399 
1400 	for (drp = drs; drp < &drs[max_ads]; drp++)
1401 		if (drp->dr_ts != 0)
1402 			trace_dr(drp);
1403 }
1404 
1405 void
1406 rdisc_suppress(struct interface *ifp)
1407 {
1408 	if (ifp->int_state & IS_ADV_OUT) {
1409 		msglog("%s \"rdisc_adv\" specified, will not "
1410 		    "suppress rdisc adv", ifp->int_name);
1411 	} else {
1412 		if (ifp->int_state & IS_SUPPRESS_RDISC)
1413 			return;
1414 		ifp->int_state |= (IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
1415 		trace_misc("suppress rdisc adv on %s", ifp->int_name);
1416 		rdisc_timer.tv_sec = 0;
1417 	}
1418 }
1419 
1420 void
1421 rdisc_restore(struct interface *ifp)
1422 {
1423 	if ((ifp->int_state & IS_SUPPRESS_RDISC) == 0)
1424 		return;
1425 	ifp->int_state &= ~(IS_SUPPRESS_RDISC|IS_FLUSH_RDISC);
1426 	trace_misc("restoring rdisc adv on %s", ifp->int_name);
1427 	rdisc_timer.tv_sec = 0;
1428 }
1429 
1430 void
1431 process_d_mib_sock(void)
1432 {
1433 
1434 	socklen_t fromlen;
1435 	struct sockaddr_un from;
1436 	ssize_t	len;
1437 	int command;
1438 	struct dr *drp;
1439 	rdisc_info_t rdisc_info;
1440 	defr_t def_router;
1441 	extern int max_ads;
1442 	int num = 0;
1443 
1444 	fromlen = (socklen_t)sizeof (from);
1445 	len = recvfrom(rdisc_mib_sock, &command, sizeof (int), 0,
1446 	    (struct sockaddr *)&from, &fromlen);
1447 
1448 	if (len < sizeof (int) || command != RDISC_SNMP_INFO_REQ) {
1449 		trace_misc("Bad command on rdisc_mib_sock");
1450 		return;
1451 	}
1452 
1453 	/*
1454 	 * Count number of good routers
1455 	 */
1456 	for (drp = drs; drp < &drs[max_ads]; drp++) {
1457 		if (drp->dr_ts != 0) {
1458 			num++;
1459 		}
1460 	}
1461 
1462 	rdisc_info.info_type = RDISC_SNMP_INFO_RESPONSE;
1463 	rdisc_info.info_version = RDISC_SNMP_INFO_VER;
1464 	rdisc_info.info_num_of_routers = num;
1465 
1466 	(void) sendto(rdisc_mib_sock, &rdisc_info, sizeof (rdisc_info_t), 0,
1467 	    (struct sockaddr *)&from, fromlen);
1468 
1469 	for (drp = drs; drp < &drs[max_ads]; drp++) {
1470 		if (drp->dr_ts != 0) {
1471 			def_router.defr_info_type = RDISC_DEF_ROUTER_INFO;
1472 			def_router.defr_version = RDISC_DEF_ROUTER_VER;
1473 			def_router.defr_index =
1474 			    drp->dr_ifp->int_phys->phyi_index;
1475 			def_router.defr_life = drp->dr_life;
1476 			def_router.defr_addr.s_addr = drp->dr_gate;
1477 			def_router.defr_pref = drp->dr_pref;
1478 			(void) sendto(rdisc_mib_sock, &def_router,
1479 			    sizeof (defr_t), 0, (struct sockaddr *)&from,
1480 			    fromlen);
1481 		}
1482 	}
1483 }
1484