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