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