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 /*
236e91bba0SGirish Moodalbail  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
27a36f6bdeSDan McDonald /*
28a36f6bdeSDan McDonald  * Copyright 2015, OmniTI Computer Consulting, Inc. All rights reserved.
29a36f6bdeSDan McDonald  */
30a36f6bdeSDan McDonald 
317c478bd9Sstevel@tonic-gate #include "defs.h"
327c478bd9Sstevel@tonic-gate #include "tables.h"
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
357c478bd9Sstevel@tonic-gate 
36d04ccbb3Scarlsonj #include <dhcpagent_ipc.h>
37d04ccbb3Scarlsonj #include <dhcpagent_util.h>
38d04ccbb3Scarlsonj 
397c478bd9Sstevel@tonic-gate static boolean_t verify_opt_len(struct nd_opt_hdr *opt, int optlen,
407c478bd9Sstevel@tonic-gate 		    struct phyint *pi, struct sockaddr_in6 *from);
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate static void	incoming_rs(struct phyint *pi, struct nd_router_solicit *rs,
437c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate void		incoming_ra(struct phyint *pi, struct nd_router_advert *ra,
467c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from, boolean_t loopback);
477c478bd9Sstevel@tonic-gate static void	incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
487c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback);
49d04ccbb3Scarlsonj static void	incoming_prefix_onlink(struct phyint *pi, uchar_t *opt);
507c478bd9Sstevel@tonic-gate void		incoming_prefix_onlink_process(struct prefix *pr,
517c478bd9Sstevel@tonic-gate 		    uchar_t *opt);
52d04ccbb3Scarlsonj static void	incoming_prefix_stateful(struct phyint *, uchar_t *);
537c478bd9Sstevel@tonic-gate static boolean_t	incoming_prefix_addrconf(struct phyint *pi,
547c478bd9Sstevel@tonic-gate 		    uchar_t *opt, struct sockaddr_in6 *from,
557c478bd9Sstevel@tonic-gate 		    boolean_t loopback);
567c478bd9Sstevel@tonic-gate boolean_t	incoming_prefix_addrconf_process(struct phyint *pi,
577c478bd9Sstevel@tonic-gate 		    struct prefix *pr, uchar_t *opt,
587c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, boolean_t loopback,
597c478bd9Sstevel@tonic-gate 		    boolean_t new_prefix);
607c478bd9Sstevel@tonic-gate static void	incoming_mtu_opt(struct phyint *pi, uchar_t *opt,
617c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from);
627c478bd9Sstevel@tonic-gate static void	incoming_lla_opt(struct phyint *pi, uchar_t *opt,
637c478bd9Sstevel@tonic-gate 		    struct sockaddr_in6 *from, int isrouter);
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate static void	verify_ra_consistency(struct phyint *pi,
667c478bd9Sstevel@tonic-gate 		    struct nd_router_advert *ra,
677c478bd9Sstevel@tonic-gate 		    int len, struct sockaddr_in6 *from);
687c478bd9Sstevel@tonic-gate static void	verify_prefix_opt(struct phyint *pi, uchar_t *opt,
697c478bd9Sstevel@tonic-gate 		    char *frombuf);
707c478bd9Sstevel@tonic-gate static void	verify_mtu_opt(struct phyint *pi, uchar_t *opt,
717c478bd9Sstevel@tonic-gate 		    char *frombuf);
727c478bd9Sstevel@tonic-gate 
73b0f490f4Smh static void	update_ra_flag(const struct phyint *pi,
746b27086dSdd 		    const struct sockaddr_in6 *from, int isrouter);
756b27086dSdd 
767c478bd9Sstevel@tonic-gate /*
777c478bd9Sstevel@tonic-gate  * Return a pointer to the specified option buffer.
787c478bd9Sstevel@tonic-gate  * If not found return NULL.
797c478bd9Sstevel@tonic-gate  */
807c478bd9Sstevel@tonic-gate static void *
find_ancillary(struct msghdr * msg,int cmsg_type)817c478bd9Sstevel@tonic-gate find_ancillary(struct msghdr *msg, int cmsg_type)
827c478bd9Sstevel@tonic-gate {
837c478bd9Sstevel@tonic-gate 	struct cmsghdr *cmsg;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
867c478bd9Sstevel@tonic-gate 	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
877c478bd9Sstevel@tonic-gate 		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
887c478bd9Sstevel@tonic-gate 		    cmsg->cmsg_type == cmsg_type) {
897c478bd9Sstevel@tonic-gate 			return (CMSG_DATA(cmsg));
907c478bd9Sstevel@tonic-gate 		}
917c478bd9Sstevel@tonic-gate 	}
927c478bd9Sstevel@tonic-gate 	return (NULL);
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate void
in_data(struct phyint * pi)967c478bd9Sstevel@tonic-gate in_data(struct phyint *pi)
977c478bd9Sstevel@tonic-gate {
987c478bd9Sstevel@tonic-gate 	struct sockaddr_in6 from;
997c478bd9Sstevel@tonic-gate 	struct icmp6_hdr *icmp;
1007c478bd9Sstevel@tonic-gate 	struct nd_router_solicit *rs;
1017c478bd9Sstevel@tonic-gate 	struct nd_router_advert *ra;
1027c478bd9Sstevel@tonic-gate 	static uint64_t in_packet[(IP_MAXPACKET + 1)/8];
1037c478bd9Sstevel@tonic-gate 	static uint64_t ancillary_data[(IP_MAXPACKET + 1)/8];
1047c478bd9Sstevel@tonic-gate 	int len;
1057c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
1067c478bd9Sstevel@tonic-gate 	const char *msgbuf;
1077c478bd9Sstevel@tonic-gate 	struct msghdr msg;
1087c478bd9Sstevel@tonic-gate 	struct iovec iov;
1097c478bd9Sstevel@tonic-gate 	uchar_t *opt;
1107c478bd9Sstevel@tonic-gate 	uint_t hoplimit;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	iov.iov_base = (char *)in_packet;
1137c478bd9Sstevel@tonic-gate 	iov.iov_len = sizeof (in_packet);
1147c478bd9Sstevel@tonic-gate 	msg.msg_iov = &iov;
1157c478bd9Sstevel@tonic-gate 	msg.msg_iovlen = 1;
1167c478bd9Sstevel@tonic-gate 	msg.msg_name = (struct sockaddr *)&from;
1177c478bd9Sstevel@tonic-gate 	msg.msg_namelen = sizeof (from);
1187c478bd9Sstevel@tonic-gate 	msg.msg_control = ancillary_data;
1197c478bd9Sstevel@tonic-gate 	msg.msg_controllen = sizeof (ancillary_data);
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	if ((len = recvmsg(pi->pi_sock, &msg, 0)) < 0) {
1227c478bd9Sstevel@tonic-gate 		logperror_pi(pi, "in_data: recvfrom");
1237c478bd9Sstevel@tonic-gate 		return;
1247c478bd9Sstevel@tonic-gate 	}
1257c478bd9Sstevel@tonic-gate 	if (len == 0)
1267c478bd9Sstevel@tonic-gate 		return;
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	if (inet_ntop(AF_INET6, (void *)&from.sin6_addr,
1297c478bd9Sstevel@tonic-gate 	    abuf, sizeof (abuf)) == NULL)
1307c478bd9Sstevel@tonic-gate 		msgbuf = "Unspecified Router";
1317c478bd9Sstevel@tonic-gate 	else
1327c478bd9Sstevel@tonic-gate 		msgbuf = abuf;
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	/* Ignore packets > 64k or control buffers that don't fit */
1357c478bd9Sstevel@tonic-gate 	if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {
1367c478bd9Sstevel@tonic-gate 		if (debug & D_PKTBAD) {
1377c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG, "Truncated message: msg_flags 0x%x "
1387c478bd9Sstevel@tonic-gate 			    "from %s\n", msg.msg_flags, msgbuf);
1397c478bd9Sstevel@tonic-gate 		}
1407c478bd9Sstevel@tonic-gate 		return;
1417c478bd9Sstevel@tonic-gate 	}
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	icmp = (struct icmp6_hdr *)in_packet;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	if (len < ICMP6_MINLEN) {
1467c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Too short ICMP packet: %d bytes "
1477c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
1487c478bd9Sstevel@tonic-gate 		    len, msgbuf, pi->pi_name);
1497c478bd9Sstevel@tonic-gate 		return;
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_HOPLIMIT);
1537c478bd9Sstevel@tonic-gate 	if (opt == NULL) {
1547c478bd9Sstevel@tonic-gate 		/* Unknown hoplimit - must drop */
1557c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "Unknown hop limit from %s on %s\n",
1567c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
1577c478bd9Sstevel@tonic-gate 		return;
1587c478bd9Sstevel@tonic-gate 	}
1597c478bd9Sstevel@tonic-gate 	hoplimit = *(uint_t *)opt;
1607c478bd9Sstevel@tonic-gate 	opt = find_ancillary(&msg, IPV6_RTHDR);
1617c478bd9Sstevel@tonic-gate 	if (opt != NULL) {
1627c478bd9Sstevel@tonic-gate 		/* Can't allow routing headers in ND messages */
1637c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "ND message with routing header "
1647c478bd9Sstevel@tonic-gate 		    "from %s on %s\n",
1657c478bd9Sstevel@tonic-gate 		    msgbuf, pi->pi_name);
1667c478bd9Sstevel@tonic-gate 		return;
1677c478bd9Sstevel@tonic-gate 	}
1687c478bd9Sstevel@tonic-gate 	switch (icmp->icmp6_type) {
1697c478bd9Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
1707c478bd9Sstevel@tonic-gate 		if (!pi->pi_AdvSendAdvertisements)
1717c478bd9Sstevel@tonic-gate 			return;
1727c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
1737c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
1747c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RS packet "
1757c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
1767c478bd9Sstevel@tonic-gate 				    pi->pi_name);
1777c478bd9Sstevel@tonic-gate 			}
1787c478bd9Sstevel@tonic-gate 			return;
1797c478bd9Sstevel@tonic-gate 		}
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 		/*
1827c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
1837c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
1847c478bd9Sstevel@tonic-gate 		 */
1857c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
1869ba4fd8dSrshoaib 			logmsg(LOG_DEBUG, "RS hop limit: %d from %s on %s\n",
1877c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
1887c478bd9Sstevel@tonic-gate 			return;
1897c478bd9Sstevel@tonic-gate 		}
1907c478bd9Sstevel@tonic-gate 
1917c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
1927c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS code: %d from %s on %s\n",
1937c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
1947c478bd9Sstevel@tonic-gate 			return;
1957c478bd9Sstevel@tonic-gate 		}
1967c478bd9Sstevel@tonic-gate 
1977c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_solicit)) {
1987c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RS too short: %d bytes "
1997c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
2007c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
2017c478bd9Sstevel@tonic-gate 			return;
2027c478bd9Sstevel@tonic-gate 		}
2037c478bd9Sstevel@tonic-gate 		rs = (struct nd_router_solicit *)icmp;
2047c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_solicit)) {
2057c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&rs[1],
2067c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_solicit), pi, &from))
2077c478bd9Sstevel@tonic-gate 				return;
2087c478bd9Sstevel@tonic-gate 		}
2097c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
2107c478bd9Sstevel@tonic-gate 			print_route_sol("Received valid solicit from ", pi,
2117c478bd9Sstevel@tonic-gate 			    rs, len, &from);
2127c478bd9Sstevel@tonic-gate 		}
2137c478bd9Sstevel@tonic-gate 		incoming_rs(pi, rs, len, &from);
2147c478bd9Sstevel@tonic-gate 		break;
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
2177c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_UNSPECIFIED(&from.sin6_addr)) {
2187c478bd9Sstevel@tonic-gate 			/*
2197c478bd9Sstevel@tonic-gate 			 * Router advt. must have address!
2207c478bd9Sstevel@tonic-gate 			 * Logging the news and returning.
2217c478bd9Sstevel@tonic-gate 			 */
2227c478bd9Sstevel@tonic-gate 			logmsg(LOG_DEBUG,
2237c478bd9Sstevel@tonic-gate 			    "Router's address unspecified in advertisement\n");
2247c478bd9Sstevel@tonic-gate 			return;
2257c478bd9Sstevel@tonic-gate 		}
2267c478bd9Sstevel@tonic-gate 		if (pi->pi_flags & IFF_NORTEXCH) {
2277c478bd9Sstevel@tonic-gate 			if (debug & D_PKTIN) {
2287c478bd9Sstevel@tonic-gate 				logmsg(LOG_DEBUG, "Ignore received RA packet "
2297c478bd9Sstevel@tonic-gate 				    "on %s (no route exchange on interface)\n",
2307c478bd9Sstevel@tonic-gate 				    pi->pi_name);
2317c478bd9Sstevel@tonic-gate 			}
2327c478bd9Sstevel@tonic-gate 			return;
2337c478bd9Sstevel@tonic-gate 		}
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 		/*
2367c478bd9Sstevel@tonic-gate 		 * Assumes that the kernel has verified the AH (if present)
2377c478bd9Sstevel@tonic-gate 		 * and the ICMP checksum.
2387c478bd9Sstevel@tonic-gate 		 */
2397c478bd9Sstevel@tonic-gate 		if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
2409ba4fd8dSrshoaib 			logmsg(LOG_DEBUG, "RA from %s - not link local on %s\n",
2417c478bd9Sstevel@tonic-gate 			    msgbuf, pi->pi_name);
2427c478bd9Sstevel@tonic-gate 			return;
2437c478bd9Sstevel@tonic-gate 		}
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 		if (hoplimit != IPV6_MAX_HOPS) {
2467c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA hop limit: %d from %s on %s\n",
2477c478bd9Sstevel@tonic-gate 			    hoplimit, msgbuf, pi->pi_name);
2487c478bd9Sstevel@tonic-gate 			return;
2497c478bd9Sstevel@tonic-gate 		}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 		if (icmp->icmp6_code != 0) {
2527c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA code: %d from %s on %s\n",
2537c478bd9Sstevel@tonic-gate 			    icmp->icmp6_code, msgbuf, pi->pi_name);
2547c478bd9Sstevel@tonic-gate 			return;
2557c478bd9Sstevel@tonic-gate 		}
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 		if (len < sizeof (struct nd_router_advert)) {
2587c478bd9Sstevel@tonic-gate 			logmsg(LOG_INFO, "RA too short: %d bytes "
2597c478bd9Sstevel@tonic-gate 			    "from %s on %s\n",
2607c478bd9Sstevel@tonic-gate 			    len, msgbuf, pi->pi_name);
2617c478bd9Sstevel@tonic-gate 			return;
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 		ra = (struct nd_router_advert *)icmp;
2647c478bd9Sstevel@tonic-gate 		if (len > sizeof (struct nd_router_advert)) {
2657c478bd9Sstevel@tonic-gate 			if (!verify_opt_len((struct nd_opt_hdr *)&ra[1],
2667c478bd9Sstevel@tonic-gate 			    len - sizeof (struct nd_router_advert), pi, &from))
2677c478bd9Sstevel@tonic-gate 				return;
2687c478bd9Sstevel@tonic-gate 		}
2697c478bd9Sstevel@tonic-gate 		if (debug & D_PKTIN) {
2707c478bd9Sstevel@tonic-gate 			print_route_adv("Received valid advert from ", pi,
2717c478bd9Sstevel@tonic-gate 			    ra, len, &from);
2727c478bd9Sstevel@tonic-gate 		}
2737c478bd9Sstevel@tonic-gate 		if (pi->pi_AdvSendAdvertisements)
2747c478bd9Sstevel@tonic-gate 			verify_ra_consistency(pi, ra, len, &from);
2757c478bd9Sstevel@tonic-gate 		else
2767c478bd9Sstevel@tonic-gate 			incoming_ra(pi, ra, len, &from, _B_FALSE);
2777c478bd9Sstevel@tonic-gate 		break;
2787c478bd9Sstevel@tonic-gate 	}
2797c478bd9Sstevel@tonic-gate }
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate /*
2827c478bd9Sstevel@tonic-gate  * Process a received router solicitation.
2837c478bd9Sstevel@tonic-gate  * Check for source link-layer address option and check if it
2847c478bd9Sstevel@tonic-gate  * is time to advertise.
2857c478bd9Sstevel@tonic-gate  */
2867c478bd9Sstevel@tonic-gate static void
incoming_rs(struct phyint * pi,struct nd_router_solicit * rs,int len,struct sockaddr_in6 * from)2877c478bd9Sstevel@tonic-gate incoming_rs(struct phyint *pi, struct nd_router_solicit *rs, int len,
2887c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from)
2897c478bd9Sstevel@tonic-gate {
2907c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
2917c478bd9Sstevel@tonic-gate 	int optlen;
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate 	/* Process any options */
2947c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_solicit);
2957c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&rs[1];
2967c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
2977c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
2987c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
2997c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
3007c478bd9Sstevel@tonic-gate 			incoming_lla_opt(pi, (uchar_t *)opt,
3017c478bd9Sstevel@tonic-gate 			    from, NDF_ISROUTER_OFF);
3027c478bd9Sstevel@tonic-gate 			break;
3037c478bd9Sstevel@tonic-gate 		default:
3047c478bd9Sstevel@tonic-gate 			break;
3057c478bd9Sstevel@tonic-gate 		}
3067c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
3077c478bd9Sstevel@tonic-gate 		len -= optlen;
3087c478bd9Sstevel@tonic-gate 	}
3097c478bd9Sstevel@tonic-gate 	/* Simple algorithm: treat unicast and multicast RSs the same */
3107c478bd9Sstevel@tonic-gate 	check_to_advertise(pi, RECEIVED_SOLICIT);
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate 
313d04ccbb3Scarlsonj /*
3146e91bba0SGirish Moodalbail  * Function that sends commands to dhcpagent daemon.
315d04ccbb3Scarlsonj  */
3166e91bba0SGirish Moodalbail int
dhcp_op(struct phyint * pi,int type)3176e91bba0SGirish Moodalbail dhcp_op(struct phyint *pi, int type)
318d04ccbb3Scarlsonj {
319d04ccbb3Scarlsonj 	dhcp_ipc_request_t	*request;
320d04ccbb3Scarlsonj 	dhcp_ipc_reply_t	*reply	= NULL;
321d04ccbb3Scarlsonj 	int			error;
322d04ccbb3Scarlsonj 
323d04ccbb3Scarlsonj 	request = dhcp_ipc_alloc_request(type | DHCP_V6, pi->pi_name, NULL, 0,
324d04ccbb3Scarlsonj 	    DHCP_TYPE_NONE);
325d04ccbb3Scarlsonj 	if (request == NULL) {
3266e91bba0SGirish Moodalbail 		logmsg(LOG_ERR, "dhcp_op: out of memory\n");
327d04ccbb3Scarlsonj 		/* make sure we try again next time there's a chance */
3286e91bba0SGirish Moodalbail 		if (type != DHCP_RELEASE) {
3296e91bba0SGirish Moodalbail 			pi->pi_ra_flags &=
3306e91bba0SGirish Moodalbail 			    ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER;
3316e91bba0SGirish Moodalbail 		}
3326e91bba0SGirish Moodalbail 		return (DHCP_IPC_E_MEMORY);
333d04ccbb3Scarlsonj 	}
334d04ccbb3Scarlsonj 
335d04ccbb3Scarlsonj 	error = dhcp_ipc_make_request(request, &reply, 0);
336d04ccbb3Scarlsonj 	free(request);
337d04ccbb3Scarlsonj 	if (error != 0) {
3386e91bba0SGirish Moodalbail 		logmsg(LOG_ERR, "could not send request to dhcpagent: "
3396e91bba0SGirish Moodalbail 		    "%s: %s\n", pi->pi_name, dhcp_ipc_strerror(error));
3406e91bba0SGirish Moodalbail 		return (error);
341d04ccbb3Scarlsonj 	}
342d04ccbb3Scarlsonj 
343d04ccbb3Scarlsonj 	error = reply->return_code;
344d04ccbb3Scarlsonj 	free(reply);
345d04ccbb3Scarlsonj 
3466e91bba0SGirish Moodalbail 	return (error);
3476e91bba0SGirish Moodalbail }
3486e91bba0SGirish Moodalbail 
3496e91bba0SGirish Moodalbail /*
3506e91bba0SGirish Moodalbail  * Start up DHCPv6 on a given physical interface. Does not wait for
3516e91bba0SGirish Moodalbail  * a message to be returned from the daemon.
3526e91bba0SGirish Moodalbail  */
3536e91bba0SGirish Moodalbail void
start_dhcp(struct phyint * pi)3546e91bba0SGirish Moodalbail start_dhcp(struct phyint *pi)
3556e91bba0SGirish Moodalbail {
3566e91bba0SGirish Moodalbail 	int	error;
3576e91bba0SGirish Moodalbail 	int	type;
3586e91bba0SGirish Moodalbail 
3596e91bba0SGirish Moodalbail 	if (dhcp_start_agent(DHCP_IPC_MAX_WAIT) == -1) {
3606e91bba0SGirish Moodalbail 		logmsg(LOG_ERR, "unable to start %s\n", DHCP_AGENT_PATH);
3616e91bba0SGirish Moodalbail 		/* make sure we try again next time there's a chance */
3626e91bba0SGirish Moodalbail 		pi->pi_ra_flags &= ~ND_RA_FLAG_MANAGED & ~ND_RA_FLAG_OTHER;
3636e91bba0SGirish Moodalbail 		return;
3646e91bba0SGirish Moodalbail 	}
3656e91bba0SGirish Moodalbail 
3666e91bba0SGirish Moodalbail 	else if (pi->pi_ra_flags & ND_RA_FLAG_MANAGED)
3676e91bba0SGirish Moodalbail 		type = DHCP_START;
3686e91bba0SGirish Moodalbail 	else
3696e91bba0SGirish Moodalbail 		type = DHCP_INFORM;
3706e91bba0SGirish Moodalbail 
3716e91bba0SGirish Moodalbail 	error = dhcp_op(pi, type);
372d04ccbb3Scarlsonj 	/*
373d04ccbb3Scarlsonj 	 * Timeout is considered to be "success" because we don't wait for DHCP
374d04ccbb3Scarlsonj 	 * to do its exchange.
375d04ccbb3Scarlsonj 	 */
376d04ccbb3Scarlsonj 	if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING &&
377d04ccbb3Scarlsonj 	    error != DHCP_IPC_E_TIMEOUT) {
3786e91bba0SGirish Moodalbail 		logmsg(LOG_ERR, "Error in dhcpagent: %s: %s\n",
3796e91bba0SGirish Moodalbail 		    pi->pi_name, dhcp_ipc_strerror(error));
3806e91bba0SGirish Moodalbail 	}
3816e91bba0SGirish Moodalbail }
3826e91bba0SGirish Moodalbail 
3836e91bba0SGirish Moodalbail /*
3846e91bba0SGirish Moodalbail  * Release the acquired DHCPv6 lease on a given physical interface.
3856e91bba0SGirish Moodalbail  * Does not wait for a message to be returned from the daemon.
3866e91bba0SGirish Moodalbail  */
3876e91bba0SGirish Moodalbail void
release_dhcp(struct phyint * pi)3886e91bba0SGirish Moodalbail release_dhcp(struct phyint *pi)
3896e91bba0SGirish Moodalbail {
3906e91bba0SGirish Moodalbail 	int	error;
3916e91bba0SGirish Moodalbail 	int	type;
3926e91bba0SGirish Moodalbail 
3936e91bba0SGirish Moodalbail 	type = DHCP_RELEASE;
3946e91bba0SGirish Moodalbail retry:
3956e91bba0SGirish Moodalbail 	error = dhcp_op(pi, type);
3966e91bba0SGirish Moodalbail 	if (error != DHCP_IPC_SUCCESS && error != DHCP_IPC_E_RUNNING &&
3976e91bba0SGirish Moodalbail 	    error != DHCP_IPC_E_TIMEOUT) {
3986e91bba0SGirish Moodalbail 		if (type == DHCP_RELEASE && error == DHCP_IPC_E_OUTSTATE) {
3996e91bba0SGirish Moodalbail 			/*
4006e91bba0SGirish Moodalbail 			 * Drop the dhcp control if we cannot release it.
4016e91bba0SGirish Moodalbail 			 */
4026e91bba0SGirish Moodalbail 			type = DHCP_DROP;
4036e91bba0SGirish Moodalbail 			goto retry;
4046e91bba0SGirish Moodalbail 		}
4056e91bba0SGirish Moodalbail 		logmsg(LOG_ERR, "Error in dhcpagent: %s: %s\n",
4066e91bba0SGirish Moodalbail 		    pi->pi_name, dhcp_ipc_strerror(error));
407d04ccbb3Scarlsonj 	}
408d04ccbb3Scarlsonj }
409d04ccbb3Scarlsonj 
410a36f6bdeSDan McDonald /*
411a36f6bdeSDan McDonald  * Globals to check if we're seeing unusual hop counts in Router
412a36f6bdeSDan McDonald  * Advertisements (RAs).  We record the hopcounts in the kernel using
413a36f6bdeSDan McDonald  * SIOCSLIFLNKINFO, but the kernel ignores these when actually setting IPv6
414a36f6bdeSDan McDonald  * hop counts for packets.
415a36f6bdeSDan McDonald  *
416a36f6bdeSDan McDonald  * RFC 3756 does mention the possibility of an adversary throttling down
417a36f6bdeSDan McDonald  * hopcounts using unsolicited RAs.  These variables can be tuned with 'mdb -p'
418a36f6bdeSDan McDonald  * to reduce/increase our logging threshholds.
419a36f6bdeSDan McDonald  */
420a36f6bdeSDan McDonald /* Really a boolean... if set, also log the offending sending address. */
421a36f6bdeSDan McDonald int bad_hopcount_record_addr = 0;
422a36f6bdeSDan McDonald /* Anything less triggers a warning.  Set to 0 to disable. */
423a36f6bdeSDan McDonald int bad_hopcount_threshhold = 16;
424a36f6bdeSDan McDonald /* Number of packets received below the threshhold. */
425a36f6bdeSDan McDonald uint64_t bad_hopcount_packets;
426a36f6bdeSDan McDonald 
4277c478bd9Sstevel@tonic-gate /*
4287c478bd9Sstevel@tonic-gate  * Process a received router advertisement.
4297c478bd9Sstevel@tonic-gate  * Called both when packets arrive as well as when we send RAs.
4307c478bd9Sstevel@tonic-gate  * In the latter case 'loopback' is set.
4317c478bd9Sstevel@tonic-gate  */
4327c478bd9Sstevel@tonic-gate void
incoming_ra(struct phyint * pi,struct nd_router_advert * ra,int len,struct sockaddr_in6 * from,boolean_t loopback)4337c478bd9Sstevel@tonic-gate incoming_ra(struct phyint *pi, struct nd_router_advert *ra, int len,
4347c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
4357c478bd9Sstevel@tonic-gate {
4367c478bd9Sstevel@tonic-gate 	struct nd_opt_hdr *opt;
4377c478bd9Sstevel@tonic-gate 	int optlen;
4387c478bd9Sstevel@tonic-gate 	struct lifreq lifr;
4397c478bd9Sstevel@tonic-gate 	boolean_t set_needed = _B_FALSE;
4407c478bd9Sstevel@tonic-gate 	struct router *dr;
4417c478bd9Sstevel@tonic-gate 	uint16_t router_lifetime;
4427c478bd9Sstevel@tonic-gate 	uint_t reachable, retrans;
4437c478bd9Sstevel@tonic-gate 	boolean_t reachable_time_changed = _B_FALSE;
4446b27086dSdd 	boolean_t slla_opt_present	 = _B_FALSE;
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate 	if (no_loopback && loopback)
4477c478bd9Sstevel@tonic-gate 		return;
4487c478bd9Sstevel@tonic-gate 
449e11c3f44Smeem 	bzero(&lifr, sizeof (lifr));
450e11c3f44Smeem 	(void) strlcpy(lifr.lifr_name, pi->pi_name, sizeof (lifr.lifr_name));
4517c478bd9Sstevel@tonic-gate 
4520a12cda4Sdd 	if (ra->nd_ra_curhoplimit != CURHOP_UNSPECIFIED &&
4530a12cda4Sdd 	    ra->nd_ra_curhoplimit != pi->pi_CurHopLimit) {
4547c478bd9Sstevel@tonic-gate 		pi->pi_CurHopLimit = ra->nd_ra_curhoplimit;
4557c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_maxhops = pi->pi_CurHopLimit;
4567c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
457a36f6bdeSDan McDonald 
458a36f6bdeSDan McDonald 		if (pi->pi_CurHopLimit < bad_hopcount_threshhold) {
459a36f6bdeSDan McDonald 			char abuf[INET6_ADDRSTRLEN];
460a36f6bdeSDan McDonald 
461a36f6bdeSDan McDonald 			bad_hopcount_packets++;
462a36f6bdeSDan McDonald 			logmsg(LOG_ALERT,
463a36f6bdeSDan McDonald 			    "Low hopcount %d received on %s%s%s\n",
464a36f6bdeSDan McDonald 			    pi->pi_CurHopLimit, pi->pi_name,
465a36f6bdeSDan McDonald 			    bad_hopcount_record_addr ? " from " : "",
466a36f6bdeSDan McDonald 			    bad_hopcount_record_addr ?
467a36f6bdeSDan McDonald 			    inet_ntop(AF_INET6, &from->sin6_addr, abuf,
468a36f6bdeSDan McDonald 			    INET6_ADDRSTRLEN) : "");
469a36f6bdeSDan McDonald 		}
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	reachable = ntohl(ra->nd_ra_reachable);
4737c478bd9Sstevel@tonic-gate 	if (reachable != 0 &&
4747c478bd9Sstevel@tonic-gate 	    reachable != pi->pi_BaseReachableTime) {
4757c478bd9Sstevel@tonic-gate 		pi->pi_BaseReachableTime = reachable;
4767c478bd9Sstevel@tonic-gate 		reachable_time_changed = _B_TRUE;
4777c478bd9Sstevel@tonic-gate 	}
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	if (pi->pi_reach_time_since_random < MIN_REACH_RANDOM_INTERVAL ||
4807c478bd9Sstevel@tonic-gate 	    reachable_time_changed) {
4817c478bd9Sstevel@tonic-gate 		phyint_reach_random(pi, _B_FALSE);
4827c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
4837c478bd9Sstevel@tonic-gate 	}
4847c478bd9Sstevel@tonic-gate 	lifr.lifr_ifinfo.lir_reachtime = pi->pi_ReachableTime;
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate 	retrans = ntohl(ra->nd_ra_retransmit);
4877c478bd9Sstevel@tonic-gate 	if (retrans != 0 &&
4887c478bd9Sstevel@tonic-gate 	    pi->pi_RetransTimer != retrans) {
4897c478bd9Sstevel@tonic-gate 		pi->pi_RetransTimer = retrans;
4907c478bd9Sstevel@tonic-gate 		lifr.lifr_ifinfo.lir_reachretrans = pi->pi_RetransTimer;
4917c478bd9Sstevel@tonic-gate 		set_needed = _B_TRUE;
4927c478bd9Sstevel@tonic-gate 	}
4937c478bd9Sstevel@tonic-gate 
4947c478bd9Sstevel@tonic-gate 	if (set_needed) {
4957c478bd9Sstevel@tonic-gate 		if (ioctl(pi->pi_sock, SIOCSLIFLNKINFO, (char *)&lifr) < 0) {
4967c478bd9Sstevel@tonic-gate 			logperror_pi(pi, "incoming_ra: SIOCSLIFLNKINFO");
4977c478bd9Sstevel@tonic-gate 			return;
4987c478bd9Sstevel@tonic-gate 		}
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
501d04ccbb3Scarlsonj 	/*
502d04ccbb3Scarlsonj 	 * If the "managed" flag is set, then just assume that the "other" flag
503d04ccbb3Scarlsonj 	 * is set as well.  It's not legal to get addresses alone without
504d04ccbb3Scarlsonj 	 * getting other data.
505d04ccbb3Scarlsonj 	 */
506d04ccbb3Scarlsonj 	if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
507d04ccbb3Scarlsonj 		ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER;
508d04ccbb3Scarlsonj 
509d04ccbb3Scarlsonj 	/*
510d04ccbb3Scarlsonj 	 * If either the "managed" or "other" bits have turned on, then it's
511d04ccbb3Scarlsonj 	 * now time to invoke DHCP.  If only the "other" bit is set, then don't
512d04ccbb3Scarlsonj 	 * get addresses via DHCP; only "other" data.  If "managed" is set,
513d04ccbb3Scarlsonj 	 * then we must always get both addresses and "other" data.
514d04ccbb3Scarlsonj 	 */
5156e91bba0SGirish Moodalbail 	if (pi->pi_autoconf && pi->pi_stateful &&
516d04ccbb3Scarlsonj 	    (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags &
517d04ccbb3Scarlsonj 	    (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))) {
518d04ccbb3Scarlsonj 		if (debug & D_DHCP) {
519d04ccbb3Scarlsonj 			logmsg(LOG_DEBUG,
520d04ccbb3Scarlsonj 			    "incoming_ra: trigger dhcp %s on %s\n",
521d04ccbb3Scarlsonj 			    (ra->nd_ra_flags_reserved & ~pi->pi_ra_flags &
522e11c3f44Smeem 			    ND_RA_FLAG_MANAGED) ? "MANAGED" : "OTHER",
523d04ccbb3Scarlsonj 			    pi->pi_name);
5247c478bd9Sstevel@tonic-gate 		}
525d04ccbb3Scarlsonj 		pi->pi_ra_flags |= ra->nd_ra_flags_reserved;
526d04ccbb3Scarlsonj 		start_dhcp(pi);
5277c478bd9Sstevel@tonic-gate 	}
528d04ccbb3Scarlsonj 
5297c478bd9Sstevel@tonic-gate 	/* Skip default router code if sent from ourselves */
5307c478bd9Sstevel@tonic-gate 	if (!loopback) {
5317c478bd9Sstevel@tonic-gate 		/* Find and update or add default router in list */
5327c478bd9Sstevel@tonic-gate 		dr = router_lookup(pi, from->sin6_addr);
5337c478bd9Sstevel@tonic-gate 		router_lifetime = ntohs(ra->nd_ra_router_lifetime);
5347c478bd9Sstevel@tonic-gate 		if (dr == NULL) {
5357c478bd9Sstevel@tonic-gate 			if (router_lifetime != 0) {
5367c478bd9Sstevel@tonic-gate 				dr = router_create(pi, from->sin6_addr,
5377c478bd9Sstevel@tonic-gate 				    MILLISEC * router_lifetime);
5387c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
5397c478bd9Sstevel@tonic-gate 			}
5407c478bd9Sstevel@tonic-gate 		} else {
5417c478bd9Sstevel@tonic-gate 			dr->dr_lifetime = MILLISEC * router_lifetime;
5427c478bd9Sstevel@tonic-gate 			if (dr->dr_lifetime != 0)
5437c478bd9Sstevel@tonic-gate 				timer_schedule(dr->dr_lifetime);
5447c478bd9Sstevel@tonic-gate 			if ((dr->dr_lifetime != 0 && !dr->dr_inkernel) ||
5457c478bd9Sstevel@tonic-gate 			    (dr->dr_lifetime == 0 && dr->dr_inkernel))
5467c478bd9Sstevel@tonic-gate 				router_update_k(dr);
5477c478bd9Sstevel@tonic-gate 		}
5487c478bd9Sstevel@tonic-gate 	}
5497c478bd9Sstevel@tonic-gate 	/* Process any options */
5507c478bd9Sstevel@tonic-gate 	len -= sizeof (struct nd_router_advert);
5517c478bd9Sstevel@tonic-gate 	opt = (struct nd_opt_hdr *)&ra[1];
5527c478bd9Sstevel@tonic-gate 	while (len >= sizeof (struct nd_opt_hdr)) {
5537c478bd9Sstevel@tonic-gate 		optlen = opt->nd_opt_len * 8;
5547c478bd9Sstevel@tonic-gate 		switch (opt->nd_opt_type) {
5557c478bd9Sstevel@tonic-gate 		case ND_OPT_PREFIX_INFORMATION:
5567c478bd9Sstevel@tonic-gate 			incoming_prefix_opt(pi, (uchar_t *)opt, from,
5577c478bd9Sstevel@tonic-gate 			    loopback);
5587c478bd9Sstevel@tonic-gate 			break;
5597c478bd9Sstevel@tonic-gate 		case ND_OPT_MTU:
5607c478bd9Sstevel@tonic-gate 			incoming_mtu_opt(pi, (uchar_t *)opt, from);
5617c478bd9Sstevel@tonic-gate 			break;
5627c478bd9Sstevel@tonic-gate 		case ND_OPT_SOURCE_LINKADDR:
5637c478bd9Sstevel@tonic-gate 			/* skip lla option if sent from ourselves! */
5647c478bd9Sstevel@tonic-gate 			if (!loopback) {
5657c478bd9Sstevel@tonic-gate 				incoming_lla_opt(pi, (uchar_t *)opt,
5667c478bd9Sstevel@tonic-gate 				    from, NDF_ISROUTER_ON);
5676b27086dSdd 				slla_opt_present = _B_TRUE;
5687c478bd9Sstevel@tonic-gate 			}
5697c478bd9Sstevel@tonic-gate 			break;
5707c478bd9Sstevel@tonic-gate 		default:
5717c478bd9Sstevel@tonic-gate 			break;
5727c478bd9Sstevel@tonic-gate 		}
5737c478bd9Sstevel@tonic-gate 		opt = (struct nd_opt_hdr *)((char *)opt + optlen);
5747c478bd9Sstevel@tonic-gate 		len -= optlen;
5757c478bd9Sstevel@tonic-gate 	}
576b0f490f4Smh 	if (!loopback && !slla_opt_present)
5776b27086dSdd 		update_ra_flag(pi, from, NDF_ISROUTER_ON);
5787c478bd9Sstevel@tonic-gate 	/* Stop sending solicitations */
5797c478bd9Sstevel@tonic-gate 	check_to_solicit(pi, SOLICIT_DONE);
5807c478bd9Sstevel@tonic-gate }
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate /*
5837c478bd9Sstevel@tonic-gate  * Process a received prefix option.
5847c478bd9Sstevel@tonic-gate  * Unless addrconf is turned off we process both the addrconf and the
5857c478bd9Sstevel@tonic-gate  * onlink aspects of the prefix option.
5867c478bd9Sstevel@tonic-gate  *
5877c478bd9Sstevel@tonic-gate  * Note that when a flag (onlink or auto) is turned off we do nothing -
5887c478bd9Sstevel@tonic-gate  * the prefix will time out.
5897c478bd9Sstevel@tonic-gate  */
5907c478bd9Sstevel@tonic-gate static void
incoming_prefix_opt(struct phyint * pi,uchar_t * opt,struct sockaddr_in6 * from,boolean_t loopback)5917c478bd9Sstevel@tonic-gate incoming_prefix_opt(struct phyint *pi, uchar_t *opt,
5927c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
5937c478bd9Sstevel@tonic-gate {
5947c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
5957c478bd9Sstevel@tonic-gate 	boolean_t	good_prefix = _B_TRUE;
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	if (8 * po->nd_opt_pi_len != sizeof (*po)) {
5987c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
5997c478bd9Sstevel@tonic-gate 
6007c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
6017c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
6027c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "prefix option from %s on %s wrong size "
6037c478bd9Sstevel@tonic-gate 		    "(%d bytes)\n",
6047c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name,
6057c478bd9Sstevel@tonic-gate 		    8 * (int)po->nd_opt_pi_len);
6067c478bd9Sstevel@tonic-gate 		return;
6077c478bd9Sstevel@tonic-gate 	}
6087c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_LINKLOCAL(&po->nd_opt_pi_prefix)) {
6097c478bd9Sstevel@tonic-gate 		char abuf[INET6_ADDRSTRLEN];
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
6127c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
6137c478bd9Sstevel@tonic-gate 		logmsg(LOG_INFO, "RA from %s on %s contains link-local prefix "
6147c478bd9Sstevel@tonic-gate 		    "- ignored\n",
6157c478bd9Sstevel@tonic-gate 		    abuf, pi->pi_name);
6167c478bd9Sstevel@tonic-gate 		return;
6177c478bd9Sstevel@tonic-gate 	}
6187c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) &&
6196e91bba0SGirish Moodalbail 	    pi->pi_stateless && pi->pi_autoconf) {
6207c478bd9Sstevel@tonic-gate 		good_prefix = incoming_prefix_addrconf(pi, opt, from, loopback);
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 	if ((po->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) &&
6237c478bd9Sstevel@tonic-gate 	    good_prefix) {
624d04ccbb3Scarlsonj 		incoming_prefix_onlink(pi, opt);
6257c478bd9Sstevel@tonic-gate 	}
6266e91bba0SGirish Moodalbail 	if (pi->pi_stateful && pi->pi_autoconf)
627d04ccbb3Scarlsonj 		incoming_prefix_stateful(pi, opt);
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate 
6307c478bd9Sstevel@tonic-gate /*
6317c478bd9Sstevel@tonic-gate  * Process prefix options with the onlink flag set.
6327c478bd9Sstevel@tonic-gate  *
6337c478bd9Sstevel@tonic-gate  * If there are no routers ndpd will add an onlink
6347c478bd9Sstevel@tonic-gate  * default route which will allow communication
6357c478bd9Sstevel@tonic-gate  * between neighbors.
6367c478bd9Sstevel@tonic-gate  *
6377c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
6387c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
6397c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
6407c478bd9Sstevel@tonic-gate  */
6417c478bd9Sstevel@tonic-gate static void
incoming_prefix_onlink(struct phyint * pi,uchar_t * opt)642d04ccbb3Scarlsonj incoming_prefix_onlink(struct phyint *pi, uchar_t *opt)
6437c478bd9Sstevel@tonic-gate {
6447c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
6457c478bd9Sstevel@tonic-gate 	int plen;
6467c478bd9Sstevel@tonic-gate 	struct prefix *pr;
6477c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
6487c478bd9Sstevel@tonic-gate 	boolean_t found_one = _B_FALSE;
6497c478bd9Sstevel@tonic-gate 
6507c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
6517c478bd9Sstevel@tonic-gate 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
6527c478bd9Sstevel@tonic-gate 		if (pr->pr_prefix_len == plen &&
6537c478bd9Sstevel@tonic-gate 		    prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix, plen)) {
6547c478bd9Sstevel@tonic-gate 			/* Exclude static prefixes */
6557c478bd9Sstevel@tonic-gate 			if (pr->pr_state & PR_STATIC)
6567c478bd9Sstevel@tonic-gate 				continue;
6577c478bd9Sstevel@tonic-gate 			found_one = _B_TRUE;
6587c478bd9Sstevel@tonic-gate 			incoming_prefix_onlink_process(pr, opt);
6597c478bd9Sstevel@tonic-gate 		}
6607c478bd9Sstevel@tonic-gate 	}
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	 * If we have found a matching prefix already or validtime
6657c478bd9Sstevel@tonic-gate 	 * is zero, we have nothing to do.
6667c478bd9Sstevel@tonic-gate 	 */
6677c478bd9Sstevel@tonic-gate 	if (validtime == 0 || found_one)
6687c478bd9Sstevel@tonic-gate 		return;
6697c478bd9Sstevel@tonic-gate 	pr = prefix_create(pi, po->nd_opt_pi_prefix, plen, 0);
6707c478bd9Sstevel@tonic-gate 	if (pr == NULL)
6717c478bd9Sstevel@tonic-gate 		return;
6727c478bd9Sstevel@tonic-gate 	incoming_prefix_onlink_process(pr, opt);
6737c478bd9Sstevel@tonic-gate }
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate void
incoming_prefix_onlink_process(struct prefix * pr,uchar_t * opt)6767c478bd9Sstevel@tonic-gate incoming_prefix_onlink_process(struct prefix *pr, uchar_t *opt)
6777c478bd9Sstevel@tonic-gate {
6787c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
6797c478bd9Sstevel@tonic-gate 	uint32_t validtime;	/* Without 2 hour rule */
6807c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
6817c478bd9Sstevel@tonic-gate 
6827c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
6837c478bd9Sstevel@tonic-gate 	if (validtime != 0)
6847c478bd9Sstevel@tonic-gate 		pr->pr_state |= PR_ONLINK;
6857c478bd9Sstevel@tonic-gate 	else
6867c478bd9Sstevel@tonic-gate 		pr->pr_state &= ~PR_ONLINK;
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	/*
6897c478bd9Sstevel@tonic-gate 	 * Convert from seconds to milliseconds avoiding overflow.
6907c478bd9Sstevel@tonic-gate 	 * If the lifetime in the packet is e.g. PREFIX_INFINITY - 1
6917c478bd9Sstevel@tonic-gate 	 * (4 billion seconds - about 130 years) we will in fact time
6927c478bd9Sstevel@tonic-gate 	 * out the prefix after 4 billion milliseconds - 46 days).
6937c478bd9Sstevel@tonic-gate 	 * Thus the longest lifetime (apart from infinity) is 46 days.
6947c478bd9Sstevel@tonic-gate 	 * Note that this ensures that PREFIX_INFINITY still means "forever".
6957c478bd9Sstevel@tonic-gate 	 */
6967c478bd9Sstevel@tonic-gate 	if (pr->pr_flags & IFF_TEMPORARY) {
6977c478bd9Sstevel@tonic-gate 		pr->pr_OnLinkLifetime = pr->pr_ValidLifetime;
6987c478bd9Sstevel@tonic-gate 	} else {
6997c478bd9Sstevel@tonic-gate 		if (validtime >= PREFIX_INFINITY / MILLISEC)
7007c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = PREFIX_INFINITY - 1;
7017c478bd9Sstevel@tonic-gate 		else
7027c478bd9Sstevel@tonic-gate 			pr->pr_OnLinkLifetime = validtime * MILLISEC;
7037c478bd9Sstevel@tonic-gate 	}
7047c478bd9Sstevel@tonic-gate 	pr->pr_OnLinkFlag = _B_TRUE;
7057c478bd9Sstevel@tonic-gate 	if (debug & (D_PREFIX|D_TMP)) {
7067c478bd9Sstevel@tonic-gate 		logmsg(LOG_DEBUG, "incoming_prefix_onlink_process(%s, %s/%u) "
7077c478bd9Sstevel@tonic-gate 		    "onlink %u state 0x%x, kstate 0x%x\n",
7087c478bd9Sstevel@tonic-gate 		    pr->pr_name, inet_ntop(AF_INET6, (void *)&pr->pr_prefix,
7097c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf)), pr->pr_prefix_len,
7107c478bd9Sstevel@tonic-gate 		    pr->pr_OnLinkLifetime, pr->pr_state, pr->pr_kernel_state);
7117c478bd9Sstevel@tonic-gate 	}
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 	if (pr->pr_kernel_state != pr->pr_state) {
7147c478bd9Sstevel@tonic-gate 		prefix_update_k(pr);
7157c478bd9Sstevel@tonic-gate 	}
7167c478bd9Sstevel@tonic-gate 
7177c478bd9Sstevel@tonic-gate 	if (pr->pr_OnLinkLifetime != 0)
7187c478bd9Sstevel@tonic-gate 		timer_schedule(pr->pr_OnLinkLifetime);
7197c478bd9Sstevel@tonic-gate }
7207c478bd9Sstevel@tonic-gate 
721d04ccbb3Scarlsonj /*
722d04ccbb3Scarlsonj  * Process all prefix options by locating the DHCPv6-configured interfaces, and
723d04ccbb3Scarlsonj  * applying the netmasks as needed.
724d04ccbb3Scarlsonj  */
725d04ccbb3Scarlsonj static void
incoming_prefix_stateful(struct phyint * pi,uchar_t * opt)726d04ccbb3Scarlsonj incoming_prefix_stateful(struct phyint *pi, uchar_t *opt)
727d04ccbb3Scarlsonj {
728d04ccbb3Scarlsonj 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
729d04ccbb3Scarlsonj 	struct prefix *pr;
730d04ccbb3Scarlsonj 	boolean_t foundpref;
731d04ccbb3Scarlsonj 	char abuf[INET6_ADDRSTRLEN];
732d04ccbb3Scarlsonj 
733d04ccbb3Scarlsonj 	/* Make sure it's a valid prefix. */
734d04ccbb3Scarlsonj 	if (ntohl(po->nd_opt_pi_valid_time) == 0) {
735d04ccbb3Scarlsonj 		if (debug & D_DHCP)
736d04ccbb3Scarlsonj 			logmsg(LOG_DEBUG, "incoming_prefix_stateful: ignoring "
737d04ccbb3Scarlsonj 			    "prefix with no valid time\n");
738d04ccbb3Scarlsonj 		return;
739d04ccbb3Scarlsonj 	}
740d04ccbb3Scarlsonj 
741d04ccbb3Scarlsonj 	if (debug & D_DHCP)
742d04ccbb3Scarlsonj 		logmsg(LOG_DEBUG, "incoming_prefix_stateful(%s, %s/%d)\n",
743d04ccbb3Scarlsonj 		    pi->pi_name, inet_ntop(AF_INET6,
744d04ccbb3Scarlsonj 		    (void *)&po->nd_opt_pi_prefix, abuf, sizeof (abuf)),
745d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len);
746d04ccbb3Scarlsonj 	foundpref = _B_FALSE;
747d04ccbb3Scarlsonj 	for (pr = pi->pi_prefix_list; pr != NULL; pr = pr->pr_next) {
7480ea48bbfSRamesh Kumar Katla 		if (prefix_equal(po->nd_opt_pi_prefix, pr->pr_prefix,
749d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len)) {
750d04ccbb3Scarlsonj 			if ((pr->pr_flags & IFF_DHCPRUNNING) &&
751d04ccbb3Scarlsonj 			    pr->pr_prefix_len != po->nd_opt_pi_prefix_len) {
752d04ccbb3Scarlsonj 				pr->pr_prefix_len = po->nd_opt_pi_prefix_len;
753d04ccbb3Scarlsonj 				if (pr->pr_flags & IFF_UP) {
754d04ccbb3Scarlsonj 					if (debug & D_DHCP)
755d04ccbb3Scarlsonj 						logmsg(LOG_DEBUG,
756d04ccbb3Scarlsonj 						    "incoming_prefix_stateful:"
757d04ccbb3Scarlsonj 						    " set mask on DHCP %s\n",
758d04ccbb3Scarlsonj 						    pr->pr_name);
759d04ccbb3Scarlsonj 					prefix_update_dhcp(pr);
760d04ccbb3Scarlsonj 				}
761d04ccbb3Scarlsonj 			}
762d04ccbb3Scarlsonj 			if (pr->pr_prefix_len == po->nd_opt_pi_prefix_len &&
763d04ccbb3Scarlsonj 			    (!(pr->pr_state & PR_STATIC) ||
764d04ccbb3Scarlsonj 			    (pr->pr_flags & IFF_DHCPRUNNING)))
765d04ccbb3Scarlsonj 				foundpref = _B_TRUE;
766d04ccbb3Scarlsonj 		}
767d04ccbb3Scarlsonj 	}
768d04ccbb3Scarlsonj 	/*
769d04ccbb3Scarlsonj 	 * If there's no matching DHCPv6 prefix present, then create an empty
770d04ccbb3Scarlsonj 	 * one so that we'll be able to configure it later.
771d04ccbb3Scarlsonj 	 */
772d04ccbb3Scarlsonj 	if (!foundpref) {
773d04ccbb3Scarlsonj 		pr = prefix_create(pi, po->nd_opt_pi_prefix,
774d04ccbb3Scarlsonj 		    po->nd_opt_pi_prefix_len, IFF_DHCPRUNNING);
775d04ccbb3Scarlsonj 		if (pr != NULL) {
776d04ccbb3Scarlsonj 			pr->pr_state = PR_STATIC;
777d04ccbb3Scarlsonj 			if (debug & D_DHCP)
778d04ccbb3Scarlsonj 				logmsg(LOG_DEBUG,
779d04ccbb3Scarlsonj 				    "incoming_prefix_stateful: created dummy "
780d04ccbb3Scarlsonj 				    "prefix for later\n");
781d04ccbb3Scarlsonj 		}
782d04ccbb3Scarlsonj 	}
783d04ccbb3Scarlsonj }
784d04ccbb3Scarlsonj 
7857c478bd9Sstevel@tonic-gate /*
7867c478bd9Sstevel@tonic-gate  * Process prefix options with the autonomous flag set.
7877c478bd9Sstevel@tonic-gate  * Returns false if this prefix results in a bad address (duplicate)
7887c478bd9Sstevel@tonic-gate  * This function needs to loop to find the same prefix multiple times
7897c478bd9Sstevel@tonic-gate  * as if a failover happened earlier, the addresses belonging to
7907c478bd9Sstevel@tonic-gate  * a different interface may be found here on this interface.
7917c478bd9Sstevel@tonic-gate  */
7927c478bd9Sstevel@tonic-gate static boolean_t
incoming_prefix_addrconf(struct phyint * pi,uchar_t * opt,struct sockaddr_in6 * from,boolean_t loopback)7937c478bd9Sstevel@tonic-gate incoming_prefix_addrconf(struct phyint *pi, uchar_t *opt,
7947c478bd9Sstevel@tonic-gate     struct sockaddr_in6 *from, boolean_t loopback)
7957c478bd9Sstevel@tonic-gate {
7967c478bd9Sstevel@tonic-gate 	struct nd_opt_prefix_info *po = (struct nd_opt_prefix_info *)opt;
7977c478bd9Sstevel@tonic-gate 	int plen;
7987c478bd9Sstevel@tonic-gate 	struct prefix *pr;
7997c478bd9Sstevel@tonic-gate 	uint32_t validtime, preftime;	/* In seconds */
8007c478bd9Sstevel@tonic-gate 	char abuf[INET6_ADDRSTRLEN];
8017c478bd9Sstevel@tonic-gate 	char pbuf[INET6_ADDRSTRLEN];
8027c478bd9Sstevel@tonic-gate 	boolean_t found_pub = _B_FALSE;
8037c478bd9Sstevel@tonic-gate 	boolean_t found_tmp = _B_FALSE;
8047c478bd9Sstevel@tonic-gate 	boolean_t ret;
8057c478bd9Sstevel@tonic-gate 
8067c478bd9Sstevel@tonic-gate 	validtime = ntohl(po->nd_opt_pi_valid_time);
8077c478bd9Sstevel@tonic-gate 	preftime = ntohl(po->nd_opt_pi_preferred_time);
8087c478bd9Sstevel@tonic-gate 	plen = po->nd_opt_pi_prefix_len;
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 	/* Sanity checks */
8117c478bd9Sstevel@tonic-gate 	if (validtime < preftime) {
8127c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6, (void *)&from->sin6_addr,
8137c478bd9Sstevel@tonic-gate 		    abuf, sizeof (abuf));
8147c478bd9Sstevel@tonic-gate 		(void) inet_ntop(AF_INET6,
8157c478bd9Sstevel@tonic-gate 		    (void *)&po->nd_opt_pi_prefix,
8167c478bd9Sstevel@tonic-gate 		    pbuf, sizeof (pbuf));
8177c478bd9Sstevel@tonic-gate 		logmsg(LOG_WARNING, "prefix option %s/%u from %s on %s: "
8187c478bd9Sstevel@tonic-gate 		    "valid %u < pref %u ignored\n",
8197c478bd9Sstevel@tonic-gate 		    pbuf, plen, abuf, pi->pi_name,
8207c478bd9Sstevel@tonic-gate 		    validtime, preftime);
8217c478bd9Sstevel@tonic-gate 		return (_B_FALSE);
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate 
824