17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
59ba4fd8dSrshoaib  * Common Development and Distribution License (the "License").
69ba4fd8dSrshoaib  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
219ba4fd8dSrshoaib 
227c478bd9Sstevel@tonic-gate /*
23*d04ccbb3Scarlsonj  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include "defs.h"
307c478bd9Sstevel@tonic-gate #include "tables.h"
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
337c478bd9Sstevel@tonic-gate 
34*d04ccbb3Scarlsonj #include <dhcpagent_ipc.h>
35*d04ccbb3Scarlsonj #include <dhcpagent_util.h>
36*d04ccbb3Scarlsonj 
377c478bd9Sstevel@tonic-gate static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
387c478bd9Sstevel@tonic-gate 		    struct phyint *pi, struct sockaddr_in6 *from);
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static void	incoming_rs(struct phyint *pi, struct nd_router_solicit *rs,
417c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate void		incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
447c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from, boolean_t loopback);
457c478bd9Sstevel@tonic-gate static void	incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
467c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback);
47*d04ccbb3Scarlsonj static void	incoming_prefix_onlink(struct phyint *pi, uchar_t *opt);
487c478bd9Sstevel@tonic-gate void		incoming_prefix_onlink_process(struct prefix *pr,
497c478bd9Sstevel@tonic-gate 		    uchar_t *opt);
50*d04ccbb3Scarlsonj static void	incoming_prefix_stateful(struct phyint *, uchar_t *);
517c478bd9Sstevel@tonic-gate static boolean_t	incoming_prefix_addrconf(struct phyint *pi,
527c478bd9Sstevel@tonic-gate 		    uchar_t *opt, struct sockaddr_in6 *from,
537c478bd9Sstevel@tonic-gate 		    boolean_t loopback);
547c478bd9Sstevel@tonic-gate boolean_t	incoming_prefix_addrconf_process(struct phyint *pi,
557c478bd9Sstevel@tonic-gate 		    struct prefix *pr, uchar_t *opt,
567c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback,
577c478bd9Sstevel@tonic-gate 		    boolean_t new_prefix);
587c478bd9Sstevel@tonic-gate static void	incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
597c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from);
607c478bd9Sstevel@tonic-gate static void	incoming_lla_opt(struct phyint *pi, uchar_t *opt,
617c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, int isrouter);
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate static void	verify_ra_consistency(struct phyint *pi,
647c478bd9Sstevel@tonic-gate 		    struct nd_router_advert *ra,
657c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
667c478bd9Sstevel@tonic-gate static void	verify_prefix_opt(struct phyint *pi, uchar_t *opt,
677c478bd9Sstevel@tonic-gate 		    char *frombuf);
687c478bd9Sstevel@tonic-gate static void	verify_mtu_opt(struct phyint *pi, uchar_t *opt,
697c478bd9Sstevel@tonic-gate 		    char *frombuf);
707c478bd9Sstevel@tonic-gate 
71b0f490f4Smh static void	update_ra_flag(const struct phyint *pi,
726b27086dSdd 		    const struct sockaddr_in6 *from, int isrouter);
736b27086dSdd 
747c478bd9Sstevel@tonic-gate /*
757c478bd9Sstevel@tonic-gate  * Return a pointer to the specified option buffer.
767c478bd9Sstevel@tonic-gate  * If not found return NULL.
777c478bd9Sstevel@tonic-gate  */
787c478bd9Sstevel@tonic-gate static void *
797c478bd9Sstevel@tonic-gate find_ancillary(struct msghdr *msg, int cmsg_type)
807c478bd9Sstevel@tonic-gate {
817c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsg;
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
847c478bd9Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
857c478bd9Sstevel@tonic-gate 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
867c478bd9Sstevel@tonic-gate 		    cmsg->cmsg_type == cmsg_type) {
877c478bd9Sstevel@tonic-gate 			return (CMSG_DATA(cmsg));
887c478bd9Sstevel@tonic-gate 		}
897c478bd9Sstevel@tonic-gate 	}
907c478bd9Sstevel@tonic-gate 	return (NULL);
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate void
947c478bd9Sstevel@tonic-gate in_data(struct phyint *pi)
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from;
977c478bd9Sstevel@tonic-gate 	struct icmp6_hdr *icmp;
987c478bd9Sstevel@tonic-gate 	struct nd_router_solicit *rs;
997c478bd9Sstevel@tonic-gate 	struct nd_router_advert *ra;
1007c478bd9Sstevel@tonic-gate 	static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
1017c478bd9Sstevel@tonic-gate 	static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
1027c478bd9Sstevel@tonic-gate 	int len;
1037c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
1047c478bd9Sstevel@tonic-gate 	const char *msgbuf;
1057c478bd9Sstevel@tonic-gate 	struct msghdr msg;
1067c478bd9Sstevel@tonic-gate 	struct iovec iov;
1077c478bd9Sstevel@tonic-gate 	uchar_t *opt;
1087c478bd9Sstevel@tonic-gate 	uint_t hoplimit;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	iov.iov_base = (char *)in_packet;
1117c478bd9Sstevel@tonic-gate 	iov.iov_len = sizeof (in_packet);
1127c478bd9Sstevel@tonic-gate 	msg.msg_iov = &iov;
1137c478bd9Sstevel@tonic-gate 	msg.msg_iovlen = 1;
1147c478bd9Sstevel@tonic-gate 	msg.msg_name = (struct sockaddr *)&from;
1157c478bd9Sstevel@tonic-gate 	msg.msg_namelen = sizeof (from);
1167c478bd9Sstevel@tonic-gate 	msg.msg_control = ancillary_data;
1177c478bd9Sstevel@tonic-gate 	msg.msg_controllen = sizeof (ancillary_data);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) {
1207c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "in_data: recvfrom");
1217c478bd9Sstevel@tonic-gate 		return;
1227c478bd9Sstevel@tonic-gate 	}
1237c478bd9Sstevel@tonic-gate 	if (len == 0)
1247c478bd9Sstevel@tonic-gate 		return;
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 	if (inet_ntop(AF_INET6, (void *)&from.sin6_addr,
1277c478bd9Sstevel@tonic-gate 	    abuf, sizeof (abuf)) == NULL)
1287c478bd9Sstevel@tonic-gate 		msgbuf = "Unspecified Router";
1297c478bd9Sstevel@tonic-gate 	else
1307c478bd9Sstevel@tonic-gate 		msgbuf = abuf;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	/* Ignore packets > 64k or control buffers that don't fit */
1337c478bd9Sstevel@tonic-gate 	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
1347c478bd9Sstevel@tonic-gate 		if (debug & D_PKTBAD) {
1357c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x "
1367c478bd9Sstevel@tonic-gate 			    "from %s\n", msg.msg_flags, msgbuf);
1377c478bd9Sstevel@tonic-gate 		}
1387c478bd9Sstevel@tonic-gate 		return;
1397c478bd9Sstevel@tonic-gate 	}
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	icmp = (struct icmp6_hdr *)in_packet;
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	if (len < ICMP6_MINLEN) {
1447c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Too short ICMP packet: %d bytes "
1457c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
1467c478bd9Sstevel@tonic-gate 		    len, msgbuf, pi->pi_name);
1477c478bd9Sstevel@tonic-gate 		return;
1487c478bd9Sstevel@tonic-gate 	}
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_HOPLIMIT);
1517c478bd9Sstevel@tonic-gate 	if (opt == NULL) {
1527c478bd9Sstevel@tonic-gate 		/* Unknown hoplimit - must drop */
1537c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n",
1547c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
1557c478bd9Sstevel@tonic-gate 		return;
1567c478bd9Sstevel@tonic-gate 	}
1577c478bd9Sstevel@tonic-gate 	hoplimit = *(uint_t *)opt;
1587c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_RTHDR);
1597c478bd9Sstevel@tonic-gate 	if (opt != NULL) {
1607c478bd9Sstevel@tonic-gate 		/* Can't allow routing headers in ND messages */
1617c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "ND message with routing header "
1627c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
1637c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
1647c478bd9Sstevel@tonic-gate 		return;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 	switch (icmp->icmp6_type) {
1677c478bd9Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
1687c478bd9Sstevel@tonic-gate 		if (!pi->pi_AdvSendAdvertisements)
1697c478bd9Sstevel@tonic-gate 			return;
1707c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
1717c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
1727c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RS packet "
1737c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
1747c478bd9Sstevel@tonic-gate 				    pi->pi_name);
1757c478bd9Sstevel@tonic-gate 			}
1767c478bd9Sstevel@tonic-gate 			return;
1777c478bd9Sstevel@tonic-gate 		}
1787c478bd9Sstevel@tonic-gate 
1797c478bd9Sstevel@tonic-gate 		/*
1807c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
1817c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
1827c478bd9Sstevel@tonic-gate 		 */
1837c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
1849ba4fd8dSrshoaib 			logmsg(LOG_DEBUG, "RS hop limit: %d from %s on %s\n",
1857c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
1867c478bd9Sstevel@tonic-gate 			return;
1877c478bd9Sstevel@tonic-gate 		}
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
1907c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS code: %d from %s on %s\n",
1917c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
1927c478bd9Sstevel@tonic-gate 			return;
1937c478bd9Sstevel@tonic-gate 		}
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_solicit)) {
1967c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS too short: %d bytes "
1977c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
1987c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
1997c478bd9Sstevel@tonic-gate 			return;
2007c478bd9Sstevel@tonic-gate 		}
2017c478bd9Sstevel@tonic-gate 		rs = (struct nd_router_solicit *)icmp;
2027c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_solicit)) {
2037c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&rs[1],
2047c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_solicit), pi, &from))
2057c478bd9Sstevel@tonic-gate 				return;
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
2087c478bd9Sstevel@tonic-gate 			print_route_sol("Received valid solicit from ", pi,
2097c478bd9Sstevel@tonic-gate 			    rs, len, &from);
2107c478bd9Sstevel@tonic-gate 		}
2117c478bd9Sstevel@tonic-gate 		incoming_rs(pi, rs, len, &from);
2127c478bd9Sstevel@tonic-gate 		break;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
2157c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
2167c478bd9Sstevel@tonic-gate 			/*
2177c478bd9Sstevel@tonic-gate 			 * Router advt. must have address!
2187c478bd9Sstevel@tonic-gate 			 * Logging the news and returning.
2197c478bd9Sstevel@tonic-gate 			 */
2207c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG,
2217c478bd9Sstevel@tonic-gate 			    "Router's address unspecified in advertisement\n");
2227c478bd9Sstevel@tonic-gate 			return;
2237c478bd9Sstevel@tonic-gate 		}
2247c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
2257c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
2267c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RA packet "
2277c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
2287c478bd9Sstevel@tonic-gate 				    pi->pi_name);
2297c478bd9Sstevel@tonic-gate 			}
2307c478bd9Sstevel@tonic-gate 			return;
2317c478bd9Sstevel@tonic-gate 		}
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		/*
2347c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
2357c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
2367c478bd9Sstevel@tonic-gate 		 */
2377c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
2389ba4fd8dSrshoaib 			logmsg(LOG_DEBUG, "RA from %s - not link local on %s\n",
2397c478bd9Sstevel@tonic-gate 			    msgbuf, pi->pi_name);
2407c478bd9Sstevel@tonic-gate 			return;
2417c478bd9Sstevel@tonic-gate 		}
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
2447c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n",
2457c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
2467c478bd9Sstevel@tonic-gate 			return;
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 
2497c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
2507c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA code: %d from %s on %s\n",
2517c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
2527c478bd9Sstevel@tonic-gate 			return;
2537c478bd9Sstevel@tonic-gate 		}
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_advert)) {
2567c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA too short: %d bytes "
2577c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
2587c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
2597c478bd9Sstevel@tonic-gate 			return;
2607c478bd9Sstevel@tonic-gate 		}
2617c478bd9Sstevel@tonic-gate 		ra = (struct nd_router_advert *)icmp;
2627c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_advert)) {
2637c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&ra[1],
2647c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_advert), pi, &from))
2657c478bd9Sstevel@tonic-gate 				return;
2667c478bd9Sstevel@tonic-gate 		}
2677c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
2687c478bd9Sstevel@tonic-gate 			print_route_adv("Received valid advert from ", pi,
2697c478bd9Sstevel@tonic-gate 			    ra, len, &from);
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 		if (pi->pi_AdvSendAdvertisements)
2727c478bd9Sstevel@tonic-gate 			verify_ra_consistency(pi, ra, len, &from);
2737c478bd9Sstevel@tonic-gate 		else
2747c478bd9Sstevel@tonic-gate 			incoming_ra(pi, ra, len, &from, _B_FALSE);
2757c478bd9Sstevel@tonic-gate 		break;
2767c478bd9Sstevel@tonic-gate 	}
2777c478bd9Sstevel@tonic-gate }
2787c478bd9Sstevel@tonic-gate 
2797c478bd9Sstevel@tonic-gate /*
2807c478bd9Sstevel@tonic-gate  * Process a received router solicitation.
2817c478bd9Sstevel@tonic-gate  * Check for source link-layer address option and check if it
2827c478bd9Sstevel@tonic-gate  * is time to advertise.
2837c478bd9Sstevel@tonic-gate  */
2847c478bd9Sstevel@tonic-gate static void
2857c478bd9Sstevel@tonic-gate incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len,
2867c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
2877c478bd9Sstevel@tonic-gate {
2887c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
2897c478bd9Sstevel@tonic-gate 	int optlen;
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 	/* Process any options */
2927c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_solicit);
2937c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&rs[1];
2947c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
2957c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
2967c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
2977c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
2987c478bd9Sstevel@tonic-gate 			incoming_lla_opt(pi, (uchar_t *)opt,
2997c478bd9Sstevel@tonic-gate 			    from, NDF_ISROUTER_OFF);
3007c478bd9Sstevel@tonic-gate 			break;
3017c478bd9Sstevel@tonic-gate 		default:
3027c478bd9Sstevel@tonic-gate 			break;
3037c478bd9Sstevel@tonic-gate 		}
3047c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
3057c478bd9Sstevel@tonic-gate 		len -= optlen;
3067c478bd9Sstevel@tonic-gate 	}
3077c478bd9Sstevel@tonic-gate 	/* Simple algorithm: treat unicast and multicast RSs the same */
3087c478bd9Sstevel@tonic-gate 	check_to_advertise(pi, RECEIVED_SOLICIT);
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate 
311*d04ccbb3Scarlsonj /*
312*d04ccbb3Scarlsonj  * Start up DHCPv6 on a given physical interface.  Does not wait for a message
313*d04ccbb3Scarlsonj  * to be returned from the daemon.
314*d04ccbb3Scarlsonj  */
315*d04ccbb3Scarlsonj void
316*d04ccbb3Scarlsonj start_dhcp(struct phyint *pi)
317*d04ccbb3Scarlsonj {
318*d04ccbb3Scarlsonj 	dhcp_ipc_request_t	*request;
319*d04ccbb3Scarlsonj 	dhcp_ipc_reply_t	*reply	= NULL;
320*d04ccbb3Scarlsonj 	int			error;
321*d04ccbb3Scarlsonj 	int			type;
322*d04ccbb3Scarlsonj 
323*d04ccbb3Scarlsonj 	if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) {
324*d04ccbb3Scarlsonj 		logmsg(LOG_ERR, "start_dhcp: unable to start %s\n",
325*d04ccbb3Scarlsonj 		    DHCP_AGENT_PATH);
326*d04ccbb3Scarlsonj 		/* make sure we try again next time there's a chance */
327*d04ccbb3Scarlsonj 		pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER;
328*d04ccbb3Scarlsonj 		return;
329*d04ccbb3Scarlsonj 	}
330*d04ccbb3Scarlsonj 
331*d04ccbb3Scarlsonj 	type = (pi->pi_ra_flags & ND_RA_FLAG_MANAGED) ? DHCP_START :
332*d04ccbb3Scarlsonj 	    DHCP_INFORM;
333*d04ccbb3Scarlsonj 
334*d04ccbb3Scarlsonj 	request = dhcp_ipc_alloc_request(type | DHCP_V6, pi->pi_name, NULL, 0,
335*d04ccbb3Scarlsonj 	    DHCP_TYPE_NONE);
336*d04ccbb3Scarlsonj 	if (request == NULL) {
337*d04ccbb3Scarlsonj 		logmsg(LOG_ERR, "start_dhcp: out of memory\n");
338*d04ccbb3Scarlsonj 		/* make sure we try again next time there's a chance */
339*d04ccbb3Scarlsonj 		pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER;
340*d04ccbb3Scarlsonj 		return;
341*d04ccbb3Scarlsonj 	}
342*d04ccbb3Scarlsonj 
343*d04ccbb3Scarlsonj 	error = dhcp_ipc_make_request(request, &reply, 0);
344*d04ccbb3Scarlsonj 	free(request);
345*d04ccbb3Scarlsonj 	if (error != 0) {
346*d04ccbb3Scarlsonj 		logmsg(LOG_ERR, "start_dhcp: err: %s: %s\n", pi->pi_name,
347*d04ccbb3Scarlsonj 		    dhcp_ipc_strerror(error));
348*d04ccbb3Scarlsonj 		return;
349*d04ccbb3Scarlsonj 	}
350*d04ccbb3Scarlsonj 
351*d04ccbb3Scarlsonj 	error = reply->return_code;
352*d04ccbb3Scarlsonj 	free(reply);
353*d04ccbb3Scarlsonj 
354*d04ccbb3Scarlsonj 	/*
355*d04ccbb3Scarlsonj 	 * Timeout is considered to be "success" because we don't wait for DHCP
356*d04ccbb3Scarlsonj 	 * to do its exchange.
357*d04ccbb3Scarlsonj 	 */
358*d04ccbb3Scarlsonj 	if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING &&
359*d04ccbb3Scarlsonj 	    error != DHCP_IPC_E_TIMEOUT) {
360*d04ccbb3Scarlsonj 		logmsg(LOG_ERR, "start_dhcp: ret: %s: %s\n", pi->pi_name,
361*d04ccbb3Scarlsonj 		    dhcp_ipc_strerror(error));
362*d04ccbb3Scarlsonj 		return;
363*d04ccbb3Scarlsonj 	}
364*d04ccbb3Scarlsonj }
365*d04ccbb3Scarlsonj 
3667c478bd9Sstevel@tonic-gate /*
3677c478bd9Sstevel@tonic-gate  * Process a received router advertisement.
3687c478bd9Sstevel@tonic-gate  * Called both when packets arrive as well as when we send RAs.
3697c478bd9Sstevel@tonic-gate  * In the latter case 'loopback' is set.
3707c478bd9Sstevel@tonic-gate  */
3717c478bd9Sstevel@tonic-gate void
3727c478bd9Sstevel@tonic-gate incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len,
3737c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
3767c478bd9Sstevel@tonic-gate 	int optlen;
3777c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
3787c478bd9Sstevel@tonic-gate 	boolean_t set_needed = _B_FALSE;
3797c478bd9Sstevel@tonic-gate 	struct router *dr;
3807c478bd9Sstevel@tonic-gate 	uint16_t router_lifetime;
3817c478bd9Sstevel@tonic-gate 	uint_t reachable, retrans;
3827c478bd9Sstevel@tonic-gate 	boolean_t reachable_time_changed = _B_FALSE;
3836b27086dSdd 	boolean_t slla_opt_present	 = _B_FALSE;
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	if (no_loopback && loopback)
3867c478bd9Sstevel@tonic-gate 		return;
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	/*
3897c478bd9Sstevel@tonic-gate 	 * If the interface is FAILED or INACTIVE or OFFLINE, don't
3907c478bd9Sstevel@tonic-gate 	 * create any addresses on them. in.mpathd assumes that no new
3917c478bd9Sstevel@tonic-gate 	 * addresses will appear on these. This implies that we
3927c478bd9Sstevel@tonic-gate 	 * won't create any new prefixes advertised by the router
3937c478bd9Sstevel@tonic-gate 	 * on FAILED/INACTIVE/OFFLINE interfaces. When the state changes,
3947c478bd9Sstevel@tonic-gate 	 * the next RA will create the prefix on this interface.
3957c478bd9Sstevel@tonic-gate 	 */
3967c478bd9Sstevel@tonic-gate 	if (pi->pi_flags & (IFF_FAILED|IFF_INACTIVE|IFF_OFFLINE))
3977c478bd9Sstevel@tonic-gate 		return;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
4007c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
4017c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
4027c478bd9Sstevel@tonic-gate 		if (errno == ENXIO)
4037c478bd9Sstevel@tonic-gate 			return;
4047c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_ra: SIOCGLIFLNKINFO");
4057c478bd9Sstevel@tonic-gate 		return;
4067c478bd9Sstevel@tonic-gate 	}
4070a12cda4Sdd 	if (ra->nd_ra_curhoplimit != CURHOP_UNSPECIFIED &&
4080a12cda4Sdd 	    ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) {
4097c478bd9Sstevel@tonic-gate 		pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
4127c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
4137c478bd9Sstevel@tonic-gate 	}
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	reachable = ntohl(ra->nd_ra_reachable);
4167c478bd9Sstevel@tonic-gate 	if (reachable != 0 &&
4177c478bd9Sstevel@tonic-gate 	    reachable != pi->pi_BaseReachableTime) {
4187c478bd9Sstevel@tonic-gate 		pi->pi_BaseReachableTime = reachable;
4197c478bd9Sstevel@tonic-gate 		reachable_time_changed = _B_TRUE;
4207c478bd9Sstevel@tonic-gate 	}
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
4237c478bd9Sstevel@tonic-gate 	    reachable_time_changed) {
4247c478bd9Sstevel@tonic-gate 		phyint_reach_random(pi, _B_FALSE);
4257c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
4267c478bd9Sstevel@tonic-gate 	}
4277c478bd9Sstevel@tonic-gate 	lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate 	retrans = ntohl(ra->nd_ra_retransmit);
4307c478bd9Sstevel@tonic-gate 	if (retrans != 0 &&
4317c478bd9Sstevel@tonic-gate 	    pi->pi_RetransTimer != retrans) {
4327c478bd9Sstevel@tonic-gate 		pi->pi_RetransTimer = retrans;
4337c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
4347c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
4357c478bd9Sstevel@tonic-gate 	}
4367c478bd9Sstevel@tonic-gate 
4377c478bd9Sstevel@tonic-gate 	if (set_needed) {
4387c478bd9Sstevel@tonic-gate 		if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
4397c478bd9Sstevel@tonic-gate 			logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
4407c478bd9Sstevel@tonic-gate 			return;
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 
444*d04ccbb3Scarlsonj 	/*
445*d04ccbb3Scarlsonj 	 * If the "managed" flag is set, then just assume that the "other" flag
446*d04ccbb3Scarlsonj 	 * is set as well.  It's not legal to get addresses alone without
447*d04ccbb3Scarlsonj 	 * getting other data.
448*d04ccbb3Scarlsonj 	 */
449*d04ccbb3Scarlsonj 	if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
450*d04ccbb3Scarlsonj 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
451*d04ccbb3Scarlsonj 
452*d04ccbb3Scarlsonj 	/*
453*d04ccbb3Scarlsonj 	 * If either the "managed" or "other" bits have turned on, then it's
454*d04ccbb3Scarlsonj 	 * now time to invoke DHCP.  If only the "other" bit is set, then don't
455*d04ccbb3Scarlsonj 	 * get addresses via DHCP; only "other" data.  If "managed" is set,
456*d04ccbb3Scarlsonj 	 * then we must always get both addresses and "other" data.
457*d04ccbb3Scarlsonj 	 */
458*d04ccbb3Scarlsonj 	if (pi->pi_StatefulAddrConf &&
459*d04ccbb3Scarlsonj 	    (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags &
460*d04ccbb3Scarlsonj 	    (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) {
461*d04ccbb3Scarlsonj 		if (debug & D_DHCP) {
462*d04ccbb3Scarlsonj 			logmsg(LOG_DEBUG,
463*d04ccbb3Scarlsonj 			    "incoming_ra: trigger dhcp %s on %s\n",
464*d04ccbb3Scarlsonj 			    (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags &
465*d04ccbb3Scarlsonj 				ND_RA_FLAG_MANAGED) ? "MANAGED" : "OTHER",
466*d04ccbb3Scarlsonj 			    pi->pi_name);
4677c478bd9Sstevel@tonic-gate 		}
468*d04ccbb3Scarlsonj 		pi->pi_ra_flags |= ra->nd_ra_flags_reserved;
469*d04ccbb3Scarlsonj 		start_dhcp(pi);
4707c478bd9Sstevel@tonic-gate 	}
471*d04ccbb3Scarlsonj 
4727c478bd9Sstevel@tonic-gate 	/* Skip default router code if sent from ourselves */
4737c478bd9Sstevel@tonic-gate 	if (!loopback) {
4747c478bd9Sstevel@tonic-gate 		/* Find and update or add default router in list */
4757c478bd9Sstevel@tonic-gate 		dr = router_lookup(pi, from->sin6_addr);
4767c478bd9Sstevel@tonic-gate 		router_lifetime = ntohs(ra->nd_ra_router_lifetime);
4777c478bd9Sstevel@tonic-gate 		if (dr == NULL) {
4787c478bd9Sstevel@tonic-gate 			if (router_lifetime != 0) {
4797c478bd9Sstevel@tonic-gate 				dr = router_create(pi, from->sin6_addr,
4807c478bd9Sstevel@tonic-gate 				    MILLISEC * router_lifetime);
4817c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
4827c478bd9Sstevel@tonic-gate 			}
4837c478bd9Sstevel@tonic-gate 		} else {
4847c478bd9Sstevel@tonic-gate 			dr->dr_lifetime = MILLISEC * router_lifetime;
4857c478bd9Sstevel@tonic-gate 			if (dr->dr_lifetime != 0)
4867c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
4877c478bd9Sstevel@tonic-gate 			if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
4887c478bd9Sstevel@tonic-gate 			    (dr->dr_lifetime == 0 && dr->dr_inkernel))
4897c478bd9Sstevel@tonic-gate 				router_update_k(dr);
4907c478bd9Sstevel@tonic-gate 		}
4917c478bd9Sstevel@tonic-gate 	}
4927c478bd9Sstevel@tonic-gate 	/* Process any options */
4937c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_advert);
4947c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&ra[1];
4957c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
4967c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
4977c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
4987c478bd9Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION:
4997c478bd9Sstevel@tonic-gate 			incoming_prefix_opt(pi, (uchar_t *)opt, from,
5007c478bd9Sstevel@tonic-gate 			    loopback);
5017c478bd9Sstevel@tonic-gate 			break;
5027c478bd9Sstevel@tonic-gate 		case ND_OPT_MTU:
5037c478bd9Sstevel@tonic-gate 			incoming_mtu_opt(pi, (uchar_t *)opt, from);
5047c478bd9Sstevel@tonic-gate 			break;
5057c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
5067c478bd9Sstevel@tonic-gate 			/* skip lla option if sent from ourselves! */
5077c478bd9Sstevel@tonic-gate 			if (!loopback) {
5087c478bd9Sstevel@tonic-gate 				incoming_lla_opt(pi, (uchar_t *)opt,
5097c478bd9Sstevel@tonic-gate 				    from, NDF_ISROUTER_ON);
5106b27086dSdd 				slla_opt_present = _B_TRUE;
5117c478bd9Sstevel@tonic-gate 			}
5127c478bd9Sstevel@tonic-gate 			break;
5137c478bd9Sstevel@tonic-gate 		default:
5147c478bd9Sstevel@tonic-gate 			break;
5157c478bd9Sstevel@tonic-gate 		}
5167c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
5177c478bd9Sstevel@tonic-gate 		len -= optlen;
5187c478bd9Sstevel@tonic-gate 	}
519b0f490f4Smh 	if (!loopback && !slla_opt_present)
5206b27086dSdd 		update_ra_flag(pi, from, NDF_ISROUTER_ON);
5217c478bd9Sstevel@tonic-gate 	/* Stop sending solicitations */
5227c478bd9Sstevel@tonic-gate 	check_to_solicit(pi, SOLICIT_DONE);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate /*
5267c478bd9Sstevel@tonic-gate  * Process a received prefix option.
5277c478bd9Sstevel@tonic-gate  * Unless addrconf is turned off we process both the addrconf and the
5287c478bd9Sstevel@tonic-gate  * onlink aspects of the prefix option.
5297c478bd9Sstevel@tonic-gate  *
5307c478bd9Sstevel@tonic-gate  * Note that when a flag (onlink or auto) is turned off we do nothing -
5317c478bd9Sstevel@tonic-gate  * the prefix will time out.
5327c478bd9Sstevel@tonic-gate  */
5337c478bd9Sstevel@tonic-gate static void
5347c478bd9Sstevel@tonic-gate incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
5357c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
5367c478bd9Sstevel@tonic-gate {
5377c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
5387c478bd9Sstevel@tonic-gate 	boolean_t	good_prefix = _B_TRUE;
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
5417c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
5447c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
5457c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
5467c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
5477c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
5487c478bd9Sstevel@tonic-gate 		    8 * (int)po->nd_opt_pi_len);
5497c478bd9Sstevel@tonic-gate 		return;
5507c478bd9Sstevel@tonic-gate 	}
5517c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
5527c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
5557c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
5567c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
5577c478bd9Sstevel@tonic-gate 		    "- ignored\n",
5587c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name);
5597c478bd9Sstevel@tonic-gate 		return;
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
5627c478bd9Sstevel@tonic-gate 	    pi->pi_StatelessAddrConf) {
5637c478bd9Sstevel@tonic-gate 		good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
5647c478bd9Sstevel@tonic-gate 	}
5657c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
5667c478bd9Sstevel@tonic-gate 	    good_prefix) {
567*d04ccbb3Scarlsonj 		incoming_prefix_onlink(pi, opt);
5687c478bd9Sstevel@tonic-gate 	}
569*d04ccbb3Scarlsonj 	if (pi->pi_StatefulAddrConf)
570*d04ccbb3Scarlsonj 		incoming_prefix_stateful(pi, opt);
5717c478bd9Sstevel@tonic-gate }
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate /*
5747c478bd9Sstevel@tonic-gate  * Process prefix options with the onlink flag set.
5757c478bd9Sstevel@tonic-gate  *
5767c478bd9Sstevel@tonic-gate  * If there are no routers ndpd will add an onlink
5777c478bd9Sstevel@tonic-gate  * default route which will allow communication
5787c478bd9Sstevel@tonic-gate  * between neighbors.
5797c478bd9Sstevel@tonic-gate  *
5807c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
5817c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
5827c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
5837c478bd9Sstevel@tonic-gate  */
5847c478bd9Sstevel@tonic-gate static void
585*d04ccbb3Scarlsonj incoming_prefix_onlink(struct phyint *pi, uchar_t *opt)
5867c478bd9Sstevel@tonic-gate {
5877c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
5887c478bd9Sstevel@tonic-gate 	int plen;
5897c478bd9Sstevel@tonic-gate 	struct prefix *pr;
5907c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
5917c478bd9Sstevel@tonic-gate 	boolean_t found_one = _B_FALSE;
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
5947c478bd9Sstevel@tonic-gate 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
5957c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len == plen &&
5967c478bd9Sstevel@tonic-gate 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
5977c478bd9Sstevel@tonic-gate 			/* Exclude static prefixes */
5987c478bd9Sstevel@tonic-gate 			if (pr->pr_state & PR_STATIC)
5997c478bd9Sstevel@tonic-gate 				continue;
6007c478bd9Sstevel@tonic-gate 			found_one = _B_TRUE;
6017c478bd9Sstevel@tonic-gate 			incoming_prefix_onlink_process(pr, opt);
6027c478bd9Sstevel@tonic-gate 		}
6037c478bd9Sstevel@tonic-gate 	}
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
6067c478bd9Sstevel@tonic-gate 	/*
6077c478bd9Sstevel@tonic-gate 	 * If we have found a matching prefix already or validtime
6087c478bd9Sstevel@tonic-gate 	 * is zero, we have nothing to do.
6097c478bd9Sstevel@tonic-gate 	 */
6107c478bd9Sstevel@tonic-gate 	if (validtime == 0 || found_one)
6117c478bd9Sstevel@tonic-gate 		return;
6127c478bd9Sstevel@tonic-gate 	pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
6137c478bd9Sstevel@tonic-gate 	if (pr == NULL)
6147c478bd9Sstevel@tonic-gate 		return;
6157c478bd9Sstevel@tonic-gate 	incoming_prefix_onlink_process(pr, opt);
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate 
6187c478bd9Sstevel@tonic-gate void
6197c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
6207c478bd9Sstevel@tonic-gate {
6217c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
6227c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
6237c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
6267c478bd9Sstevel@tonic-gate 	if (validtime != 0)
6277c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_ONLINK;
6287c478bd9Sstevel@tonic-gate 	else
6297c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~PR_ONLINK;
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	/*
6327c478bd9Sstevel@tonic-gate 	 * Convert from seconds to milliseconds avoiding overflow.
6337c478bd9Sstevel@tonic-gate 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
6347c478bd9Sstevel@tonic-gate 	 * (4 billion seconds - about 130 years) we will in fact time
6357c478bd9Sstevel@tonic-gate 	 * out the prefix after 4 billion milliseconds - 46 days).
6367c478bd9Sstevel@tonic-gate 	 * Thus the longest lifetime (apart from infinity) is 46 days.
6377c478bd9Sstevel@tonic-gate 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
6387c478bd9Sstevel@tonic-gate 	 */
6397c478bd9Sstevel@tonic-gate 	if (pr->pr_flags & IFF_TEMPORARY) {
6407c478bd9Sstevel@tonic-gate 		pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
6417c478bd9Sstevel@tonic-gate 	} else {
6427c478bd9Sstevel@tonic-gate 		if (validtime >= PREFIX_INFINITY / MILLISEC)
6437c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
6447c478bd9Sstevel@tonic-gate 		else
6457c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = validtime * MILLISEC;
6467c478bd9Sstevel@tonic-gate 	}
6477c478bd9Sstevel@tonic-gate 	pr->pr_OnLinkFlag = _B_TRUE;
6487c478bd9Sstevel@tonic-gate 	if (debug & (D_PREFIX|D_TMP)) {
6497c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
6507c478bd9Sstevel@tonic-gate 		    "onlink %u state 0x%x, kstate 0x%x\n",
6517c478bd9Sstevel@tonic-gate 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
6527c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
6537c478bd9Sstevel@tonic-gate 		    pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
6547c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != pr->pr_state) {
6577c478bd9Sstevel@tonic-gate 		prefix_update_k(pr);
6587c478bd9Sstevel@tonic-gate 	}
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate 	if (pr->pr_OnLinkLifetime != 0)
6617c478bd9Sstevel@tonic-gate 		timer_schedule(pr->pr_OnLinkLifetime);
6627c478bd9Sstevel@tonic-gate }
6637c478bd9Sstevel@tonic-gate 
664*d04ccbb3Scarlsonj /*
665*d04ccbb3Scarlsonj  * Process all prefix options by locating the DHCPv6-configured interfaces, and
666*d04ccbb3Scarlsonj  * applying the netmasks as needed.
667*d04ccbb3Scarlsonj  */
668*d04ccbb3Scarlsonj static void
669*d04ccbb3Scarlsonj incoming_prefix_stateful(struct phyint *pi, uchar_t *opt)
670*d04ccbb3Scarlsonj {
671*d04ccbb3Scarlsonj 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
672*d04ccbb3Scarlsonj 	struct prefix *pr;
673*d04ccbb3Scarlsonj 	boolean_t foundpref;
674*d04ccbb3Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
675*d04ccbb3Scarlsonj 
676*d04ccbb3Scarlsonj 	/* Make sure it's a valid prefix. */
677*d04ccbb3Scarlsonj 	if (ntohl(po->nd_opt_pi_valid_time) == 0) {
678*d04ccbb3Scarlsonj 		if (debug & D_DHCP)
679*d04ccbb3Scarlsonj 			logmsg(LOG_DEBUG, "incoming_prefix_stateful: ignoring "
680*d04ccbb3Scarlsonj 			    "prefix with no valid time\n");
681*d04ccbb3Scarlsonj 		return;
682*d04ccbb3Scarlsonj 	}
683*d04ccbb3Scarlsonj 
684*d04ccbb3Scarlsonj 	if (debug & D_DHCP)
685*d04ccbb3Scarlsonj 		logmsg(LOG_DEBUG, "incoming_prefix_stateful(%s, %s/%d)\n",
686*d04ccbb3Scarlsonj 		    pi->pi_name, inet_ntop(AF_INET6,
687*d04ccbb3Scarlsonj 		    (void *)&po->nd_opt_pi_prefix, abuf, sizeof (abuf)),
688*d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len);
689*d04ccbb3Scarlsonj 	foundpref = _B_FALSE;
690*d04ccbb3Scarlsonj 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
691*d04ccbb3Scarlsonj 		if (prefix_equal(po->nd_opt_pi_prefix, pr->pr_address,
692*d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len)) {
693*d04ccbb3Scarlsonj 			if ((pr->pr_flags & IFF_DHCPRUNNING) &&
694*d04ccbb3Scarlsonj 			    pr->pr_prefix_len != po->nd_opt_pi_prefix_len) {
695*d04ccbb3Scarlsonj 				pr->pr_prefix_len = po->nd_opt_pi_prefix_len;
696*d04ccbb3Scarlsonj 				if (pr->pr_flags & IFF_UP) {
697*d04ccbb3Scarlsonj 					if (debug & D_DHCP)
698*d04ccbb3Scarlsonj 						logmsg(LOG_DEBUG,
699*d04ccbb3Scarlsonj 						    "incoming_prefix_stateful:"
700*d04ccbb3Scarlsonj 						    " set mask on DHCP %s\n",
701*d04ccbb3Scarlsonj 						    pr->pr_name);
702*d04ccbb3Scarlsonj 					prefix_update_dhcp(pr);
703*d04ccbb3Scarlsonj 				}
704*d04ccbb3Scarlsonj 			}
705*d04ccbb3Scarlsonj 			if (pr->pr_prefix_len == po->nd_opt_pi_prefix_len &&
706*d04ccbb3Scarlsonj 			    (!(pr->pr_state & PR_STATIC) ||
707*d04ccbb3Scarlsonj 			    (pr->pr_flags & IFF_DHCPRUNNING)))
708*d04ccbb3Scarlsonj 				foundpref = _B_TRUE;
709*d04ccbb3Scarlsonj 		}
710*d04ccbb3Scarlsonj 	}
711*d04ccbb3Scarlsonj 	/*
712*d04ccbb3Scarlsonj 	 * If there's no matching DHCPv6 prefix present, then create an empty
713*d04ccbb3Scarlsonj 	 * one so that we'll be able to configure it later.
714*d04ccbb3Scarlsonj 	 */
715*d04ccbb3Scarlsonj 	if (!foundpref) {
716*d04ccbb3Scarlsonj 		pr = prefix_create(pi, po->nd_opt_pi_prefix,
717*d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len, IFF_DHCPRUNNING);
718*d04ccbb3Scarlsonj 		if (pr != NULL) {
719*d04ccbb3Scarlsonj 			pr->pr_state = PR_STATIC;
720*d04ccbb3Scarlsonj 			if (debug & D_DHCP)
721*d04ccbb3Scarlsonj 				logmsg(LOG_DEBUG,
722*d04ccbb3Scarlsonj 				    "incoming_prefix_stateful: created dummy "
723*d04ccbb3Scarlsonj 				    "prefix for later\n");
724*d04ccbb3Scarlsonj 		}
725*d04ccbb3Scarlsonj 	}
726*d04ccbb3Scarlsonj }
727*d04ccbb3Scarlsonj 
7287c478bd9Sstevel@tonic-gate /*
7297c478bd9Sstevel@tonic-gate  * Process prefix options with the autonomous flag set.
7307c478bd9Sstevel@tonic-gate  * Returns false if this prefix results in a bad address (duplicate)
7317c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
7327c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
7337c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
7347c478bd9Sstevel@tonic-gate  */
7357c478bd9Sstevel@tonic-gate static boolean_t
7367c478bd9Sstevel@tonic-gate incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
7377c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
7387c478bd9Sstevel@tonic-gate {
7397c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
7407c478bd9Sstevel@tonic-gate 	int plen;
7417c478bd9Sstevel@tonic-gate 	struct prefix *pr;
7427c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;	/* In seconds */
7437c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
7447c478bd9Sstevel@tonic-gate 	char pbuf[INET6_ADDRSTRLEN];
7457c478bd9Sstevel@tonic-gate 	boolean_t found_pub = _B_FALSE;
7467c478bd9Sstevel@tonic-gate 	boolean_t found_tmp = _B_FALSE;
7477c478bd9Sstevel@tonic-gate 	boolean_t ret;
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
7507c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
7517c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
7527c478bd9Sstevel@tonic-gate 
7537c478bd9Sstevel@tonic-gate 	/* Sanity checks */
7547c478bd9Sstevel@tonic-gate 	if (validtime < preftime) {
7557c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
7567c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
7577c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6,
7587c478bd9Sstevel@tonic-gate 		    (void *)&po->nd_opt_pi_prefix,
7597c478bd9Sstevel@tonic-gate 		    pbuf, sizeof (pbuf));
7607c478bd9Sstevel@tonic-gate 		logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
7617c478bd9Sstevel@tonic-gate 		    "valid %u < pref %u ignored\n",
7627c478bd9Sstevel@tonic-gate 		    pbuf, plen, abuf, pi->pi_name,
7637c478bd9Sstevel@tonic-gate 		    validtime, preftime);
7647c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
7657c478bd9Sstevel@tonic-gate 	}
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
7687c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len == plen &&
7697c478bd9Sstevel@tonic-gate 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
7707c478bd9Sstevel@tonic-gate 
771*d04ccbb3Scarlsonj 			/* Exclude static prefixes and DHCP */
772*d04ccbb3Scarlsonj 			if ((pr->pr_state & PR_STATIC) ||
773*d04ccbb3Scarlsonj 			    (pr->pr_flags & IFF_DHCPRUNNING))
7747c478bd9Sstevel@tonic-gate 				continue;
7757c478bd9Sstevel@tonic-gate 			if (pr->pr_flags & IFF_TEMPORARY) {
7767c478bd9Sstevel@tonic-gate 				/*
7777c478bd9Sstevel@tonic-gate 				 * If this address is deprecated and its token
7787c478bd9Sstevel@tonic-gate 				 * doesn't match the current tmp token, we want
7797c478bd9Sstevel@tonic-gate 				 * to create a new address with the current
7807c478bd9Sstevel@tonic-gate 				 * token.  So don't count this addr as a match.
7817c478bd9Sstevel@tonic-gate 				 */
7827c478bd9Sstevel@tonic-gate 				if (!((pr->pr_flags & IFF_DEPRECATED) &&
7837c478bd9Sstevel@tonic-gate 				    !token_equal(pi->pi_tmp_token,
7847c478bd9Sstevel@tonic-gate 				    pr->pr_address, TMP_TOKEN_BITS)))
7857c478bd9Sstevel@tonic-gate 					found_tmp = _B_TRUE;
7867c478bd9Sstevel@tonic-gate 			} else {
7877c478bd9Sstevel@tonic-gate 				found_pub = _B_TRUE;
7887c478bd9Sstevel@tonic-gate 			}
7897c478bd9Sstevel@tonic-gate 			(void) incoming_prefix_addrconf_process(pi, pr, opt,
7907c478bd9Sstevel@tonic-gate 			    from, loopback, _B_FALSE);
7917c478bd9Sstevel@tonic-gate 		}
7927c478bd9Sstevel@tonic-gate 	}
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	/*
7957c478bd9Sstevel@tonic-gate 	 * If we have found a matching prefix (for public and, if temp addrs
7967c478bd9Sstevel@tonic-gate 	 * are enabled, for temporary) already or validtime is zero, we have
7977c478bd9Sstevel@tonic-gate 	 * nothing to do.
7987c478bd9Sstevel@tonic-gate 	 */
7997c478bd9Sstevel@tonic-gate 	if (validtime == 0 ||
8007c478bd9Sstevel@tonic-gate 	    (found_pub && (!pi->pi_TmpAddrsEnabled || found_tmp)))
8017c478bd9Sstevel@tonic-gate 		return (_B_TRUE);
8027c478bd9Sstevel@tonic-gate 
8037c478bd9Sstevel@tonic-gate 	if (!found_pub) {
8047c478bd9Sstevel@tonic-gate 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
8057c478bd9Sstevel@tonic-gate 		if (pr == NULL)
8067c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
8077c478bd9Sstevel@tonic-gate 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
8087c478bd9Sstevel@tonic-gate 		    loopback, _B_TRUE);
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 	/*
8117c478bd9Sstevel@tonic-gate 	 * if processing of the public address failed,
8127c478bd9Sstevel@tonic-gate 	 * don't bother with the temporary address.
8137c478bd9Sstevel@tonic-gate 	 */
8147c478bd9Sstevel@tonic-gate 	if (ret == _B_FALSE)
8157c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	if (pi->pi_TmpAddrsEnabled && !found_tmp) {
8187c478bd9Sstevel@tonic-gate 		pr = prefix_create(pi, po->nd_opt_pi_prefix, plen,
8197c478bd9Sstevel@tonic-gate 		    IFF_TEMPORARY);
8207c478bd9Sstevel@tonic-gate 		if (pr == NULL)
8217c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
8227c478bd9Sstevel@tonic-gate 		ret = incoming_prefix_addrconf_process(pi, pr, opt, from,
8237c478bd9Sstevel@tonic-gate 		    loopback, _B_TRUE);
8247c478bd9Sstevel@tonic-gate 	}
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	return (ret);
8277c478bd9Sstevel@tonic-gate }
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate boolean_t
8307c478bd9Sstevel@tonic-gate incoming_prefix_addrconf_process(struct phyint *pi, struct prefix *pr,
8317c478bd9Sstevel@tonic-gate     uchar_t *opt, struct sockaddr_in6 *from, boolean_t loopback,
8327c478bd9Sstevel@tonic-gate     boolean_t new_prefix)
8337c478bd9Sstevel@tonic-gate {
8347c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
8357c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
8367c478bd9Sstevel@tonic-gate 	char pbuf[INET6_ADDRSTRLEN];
8377c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;	/* In seconds */
8387c478bd9Sstevel@tonic-gate 	uint32_t recorded_validtime;	/* In seconds */
83969bb4bb4Scarlsonj 	int plen;
8407c478bd9Sstevel@tonic-gate 	struct prefix *other_pr;
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
8437c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
8447c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
8457c478bd9Sstevel@tonic-gate 	if (!new_prefix) {
8467c478bd9Sstevel@tonic-gate 		/*
8477c478bd9Sstevel@tonic-gate 		 * Check 2 hour rule on valid lifetime.
8487c478bd9Sstevel@tonic-gate 		 * Follows: RFC 2462
8497c478bd9Sstevel@tonic-gate 		 * If we advertised this prefix ourselves we skip
8507c478bd9Sstevel@tonic-gate 		 * these checks. They are also skipped if we did not
8517c478bd9Sstevel@tonic-gate 		 * previously do addrconf on this prefix.
8527c478bd9Sstevel@tonic-gate 		 */
8537c478bd9Sstevel@tonic-gate 		recorded_validtime = pr->pr_ValidLifetime / MILLISEC;
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 		if (loopback || !(pr->pr_state & PR_AUTO) ||
8567c478bd9Sstevel@tonic-gate 		    validtime >= MIN_VALID_LIFETIME ||
8577c478bd9Sstevel@tonic-gate 		    /* LINTED - statement has no consequent */
8587c478bd9Sstevel@tonic-gate 		    validtime >= recorded_validtime) {
8597c478bd9Sstevel@tonic-gate 			/* OK */
8607c478bd9Sstevel@tonic-gate 		} else if (recorded_validtime < MIN_VALID_LIFETIME &&
8617c478bd9Sstevel@tonic-gate 		    validtime < recorded_validtime) {
8627c478bd9Sstevel@tonic-gate 			/* Ignore the prefix */
8637c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
8647c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
8657c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
8667c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
8677c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
8687c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
8697c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
8707c478bd9Sstevel@tonic-gate 			    "too short valid lifetime %u stored %u "
8717c478bd9Sstevel@tonic-gate 			    "- ignored\n",
8727c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
8737c478bd9Sstevel@tonic-gate 			    validtime, recorded_validtime);
8747c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
8757c478bd9Sstevel@tonic-gate 		} else {
8767c478bd9Sstevel@tonic-gate 			/*
8777c478bd9Sstevel@tonic-gate 			 * If the router clock runs slower than the
8787c478bd9Sstevel@tonic-gate 			 * host by 1 second over 2 hours then this
8797c478bd9Sstevel@tonic-gate 			 * test will set the lifetime back to 2 hours
8807c478bd9Sstevel@tonic-gate 			 * once i.e. a lifetime decrementing in
8817c478bd9Sstevel@tonic-gate 			 * realtime might cause the prefix to live an
8827c478bd9Sstevel@tonic-gate 			 * extra 2 hours on the host.
8837c478bd9Sstevel@tonic-gate 			 */
8847c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
8857c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
8867c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
8877c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
8887c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
8897c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
8907c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
8917c478bd9Sstevel@tonic-gate 			    "valid time %u stored %u rounded up "
8927c478bd9Sstevel@tonic-gate 			    "to %u\n",
8937c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
8947c478bd9Sstevel@tonic-gate 			    validtime, recorded_validtime,
8957c478bd9Sstevel@tonic-gate 			    MIN_VALID_LIFETIME);
8967c478bd9Sstevel@tonic-gate 			validtime = MIN_VALID_LIFETIME;
8977c478bd9Sstevel@tonic-gate 		}
8987c478bd9Sstevel@tonic-gate 	}
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	/*
9017c478bd9Sstevel@tonic-gate 	 * For RFC3041 addresses, need to take token lifetime
9027c478bd9Sstevel@tonic-gate 	 * into account, too.
9037c478bd9Sstevel@tonic-gate 	 */
9047c478bd9Sstevel@tonic-gate 	if (pr->pr_flags & IFF_TEMPORARY) {
9057c478bd9Sstevel@tonic-gate 		uint_t	cur_tpreftime =
9067c478bd9Sstevel@tonic-gate 		    pi->pi_TmpPreferredLifetime - pi->pi_TmpDesyncFactor;
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate 		if (new_prefix) {
9097c478bd9Sstevel@tonic-gate 			validtime = MIN(validtime, pi->pi_TmpValidLifetime);
9107c478bd9Sstevel@tonic-gate 			preftime = MIN(preftime, cur_tpreftime);
9117c478bd9Sstevel@tonic-gate 		} else {
9127c478bd9Sstevel@tonic-gate 			uint_t cur_vexp, cur_pexp, curtime;
9137c478bd9Sstevel@tonic-gate 			curtime = getcurrenttime() / MILLISEC;
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 			cur_vexp = pr->pr_CreateTime + pi->pi_TmpValidLifetime;
9167c478bd9Sstevel@tonic-gate 			cur_pexp = pr->pr_CreateTime + cur_tpreftime;
9177c478bd9Sstevel@tonic-gate 			if (curtime > cur_vexp)
9187c478bd9Sstevel@tonic-gate 				validtime = 0;
9197c478bd9Sstevel@tonic-gate 			else if ((curtime + validtime) > cur_vexp)
9207c478bd9Sstevel@tonic-gate 				validtime = cur_vexp - curtime;
9217c478bd9Sstevel@tonic-gate 			/*
9227c478bd9Sstevel@tonic-gate 			 * If this is an existing address which was deprecated
9237c478bd9Sstevel@tonic-gate 			 * because of a bad token, we don't want to update its
9247c478bd9Sstevel@tonic-gate 			 * preferred lifetime!
9257c478bd9Sstevel@tonic-gate 			 */
9267c478bd9Sstevel@tonic-gate 			if ((pr->pr_PreferredLifetime == 0) &&
9277c478bd9Sstevel@tonic-gate 			    !token_equal(pr->pr_address, pi->pi_tmp_token,
9287c478bd9Sstevel@tonic-gate 			    TMP_TOKEN_BITS))
9297c478bd9Sstevel@tonic-gate 				preftime = 0;
9307c478bd9Sstevel@tonic-gate 			else if (curtime > cur_pexp)
9317c478bd9Sstevel@tonic-gate 				preftime = 0;
9327c478bd9Sstevel@tonic-gate 			else if ((curtime + preftime) > cur_pexp)
9337c478bd9Sstevel@tonic-gate 				preftime = cur_pexp - curtime;
9347c478bd9Sstevel@tonic-gate 		}
9357c478bd9Sstevel@tonic-gate 		if ((preftime != 0) && (preftime <= pi->pi_TmpRegenAdvance)) {
9367c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
9377c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
9387c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
9397c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
9407c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
9417c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
9427c478bd9Sstevel@tonic-gate 			logmsg(LOG_WARNING, "prefix opt %s/%u from %s on %s: "
9437c478bd9Sstevel@tonic-gate 			    "preferred lifetime(%d) <= TmpRegenAdvance(%d)\n",
9447c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name, preftime,
9457c478bd9Sstevel@tonic-gate 			    pi->pi_TmpRegenAdvance);
9467c478bd9Sstevel@tonic-gate 			if (new_prefix)
9477c478bd9Sstevel@tonic-gate 				prefix_delete(pr);
9487c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
9497c478bd9Sstevel@tonic-gate 		}
9507c478bd9Sstevel@tonic-gate 	}
9517c478bd9Sstevel@tonic-gate 	if (debug & D_TMP)
9527c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "calculated lifetimes(%s, 0x%llx): v %d, "
9537c478bd9Sstevel@tonic-gate 		    "p %d\n", pr->pr_name, pr->pr_flags, validtime, preftime);
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	if (!(pr->pr_state & PR_AUTO)) {
9567c478bd9Sstevel@tonic-gate 		int i, tokenlen;
9577c478bd9Sstevel@tonic-gate 		in6_addr_t *token;
9587c478bd9Sstevel@tonic-gate 		/*
9597c478bd9Sstevel@tonic-gate 		 * Form a new local address if the lengths match.
9607c478bd9Sstevel@tonic-gate 		 */
9617c478bd9Sstevel@tonic-gate 		if (pr->pr_flags && IFF_TEMPORARY) {
9627c478bd9Sstevel@tonic-gate 			if (IN6_IS_ADDR_UNSPECIFIED(&pi->pi_tmp_token)) {
9637c478bd9Sstevel@tonic-gate 				if (!tmptoken_create(pi)) {
9647c478bd9Sstevel@tonic-gate 					prefix_delete(pr);
9657c478bd9Sstevel@tonic-gate 					return (_B_TRUE);
9667c478bd9Sstevel@tonic-gate 				}
9677c478bd9Sstevel@tonic-gate 			}
9687c478bd9Sstevel@tonic-gate 			tokenlen = TMP_TOKEN_BITS;
9697c478bd9Sstevel@tonic-gate 			token = &pi->pi_tmp_token;
9707c478bd9Sstevel@tonic-gate 		} else {
9717c478bd9Sstevel@tonic-gate 			tokenlen = pi->pi_token_length;
9727c478bd9Sstevel@tonic-gate 			token = &pi->pi_token;
9737c478bd9Sstevel@tonic-gate 		}
9747c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len + tokenlen != IPV6_ABITS) {
9757c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
9767c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
9777c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
9787c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
9797c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix,
9807c478bd9Sstevel@tonic-gate 			    pbuf, sizeof (pbuf));
9817c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "prefix option %s/%u from %s on %s: "
9827c478bd9Sstevel@tonic-gate 			    "mismatched length %d token length %d\n",
9837c478bd9Sstevel@tonic-gate 			    pbuf, plen, abuf, pi->pi_name,
9847c478bd9Sstevel@tonic-gate 			    pr->pr_prefix_len, tokenlen);
9857c478bd9Sstevel@tonic-gate 			return (_B_TRUE);
9867c478bd9Sstevel@tonic-gate 		}
9877c478bd9Sstevel@tonic-gate 		for (i = 0; i < 16; i++) {
9887c478bd9Sstevel@tonic-gate 			/*
9897c478bd9Sstevel@tonic-gate 			 * prefix_create ensures that pr_prefix has all-zero
9907c478bd9Sstevel@tonic-gate 			 * bits after prefixlen.
9917c478bd9Sstevel@tonic-gate 			 */
9927c478bd9Sstevel@tonic-gate 			pr->pr_address.s6_addr[i] = pr->pr_prefix.s6_addr[i] |
9937c478bd9Sstevel@tonic-gate 			    token->s6_addr[i];
9947c478bd9Sstevel@tonic-gate 		}
9957c478bd9Sstevel@tonic-gate 		/*
9967c478bd9Sstevel@tonic-gate 		 * Check if any other physical interface has the same
9977c478bd9Sstevel@tonic-gate 		 * address configured already
9987c478bd9Sstevel@tonic-gate 		 */
9997c478bd9Sstevel@tonic-gate 		if ((other_pr = prefix_lookup_addr_match(pr)) != NULL) {
10007c478bd9Sstevel@tonic-gate 			/*
10017c478bd9Sstevel@tonic-gate 			 * Delete this prefix structure as kernel
10027c478bd9Sstevel@tonic-gate 			 * does not allow duplicated addresses
10037c478bd9Sstevel@tonic-gate 			 */
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
10067c478bd9Sstevel@tonic-gate 			    "Duplicate prefix  %s received on interface %s\n",
10077c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6,
10087c478bd9Sstevel@tonic-gate 			    (void *)&po->nd_opt_pi_prefix, abuf,
10097c478bd9Sstevel@tonic-gate 			    sizeof (abuf)), pi->pi_name);
10107c478bd9Sstevel@tonic-gate 			logmsg(LOG_ERR, "incoming_prefix_addrconf_process: "
10117c478bd9Sstevel@tonic-gate 			    "Prefix already exists in interface %s\n",
10127c478bd9Sstevel@tonic-gate 			    other_pr->pr_physical->pi_name);
10137c478bd9Sstevel@tonic-gate 			if (new_prefix) {
10147c478bd9Sstevel@tonic-gate 				prefix_delete(pr);
10157c478bd9Sstevel@tonic-gate 				return (_B_FALSE);
10167c478bd9Sstevel@tonic-gate 			}
10177c478bd9Sstevel@tonic-gate 			/* Ignore for addrconf purposes */
10187c478bd9Sstevel@tonic-gate 			validtime = preftime = 0;
10197c478bd9Sstevel@tonic-gate 		}
10207c478bd9Sstevel@tonic-gate 		if ((pr->pr_flags & IFF_TEMPORARY) && new_prefix) {
10217c478bd9Sstevel@tonic-gate 			pr->pr_CreateTime = getcurrenttime() / MILLISEC;
10227c478bd9Sstevel@tonic-gate 			if (debug & D_TMP)
10237c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG,
10247c478bd9Sstevel@tonic-gate 				    "created tmp addr(%s v %d p %d)\n",
10257c478bd9Sstevel@tonic-gate 				    pr->pr_name, validtime, preftime);
10267c478bd9Sstevel@tonic-gate 		}
10277c478bd9Sstevel@tonic-gate 	}
10287c478bd9Sstevel@tonic-gate 
10297c478bd9Sstevel@tonic-gate 	if (validtime != 0)
10307c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_AUTO;
10317c478bd9Sstevel@tonic-gate 	else
10327c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~(PR_AUTO|PR_DEPRECATED);
10337c478bd9Sstevel@tonic-gate 	if (preftime != 0 || !(pr->pr_state & PR_AUTO))
10347c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~PR_DEPRECATED;
10357c478bd9Sstevel@tonic-gate 	else
10367c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_DEPRECATED;
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	/*
10397c478bd9Sstevel@tonic-gate 	 * Convert from seconds to milliseconds avoiding overflow.
10407c478bd9Sstevel@tonic-gate 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
10417c478bd9Sstevel@tonic-gate 	 * (4 billion seconds - about 130 years) we will in fact time
10427c478bd9Sstevel@tonic-gate 	 * out the prefix after 4 billion milliseconds - 46 days).
10437c478bd9Sstevel@tonic-gate 	 * Thus the longest lifetime (apart from infinity) is 46 days.
10447c478bd9Sstevel@tonic-gate 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
10457c478bd9Sstevel@tonic-gate 	 */
10467c478bd9Sstevel@tonic-gate 	if (validtime >= PREFIX_INFINITY / MILLISEC)
10477c478bd9Sstevel@tonic-gate 		pr->pr_ValidLifetime = PREFIX_INFINITY - 1;
10487c478bd9Sstevel@tonic-gate 	else
10497c478bd9Sstevel@tonic-gate 		pr->pr_ValidLifetime = validtime * MILLISEC;
10507c478bd9Sstevel@tonic-gate 	if (preftime >= PREFIX_INFINITY / MILLISEC)
10517c478bd9Sstevel@tonic-gate 		pr->pr_PreferredLifetime = PREFIX_INFINITY - 1;
10527c478bd9Sstevel@tonic-gate 	else
10537c478bd9Sstevel@tonic-gate 		pr->pr_PreferredLifetime = preftime * MILLISEC;
10547c478bd9Sstevel@tonic-gate 	pr->pr_AutonomousFlag = _B_TRUE;
10557c478bd9Sstevel@tonic-gate 
10567c478bd9Sstevel@tonic-gate 	if (debug & D_PREFIX) {
10577c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "incoming_prefix_addrconf_process(%s, %s/%u) "
10587c478bd9Sstevel@tonic-gate 		    "valid %u pref %u\n",
10597c478bd9Sstevel@tonic-gate 		    pr->pr_physical->pi_name,
10607c478bd9Sstevel@tonic-gate 		    inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
10617c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
10627c478bd9Sstevel@tonic-gate 		    pr->pr_ValidLifetime, pr->pr_PreferredLifetime);
10637c478bd9Sstevel@tonic-gate 	}
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate 	if (pr->pr_state & PR_AUTO) {
10667c478bd9Sstevel@tonic-gate 		/* Take the min of the two timeouts by calling it twice */
10677c478bd9Sstevel@tonic-gate 		if (pr->pr_ValidLifetime != 0)
10687c478bd9Sstevel@tonic-gate 			timer_schedule(pr->pr_ValidLifetime);
10697c478bd9Sstevel@tonic-gate 		if (pr->pr_PreferredLifetime != 0)
10707c478bd9Sstevel@tonic-gate 			timer_schedule(pr->pr_PreferredLifetime);
10717c478bd9Sstevel@tonic-gate 	}
10727c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != pr->pr_state) {
10737c478bd9Sstevel@tonic-gate 		/* Log a message when an addrconf prefix goes away */
10747c478bd9Sstevel@tonic-gate 		if ((pr->pr_kernel_state & PR_AUTO) &&
10757c478bd9Sstevel@tonic-gate 		    !(pr->pr_state & PR_AUTO)) {
10767c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 			logmsg(LOG_WARNING, "Address removed due to zero "
10797c478bd9Sstevel@tonic-gate 			    "valid lifetime %s\n",
10807c478bd9Sstevel@tonic-gate 			    inet_ntop(AF_INET6, (void *)&pr->pr_address,
10817c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf)));
10827c478bd9Sstevel@tonic-gate 		}
10837c478bd9Sstevel@tonic-gate 		prefix_update_k(pr);
10847c478bd9Sstevel@tonic-gate 	}
10857c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
10867c478bd9Sstevel@tonic-gate }
10877c478bd9Sstevel@tonic-gate 
10887c478bd9Sstevel@tonic-gate /*
10897c478bd9Sstevel@tonic-gate  * Process an MTU option received in a router advertisement.
10907c478bd9Sstevel@tonic-gate  */
10917c478bd9Sstevel@tonic-gate static void
10927c478bd9Sstevel@tonic-gate incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
10937c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
10947c478bd9Sstevel@tonic-gate {
10957c478bd9Sstevel@tonic-gate 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
10967c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
10977c478bd9Sstevel@tonic-gate 	uint32_t mtu;
10987c478bd9Sstevel@tonic-gate 
10997c478bd9Sstevel@tonic-gate 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
11007c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
11017c478bd9Sstevel@tonic-gate 
11027c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
11037c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
11047c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
11057c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
11067c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
11077c478bd9Sstevel@tonic-gate 		    8 * (int)mo->nd_opt_mtu_len);
11087c478bd9Sstevel@tonic-gate 		return;
11097c478bd9Sstevel@tonic-gate 	}
11107c478bd9Sstevel@tonic-gate 	mtu = ntohl(mo->nd_opt_mtu_mtu);
11117c478bd9Sstevel@tonic-gate 	if (pi->pi_LinkMTU == mtu)
11127c478bd9Sstevel@tonic-gate 		return;	/* No change */
11137c478bd9Sstevel@tonic-gate 	if (mtu > pi->pi_mtu) {
11147c478bd9Sstevel@tonic-gate 		/* Can't exceed physical MTU */
11157c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
11167c478bd9Sstevel@tonic-gate 
11177c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
11187c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
11197c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s too large "
11207c478bd9Sstevel@tonic-gate 		    "MTU %d - %d\n", abuf, pi->pi_name, mtu, pi->pi_mtu);
11217c478bd9Sstevel@tonic-gate 		return;
11227c478bd9Sstevel@tonic-gate 	}
11237c478bd9Sstevel@tonic-gate 	if (mtu < IPV6_MIN_MTU) {
11247c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
11277c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
11287c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s too small "
11297c478bd9Sstevel@tonic-gate 		    "MTU (%d)\n", abuf, pi->pi_name, mtu);
11307c478bd9Sstevel@tonic-gate 		return;
11317c478bd9Sstevel@tonic-gate 	}
11327c478bd9Sstevel@tonic-gate 
11337c478bd9Sstevel@tonic-gate 	pi->pi_LinkMTU = mtu;
11347c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
11357c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
11367c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCGLIFLNKINFO, (char *)&lifr) < 0) {
11377c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_mtu_opt: SIOCGLIFLNKINFO");
11387c478bd9Sstevel@tonic-gate 		return;
11397c478bd9Sstevel@tonic-gate 	}
11407c478bd9Sstevel@tonic-gate 	lifr.lifr_ifinfo.lir_maxmtu = pi->pi_LinkMTU;
11417c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
11427c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_mtu_opt: SIOCSLIFLNKINFO");
11437c478bd9Sstevel@tonic-gate 		return;
11447c478bd9Sstevel@tonic-gate 	}
11457c478bd9Sstevel@tonic-gate }
11467c478bd9Sstevel@tonic-gate 
11477c478bd9Sstevel@tonic-gate /*
11487c478bd9Sstevel@tonic-gate  * Process a source link-layer address option received in a router
11497c478bd9Sstevel@tonic-gate  * advertisement or solicitation.
11507c478bd9Sstevel@tonic-gate  */
11517c478bd9Sstevel@tonic-gate static void
11527c478bd9Sstevel@tonic-gate incoming_lla_opt(struct phyint *pi, uchar_t *opt,
11537c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, int isrouter)
11547c478bd9Sstevel@tonic-gate {
11557c478bd9Sstevel@tonic-gate 	struct nd_opt_lla *lo = (struct nd_opt_lla *)opt;
11567c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
11577c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 *sin6;
11587c478bd9Sstevel@tonic-gate 	int max_content_len;
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate 	if (pi->pi_hdw_addr_len == 0)
11617c478bd9Sstevel@tonic-gate 		return;
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 	/*
11647c478bd9Sstevel@tonic-gate 	 * Can't remove padding since it is link type specific.
11657c478bd9Sstevel@tonic-gate 	 * However, we check against the length of our link-layer
11667c478bd9Sstevel@tonic-gate 	 * address.
11677c478bd9Sstevel@tonic-gate 	 * Note: assumes that all links have a fixed lengh address.
11687c478bd9Sstevel@tonic-gate 	 */
11697c478bd9Sstevel@tonic-gate 	max_content_len = lo->nd_opt_lla_len * 8 - sizeof (struct nd_opt_hdr);
11707c478bd9Sstevel@tonic-gate 	if (max_content_len < pi->pi_hdw_addr_len ||
11717c478bd9Sstevel@tonic-gate 	    (max_content_len >= 8 &&
11727c478bd9Sstevel@tonic-gate 	    max_content_len - 7 > pi->pi_hdw_addr_len)) {
11737c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
11747c478bd9Sstevel@tonic-gate 
11757c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
11767c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
11777c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "lla option from %s on %s too long with bad "
11787c478bd9Sstevel@tonic-gate 		    "physaddr length (%d vs. %d bytes)\n",
11797c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
11807c478bd9Sstevel@tonic-gate 		    max_content_len, pi->pi_hdw_addr_len);
11817c478bd9Sstevel@tonic-gate 		return;
11827c478bd9Sstevel@tonic-gate 	}
11837c478bd9Sstevel@tonic-gate 
11847c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_hdw_len = pi->pi_hdw_addr_len;
11857c478bd9Sstevel@tonic-gate 	bcopy((char *)lo->nd_opt_lla_hdw_addr,
11867c478bd9Sstevel@tonic-gate 	    (char *)lifr.lifr_nd.lnr_hdw_addr,
11877c478bd9Sstevel@tonic-gate 	    lifr.lifr_nd.lnr_hdw_len);
11887c478bd9Sstevel@tonic-gate 
11897c478bd9Sstevel@tonic-gate 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
11907c478bd9Sstevel@tonic-gate 	bzero(sin6, sizeof (struct sockaddr_in6));
11917c478bd9Sstevel@tonic-gate 	sin6->sin6_family = AF_INET6;
11927c478bd9Sstevel@tonic-gate 	sin6->sin6_addr = from->sin6_addr;
11937c478bd9Sstevel@tonic-gate 
11947c478bd9Sstevel@tonic-gate 	/*
11957c478bd9Sstevel@tonic-gate 	 * Set IsRouter flag if RA; clear if RS.
11967c478bd9Sstevel@tonic-gate 	 */
11977c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_create = ND_STALE;
11987c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
11997c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_state_diff_lla = ND_STALE;
12007c478bd9Sstevel@tonic-gate 	lifr.lifr_nd.lnr_flags = isrouter;
12017c478bd9Sstevel@tonic-gate 	(void) strncpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
12027c478bd9Sstevel@tonic-gate 	lifr.lifr_name[sizeof (lifr.lifr_name) - 1] = '\0';
12037c478bd9Sstevel@tonic-gate 	if (ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr) < 0) {
12047c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "incoming_lla_opt: SIOCLIFSETND");
12057c478bd9Sstevel@tonic-gate 		return;
12067c478bd9Sstevel@tonic-gate 	}
12077c478bd9Sstevel@tonic-gate }
12087c478bd9Sstevel@tonic-gate 
12097c478bd9Sstevel@tonic-gate /*
12107c478bd9Sstevel@tonic-gate  * Verify the content of the received router advertisement against our
12117c478bd9Sstevel@tonic-gate  * own configuration as specified in RFC 2461.
12127c478bd9Sstevel@tonic-gate  */
12137c478bd9Sstevel@tonic-gate static void
12147c478bd9Sstevel@tonic-gate verify_ra_consistency(struct phyint *pi, struct nd_router_advert *ra, int len,
12157c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
12167c478bd9Sstevel@tonic-gate {
12177c478bd9Sstevel@tonic-gate 	char frombuf[INET6_ADDRSTRLEN];
12187c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
12197c478bd9Sstevel@tonic-gate 	int optlen;
12207c478bd9Sstevel@tonic-gate 	uint_t reachable, retrans;
12217c478bd9Sstevel@tonic-gate 	boolean_t pktflag, myflag;
12227c478bd9Sstevel@tonic-gate 
12237c478bd9Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
12247c478bd9Sstevel@tonic-gate 	    frombuf, sizeof (frombuf));
12257c478bd9Sstevel@tonic-gate 
12267c478bd9Sstevel@tonic-gate 	if (ra->nd_ra_curhoplimit != 0 &&
12277c478bd9Sstevel@tonic-gate 	    pi->pi_AdvCurHopLimit != 0 &&
12287c478bd9Sstevel@tonic-gate 	    ra->nd_ra_curhoplimit != pi->pi_AdvCurHopLimit) {
12297c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent cur hop "
12307c478bd9Sstevel@tonic-gate 		    "limit:\n\treceived %d configuration %d\n",
12317c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
12327c478bd9Sstevel@tonic-gate 		    ra->nd_ra_curhoplimit, pi->pi_AdvCurHopLimit);
12337c478bd9Sstevel@tonic-gate 	}
12347c478bd9Sstevel@tonic-gate 
12357c478bd9Sstevel@tonic-gate 	reachable = ntohl(ra->nd_ra_reachable);
12367c478bd9Sstevel@tonic-gate 	if (reachable != 0 && pi->pi_AdvReachableTime != 0 &&
12377c478bd9Sstevel@tonic-gate 	    reachable != pi->pi_AdvReachableTime) {
12387c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent reachable "
12397c478bd9Sstevel@tonic-gate 		    "time:\n\treceived %d configuration %d\n",
12407c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
12417c478bd9Sstevel@tonic-gate 		    reachable, pi->pi_AdvReachableTime);
12427c478bd9Sstevel@tonic-gate 	}
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	retrans = ntohl(ra->nd_ra_retransmit);
12457c478bd9Sstevel@tonic-gate 	if (retrans != 0 && pi->pi_AdvRetransTimer != 0 &&
12467c478bd9Sstevel@tonic-gate 	    retrans != pi->pi_AdvRetransTimer) {
12477c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent retransmit "
12487c478bd9Sstevel@tonic-gate 		    "timer:\n\treceived %d configuration %d\n",
12497c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
12507c478bd9Sstevel@tonic-gate 		    retrans, pi->pi_AdvRetransTimer);
12517c478bd9Sstevel@tonic-gate 	}
12527c478bd9Sstevel@tonic-gate 
12537c478bd9Sstevel@tonic-gate 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) != 0);
12547c478bd9Sstevel@tonic-gate 	myflag = (pi->pi_AdvManagedFlag != 0);
12557c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
12567c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent managed "
12577c478bd9Sstevel@tonic-gate 		    "flag:\n\treceived %s configuration %s\n",
12587c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
12597c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
12607c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
12617c478bd9Sstevel@tonic-gate 	}
12627c478bd9Sstevel@tonic-gate 	pktflag = ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) != 0);
12637c478bd9Sstevel@tonic-gate 	myflag = (pi->pi_AdvOtherConfigFlag != 0);
12647c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
12657c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent other config "
12667c478bd9Sstevel@tonic-gate 		    "flag:\n\treceived %s configuration %s\n",
12677c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
12687c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
12697c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
12707c478bd9Sstevel@tonic-gate 	}
12717c478bd9Sstevel@tonic-gate 
12727c478bd9Sstevel@tonic-gate 	/* Process any options */
12737c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_advert);
12747c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&ra[1];
12757c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
12767c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
12777c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
12787c478bd9Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION:
12797c478bd9Sstevel@tonic-gate 			verify_prefix_opt(pi, (uchar_t *)opt, frombuf);
12807c478bd9Sstevel@tonic-gate 			break;
12817c478bd9Sstevel@tonic-gate 		case ND_OPT_MTU:
12827c478bd9Sstevel@tonic-gate 			verify_mtu_opt(pi, (uchar_t *)opt, frombuf);
12837c478bd9Sstevel@tonic-gate 			break;
12847c478bd9Sstevel@tonic-gate 		default:
12857c478bd9Sstevel@tonic-gate 			break;
12867c478bd9Sstevel@tonic-gate 		}
12877c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
12887c478bd9Sstevel@tonic-gate 		len -= optlen;
12897c478bd9Sstevel@tonic-gate 	}
12907c478bd9Sstevel@tonic-gate }
12917c478bd9Sstevel@tonic-gate 
12927c478bd9Sstevel@tonic-gate /*
12937c478bd9Sstevel@tonic-gate  * Verify that the lifetimes and onlink/auto flags are consistent
12947c478bd9Sstevel@tonic-gate  * with our settings.
12957c478bd9Sstevel@tonic-gate  */
12967c478bd9Sstevel@tonic-gate static void
12977c478bd9Sstevel@tonic-gate verify_prefix_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
12987c478bd9Sstevel@tonic-gate {
12997c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
13007c478bd9Sstevel@tonic-gate 	int plen;
13017c478bd9Sstevel@tonic-gate 	struct adv_prefix *adv_pr;
13027c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;
13037c478bd9Sstevel@tonic-gate 	char prefixbuf[INET6_ADDRSTRLEN];
13047c478bd9Sstevel@tonic-gate 	int pktflag, myflag;
13057c478bd9Sstevel@tonic-gate 
13067c478bd9Sstevel@tonic-gate 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
13077c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA prefix option from %s on %s wrong size "
13087c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
13097c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
13107c478bd9Sstevel@tonic-gate 		    8 * (int)po->nd_opt_pi_len);
13117c478bd9Sstevel@tonic-gate 		return;
13127c478bd9Sstevel@tonic-gate 	}
13137c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
13147c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s contains link-local "
13157c478bd9Sstevel@tonic-gate 		    "prefix - ignored\n",
13167c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name);
13177c478bd9Sstevel@tonic-gate 		return;
13187c478bd9Sstevel@tonic-gate 	}
13197c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
13207c478bd9Sstevel@tonic-gate 	adv_pr = adv_prefix_lookup(pi, po->nd_opt_pi_prefix, plen);
13217c478bd9Sstevel@tonic-gate 	if (adv_pr == NULL)
13227c478bd9Sstevel@tonic-gate 		return;
13237c478bd9Sstevel@tonic-gate 
13247c478bd9Sstevel@tonic-gate 	/* Ignore prefixes which we do not advertise */
13257c478bd9Sstevel@tonic-gate 	if (!adv_pr->adv_pr_AdvAutonomousFlag && !adv_pr->adv_pr_AdvOnLinkFlag)
13267c478bd9Sstevel@tonic-gate 		return;
13277c478bd9Sstevel@tonic-gate 	(void) inet_ntop(AF_INET6, (void *)&adv_pr->adv_pr_prefix,
13287c478bd9Sstevel@tonic-gate 	    prefixbuf, sizeof (prefixbuf));
13297c478bd9Sstevel@tonic-gate 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) != 0);
13307c478bd9Sstevel@tonic-gate 	myflag = (adv_pr->adv_pr_AdvAutonomousFlag != 0);
13317c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
13327c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO,
1333*d04ccbb3Scarlsonj 		    "RA from %s on %s inconsistent autonomous flag for \n\t"
13347c478bd9Sstevel@tonic-gate 		    "prefix %s/%u: received %s configuration %s\n",
13357c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
13367c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
13377c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
13387c478bd9Sstevel@tonic-gate 	}
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 	pktflag = ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) != 0);
13417c478bd9Sstevel@tonic-gate 	myflag = (adv_pr->adv_pr_AdvOnLinkFlag != 0);
13427c478bd9Sstevel@tonic-gate 	if (pktflag != myflag) {
13437c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent on link flag "
13447c478bd9Sstevel@tonic-gate 		    "for \n\tprefix %s/%u: received %s configuration %s\n",
13457c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name, prefixbuf, adv_pr->adv_pr_prefix_len,
13467c478bd9Sstevel@tonic-gate 		    (pktflag ? "ON" : "OFF"),
13477c478bd9Sstevel@tonic-gate 		    (myflag ? "ON" : "OFF"));
13487c478bd9Sstevel@tonic-gate 	}
13497c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
13507c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
13517c478bd9Sstevel@tonic-gate 
13527c478bd9Sstevel@tonic-gate 	/*
13537c478bd9Sstevel@tonic-gate 	 * Take into account variation for lifetimes decrementing
13547c478bd9Sstevel@tonic-gate 	 * in real time. Allow +/- 10 percent and +/- 10 seconds.
13557c478bd9Sstevel@tonic-gate 	 */
13567c478bd9Sstevel@tonic-gate #define	LOWER_LIMIT(val)	((val) - (val)/10 - 10)
13577c478bd9Sstevel@tonic-gate #define	UPPER_LIMIT(val)	((val) + (val)/10 + 10)
13587c478bd9Sstevel@tonic-gate 	if (adv_pr->adv_pr_AdvValidRealTime) {
13597c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvValidExpiration > 0 &&
13607c478bd9Sstevel@tonic-gate 		    (validtime <
13617c478bd9Sstevel@tonic-gate 		    LOWER_LIMIT(adv_pr->adv_pr_AdvValidExpiration) ||
13627c478bd9Sstevel@tonic-gate 		    validtime >
13637c478bd9Sstevel@tonic-gate 		    UPPER_LIMIT(adv_pr->adv_pr_AdvValidExpiration))) {
13647c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
13657c478bd9Sstevel@tonic-gate 			    "lifetime for\n\tprefix %s/%u: received %d "
13667c478bd9Sstevel@tonic-gate 			    "configuration %d\n",
13677c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
13687c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
13697c478bd9Sstevel@tonic-gate 			    validtime, adv_pr->adv_pr_AdvValidExpiration);
13707c478bd9Sstevel@tonic-gate 		}
13717c478bd9Sstevel@tonic-gate 	} else {
13727c478bd9Sstevel@tonic-gate 		if (validtime != adv_pr->adv_pr_AdvValidLifetime) {
13737c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent valid "
13747c478bd9Sstevel@tonic-gate 			    "lifetime for\n\tprefix %s/%u: received %d "
13757c478bd9Sstevel@tonic-gate 			    "configuration %d\n",
13767c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
13777c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
13787c478bd9Sstevel@tonic-gate 			    validtime, adv_pr->adv_pr_AdvValidLifetime);
13797c478bd9Sstevel@tonic-gate 		}
13807c478bd9Sstevel@tonic-gate 	}
13817c478bd9Sstevel@tonic-gate 
13827c478bd9Sstevel@tonic-gate 	if (adv_pr->adv_pr_AdvPreferredRealTime) {
13837c478bd9Sstevel@tonic-gate 		if (adv_pr->adv_pr_AdvPreferredExpiration > 0 &&
13847c478bd9Sstevel@tonic-gate 		    (preftime <
13857c478bd9Sstevel@tonic-gate 		    LOWER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration) ||
13867c478bd9Sstevel@tonic-gate 		    preftime >
13877c478bd9Sstevel@tonic-gate 		    UPPER_LIMIT(adv_pr->adv_pr_AdvPreferredExpiration))) {
13887c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
13897c478bd9Sstevel@tonic-gate 			    "preferred lifetime for\n\tprefix %s/%u: "
13907c478bd9Sstevel@tonic-gate 			    "received %d configuration %d\n",
13917c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
13927c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
13937c478bd9Sstevel@tonic-gate 			    preftime, adv_pr->adv_pr_AdvPreferredExpiration);
13947c478bd9Sstevel@tonic-gate 		}
13957c478bd9Sstevel@tonic-gate 	} else {
13967c478bd9Sstevel@tonic-gate 		if (preftime != adv_pr->adv_pr_AdvPreferredLifetime) {
13977c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA from %s on %s inconsistent "
13987c478bd9Sstevel@tonic-gate 			    "preferred lifetime for\n\tprefix %s/%u: "
13997c478bd9Sstevel@tonic-gate 			    "received %d configuration %d\n",
14007c478bd9Sstevel@tonic-gate 			    frombuf, pi->pi_name, prefixbuf,
14017c478bd9Sstevel@tonic-gate 			    adv_pr->adv_pr_prefix_len,
14027c478bd9Sstevel@tonic-gate 			    preftime, adv_pr->adv_pr_AdvPreferredLifetime);
14037c478bd9Sstevel@tonic-gate 		}
14047c478bd9Sstevel@tonic-gate 	}
14057c478bd9Sstevel@tonic-gate }
14067c478bd9Sstevel@tonic-gate 
14077c478bd9Sstevel@tonic-gate /*
14087c478bd9Sstevel@tonic-gate  * Verify the received MTU against our own configuration.
14097c478bd9Sstevel@tonic-gate  */
14107c478bd9Sstevel@tonic-gate static void
14117c478bd9Sstevel@tonic-gate verify_mtu_opt(struct phyint *pi, uchar_t *opt, char *frombuf)
14127c478bd9Sstevel@tonic-gate {
14137c478bd9Sstevel@tonic-gate 	struct nd_opt_mtu *mo = (struct nd_opt_mtu *)opt;
14147c478bd9Sstevel@tonic-gate 	uint32_t mtu;
14157c478bd9Sstevel@tonic-gate 
14167c478bd9Sstevel@tonic-gate 	if (8 * mo->nd_opt_mtu_len != sizeof (*mo)) {
14177c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "mtu option from %s on %s wrong size "
14187c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
14197c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
14207c478bd9Sstevel@tonic-gate 		    8 * (int)mo->nd_opt_mtu_len);
14217c478bd9Sstevel@tonic-gate 		return;
14227c478bd9Sstevel@tonic-gate 	}
14237c478bd9Sstevel@tonic-gate 	mtu = ntohl(mo->nd_opt_mtu_mtu);
14247c478bd9Sstevel@tonic-gate 	if (pi->pi_AdvLinkMTU != 0 &&
14257c478bd9Sstevel@tonic-gate 	    pi->pi_AdvLinkMTU != mtu) {
14267c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s inconsistent MTU: "
14277c478bd9Sstevel@tonic-gate 		    "received %d configuration %d\n",
14287c478bd9Sstevel@tonic-gate 		    frombuf, pi->pi_name,
14297c478bd9Sstevel@tonic-gate 		    mtu, pi->pi_AdvLinkMTU);
14307c478bd9Sstevel@tonic-gate 	}
14317c478bd9Sstevel@tonic-gate }
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate /*
14347c478bd9Sstevel@tonic-gate  * Verify that all options have a non-zero length and that
14357c478bd9Sstevel@tonic-gate  * the options fit within the total length of the packet (optlen).
14367c478bd9Sstevel@tonic-gate  */
14377c478bd9Sstevel@tonic-gate static boolean_t
14387c478bd9Sstevel@tonic-gate verify_opt_len(struct nd_opt_hdr *opt, int optlen,
14397c478bd9Sstevel@tonic-gate     struct phyint *pi, struct sockaddr_in6 *from)
14407c478bd9Sstevel@tonic-gate {
14417c478bd9Sstevel@tonic-gate 	while (optlen > 0) {
14427c478bd9Sstevel@tonic-gate 		if (opt->nd_opt_len == 0) {
14437c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
14467c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
14477c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "Zero length option type 0x%x "
14507c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
14517c478bd9Sstevel@tonic-gate 			    opt->nd_opt_type, abuf, pi->pi_name);
14527c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
14537c478bd9Sstevel@tonic-gate 		}
14547c478bd9Sstevel@tonic-gate 		optlen -= 8 * opt->nd_opt_len;
14557c478bd9Sstevel@tonic-gate 		if (optlen < 0) {
14567c478bd9Sstevel@tonic-gate 			char abuf[INET6_ADDRSTRLEN];
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 			(void) inet_ntop(AF_INET6,
14597c478bd9Sstevel@tonic-gate 			    (void *)&from->sin6_addr,
14607c478bd9Sstevel@tonic-gate 			    abuf, sizeof (abuf));
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "Too large option: type 0x%x len %u "
14637c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
14647c478bd9Sstevel@tonic-gate 			    opt->nd_opt_type, opt->nd_opt_len,
14657c478bd9Sstevel@tonic-gate 			    abuf, pi->pi_name);
14667c478bd9Sstevel@tonic-gate 			return (_B_FALSE);
14677c478bd9Sstevel@tonic-gate 		}
14687c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt +
14697c478bd9Sstevel@tonic-gate 		    8 * opt->nd_opt_len);
14707c478bd9Sstevel@tonic-gate 	}
14717c478bd9Sstevel@tonic-gate 	return (_B_TRUE);
14727c478bd9Sstevel@tonic-gate }
14736b27086dSdd 
14746b27086dSdd /*
14756b27086dSdd  * Update IsRouter Flag for Host turning into a router or vice-versa.
14766b27086dSdd  */
14776b27086dSdd static void
1478b0f490f4Smh update_ra_flag(const struct phyint *pi, const struct sockaddr_in6 *from,
1479b0f490f4Smh     int isrouter)
14806b27086dSdd {
14816b27086dSdd 	struct lifreq lifr;
14826b27086dSdd 	char abuf[INET6_ADDRSTRLEN];
14836b27086dSdd 	struct sockaddr_in6 *sin6;
14846b27086dSdd 
14856b27086dSdd 	/* check if valid flag is being set */
14866b27086dSdd 	if ((isrouter != NDF_ISROUTER_ON) &&
14876b27086dSdd 	    (isrouter != NDF_ISROUTER_OFF)) {
14886b27086dSdd 		logmsg(LOG_ERR, "update_ra_flag: Invalid IsRouter "
14896b27086dSdd 		    "flag %d\n", isrouter);
14906b27086dSdd 		return;
14916b27086dSdd 	}
14926b27086dSdd 
14936b27086dSdd 	sin6 = (struct sockaddr_in6 *)&lifr.lifr_nd.lnr_addr;
14946b27086dSdd 	bzero(sin6, sizeof (*sin6));
14956b27086dSdd 	sin6->sin6_family = AF_INET6;
14966b27086dSdd 	sin6->sin6_addr = from->sin6_addr;
14976b27086dSdd 
14986b27086dSdd 	(void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
14996b27086dSdd 
15006b27086dSdd 	if (ioctl(pi->pi_sock, SIOCLIFGETND, (char *)&lifr) < 0) {
1501b0f490f4Smh 		if (errno == ESRCH) {
1502b0f490f4Smh 			if (debug & D_IFSCAN) {
1503b0f490f4Smh 				logmsg(LOG_DEBUG,
1504b0f490f4Smh "update_ra_flag: SIOCLIFGETND: nce doesn't exist, not setting IFF_ROUTER");
1505b0f490f4Smh 			}
1506b0f490f4Smh 		} else {
1507b0f490f4Smh 			logperror_pi(pi, "update_ra_flag: SIOCLIFGETND");
1508b0f490f4Smh 		}
15096b27086dSdd 	} else {
15106b27086dSdd 		/*
15116b27086dSdd 		 * The lif_nd_req structure has three state values to be used
15126b27086dSdd 		 * when changing/updating nces :
15136b27086dSdd 		 * lnr_state_create, lnr_state_same_lla, and lnr_state_diff_lla.
15146b27086dSdd 		 *
15156b27086dSdd 		 * In this case, we're updating an nce, without changing lla;
15166b27086dSdd 		 * so we set lnr_state_same_lla to ND_UNCHANGED, indicating that
15176b27086dSdd 		 * nce's state should not be affected by our flag change.
15186b27086dSdd 		 *
15196b27086dSdd 		 * The kernel implementation also expects the lnr_state_create
15206b27086dSdd 		 * field be always set, before processing ioctl request for NCE
15216b27086dSdd 		 * update.
15226b27086dSdd 		 * We use the state as STALE, while addressing the possibility
15236b27086dSdd 		 * of NCE deletion when ioctl with SIOCLIFGETND argument
15246b27086dSdd 		 * in earlier step is returned - further in such case we don't
15256b27086dSdd 		 * want to re-create the entry in the reachable state.
15266b27086dSdd 		 */
15276b27086dSdd 		lifr.lifr_nd.lnr_state_create = ND_STALE;
15286b27086dSdd 		lifr.lifr_nd.lnr_state_same_lla = ND_UNCHANGED;
15296b27086dSdd 		lifr.lifr_nd.lnr_flags = isrouter;
15306b27086dSdd 		if ((ioctl(pi->pi_sock, SIOCLIFSETND, (char *)&lifr)) < 0) {
15316b27086dSdd 			logperror_pi(pi, "update_ra_flag: SIOCLIFSETND");
15326b27086dSdd 		} else {
15336b27086dSdd 			(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
15346b27086dSdd 			    abuf, sizeof (abuf));
15356b27086dSdd 			logmsg(LOG_INFO, "update_ra_flag: IsRouter flag "
15366b27086dSdd 			    "updated for %s\n", abuf);
15376b27086dSdd 		}
15386b27086dSdd 	}
15396b27086dSdd }
1540