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