xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/in.ndpd/ndp.c (revision 0a12cda44900dce580fc40f3acf669818520c3ee)
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, dadfails = 0;
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 RETRY_TOKEN:
829 			if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
830 				if (!tmptoken_create(pi)) {
831 					prefix_delete(pr);
832 					return (_B_TRUE);
833 				}
834 			}
835 			tokenlen = TMP_TOKEN_BITS;
836 			token = &pi->pi_tmp_token;
837 		} else {
838 			tokenlen = pi->pi_token_length;
839 			token = &pi->pi_token;
840 		}
841 		if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
842 			(void) inet_ntop(AF_INET6,
843 			    (void *)&from->sin6_addr,
844 			    abuf, sizeof (abuf));
845 			(void) inet_ntop(AF_INET6,
846 			    (void *)&po->nd_opt_pi_prefix,
847 			    pbuf, sizeof (pbuf));
848 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
849 			    "mismatched length %d token length %d\n",
850 			    pbuf, plen, abuf, pi->pi_name,
851 			    pr->pr_prefix_len, tokenlen);
852 			return (_B_TRUE);
853 		}
854 		for (i = 0; i < 16; i++) {
855 			/*
856 			 * prefix_create ensures that pr_prefix has all-zero
857 			 * bits after prefixlen.
858 			 */
859 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
860 			    token->s6_addr[i];
861 		}
862 		/*
863 		 * Check if any other physical interface has the same
864 		 * address configured already
865 		 */
866 		if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
867 			/*
868 			 * Delete this prefix structure as kernel
869 			 * does not allow duplicated addresses
870 			 */
871 
872 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
873 			    "Duplicate prefix  %s received on interface %s\n",
874 			    inet_ntop(AF_INET6,
875 			    (void *)&po->nd_opt_pi_prefix, abuf,
876 			    sizeof (abuf)), pi->pi_name);
877 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
878 			    "Prefix already exists in interface %s\n",
879 			    other_pr->pr_physical->pi_name);
880 			if (new_prefix) {
881 				prefix_delete(pr);
882 				return (_B_FALSE);
883 			}
884 			/* Ignore for addrconf purposes */
885 			validtime = preftime = 0;
886 		}
887 		if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
888 			struct sockaddr_in6 sin6;
889 			sin6.sin6_family = AF_INET6;
890 			sin6.sin6_addr = pr->pr_address;
891 			if (do_dad(pi->pi_name, &sin6) != 0) {
892 				/* DAD failed, need a new token */
893 				dadfails++;
894 				logmsg(LOG_WARNING,
895 				    "incoming_prefix_addrconf_process: "
896 				    "deprecating temporary token %s\n",
897 				    inet_ntop(AF_INET6,
898 				    (void *)&pi->pi_tmp_token, abuf,
899 				    sizeof (abuf)));
900 				tmptoken_delete(pi);
901 				if (dadfails == MAX_DAD_FAILURES) {
902 					logmsg(LOG_ERR, "Too many DAD "
903 					    "failures; disabling temporary "
904 					    "addresses on %s\n", pi->pi_name);
905 					pi->pi_TmpAddrsEnabled = 0;
906 					prefix_delete(pr);
907 					return (_B_TRUE);
908 				}
909 				goto RETRY_TOKEN;
910 			}
911 			pr->pr_CreateTime = getcurrenttime() / MILLISEC;
912 			if (debug & D_TMP)
913 				logmsg(LOG_DEBUG,
914 				    "created tmp addr(%s v %d p %d)\n",
915 				    pr->pr_name, validtime, preftime);
916 		}
917 	}
918 
919 	if (validtime != 0)
920 		pr->pr_state |= PR_AUTO;
921 	else
922 		pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
923 	if (preftime != 0 || !(pr->pr_state & PR_AUTO))
924 		pr->pr_state &= ~PR_DEPRECATED;
925 	else
926 		pr->pr_state |= PR_DEPRECATED;
927 
928 	/*
929 	 * Convert from seconds to milliseconds avoiding overflow.
930 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
931 	 * (4 billion seconds - about 130 years) we will in fact time
932 	 * out the prefix after 4 billion milliseconds - 46 days).
933 	 * Thus the longest lifetime (apart from infinity) is 46 days.
934 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
935 	 */
936 	if (validtime >= PREFIX_INFINITY / MILLISEC)
937 		pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
938 	else
939 		pr->pr_ValidLifetime = validtime * MILLISEC;
940 	if (preftime >= PREFIX_INFINITY / MILLISEC)
941 		pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
942 	else
943 		pr->pr_PreferredLifetime = preftime * MILLISEC;
944 	pr->pr_AutonomousFlag = _B_TRUE;
945 
946 	if (debug & D_PREFIX) {
947 		logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
948 		    "valid %u pref %u\n",
949 		    pr->pr_physical->pi_name,
950 		    inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
951 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
952 		    pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
953 	}
954 
955 	if (pr->pr_state & PR_AUTO) {
956 		/* Take the min of the two timeouts by calling it twice */
957 		if (pr->pr_ValidLifetime != 0)
958 			timer_schedule(pr->pr_ValidLifetime);
959 		if (pr->pr_PreferredLifetime != 0)
960 			timer_schedule(pr->pr_PreferredLifetime);
961 	}
962 	if (pr->pr_kernel_state != pr->pr_state) {
963 		/* Log a message when an addrconf prefix goes away */
964 		if ((pr->pr_kernel_state & PR_AUTO) &&
965 		    !(pr->pr_state & PR_AUTO)) {
966 			char abuf[INET6_ADDRSTRLEN];
967 
968 			logmsg(LOG_WARNING, "Address removed due to zero "
969 			    "valid lifetime %s\n",
970 			    inet_ntop(AF_INET6, (void *)&pr->pr_address,
971 			    abuf, sizeof (abuf)));
972 		}
973 		prefix_update_k(pr);
974 	}
975 	return (_B_TRUE);
976 }
977 
978 /*
979  * Process an MTU option received in a router advertisement.
980  */
981 static void
982 incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
983     struct sockaddr_in6 *from)
984 {
985 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
986 	struct lifreq lifr;
987 	uint32_t mtu;
988 
989 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
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 wrong size "
995 		    "(%d bytes)\n",
996 		    abuf, pi->pi_name,
997 		    8 * (int)mo->nd_opt_mtu_len);
998 		return;
999 	}
1000 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1001 	if (pi->pi_LinkMTU == mtu)
1002 		return;	/* No change */
1003 	if (mtu > pi->pi_mtu) {
1004 		/* Can't exceed physical MTU */
1005 		char abuf[INET6_ADDRSTRLEN];
1006 
1007 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1008 		    abuf, sizeof (abuf));
1009 		logmsg(LOG_INFO, "mtu option from %s on %s too large "
1010 		    "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
1011 		return;
1012 	}
1013 	if (mtu < IPV6_MIN_MTU) {
1014 		char abuf[INET6_ADDRSTRLEN];
1015 
1016 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1017 		    abuf, sizeof (abuf));
1018 		logmsg(LOG_INFO, "mtu option from %s on %s too small "
1019 		    "MTU (%d)\n", abuf, pi->pi_name, mtu);
1020 		return;
1021 	}
1022 
1023 	pi->pi_LinkMTU = mtu;
1024 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1025 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1026 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
1027 		logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
1028 		return;
1029 	}
1030 	lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
1031 	if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
1032 		logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
1033 		return;
1034 	}
1035 }
1036 
1037 /*
1038  * Process a source link-layer address option received in a router
1039  * advertisement or solicitation.
1040  */
1041 static void
1042 incoming_lla_opt(struct phyint *pi, uchar_t *opt,
1043     struct sockaddr_in6 *from, int isrouter)
1044 {
1045 	struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
1046 	struct lifreq lifr;
1047 	struct sockaddr_in6 *sin6;
1048 	int max_content_len;
1049 
1050 	if (pi->pi_hdw_addr_len == 0)
1051 		return;
1052 
1053 	/*
1054 	 * Can't remove padding since it is link type specific.
1055 	 * However, we check against the length of our link-layer
1056 	 * address.
1057 	 * Note: assumes that all links have a fixed lengh address.
1058 	 */
1059 	max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
1060 	if (max_content_len < pi->pi_hdw_addr_len ||
1061 	    (max_content_len >= 8 &&
1062 	    max_content_len - 7 > pi->pi_hdw_addr_len)) {
1063 		char abuf[INET6_ADDRSTRLEN];
1064 
1065 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1066 		    abuf, sizeof (abuf));
1067 		logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
1068 		    "physaddr length (%d vs. %d bytes)\n",
1069 		    abuf, pi->pi_name,
1070 		    max_content_len, pi->pi_hdw_addr_len);
1071 		return;
1072 	}
1073 
1074 	lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
1075 	bcopy((char *)lo->nd_opt_lla_hdw_addr,
1076 	    (char *)lifr.lifr_nd.lnr_hdw_addr,
1077 	    lifr.lifr_nd.lnr_hdw_len);
1078 
1079 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1080 	bzero(sin6, sizeof (struct sockaddr_in6));
1081 	sin6->sin6_family = AF_INET6;
1082 	sin6->sin6_addr = from->sin6_addr;
1083 
1084 	/*
1085 	 * Set IsRouter flag if RA; clear if RS.
1086 	 */
1087 	lifr.lifr_nd.lnr_state_create = ND_STALE;
1088 	lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1089 	lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
1090 	lifr.lifr_nd.lnr_flags = isrouter;
1091 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1092 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1093 	if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
1094 		logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
1095 		return;
1096 	}
1097 }
1098 
1099 /*
1100  * Verify the content of the received router advertisement against our
1101  * own configuration as specified in RFC 2461.
1102  */
1103 static void
1104 verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
1105     struct sockaddr_in6 *from)
1106 {
1107 	char frombuf[INET6_ADDRSTRLEN];
1108 	struct nd_opt_hdr *opt;
1109 	int optlen;
1110 	uint_t reachable, retrans;
1111 	boolean_t pktflag, myflag;
1112 
1113 	(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1114 	    frombuf, sizeof (frombuf));
1115 
1116 	if (ra->nd_ra_curhoplimit != 0 &&
1117 	    pi->pi_AdvCurHopLimit != 0 &&
1118 	    ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
1119 		logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
1120 		    "limit:\n\treceived %d configuration %d\n",
1121 		    frombuf, pi->pi_name,
1122 		    ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
1123 	}
1124 
1125 	reachable = ntohl(ra->nd_ra_reachable);
1126 	if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
1127 	    reachable != pi->pi_AdvReachableTime) {
1128 		logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
1129 		    "time:\n\treceived %d configuration %d\n",
1130 		    frombuf, pi->pi_name,
1131 		    reachable, pi->pi_AdvReachableTime);
1132 	}
1133 
1134 	retrans = ntohl(ra->nd_ra_retransmit);
1135 	if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
1136 	    retrans != pi->pi_AdvRetransTimer) {
1137 		logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
1138 		    "timer:\n\treceived %d configuration %d\n",
1139 		    frombuf, pi->pi_name,
1140 		    retrans, pi->pi_AdvRetransTimer);
1141 	}
1142 
1143 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
1144 	myflag = (pi->pi_AdvManagedFlag != 0);
1145 	if (pktflag != myflag) {
1146 		logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
1147 		    "flag:\n\treceived %s configuration %s\n",
1148 		    frombuf, pi->pi_name,
1149 		    (pktflag ? "ON" : "OFF"),
1150 		    (myflag ? "ON" : "OFF"));
1151 	}
1152 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
1153 	myflag = (pi->pi_AdvOtherConfigFlag != 0);
1154 	if (pktflag != myflag) {
1155 		logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
1156 		    "flag:\n\treceived %s configuration %s\n",
1157 		    frombuf, pi->pi_name,
1158 		    (pktflag ? "ON" : "OFF"),
1159 		    (myflag ? "ON" : "OFF"));
1160 	}
1161 
1162 	/* Process any options */
1163 	len -= sizeof (struct nd_router_advert);
1164 	opt = (struct nd_opt_hdr *)&ra[1];
1165 	while (len >= sizeof (struct nd_opt_hdr)) {
1166 		optlen = opt->nd_opt_len * 8;
1167 		switch (opt->nd_opt_type) {
1168 		case ND_OPT_PREFIX_INFORMATION:
1169 			verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
1170 			break;
1171 		case ND_OPT_MTU:
1172 			verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
1173 			break;
1174 		default:
1175 			break;
1176 		}
1177 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1178 		len -= optlen;
1179 	}
1180 }
1181 
1182 /*
1183  * Verify that the lifetimes and onlink/auto flags are consistent
1184  * with our settings.
1185  */
1186 static void
1187 verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1188 {
1189 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
1190 	int plen;
1191 	struct adv_prefix *adv_pr;
1192 	uint32_t validtime, preftime;
1193 	char prefixbuf[INET6_ADDRSTRLEN];
1194 	int pktflag, myflag;
1195 
1196 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
1197 		logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
1198 		    "(%d bytes)\n",
1199 		    frombuf, pi->pi_name,
1200 		    8 * (int)po->nd_opt_pi_len);
1201 		return;
1202 	}
1203 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1204 		logmsg(LOG_INFO, "RA from %s on %s contains link-local "
1205 		    "prefix - ignored\n",
1206 		    frombuf, pi->pi_name);
1207 		return;
1208 	}
1209 	plen = po->nd_opt_pi_prefix_len;
1210 	adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
1211 	if (adv_pr == NULL)
1212 		return;
1213 
1214 	/* Ignore prefixes which we do not advertise */
1215 	if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
1216 		return;
1217 	(void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
1218 	    prefixbuf, sizeof (prefixbuf));
1219 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
1220 	myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
1221 	if (pktflag != myflag) {
1222 		logmsg(LOG_INFO,
1223 		    "RA from %s on %s inconsistent autonumous flag for \n\t"
1224 		    "prefix %s/%u: received %s configuration %s\n",
1225 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1226 		    (pktflag ? "ON" : "OFF"),
1227 		    (myflag ? "ON" : "OFF"));
1228 	}
1229 
1230 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
1231 	myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
1232 	if (pktflag != myflag) {
1233 		logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
1234 		    "for \n\tprefix %s/%u: received %s configuration %s\n",
1235 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1236 		    (pktflag ? "ON" : "OFF"),
1237 		    (myflag ? "ON" : "OFF"));
1238 	}
1239 	validtime = ntohl(po->nd_opt_pi_valid_time);
1240 	preftime = ntohl(po->nd_opt_pi_preferred_time);
1241 
1242 	/*
1243 	 * Take into account variation for lifetimes decrementing
1244 	 * in real time. Allow +/- 10 percent and +/- 10 seconds.
1245 	 */
1246 #define	LOWER_LIMIT(val)	((val) - (val)/10 - 10)
1247 #define	UPPER_LIMIT(val)	((val) + (val)/10 + 10)
1248 	if (adv_pr->adv_pr_AdvValidRealTime) {
1249 		if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
1250 		    (validtime <
1251 		    LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
1252 		    validtime >
1253 		    UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
1254 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1255 			    "lifetime for\n\tprefix %s/%u: received %d "
1256 			    "configuration %d\n",
1257 			    frombuf, pi->pi_name, prefixbuf,
1258 			    adv_pr->adv_pr_prefix_len,
1259 			    validtime, adv_pr->adv_pr_AdvValidExpiration);
1260 		}
1261 	} else {
1262 		if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
1263 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1264 			    "lifetime for\n\tprefix %s/%u: received %d "
1265 			    "configuration %d\n",
1266 			    frombuf, pi->pi_name, prefixbuf,
1267 			    adv_pr->adv_pr_prefix_len,
1268 			    validtime, adv_pr->adv_pr_AdvValidLifetime);
1269 		}
1270 	}
1271 
1272 	if (adv_pr->adv_pr_AdvPreferredRealTime) {
1273 		if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
1274 		    (preftime <
1275 		    LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
1276 		    preftime >
1277 		    UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
1278 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1279 			    "preferred lifetime for\n\tprefix %s/%u: "
1280 			    "received %d configuration %d\n",
1281 			    frombuf, pi->pi_name, prefixbuf,
1282 			    adv_pr->adv_pr_prefix_len,
1283 			    preftime, adv_pr->adv_pr_AdvPreferredExpiration);
1284 		}
1285 	} else {
1286 		if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
1287 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1288 			    "preferred lifetime for\n\tprefix %s/%u: "
1289 			    "received %d configuration %d\n",
1290 			    frombuf, pi->pi_name, prefixbuf,
1291 			    adv_pr->adv_pr_prefix_len,
1292 			    preftime, adv_pr->adv_pr_AdvPreferredLifetime);
1293 		}
1294 	}
1295 }
1296 
1297 /*
1298  * Verify the received MTU against our own configuration.
1299  */
1300 static void
1301 verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1302 {
1303 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
1304 	uint32_t mtu;
1305 
1306 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
1307 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
1308 		    "(%d bytes)\n",
1309 		    frombuf, pi->pi_name,
1310 		    8 * (int)mo->nd_opt_mtu_len);
1311 		return;
1312 	}
1313 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1314 	if (pi->pi_AdvLinkMTU != 0 &&
1315 	    pi->pi_AdvLinkMTU != mtu) {
1316 		logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
1317 		    "received %d configuration %d\n",
1318 		    frombuf, pi->pi_name,
1319 		    mtu, pi->pi_AdvLinkMTU);
1320 	}
1321 }
1322 
1323 /*
1324  * Verify that all options have a non-zero length and that
1325  * the options fit within the total length of the packet (optlen).
1326  */
1327 static boolean_t
1328 verify_opt_len(struct nd_opt_hdr *opt, int optlen,
1329     struct phyint *pi, struct sockaddr_in6 *from)
1330 {
1331 	while (optlen > 0) {
1332 		if (opt->nd_opt_len == 0) {
1333 			char abuf[INET6_ADDRSTRLEN];
1334 
1335 			(void) inet_ntop(AF_INET6,
1336 			    (void *)&from->sin6_addr,
1337 			    abuf, sizeof (abuf));
1338 
1339 			logmsg(LOG_INFO, "Zero length option type 0x%x "
1340 			    "from %s on %s\n",
1341 			    opt->nd_opt_type, abuf, pi->pi_name);
1342 			return (_B_FALSE);
1343 		}
1344 		optlen -= 8 * opt->nd_opt_len;
1345 		if (optlen < 0) {
1346 			char abuf[INET6_ADDRSTRLEN];
1347 
1348 			(void) inet_ntop(AF_INET6,
1349 			    (void *)&from->sin6_addr,
1350 			    abuf, sizeof (abuf));
1351 
1352 			logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
1353 			    "from %s on %s\n",
1354 			    opt->nd_opt_type, opt->nd_opt_len,
1355 			    abuf, pi->pi_name);
1356 			return (_B_FALSE);
1357 		}
1358 		opt = (struct nd_opt_hdr *)((char *)opt +
1359 		    8 * opt->nd_opt_len);
1360 	}
1361 	return (_B_TRUE);
1362 }
1363 
1364 /*
1365  * Update IsRouter Flag for Host turning into a router or vice-versa.
1366  */
1367 static void
1368 update_ra_flag(const struct phyint *pi, const struct sockaddr_in6 *from,
1369     int isrouter)
1370 {
1371 	struct lifreq lifr;
1372 	char abuf[INET6_ADDRSTRLEN];
1373 	struct sockaddr_in6 *sin6;
1374 
1375 	/* check if valid flag is being set */
1376 	if ((isrouter != NDF_ISROUTER_ON) &&
1377 	    (isrouter != NDF_ISROUTER_OFF)) {
1378 		logmsg(LOG_ERR, "update_ra_flag: Invalid IsRouter "
1379 		    "flag %d\n", isrouter);
1380 		return;
1381 	}
1382 
1383 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1384 	bzero(sin6, sizeof (*sin6));
1385 	sin6->sin6_family = AF_INET6;
1386 	sin6->sin6_addr = from->sin6_addr;
1387 
1388 	(void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1389 
1390 	if (ioctl(pi->pi_sock, SIOCLIFGETND, (char *)&lifr) < 0) {
1391 		if (errno == ESRCH) {
1392 			if (debug & D_IFSCAN) {
1393 				logmsg(LOG_DEBUG,
1394 "update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER");
1395 			}
1396 		} else {
1397 			logperror_pi(pi, "update_ra_flag: SIOCLIFGETND");
1398 		}
1399 	} else {
1400 		/*
1401 		 * The lif_nd_req structure has three state values to be used
1402 		 * when changing/updating nces :
1403 		 * lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla.
1404 		 *
1405 		 * In this case, we're updating an nce, without changing lla;
1406 		 * so we set lnr_state_same_lla to ND_UNCHANGED, indicating that
1407 		 * nce's state should not be affected by our flag change.
1408 		 *
1409 		 * The kernel implementation also expects the lnr_state_create
1410 		 * field be always set, before processing ioctl request for NCE
1411 		 * update.
1412 		 * We use the state as STALE, while addressing the possibility
1413 		 * of NCE deletion when ioctl with SIOCLIFGETND argument
1414 		 * in earlier step is returned - further in such case we don't
1415 		 * want to re-create the entry in the reachable state.
1416 		 */
1417 		lifr.lifr_nd.lnr_state_create = ND_STALE;
1418 		lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1419 		lifr.lifr_nd.lnr_flags = isrouter;
1420 		if ((ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr)) < 0) {
1421 			logperror_pi(pi, "update_ra_flag: SIOCLIFSETND");
1422 		} else {
1423 			(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1424 			    abuf, sizeof (abuf));
1425 			logmsg(LOG_INFO, "update_ra_flag: IsRouter flag "
1426 			    "updated for %s\n", abuf);
1427 		}
1428 	}
1429 }
1430