6201425 setting IPV6_UNICAST_HOPS on SCTP socket doesn't do anything
6201431 IPV6_UNICAST_HOPS steps on IPV6_MULTICAST_HOPS' toes
6290936 Solaris 10 IPv6 traceroute does not increment the Hop limit
6292943 IPV6_HOPLIMIT is not a socket option
6327929 CurHopLimit in IPv6 Router Advertisement shouldn't affect multicast packets
diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h
index c499914..9caf225 100644
--- a/usr/src/uts/common/inet/ip.h
+++ b/usr/src/uts/common/inet/ip.h
@@ -2393,8 +2393,9 @@
 	uint_t		ipp_sticky_ignored;	/* sticky fields to ignore */
 	uint_t		ipp_ifindex;		/* pktinfo ifindex */
 	in6_addr_t	ipp_addr;		/* pktinfo src/dst addr */
-	uint_t		ipp_hoplimit;
-	uint_t		ipp_multi_hoplimit;
+	uint_t		ipp_unicast_hops;	/* IPV6_UNICAST_HOPS */
+	uint_t		ipp_multicast_hops;	/* IPV6_MULTICAST_HOPS */
+	uint_t		ipp_hoplimit;		/* IPV6_HOPLIMIT */
 	uint_t		ipp_hopoptslen;
 	uint_t		ipp_rtdstoptslen;
 	uint_t		ipp_rthdrlen;
@@ -2448,13 +2449,14 @@
 
 #define	IPPF_TCLASS	0x1000
 #define	IPPF_DONTFRAG	0x2000
-#define	IPPF_USE_MIN_MTU	0x4000
-#define	IPPF_MULTI_HOPLIMIT	0x8000
+#define	IPPF_USE_MIN_MTU	0x04000
+#define	IPPF_MULTICAST_HOPS	0x08000
+#define	IPPF_UNICAST_HOPS	0x10000
 
 #define	IPPF_HAS_IP6I \
 	(IPPF_IFINDEX|IPPF_ADDR|IPPF_NEXTHOP|IPPF_SCOPE_ID| \
 	IPPF_NO_CKSUM|IPPF_RAW_CKSUM|IPPF_HOPLIMIT|IPPF_DONTFRAG| \
-	IPPF_USE_MIN_MTU|IPPF_MULTI_HOPLIMIT)
+	IPPF_USE_MIN_MTU|IPPF_MULTICAST_HOPS|IPPF_UNICAST_HOPS)
 
 #define	TCP_PORTS_OFFSET	0
 #define	UDP_PORTS_OFFSET	0
diff --git a/usr/src/uts/common/inet/ip/icmp.c b/usr/src/uts/common/inet/ip/icmp.c
index 6783ee6..9c3863b 100644
--- a/usr/src/uts/common/inet/ip/icmp.c
+++ b/usr/src/uts/common/inet/ip/icmp.c
@@ -1694,12 +1694,6 @@
 				pkti->ipi6_addr = ipv6_all_zeros;
 			return (sizeof (struct in6_pktinfo));
 		}
