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