xref: /titanic_50/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c (revision 06e1a7147edd272b7296f208141627a5b1191731)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "defs.h"
30 #include "tables.h"
31 
32 #include <sys/sysmacros.h>
33 
34 static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
35 		    struct phyint *pi, struct sockaddr_in6 *from);
36 
37 static void	incoming_rs(struct phyint *pi, struct nd_router_solicit *rs,
38 		    int len, struct sockaddr_in6 *from);
39 
40 void		incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
41 		    int len, struct sockaddr_in6 *from, boolean_t loopback);
42 static void	incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
43 		    struct sockaddr_in6 *from, boolean_t loopback);
44 static void	incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
45 		    struct sockaddr_in6 *from, boolean_t loopback);
46 void		incoming_prefix_onlink_process(struct prefix *pr,
47 		    uchar_t *opt);
48 static boolean_t	incoming_prefix_addrconf(struct phyint *pi,
49 		    uchar_t *opt, struct sockaddr_in6 *from,
50 		    boolean_t loopback);
51 boolean_t	incoming_prefix_addrconf_process(struct phyint *pi,
52 		    struct prefix *pr, uchar_t *opt,
53 		    struct sockaddr_in6 *from, boolean_t loopback,
54 		    boolean_t new_prefix);
55 static void	incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
56 		    struct sockaddr_in6 *from);
57 static void	incoming_lla_opt(struct phyint *pi, uchar_t *opt,
58 		    struct sockaddr_in6 *from, int isrouter);
59 
60 static void	verify_ra_consistency(struct phyint *pi,
61 		    struct nd_router_advert *ra,
62 		    int len, struct sockaddr_in6 *from);
63 static void	verify_prefix_opt(struct phyint *pi, uchar_t *opt,
64 		    char *frombuf);
65 static void	verify_mtu_opt(struct phyint *pi, uchar_t *opt,
66 		    char *frombuf);
67 
68 static void	update_ra_flag(const struct phyint *pi,
69 		    const struct sockaddr_in6 *from, int isrouter);
70 
71 static uint_t	ra_flags;	/* Global to detect when to trigger DHCP */
72 
73 /*
74  * Return a pointer to the specified option buffer.
75  * If not found return NULL.
76  */
77 static void *
78 find_ancillary(struct msghdr *msg, int cmsg_type)
79 {
80 	struct cmsghdr *cmsg;
81 
82 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
83 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
84 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
85 		    cmsg->cmsg_type == cmsg_type) {
86 			return (CMSG_DATA(cmsg));
87 		}
88 	}
89 	return (NULL);
90 }
91 
92 void
93 in_data(struct phyint *pi)
94 {
95 	struct sockaddr_in6 from;
96 	struct icmp6_hdr *icmp;
97 	struct nd_router_solicit *rs;
98 	struct nd_router_advert *ra;
99 	static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
100 	static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
101 	int len;
102 	char abuf[INET6_ADDRSTRLEN];
103 	const char *msgbuf;
104 	struct msghdr msg;
105 	struct iovec iov;
106 	uchar_t *opt;
107 	uint_t hoplimit;
108 
109 	iov.iov_base = (char *)in_packet;
110 	iov.iov_len = sizeof (in_packet);
111 	msg.msg_iov = &iov;
112 	msg.msg_iovlen = 1;
113 	msg.msg_name = (struct sockaddr *)&from;
114 	msg.msg_namelen = sizeof (from);
115 	msg.msg_control = ancillary_data;
116 	msg.msg_controllen = sizeof (ancillary_data);
117 
118 	if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) {
119 		logperror_pi(pi, "in_data: recvfrom");
120 		return;
121 	}
122 	if (len == 0)
123 		return;
124 
125 	if (inet_ntop(AF_INET6, (void *)&from.sin6_addr,
126 	    abuf, sizeof (abuf)) == NULL)
127 		msgbuf = "Unspecified Router";
128 	else
129 		msgbuf = abuf;
130 
131 	/* Ignore packets > 64k or control buffers that don't fit */
132 	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
133 		if (debug & D_PKTBAD) {
134 			logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x "
135 			    "from %s\n", msg.msg_flags, msgbuf);
136 		}
137 		return;
138 	}
139 
140 	icmp = (struct icmp6_hdr *)in_packet;
141 
142 	if (len < ICMP6_MINLEN) {
143 		logmsg(LOG_INFO, "Too short ICMP packet: %d bytes "
144 		    "from %s on %s\n",
145 		    len, msgbuf, pi->pi_name);
146 		return;
147 	}
148 
149 	opt = find_ancillary(&msg, IPV6_HOPLIMIT);
150 	if (opt == NULL) {
151 		/* Unknown hoplimit - must drop */
152 		logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n",
153 		    msgbuf, pi->pi_name);
154 		return;
155 	}
156 	hoplimit = *(uint_t *)opt;
157 	opt = find_ancillary(&msg, IPV6_RTHDR);
158 	if (opt != NULL) {
159 		/* Can't allow routing headers in ND messages */
160 		logmsg(LOG_INFO, "ND message with routing header "
161 		    "from %s on %s\n",
162 		    msgbuf, pi->pi_name);
163 		return;
164 	}
165 	switch (icmp->icmp6_type) {
166 	case ND_ROUTER_SOLICIT:
167 		if (!pi->pi_AdvSendAdvertisements)
168 			return;
169 		if (pi->pi_flags & IFF_NORTEXCH) {
170 			if (debug & D_PKTIN) {
171 				logmsg(LOG_DEBUG, "Ignore received RS packet "
172 				    "on %s (no route exchange on interface)\n",
173 				    pi->pi_name);
174 			}
175 			return;
176 		}
177 
178 		/*
179 		 * Assumes that the kernel has verified the AH (if present)
180 		 * and the ICMP checksum.
181 		 */
182 		if (hoplimit != IPV6_MAX_HOPS) {
183 			logmsg(LOG_DEBUG, "RS hop limit: %d from %s on %s\n",
184 			    hoplimit, msgbuf, pi->pi_name);
185 			return;
186 		}
187 
188 		if (icmp->icmp6_code != 0) {
189 			logmsg(LOG_INFO, "RS code: %d from %s on %s\n",
190 			    icmp->icmp6_code, msgbuf, pi->pi_name);
191 			return;
192 		}
193 
194 		if (len < sizeof (struct nd_router_solicit)) {
195 			logmsg(LOG_INFO, "RS too short: %d bytes "
196 			    "from %s on %s\n",
197 			    len, msgbuf, pi->pi_name);
198 			return;
199 		}
200 		rs = (struct nd_router_solicit *)icmp;
201 		if (len > sizeof (struct nd_router_solicit)) {
202 			if (!verify_opt_len((struct nd_opt_hdr *)&rs[1],
203 			    len - sizeof (struct nd_router_solicit), pi, &from))
204 				return;
205 		}
206 		if (debug & D_PKTIN) {
207 			print_route_sol("Received valid solicit from ", pi,
208 			    rs, len, &from);
209 		}
210 		incoming_rs(pi, rs, len, &from);
211 		break;
212 
213 	case ND_ROUTER_ADVERT:
214 		if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
215 			/*
216 			 * Router advt. must have address!
217 			 * Logging the news and returning.
218 			 */
219 			logmsg(LOG_DEBUG,
220 			    "Router's address unspecified in advertisement\n");
221 			return;
222 		}
223 		if (pi->pi_flags & IFF_NORTEXCH) {
224 			if (debug & D_PKTIN) {
225 				logmsg(LOG_DEBUG, "Ignore received RA packet "
226 				    "on %s (no route exchange on interface)\n",
227 				    pi->pi_name);
228 			}
229 			return;
230 		}
231 
232 		/*
233 		 * Assumes that the kernel has verified the AH (if present)
234 		 * and the ICMP checksum.
235 		 */
236 		if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
237 			logmsg(LOG_DEBUG, "RA from %s - not link local on %s\n",
238 			    msgbuf, pi->pi_name);
239 			return;
240 		}
241 
242 		if (hoplimit != IPV6_MAX_HOPS) {
243 			logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n",
244 			    hoplimit, msgbuf, pi->pi_name);
245 			return;
246 		}
247 
248 		if (icmp->icmp6_code != 0) {
249 			logmsg(LOG_INFO, "RA code: %d from %s on %s\n",
250 			    icmp->icmp6_code, msgbuf, pi->pi_name);
251 			return;
252 		}
253 
254 		if (len < sizeof (struct nd_router_advert)) {
255 			logmsg(LOG_INFO, "RA too short: %d bytes "
256 			    "from %s on %s\n",
257 			    len, msgbuf, pi->pi_name);
258 			return;
259 		}
260 		ra = (struct nd_router_advert *)icmp;
261 		if (len > sizeof (struct nd_router_advert)) {
262 			if (!verify_opt_len((struct nd_opt_hdr *)&ra[1],
263 			    len - sizeof (struct nd_router_advert), pi, &from))
264 				return;
265 		}
266 		if (debug & D_PKTIN) {
267 			print_route_adv("Received valid advert from ", pi,
268 			    ra, len, &from);
269 		}
270 		if (pi->pi_AdvSendAdvertisements)
271 			verify_ra_consistency(pi, ra, len, &from);
272 		else
273 			incoming_ra(pi, ra, len, &from, _B_FALSE);
274 		break;
275 	}
276 }
277 
278 /*
279  * Process a received router solicitation.
280  * Check for source link-layer address option and check if it
281  * is time to advertise.
282  */
283 static void
284 incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len,
285     struct sockaddr_in6 *from)
286 {
287 	struct nd_opt_hdr *opt;
288 	int optlen;
289 
290 	/* Process any options */
291 	len -= sizeof (struct nd_router_solicit);
292 	opt = (struct nd_opt_hdr *)&rs[1];
293 	while (len >= sizeof (struct nd_opt_hdr)) {
294 		optlen = opt->nd_opt_len * 8;
295 		switch (opt->nd_opt_type) {
296 		case ND_OPT_SOURCE_LINKADDR:
297 			incoming_lla_opt(pi, (uchar_t *)opt,
298 			    from, NDF_ISROUTER_OFF);
299 			break;
300 		default:
301 			break;
302 		}
303 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
304 		len -= optlen;
305 	}
306 	/* Simple algorithm: treat unicast and multicast RSs the same */
307 	check_to_advertise(pi, RECEIVED_SOLICIT);
308 }
309 
310 /*
311  * Process a received router advertisement.
312  * Called both when packets arrive as well as when we send RAs.
313  * In the latter case 'loopback' is set.
314  */
315 void
316 incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len,
317     struct sockaddr_in6 *from, boolean_t loopback)
318 {
319 	struct nd_opt_hdr *opt;
320 	int optlen;
321 	struct lifreq lifr;
322 	boolean_t set_needed = _B_FALSE;
323 	struct router *dr;
324 	uint16_t router_lifetime;
325 	uint_t reachable, retrans;
326 	boolean_t reachable_time_changed = _B_FALSE;
327 	boolean_t slla_opt_present	 = _B_FALSE;
328 
329 	if (no_loopback && loopback)
330 		return;
331 
332 	/*
333 	 * If the interface is FAILED or INACTIVE or OFFLINE, don't
334 	 * create any addresses on them. in.mpathd assumes that no new
335 	 * addresses will appear on these. This implies that we
336 	 * won't create any new prefixes advertised by the router
337 	 * on FAILED/INACTIVE/OFFLINE interfaces. When the state changes,
338 	 * the next RA will create the prefix on this interface.
339 	 */
340 	if (pi->pi_flags & (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE))
341 		return;
342 
343 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
344 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
345 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
346 		if (errno == ENXIO)
347 			return;
348 		logperror_pi(pi, "incoming_ra: SIOCGLIFLNKINFO");
349 		return;
350 	}
351 	if (ra->nd_ra_curhoplimit != CURHOP_UNSPECIFIED &&
352 	    ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) {
353 		pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
354 
355 		lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
356 		set_needed = _B_TRUE;
357 	}
358 
359 	reachable = ntohl(ra->nd_ra_reachable);
360 	if (reachable != 0 &&
361 	    reachable != pi->pi_BaseReachableTime) {
362 		pi->pi_BaseReachableTime = reachable;
363 		reachable_time_changed = _B_TRUE;
364 	}
365 
366 	if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
367 	    reachable_time_changed) {
368 		phyint_reach_random(pi, _B_FALSE);
369 		set_needed = _B_TRUE;
370 	}
371 	lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
372 
373 	retrans = ntohl(ra->nd_ra_retransmit);
374 	if (retrans != 0 &&
375 	    pi->pi_RetransTimer != retrans) {
376 		pi->pi_RetransTimer = retrans;
377 		lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
378 		set_needed = _B_TRUE;
379 	}
380 
381 	if (set_needed) {
382 		if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
383 			logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
384 			return;
385 		}
386 	}
387 
388 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) &&
389 	    !(ra_flags & ND_RA_FLAG_MANAGED)) {
390 		ra_flags |= ND_RA_FLAG_MANAGED;
391 		/* TODO trigger dhcpv6 */
392 		logmsg(LOG_DEBUG, "incoming_ra: trigger dhcp MANAGED\n");
393 	}
394 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) &&
395 	    !(ra_flags & ND_RA_FLAG_OTHER)) {
396 		ra_flags |= ND_RA_FLAG_OTHER;
397 		if (!(ra_flags & ND_RA_FLAG_MANAGED)) {
398 			/* TODO trigger dhcpv6 for non-address info */
399 			logmsg(LOG_DEBUG, "incoming_ra: trigger dhcp OTHER\n");
400 		}
401 	}
402 	/* Skip default router code if sent from ourselves */
403 	if (!loopback) {
404 		/* Find and update or add default router in list */
405 		dr = router_lookup(pi, from->sin6_addr);
406 		router_lifetime = ntohs(ra->nd_ra_router_lifetime);
407 		if (dr == NULL) {
408 			if (router_lifetime != 0) {
409 				dr = router_create(pi, from->sin6_addr,
410 				    MILLISEC * router_lifetime);
411 				timer_schedule(dr->dr_lifetime);
412 			}
413 		} else {
414 			dr->dr_lifetime = MILLISEC * router_lifetime;
415 			if (dr->dr_lifetime != 0)
416 				timer_schedule(dr->dr_lifetime);
417 			if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
418 			    (dr->dr_lifetime == 0 && dr->dr_inkernel))
419 				router_update_k(dr);
420 		}
421 	}
422 	/* Process any options */
423 	len -= sizeof (struct nd_router_advert);
424 	opt = (struct nd_opt_hdr *)&ra[1];
425 	while (len >= sizeof (struct nd_opt_hdr)) {
426 		optlen = opt->nd_opt_len * 8;
427 		switch (opt->nd_opt_type) {
428 		case ND_OPT_PREFIX_INFORMATION:
429 			incoming_prefix_opt(pi, (uchar_t *)opt, from,
430 			    loopback);
431 			break;
432 		case ND_OPT_MTU:
433 			incoming_mtu_opt(pi, (uchar_t *)opt, from);
434 			break;
435 		case ND_OPT_SOURCE_LINKADDR:
436 			/* skip lla option if sent from ourselves! */
437 			if (!loopback) {
438 				incoming_lla_opt(pi, (uchar_t *)opt,
439 				    from, NDF_ISROUTER_ON);
440 				slla_opt_present = _B_TRUE;
441 			}
442 			break;
443 		default:
444 			break;
445 		}
446 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
447 		len -= optlen;
448 	}
449 	if (!loopback && !slla_opt_present)
450 		update_ra_flag(pi, from, NDF_ISROUTER_ON);
451 	/* Stop sending solicitations */
452 	check_to_solicit(pi, SOLICIT_DONE);
453 }
454 
455 /*
456  * Process a received prefix option.
457  * Unless addrconf is turned off we process both the addrconf and the
458  * onlink aspects of the prefix option.
459  *
460  * Note that when a flag (onlink or auto) is turned off we do nothing -
461  * the prefix will time out.
462  */
463 static void
464 incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
465     struct sockaddr_in6 *from, boolean_t loopback)
466 {
467 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
468 	boolean_t	good_prefix = _B_TRUE;
469 
470 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
471 		char abuf[INET6_ADDRSTRLEN];
472 
473 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
474 		    abuf, sizeof (abuf));
475 		logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
476 		    "(%d bytes)\n",
477 		    abuf, pi->pi_name,
478 		    8 * (int)po->nd_opt_pi_len);
479 		return;
480 	}
481 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
482 		char abuf[INET6_ADDRSTRLEN];
483 
484 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
485 		    abuf, sizeof (abuf));
486 		logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
487 		    "- ignored\n",
488 		    abuf, pi->pi_name);
489 		return;
490 	}
491 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
492 	    pi->pi_StatelessAddrConf) {
493 		good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
494 	}
495 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
496 	    good_prefix) {
497 		incoming_prefix_onlink(pi, opt, from, loopback);
498 	}
499 }
500 
501 /*
502  * Process prefix options with the onlink flag set.
503  *
504  * If there are no routers ndpd will add an onlink
505  * default route which will allow communication
506  * between neighbors.
507  *
508  * This function needs to loop to find the same prefix multiple times
509  * as if a failover happened earlier, the addresses belonging to
510  * a different interface may be found here on this interface.
511  */
512 /* ARGSUSED2 */
513 static void
514 incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
515     struct sockaddr_in6 *from, boolean_t loopback)
516 {
517 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
518 	int plen;
519 	struct prefix *pr;
520 	uint32_t validtime;	/* Without 2 hour rule */
521 	boolean_t found_one = _B_FALSE;
522 
523 	plen = po->nd_opt_pi_prefix_len;
524 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
525 		if (pr->pr_prefix_len == plen &&
526 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
527 			/* Exclude static prefixes */
528 			if (pr->pr_state & PR_STATIC)
529 				continue;
530 			found_one = _B_TRUE;
531 			incoming_prefix_onlink_process(pr, opt);
532 		}
533 	}
534 
535 	validtime = ntohl(po->nd_opt_pi_valid_time);
536 	/*
537 	 * If we have found a matching prefix already or validtime
538 	 * is zero, we have nothing to do.
539 	 */
540 	if (validtime == 0 || found_one)
541 		return;
542 	pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
543 	if (pr == NULL)
544 		return;
545 	incoming_prefix_onlink_process(pr, opt);
546 }
547 
548 void
549 incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
550 {
551 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
552 	uint32_t validtime;	/* Without 2 hour rule */
553 	char abuf[INET6_ADDRSTRLEN];
554 
555 	validtime = ntohl(po->nd_opt_pi_valid_time);
556 	if (validtime != 0)
557 		pr->pr_state |= PR_ONLINK;
558 	else
559 		pr->pr_state &= ~PR_ONLINK;
560 
561 	/*
562 	 * Convert from seconds to milliseconds avoiding overflow.
563 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
564 	 * (4 billion seconds - about 130 years) we will in fact time
565 	 * out the prefix after 4 billion milliseconds - 46 days).
566 	 * Thus the longest lifetime (apart from infinity) is 46 days.
567 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
568 	 */
569 	if (pr->pr_flags & IFF_TEMPORARY) {
570 		pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
571 	} else {
572 		if (validtime >= PREFIX_INFINITY / MILLISEC)
573 			pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
574 		else
575 			pr->pr_OnLinkLifetime = validtime * MILLISEC;
576 	}
577 	pr->pr_OnLinkFlag = _B_TRUE;
578 	if (debug & (D_PREFIX|D_TMP)) {
579 		logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
580 		    "onlink %u state 0x%x, kstate 0x%x\n",
581 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
582 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
583 		    pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
584 	}
585 
586 	if (pr->pr_kernel_state != pr->pr_state) {
587 		prefix_update_k(pr);
588 	}
589 
590 	if (pr->pr_OnLinkLifetime != 0)
591 		timer_schedule(pr->pr_OnLinkLifetime);
592 }
593 
594 /*
595  * Process prefix options with the autonomous flag set.
596  * Returns false if this prefix results in a bad address (duplicate)
597  * This function needs to loop to find the same prefix multiple times
598  * as if a failover happened earlier, the addresses belonging to
599  * a different interface may be found here on this interface.
600  */
601 /* ARGSUSED2 */
602 static boolean_t
603 incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
604     struct sockaddr_in6 *from, boolean_t loopback)
605 {
606 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
607 	int plen;
608 	struct prefix *pr;
609 	uint32_t validtime, preftime;	/* In seconds */
610 	char abuf[INET6_ADDRSTRLEN];
611 	char pbuf[INET6_ADDRSTRLEN];
612 	boolean_t found_pub = _B_FALSE;
613 	boolean_t found_tmp = _B_FALSE;
614 	boolean_t ret;
615 
616 	validtime = ntohl(po->nd_opt_pi_valid_time);
617 	preftime = ntohl(po->nd_opt_pi_preferred_time);
618 	plen = po->nd_opt_pi_prefix_len;
619 
620 	/* Sanity checks */
621 	if (validtime < preftime) {
622 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
623 		    abuf, sizeof (abuf));
624 		(void) inet_ntop(AF_INET6,
625 		    (void *)&po->nd_opt_pi_prefix,
626 		    pbuf, sizeof (pbuf));
627 		logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
628 		    "valid %u < pref %u ignored\n",
629 		    pbuf, plen, abuf, pi->pi_name,
630 		    validtime, preftime);
631 		return (_B_FALSE);
632 	}
633 
634 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
635 		if (pr->pr_prefix_len == plen &&
636 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
637 
638 			/* Exclude static prefixes */
639 			if (pr->pr_state & PR_STATIC)
640 				continue;
641 			if (pr->pr_flags & IFF_TEMPORARY) {
642 				/*
643 				 * If this address is deprecated and its token
644 				 * doesn't match the current tmp token, we want
645 				 * to create a new address with the current
646 				 * token.  So don't count this addr as a match.
647 				 */
648 				if (!((pr->pr_flags & IFF_DEPRECATED) &&
649 				    !token_equal(pi->pi_tmp_token,
650 				    pr->pr_address, TMP_TOKEN_BITS)))
651 					found_tmp = _B_TRUE;
652 			} else {
653 				found_pub = _B_TRUE;
654 			}
655 			(void) incoming_prefix_addrconf_process(pi, pr, opt,
656 			    from, loopback, _B_FALSE);
657 		}
658 	}
659 
660 	/*
661 	 * If we have found a matching prefix (for public and, if temp addrs
662 	 * are enabled, for temporary) already or validtime is zero, we have
663 	 * nothing to do.
664 	 */
665 	if (validtime == 0 ||
666 	    (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp)))
667 		return (_B_TRUE);
668 
669 	if (!found_pub) {
670 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
671 		if (pr == NULL)
672 			return (_B_TRUE);
673 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
674 		    loopback, _B_TRUE);
675 	}
676 	/*
677 	 * if processing of the public address failed,
678 	 * don't bother with the temporary address.
679 	 */
680 	if (ret == _B_FALSE)
681 		return (_B_FALSE);
682 
683 	if (pi->pi_TmpAddrsEnabled && !found_tmp) {
684 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen,
685 		    IFF_TEMPORARY);
686 		if (pr == NULL)
687 			return (_B_TRUE);
688 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
689 		    loopback, _B_TRUE);
690 	}
691 
692 	return (ret);
693 }
694 
695 boolean_t
696 incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr,
697     uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback,
698     boolean_t new_prefix)
699 {
700 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
701 	char abuf[INET6_ADDRSTRLEN];
702 	char pbuf[INET6_ADDRSTRLEN];
703 	uint32_t validtime, preftime;	/* In seconds */
704 	uint32_t recorded_validtime;	/* In seconds */
705 	int plen;
706 	struct prefix *other_pr;
707 
708 	validtime = ntohl(po->nd_opt_pi_valid_time);
709 	preftime = ntohl(po->nd_opt_pi_preferred_time);
710 	plen = po->nd_opt_pi_prefix_len;
711 	if (!new_prefix) {
712 		/*
713 		 * Check 2 hour rule on valid lifetime.
714 		 * Follows: RFC 2462
715 		 * If we advertised this prefix ourselves we skip
716 		 * these checks. They are also skipped if we did not
717 		 * previously do addrconf on this prefix.
718 		 */
719 		recorded_validtime = pr->pr_ValidLifetime / MILLISEC;
720 
721 		if (loopback || !(pr->pr_state & PR_AUTO) ||
722 		    validtime >= MIN_VALID_LIFETIME ||
723 		    /* LINTED - statement has no consequent */
724 		    validtime >= recorded_validtime) {
725 			/* OK */
726 		} else if (recorded_validtime < MIN_VALID_LIFETIME &&
727 		    validtime < recorded_validtime) {
728 			/* Ignore the prefix */
729 			(void) inet_ntop(AF_INET6,
730 			    (void *)&from->sin6_addr,
731 			    abuf, sizeof (abuf));
732 			(void) inet_ntop(AF_INET6,
733 			    (void *)&po->nd_opt_pi_prefix,
734 			    pbuf, sizeof (pbuf));
735 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
736 			    "too short valid lifetime %u stored %u "
737 			    "- ignored\n",
738 			    pbuf, plen, abuf, pi->pi_name,
739 			    validtime, recorded_validtime);
740 			return (_B_TRUE);
741 		} else {
742 			/*
743 			 * If the router clock runs slower than the
744 			 * host by 1 second over 2 hours then this
745 			 * test will set the lifetime back to 2 hours
746 			 * once i.e. a lifetime decrementing in
747 			 * realtime might cause the prefix to live an
748 			 * extra 2 hours on the host.
749 			 */
750 			(void) inet_ntop(AF_INET6,
751 			    (void *)&from->sin6_addr,
752 			    abuf, sizeof (abuf));
753 			(void) inet_ntop(AF_INET6,
754 			    (void *)&po->nd_opt_pi_prefix,
755 			    pbuf, sizeof (pbuf));
756 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
757 			    "valid time %u stored %u rounded up "
758 			    "to %u\n",
759 			    pbuf, plen, abuf, pi->pi_name,
760 			    validtime, recorded_validtime,
761 			    MIN_VALID_LIFETIME);
762 			validtime = MIN_VALID_LIFETIME;
763 		}
764 	}
765 
766 	/*
767 	 * For RFC3041 addresses, need to take token lifetime
768 	 * into account, too.
769 	 */
770 	if (pr->pr_flags & IFF_TEMPORARY) {
771 		uint_t	cur_tpreftime =
772 		    pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor;
773 
774 		if (new_prefix) {
775 			validtime = MIN(validtime, pi->pi_TmpValidLifetime);
776 			preftime = MIN(preftime, cur_tpreftime);
777 		} else {
778 			uint_t cur_vexp, cur_pexp, curtime;
779 			curtime = getcurrenttime() / MILLISEC;
780 
781 			cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime;
782 			cur_pexp = pr->pr_CreateTime + cur_tpreftime;
783 			if (curtime > cur_vexp)
784 				validtime = 0;
785 			else if ((curtime + validtime) > cur_vexp)
786 				validtime = cur_vexp - curtime;
787 			/*
788 			 * If this is an existing address which was deprecated
789 			 * because of a bad token, we don't want to update its
790 			 * preferred lifetime!
791 			 */
792 			if ((pr->pr_PreferredLifetime == 0) &&
793 			    !token_equal(pr->pr_address, pi->pi_tmp_token,
794 			    TMP_TOKEN_BITS))
795 				preftime = 0;
796 			else if (curtime > cur_pexp)
797 				preftime = 0;
798 			else if ((curtime + preftime) > cur_pexp)
799 				preftime = cur_pexp - curtime;
800 		}
801 		if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) {
802 			(void) inet_ntop(AF_INET6,
803 			    (void *)&from->sin6_addr,
804 			    abuf, sizeof (abuf));
805 			(void) inet_ntop(AF_INET6,
806 			    (void *)&po->nd_opt_pi_prefix,
807 			    pbuf, sizeof (pbuf));
808 			logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: "
809 			    "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
810 			    pbuf, plen, abuf, pi->pi_name, preftime,
811 			    pi->pi_TmpRegenAdvance);
812 			if (new_prefix)
813 				prefix_delete(pr);
814 			return (_B_TRUE);
815 		}
816 	}
817 	if (debug & D_TMP)
818 		logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, "
819 		    "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime);
820 
821 	if (!(pr->pr_state & PR_AUTO)) {
822 		int i, tokenlen;
823 		in6_addr_t *token;
824 		/*
825 		 * Form a new local address if the lengths match.
826 		 */
827 		if (pr->pr_flags && IFF_TEMPORARY) {
828 			if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
829 				if (!tmptoken_create(pi)) {
830 					prefix_delete(pr);
831 					return (_B_TRUE);
832 				}
833 			}
834 			tokenlen = TMP_TOKEN_BITS;
835 			token = &pi->pi_tmp_token;
836 		} else {
837 			tokenlen = pi->pi_token_length;
838 			token = &pi->pi_token;
839 		}
840 		if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
841 			(void) inet_ntop(AF_INET6,
842 			    (void *)&from->sin6_addr,
843 			    abuf, sizeof (abuf));
844 			(void) inet_ntop(AF_INET6,
845 			    (void *)&po->nd_opt_pi_prefix,
846 			    pbuf, sizeof (pbuf));
847 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
848 			    "mismatched length %d token length %d\n",
849 			    pbuf, plen, abuf, pi->pi_name,
850 			    pr->pr_prefix_len, tokenlen);
851 			return (_B_TRUE);
852 		}
853 		for (i = 0; i < 16; i++) {
854 			/*
855 			 * prefix_create ensures that pr_prefix has all-zero
856 			 * bits after prefixlen.
857 			 */
858 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
859 			    token->s6_addr[i];
860 		}
861 		/*
862 		 * Check if any other physical interface has the same
863 		 * address configured already
864 		 */
865 		if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
866 			/*
867 			 * Delete this prefix structure as kernel
868 			 * does not allow duplicated addresses
869 			 */
870 
871 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
872 			    "Duplicate prefix  %s received on interface %s\n",
873 			    inet_ntop(AF_INET6,
874 			    (void *)&po->nd_opt_pi_prefix, abuf,
875 			    sizeof (abuf)), pi->pi_name);
876 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
877 			    "Prefix already exists in interface %s\n",
878 			    other_pr->pr_physical->pi_name);
879 			if (new_prefix) {
880 				prefix_delete(pr);
881 				return (_B_FALSE);
882 			}
883 			/* Ignore for addrconf purposes */
884 			validtime = preftime = 0;
885 		}
886 		if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
887 			pr->pr_CreateTime = getcurrenttime() / MILLISEC;
888 			if (debug & D_TMP)
889 				logmsg(LOG_DEBUG,
890 				    "created tmp addr(%s v %d p %d)\n",
891 				    pr->pr_name, validtime, preftime);
892 		}
893 	}
894 
895 	if (validtime != 0)
896 		pr->pr_state |= PR_AUTO;
897 	else
898 		pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
899 	if (preftime != 0 || !(pr->pr_state & PR_AUTO))
900 		pr->pr_state &= ~PR_DEPRECATED;
901 	else
902 		pr->pr_state |= PR_DEPRECATED;
903 
904 	/*
905 	 * Convert from seconds to milliseconds avoiding overflow.
906 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
907 	 * (4 billion seconds - about 130 years) we will in fact time
908 	 * out the prefix after 4 billion milliseconds - 46 days).
909 	 * Thus the longest lifetime (apart from infinity) is 46 days.
910 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
911 	 */
912 	if (validtime >= PREFIX_INFINITY / MILLISEC)
913 		pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
914 	else
915 		pr->pr_ValidLifetime = validtime * MILLISEC;
916 	if (preftime >= PREFIX_INFINITY / MILLISEC)
917 		pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
918 	else
919 		pr->pr_PreferredLifetime = preftime * MILLISEC;
920 	pr->pr_AutonomousFlag = _B_TRUE;
921 
922 	if (debug & D_PREFIX) {
923 		logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
924 		    "valid %u pref %u\n",
925 		    pr->pr_physical->pi_name,
926 		    inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
927 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
928 		    pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
929 	}
930 
931 	if (pr->pr_state & PR_AUTO) {
932 		/* Take the min of the two timeouts by calling it twice */
933 		if (pr->pr_ValidLifetime != 0)
934 			timer_schedule(pr->pr_ValidLifetime);
935 		if (pr->pr_PreferredLifetime != 0)
936 			timer_schedule(pr->pr_PreferredLifetime);
937 	}
938 	if (pr->pr_kernel_state != pr->pr_state) {
939 		/* Log a message when an addrconf prefix goes away */
940 		if ((pr->pr_kernel_state & PR_AUTO) &&
941 		    !(pr->pr_state & PR_AUTO)) {
942 			char abuf[INET6_ADDRSTRLEN];
943 
944 			logmsg(LOG_WARNING, "Address removed due to zero "
945 			    "valid lifetime %s\n",
946 			    inet_ntop(AF_INET6, (void *)&pr->pr_address,
947 			    abuf, sizeof (abuf)));
948 		}
949 		prefix_update_k(pr);
950 	}
951 	return (_B_TRUE);
952 }
953 
954 /*
955  * Process an MTU option received in a router advertisement.
956  */
957 static void
958 incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
959     struct sockaddr_in6 *from)
960 {
961 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
962 	struct lifreq lifr;
963 	uint32_t mtu;
964 
965 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
966 		char abuf[INET6_ADDRSTRLEN];
967 
968 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
969 		    abuf, sizeof (abuf));
970 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
971 		    "(%d bytes)\n",
972 		    abuf, pi->pi_name,
973 		    8 * (int)mo->nd_opt_mtu_len);
974 		return;
975 	}
976 	mtu = ntohl(mo->nd_opt_mtu_mtu);
977 	if (pi->pi_LinkMTU == mtu)
978 		return;	/* No change */
979 	if (mtu > pi->pi_mtu) {
980 		/* Can't exceed physical MTU */
981 		char abuf[INET6_ADDRSTRLEN];
982 
983 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
984 		    abuf, sizeof (abuf));
985 		logmsg(LOG_INFO, "mtu option from %s on %s too large "
986 		    "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
987 		return;
988 	}
989 	if (mtu < IPV6_MIN_MTU) {
990 		char abuf[INET6_ADDRSTRLEN];
991 
992 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
993 		    abuf, sizeof (abuf));
994 		logmsg(LOG_INFO, "mtu option from %s on %s too small "
995 		    "MTU (%d)\n", abuf, pi->pi_name, mtu);
996 		return;
997 	}
998 
999 	pi->pi_LinkMTU = mtu;
1000 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1001 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1002 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
1003 		logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
1004 		return;
1005 	}
1006 	lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
1007 	if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
1008 		logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
1009 		return;
1010 	}
1011 }
1012 
1013 /*
1014  * Process a source link-layer address option received in a router
1015  * advertisement or solicitation.
1016  */
1017 static void
1018 incoming_lla_opt(struct phyint *pi, uchar_t *opt,
1019     struct sockaddr_in6 *from, int isrouter)
1020 {
1021 	struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
1022 	struct lifreq lifr;
1023 	struct sockaddr_in6 *sin6;
1024 	int max_content_len;
1025 
1026 	if (pi->pi_hdw_addr_len == 0)
1027 		return;
1028 
1029 	/*
1030 	 * Can't remove padding since it is link type specific.
1031 	 * However, we check against the length of our link-layer
1032 	 * address.
1033 	 * Note: assumes that all links have a fixed lengh address.
1034 	 */
1035 	max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
1036 	if (max_content_len < pi->pi_hdw_addr_len ||
1037 	    (max_content_len >= 8 &&
1038 	    max_content_len - 7 > pi->pi_hdw_addr_len)) {
1039 		char abuf[INET6_ADDRSTRLEN];
1040 
1041 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1042 		    abuf, sizeof (abuf));
1043 		logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
1044 		    "physaddr length (%d vs. %d bytes)\n",
1045 		    abuf, pi->pi_name,
1046 		    max_content_len, pi->pi_hdw_addr_len);
1047 		return;
1048 	}
1049 
1050 	lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
1051 	bcopy((char *)lo->nd_opt_lla_hdw_addr,
1052 	    (char *)lifr.lifr_nd.lnr_hdw_addr,
1053 	    lifr.lifr_nd.lnr_hdw_len);
1054 
1055 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1056 	bzero(sin6, sizeof (struct sockaddr_in6));
1057 	sin6->sin6_family = AF_INET6;
1058 	sin6->sin6_addr = from->sin6_addr;
1059 
1060 	/*
1061 	 * Set IsRouter flag if RA; clear if RS.
1062 	 */
1063 	lifr.lifr_nd.lnr_state_create = ND_STALE;
1064 	lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1065 	lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
1066 	lifr.lifr_nd.lnr_flags = isrouter;
1067 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1068 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1069 	if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
1070 		logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
1071 		return;
1072 	}
1073 }
1074 
1075 /*
1076  * Verify the content of the received router advertisement against our
1077  * own configuration as specified in RFC 2461.
1078  */
1079 static void
1080 verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
1081     struct sockaddr_in6 *from)
1082 {
1083 	char frombuf[INET6_ADDRSTRLEN];
1084 	struct nd_opt_hdr *opt;
1085 	int optlen;
1086 	uint_t reachable, retrans;
1087 	boolean_t pktflag, myflag;
1088 
1089 	(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1090 	    frombuf, sizeof (frombuf));
1091 
1092 	if (ra->nd_ra_curhoplimit != 0 &&
1093 	    pi->pi_AdvCurHopLimit != 0 &&
1094 	    ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
1095 		logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
1096 		    "limit:\n\treceived %d configuration %d\n",
1097 		    frombuf, pi->pi_name,
1098 		    ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
1099 	}
1100 
1101 	reachable = ntohl(ra->nd_ra_reachable);
1102 	if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
1103 	    reachable != pi->pi_AdvReachableTime) {
1104 		logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
1105 		    "time:\n\treceived %d configuration %d\n",
1106 		    frombuf, pi->pi_name,
1107 		    reachable, pi->pi_AdvReachableTime);
1108 	}
1109 
1110 	retrans = ntohl(ra->nd_ra_retransmit);
1111 	if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
1112 	    retrans != pi->pi_AdvRetransTimer) {
1113 		logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
1114 		    "timer:\n\treceived %d configuration %d\n",
1115 		    frombuf, pi->pi_name,
1116 		    retrans, pi->pi_AdvRetransTimer);
1117 	}
1118 
1119 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
1120 	myflag = (pi->pi_AdvManagedFlag != 0);
1121 	if (pktflag != myflag) {
1122 		logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
1123 		    "flag:\n\treceived %s configuration %s\n",
1124 		    frombuf, pi->pi_name,
1125 		    (pktflag ? "ON" : "OFF"),
1126 		    (myflag ? "ON" : "OFF"));
1127 	}
1128 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
1129 	myflag = (pi->pi_AdvOtherConfigFlag != 0);
1130 	if (pktflag != myflag) {
1131 		logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
1132 		    "flag:\n\treceived %s configuration %s\n",
1133 		    frombuf, pi->pi_name,
1134 		    (pktflag ? "ON" : "OFF"),
1135 		    (myflag ? "ON" : "OFF"));
1136 	}
1137 
1138 	/* Process any options */
1139 	len -= sizeof (struct nd_router_advert);
1140 	opt = (struct nd_opt_hdr *)&ra[1];
1141 	while (len >= sizeof (struct nd_opt_hdr)) {
1142 		optlen = opt->nd_opt_len * 8;
1143 		switch (opt->nd_opt_type) {
1144 		case ND_OPT_PREFIX_INFORMATION:
1145 			verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
1146 			break;
1147 		case ND_OPT_MTU:
1148 			verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
1149 			break;
1150 		default:
1151 			break;
1152 		}
1153 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1154 		len -= optlen;
1155 	}
1156 }
1157 
1158 /*
1159  * Verify that the lifetimes and onlink/auto flags are consistent
1160  * with our settings.
1161  */
1162 static void
1163 verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1164 {
1165 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
1166 	int plen;
1167 	struct adv_prefix *adv_pr;
1168 	uint32_t validtime, preftime;
1169 	char prefixbuf[INET6_ADDRSTRLEN];
1170 	int pktflag, myflag;
1171 
1172 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
1173 		logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
1174 		    "(%d bytes)\n",
1175 		    frombuf, pi->pi_name,
1176 		    8 * (int)po->nd_opt_pi_len);
1177 		return;
1178 	}
1179 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1180 		logmsg(LOG_INFO, "RA from %s on %s contains link-local "
1181 		    "prefix - ignored\n",
1182 		    frombuf, pi->pi_name);
1183 		return;
1184 	}
1185 	plen = po->nd_opt_pi_prefix_len;
1186 	adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
1187 	if (adv_pr == NULL)
1188 		return;
1189 
1190 	/* Ignore prefixes which we do not advertise */
1191 	if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
1192 		return;
1193 	(void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
1194 	    prefixbuf, sizeof (prefixbuf));
1195 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
1196 	myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
1197 	if (pktflag != myflag) {
1198 		logmsg(LOG_INFO,
1199 		    "RA from %s on %s inconsistent autonumous flag for \n\t"
1200 		    "prefix %s/%u: received %s configuration %s\n",
1201 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1202 		    (pktflag ? "ON" : "OFF"),
1203 		    (myflag ? "ON" : "OFF"));
1204 	}
1205 
1206 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
1207 	myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
1208 	if (pktflag != myflag) {
1209 		logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
1210 		    "for \n\tprefix %s/%u: received %s configuration %s\n",
1211 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1212 		    (pktflag ? "ON" : "OFF"),
1213 		    (myflag ? "ON" : "OFF"));
1214 	}
1215 	validtime = ntohl(po->nd_opt_pi_valid_time);
1216 	preftime = ntohl(po->nd_opt_pi_preferred_time);
1217 
1218 	/*
1219 	 * Take into account variation for lifetimes decrementing
1220 	 * in real time. Allow +/- 10 percent and +/- 10 seconds.
1221 	 */
1222 #define	LOWER_LIMIT(val)	((val) - (val)/10 - 10)
1223 #define	UPPER_LIMIT(val)	((val) + (val)/10 + 10)
1224 	if (adv_pr->adv_pr_AdvValidRealTime) {
1225 		if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
1226 		    (validtime <
1227 		    LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
1228 		    validtime >
1229 		    UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
1230 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1231 			    "lifetime for\n\tprefix %s/%u: received %d "
1232 			    "configuration %d\n",
1233 			    frombuf, pi->pi_name, prefixbuf,
1234 			    adv_pr->adv_pr_prefix_len,
1235 			    validtime, adv_pr->adv_pr_AdvValidExpiration);
1236 		}
1237 	} else {
1238 		if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
1239 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1240 			    "lifetime for\n\tprefix %s/%u: received %d "
1241 			    "configuration %d\n",
1242 			    frombuf, pi->pi_name, prefixbuf,
1243 			    adv_pr->adv_pr_prefix_len,
1244 			    validtime, adv_pr->adv_pr_AdvValidLifetime);
1245 		}
1246 	}
1247 
1248 	if (adv_pr->adv_pr_AdvPreferredRealTime) {
1249 		if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
1250 		    (preftime <
1251 		    LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
1252 		    preftime >
1253 		    UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
1254 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1255 			    "preferred lifetime for\n\tprefix %s/%u: "
1256 			    "received %d configuration %d\n",
1257 			    frombuf, pi->pi_name, prefixbuf,
1258 			    adv_pr->adv_pr_prefix_len,
1259 			    preftime, adv_pr->adv_pr_AdvPreferredExpiration);
1260 		}
1261 	} else {
1262 		if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
1263 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1264 			    "preferred lifetime for\n\tprefix %s/%u: "
1265 			    "received %d configuration %d\n",
1266 			    frombuf, pi->pi_name, prefixbuf,
1267 			    adv_pr->adv_pr_prefix_len,
1268 			    preftime, adv_pr->adv_pr_AdvPreferredLifetime);
1269 		}
1270 	}
1271 }
1272 
1273 /*
1274  * Verify the received MTU against our own configuration.
1275  */
1276 static void
1277 verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1278 {
1279 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
1280 	uint32_t mtu;
1281 
1282 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
1283 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
1284 		    "(%d bytes)\n",
1285 		    frombuf, pi->pi_name,
1286 		    8 * (int)mo->nd_opt_mtu_len);
1287 		return;
1288 	}
1289 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1290 	if (pi->pi_AdvLinkMTU != 0 &&
1291 	    pi->pi_AdvLinkMTU != mtu) {
1292 		logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
1293 		    "received %d configuration %d\n",
1294 		    frombuf, pi->pi_name,
1295 		    mtu, pi->pi_AdvLinkMTU);
1296 	}
1297 }
1298 
1299 /*
1300  * Verify that all options have a non-zero length and that
1301  * the options fit within the total length of the packet (optlen).
1302  */
1303 static boolean_t
1304 verify_opt_len(struct nd_opt_hdr *opt, int optlen,
1305     struct phyint *pi, struct sockaddr_in6 *from)
1306 {
1307 	while (optlen > 0) {
1308 		if (opt->nd_opt_len == 0) {
1309 			char abuf[INET6_ADDRSTRLEN];
1310 
1311 			(void) inet_ntop(AF_INET6,
1312 			    (void *)&from->sin6_addr,
1313 			    abuf, sizeof (abuf));
1314 
1315 			logmsg(LOG_INFO, "Zero length option type 0x%x "
1316 			    "from %s on %s\n",
1317 			    opt->nd_opt_type, abuf, pi->pi_name);
1318 			return (_B_FALSE);
1319 		}
1320 		optlen -= 8 * opt->nd_opt_len;
1321 		if (optlen < 0) {
1322 			char abuf[INET6_ADDRSTRLEN];
1323 
1324 			(void) inet_ntop(AF_INET6,
1325 			    (void *)&from->sin6_addr,
1326 			    abuf, sizeof (abuf));
1327 
1328 			logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
1329 			    "from %s on %s\n",
1330 			    opt->nd_opt_type, opt->nd_opt_len,
1331 			    abuf, pi->pi_name);
1332 			return (_B_FALSE);
1333 		}
1334 		opt = (struct nd_opt_hdr *)((char *)opt +
1335 		    8 * opt->nd_opt_len);
1336 	}
1337 	return (_B_TRUE);
1338 }
1339 
1340 /*
1341  * Update IsRouter Flag for Host turning into a router or vice-versa.
1342  */
1343 static void
1344 update_ra_flag(const struct phyint *pi, const struct sockaddr_in6 *from,
1345     int isrouter)
1346 {
1347 	struct lifreq lifr;
1348 	char abuf[INET6_ADDRSTRLEN];
1349 	struct sockaddr_in6 *sin6;
1350 
1351 	/* check if valid flag is being set */
1352 	if ((isrouter != NDF_ISROUTER_ON) &&
1353 	    (isrouter != NDF_ISROUTER_OFF)) {
1354 		logmsg(LOG_ERR, "update_ra_flag: Invalid IsRouter "
1355 		    "flag %d\n", isrouter);
1356 		return;
1357 	}
1358 
1359 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1360 	bzero(sin6, sizeof (*sin6));
1361 	sin6->sin6_family = AF_INET6;
1362 	sin6->sin6_addr = from->sin6_addr;
1363 
1364 	(void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1365 
1366 	if (ioctl(pi->pi_sock, SIOCLIFGETND, (char *)&lifr) < 0) {
1367 		if (errno == ESRCH) {
1368 			if (debug & D_IFSCAN) {
1369 				logmsg(LOG_DEBUG,
1370 "update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER");
1371 			}
1372 		} else {
1373 			logperror_pi(pi, "update_ra_flag: SIOCLIFGETND");
1374 		}
1375 	} else {
1376 		/*
1377 		 * The lif_nd_req structure has three state values to be used
1378 		 * when changing/updating nces :
1379 		 * lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla.
1380 		 *
1381 		 * In this case, we're updating an nce, without changing lla;
1382 		 * so we set lnr_state_same_lla to ND_UNCHANGED, indicating that
1383 		 * nce's state should not be affected by our flag change.
1384 		 *
1385 		 * The kernel implementation also expects the lnr_state_create
1386 		 * field be always set, before processing ioctl request for NCE
1387 		 * update.
1388 		 * We use the state as STALE, while addressing the possibility
1389 		 * of NCE deletion when ioctl with SIOCLIFGETND argument
1390 		 * in earlier step is returned - further in such case we don't
1391 		 * want to re-create the entry in the reachable state.
1392 		 */
1393 		lifr.lifr_nd.lnr_state_create = ND_STALE;
1394 		lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1395 		lifr.lifr_nd.lnr_flags = isrouter;
1396 		if ((ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr)) < 0) {
1397 			logperror_pi(pi, "update_ra_flag: SIOCLIFSETND");
1398 		} else {
1399 			(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1400 			    abuf, sizeof (abuf));
1401 			logmsg(LOG_INFO, "update_ra_flag: IsRouter flag "
1402 			    "updated for %s\n", abuf);
1403 		}
1404 	}
1405 }
1406