-		case IPV6_HOPLIMIT:
-			if (ipp->ipp_fields & IPPF_HOPLIMIT)
-				*i1 = ipp->ipp_hoplimit;
-			else
-				*i1 = -1; /* Not set */
-			break;
 		case IPV6_NEXTHOP: {
 			sin6_t *sin6 = (sin6_t *)ptr;
 
@@ -2130,15 +2124,15 @@
 			}
 			if (!checkonly) {
 				if (*i1 == -1) {
-					icmp->icmp_ttl = ipp->ipp_hoplimit =
+					icmp->icmp_ttl = ipp->ipp_unicast_hops =
 					    icmp_ipv6_hoplimit;
-					ipp->ipp_fields &= ~IPPF_HOPLIMIT;
+					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
 					/* Pass modified value to IP. */
 					*i1 = ipp->ipp_hoplimit;
 				} else {
-					icmp->icmp_ttl = ipp->ipp_hoplimit =
+					icmp->icmp_ttl = ipp->ipp_unicast_hops =
 					    (uint8_t)*i1;
-					ipp->ipp_fields |= IPPF_HOPLIMIT;
+					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
 				}
 				/* Rebuild the header template */
 				error = icmp_build_hdrs(q, icmp);
@@ -2157,22 +2151,16 @@
 			if (!checkonly) {
 				if (*i1 == -1) {
 					icmp->icmp_multicast_ttl =
-					    ipp->ipp_multi_hoplimit =
+					    ipp->ipp_multicast_hops =
 					    IP_DEFAULT_MULTICAST_TTL;
-					ipp->ipp_fields &= ~IPPF_MULTI_HOPLIMIT;
+					ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS;
 					/* Pass modified value to IP. */
-					*i1 = ipp->ipp_multi_hoplimit;
+					*i1 = icmp->icmp_multicast_ttl;
 				} else {
 					icmp->icmp_multicast_ttl =
-					    ipp->ipp_multi_hoplimit =
+					    ipp->ipp_multicast_hops =
 					    (uint8_t)*i1;
-					ipp->ipp_fields |= IPPF_MULTI_HOPLIMIT;
-				}
-				/* Rebuild the header template */
-				error = icmp_build_hdrs(q, icmp);
-				if (error != 0) {
-					*outlenp = 0;
-					return (error);
+					ipp->ipp_fields |= IPPF_MULTICAST_HOPS;
 				}
 			}
 			break;
@@ -2322,6 +2310,9 @@
 			}
 			break;
 		case IPV6_HOPLIMIT:
+			/* This option can only be used as ancillary data. */
+			if (sticky)
+				return (EINVAL);
 			if (inlen != 0 && inlen != sizeof (int))
 				return (EINVAL);
 			if (checkonly)
@@ -2339,11 +2330,6 @@
 					ipp->ipp_hoplimit = *i1;
 				ipp->ipp_fields |= IPPF_HOPLIMIT;
 			}
-			if (sticky) {
-				error = icmp_build_hdrs(q, icmp);
-				if (error != 0)
-					return (error);
-			}
 			break;
 		case IPV6_TCLASS:
 			/*
@@ -2727,15 +2713,6 @@
 	if (!(ipp->ipp_fields & IPPF_ADDR))
 		ip6h->ip6_src = icmp->icmp_v6src;
 
-	/*
-	 * If IPV6_HOPLIMIT was set in ipp, use that value.
-	 * For sticky options, if it does not exist use
-	 * the value in the icmp structure.
-	 * All this as per RFC 2922.
-	 */
-	if (!(ipp->ipp_fields & IPPF_HOPLIMIT))
-		ip6h->ip6_hops = icmp->icmp_ttl;
-
 	/* Try to get everything in a single mblk */
 	if (hdrs_len > icmp->icmp_max_hdr_len) {
 		icmp->icmp_max_hdr_len = hdrs_len;
@@ -4247,13 +4224,21 @@
 		}
 	}
 
