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 != pi->pi_CurHopLimit) {
352 		pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
353 
354 		lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
355 		set_needed = _B_TRUE;
356 	}
357 
358 	reachable = ntohl(ra->nd_ra_reachable);
359 	if (reachable != 0 &&
360 	    reachable != pi->pi_BaseReachableTime) {
361 		pi->pi_BaseReachableTime = reachable;
362 		reachable_time_changed = _B_TRUE;
363 	}
364 
365 	if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
366 	    reachable_time_changed) {
367 		phyint_reach_random(pi, _B_FALSE);
368 		set_needed = _B_TRUE;
369 	}
370 	lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
371 
372 	retrans = ntohl(ra->nd_ra_retransmit);
373 	if (retrans != 0 &&
374 	    pi->pi_RetransTimer != retrans) {
375 		pi->pi_RetransTimer = retrans;
376 		lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
377 		set_needed = _B_TRUE;
378 	}
379 
380 	if (set_needed) {
381 		if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
382 			logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
383 			return;
384 		}
385 	}
386 
387 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) &&
388 	    !(ra_flags & ND_RA_FLAG_MANAGED)) {
389 		ra_flags |= ND_RA_FLAG_MANAGED;
390 		/* TODO trigger dhcpv6 */
391 		logmsg(LOG_DEBUG, "incoming_ra: trigger dhcp MANAGED\n");
392 	}
393 	if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) &&
394 	    !(ra_flags & ND_RA_FLAG_OTHER)) {
395 		ra_flags |= ND_RA_FLAG_OTHER;
396 		if (!(ra_flags & ND_RA_FLAG_MANAGED)) {
397 			/* TODO trigger dhcpv6 for non-address info */
398 			logmsg(LOG_DEBUG, "incoming_ra: trigger dhcp OTHER\n");
399 		}
400 	}
401 	/* Skip default router code if sent from ourselves */
402 	if (!loopback) {
403 		/* Find and update or add default router in list */
404 		dr = router_lookup(pi, from->sin6_addr);
405 		router_lifetime = ntohs(ra->nd_ra_router_lifetime);
406 		if (dr == NULL) {
407 			if (router_lifetime != 0) {
408 				dr = router_create(pi, from->sin6_addr,
409 				    MILLISEC * router_lifetime);
410 				timer_schedule(dr->dr_lifetime);
411 			}
412 		} else {
413 			dr->dr_lifetime = MILLISEC * router_lifetime;
414 			if (dr->dr_lifetime != 0)
415 				timer_schedule(dr->dr_lifetime);
416 			if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
417 			    (dr->dr_lifetime == 0 && dr->dr_inkernel))
418 				router_update_k(dr);
419 		}
420 	}
421 	/* Process any options */
422 	len -= sizeof (struct nd_router_advert);
423 	opt = (struct nd_opt_hdr *)&ra[1];
424 	while (len >= sizeof (struct nd_opt_hdr)) {
425 		optlen = opt->nd_opt_len * 8;
426 		switch (opt->nd_opt_type) {
427 		case ND_OPT_PREFIX_INFORMATION:
428 			incoming_prefix_opt(pi, (uchar_t *)opt, from,
429 			    loopback);
430 			break;
431 		case ND_OPT_MTU:
432 			incoming_mtu_opt(pi, (uchar_t *)opt, from);
433 			break;
434 		case ND_OPT_SOURCE_LINKADDR:
435 			/* skip lla option if sent from ourselves! */
436 			if (!loopback) {
437 				incoming_lla_opt(pi, (uchar_t *)opt,
438 				    from, NDF_ISROUTER_ON);
439 				slla_opt_present = _B_TRUE;
440 			}
441 			break;
442 		default:
443 			break;
444 		}
445 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
446 		len -= optlen;
447 	}
448 	if (!loopback && !slla_opt_present)
449 		update_ra_flag(pi, from, NDF_ISROUTER_ON);
450 	/* Stop sending solicitations */
451 	check_to_solicit(pi, SOLICIT_DONE);
452 }
453 
454 /*
455  * Process a received prefix option.
456  * Unless addrconf is turned off we process both the addrconf and the
457  * onlink aspects of the prefix option.
458  *
459  * Note that when a flag (onlink or auto) is turned off we do nothing -
460  * the prefix will time out.
461  */
462 static void
463 incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
464     struct sockaddr_in6 *from, boolean_t loopback)
465 {
466 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
467 	boolean_t	good_prefix = _B_TRUE;
468 
469 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
470 		char abuf[INET6_ADDRSTRLEN];
471 
472 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
473 		    abuf, sizeof (abuf));
474 		logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
475 		    "(%d bytes)\n",
476 		    abuf, pi->pi_name,
477 		    8 * (int)po->nd_opt_pi_len);
478 		return;
479 	}
480 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
481 		char abuf[INET6_ADDRSTRLEN];
482 
483 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
484 		    abuf, sizeof (abuf));
485 		logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
486 		    "- ignored\n",
487 		    abuf, pi->pi_name);
488 		return;
489 	}
490 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
491 	    pi->pi_StatelessAddrConf) {
492 		good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
493 	}
494 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
495 	    good_prefix) {
496 		incoming_prefix_onlink(pi, opt, from, loopback);
497 	}
498 }
499 
500 /*
501  * Process prefix options with the onlink flag set.
502  *
503  * If there are no routers ndpd will add an onlink
504  * default route which will allow communication
505  * between neighbors.
506  *
507  * This function needs to loop to find the same prefix multiple times
508  * as if a failover happened earlier, the addresses belonging to
509  * a different interface may be found here on this interface.
510  */
511 /* ARGSUSED2 */
512 static void
513 incoming_prefix_onlink(struct phyint *pi, uchar_t *opt,
514     struct sockaddr_in6 *from, boolean_t loopback)
515 {
516 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
517 	int plen;
518 	struct prefix *pr;
519 	uint32_t validtime;	/* Without 2 hour rule */
520 	boolean_t found_one = _B_FALSE;
521 
522 	plen = po->nd_opt_pi_prefix_len;
523 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
524 		if (pr->pr_prefix_len == plen &&
525 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
526 			/* Exclude static prefixes */
527 			if (pr->pr_state & PR_STATIC)
528 				continue;
529 			found_one = _B_TRUE;
530 			incoming_prefix_onlink_process(pr, opt);
531 		}
532 	}
533 
534 	validtime = ntohl(po->nd_opt_pi_valid_time);
535 	/*
536 	 * If we have found a matching prefix already or validtime
537 	 * is zero, we have nothing to do.
538 	 */
539 	if (validtime == 0 || found_one)
540 		return;
541 	pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
542 	if (pr == NULL)
543 		return;
544 	incoming_prefix_onlink_process(pr, opt);
545 }
546 
547 void
548 incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
549 {
550 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
551 	uint32_t validtime;	/* Without 2 hour rule */
552 	char abuf[INET6_ADDRSTRLEN];
553 
554 	validtime = ntohl(po->nd_opt_pi_valid_time);
555 	if (validtime != 0)
556 		pr->pr_state |= PR_ONLINK;
557 	else
558 		pr->pr_state &= ~PR_ONLINK;
559 
560 	/*
561 	 * Convert from seconds to milliseconds avoiding overflow.
562 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
563 	 * (4 billion seconds - about 130 years) we will in fact time
564 	 * out the prefix after 4 billion milliseconds - 46 days).
565 	 * Thus the longest lifetime (apart from infinity) is 46 days.
566 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
567 	 */
568 	if (pr->pr_flags & IFF_TEMPORARY) {
569 		pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
570 	} else {
571 		if (validtime >= PREFIX_INFINITY / MILLISEC)
572 			pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
573 		else
574 			pr->pr_OnLinkLifetime = validtime * MILLISEC;
575 	}
576 	pr->pr_OnLinkFlag = _B_TRUE;
577 	if (debug & (D_PREFIX|D_TMP)) {
578 		logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
579 		    "onlink %u state 0x%x, kstate 0x%x\n",
580 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
581 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
582 		    pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
583 	}
584 
585 	if (pr->pr_kernel_state != pr->pr_state) {
586 		prefix_update_k(pr);
587 	}
588 
589 	if (pr->pr_OnLinkLifetime != 0)
590 		timer_schedule(pr->pr_OnLinkLifetime);
591 }
592 
593 /*
594  * Process prefix options with the autonomous flag set.
595  * Returns false if this prefix results in a bad address (duplicate)
596  * This function needs to loop to find the same prefix multiple times
597  * as if a failover happened earlier, the addresses belonging to
598  * a different interface may be found here on this interface.
599  */
600 /* ARGSUSED2 */
601 static boolean_t
602 incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
603     struct sockaddr_in6 *from, boolean_t loopback)
604 {
605 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
606 	int plen;
607 	struct prefix *pr;
608 	uint32_t validtime, preftime;	/* In seconds */
609 	char abuf[INET6_ADDRSTRLEN];
610 	char pbuf[INET6_ADDRSTRLEN];
611 	boolean_t found_pub = _B_FALSE;
612 	boolean_t found_tmp = _B_FALSE;
613 	boolean_t ret;
614 
615 	validtime = ntohl(po->nd_opt_pi_valid_time);
616 	preftime = ntohl(po->nd_opt_pi_preferred_time);
617 	plen = po->nd_opt_pi_prefix_len;
618 
619 	/* Sanity checks */
620 	if (validtime < preftime) {
621 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
622 		    abuf, sizeof (abuf));
623 		(void) inet_ntop(AF_INET6,
624 		    (void *)&po->nd_opt_pi_prefix,
625 		    pbuf, sizeof (pbuf));
626 		logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
627 		    "valid %u < pref %u ignored\n",
628 		    pbuf, plen, abuf, pi->pi_name,
629 		    validtime, preftime);
630 		return (_B_FALSE);
631 	}
632 
633 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
634 		if (pr->pr_prefix_len == plen &&
635 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
636 
637 			/* Exclude static prefixes */
638 			if (pr->pr_state & PR_STATIC)
639 				continue;
640 			if (pr->pr_flags & IFF_TEMPORARY) {
641 				/*
642 				 * If this address is deprecated and its token
643 				 * doesn't match the current tmp token, we want
644 				 * to create a new address with the current
645 				 * token.  So don't count this addr as a match.
646 				 */
647 				if (!((pr->pr_flags & IFF_DEPRECATED) &&
648 				    !token_equal(pi->pi_tmp_token,
649 				    pr->pr_address, TMP_TOKEN_BITS)))
650 					found_tmp = _B_TRUE;
651 			} else {
652 				found_pub = _B_TRUE;
653 			}
654 			(void) incoming_prefix_addrconf_process(pi, pr, opt,
655 			    from, loopback, _B_FALSE);
656 		}
657 	}
658 
659 	/*
660 	 * If we have found a matching prefix (for public and, if temp addrs
661 	 * are enabled, for temporary) already or validtime is zero, we have
662 	 * nothing to do.
663 	 */
664 	if (validtime == 0 ||
665 	    (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp)))
666 		return (_B_TRUE);
667 
668 	if (!found_pub) {
669 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
670 		if (pr == NULL)
671 			return (_B_TRUE);
672 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
673 		    loopback, _B_TRUE);
674 	}
675 	/*
676 	 * if processing of the public address failed,
677 	 * don't bother with the temporary address.
678 	 */
679 	if (ret == _B_FALSE)
680 		return (_B_FALSE);
681 
682 	if (pi->pi_TmpAddrsEnabled && !found_tmp) {
683 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen,
684 		    IFF_TEMPORARY);
685 		if (pr == NULL)
686 			return (_B_TRUE);
687 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
688 		    loopback, _B_TRUE);
689 	}
690 
691 	return (ret);
692 }
693 
694 boolean_t
695 incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr,
696     uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback,
697     boolean_t new_prefix)
698 {
699 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
700 	char abuf[INET6_ADDRSTRLEN];
701 	char pbuf[INET6_ADDRSTRLEN];
702 	uint32_t validtime, preftime;	/* In seconds */
703 	uint32_t recorded_validtime;	/* In seconds */
704 	int plen, dadfails = 0;
705 	struct prefix *other_pr;
706 
707 	validtime = ntohl(po->nd_opt_pi_valid_time);
708 	preftime = ntohl(po->nd_opt_pi_preferred_time);
709 	plen = po->nd_opt_pi_prefix_len;
710 	if (!new_prefix) {
711 		/*
712 		 * Check 2 hour rule on valid lifetime.
713 		 * Follows: RFC 2462
714 		 * If we advertised this prefix ourselves we skip
715 		 * these checks. They are also skipped if we did not
716 		 * previously do addrconf on this prefix.
717 		 */
718 		recorded_validtime = pr->pr_ValidLifetime / MILLISEC;
719 
720 		if (loopback || !(pr->pr_state & PR_AUTO) ||
721 		    validtime >= MIN_VALID_LIFETIME ||
722 		    /* LINTED - statement has no consequent */
723 		    validtime >= recorded_validtime) {
724 			/* OK */
725 		} else if (recorded_validtime < MIN_VALID_LIFETIME &&
726 		    validtime < recorded_validtime) {
727 			/* Ignore the prefix */
728 			(void) inet_ntop(AF_INET6,
729 			    (void *)&from->sin6_addr,
730 			    abuf, sizeof (abuf));
731 			(void) inet_ntop(AF_INET6,
732 			    (void *)&po->nd_opt_pi_prefix,
733 			    pbuf, sizeof (pbuf));
734 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
735 			    "too short valid lifetime %u stored %u "
736 			    "- ignored\n",
737 			    pbuf, plen, abuf, pi->pi_name,
738 			    validtime, recorded_validtime);
739 			return (_B_TRUE);
740 		} else {
741 			/*
742 			 * If the router clock runs slower than the
743 			 * host by 1 second over 2 hours then this
744 			 * test will set the lifetime back to 2 hours
745 			 * once i.e. a lifetime decrementing in
746 			 * realtime might cause the prefix to live an
747 			 * extra 2 hours on the host.
748 			 */
749 			(void) inet_ntop(AF_INET6,
750 			    (void *)&from->sin6_addr,
751 			    abuf, sizeof (abuf));
752 			(void) inet_ntop(AF_INET6,
753 			    (void *)&po->nd_opt_pi_prefix,
754 			    pbuf, sizeof (pbuf));
755 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
756 			    "valid time %u stored %u rounded up "
757 			    "to %u\n",
758 			    pbuf, plen, abuf, pi->pi_name,
759 			    validtime, recorded_validtime,
760 			    MIN_VALID_LIFETIME);
761 			validtime = MIN_VALID_LIFETIME;
762 		}
763 	}
764 
765 	/*
766 	 * For RFC3041 addresses, need to take token lifetime
767 	 * into account, too.
768 	 */
769 	if (pr->pr_flags & IFF_TEMPORARY) {
770 		uint_t	cur_tpreftime =
771 		    pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor;
772 
773 		if (new_prefix) {
774 			validtime = MIN(validtime, pi->pi_TmpValidLifetime);
775 			preftime = MIN(preftime, cur_tpreftime);
776 		} else {
777 			uint_t cur_vexp, cur_pexp, curtime;
778 			curtime = getcurrenttime() / MILLISEC;
779 
780 			cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime;
781 			cur_pexp = pr->pr_CreateTime + cur_tpreftime;
782 			if (curtime > cur_vexp)
783 				validtime = 0;
784 			else if ((curtime + validtime) > cur_vexp)
785 				validtime = cur_vexp - curtime;
786 			/*
787 			 * If this is an existing address which was deprecated
788 			 * because of a bad token, we don't want to update its
789 			 * preferred lifetime!
790 			 */
791 			if ((pr->pr_PreferredLifetime == 0) &&
792 			    !token_equal(pr->pr_address, pi->pi_tmp_token,
793 			    TMP_TOKEN_BITS))
794 				preftime = 0;
795 			else if (curtime > cur_pexp)
796 				preftime = 0;
797 			else if ((curtime + preftime) > cur_pexp)
798 				preftime = cur_pexp - curtime;
799 		}
800 		if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) {
801 			(void) inet_ntop(AF_INET6,
802 			    (void *)&from->sin6_addr,
803 			    abuf, sizeof (abuf));
804 			(void) inet_ntop(AF_INET6,
805 			    (void *)&po->nd_opt_pi_prefix,
806 			    pbuf, sizeof (pbuf));
807 			logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: "
808 			    "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
809 			    pbuf, plen, abuf, pi->pi_name, preftime,
810 			    pi->pi_TmpRegenAdvance);
811 			if (new_prefix)
812 				prefix_delete(pr);
813 			return (_B_TRUE);
814 		}
815 	}
816 	if (debug & D_TMP)
817 		logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, "
818 		    "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime);
819 
820 	if (!(pr->pr_state & PR_AUTO)) {
821 		int i, tokenlen;
822 		in6_addr_t *token;
823 		/*
824 		 * Form a new local address if the lengths match.
825 		 */
826 		if (pr->pr_flags && IFF_TEMPORARY) {
827 RETRY_TOKEN:
828 			if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
829 				if (!tmptoken_create(pi)) {
830 					prefix_delete(pr);
831 					return (_B_TRUE);
832 				}
833 			}
834 			tokenlen = TMP_TOKEN_BITS;
835 			token = &pi->pi_tmp_token;
836 		} else {
837 			tokenlen = pi->pi_token_length;
838 			token = &pi->pi_token;
839 		}
840 		if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
841 			(void) inet_ntop(AF_INET6,
842 			    (void *)&from->sin6_addr,
843 			    abuf, sizeof (abuf));
844 			(void) inet_ntop(AF_INET6,
845 			    (void *)&po->nd_opt_pi_prefix,
846 			    pbuf, sizeof (pbuf));
847 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
848 			    "mismatched length %d token length %d\n",
849 			    pbuf, plen, abuf, pi->pi_name,
850 			    pr->pr_prefix_len, tokenlen);
851 			return (_B_TRUE);
852 		}
853 		for (i = 0; i < 16; i++) {
854 			/*
855 			 * prefix_create ensures that pr_prefix has all-zero
856 			 * bits after prefixlen.
857 			 */
858 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
859 			    token->s6_addr[i];
860 		}
861 		/*
862 		 * Check if any other physical interface has the same
863 		 * address configured already
864 		 */
865 		if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
866 			/*
867 			 * Delete this prefix structure as kernel
868 			 * does not allow duplicated addresses
869 			 */
870 
871 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
872 			    "Duplicate prefix  %s received on interface %s\n",
873 			    inet_ntop(AF_INET6,
874 			    (void *)&po->nd_opt_pi_prefix, abuf,
875 			    sizeof (abuf)), pi->pi_name);
876 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
877 			    "Prefix already exists in interface %s\n",
878 			    other_pr->pr_physical->pi_name);
879 			if (new_prefix) {
880 				prefix_delete(pr);
881 				return (_B_FALSE);
882 			}
883 			/* Ignore for addrconf purposes */
884 			validtime = preftime = 0;
885 		}
886 		if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
887 			struct sockaddr_in6 sin6;
888 			sin6.sin6_family = AF_INET6;
889 			sin6.sin6_addr = pr->pr_address;
890 			if (do_dad(pi->pi_name, &sin6) != 0) {
891 				/* DAD failed, need a new token */
892 				dadfails++;
893 				logmsg(LOG_WARNING,
894 				    "incoming_prefix_addrconf_process: "
895 				    "deprecating temporary token %s\n",
896 				    inet_ntop(AF_INET6,
897 				    (void *)&pi->pi_tmp_token, abuf,
898 				    sizeof (abuf)));
899 				tmptoken_delete(pi);
900 				if (dadfails == MAX_DAD_FAILURES) {
901 					logmsg(LOG_ERR, "Too many DAD "
902 					    "failures; disabling temporary "
903 					    "addresses on %s\n", pi->pi_name);
904 					pi->pi_TmpAddrsEnabled = 0;
905 					prefix_delete(pr);
906 					return (_B_TRUE);
907 				}
908 				goto RETRY_TOKEN;
909 			}
910 			pr->pr_CreateTime = getcurrenttime() / MILLISEC;
911 			if (debug & D_TMP)
912 				logmsg(LOG_DEBUG,
913 				    "created tmp addr(%s v %d p %d)\n",
914 				    pr->pr_name, validtime, preftime);
915 		}
916 	}
917 
918 	if (validtime != 0)
919 		pr->pr_state |= PR_AUTO;
920 	else
921 		pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
922 	if (preftime != 0 || !(pr->pr_state & PR_AUTO))
923 		pr->pr_state &= ~PR_DEPRECATED;
924 	else
925 		pr->pr_state |= PR_DEPRECATED;
926 
927 	/*
928 	 * Convert from seconds to milliseconds avoiding overflow.
929 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
930 	 * (4 billion seconds - about 130 years) we will in fact time
931 	 * out the prefix after 4 billion milliseconds - 46 days).
932 	 * Thus the longest lifetime (apart from infinity) is 46 days.
933 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
934 	 */
935 	if (validtime >= PREFIX_INFINITY / MILLISEC)
936 		pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
937 	else
938 		pr->pr_ValidLifetime = validtime * MILLISEC;
939 	if (preftime >= PREFIX_INFINITY / MILLISEC)
940 		pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
941 	else
942 		pr->pr_PreferredLifetime = preftime * MILLISEC;
943 	pr->pr_AutonomousFlag = _B_TRUE;
944 
945 	if (debug & D_PREFIX) {
946 		logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
947 		    "valid %u pref %u\n",
948 		    pr->pr_physical->pi_name,
949 		    inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
950 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
951 		    pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
952 	}
953 
954 	if (pr->pr_state & PR_AUTO) {
955 		/* Take the min of the two timeouts by calling it twice */
956 		if (pr->pr_ValidLifetime != 0)
957 			timer_schedule(pr->pr_ValidLifetime);
958 		if (pr->pr_PreferredLifetime != 0)
959 			timer_schedule(pr->pr_PreferredLifetime);
960 	}
961 	if (pr->pr_kernel_state != pr->pr_state) {
962 		/* Log a message when an addrconf prefix goes away */
963 		if ((pr->pr_kernel_state & PR_AUTO) &&
964 		    !(pr->pr_state & PR_AUTO)) {
965 			char abuf[INET6_ADDRSTRLEN];
966 
967 			logmsg(LOG_WARNING, "Address removed due to zero "
968 			    "valid lifetime %s\n",
969 			    inet_ntop(AF_INET6, (void *)&pr->pr_address,
970 			    abuf, sizeof (abuf)));
971 		}
972 		prefix_update_k(pr);
973 	}
974 	return (_B_TRUE);
975 }
976 
977 /*
978  * Process an MTU option received in a router advertisement.
979  */
980 static void
981 incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
982     struct sockaddr_in6 *from)
983 {
984 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
985 	struct lifreq lifr;
986 	uint32_t mtu;
987 
988 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
989 		char abuf[INET6_ADDRSTRLEN];
990 
991 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
992 		    abuf, sizeof (abuf));
993 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
994 		    "(%d bytes)\n",
995 		    abuf, pi->pi_name,
996 		    8 * (int)mo->nd_opt_mtu_len);
997 		return;
998 	}
999 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1000 	if (pi->pi_LinkMTU == mtu)
1001 		return;	/* No change */
1002 	if (mtu > pi->pi_mtu) {
1003 		/* Can't exceed physical MTU */
1004 		char abuf[INET6_ADDRSTRLEN];
1005 
1006 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1007 		    abuf, sizeof (abuf));
1008 		logmsg(LOG_INFO, "mtu option from %s on %s too large "
1009 		    "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
1010 		return;
1011 	}
1012 	if (mtu < IPV6_MIN_MTU) {
1013 		char abuf[INET6_ADDRSTRLEN];
1014 
1015 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1016 		    abuf, sizeof (abuf));
1017 		logmsg(LOG_INFO, "mtu option from %s on %s too small "
1018 		    "MTU (%d)\n", abuf, pi->pi_name, mtu);
1019 		return;
1020 	}
1021 
1022 	pi->pi_LinkMTU = mtu;
1023 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1024 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1025 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
1026 		logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
1027 		return;
1028 	}
1029 	lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
1030 	if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
1031 		logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
1032 		return;
1033 	}
1034 }
1035 
1036 /*
1037  * Process a source link-layer address option received in a router
1038  * advertisement or solicitation.
1039  */
1040 static void
1041 incoming_lla_opt(struct phyint *pi, uchar_t *opt,
1042     struct sockaddr_in6 *from, int isrouter)
1043 {
1044 	struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
1045 	struct lifreq lifr;
1046 	struct sockaddr_in6 *sin6;
1047 	int max_content_len;
1048 
1049 	if (pi->pi_hdw_addr_len == 0)
1050 		return;
1051 
1052 	/*
1053 	 * Can't remove padding since it is link type specific.
1054 	 * However, we check against the length of our link-layer
1055 	 * address.
1056 	 * Note: assumes that all links have a fixed lengh address.
1057 	 */
1058 	max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
1059 	if (max_content_len < pi->pi_hdw_addr_len ||
1060 	    (max_content_len >= 8 &&
1061 	    max_content_len - 7 > pi->pi_hdw_addr_len)) {
1062 		char abuf[INET6_ADDRSTRLEN];
1063 
1064 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1065 		    abuf, sizeof (abuf));
1066 		logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
1067 		    "physaddr length (%d vs. %d bytes)\n",
1068 		    abuf, pi->pi_name,
1069 		    max_content_len, pi->pi_hdw_addr_len);
1070 		return;
1071 	}
1072 
1073 	lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
1074 	bcopy((char *)lo->nd_opt_lla_hdw_addr,
1075 	    (char *)lifr.lifr_nd.lnr_hdw_addr,
1076 	    lifr.lifr_nd.lnr_hdw_len);
1077 
1078 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1079 	bzero(sin6, sizeof (struct sockaddr_in6));
1080 	sin6->sin6_family = AF_INET6;
1081 	sin6->sin6_addr = from->sin6_addr;
1082 
1083 	/*
1084 	 * Set IsRouter flag if RA; clear if RS.
1085 	 */
1086 	lifr.lifr_nd.lnr_state_create = ND_STALE;
1087 	lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1088 	lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
1089 	lifr.lifr_nd.lnr_flags = isrouter;
1090 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1091 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
1092 	if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
1093 		logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
1094 		return;
1095 	}
1096 }
1097 
1098 /*
1099  * Verify the content of the received router advertisement against our
1100  * own configuration as specified in RFC 2461.
1101  */
1102 static void
1103 verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
1104     struct sockaddr_in6 *from)
1105 {
1106 	char frombuf[INET6_ADDRSTRLEN];
1107 	struct nd_opt_hdr *opt;
1108 	int optlen;
1109 	uint_t reachable, retrans;
1110 	boolean_t pktflag, myflag;
1111 
1112 	(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1113 	    frombuf, sizeof (frombuf));
1114 
1115 	if (ra->nd_ra_curhoplimit != 0 &&
1116 	    pi->pi_AdvCurHopLimit != 0 &&
1117 	    ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
1118 		logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
1119 		    "limit:\n\treceived %d configuration %d\n",
1120 		    frombuf, pi->pi_name,
1121 		    ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
1122 	}
1123 
1124 	reachable = ntohl(ra->nd_ra_reachable);
1125 	if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
1126 	    reachable != pi->pi_AdvReachableTime) {
1127 		logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
1128 		    "time:\n\treceived %d configuration %d\n",
1129 		    frombuf, pi->pi_name,
1130 		    reachable, pi->pi_AdvReachableTime);
1131 	}
1132 
1133 	retrans = ntohl(ra->nd_ra_retransmit);
1134 	if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
1135 	    retrans != pi->pi_AdvRetransTimer) {
1136 		logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
1137 		    "timer:\n\treceived %d configuration %d\n",
1138 		    frombuf, pi->pi_name,
1139 		    retrans, pi->pi_AdvRetransTimer);
1140 	}
1141 
1142 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
1143 	myflag = (pi->pi_AdvManagedFlag != 0);
1144 	if (pktflag != myflag) {
1145 		logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
1146 		    "flag:\n\treceived %s configuration %s\n",
1147 		    frombuf, pi->pi_name,
1148 		    (pktflag ? "ON" : "OFF"),
1149 		    (myflag ? "ON" : "OFF"));
1150 	}
1151 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
1152 	myflag = (pi->pi_AdvOtherConfigFlag != 0);
1153 	if (pktflag != myflag) {
1154 		logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
1155 		    "flag:\n\treceived %s configuration %s\n",
1156 		    frombuf, pi->pi_name,
1157 		    (pktflag ? "ON" : "OFF"),
1158 		    (myflag ? "ON" : "OFF"));
1159 	}
1160 
1161 	/* Process any options */
1162 	len -= sizeof (struct nd_router_advert);
1163 	opt = (struct nd_opt_hdr *)&ra[1];
1164 	while (len >= sizeof (struct nd_opt_hdr)) {
1165 		optlen = opt->nd_opt_len * 8;
1166 		switch (opt->nd_opt_type) {
1167 		case ND_OPT_PREFIX_INFORMATION:
1168 			verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
1169 			break;
1170 		case ND_OPT_MTU:
1171 			verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
1172 			break;
1173 		default:
1174 			break;
1175 		}
1176 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
1177 		len -= optlen;
1178 	}
1179 }
1180 
1181 /*
1182  * Verify that the lifetimes and onlink/auto flags are consistent
1183  * with our settings.
1184  */
1185 static void
1186 verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1187 {
1188 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
1189 	int plen;
1190 	struct adv_prefix *adv_pr;
1191 	uint32_t validtime, preftime;
1192 	char prefixbuf[INET6_ADDRSTRLEN];
1193 	int pktflag, myflag;
1194 
1195 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
1196 		logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
1197 		    "(%d bytes)\n",
1198 		    frombuf, pi->pi_name,
1199 		    8 * (int)po->nd_opt_pi_len);
1200 		return;
1201 	}
1202 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
1203 		logmsg(LOG_INFO, "RA from %s on %s contains link-local "
1204 		    "prefix - ignored\n",
1205 		    frombuf, pi->pi_name);
1206 		return;
1207 	}
1208 	plen = po->nd_opt_pi_prefix_len;
1209 	adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
1210 	if (adv_pr == NULL)
1211 		return;
1212 
1213 	/* Ignore prefixes which we do not advertise */
1214 	if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
1215 		return;
1216 	(void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
1217 	    prefixbuf, sizeof (prefixbuf));
1218 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
1219 	myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
1220 	if (pktflag != myflag) {
1221 		logmsg(LOG_INFO,
1222 		    "RA from %s on %s inconsistent autonumous flag for \n\t"
1223 		    "prefix %s/%u: received %s configuration %s\n",
1224 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1225 		    (pktflag ? "ON" : "OFF"),
1226 		    (myflag ? "ON" : "OFF"));
1227 	}
1228 
1229 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
1230 	myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
1231 	if (pktflag != myflag) {
1232 		logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
1233 		    "for \n\tprefix %s/%u: received %s configuration %s\n",
1234 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
1235 		    (pktflag ? "ON" : "OFF"),
1236 		    (myflag ? "ON" : "OFF"));
1237 	}
1238 	validtime = ntohl(po->nd_opt_pi_valid_time);
1239 	preftime = ntohl(po->nd_opt_pi_preferred_time);
1240 
1241 	/*
1242 	 * Take into account variation for lifetimes decrementing
1243 	 * in real time. Allow +/- 10 percent and +/- 10 seconds.
1244 	 */
1245 #define	LOWER_LIMIT(val)	((val) - (val)/10 - 10)
1246 #define	UPPER_LIMIT(val)	((val) + (val)/10 + 10)
1247 	if (adv_pr->adv_pr_AdvValidRealTime) {
1248 		if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
1249 		    (validtime <
1250 		    LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
1251 		    validtime >
1252 		    UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
1253 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1254 			    "lifetime for\n\tprefix %s/%u: received %d "
1255 			    "configuration %d\n",
1256 			    frombuf, pi->pi_name, prefixbuf,
1257 			    adv_pr->adv_pr_prefix_len,
1258 			    validtime, adv_pr->adv_pr_AdvValidExpiration);
1259 		}
1260 	} else {
1261 		if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
1262 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
1263 			    "lifetime for\n\tprefix %s/%u: received %d "
1264 			    "configuration %d\n",
1265 			    frombuf, pi->pi_name, prefixbuf,
1266 			    adv_pr->adv_pr_prefix_len,
1267 			    validtime, adv_pr->adv_pr_AdvValidLifetime);
1268 		}
1269 	}
1270 
1271 	if (adv_pr->adv_pr_AdvPreferredRealTime) {
1272 		if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
1273 		    (preftime <
1274 		    LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
1275 		    preftime >
1276 		    UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
1277 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1278 			    "preferred lifetime for\n\tprefix %s/%u: "
1279 			    "received %d configuration %d\n",
1280 			    frombuf, pi->pi_name, prefixbuf,
1281 			    adv_pr->adv_pr_prefix_len,
1282 			    preftime, adv_pr->adv_pr_AdvPreferredExpiration);
1283 		}
1284 	} else {
1285 		if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
1286 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
1287 			    "preferred lifetime for\n\tprefix %s/%u: "
1288 			    "received %d configuration %d\n",
1289 			    frombuf, pi->pi_name, prefixbuf,
1290 			    adv_pr->adv_pr_prefix_len,
1291 			    preftime, adv_pr->adv_pr_AdvPreferredLifetime);
1292 		}
1293 	}
1294 }
1295 
1296 /*
1297  * Verify the received MTU against our own configuration.
1298  */
1299 static void
1300 verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
1301 {
1302 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
1303 	uint32_t mtu;
1304 
1305 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
1306 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
1307 		    "(%d bytes)\n",
1308 		    frombuf, pi->pi_name,
1309 		    8 * (int)mo->nd_opt_mtu_len);
1310 		return;
1311 	}
1312 	mtu = ntohl(mo->nd_opt_mtu_mtu);
1313 	if (pi->pi_AdvLinkMTU != 0 &&
1314 	    pi->pi_AdvLinkMTU != mtu) {
1315 		logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
1316 		    "received %d configuration %d\n",
1317 		    frombuf, pi->pi_name,
1318 		    mtu, pi->pi_AdvLinkMTU);
1319 	}
1320 }
1321 
1322 /*
1323  * Verify that all options have a non-zero length and that
1324  * the options fit within the total length of the packet (optlen).
1325  */
1326 static boolean_t
1327 verify_opt_len(struct nd_opt_hdr *opt, int optlen,
1328     struct phyint *pi, struct sockaddr_in6 *from)
1329 {
1330 	while (optlen > 0) {
1331 		if (opt->nd_opt_len == 0) {
1332 			char abuf[INET6_ADDRSTRLEN];
1333 
1334 			(void) inet_ntop(AF_INET6,
1335 			    (void *)&from->sin6_addr,
1336 			    abuf, sizeof (abuf));
1337 
1338 			logmsg(LOG_INFO, "Zero length option type 0x%x "
1339 			    "from %s on %s\n",
1340 			    opt->nd_opt_type, abuf, pi->pi_name);
1341 			return (_B_FALSE);
1342 		}
1343 		optlen -= 8 * opt->nd_opt_len;
1344 		if (optlen < 0) {
1345 			char abuf[INET6_ADDRSTRLEN];
1346 
1347 			(void) inet_ntop(AF_INET6,
1348 			    (void *)&from->sin6_addr,
1349 			    abuf, sizeof (abuf));
1350 
1351 			logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
1352 			    "from %s on %s\n",
1353 			    opt->nd_opt_type, opt->nd_opt_len,
1354 			    abuf, pi->pi_name);
1355 			return (_B_FALSE);
1356 		}
1357 		opt = (struct nd_opt_hdr *)((char *)opt +
1358 		    8 * opt->nd_opt_len);
1359 	}
1360 	return (_B_TRUE);
1361 }
1362 
1363 /*
1364  * Update IsRouter Flag for Host turning into a router or vice-versa.
1365  */
1366 static void
1367 update_ra_flag(const struct phyint *pi, const struct sockaddr_in6 *from,
1368     int isrouter)
1369 {
1370 	struct lifreq lifr;
1371 	char abuf[INET6_ADDRSTRLEN];
1372 	struct sockaddr_in6 *sin6;
1373 
1374 	/* check if valid flag is being set */
1375 	if ((isrouter != NDF_ISROUTER_ON) &&
1376 	    (isrouter != NDF_ISROUTER_OFF)) {
1377 		logmsg(LOG_ERR, "update_ra_flag: Invalid IsRouter "
1378 		    "flag %d\n", isrouter);
1379 		return;
1380 	}
1381 
1382 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
1383 	bzero(sin6, sizeof (*sin6));
1384 	sin6->sin6_family = AF_INET6;
1385 	sin6->sin6_addr = from->sin6_addr;
1386 
1387 	(void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
1388 
1389 	if (ioctl(pi->pi_sock, SIOCLIFGETND, (char *)&lifr) < 0) {
1390 		if (errno == ESRCH) {
1391 			if (debug & D_IFSCAN) {
1392 				logmsg(LOG_DEBUG,
1393 "update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER");
1394 			}
1395 		} else {
1396 			logperror_pi(pi, "update_ra_flag: SIOCLIFGETND");
1397 		}
1398 	} else {
1399 		/*
1400 		 * The lif_nd_req structure has three state values to be used
1401 		 * when changing/updating nces :
1402 		 * lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla.
1403 		 *
1404 		 * In this case, we're updating an nce, without changing lla;
1405 		 * so we set lnr_state_same_lla to ND_UNCHANGED, indicating that
1406 		 * nce's state should not be affected by our flag change.
1407 		 *
1408 		 * The kernel implementation also expects the lnr_state_create
1409 		 * field be always set, before processing ioctl request for NCE
1410 		 * update.
1411 		 * We use the state as STALE, while addressing the possibility
1412 		 * of NCE deletion when ioctl with SIOCLIFGETND argument
1413 		 * in earlier step is returned - further in such case we don't
1414 		 * want to re-create the entry in the reachable state.
1415 		 */
1416 		lifr.lifr_nd.lnr_state_create = ND_STALE;
1417 		lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
1418 		lifr.lifr_nd.lnr_flags = isrouter;
1419 		if ((ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr)) < 0) {
1420 			logperror_pi(pi, "update_ra_flag: SIOCLIFSETND");
1421 		} else {
1422 			(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
1423 			    abuf, sizeof (abuf));
1424 			logmsg(LOG_INFO, "update_ra_flag: IsRouter flag "
1425 			    "updated for %s\n", abuf);
1426 		}
1427 	}
1428 }
1429