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