-	if (!(ignore & IPPF_HOPLIMIT)) {
-		if (ipp->ipp_fields & IPPF_HOPLIMIT) {
-			option_exists |= IPPF_HOPLIMIT;
-		} else if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT) {
-			option_exists |= IPPF_HOPLIMIT;
-			is_sticky |= IPPF_HOPLIMIT;
-		}
+	if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT))
+		option_exists |= IPPF_HOPLIMIT;
+	/* IPV6_HOPLIMIT can never be sticky */
+	ASSERT(!(icmp->icmp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT));
+
+	if (!(ignore & IPPF_UNICAST_HOPS) &&
+	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) {
+		option_exists |= IPPF_UNICAST_HOPS;
+		is_sticky |= IPPF_UNICAST_HOPS;
+	}
+
+	if (!(ignore & IPPF_MULTICAST_HOPS) &&
+	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) {
+		option_exists |= IPPF_MULTICAST_HOPS;
+		is_sticky |= IPPF_MULTICAST_HOPS;
 	}
 
 	if (icmp->icmp_sticky_ipp.ipp_fields & IPPF_NO_CKSUM) {
@@ -4373,15 +4358,19 @@
 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
 
+	/* Set the hoplimit of the outgoing packet. */
 	if (option_exists & IPPF_HOPLIMIT) {
-		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPLIMIT);
-		ip6h->ip6_hops = tipp->ipp_hoplimit;
-	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
-	    (icmp->icmp_sticky_ipp.ipp_fields & IPPF_MULTI_HOPLIMIT)) {
-		ip6h->ip6_hops = icmp->icmp_multicast_ttl;
+		/* IPV6_HOPLIMIT ancillary data overrides all other settings. */
+		ip6h->ip6_hops = ipp->ipp_hoplimit;
 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
+	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+		ip6h->ip6_hops = icmp->icmp_multicast_ttl;
+		if (option_exists & IPPF_MULTICAST_HOPS)
+			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
 	} else {
 		ip6h->ip6_hops = icmp->icmp_ttl;
+		if (option_exists & IPPF_UNICAST_HOPS)
+			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
 	}
 
 	if (option_exists & IPPF_ADDR) {
diff --git a/usr/src/uts/common/inet/ip/ip6.c b/usr/src/uts/common/inet/ip/ip6.c
index 2f84c39..d701d17 100644
--- a/usr/src/uts/common/inet/ip/ip6.c
+++ b/usr/src/uts/common/inet/ip/ip6.c
@@ -10263,12 +10263,14 @@
 
 	/*
 	 * If the sender didn't supply the hop limit and there is a default
-	 * hop limit associated with the output interface, we use that.
-	 * Interface specific hop limits as set via the SIOCSLIFLNKINFO
-	 * ioctl.
+	 * unicast hop limit associated with the output interface, we use
+	 * that if the packet is unicast.  Interface specific unicast hop
+	 * limits as set via the SIOCSLIFLNKINFO ioctl.
 	 */
-	if (!(flags & IP6I_HOPLIMIT) && ill->ill_max_hops != 0)
+	if (ill->ill_max_hops != 0 && !(flags & IP6I_HOPLIMIT) &&
+	    !(IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) {
 		ip6h->ip6_hops = ill->ill_max_hops;
+	}
 
 	if (ire->ire_type == IRE_LOCAL && ire->ire_zoneid != zoneid) {
 		/*
@@ -11724,8 +11726,8 @@
 			    &ipp->ipp_addr));
 			ip6i->ip6i_flags |= IP6I_VERIFY_SRC;
 		}
-		if (ipp->ipp_fields & IPPF_HOPLIMIT) {
-			ip6i->ip6i_hops = ip6h->ip6_hops = ipp->ipp_hoplimit;
+		if (ipp->ipp_fields & IPPF_UNICAST_HOPS) {
+			ip6h->ip6_hops = ipp->ipp_unicast_hops;
 			/*
 			 * We need to set this flag so that IP doesn't
 			 * rewrite the IPv6 header's hoplimit with the
diff --git a/usr/src/uts/common/inet/sctp/sctp_common.c b/usr/src/uts/common/inet/sctp/sctp_common.c
index d8fcc22..7b1a73c 100644
--- a/usr/src/uts/common/inet/sctp/sctp_common.c
+++ b/usr/src/uts/common/inet/sctp/sctp_common.c
@@ -422,6 +422,12 @@
 		iph->ipha_length = htons(sum);
 	} else {
 		ip6h = (ip6_t *)mp->b_rptr;
+		/*
+		 * If an ip6i_t is present, the real IPv6 header
+		 * immediately follows.
+		 */
+		if (ip6h->ip6_nxt == IPPROTO_RAW)
+			ip6h = (ip6_t *)&ip6h[1];
 		ip6h->ip6_plen = htons(sum - ((char *)&sctp->sctp_ip6h[1] -
 		    sctp->sctp_iphc6));
 	}
@@ -904,14 +910,12 @@
 	ip6_pkt_t	*ipp = &sctp->sctp_sticky_ipp;
 	in6_addr_t	src;
 	in6_addr_t	dst;
-	uint8_t		hoplimit;
 	/*
 	 * save the existing sctp header and source/dest IP addresses
 	 */
 	bcopy(sctp->sctp_sctph6, buf, sizeof (sctp_hdr_t));
 	src = sctp->sctp_ip6h->ip6_src;
 	dst = sctp->sctp_ip6h->ip6_dst;
-	hoplimit = sctp->sctp_ip6h->ip6_hops;
 	hdrs_len = ip_total_hdrs_len_v6(ipp) + SCTP_MAX_HDR_LENGTH;
 	ASSERT(hdrs_len != 0);
 	if (hdrs_len > sctp->sctp_iphc6_len) {
@@ -948,20 +952,11 @@
 	sctp->sctp_ip6h->ip6_src = src;
 	sctp->sctp_ip6h->ip6_dst = dst;
 	/*
-	 * If IPV6_HOPLIMIT was set in ipp, use that value.
-	 * For sticky options, if it does not exist use
-	 * the default/saved value (which was set in ip_build_hdrs_v6())
-	 * All this as per RFC 2922.
+	 * If the hoplimit was not set by ip_build_hdrs_v6(), we need to
+	 * set it to the default value for SCTP.
 	 */
-	if (!(ipp->ipp_fields & IPPF_HOPLIMIT))
-		sctp->sctp_ip6h->ip6_hops = hoplimit;
-	/*
-	 * Set the IPv6 header payload length.
-	 * If there's an ip6i_t included, don't count it in the length.
-	 */
-	sctp->sctp_ip6h->ip6_plen = sctp->sctp_hdr6_len - IPV6_HDR_LEN;
-	if (ipp->ipp_fields & IPPF_HAS_IP6I)
-		sctp->sctp_ip6h->ip6_plen -= sizeof (ip6i_t);
+	if (!(ipp->ipp_fields & IPPF_UNICAST_HOPS))
+		sctp->sctp_ip6h->ip6_hops = sctp_ipv6_hoplimit;
 	/*
 	 * If we're setting extension headers after a connection
 	 * has been established, and if we have a routing header
diff --git a/usr/src/uts/common/inet/sctp/sctp_opt_data.c b/usr/src/uts/common/inet/sctp/sctp_opt_data.c
index a335a90..b283ab3 100644
--- a/usr/src/uts/common/inet/sctp/sctp_opt_data.c
+++ b/usr/src/uts/common/inet/sctp/sctp_opt_data.c
@@ -1122,12 +1122,6 @@
 			*optlen = sizeof (struct in6_pktinfo);
 			break;
 		}
-		case IPV6_HOPLIMIT:
-			if (ipp->ipp_fields & IPPF_HOPLIMIT)
-				*i1 = ipp->ipp_hoplimit;
-			else
-				*i1 = -1; /* Not set */
-			break;	/* goto sizeof (int) option return */
 		case IPV6_NEXTHOP: {
 			sin6_t *sin6;
 
@@ -1515,10 +1509,14 @@
 				retval = EINVAL;
 				break;
 			}
-			if (*i1 == -1)
-				sctp->sctp_ip6h->ip6_hops = sctp_ipv6_hoplimit;
-			else
-				sctp->sctp_ip6h->ip6_hops = (uint8_t)*i1;
+			if (*i1 == -1) {
+				ipp->ipp_unicast_hops = sctp_ipv6_hoplimit;
+				ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
+			} else {
+				ipp->ipp_unicast_hops = (uint8_t)*i1;
+				ipp->ipp_fields |= IPPF_UNICAST_HOPS;
+			}
+			retval = sctp_build_hdrs(sctp);
 			break;
 		case IPV6_UNSPEC_SRC:
 			if (inlen < sizeof (int32_t)) {
@@ -1635,26 +1633,6 @@
 			}
 			retval = sctp_build_hdrs(sctp);
 			break;
-		case IPV6_HOPLIMIT:
-			if (inlen != 0 && inlen != sizeof (int)) {
-				retval = EINVAL;
-				break;
-			}
-			if (inlen == 0) {
-				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
-			} else {
-				if (*i1 > 255 || *i1 < -1) {
-					retval = EINVAL;
-					break;
-				}
-				if (*i1 == -1)
-					ipp->ipp_hoplimit = sctp_ipv6_hoplimit;
-				else
-					ipp->ipp_hoplimit = *i1;
-				ipp->ipp_fields |= IPPF_HOPLIMIT;
-			}
-			retval = sctp_build_hdrs(sctp);
-			break;
 		case IPV6_NEXTHOP: {
 			struct sockaddr_in6 *sin6;
 
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index 7ed6170..8c651d1 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -10220,12 +10220,6 @@
 				pkti->ipi6_addr = ipv6_all_zeros;
 			return (sizeof (struct in6_pktinfo));
 		}
-		case IPV6_HOPLIMIT:
-			if (ipp->ipp_fields & IPPF_HOPLIMIT)
-				*i1 = ipp->ipp_hoplimit;
-			else
-				*i1 = -1; /* Not set */
-			break;	/* goto sizeof (int) option return */
 		case IPV6_TCLASS:
 			if (ipp->ipp_fields & IPPF_TCLASS)
 				*i1 = ipp->ipp_tclass;
@@ -10722,16 +10716,20 @@
 			if (!checkonly) {
 				if (*i1 == -1) {
 					tcp->tcp_ip6h->ip6_hops =
-					    ipp->ipp_hoplimit =
+					    ipp->ipp_unicast_hops =
 					    (uint8_t)tcp_ipv6_hoplimit;
-					ipp->ipp_fields &= ~IPPF_HOPLIMIT;
+					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
 					/* Pass modified value to IP. */
 					*i1 = tcp->tcp_ip6h->ip6_hops;
 				} else {
 					tcp->tcp_ip6h->ip6_hops =
-					    ipp->ipp_hoplimit = (uint8_t)*i1;
-					ipp->ipp_fields |= IPPF_HOPLIMIT;
+					    ipp->ipp_unicast_hops =
+					    (uint8_t)*i1;
+					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
 				}
+				reterr = tcp_build_hdrs(q, tcp);
+				if (reterr != 0)
+					return (reterr);
 			}
 			break;
 		case IPV6_BOUND_IF:
@@ -10877,33 +10875,6 @@
 			if (reterr != 0)
 				return (reterr);
 			break;
-		case IPV6_HOPLIMIT:
-			if (inlen != 0 && inlen != sizeof (int))
-				return (EINVAL);
-			if (checkonly)
-				break;
-
-			if (inlen == 0) {
-				ipp->ipp_fields &= ~IPPF_HOPLIMIT;
-				tcp->tcp_ip6_hops =
-				    (uint8_t)tcp_ipv6_hoplimit;
-			} else {
-				if (*i1 > 255 || *i1 < -1)
-					return (EINVAL);
-				if (*i1 == -1) {
-					ipp->ipp_hoplimit = tcp_ipv6_hoplimit;
-					*i1 = tcp_ipv6_hoplimit;
-				} else {
-					ipp->ipp_hoplimit = *i1;
-				}
-				ipp->ipp_fields |= IPPF_HOPLIMIT;
-				tcp->tcp_ip6_hops =
-				    ipp->ipp_hoplimit;
-			}
-			reterr = tcp_build_hdrs(q, tcp);
-			if (reterr != 0)
-				return (reterr);
-			break;
 		case IPV6_TCLASS:
 			if (inlen != 0 && inlen != sizeof (int))
 				return (EINVAL);
@@ -11175,7 +11146,6 @@
 	char	buf[TCP_MAX_HDR_LENGTH];
 	ip6_pkt_t *ipp = &tcp->tcp_sticky_ipp;
 	in6_addr_t src, dst;
-	uint8_t hops;
 
 	/*
 	 * save the existing tcp header and source/dest IP addresses
@@ -11183,7 +11153,6 @@
 	bcopy(tcp->tcp_tcph, buf, tcp->tcp_tcp_hdr_len);
 	src = tcp->tcp_ip6h->ip6_src;
 	dst = tcp->tcp_ip6h->ip6_dst;
-	hops = tcp->tcp_ip6h->ip6_hops;
 	hdrs_len = ip_total_hdrs_len_v6(ipp) + TCP_MAX_HDR_LENGTH;
 	ASSERT(hdrs_len != 0);
 	if (hdrs_len > tcp->tcp_iphc_len) {
@@ -11230,20 +11199,13 @@
 	tcp->tcp_ip6h->ip6_dst = dst;
 
 	/*
-	 * If the hop limit was not set by ip_build_hdrs_v6(), restore
-	 * the saved value.
+	 * If the hop limit was not set by ip_build_hdrs_v6(), set it to
+	 * the default value for TCP.
 	 */
-	if (!(ipp->ipp_fields & IPPF_HOPLIMIT))
-		tcp->tcp_ip6h->ip6_hops = hops;
+	if (!(ipp->ipp_fields & IPPF_UNICAST_HOPS))
+		tcp->tcp_ip6h->ip6_hops = tcp_ipv6_hoplimit;
 
 	/*
-	 * Set the IPv6 header payload length.
-	 * If there's an ip6i_t included, don't count it in the length.
-	 */
-	tcp->tcp_ip6h->ip6_plen = tcp->tcp_hdr_len - IPV6_HDR_LEN;
-	if (ipp->ipp_fields & IPPF_HAS_IP6I)
-		tcp->tcp_ip6h->ip6_plen -= sizeof (ip6i_t);
-	/*
 	 * If we're setting extension headers after a connection
 	 * has been established, and if we have a routing header
 	 * among the extension headers, call ip_massage_options_v6 to
diff --git a/usr/src/uts/common/inet/tcp/tcp_opt_data.c b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
index 2f1d9d3..d0ab3d8 100644
--- a/usr/src/uts/common/inet/tcp/tcp_opt_data.c
+++ b/usr/src/uts/common/inet/tcp/tcp_opt_data.c
@@ -149,9 +149,6 @@
 { IPV6_PKTINFO, IPPROTO_IPV6, OA_RW, OA_RW, OP_NP,
 	(OP_PASSNEXT|OP_NODEFAULT|OP_VARLEN),
 	sizeof (struct in6_pktinfo), -1 /* not initialized */ },
-{ IPV6_HOPLIMIT, IPPROTO_IPV6, OA_RW, OA_RW, OP_NP,
-	(OP_PASSNEXT|OP_NODEFAULT),
-	sizeof (int), -1 /* not initialized */ },
 { IPV6_NEXTHOP, IPPROTO_IPV6, OA_RW, OA_RW, OP_NP,
 	(OP_PASSNEXT|OP_NODEFAULT),
 	sizeof (sin6_t), -1 /* not initialized */ },
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index 9f31e3a..5bed5bf 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -2455,12 +2455,6 @@
 				pkti->ipi6_addr = ipv6_all_zeros;
 			return (sizeof (struct in6_pktinfo));
 		}
-		case IPV6_HOPLIMIT:
-			if (ipp->ipp_fields & IPPF_HOPLIMIT)
-				*i1 = ipp->ipp_hoplimit;
-			else
-				*i1 = -1; /* Not set */
-			break;	/* goto sizeof (int) option return */
 		case IPV6_TCLASS:
 			if (ipp->ipp_fields & IPPF_TCLASS)
 				*i1 = ipp->ipp_tclass;
@@ -2807,15 +2801,15 @@
 			}
 			if (!checkonly) {
 				if (*i1 == -1) {
-					udp->udp_ttl = ipp->ipp_hoplimit =
+					udp->udp_ttl = ipp->ipp_unicast_hops =
 					    udp_ipv6_hoplimit;
-					ipp->ipp_fields &= ~IPPF_HOPLIMIT;
+					ipp->ipp_fields &= ~IPPF_UNICAST_HOPS;
 					/* Pass modified value to IP. */
 					*i1 = udp->udp_ttl;
 				} else {
-					udp->udp_ttl = ipp->ipp_hoplimit =
+					udp->udp_ttl = ipp->ipp_unicast_hops =
 					    (uint8_t)*i1;
-					ipp->ipp_fields |= IPPF_HOPLIMIT;
+					ipp->ipp_fields |= IPPF_UNICAST_HOPS;
 				}
 				/* Rebuild the header template */
 				error = udp_build_hdrs(q, udp);
@@ -2834,22 +2828,16 @@
 			if (!checkonly) {
 				if (*i1 == -1) {
 					udp->udp_multicast_ttl =
-					    ipp->ipp_multi_hoplimit =
+					    ipp->ipp_multicast_hops =
 					    IP_DEFAULT_MULTICAST_TTL;
-					ipp->ipp_fields &= ~IPPF_MULTI_HOPLIMIT;
+					ipp->ipp_fields &= ~IPPF_MULTICAST_HOPS;
 					/* Pass modified value to IP. */
 					*i1 = udp->udp_multicast_ttl;
 				} else {
 					udp->udp_multicast_ttl =
-					    ipp->ipp_multi_hoplimit =
+					    ipp->ipp_multicast_hops =
 					    (uint8_t)*i1;
-					ipp->ipp_fields |= IPPF_MULTI_HOPLIMIT;
-				}
-				/* Rebuild the header template */
-				error = udp_build_hdrs(q, udp);
-				if (error != 0) {
-					*outlenp = 0;
-					return (error);
+					ipp->ipp_fields |= IPPF_MULTICAST_HOPS;
 				}
 			}
 			break;
@@ -2967,6 +2955,8 @@
 			}
 			break;
 		case IPV6_HOPLIMIT:
+			if (sticky)
+				return (EINVAL);
 			if (inlen != 0 && inlen != sizeof (int))
 				return (EINVAL);
 			if (checkonly)
@@ -2984,11 +2974,6 @@
 					ipp->ipp_hoplimit = *i1;
 				ipp->ipp_fields |= IPPF_HOPLIMIT;
 			}
-			if (sticky) {
-				error = udp_build_hdrs(q, udp);
-				if (error != 0)
-					return (error);
-			}
 			break;
 		case IPV6_TCLASS:
 			if (inlen != 0 && inlen != sizeof (int))
@@ -3326,15 +3311,6 @@
 	if (!(ipp->ipp_fields & IPPF_ADDR))
 		ip6h->ip6_src = udp->udp_v6src;
 
-	/*
-	 * If IPV6_HOPLIMIT was set in ipp, use that value.
-	 * For sticky options, if it does not exist use
-	 * the value in the udp structure.
-	 * All this as per RFC 2922.
-	 */
-	if (!(ipp->ipp_fields & IPPF_HOPLIMIT))
-		ip6h->ip6_hops = udp->udp_ttl;
-
 	udpha = (udpha_t *)(udp->udp_sticky_hdrs + hdrs_len - UDPH_SIZE);
 	udpha->uha_src_port = udp->udp_port;
 
@@ -5505,13 +5481,21 @@
 		}
 	}
 
-	if (!(ignore & IPPF_HOPLIMIT)) {
-		if (ipp->ipp_fields & IPPF_HOPLIMIT) {
-			option_exists |= IPPF_HOPLIMIT;
-		} else if (udp->udp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT) {
-			option_exists |= IPPF_HOPLIMIT;
-			is_sticky |= IPPF_HOPLIMIT;
-		}
+	if (!(ignore & IPPF_HOPLIMIT) && (ipp->ipp_fields & IPPF_HOPLIMIT))
+		option_exists |= IPPF_HOPLIMIT;
+	/* IPV6_HOPLIMIT can never be sticky */
+	ASSERT(!(udp->udp_sticky_ipp.ipp_fields & IPPF_HOPLIMIT));
+
+	if (!(ignore & IPPF_UNICAST_HOPS) &&
+	    (udp->udp_sticky_ipp.ipp_fields & IPPF_UNICAST_HOPS)) {
+		option_exists |= IPPF_UNICAST_HOPS;
+		is_sticky |= IPPF_UNICAST_HOPS;
+	}
+
+	if (!(ignore & IPPF_MULTICAST_HOPS) &&
+	    (udp->udp_sticky_ipp.ipp_fields & IPPF_MULTICAST_HOPS)) {
+		option_exists |= IPPF_MULTICAST_HOPS;
+		is_sticky |= IPPF_MULTICAST_HOPS;
 	}
 
 	if (!(ignore & IPPF_TCLASS)) {
@@ -5611,15 +5595,19 @@
 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
 	bzero(&ip6h->ip6_src, sizeof (ip6h->ip6_src));
 
+	/* Set the hoplimit of the outgoing packet. */
 	if (option_exists & IPPF_HOPLIMIT) {
-		tipp = ANCIL_OR_STICKY_PTR(IPPF_HOPLIMIT);
-		ip6h->ip6_hops = tipp->ipp_hoplimit;
-	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr) &&
-	    (udp->udp_sticky_ipp.ipp_fields & IPPF_MULTI_HOPLIMIT)) {
-		ip6h->ip6_hops = udp->udp_multicast_ttl;
+		/* IPV6_HOPLIMIT ancillary data overrides all other settings. */
+		ip6h->ip6_hops = ipp->ipp_hoplimit;
 		ip6i->ip6i_flags |= IP6I_HOPLIMIT;
+	} else if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+		ip6h->ip6_hops = udp->udp_multicast_ttl;
+		if (option_exists & IPPF_MULTICAST_HOPS)
+			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
 	} else {
 		ip6h->ip6_hops = udp->udp_ttl;
+		if (option_exists & IPPF_UNICAST_HOPS)
+			ip6i->ip6i_flags |= IP6I_HOPLIMIT;
 	}
 
 	if (option_exists & IPPF_ADDR) {