xref: /titanic_50/usr/src/uts/common/inet/ip/ip6.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
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
595579064Sja97890  * Common Development and Distribution License (the "License").
695579064Sja97890  * 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  */
217c478bd9Sstevel@tonic-gate /*
2253287767SSowmini Varadhan  * Copyright (c) 1991, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  * Copyright (c) 1990 Mentat Inc.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <sys/types.h>
277c478bd9Sstevel@tonic-gate #include <sys/stream.h>
287c478bd9Sstevel@tonic-gate #include <sys/dlpi.h>
297c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
327c478bd9Sstevel@tonic-gate #include <sys/strlog.h>
337c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
347c478bd9Sstevel@tonic-gate #define	_SUN_TPI_VERSION	2
357c478bd9Sstevel@tonic-gate #include <sys/tihdr.h>
367c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
377c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
387c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
397c478bd9Sstevel@tonic-gate #include <sys/debug.h>
40381a2a9aSdr146992 #include <sys/sdt.h>
417c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
427c478bd9Sstevel@tonic-gate #include <sys/zone.h>
43381a2a9aSdr146992 #include <sys/neti.h>
44381a2a9aSdr146992 #include <sys/hook.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
477c478bd9Sstevel@tonic-gate #include <sys/systm.h>
487c478bd9Sstevel@tonic-gate #include <sys/param.h>
497c478bd9Sstevel@tonic-gate #include <sys/socket.h>
507c478bd9Sstevel@tonic-gate #include <sys/vtrace.h>
517c478bd9Sstevel@tonic-gate #include <sys/isa_defs.h>
527c478bd9Sstevel@tonic-gate #include <sys/atomic.h>
537c478bd9Sstevel@tonic-gate #include <sys/policy.h>
54bd670b35SErik Nordmark #include <sys/mac.h>
557c478bd9Sstevel@tonic-gate #include <net/if.h>
56ff550d0eSmasputra #include <net/if_types.h>
577c478bd9Sstevel@tonic-gate #include <net/route.h>
587c478bd9Sstevel@tonic-gate #include <net/if_dl.h>
597c478bd9Sstevel@tonic-gate #include <sys/sockio.h>
607c478bd9Sstevel@tonic-gate #include <netinet/in.h>
617c478bd9Sstevel@tonic-gate #include <netinet/ip6.h>
627c478bd9Sstevel@tonic-gate #include <netinet/icmp6.h>
637c478bd9Sstevel@tonic-gate #include <netinet/sctp.h>
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate #include <inet/common.h>
667c478bd9Sstevel@tonic-gate #include <inet/mi.h>
67fc80c0dfSnordmark #include <inet/optcom.h>
687c478bd9Sstevel@tonic-gate #include <inet/mib2.h>
697c478bd9Sstevel@tonic-gate #include <inet/nd.h>
707c478bd9Sstevel@tonic-gate #include <inet/arp.h>
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate #include <inet/ip.h>
73ff550d0eSmasputra #include <inet/ip_impl.h>
747c478bd9Sstevel@tonic-gate #include <inet/ip6.h>
757c478bd9Sstevel@tonic-gate #include <inet/ip6_asp.h>
767c478bd9Sstevel@tonic-gate #include <inet/tcp.h>
77ff550d0eSmasputra #include <inet/tcp_impl.h>
78ff550d0eSmasputra #include <inet/udp_impl.h>
797c478bd9Sstevel@tonic-gate #include <inet/ipp_common.h>
807c478bd9Sstevel@tonic-gate 
817c478bd9Sstevel@tonic-gate #include <inet/ip_multi.h>
827c478bd9Sstevel@tonic-gate #include <inet/ip_if.h>
837c478bd9Sstevel@tonic-gate #include <inet/ip_ire.h>
847c478bd9Sstevel@tonic-gate #include <inet/ip_rts.h>
857c478bd9Sstevel@tonic-gate #include <inet/ip_ndp.h>
867c478bd9Sstevel@tonic-gate #include <net/pfkeyv2.h>
877c478bd9Sstevel@tonic-gate #include <inet/sadb.h>
887c478bd9Sstevel@tonic-gate #include <inet/ipsec_impl.h>
892b24ab6bSSebastien Roy #include <inet/iptun/iptun_impl.h>
907c478bd9Sstevel@tonic-gate #include <inet/sctp_ip.h>
917c478bd9Sstevel@tonic-gate #include <sys/pattr.h>
927c478bd9Sstevel@tonic-gate #include <inet/ipclassifier.h>
937c478bd9Sstevel@tonic-gate #include <inet/ipsecah.h>
94fc80c0dfSnordmark #include <inet/rawip_impl.h>
95fc80c0dfSnordmark #include <inet/rts_impl.h>
96da14cebeSEric Cheng #include <sys/squeue_impl.h>
977c478bd9Sstevel@tonic-gate #include <sys/squeue.h>
987c478bd9Sstevel@tonic-gate 
9945916cd2Sjpk #include <sys/tsol/label.h>
10045916cd2Sjpk #include <sys/tsol/tnet.h>
10145916cd2Sjpk 
10269bb4bb4Scarlsonj /* Temporary; for CR 6451644 work-around */
10369bb4bb4Scarlsonj #include <sys/ethernet.h>
10469bb4bb4Scarlsonj 
1057c478bd9Sstevel@tonic-gate /*
1067c478bd9Sstevel@tonic-gate  * Naming conventions:
1077c478bd9Sstevel@tonic-gate  *      These rules should be judiciously applied
1087c478bd9Sstevel@tonic-gate  *	if there is a need to identify something as IPv6 versus IPv4
1097c478bd9Sstevel@tonic-gate  *	IPv6 funcions will end with _v6 in the ip module.
1107c478bd9Sstevel@tonic-gate  *	IPv6 funcions will end with _ipv6 in the transport modules.
1117c478bd9Sstevel@tonic-gate  *	IPv6 macros:
1127c478bd9Sstevel@tonic-gate  *		Some macros end with _V6; e.g. ILL_FRAG_HASH_V6
1137c478bd9Sstevel@tonic-gate  *		Some macros start with V6_; e.g. V6_OR_V4_INADDR_ANY
1147c478bd9Sstevel@tonic-gate  *		And then there are ..V4_PART_OF_V6.
1157c478bd9Sstevel@tonic-gate  *		The intent is that macros in the ip module end with _V6.
1167c478bd9Sstevel@tonic-gate  *	IPv6 global variables will start with ipv6_
1177c478bd9Sstevel@tonic-gate  *	IPv6 structures will start with ipv6
1187c478bd9Sstevel@tonic-gate  *	IPv6 defined constants should start with IPV6_
1197c478bd9Sstevel@tonic-gate  *		(but then there are NDP_DEFAULT_VERS_PRI_AND_FLOW, etc)
1207c478bd9Sstevel@tonic-gate  */
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate /*
12345916cd2Sjpk  * ip6opt_ls is used to enable IPv6 (via /etc/system on TX systems).
12445916cd2Sjpk  * We need to do this because we didn't obtain the IP6OPT_LS (0x0a)
12545916cd2Sjpk  * from IANA. This mechanism will remain in effect until an official
12645916cd2Sjpk  * number is obtained.
12745916cd2Sjpk  */
12845916cd2Sjpk uchar_t ip6opt_ls;
12945916cd2Sjpk 
1307c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_ones =
1317c478bd9Sstevel@tonic-gate 	{ 0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU };
1327c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_zeros = { 0, 0, 0, 0 };
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
1357c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0xff000000U, 0, 0, 0 };
1367c478bd9Sstevel@tonic-gate #else	/* _BIG_ENDIAN */
1377c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_unspecified_group = { 0x000000ffU, 0, 0, 0 };
1387c478bd9Sstevel@tonic-gate #endif	/* _BIG_ENDIAN */
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate #ifdef	_BIG_ENDIAN
1417c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x00000001U };
1427c478bd9Sstevel@tonic-gate #else  /* _BIG_ENDIAN */
1437c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_loopback = { 0, 0, 0, 0x01000000U };
1447c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1477c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0xff020000U, 0, 0, 0x00000001U };
1487c478bd9Sstevel@tonic-gate #else  /* _BIG_ENDIAN */
1497c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_hosts_mcast = { 0x000002ffU, 0, 0, 0x01000000U };
1507c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1537c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0xff020000U, 0, 0, 0x00000002U };
1547c478bd9Sstevel@tonic-gate #else  /* _BIG_ENDIAN */
1557c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_rtrs_mcast = { 0x000002ffU, 0, 0, 0x02000000U };
1567c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1597c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0xff020000U, 0, 0, 0x00000016U };
1607c478bd9Sstevel@tonic-gate #else  /* _BIG_ENDIAN */
1617c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_all_v2rtrs_mcast = { 0x000002ffU, 0, 0, 0x16000000U };
1627c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate #ifdef _BIG_ENDIAN
1657c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast =
1667c478bd9Sstevel@tonic-gate 			{ 0xff020000U, 0, 0x00000001U, 0xff000000U };
1677c478bd9Sstevel@tonic-gate #else  /* _BIG_ENDIAN */
1687c478bd9Sstevel@tonic-gate const in6_addr_t ipv6_solicited_node_mcast =
1697c478bd9Sstevel@tonic-gate 			{ 0x000002ffU, 0, 0x01000000U, 0x000000ffU };
1707c478bd9Sstevel@tonic-gate #endif /* _BIG_ENDIAN */
1717c478bd9Sstevel@tonic-gate 
172bd670b35SErik Nordmark static boolean_t icmp_inbound_verify_v6(mblk_t *, icmp6_t *, ip_recv_attr_t *);
173bd670b35SErik Nordmark static void	icmp_inbound_too_big_v6(icmp6_t *, ip_recv_attr_t *);
174bd670b35SErik Nordmark static void	icmp_pkt_v6(mblk_t *, void *, size_t, const in6_addr_t *,
175bd670b35SErik Nordmark     ip_recv_attr_t *);
176bd670b35SErik Nordmark static void	icmp_redirect_v6(mblk_t *, ip6_t *, nd_redirect_t *,
177bd670b35SErik Nordmark     ip_recv_attr_t *);
178bd670b35SErik Nordmark static void	icmp_send_redirect_v6(mblk_t *, in6_addr_t *,
179bd670b35SErik Nordmark     in6_addr_t *, ip_recv_attr_t *);
180bd670b35SErik Nordmark static void	icmp_send_reply_v6(mblk_t *, ip6_t *, icmp6_t *,
181bd670b35SErik Nordmark     ip_recv_attr_t *);
182f4b3ec61Sdh155122 static boolean_t	ip_source_routed_v6(ip6_t *, mblk_t *, ip_stack_t *);
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate /*
185bd670b35SErik Nordmark  * icmp_inbound_v6 deals with ICMP messages that are handled by IP.
186bd670b35SErik Nordmark  * If the ICMP message is consumed by IP, i.e., it should not be delivered
187bd670b35SErik Nordmark  * to any IPPROTO_ICMP raw sockets, then it returns NULL.
188bd670b35SErik Nordmark  * Likewise, if the ICMP error is misformed (too short, etc), then it
189bd670b35SErik Nordmark  * returns NULL. The caller uses this to determine whether or not to send
190bd670b35SErik Nordmark  * to raw sockets.
1917c478bd9Sstevel@tonic-gate  *
1927c478bd9Sstevel@tonic-gate  * All error messages are passed to the matching transport stream.
1937c478bd9Sstevel@tonic-gate  *
194bd670b35SErik Nordmark  * See comment for icmp_inbound_v4() on how IPsec is handled.
1957c478bd9Sstevel@tonic-gate  */
196bd670b35SErik Nordmark mblk_t *
icmp_inbound_v6(mblk_t * mp,ip_recv_attr_t * ira)197bd670b35SErik Nordmark icmp_inbound_v6(mblk_t *mp, ip_recv_attr_t *ira)
1987c478bd9Sstevel@tonic-gate {
1997c478bd9Sstevel@tonic-gate 	icmp6_t		*icmp6;
200bd670b35SErik Nordmark 	ip6_t		*ip6h;		/* Outer header */
201bd670b35SErik Nordmark 	int		ip_hdr_length;	/* Outer header length */
2027c478bd9Sstevel@tonic-gate 	boolean_t	interested;
203bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
204f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
205bd670b35SErik Nordmark 	mblk_t		*mp_ret = NULL;
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInMsgs);
2107c478bd9Sstevel@tonic-gate 
21101685f97SSowmini Varadhan 	/* Check for Martian packets  */
21201685f97SSowmini Varadhan 	if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src)) {
21301685f97SSowmini Varadhan 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
21401685f97SSowmini Varadhan 		ip_drop_input("ipIfStatsInAddrErrors: mcast src", mp, ill);
21501685f97SSowmini Varadhan 		freemsg(mp);
21601685f97SSowmini Varadhan 		return (NULL);
21701685f97SSowmini Varadhan 	}
21801685f97SSowmini Varadhan 
219bd670b35SErik Nordmark 	/* Make sure ira_l2src is set for ndp_input */
220bd670b35SErik Nordmark 	if (!(ira->ira_flags & IRAF_L2SRC_SET))
221bd670b35SErik Nordmark 		ip_setl2src(mp, ira, ira->ira_rill);
2227c478bd9Sstevel@tonic-gate 
223bd670b35SErik Nordmark 	ip_hdr_length = ira->ira_ip_hdr_length;
224bd670b35SErik Nordmark 	if ((mp->b_wptr - mp->b_rptr) < (ip_hdr_length + ICMP6_MINLEN)) {
225bd670b35SErik Nordmark 		if (ira->ira_pktlen < (ip_hdr_length + ICMP6_MINLEN)) {
226bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInTruncatedPkts);
227bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInTruncatedPkts", mp, ill);
228bd670b35SErik Nordmark 			freemsg(mp);
229bd670b35SErik Nordmark 			return (NULL);
230bd670b35SErik Nordmark 		}
231bd670b35SErik Nordmark 		ip6h = ip_pullup(mp, ip_hdr_length + ICMP6_MINLEN, ira);
232bd670b35SErik Nordmark 		if (ip6h == NULL) {
23345916cd2Sjpk 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
234bd670b35SErik Nordmark 			freemsg(mp);
235bd670b35SErik Nordmark 			return (NULL);
23645916cd2Sjpk 		}
23745916cd2Sjpk 	}
23845916cd2Sjpk 
239bd670b35SErik Nordmark 	icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
240bd670b35SErik Nordmark 	DTRACE_PROBE2(icmp__inbound__v6, ip6_t *, ip6h, icmp6_t *, icmp6);
2417c478bd9Sstevel@tonic-gate 	ip2dbg(("icmp_inbound_v6: type %d code %d\n", icmp6->icmp6_type,
2427c478bd9Sstevel@tonic-gate 	    icmp6->icmp6_code));
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	/*
245bd670b35SErik Nordmark 	 * We will set "interested" to "true" if we should pass a copy to
246bd670b35SErik Nordmark 	 * the transport i.e., if it is an error message.
2477c478bd9Sstevel@tonic-gate 	 */
248bd670b35SErik Nordmark 	interested = !(icmp6->icmp6_type & ICMP6_INFOMSG_MASK);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
2517c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
2527c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInDestUnreachs);
2537c478bd9Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
2547c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInAdminProhibs);
2557c478bd9Sstevel@tonic-gate 		break;
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
2587c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInTimeExcds);
2597c478bd9Sstevel@tonic-gate 		break;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
2627c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInParmProblems);
2637c478bd9Sstevel@tonic-gate 		break;
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG:
266bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInPktTooBigs);
267bd670b35SErik Nordmark 		break;
268bd670b35SErik Nordmark 
2697c478bd9Sstevel@tonic-gate 	case ICMP6_ECHO_REQUEST:
2707c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchos);
2717c478bd9Sstevel@tonic-gate 		if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst) &&
272f4b3ec61Sdh155122 		    !ipst->ips_ipv6_resp_echo_mcast)
2737c478bd9Sstevel@tonic-gate 			break;
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 		/*
2767c478bd9Sstevel@tonic-gate 		 * We must have exclusive use of the mblk to convert it to
2777c478bd9Sstevel@tonic-gate 		 * a response.
2787c478bd9Sstevel@tonic-gate 		 * If not, we copy it.
2797c478bd9Sstevel@tonic-gate 		 */
2807c478bd9Sstevel@tonic-gate 		if (mp->b_datap->db_ref > 1) {
2817c478bd9Sstevel@tonic-gate 			mblk_t	*mp1;
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 			mp1 = copymsg(mp);
2847c478bd9Sstevel@tonic-gate 			if (mp1 == NULL) {
285bd670b35SErik Nordmark 				BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
286bd670b35SErik Nordmark 				ip_drop_input("ipIfStatsInDiscards - copymsg",
287bd670b35SErik Nordmark 				    mp, ill);
288bd670b35SErik Nordmark 				freemsg(mp);
289bd670b35SErik Nordmark 				return (NULL);
2907c478bd9Sstevel@tonic-gate 			}
291bd670b35SErik Nordmark 			freemsg(mp);
2927c478bd9Sstevel@tonic-gate 			mp = mp1;
2937c478bd9Sstevel@tonic-gate 			ip6h = (ip6_t *)mp->b_rptr;
294bd670b35SErik Nordmark 			icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
2957c478bd9Sstevel@tonic-gate 		}
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 		icmp6->icmp6_type = ICMP6_ECHO_REPLY;
298bd670b35SErik Nordmark 		icmp_send_reply_v6(mp, ip6h, icmp6, ira);
299bd670b35SErik Nordmark 		return (NULL);
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate 	case ICMP6_ECHO_REPLY:
3027c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInEchoReplies);
3037c478bd9Sstevel@tonic-gate 		break;
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
3067c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterSolicits);
3077c478bd9Sstevel@tonic-gate 		break;
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
3107c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRouterAdvertisements);
3117c478bd9Sstevel@tonic-gate 		break;
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 	case ND_NEIGHBOR_SOLICIT:
3147c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInNeighborSolicits);
315bd670b35SErik Nordmark 		ndp_input(mp, ira);
316bd670b35SErik Nordmark 		return (NULL);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 	case ND_NEIGHBOR_ADVERT:
3197c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib,
3207c478bd9Sstevel@tonic-gate 		    ipv6IfIcmpInNeighborAdvertisements);
321bd670b35SErik Nordmark 		ndp_input(mp, ira);
322bd670b35SErik Nordmark 		return (NULL);
3237c478bd9Sstevel@tonic-gate 
324bd670b35SErik Nordmark 	case ND_REDIRECT:
3257c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInRedirects);
3267c478bd9Sstevel@tonic-gate 
327f4b3ec61Sdh155122 		if (ipst->ips_ipv6_ignore_redirect)
3287c478bd9Sstevel@tonic-gate 			break;
3297c478bd9Sstevel@tonic-gate 
330bd670b35SErik Nordmark 		/* We now allow a RAW socket to receive this. */
331bd670b35SErik Nordmark 		interested = B_TRUE;
3327c478bd9Sstevel@tonic-gate 		break;
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	/*
3357c478bd9Sstevel@tonic-gate 	 * The next three icmp messages will be handled by MLD.
3367c478bd9Sstevel@tonic-gate 	 * Pass all valid MLD packets up to any process(es)
337bd670b35SErik Nordmark 	 * listening on a raw ICMP socket.
3387c478bd9Sstevel@tonic-gate 	 */
3397c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_QUERY:
3407c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REPORT:
3417c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REDUCTION:
342bd670b35SErik Nordmark 		mp = mld_input(mp, ira);
343bd670b35SErik Nordmark 		return (mp);
3447c478bd9Sstevel@tonic-gate 	default:
3457c478bd9Sstevel@tonic-gate 		break;
3467c478bd9Sstevel@tonic-gate 	}
347bd670b35SErik Nordmark 	/*
348bd670b35SErik Nordmark 	 * See if there is an ICMP client to avoid an extra copymsg/freemsg
349bd670b35SErik Nordmark 	 * if there isn't one.
350bd670b35SErik Nordmark 	 */
351bd670b35SErik Nordmark 	if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_ICMPV6].connf_head != NULL) {
352bd670b35SErik Nordmark 		/* If there is an ICMP client and we want one too, copy it. */
353bd670b35SErik Nordmark 
354bd670b35SErik Nordmark 		if (!interested) {
355bd670b35SErik Nordmark 			/* Caller will deliver to RAW sockets */
356bd670b35SErik Nordmark 			return (mp);
3577c478bd9Sstevel@tonic-gate 		}
358bd670b35SErik Nordmark 		mp_ret = copymsg(mp);
359bd670b35SErik Nordmark 		if (mp_ret == NULL) {
360bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
361bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
362bd670b35SErik Nordmark 		}
363bd670b35SErik Nordmark 	} else if (!interested) {
364bd670b35SErik Nordmark 		/* Neither we nor raw sockets are interested. Drop packet now */
365bd670b35SErik Nordmark 		freemsg(mp);
366bd670b35SErik Nordmark 		return (NULL);
3677c478bd9Sstevel@tonic-gate 	}
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 	/*
370bd670b35SErik Nordmark 	 * ICMP error or redirect packet. Make sure we have enough of
371bd670b35SErik Nordmark 	 * the header and that db_ref == 1 since we might end up modifying
372bd670b35SErik Nordmark 	 * the packet.
3737c478bd9Sstevel@tonic-gate 	 */
374bd670b35SErik Nordmark 	if (mp->b_cont != NULL) {
375bd670b35SErik Nordmark 		if (ip_pullup(mp, -1, ira) == NULL) {
376bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
377bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards - ip_pullup",
378bd670b35SErik Nordmark 			    mp, ill);
379bd670b35SErik Nordmark 			freemsg(mp);
380bd670b35SErik Nordmark 			return (mp_ret);
381bd670b35SErik Nordmark 		}
382bd670b35SErik Nordmark 	}
3837c478bd9Sstevel@tonic-gate 
3847c478bd9Sstevel@tonic-gate 	if (mp->b_datap->db_ref > 1) {
3857c478bd9Sstevel@tonic-gate 		mblk_t	*mp1;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 		mp1 = copymsg(mp);
3887c478bd9Sstevel@tonic-gate 		if (mp1 == NULL) {
3893173664eSapersson 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
390bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards - copymsg", mp, ill);
391bd670b35SErik Nordmark 			freemsg(mp);
392bd670b35SErik Nordmark 			return (mp_ret);
3937c478bd9Sstevel@tonic-gate 		}
394bd670b35SErik Nordmark 		freemsg(mp);
3957c478bd9Sstevel@tonic-gate 		mp = mp1;
3967c478bd9Sstevel@tonic-gate 	}
3977c478bd9Sstevel@tonic-gate 
3987c478bd9Sstevel@tonic-gate 	/*
399bd670b35SErik Nordmark 	 * In case mp has changed, verify the message before any further
400bd670b35SErik Nordmark 	 * processes.
401bd670b35SErik Nordmark 	 */
402bd670b35SErik Nordmark 	ip6h = (ip6_t *)mp->b_rptr;
403bd670b35SErik Nordmark 	icmp6 = (icmp6_t *)(&mp->b_rptr[ip_hdr_length]);
404bd670b35SErik Nordmark 	if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
405bd670b35SErik Nordmark 		freemsg(mp);
406bd670b35SErik Nordmark 		return (mp_ret);
407bd670b35SErik Nordmark 	}
408bd670b35SErik Nordmark 
409bd670b35SErik Nordmark 	switch (icmp6->icmp6_type) {
410bd670b35SErik Nordmark 	case ND_REDIRECT:
411bd670b35SErik Nordmark 		icmp_redirect_v6(mp, ip6h, (nd_redirect_t *)icmp6, ira);
412bd670b35SErik Nordmark 		break;
413bd670b35SErik Nordmark 	case ICMP6_PACKET_TOO_BIG:
414bd670b35SErik Nordmark 		/* Update DCE and adjust MTU is icmp header if needed */
415bd670b35SErik Nordmark 		icmp_inbound_too_big_v6(icmp6, ira);
416bd670b35SErik Nordmark 		/* FALLTHRU */
417bd670b35SErik Nordmark 	default:
418bd670b35SErik Nordmark 		icmp_inbound_error_fanout_v6(mp, icmp6, ira);
419bd670b35SErik Nordmark 		break;
420bd670b35SErik Nordmark 	}
421bd670b35SErik Nordmark 
422bd670b35SErik Nordmark 	return (mp_ret);
423bd670b35SErik Nordmark }
424bd670b35SErik Nordmark 
425bd670b35SErik Nordmark /*
426bd670b35SErik Nordmark  * Send an ICMP echo reply.
427bd670b35SErik Nordmark  * The caller has already updated the payload part of the packet.
428bd670b35SErik Nordmark  * We handle the ICMP checksum, IP source address selection and feed
429bd670b35SErik Nordmark  * the packet into ip_output_simple.
430bd670b35SErik Nordmark  */
431bd670b35SErik Nordmark static void
icmp_send_reply_v6(mblk_t * mp,ip6_t * ip6h,icmp6_t * icmp6,ip_recv_attr_t * ira)432bd670b35SErik Nordmark icmp_send_reply_v6(mblk_t *mp, ip6_t *ip6h, icmp6_t *icmp6,
433bd670b35SErik Nordmark     ip_recv_attr_t *ira)
434bd670b35SErik Nordmark {
435bd670b35SErik Nordmark 	uint_t		ip_hdr_length = ira->ira_ip_hdr_length;
436bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
437bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
438bd670b35SErik Nordmark 	ip_xmit_attr_t	ixas;
439bd670b35SErik Nordmark 	in6_addr_t	origsrc;
440bd670b35SErik Nordmark 
441bd670b35SErik Nordmark 	/*
442bd670b35SErik Nordmark 	 * Remove any extension headers (do not reverse a source route)
443bd670b35SErik Nordmark 	 * and clear the flow id (keep traffic class for now).
444bd670b35SErik Nordmark 	 */
445bd670b35SErik Nordmark 	if (ip_hdr_length != IPV6_HDR_LEN) {
446bd670b35SErik Nordmark 		int	i;
447bd670b35SErik Nordmark 
448bd670b35SErik Nordmark 		for (i = 0; i < IPV6_HDR_LEN; i++) {
449bd670b35SErik Nordmark 			mp->b_rptr[ip_hdr_length - i - 1] =
450bd670b35SErik Nordmark 			    mp->b_rptr[IPV6_HDR_LEN - i - 1];
451bd670b35SErik Nordmark 		}
452bd670b35SErik Nordmark 		mp->b_rptr += (ip_hdr_length - IPV6_HDR_LEN);
453bd670b35SErik Nordmark 		ip6h = (ip6_t *)mp->b_rptr;
454bd670b35SErik Nordmark 		ip6h->ip6_nxt = IPPROTO_ICMPV6;
455bd670b35SErik Nordmark 		i = ntohs(ip6h->ip6_plen);
456bd670b35SErik Nordmark 		i -= (ip_hdr_length - IPV6_HDR_LEN);
457bd670b35SErik Nordmark 		ip6h->ip6_plen = htons(i);
458bd670b35SErik Nordmark 		ip_hdr_length = IPV6_HDR_LEN;
459bd670b35SErik Nordmark 		ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == msgdsize(mp));
460bd670b35SErik Nordmark 	}
461bd670b35SErik Nordmark 	ip6h->ip6_vcf &= ~IPV6_FLOWINFO_FLOWLABEL;
462bd670b35SErik Nordmark 
463bd670b35SErik Nordmark 	/* Reverse the source and destination addresses. */
464bd670b35SErik Nordmark 	origsrc = ip6h->ip6_src;
465bd670b35SErik Nordmark 	ip6h->ip6_src = ip6h->ip6_dst;
466bd670b35SErik Nordmark 	ip6h->ip6_dst = origsrc;
467bd670b35SErik Nordmark 
468bd670b35SErik Nordmark 	/* set the hop limit */
469bd670b35SErik Nordmark 	ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
470bd670b35SErik Nordmark 
471bd670b35SErik Nordmark 	/*
472bd670b35SErik Nordmark 	 * Prepare for checksum by putting icmp length in the icmp
473bd670b35SErik Nordmark 	 * checksum field. The checksum is calculated in ip_output
474bd670b35SErik Nordmark 	 */
475bd670b35SErik Nordmark 	icmp6->icmp6_cksum = ip6h->ip6_plen;
476bd670b35SErik Nordmark 
477bd670b35SErik Nordmark 	bzero(&ixas, sizeof (ixas));
478bd670b35SErik Nordmark 	ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
479bd670b35SErik Nordmark 	ixas.ixa_zoneid = ira->ira_zoneid;
480bd670b35SErik Nordmark 	ixas.ixa_cred = kcred;
481bd670b35SErik Nordmark 	ixas.ixa_cpid = NOPID;
482bd670b35SErik Nordmark 	ixas.ixa_tsl = ira->ira_tsl;	/* Behave as a multi-level responder */
483bd670b35SErik Nordmark 	ixas.ixa_ifindex = 0;
484bd670b35SErik Nordmark 	ixas.ixa_ipst = ipst;
485bd670b35SErik Nordmark 	ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
486bd670b35SErik Nordmark 
487bd670b35SErik Nordmark 	if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
488bd670b35SErik Nordmark 		/*
489bd670b35SErik Nordmark 		 * This packet should go out the same way as it
490bd670b35SErik Nordmark 		 * came in i.e in clear, independent of the IPsec
491bd670b35SErik Nordmark 		 * policy for transmitting packets.
492bd670b35SErik Nordmark 		 */
493bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_NO_IPSEC;
494bd670b35SErik Nordmark 	} else {
495bd670b35SErik Nordmark 		if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
496bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
497bd670b35SErik Nordmark 			/* Note: mp already consumed and ip_drop_packet done */
498bd670b35SErik Nordmark 			return;
499bd670b35SErik Nordmark 		}
500bd670b35SErik Nordmark 	}
501bd670b35SErik Nordmark 
502bd670b35SErik Nordmark 	/* Was the destination (now source) link-local? Send out same group */
503bd670b35SErik Nordmark 	if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
504bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_SCOPEID_SET;
505bd670b35SErik Nordmark 		if (IS_UNDER_IPMP(ill))
506bd670b35SErik Nordmark 			ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
507bd670b35SErik Nordmark 		else
508bd670b35SErik Nordmark 			ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
509bd670b35SErik Nordmark 	}
510bd670b35SErik Nordmark 
511bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_MULTIBROADCAST) {
512bd670b35SErik Nordmark 		/*
513bd670b35SErik Nordmark 		 * Not one or our addresses (IRE_LOCALs), thus we let
514bd670b35SErik Nordmark 		 * ip_output_simple pick the source.
515bd670b35SErik Nordmark 		 */
516bd670b35SErik Nordmark 		ip6h->ip6_src = ipv6_all_zeros;
517bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_SET_SOURCE;
518bd670b35SErik Nordmark 	}
519bd670b35SErik Nordmark 
520bd670b35SErik Nordmark 	/* Should we send using dce_pmtu? */
521bd670b35SErik Nordmark 	if (ipst->ips_ipv6_icmp_return_pmtu)
522bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_PMTU_DISCOVERY;
523bd670b35SErik Nordmark 
524bd670b35SErik Nordmark 	(void) ip_output_simple(mp, &ixas);
525bd670b35SErik Nordmark 	ixa_cleanup(&ixas);
526bd670b35SErik Nordmark 
527bd670b35SErik Nordmark }
528bd670b35SErik Nordmark 
529bd670b35SErik Nordmark /*
530bd670b35SErik Nordmark  * Verify the ICMP messages for either for ICMP error or redirect packet.
531bd670b35SErik Nordmark  * The caller should have fully pulled up the message. If it's a redirect
532bd670b35SErik Nordmark  * packet, only basic checks on IP header will be done; otherwise, verify
533bd670b35SErik Nordmark  * the packet by looking at the included ULP header.
534bd670b35SErik Nordmark  *
535bd670b35SErik Nordmark  * Called before icmp_inbound_error_fanout_v6 is called.
536bd670b35SErik Nordmark  */
537bd670b35SErik Nordmark static boolean_t
icmp_inbound_verify_v6(mblk_t * mp,icmp6_t * icmp6,ip_recv_attr_t * ira)538bd670b35SErik Nordmark icmp_inbound_verify_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
539bd670b35SErik Nordmark {
540bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
541bd670b35SErik Nordmark 	uint16_t	hdr_length;
542bd670b35SErik Nordmark 	uint8_t		*nexthdrp;
543bd670b35SErik Nordmark 	uint8_t		nexthdr;
544bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
545bd670b35SErik Nordmark 	conn_t		*connp;
546bd670b35SErik Nordmark 	ip6_t		*ip6h;	/* Inner header */
547bd670b35SErik Nordmark 
548bd670b35SErik Nordmark 	ip6h = (ip6_t *)&icmp6[1];
549bd670b35SErik Nordmark 	if ((uchar_t *)ip6h + IPV6_HDR_LEN > mp->b_wptr)
550bd670b35SErik Nordmark 		goto truncated;
551bd670b35SErik Nordmark 
552bd670b35SErik Nordmark 	if (icmp6->icmp6_type == ND_REDIRECT) {
553bd670b35SErik Nordmark 		hdr_length = sizeof (nd_redirect_t);
554bd670b35SErik Nordmark 	} else {
555bd670b35SErik Nordmark 		if ((IPH_HDR_VERSION(ip6h) != IPV6_VERSION))
556bd670b35SErik Nordmark 			goto discard_pkt;
557bd670b35SErik Nordmark 		hdr_length = IPV6_HDR_LEN;
558bd670b35SErik Nordmark 	}
559bd670b35SErik Nordmark 
560bd670b35SErik Nordmark 	if ((uchar_t *)ip6h + hdr_length > mp->b_wptr)
561bd670b35SErik Nordmark 		goto truncated;
562bd670b35SErik Nordmark 
563bd670b35SErik Nordmark 	/*
564bd670b35SErik Nordmark 	 * Stop here for ICMP_REDIRECT.
565bd670b35SErik Nordmark 	 */
566bd670b35SErik Nordmark 	if (icmp6->icmp6_type == ND_REDIRECT)
567bd670b35SErik Nordmark 		return (B_TRUE);
568bd670b35SErik Nordmark 
569bd670b35SErik Nordmark 	/*
570bd670b35SErik Nordmark 	 * ICMP errors only.
571bd670b35SErik Nordmark 	 */
572bd670b35SErik Nordmark 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
573bd670b35SErik Nordmark 		goto discard_pkt;
574bd670b35SErik Nordmark 	nexthdr = *nexthdrp;
575bd670b35SErik Nordmark 
576bd670b35SErik Nordmark 	/* Try to pass the ICMP message to clients who need it */
577bd670b35SErik Nordmark 	switch (nexthdr) {
578bd670b35SErik Nordmark 	case IPPROTO_UDP:
579bd670b35SErik Nordmark 		/*
580bd670b35SErik Nordmark 		 * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
581bd670b35SErik Nordmark 		 * transport header.
582bd670b35SErik Nordmark 		 */
583bd670b35SErik Nordmark 		if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
584bd670b35SErik Nordmark 		    mp->b_wptr)
585bd670b35SErik Nordmark 			goto truncated;
586bd670b35SErik Nordmark 		break;
587bd670b35SErik Nordmark 	case IPPROTO_TCP: {
588bd670b35SErik Nordmark 		tcpha_t		*tcpha;
589bd670b35SErik Nordmark 
590bd670b35SErik Nordmark 		/*
591bd670b35SErik Nordmark 		 * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
592bd670b35SErik Nordmark 		 * transport header.
593bd670b35SErik Nordmark 		 */
594bd670b35SErik Nordmark 		if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
595bd670b35SErik Nordmark 		    mp->b_wptr)
596bd670b35SErik Nordmark 			goto truncated;
597bd670b35SErik Nordmark 
598bd670b35SErik Nordmark 		tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
599bd670b35SErik Nordmark 		/*
600bd670b35SErik Nordmark 		 * With IPMP we need to match across group, which we do
601bd670b35SErik Nordmark 		 * since we have the upper ill from ira_ill.
602bd670b35SErik Nordmark 		 */
603bd670b35SErik Nordmark 		connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha, TCPS_LISTEN,
604bd670b35SErik Nordmark 		    ill->ill_phyint->phyint_ifindex, ipst);
605bd670b35SErik Nordmark 		if (connp == NULL)
606bd670b35SErik Nordmark 			goto discard_pkt;
607bd670b35SErik Nordmark 
608bd670b35SErik Nordmark 		if ((connp->conn_verifyicmp != NULL) &&
609bd670b35SErik Nordmark 		    !connp->conn_verifyicmp(connp, tcpha, NULL, icmp6, ira)) {
610bd670b35SErik Nordmark 			CONN_DEC_REF(connp);
611bd670b35SErik Nordmark 			goto discard_pkt;
612bd670b35SErik Nordmark 		}
613bd670b35SErik Nordmark 		CONN_DEC_REF(connp);
614bd670b35SErik Nordmark 		break;
615bd670b35SErik Nordmark 	}
616bd670b35SErik Nordmark 	case IPPROTO_SCTP:
617bd670b35SErik Nordmark 		/*
618bd670b35SErik Nordmark 		 * Verify we have at least ICMP_MIN_TP_HDR_LEN bytes of
619bd670b35SErik Nordmark 		 * transport header.
620bd670b35SErik Nordmark 		 */
621bd670b35SErik Nordmark 		if ((uchar_t *)ip6h + hdr_length + ICMP_MIN_TP_HDR_LEN >
622bd670b35SErik Nordmark 		    mp->b_wptr)
623bd670b35SErik Nordmark 			goto truncated;
624bd670b35SErik Nordmark 		break;
625bd670b35SErik Nordmark 	case IPPROTO_ESP:
626bd670b35SErik Nordmark 	case IPPROTO_AH:
627bd670b35SErik Nordmark 		break;
628bd670b35SErik Nordmark 	case IPPROTO_ENCAP:
629bd670b35SErik Nordmark 	case IPPROTO_IPV6: {
630bd670b35SErik Nordmark 		/* Look for self-encapsulated packets that caused an error */
631bd670b35SErik Nordmark 		ip6_t *in_ip6h;
632bd670b35SErik Nordmark 
633bd670b35SErik Nordmark 		in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
634bd670b35SErik Nordmark 		if ((uint8_t *)in_ip6h + (nexthdr == IPPROTO_ENCAP ?
635bd670b35SErik Nordmark 		    sizeof (ipha_t) : sizeof (ip6_t)) > mp->b_wptr)
636bd670b35SErik Nordmark 			goto truncated;
637bd670b35SErik Nordmark 		break;
638bd670b35SErik Nordmark 	}
639bd670b35SErik Nordmark 	default:
640bd670b35SErik Nordmark 		break;
641bd670b35SErik Nordmark 	}
642bd670b35SErik Nordmark 
643bd670b35SErik Nordmark 	return (B_TRUE);
644bd670b35SErik Nordmark 
645bd670b35SErik Nordmark discard_pkt:
646bd670b35SErik Nordmark 	/* Bogus ICMP error. */
647bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
648bd670b35SErik Nordmark 	return (B_FALSE);
649bd670b35SErik Nordmark 
650bd670b35SErik Nordmark truncated:
651bd670b35SErik Nordmark 	/* We pulled up everthing already. Must be truncated */
652bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
653bd670b35SErik Nordmark 	return (B_FALSE);
654bd670b35SErik Nordmark }
655bd670b35SErik Nordmark 
656bd670b35SErik Nordmark /*
657bd670b35SErik Nordmark  * Process received IPv6 ICMP Packet too big.
658bd670b35SErik Nordmark  * The caller is responsible for validating the packet before passing it in
659bd670b35SErik Nordmark  * and also to fanout the ICMP error to any matching transport conns. Assumes
660bd670b35SErik Nordmark  * the message has been fully pulled up.
661bd670b35SErik Nordmark  *
662bd670b35SErik Nordmark  * Before getting here, the caller has called icmp_inbound_verify_v6()
663bd670b35SErik Nordmark  * that should have verified with ULP to prevent undoing the changes we're
664bd670b35SErik Nordmark  * going to make to DCE. For example, TCP might have verified that the packet
665bd670b35SErik Nordmark  * which generated error is in the send window.
666bd670b35SErik Nordmark  *
667bd670b35SErik Nordmark  * In some cases modified this MTU in the ICMP header packet; the caller
668bd670b35SErik Nordmark  * should pass to the matching ULP after this returns.
669bd670b35SErik Nordmark  */
670bd670b35SErik Nordmark static void
icmp_inbound_too_big_v6(icmp6_t * icmp6,ip_recv_attr_t * ira)671bd670b35SErik Nordmark icmp_inbound_too_big_v6(icmp6_t *icmp6, ip_recv_attr_t *ira)
672bd670b35SErik Nordmark {
673bd670b35SErik Nordmark 	uint32_t	mtu;
674bd670b35SErik Nordmark 	dce_t		*dce;
675bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;	/* Upper ill if IPMP */
676bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
677bd670b35SErik Nordmark 	int		old_max_frag;
678bd670b35SErik Nordmark 	in6_addr_t	final_dst;
679bd670b35SErik Nordmark 	ip6_t		*ip6h;	/* Inner IP header */
680bd670b35SErik Nordmark 
681bd670b35SErik Nordmark 	/* Caller has already pulled up everything. */
682bd670b35SErik Nordmark 	ip6h = (ip6_t *)&icmp6[1];
683bd670b35SErik Nordmark 	final_dst = ip_get_dst_v6(ip6h, NULL, NULL);
684bd670b35SErik Nordmark 
685bd670b35SErik Nordmark 	/*
686bd670b35SErik Nordmark 	 * For link local destinations matching simply on address is not
6877c478bd9Sstevel@tonic-gate 	 * sufficient. Same link local addresses for different ILL's is
6887c478bd9Sstevel@tonic-gate 	 * possible.
6897c478bd9Sstevel@tonic-gate 	 */
690bd670b35SErik Nordmark 	if (IN6_IS_ADDR_LINKSCOPE(&final_dst)) {
691bd670b35SErik Nordmark 		dce = dce_lookup_and_add_v6(&final_dst,
692bd670b35SErik Nordmark 		    ill->ill_phyint->phyint_ifindex, ipst);
693bd670b35SErik Nordmark 	} else {
694bd670b35SErik Nordmark 		dce = dce_lookup_and_add_v6(&final_dst, 0, ipst);
695bd670b35SErik Nordmark 	}
696bd670b35SErik Nordmark 	if (dce == NULL) {
697bd670b35SErik Nordmark 		/* Couldn't add a unique one - ENOMEM */
6987c478bd9Sstevel@tonic-gate 		if (ip_debug > 2) {
6997c478bd9Sstevel@tonic-gate 			/* ip1dbg */
7007c478bd9Sstevel@tonic-gate 			pr_addr_dbg("icmp_inbound_too_big_v6:"
701bd670b35SErik Nordmark 			    "no dce for dst %s\n", AF_INET6,
702bd670b35SErik Nordmark 			    &final_dst);
7037c478bd9Sstevel@tonic-gate 		}
7047c478bd9Sstevel@tonic-gate 		return;
7057c478bd9Sstevel@tonic-gate 	}
7067c478bd9Sstevel@tonic-gate 
7077c478bd9Sstevel@tonic-gate 	mtu = ntohl(icmp6->icmp6_mtu);
708bd670b35SErik Nordmark 
709bd670b35SErik Nordmark 	mutex_enter(&dce->dce_lock);
710bd670b35SErik Nordmark 	if (dce->dce_flags & DCEF_PMTU)
711bd670b35SErik Nordmark 		old_max_frag = dce->dce_pmtu;
712*1eee170aSErik Nordmark 	else if (IN6_IS_ADDR_MULTICAST(&final_dst))
713*1eee170aSErik Nordmark 		old_max_frag = ill->ill_mc_mtu;
714bd670b35SErik Nordmark 	else
715bd670b35SErik Nordmark 		old_max_frag = ill->ill_mtu;
716bd670b35SErik Nordmark 
7177c478bd9Sstevel@tonic-gate 	if (mtu < IPV6_MIN_MTU) {
7187c478bd9Sstevel@tonic-gate 		ip1dbg(("Received mtu less than IPv6 "
7197c478bd9Sstevel@tonic-gate 		    "min mtu %d: %d\n", IPV6_MIN_MTU, mtu));
7207c478bd9Sstevel@tonic-gate 		mtu = IPV6_MIN_MTU;
7217c478bd9Sstevel@tonic-gate 		/*
7227c478bd9Sstevel@tonic-gate 		 * If an mtu less than IPv6 min mtu is received,
7237c478bd9Sstevel@tonic-gate 		 * we must include a fragment header in
7247c478bd9Sstevel@tonic-gate 		 * subsequent packets.
7257c478bd9Sstevel@tonic-gate 		 */
726bd670b35SErik Nordmark 		dce->dce_flags |= DCEF_TOO_SMALL_PMTU;
727bd670b35SErik Nordmark 	} else {
728bd670b35SErik Nordmark 		dce->dce_flags &= ~DCEF_TOO_SMALL_PMTU;
7297c478bd9Sstevel@tonic-gate 	}
7307c478bd9Sstevel@tonic-gate 	ip1dbg(("Received mtu from router: %d\n", mtu));
731bd670b35SErik Nordmark 	dce->dce_pmtu = MIN(old_max_frag, mtu);
732bd670b35SErik Nordmark 
733bd670b35SErik Nordmark 	/* Prepare to send the new max frag size for the ULP. */
734bd670b35SErik Nordmark 	if (dce->dce_flags & DCEF_TOO_SMALL_PMTU) {
7357c478bd9Sstevel@tonic-gate 		/*
7367c478bd9Sstevel@tonic-gate 		 * If we need a fragment header in every packet
7377c478bd9Sstevel@tonic-gate 		 * (above case or multirouting), make sure the
7387c478bd9Sstevel@tonic-gate 		 * ULP takes it into account when computing the
7397c478bd9Sstevel@tonic-gate 		 * payload size.
7407c478bd9Sstevel@tonic-gate 		 */
741bd670b35SErik Nordmark 		icmp6->icmp6_mtu = htonl(dce->dce_pmtu - sizeof (ip6_frag_t));
7427c478bd9Sstevel@tonic-gate 	} else {
743bd670b35SErik Nordmark 		icmp6->icmp6_mtu = htonl(dce->dce_pmtu);
7447c478bd9Sstevel@tonic-gate 	}
745bd670b35SErik Nordmark 	/* We now have a PMTU for sure */
746bd670b35SErik Nordmark 	dce->dce_flags |= DCEF_PMTU;
747d3d50737SRafael Vanoni 	dce->dce_last_change_time = TICK_TO_SEC(ddi_get_lbolt64());
748bd670b35SErik Nordmark 	mutex_exit(&dce->dce_lock);
7497c478bd9Sstevel@tonic-gate 	/*
750bd670b35SErik Nordmark 	 * After dropping the lock the new value is visible to everyone.
751bd670b35SErik Nordmark 	 * Then we bump the generation number so any cached values reinspect
752bd670b35SErik Nordmark 	 * the dce_t.
7537c478bd9Sstevel@tonic-gate 	 */
754bd670b35SErik Nordmark 	dce_increment_generation(dce);
755bd670b35SErik Nordmark 	dce_refrele(dce);
7562b24ab6bSSebastien Roy }
7572b24ab6bSSebastien Roy 
7582b24ab6bSSebastien Roy /*
7597c478bd9Sstevel@tonic-gate  * Fanout received ICMPv6 error packets to the transports.
7607c478bd9Sstevel@tonic-gate  * Assumes the IPv6 plus ICMPv6 headers have been pulled up but nothing else.
761bd670b35SErik Nordmark  *
762bd670b35SErik Nordmark  * The caller must have called icmp_inbound_verify_v6.
7637c478bd9Sstevel@tonic-gate  */
7647c478bd9Sstevel@tonic-gate void
icmp_inbound_error_fanout_v6(mblk_t * mp,icmp6_t * icmp6,ip_recv_attr_t * ira)765bd670b35SErik Nordmark icmp_inbound_error_fanout_v6(mblk_t *mp, icmp6_t *icmp6, ip_recv_attr_t *ira)
7667c478bd9Sstevel@tonic-gate {
7677c478bd9Sstevel@tonic-gate 	uint16_t	*up;	/* Pointer to ports in ULP header */
7687c478bd9Sstevel@tonic-gate 	uint32_t	ports;	/* reversed ports for fanout */
7697c478bd9Sstevel@tonic-gate 	ip6_t		rip6h;	/* With reversed addresses */
770bd670b35SErik Nordmark 	ip6_t		*ip6h;	/* Inner IP header */
771bd670b35SErik Nordmark 	uint16_t	hdr_length; /* Inner IP header length */
7727c478bd9Sstevel@tonic-gate 	uint8_t		*nexthdrp;
7737c478bd9Sstevel@tonic-gate 	uint8_t		nexthdr;
7747c478bd9Sstevel@tonic-gate 	tcpha_t		*tcpha;
7757c478bd9Sstevel@tonic-gate 	conn_t		*connp;
776bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;	/* Upper in the case of IPMP */
777f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
778bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ipst->ips_netstack->netstack_ipsec;
7797c478bd9Sstevel@tonic-gate 
780bd670b35SErik Nordmark 	/* Caller has already pulled up everything. */
781bd670b35SErik Nordmark 	ip6h = (ip6_t *)&icmp6[1];
782bd670b35SErik Nordmark 	ASSERT(mp->b_cont == NULL);
783bd670b35SErik Nordmark 	ASSERT((uchar_t *)&ip6h[1] <= mp->b_wptr);
7847c478bd9Sstevel@tonic-gate 
7857c478bd9Sstevel@tonic-gate 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_length, &nexthdrp))
7867c478bd9Sstevel@tonic-gate 		goto drop_pkt;
7877c478bd9Sstevel@tonic-gate 	nexthdr = *nexthdrp;
788bd670b35SErik Nordmark 	ira->ira_protocol = nexthdr;
7897c478bd9Sstevel@tonic-gate 
7902b24ab6bSSebastien Roy 	/*
7912b24ab6bSSebastien Roy 	 * We need a separate IP header with the source and destination
7922b24ab6bSSebastien Roy 	 * addresses reversed to do fanout/classification because the ip6h in
7932b24ab6bSSebastien Roy 	 * the ICMPv6 error is in the form we sent it out.
7942b24ab6bSSebastien Roy 	 */
7952b24ab6bSSebastien Roy 	rip6h.ip6_src = ip6h->ip6_dst;
7962b24ab6bSSebastien Roy 	rip6h.ip6_dst = ip6h->ip6_src;
7972b24ab6bSSebastien Roy 	rip6h.ip6_nxt = nexthdr;
7982b24ab6bSSebastien Roy 
7997c478bd9Sstevel@tonic-gate 	/* Try to pass the ICMP message to clients who need it */
8007c478bd9Sstevel@tonic-gate 	switch (nexthdr) {
8017c478bd9Sstevel@tonic-gate 	case IPPROTO_UDP: {
8022b24ab6bSSebastien Roy 		/* Attempt to find a client stream based on port. */
8037c478bd9Sstevel@tonic-gate 		up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
8047c478bd9Sstevel@tonic-gate 
805bd670b35SErik Nordmark 		/* Note that we send error to all matches. */
806bd670b35SErik Nordmark 		ira->ira_flags |= IRAF_ICMP_ERROR;
807bd670b35SErik Nordmark 		ip_fanout_udp_multi_v6(mp, &rip6h, up[0], up[1], ira);
808bd670b35SErik Nordmark 		ira->ira_flags &= ~IRAF_ICMP_ERROR;
8097c478bd9Sstevel@tonic-gate 		return;
8107c478bd9Sstevel@tonic-gate 	}
8117c478bd9Sstevel@tonic-gate 	case IPPROTO_TCP: {
8127c478bd9Sstevel@tonic-gate 		/*
8137c478bd9Sstevel@tonic-gate 		 * Attempt to find a client stream based on port.
8147c478bd9Sstevel@tonic-gate 		 * Note that we do a reverse lookup since the header is
8157c478bd9Sstevel@tonic-gate 		 * in the form we sent it out.
8167c478bd9Sstevel@tonic-gate 		 */
817bd670b35SErik Nordmark 		tcpha = (tcpha_t *)((uchar_t *)ip6h + hdr_length);
818bd670b35SErik Nordmark 		/*
819bd670b35SErik Nordmark 		 * With IPMP we need to match across group, which we do
820bd670b35SErik Nordmark 		 * since we have the upper ill from ira_ill.
821bd670b35SErik Nordmark 		 */
8227c478bd9Sstevel@tonic-gate 		connp = ipcl_tcp_lookup_reversed_ipv6(ip6h, tcpha,
823f4b3ec61Sdh155122 		    TCPS_LISTEN, ill->ill_phyint->phyint_ifindex, ipst);
8247c478bd9Sstevel@tonic-gate 		if (connp == NULL) {
8257c478bd9Sstevel@tonic-gate 			goto drop_pkt;
8267c478bd9Sstevel@tonic-gate 		}
8277c478bd9Sstevel@tonic-gate 
828bd670b35SErik Nordmark 		if (CONN_INBOUND_POLICY_PRESENT_V6(connp, ipss) ||
829bd670b35SErik Nordmark 		    (ira->ira_flags & IRAF_IPSEC_SECURE)) {
830bd670b35SErik Nordmark 			mp = ipsec_check_inbound_policy(mp, connp,
831bd670b35SErik Nordmark 			    NULL, ip6h, ira);
832bd670b35SErik Nordmark 			if (mp == NULL) {
833bd670b35SErik Nordmark 				BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
834bd670b35SErik Nordmark 				/* Note that mp is NULL */
835bd670b35SErik Nordmark 				ip_drop_input("ipIfStatsInDiscards", mp, ill);
836bd670b35SErik Nordmark 				CONN_DEC_REF(connp);
837bd670b35SErik Nordmark 				return;
838bd670b35SErik Nordmark 			}
839bd670b35SErik Nordmark 		}
840bd670b35SErik Nordmark 
841bd670b35SErik Nordmark 		ira->ira_flags |= IRAF_ICMP_ERROR;
842bd670b35SErik Nordmark 		if (IPCL_IS_TCP(connp)) {
843bd670b35SErik Nordmark 			SQUEUE_ENTER_ONE(connp->conn_sqp, mp,
844bd670b35SErik Nordmark 			    connp->conn_recvicmp, connp, ira, SQ_FILL,
845bd670b35SErik Nordmark 			    SQTAG_TCP6_INPUT_ICMP_ERR);
846bd670b35SErik Nordmark 		} else {
847bd670b35SErik Nordmark 			/* Not TCP; must be SOCK_RAW, IPPROTO_TCP */
848bd670b35SErik Nordmark 			ill_t *rill = ira->ira_rill;
849bd670b35SErik Nordmark 
850bd670b35SErik Nordmark 			ira->ira_ill = ira->ira_rill = NULL;
851bd670b35SErik Nordmark 			(connp->conn_recv)(connp, mp, NULL, ira);
852bd670b35SErik Nordmark 			CONN_DEC_REF(connp);
853bd670b35SErik Nordmark 			ira->ira_ill = ill;
854bd670b35SErik Nordmark 			ira->ira_rill = rill;
855bd670b35SErik Nordmark 		}
856bd670b35SErik Nordmark 		ira->ira_flags &= ~IRAF_ICMP_ERROR;
8577c478bd9Sstevel@tonic-gate 		return;
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	}
8607c478bd9Sstevel@tonic-gate 	case IPPROTO_SCTP:
8617c478bd9Sstevel@tonic-gate 		up = (uint16_t *)((uchar_t *)ip6h + hdr_length);
862bd670b35SErik Nordmark 		/* Find a SCTP client stream for this packet. */
8637c478bd9Sstevel@tonic-gate 		((uint16_t *)&ports)[0] = up[1];
8647c478bd9Sstevel@tonic-gate 		((uint16_t *)&ports)[1] = up[0];
865bd670b35SErik Nordmark 
866bd670b35SErik Nordmark 		ira->ira_flags |= IRAF_ICMP_ERROR;
867bd670b35SErik Nordmark 		ip_fanout_sctp(mp, NULL, &rip6h, ports, ira);
868bd670b35SErik Nordmark 		ira->ira_flags &= ~IRAF_ICMP_ERROR;
8697c478bd9Sstevel@tonic-gate 		return;
870bd670b35SErik Nordmark 
8717c478bd9Sstevel@tonic-gate 	case IPPROTO_ESP:
872bd670b35SErik Nordmark 	case IPPROTO_AH:
873f4b3ec61Sdh155122 		if (!ipsec_loaded(ipss)) {
874bd670b35SErik Nordmark 			ip_proto_not_sup(mp, ira);
8757c478bd9Sstevel@tonic-gate 			return;
8767c478bd9Sstevel@tonic-gate 		}
8777c478bd9Sstevel@tonic-gate 
8787c478bd9Sstevel@tonic-gate 		if (nexthdr == IPPROTO_ESP)
879bd670b35SErik Nordmark 			mp = ipsecesp_icmp_error(mp, ira);
8807c478bd9Sstevel@tonic-gate 		else
881bd670b35SErik Nordmark 			mp = ipsecah_icmp_error(mp, ira);
882bd670b35SErik Nordmark 		if (mp == NULL)
8837c478bd9Sstevel@tonic-gate 			return;
8847c478bd9Sstevel@tonic-gate 
885bd670b35SErik Nordmark 		/* Just in case ipsec didn't preserve the NULL b_cont */
886bd670b35SErik Nordmark 		if (mp->b_cont != NULL) {
887bd670b35SErik Nordmark 			if (!pullupmsg(mp, -1))
8887c478bd9Sstevel@tonic-gate 				goto drop_pkt;
88948de1bd2Skcpoon 		}
8907c478bd9Sstevel@tonic-gate 
8917c478bd9Sstevel@tonic-gate 		/*
892bd670b35SErik Nordmark 		 * If succesful, the mp has been modified to not include
893bd670b35SErik Nordmark 		 * the ESP/AH header so we can fanout to the ULP's icmp
894bd670b35SErik Nordmark 		 * error handler.
8957c478bd9Sstevel@tonic-gate 		 */
896bd670b35SErik Nordmark 		if (mp->b_wptr - mp->b_rptr < IPV6_HDR_LEN)
897bd670b35SErik Nordmark 			goto drop_pkt;
898bd670b35SErik Nordmark 
899bd670b35SErik Nordmark 		ip6h = (ip6_t *)mp->b_rptr;
900bd670b35SErik Nordmark 		/* Don't call hdr_length_v6() unless you have to. */
901bd670b35SErik Nordmark 		if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
902bd670b35SErik Nordmark 			hdr_length = ip_hdr_length_v6(mp, ip6h);
903bd670b35SErik Nordmark 		else
904bd670b35SErik Nordmark 			hdr_length = IPV6_HDR_LEN;
905bd670b35SErik Nordmark 
906bd670b35SErik Nordmark 		/* Verify the modified message before any further processes. */
907bd670b35SErik Nordmark 		icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
908bd670b35SErik Nordmark 		if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
909bd670b35SErik Nordmark 			freemsg(mp);
910bd670b35SErik Nordmark 			return;
9112b24ab6bSSebastien Roy 		}
912bd670b35SErik Nordmark 
913bd670b35SErik Nordmark 		icmp_inbound_error_fanout_v6(mp, icmp6, ira);
914bd670b35SErik Nordmark 		return;
915bd670b35SErik Nordmark 
916bd670b35SErik Nordmark 	case IPPROTO_IPV6: {
917bd670b35SErik Nordmark 		/* Look for self-encapsulated packets that caused an error */
918bd670b35SErik Nordmark 		ip6_t *in_ip6h;
919bd670b35SErik Nordmark 
920bd670b35SErik Nordmark 		in_ip6h = (ip6_t *)((uint8_t *)ip6h + hdr_length);
921bd670b35SErik Nordmark 
922bd670b35SErik Nordmark 		if (IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_src, &ip6h->ip6_src) &&
923bd670b35SErik Nordmark 		    IN6_ARE_ADDR_EQUAL(&in_ip6h->ip6_dst, &ip6h->ip6_dst)) {
9247c478bd9Sstevel@tonic-gate 			/*
9257c478bd9Sstevel@tonic-gate 			 * Self-encapsulated case. As in the ipv4 case,
9267c478bd9Sstevel@tonic-gate 			 * we need to strip the 2nd IP header. Since mp
9277c478bd9Sstevel@tonic-gate 			 * is already pulled-up, we can simply bcopy
9287c478bd9Sstevel@tonic-gate 			 * the 3rd header + data over the 2nd header.
9297c478bd9Sstevel@tonic-gate 			 */
9307c478bd9Sstevel@tonic-gate 			uint16_t unused_len;
9317c478bd9Sstevel@tonic-gate 
9327c478bd9Sstevel@tonic-gate 			/*
9337c478bd9Sstevel@tonic-gate 			 * Make sure we don't do recursion more than once.
9347c478bd9Sstevel@tonic-gate 			 */
935bd670b35SErik Nordmark 			if (!ip_hdr_length_nexthdr_v6(mp, in_ip6h,
9367c478bd9Sstevel@tonic-gate 			    &unused_len, &nexthdrp) ||
9377c478bd9Sstevel@tonic-gate 			    *nexthdrp == IPPROTO_IPV6) {
9387c478bd9Sstevel@tonic-gate 				goto drop_pkt;
9397c478bd9Sstevel@tonic-gate 			}
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 			/*
9427c478bd9Sstevel@tonic-gate 			 * Copy the 3rd header + remaining data on top
9437c478bd9Sstevel@tonic-gate 			 * of the 2nd header.
9447c478bd9Sstevel@tonic-gate 			 */
945bd670b35SErik Nordmark 			bcopy(in_ip6h, ip6h, mp->b_wptr - (uchar_t *)in_ip6h);
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 			/*
9487c478bd9Sstevel@tonic-gate 			 * Subtract length of the 2nd header.
9497c478bd9Sstevel@tonic-gate 			 */
9507c478bd9Sstevel@tonic-gate 			mp->b_wptr -= hdr_length;
9517c478bd9Sstevel@tonic-gate 
952bd670b35SErik Nordmark 			ip6h = (ip6_t *)mp->b_rptr;
953bd670b35SErik Nordmark 			/* Don't call hdr_length_v6() unless you have to. */
954bd670b35SErik Nordmark 			if (ip6h->ip6_nxt != IPPROTO_ICMPV6)
955bd670b35SErik Nordmark 				hdr_length = ip_hdr_length_v6(mp, ip6h);
956bd670b35SErik Nordmark 			else
957bd670b35SErik Nordmark 				hdr_length = IPV6_HDR_LEN;
958bd670b35SErik Nordmark 
959bd670b35SErik Nordmark 			/*
960bd670b35SErik Nordmark 			 * Verify the modified message before any further
961bd670b35SErik Nordmark 			 * processes.
962bd670b35SErik Nordmark 			 */
963bd670b35SErik Nordmark 			icmp6 = (icmp6_t *)(&mp->b_rptr[hdr_length]);
964bd670b35SErik Nordmark 			if (!icmp_inbound_verify_v6(mp, icmp6, ira)) {
965bd670b35SErik Nordmark 				freemsg(mp);
966bd670b35SErik Nordmark 				return;
967bd670b35SErik Nordmark 			}
968bd670b35SErik Nordmark 
9697c478bd9Sstevel@tonic-gate 			/*
9707c478bd9Sstevel@tonic-gate 			 * Now recurse, and see what I _really_ should be
9717c478bd9Sstevel@tonic-gate 			 * doing here.
9727c478bd9Sstevel@tonic-gate 			 */
973bd670b35SErik Nordmark 			icmp_inbound_error_fanout_v6(mp, icmp6, ira);
9747c478bd9Sstevel@tonic-gate 			return;
9757c478bd9Sstevel@tonic-gate 		}
976bd670b35SErik Nordmark 		/* FALLTHRU */
977bd670b35SErik Nordmark 	}
978bd670b35SErik Nordmark 	case IPPROTO_ENCAP:
979bd670b35SErik Nordmark 		if ((connp = ipcl_iptun_classify_v6(&rip6h.ip6_src,
980bd670b35SErik Nordmark 		    &rip6h.ip6_dst, ipst)) != NULL) {
981bd670b35SErik Nordmark 			ira->ira_flags |= IRAF_ICMP_ERROR;
982bd670b35SErik Nordmark 			connp->conn_recvicmp(connp, mp, NULL, ira);
983bd670b35SErik Nordmark 			CONN_DEC_REF(connp);
984bd670b35SErik Nordmark 			ira->ira_flags &= ~IRAF_ICMP_ERROR;
9852b24ab6bSSebastien Roy 			return;
986bd670b35SErik Nordmark 		}
9872b24ab6bSSebastien Roy 		/*
988bd670b35SErik Nordmark 		 * No IP tunnel is interested, fallthrough and see
989bd670b35SErik Nordmark 		 * if a raw socket will want it.
9902b24ab6bSSebastien Roy 		 */
9917c478bd9Sstevel@tonic-gate 		/* FALLTHRU */
9927c478bd9Sstevel@tonic-gate 	default:
993bd670b35SErik Nordmark 		ira->ira_flags |= IRAF_ICMP_ERROR;
994bd670b35SErik Nordmark 		ASSERT(ira->ira_protocol == nexthdr);
995bd670b35SErik Nordmark 		ip_fanout_proto_v6(mp, &rip6h, ira);
996bd670b35SErik Nordmark 		ira->ira_flags &= ~IRAF_ICMP_ERROR;
9977c478bd9Sstevel@tonic-gate 		return;
9987c478bd9Sstevel@tonic-gate 	}
9997c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
10007c478bd9Sstevel@tonic-gate drop_pkt:
10017c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInErrors);
10027c478bd9Sstevel@tonic-gate 	ip1dbg(("icmp_inbound_error_fanout_v6: drop pkt\n"));
1003bd670b35SErik Nordmark 	freemsg(mp);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate 
10067c478bd9Sstevel@tonic-gate /*
10077c478bd9Sstevel@tonic-gate  * Process received IPv6 ICMP Redirect messages.
1008bd670b35SErik Nordmark  * Assumes the caller has verified that the headers are in the pulled up mblk.
1009bd670b35SErik Nordmark  * Consumes mp.
10107c478bd9Sstevel@tonic-gate  */
10117c478bd9Sstevel@tonic-gate /* ARGSUSED */
10127c478bd9Sstevel@tonic-gate static void
icmp_redirect_v6(mblk_t * mp,ip6_t * ip6h,nd_redirect_t * rd,ip_recv_attr_t * ira)1013bd670b35SErik Nordmark icmp_redirect_v6(mblk_t *mp, ip6_t *ip6h, nd_redirect_t *rd,
1014bd670b35SErik Nordmark     ip_recv_attr_t *ira)
10157c478bd9Sstevel@tonic-gate {
1016bd670b35SErik Nordmark 	ire_t		*ire, *nire;
1017bd670b35SErik Nordmark 	ire_t		*prev_ire = NULL;
10187c478bd9Sstevel@tonic-gate 	ire_t		*redir_ire;
10197c478bd9Sstevel@tonic-gate 	in6_addr_t	*src, *dst, *gateway;
10207c478bd9Sstevel@tonic-gate 	nd_opt_hdr_t	*opt;
10217c478bd9Sstevel@tonic-gate 	nce_t		*nce;
1022bd670b35SErik Nordmark 	int		ncec_flags = 0;
10237c478bd9Sstevel@tonic-gate 	int		err = 0;
10247c478bd9Sstevel@tonic-gate 	boolean_t	redirect_to_router = B_FALSE;
10257c478bd9Sstevel@tonic-gate 	int		len;
10266bdb8e66Sdd193516 	int		optlen;
1027bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_rill;
1028bd670b35SErik Nordmark 	ill_t		*rill = ira->ira_rill;
1029f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
10307c478bd9Sstevel@tonic-gate 
1031bd670b35SErik Nordmark 	/*
1032bd670b35SErik Nordmark 	 * Since ira_ill is where the IRE_LOCAL was hosted we use ira_rill
1033bd670b35SErik Nordmark 	 * and make it be the IPMP upper so avoid being confused by a packet
1034bd670b35SErik Nordmark 	 * addressed to a unicast address on a different ill.
1035bd670b35SErik Nordmark 	 */
1036bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(rill)) {
1037bd670b35SErik Nordmark 		rill = ipmp_ill_hold_ipmp_ill(rill);
1038bd670b35SErik Nordmark 		if (rill == NULL) {
1039bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1040bd670b35SErik Nordmark 			ip_drop_input("ipv6IfIcmpInBadRedirects - IPMP ill",
1041bd670b35SErik Nordmark 			    mp, ill);
1042bd670b35SErik Nordmark 			freemsg(mp);
1043bd670b35SErik Nordmark 			return;
1044bd670b35SErik Nordmark 		}
1045bd670b35SErik Nordmark 		ASSERT(rill != ira->ira_rill);
1046bd670b35SErik Nordmark 	}
10477c478bd9Sstevel@tonic-gate 
1048bd670b35SErik Nordmark 	len = mp->b_wptr - (uchar_t *)rd;
10497c478bd9Sstevel@tonic-gate 	src = &ip6h->ip6_src;
10507c478bd9Sstevel@tonic-gate 	dst = &rd->nd_rd_dst;
10517c478bd9Sstevel@tonic-gate 	gateway = &rd->nd_rd_target;
10526bdb8e66Sdd193516 
10536bdb8e66Sdd193516 	/* Verify if it is a valid redirect */
10546bdb8e66Sdd193516 	if (!IN6_IS_ADDR_LINKLOCAL(src) ||
10556bdb8e66Sdd193516 	    (ip6h->ip6_hops != IPV6_MAX_HOPS) ||
10566bdb8e66Sdd193516 	    (rd->nd_rd_code != 0) ||
10576bdb8e66Sdd193516 	    (len < sizeof (nd_redirect_t)) ||
10586bdb8e66Sdd193516 	    (IN6_IS_ADDR_V4MAPPED(dst)) ||
10596bdb8e66Sdd193516 	    (IN6_IS_ADDR_MULTICAST(dst))) {
10606bdb8e66Sdd193516 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1061bd670b35SErik Nordmark 		ip_drop_input("ipv6IfIcmpInBadRedirects - addr/len", mp, ill);
1062bd670b35SErik Nordmark 		goto fail_redirect;
10636bdb8e66Sdd193516 	}
10646bdb8e66Sdd193516 
10656bdb8e66Sdd193516 	if (!(IN6_IS_ADDR_LINKLOCAL(gateway) ||
10666bdb8e66Sdd193516 	    IN6_ARE_ADDR_EQUAL(gateway, dst))) {
10676bdb8e66Sdd193516 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1068bd670b35SErik Nordmark 		ip_drop_input("ipv6IfIcmpInBadRedirects - bad gateway",
1069bd670b35SErik Nordmark 		    mp, ill);
1070bd670b35SErik Nordmark 		goto fail_redirect;
10716bdb8e66Sdd193516 	}
10726bdb8e66Sdd193516 
1073bd670b35SErik Nordmark 	optlen = len - sizeof (nd_redirect_t);
1074bd670b35SErik Nordmark 	if (optlen != 0) {
1075bd670b35SErik Nordmark 		if (!ndp_verify_optlen((nd_opt_hdr_t *)&rd[1], optlen)) {
10766bdb8e66Sdd193516 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1077bd670b35SErik Nordmark 			ip_drop_input("ipv6IfIcmpInBadRedirects - options",
1078bd670b35SErik Nordmark 			    mp, ill);
1079bd670b35SErik Nordmark 			goto fail_redirect;
10806bdb8e66Sdd193516 		}
10816bdb8e66Sdd193516 	}
10826bdb8e66Sdd193516 
10837c478bd9Sstevel@tonic-gate 	if (!IN6_ARE_ADDR_EQUAL(gateway, dst)) {
10847c478bd9Sstevel@tonic-gate 		redirect_to_router = B_TRUE;
1085bd670b35SErik Nordmark 		ncec_flags |= NCE_F_ISROUTER;
1086bd670b35SErik Nordmark 	} else {
1087bd670b35SErik Nordmark 		gateway = dst;	/* Add nce for dst */
10887c478bd9Sstevel@tonic-gate 	}
10896bdb8e66Sdd193516 
10906bdb8e66Sdd193516 
10916bdb8e66Sdd193516 	/*
10926bdb8e66Sdd193516 	 * Verify that the IP source address of the redirect is
10936bdb8e66Sdd193516 	 * the same as the current first-hop router for the specified
10946bdb8e66Sdd193516 	 * ICMP destination address.
10956bdb8e66Sdd193516 	 * Also, Make sure we had a route for the dest in question and
10966bdb8e66Sdd193516 	 * that route was pointing to the old gateway (the source of the
10976bdb8e66Sdd193516 	 * redirect packet.)
10986b7506c7SErik Nordmark 	 * We do longest match and then compare ire_gateway_addr_v6 below.
10996bdb8e66Sdd193516 	 */
11006b7506c7SErik Nordmark 	prev_ire = ire_ftable_lookup_v6(dst, 0, 0, 0, rill,
11016b7506c7SErik Nordmark 	    ALL_ZONES, NULL, MATCH_IRE_ILL, 0, ipst, NULL);
11026bdb8e66Sdd193516 
11037c478bd9Sstevel@tonic-gate 	/*
11047c478bd9Sstevel@tonic-gate 	 * Check that
11057c478bd9Sstevel@tonic-gate 	 *	the redirect was not from ourselves
11067c478bd9Sstevel@tonic-gate 	 *	old gateway is still directly reachable
11077c478bd9Sstevel@tonic-gate 	 */
11087c478bd9Sstevel@tonic-gate 	if (prev_ire == NULL ||
1109bd670b35SErik Nordmark 	    (prev_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK)) ||
11106b7506c7SErik Nordmark 	    (prev_ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) ||
11116b7506c7SErik Nordmark 	    !IN6_ARE_ADDR_EQUAL(src, &prev_ire->ire_gateway_addr_v6)) {
11127c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1113bd670b35SErik Nordmark 		ip_drop_input("ipv6IfIcmpInBadRedirects - ire", mp, ill);
11147c478bd9Sstevel@tonic-gate 		goto fail_redirect;
11157c478bd9Sstevel@tonic-gate 	}
11167c478bd9Sstevel@tonic-gate 
1117bd670b35SErik Nordmark 	ASSERT(prev_ire->ire_ill != NULL);
1118bd670b35SErik Nordmark 	if (prev_ire->ire_ill->ill_flags & ILLF_NONUD)
1119bd670b35SErik Nordmark 		ncec_flags |= NCE_F_NONUD;
11207c478bd9Sstevel@tonic-gate 
11217c478bd9Sstevel@tonic-gate 	opt = (nd_opt_hdr_t *)&rd[1];
11226bdb8e66Sdd193516 	opt = ndp_get_option(opt, optlen, ND_OPT_TARGET_LINKADDR);
11237c478bd9Sstevel@tonic-gate 	if (opt != NULL) {
1124bd670b35SErik Nordmark 		err = nce_lookup_then_add_v6(rill,
11257c478bd9Sstevel@tonic-gate 		    (uchar_t *)&opt[1],		/* Link layer address */
1126bd670b35SErik Nordmark 		    rill->ill_phys_addr_length,
1127bd670b35SErik Nordmark 		    gateway, ncec_flags, ND_STALE, &nce);
11287c478bd9Sstevel@tonic-gate 		switch (err) {
11297c478bd9Sstevel@tonic-gate 		case 0:
1130bd670b35SErik Nordmark 			nce_refrele(nce);
11317c478bd9Sstevel@tonic-gate 			break;
11327c478bd9Sstevel@tonic-gate 		case EEXIST:
11337c478bd9Sstevel@tonic-gate 			/*
11347c478bd9Sstevel@tonic-gate 			 * Check to see if link layer address has changed and
1135bd670b35SErik Nordmark 			 * process the ncec_state accordingly.
11367c478bd9Sstevel@tonic-gate 			 */
1137bd670b35SErik Nordmark 			nce_process(nce->nce_common,
1138bd670b35SErik Nordmark 			    (uchar_t *)&opt[1], 0, B_FALSE);
1139bd670b35SErik Nordmark 			nce_refrele(nce);
11407c478bd9Sstevel@tonic-gate 			break;
11417c478bd9Sstevel@tonic-gate 		default:
11427c478bd9Sstevel@tonic-gate 			ip1dbg(("icmp_redirect_v6: NCE create failed %d\n",
11437c478bd9Sstevel@tonic-gate 			    err));
11447c478bd9Sstevel@tonic-gate 			goto fail_redirect;
11457c478bd9Sstevel@tonic-gate 		}
11467c478bd9Sstevel@tonic-gate 	}
11477c478bd9Sstevel@tonic-gate 	if (redirect_to_router) {
11487c478bd9Sstevel@tonic-gate 		ASSERT(IN6_IS_ADDR_LINKLOCAL(gateway));
11497c478bd9Sstevel@tonic-gate 
11507c478bd9Sstevel@tonic-gate 		/*
11517c478bd9Sstevel@tonic-gate 		 * Create a Route Association.  This will allow us to remember
11527c478bd9Sstevel@tonic-gate 		 * a router told us to use the particular gateway.
11537c478bd9Sstevel@tonic-gate 		 */
11547c478bd9Sstevel@tonic-gate 		ire = ire_create_v6(
11557c478bd9Sstevel@tonic-gate 		    dst,
11567c478bd9Sstevel@tonic-gate 		    &ipv6_all_ones,		/* mask */
11577c478bd9Sstevel@tonic-gate 		    gateway,			/* gateway addr */
11586bdb8e66Sdd193516 		    IRE_HOST,
1159bd670b35SErik Nordmark 		    prev_ire->ire_ill,
1160bd670b35SErik Nordmark 		    ALL_ZONES,
11617c478bd9Sstevel@tonic-gate 		    (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST),
1162f4b3ec61Sdh155122 		    NULL,
1163f4b3ec61Sdh155122 		    ipst);
11647c478bd9Sstevel@tonic-gate 	} else {
1165bd670b35SErik Nordmark 		ipif_t *ipif;
1166bd670b35SErik Nordmark 		in6_addr_t gw;
11676bdb8e66Sdd193516 
11687c478bd9Sstevel@tonic-gate 		/*
11696bdb8e66Sdd193516 		 * Just create an on link entry, i.e. interface route.
1170bd670b35SErik Nordmark 		 * The gateway field is our link-local on the ill.
11717c478bd9Sstevel@tonic-gate 		 */
1172bd670b35SErik Nordmark 		mutex_enter(&rill->ill_lock);
1173bd670b35SErik Nordmark 		for (ipif = rill->ill_ipif; ipif != NULL;
1174bd670b35SErik Nordmark 		    ipif = ipif->ipif_next) {
1175bd670b35SErik Nordmark 			if (!(ipif->ipif_state_flags & IPIF_CONDEMNED) &&
1176bd670b35SErik Nordmark 			    IN6_IS_ADDR_LINKLOCAL(&ipif->ipif_v6lcl_addr))
1177bd670b35SErik Nordmark 				break;
1178bd670b35SErik Nordmark 		}
1179bd670b35SErik Nordmark 		if (ipif == NULL) {
1180bd670b35SErik Nordmark 			/* We have no link-local address! */
1181bd670b35SErik Nordmark 			mutex_exit(&rill->ill_lock);
1182bd670b35SErik Nordmark 			goto fail_redirect;
1183bd670b35SErik Nordmark 		}
1184bd670b35SErik Nordmark 		gw = ipif->ipif_v6lcl_addr;
1185bd670b35SErik Nordmark 		mutex_exit(&rill->ill_lock);
1186bd670b35SErik Nordmark 
11877c478bd9Sstevel@tonic-gate 		ire = ire_create_v6(
11887c478bd9Sstevel@tonic-gate 		    dst,				/* gateway == dst */
11897c478bd9Sstevel@tonic-gate 		    &ipv6_all_ones,			/* mask */
1190bd670b35SErik Nordmark 		    &gw,				/* gateway addr */
1191bd670b35SErik Nordmark 		    rill->ill_net_type,			/* IF_[NO]RESOLVER */
1192bd670b35SErik Nordmark 		    prev_ire->ire_ill,
1193bd670b35SErik Nordmark 		    ALL_ZONES,
11946bdb8e66Sdd193516 		    (RTF_DYNAMIC | RTF_HOST),
1195f4b3ec61Sdh155122 		    NULL,
1196f4b3ec61Sdh155122 		    ipst);
11977c478bd9Sstevel@tonic-gate 	}
11986bdb8e66Sdd193516 
11997c478bd9Sstevel@tonic-gate 	if (ire == NULL)
12007c478bd9Sstevel@tonic-gate 		goto fail_redirect;
12017c478bd9Sstevel@tonic-gate 
1202bd670b35SErik Nordmark 	nire = ire_add(ire);
1203bd670b35SErik Nordmark 	/* Check if it was a duplicate entry */
1204bd670b35SErik Nordmark 	if (nire != NULL && nire != ire) {
1205bd670b35SErik Nordmark 		ASSERT(nire->ire_identical_ref > 1);
1206bd670b35SErik Nordmark 		ire_delete(nire);
1207bd670b35SErik Nordmark 		ire_refrele(nire);
1208bd670b35SErik Nordmark 		nire = NULL;
1209bd670b35SErik Nordmark 	}
1210bd670b35SErik Nordmark 	ire = nire;
1211bd670b35SErik Nordmark 	if (ire != NULL) {
1212bd670b35SErik Nordmark 		ire_refrele(ire);		/* Held in ire_add */
12137c478bd9Sstevel@tonic-gate 
12147c478bd9Sstevel@tonic-gate 		/* tell routing sockets that we received a redirect */
12157c478bd9Sstevel@tonic-gate 		ip_rts_change_v6(RTM_REDIRECT,
12167c478bd9Sstevel@tonic-gate 		    &rd->nd_rd_dst,
12177c478bd9Sstevel@tonic-gate 		    &rd->nd_rd_target,
1218bd670b35SErik Nordmark 		    &ipv6_all_ones, 0, src,
12197c478bd9Sstevel@tonic-gate 		    (RTF_DYNAMIC | RTF_GATEWAY | RTF_HOST), 0,
1220f4b3ec61Sdh155122 		    (RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_AUTHOR), ipst);
12217c478bd9Sstevel@tonic-gate 
12227c478bd9Sstevel@tonic-gate 		/*
12236bdb8e66Sdd193516 		 * Delete any existing IRE_HOST type ires for this destination.
12247c478bd9Sstevel@tonic-gate 		 * This together with the added IRE has the effect of
12257c478bd9Sstevel@tonic-gate 		 * modifying an existing redirect.
12267c478bd9Sstevel@tonic-gate 		 */
12276bdb8e66Sdd193516 		redir_ire = ire_ftable_lookup_v6(dst, 0, src, IRE_HOST,
1228bd670b35SErik Nordmark 		    prev_ire->ire_ill, ALL_ZONES, NULL,
1229bd670b35SErik Nordmark 		    (MATCH_IRE_GW | MATCH_IRE_TYPE | MATCH_IRE_ILL), 0, ipst,
1230bd670b35SErik Nordmark 		    NULL);
12317c478bd9Sstevel@tonic-gate 
12327c478bd9Sstevel@tonic-gate 		if (redir_ire != NULL) {
12336bdb8e66Sdd193516 			if (redir_ire->ire_flags & RTF_DYNAMIC)
12347c478bd9Sstevel@tonic-gate 				ire_delete(redir_ire);
12357c478bd9Sstevel@tonic-gate 			ire_refrele(redir_ire);
12367c478bd9Sstevel@tonic-gate 		}
12377c478bd9Sstevel@tonic-gate 	}
12387c478bd9Sstevel@tonic-gate 
12397c478bd9Sstevel@tonic-gate 	ire_refrele(prev_ire);
12407c478bd9Sstevel@tonic-gate 	prev_ire = NULL;
12417c478bd9Sstevel@tonic-gate 
12427c478bd9Sstevel@tonic-gate fail_redirect:
12437c478bd9Sstevel@tonic-gate 	if (prev_ire != NULL)
12447c478bd9Sstevel@tonic-gate 		ire_refrele(prev_ire);
12457c478bd9Sstevel@tonic-gate 	freemsg(mp);
1246bd670b35SErik Nordmark 	if (rill != ira->ira_rill)
1247bd670b35SErik Nordmark 		ill_refrele(rill);
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate 
12507c478bd9Sstevel@tonic-gate /*
12517c478bd9Sstevel@tonic-gate  * Build and ship an IPv6 ICMP message using the packet data in mp,
12527c478bd9Sstevel@tonic-gate  * and the ICMP header pointed to by "stuff".  (May be called as
12537c478bd9Sstevel@tonic-gate  * writer.)
12547c478bd9Sstevel@tonic-gate  * Note: assumes that icmp_pkt_err_ok_v6 has been called to
12557c478bd9Sstevel@tonic-gate  * verify that an icmp error packet can be sent.
12567c478bd9Sstevel@tonic-gate  *
12577c478bd9Sstevel@tonic-gate  * If v6src_ptr is set use it as a source. Otherwise select a reasonable
12587c478bd9Sstevel@tonic-gate  * source address (see above function).
12597c478bd9Sstevel@tonic-gate  */
12607c478bd9Sstevel@tonic-gate static void
icmp_pkt_v6(mblk_t * mp,void * stuff,size_t len,const in6_addr_t * v6src_ptr,ip_recv_attr_t * ira)1261bd670b35SErik Nordmark icmp_pkt_v6(mblk_t *mp, void *stuff, size_t len,
1262bd670b35SErik Nordmark     const in6_addr_t *v6src_ptr, ip_recv_attr_t *ira)
12637c478bd9Sstevel@tonic-gate {
12647c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h;
12657c478bd9Sstevel@tonic-gate 	in6_addr_t	v6dst;
12667c478bd9Sstevel@tonic-gate 	size_t		len_needed;
12677c478bd9Sstevel@tonic-gate 	size_t		msg_len;
12687c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
12697c478bd9Sstevel@tonic-gate 	icmp6_t		*icmp6;
12707c478bd9Sstevel@tonic-gate 	in6_addr_t	v6src;
1271bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
1272bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
1273bd670b35SErik Nordmark 	ip_xmit_attr_t	ixas;
12747c478bd9Sstevel@tonic-gate 
12757c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
12767c478bd9Sstevel@tonic-gate 
1277bd670b35SErik Nordmark 	bzero(&ixas, sizeof (ixas));
1278bd670b35SErik Nordmark 	ixas.ixa_flags = IXAF_BASIC_SIMPLE_V6;
1279bd670b35SErik Nordmark 	ixas.ixa_zoneid = ira->ira_zoneid;
1280bd670b35SErik Nordmark 	ixas.ixa_ifindex = 0;
1281bd670b35SErik Nordmark 	ixas.ixa_ipst = ipst;
1282bd670b35SErik Nordmark 	ixas.ixa_cred = kcred;
1283bd670b35SErik Nordmark 	ixas.ixa_cpid = NOPID;
1284bd670b35SErik Nordmark 	ixas.ixa_tsl = ira->ira_tsl;	/* Behave as a multi-level responder */
1285bd670b35SErik Nordmark 	ixas.ixa_multicast_ttl = IP_DEFAULT_MULTICAST_TTL;
12867c478bd9Sstevel@tonic-gate 
12877c478bd9Sstevel@tonic-gate 	/*
1288bd670b35SErik Nordmark 	 * If the source of the original packet was link-local, then
1289bd670b35SErik Nordmark 	 * make sure we send on the same ill (group) as we received it on.
12907c478bd9Sstevel@tonic-gate 	 */
1291bd670b35SErik Nordmark 	if (IN6_IS_ADDR_LINKSCOPE(&ip6h->ip6_src)) {
1292bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_SCOPEID_SET;
1293bd670b35SErik Nordmark 		if (IS_UNDER_IPMP(ill))
1294bd670b35SErik Nordmark 			ixas.ixa_scopeid = ill_get_upper_ifindex(ill);
1295bd670b35SErik Nordmark 		else
1296bd670b35SErik Nordmark 			ixas.ixa_scopeid = ill->ill_phyint->phyint_ifindex;
12977c478bd9Sstevel@tonic-gate 	}
1298bd670b35SErik Nordmark 
1299bd670b35SErik Nordmark 	if (ira->ira_flags & IRAF_IPSEC_SECURE) {
13007c478bd9Sstevel@tonic-gate 		/*
1301bd670b35SErik Nordmark 		 * Apply IPsec based on how IPsec was applied to
1302bd670b35SErik Nordmark 		 * the packet that had the error.
1303bd670b35SErik Nordmark 		 *
1304bd670b35SErik Nordmark 		 * If it was an outbound packet that caused the ICMP
1305bd670b35SErik Nordmark 		 * error, then the caller will have setup the IRA
1306bd670b35SErik Nordmark 		 * appropriately.
13077c478bd9Sstevel@tonic-gate 		 */
1308bd670b35SErik Nordmark 		if (!ipsec_in_to_out(ira, &ixas, mp, NULL, ip6h)) {
1309bd670b35SErik Nordmark 			BUMP_MIB(&ipst->ips_ip_mib, ipIfStatsOutDiscards);
1310bd670b35SErik Nordmark 			/* Note: mp already consumed and ip_drop_packet done */
1311bd670b35SErik Nordmark 			return;
13127c478bd9Sstevel@tonic-gate 		}
13137c478bd9Sstevel@tonic-gate 	} else {
13147c478bd9Sstevel@tonic-gate 		/*
13157c478bd9Sstevel@tonic-gate 		 * This is in clear. The icmp message we are building
1316bd670b35SErik Nordmark 		 * here should go out in clear, independent of our policy.
13177c478bd9Sstevel@tonic-gate 		 */
1318bd670b35SErik Nordmark 		ixas.ixa_flags |= IXAF_NO_IPSEC;
13197c478bd9Sstevel@tonic-gate 	}
13207c478bd9Sstevel@tonic-gate 
13217c478bd9Sstevel@tonic-gate 	/*
1322bd670b35SErik Nordmark 	 * If the caller specified the source we use that.
1323bd670b35SErik Nordmark 	 * Otherwise, if the packet was for one of our unicast addresses, make
1324bd670b35SErik Nordmark 	 * sure we respond with that as the source. Otherwise
1325bd670b35SErik Nordmark 	 * have ip_output_simple pick the source address.
13267c478bd9Sstevel@tonic-gate 	 */
13277c478bd9Sstevel@tonic-gate 	if (v6src_ptr != NULL) {
13287c478bd9Sstevel@tonic-gate 		v6src = *v6src_ptr;
13297c478bd9Sstevel@tonic-gate 	} else {
1330bd670b35SErik Nordmark 		ire_t *ire;
1331bd670b35SErik Nordmark 		uint_t match_flags = MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY;
1332bd670b35SErik Nordmark 
1333bd670b35SErik Nordmark 		if (IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ||
1334bd670b35SErik Nordmark 		    IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst))
1335bd670b35SErik Nordmark 			match_flags |= MATCH_IRE_ILL;
1336bd670b35SErik Nordmark 
1337bd670b35SErik Nordmark 		ire = ire_ftable_lookup_v6(&ip6h->ip6_dst, 0, 0,
1338bd670b35SErik Nordmark 		    (IRE_LOCAL|IRE_LOOPBACK), ill, ira->ira_zoneid, NULL,
1339bd670b35SErik Nordmark 		    match_flags, 0, ipst, NULL);
1340bd670b35SErik Nordmark 		if (ire != NULL) {
1341bd670b35SErik Nordmark 			v6src = ip6h->ip6_dst;
1342bd670b35SErik Nordmark 			ire_refrele(ire);
1343bd670b35SErik Nordmark 		} else {
1344bd670b35SErik Nordmark 			v6src = ipv6_all_zeros;
1345bd670b35SErik Nordmark 			ixas.ixa_flags |= IXAF_SET_SOURCE;
13467c478bd9Sstevel@tonic-gate 		}
13477c478bd9Sstevel@tonic-gate 	}
13487c478bd9Sstevel@tonic-gate 	v6dst = ip6h->ip6_src;
1349f4b3ec61Sdh155122 	len_needed = ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len;
13507c478bd9Sstevel@tonic-gate 	msg_len = msgdsize(mp);
13517c478bd9Sstevel@tonic-gate 	if (msg_len > len_needed) {
13527c478bd9Sstevel@tonic-gate 		if (!adjmsg(mp, len_needed - msg_len)) {
13537c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
1354bd670b35SErik Nordmark 			freemsg(mp);
13557c478bd9Sstevel@tonic-gate 			return;
13567c478bd9Sstevel@tonic-gate 		}
13577c478bd9Sstevel@tonic-gate 		msg_len = len_needed;
13587c478bd9Sstevel@tonic-gate 	}
1359bd670b35SErik Nordmark 	mp1 = allocb(IPV6_HDR_LEN + len, BPRI_MED);
13607c478bd9Sstevel@tonic-gate 	if (mp1 == NULL) {
13617c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
1362bd670b35SErik Nordmark 		freemsg(mp);
13637c478bd9Sstevel@tonic-gate 		return;
13647c478bd9Sstevel@tonic-gate 	}
13657c478bd9Sstevel@tonic-gate 	mp1->b_cont = mp;
13667c478bd9Sstevel@tonic-gate 	mp = mp1;
13677c478bd9Sstevel@tonic-gate 
13687c478bd9Sstevel@tonic-gate 	/*
1369bd670b35SErik Nordmark 	 * Set IXAF_TRUSTED_ICMP so we can let the ICMP messages this
13707c478bd9Sstevel@tonic-gate 	 * node generates be accepted in peace by all on-host destinations.
13717c478bd9Sstevel@tonic-gate 	 * If we do NOT assume that all on-host destinations trust
1372bd670b35SErik Nordmark 	 * self-generated ICMP messages, then rework here, ip6.c, and spd.c.
1373bd670b35SErik Nordmark 	 * (Look for IXAF_TRUSTED_ICMP).
13747c478bd9Sstevel@tonic-gate 	 */
1375bd670b35SErik Nordmark 	ixas.ixa_flags |= IXAF_TRUSTED_ICMP;
13767c478bd9Sstevel@tonic-gate 
13777c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
13787c478bd9Sstevel@tonic-gate 	mp1->b_wptr = (uchar_t *)ip6h + (IPV6_HDR_LEN + len);
13797c478bd9Sstevel@tonic-gate 
13807c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf = IPV6_DEFAULT_VERS_AND_FLOW;
13817c478bd9Sstevel@tonic-gate 	ip6h->ip6_nxt = IPPROTO_ICMPV6;
1382f4b3ec61Sdh155122 	ip6h->ip6_hops = ipst->ips_ipv6_def_hops;
13837c478bd9Sstevel@tonic-gate 	ip6h->ip6_dst = v6dst;
13847c478bd9Sstevel@tonic-gate 	ip6h->ip6_src = v6src;
13857c478bd9Sstevel@tonic-gate 	msg_len += IPV6_HDR_LEN + len;
13867c478bd9Sstevel@tonic-gate 	if (msg_len > IP_MAXPACKET + IPV6_HDR_LEN) {
13877c478bd9Sstevel@tonic-gate 		(void) adjmsg(mp, IP_MAXPACKET + IPV6_HDR_LEN - msg_len);
13887c478bd9Sstevel@tonic-gate 		msg_len = IP_MAXPACKET + IPV6_HDR_LEN;
13897c478bd9Sstevel@tonic-gate 	}
13907c478bd9Sstevel@tonic-gate 	ip6h->ip6_plen = htons((uint16_t)(msgdsize(mp) - IPV6_HDR_LEN));
13917c478bd9Sstevel@tonic-gate 	icmp6 = (icmp6_t *)&ip6h[1];
13927c478bd9Sstevel@tonic-gate 	bcopy(stuff, (char *)icmp6, len);
13937c478bd9Sstevel@tonic-gate 	/*
13947c478bd9Sstevel@tonic-gate 	 * Prepare for checksum by putting icmp length in the icmp
1395bd670b35SErik Nordmark 	 * checksum field. The checksum is calculated in ip_output_wire_v6.
13967c478bd9Sstevel@tonic-gate 	 */
13977c478bd9Sstevel@tonic-gate 	icmp6->icmp6_cksum = ip6h->ip6_plen;
13987c478bd9Sstevel@tonic-gate 	if (icmp6->icmp6_type == ND_REDIRECT) {
13997c478bd9Sstevel@tonic-gate 		ip6h->ip6_hops = IPV6_MAX_HOPS;
14007c478bd9Sstevel@tonic-gate 	}
1401bd670b35SErik Nordmark 
1402bd670b35SErik Nordmark 	(void) ip_output_simple(mp, &ixas);
1403bd670b35SErik Nordmark 	ixa_cleanup(&ixas);
14047c478bd9Sstevel@tonic-gate }
14057c478bd9Sstevel@tonic-gate 
14067c478bd9Sstevel@tonic-gate /*
14077c478bd9Sstevel@tonic-gate  * Update the output mib when ICMPv6 packets are sent.
14087c478bd9Sstevel@tonic-gate  */
1409bd670b35SErik Nordmark void
icmp_update_out_mib_v6(ill_t * ill,icmp6_t * icmp6)14107c478bd9Sstevel@tonic-gate icmp_update_out_mib_v6(ill_t *ill, icmp6_t *icmp6)
14117c478bd9Sstevel@tonic-gate {
14127c478bd9Sstevel@tonic-gate 	BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutMsgs);
14137c478bd9Sstevel@tonic-gate 
14147c478bd9Sstevel@tonic-gate 	switch (icmp6->icmp6_type) {
14157c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
14167c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutDestUnreachs);
14177c478bd9Sstevel@tonic-gate 		if (icmp6->icmp6_code == ICMP6_DST_UNREACH_ADMIN)
14187c478bd9Sstevel@tonic-gate 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutAdminProhibs);
14197c478bd9Sstevel@tonic-gate 		break;
14207c478bd9Sstevel@tonic-gate 
14217c478bd9Sstevel@tonic-gate 	case ICMP6_TIME_EXCEEDED:
14227c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutTimeExcds);
14237c478bd9Sstevel@tonic-gate 		break;
14247c478bd9Sstevel@tonic-gate 
14257c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
14267c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutParmProblems);
14277c478bd9Sstevel@tonic-gate 		break;
14287c478bd9Sstevel@tonic-gate 
14297c478bd9Sstevel@tonic-gate 	case ICMP6_PACKET_TOO_BIG:
14307c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutPktTooBigs);
14317c478bd9Sstevel@tonic-gate 		break;
14327c478bd9Sstevel@tonic-gate 
14337c478bd9Sstevel@tonic-gate 	case ICMP6_ECHO_REQUEST:
14347c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchos);
14357c478bd9Sstevel@tonic-gate 		break;
14367c478bd9Sstevel@tonic-gate 
14377c478bd9Sstevel@tonic-gate 	case ICMP6_ECHO_REPLY:
14387c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutEchoReplies);
14397c478bd9Sstevel@tonic-gate 		break;
14407c478bd9Sstevel@tonic-gate 
14417c478bd9Sstevel@tonic-gate 	case ND_ROUTER_SOLICIT:
14427c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterSolicits);
14437c478bd9Sstevel@tonic-gate 		break;
14447c478bd9Sstevel@tonic-gate 
14457c478bd9Sstevel@tonic-gate 	case ND_ROUTER_ADVERT:
14467c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRouterAdvertisements);
14477c478bd9Sstevel@tonic-gate 		break;
14487c478bd9Sstevel@tonic-gate 
14497c478bd9Sstevel@tonic-gate 	case ND_NEIGHBOR_SOLICIT:
14507c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutNeighborSolicits);
14517c478bd9Sstevel@tonic-gate 		break;
14527c478bd9Sstevel@tonic-gate 
14537c478bd9Sstevel@tonic-gate 	case ND_NEIGHBOR_ADVERT:
14547c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib,
14557c478bd9Sstevel@tonic-gate 		    ipv6IfIcmpOutNeighborAdvertisements);
14567c478bd9Sstevel@tonic-gate 		break;
14577c478bd9Sstevel@tonic-gate 
14587c478bd9Sstevel@tonic-gate 	case ND_REDIRECT:
14597c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutRedirects);
14607c478bd9Sstevel@tonic-gate 		break;
14617c478bd9Sstevel@tonic-gate 
14627c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_QUERY:
14637c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembQueries);
14647c478bd9Sstevel@tonic-gate 		break;
14657c478bd9Sstevel@tonic-gate 
14667c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REPORT:
14677c478bd9Sstevel@tonic-gate 	case MLD_V2_LISTENER_REPORT:
14687c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembResponses);
14697c478bd9Sstevel@tonic-gate 		break;
14707c478bd9Sstevel@tonic-gate 
14717c478bd9Sstevel@tonic-gate 	case MLD_LISTENER_REDUCTION:
14727c478bd9Sstevel@tonic-gate 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutGroupMembReductions);
14737c478bd9Sstevel@tonic-gate 		break;
14747c478bd9Sstevel@tonic-gate 	}
14757c478bd9Sstevel@tonic-gate }
14767c478bd9Sstevel@tonic-gate 
14777c478bd9Sstevel@tonic-gate /*
14787c478bd9Sstevel@tonic-gate  * Check if it is ok to send an ICMPv6 error packet in
14797c478bd9Sstevel@tonic-gate  * response to the IP packet in mp.
14807c478bd9Sstevel@tonic-gate  * Free the message and return null if no
14817c478bd9Sstevel@tonic-gate  * ICMP error packet should be sent.
14827c478bd9Sstevel@tonic-gate  */
14837c478bd9Sstevel@tonic-gate static mblk_t *
icmp_pkt_err_ok_v6(mblk_t * mp,boolean_t mcast_ok,ip_recv_attr_t * ira)1484bd670b35SErik Nordmark icmp_pkt_err_ok_v6(mblk_t *mp, boolean_t mcast_ok, ip_recv_attr_t *ira)
14857c478bd9Sstevel@tonic-gate {
1486bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
1487bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
1488bd670b35SErik Nordmark 	boolean_t	llbcast;
14897c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h;
14907c478bd9Sstevel@tonic-gate 
14917c478bd9Sstevel@tonic-gate 	if (!mp)
14927c478bd9Sstevel@tonic-gate 		return (NULL);
14937c478bd9Sstevel@tonic-gate 
1494bd670b35SErik Nordmark 	/* We view multicast and broadcast as the same.. */
1495bd670b35SErik Nordmark 	llbcast = (ira->ira_flags &
1496bd670b35SErik Nordmark 	    (IRAF_L2DST_MULTICAST|IRAF_L2DST_BROADCAST)) != 0;
14977c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
14987c478bd9Sstevel@tonic-gate 
14997c478bd9Sstevel@tonic-gate 	/* Check if source address uniquely identifies the host */
15007c478bd9Sstevel@tonic-gate 
15017c478bd9Sstevel@tonic-gate 	if (IN6_IS_ADDR_MULTICAST(&ip6h->ip6_src) ||
15027c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_V4MAPPED(&ip6h->ip6_src) ||
15037c478bd9Sstevel@tonic-gate 	    IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) {
15047c478bd9Sstevel@tonic-gate 		freemsg(mp);
15057c478bd9Sstevel@tonic-gate 		return (NULL);
15067c478bd9Sstevel@tonic-gate 	}
15077c478bd9Sstevel@tonic-gate 
15087c478bd9Sstevel@tonic-gate 	if (ip6h->ip6_nxt == IPPROTO_ICMPV6) {
15097c478bd9Sstevel@tonic-gate 		size_t	len_needed = IPV6_HDR_LEN + ICMP6_MINLEN;
15107c478bd9Sstevel@tonic-gate 		icmp6_t		*icmp6;
15117c478bd9Sstevel@tonic-gate 
15127c478bd9Sstevel@tonic-gate 		if (mp->b_wptr - mp->b_rptr < len_needed) {
15137c478bd9Sstevel@tonic-gate 			if (!pullupmsg(mp, len_needed)) {
15147c478bd9Sstevel@tonic-gate 				BUMP_MIB(ill->ill_icmp6_mib,
15157c478bd9Sstevel@tonic-gate 				    ipv6IfIcmpInErrors);
15167c478bd9Sstevel@tonic-gate 				freemsg(mp);
15177c478bd9Sstevel@tonic-gate 				return (NULL);
15187c478bd9Sstevel@tonic-gate 			}
15197c478bd9Sstevel@tonic-gate 			ip6h = (ip6_t *)mp->b_rptr;
15207c478bd9Sstevel@tonic-gate 		}
15217c478bd9Sstevel@tonic-gate 		icmp6 = (icmp6_t *)&ip6h[1];
15227c478bd9Sstevel@tonic-gate 		/* Explicitly do not generate errors in response to redirects */
15237c478bd9Sstevel@tonic-gate 		if (ICMP6_IS_ERROR(icmp6->icmp6_type) ||
15247c478bd9Sstevel@tonic-gate 		    icmp6->icmp6_type == ND_REDIRECT) {
15257c478bd9Sstevel@tonic-gate 			freemsg(mp);
15267c478bd9Sstevel@tonic-gate 			return (NULL);
15277c478bd9Sstevel@tonic-gate 		}
15287c478bd9Sstevel@tonic-gate 	}
15297c478bd9Sstevel@tonic-gate 	/*
15307c478bd9Sstevel@tonic-gate 	 * Check that the destination is not multicast and that the packet
15317c478bd9Sstevel@tonic-gate 	 * was not sent on link layer broadcast or multicast.  (Exception
15327c478bd9Sstevel@tonic-gate 	 * is Packet too big message as per the draft - when mcast_ok is set.)
15337c478bd9Sstevel@tonic-gate 	 */
15347c478bd9Sstevel@tonic-gate 	if (!mcast_ok &&
15357c478bd9Sstevel@tonic-gate 	    (llbcast || IN6_IS_ADDR_MULTICAST(&ip6h->ip6_dst))) {
15367c478bd9Sstevel@tonic-gate 		freemsg(mp);
15377c478bd9Sstevel@tonic-gate 		return (NULL);
15387c478bd9Sstevel@tonic-gate 	}
1539bd670b35SErik Nordmark 	/*
1540bd670b35SErik Nordmark 	 * If this is a labeled system, then check to see if we're allowed to
1541bd670b35SErik Nordmark 	 * send a response to this particular sender.  If not, then just drop.
1542bd670b35SErik Nordmark 	 */
1543bd670b35SErik Nordmark 	if (is_system_labeled() && !tsol_can_reply_error(mp, ira)) {
1544bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpOutErrors);
1545bd670b35SErik Nordmark 		freemsg(mp);
1546bd670b35SErik Nordmark 		return (NULL);
1547bd670b35SErik Nordmark 	}
1548bd670b35SErik Nordmark 
1549f4b3ec61Sdh155122 	if (icmp_err_rate_limit(ipst)) {
15507c478bd9Sstevel@tonic-gate 		/*
15517c478bd9Sstevel@tonic-gate 		 * Only send ICMP error packets every so often.
15527c478bd9Sstevel@tonic-gate 		 * This should be done on a per port/source basis,
15537c478bd9Sstevel@tonic-gate 		 * but for now this will suffice.
15547c478bd9Sstevel@tonic-gate 		 */
15557c478bd9Sstevel@tonic-gate 		freemsg(mp);
15567c478bd9Sstevel@tonic-gate 		return (NULL);
15577c478bd9Sstevel@tonic-gate 	}
15587c478bd9Sstevel@tonic-gate 	return (mp);
15597c478bd9Sstevel@tonic-gate }
15607c478bd9Sstevel@tonic-gate 
15617c478bd9Sstevel@tonic-gate /*
1562bd670b35SErik Nordmark  * Called when a packet was sent out the same link that it arrived on.
1563bd670b35SErik Nordmark  * Check if it is ok to send a redirect and then send it.
1564bd670b35SErik Nordmark  */
1565bd670b35SErik Nordmark void
ip_send_potential_redirect_v6(mblk_t * mp,ip6_t * ip6h,ire_t * ire,ip_recv_attr_t * ira)1566bd670b35SErik Nordmark ip_send_potential_redirect_v6(mblk_t *mp, ip6_t *ip6h, ire_t *ire,
1567bd670b35SErik Nordmark     ip_recv_attr_t *ira)
1568bd670b35SErik Nordmark {
1569bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
1570bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
1571bd670b35SErik Nordmark 	in6_addr_t	*v6targ;
1572bd670b35SErik Nordmark 	ire_t		*src_ire_v6 = NULL;
1573bd670b35SErik Nordmark 	mblk_t		*mp1;
1574bd670b35SErik Nordmark 	ire_t		*nhop_ire = NULL;
1575bd670b35SErik Nordmark 
1576bd670b35SErik Nordmark 	/*
1577bd670b35SErik Nordmark 	 * Don't send a redirect when forwarding a source
1578bd670b35SErik Nordmark 	 * routed packet.
1579bd670b35SErik Nordmark 	 */
1580bd670b35SErik Nordmark 	if (ip_source_routed_v6(ip6h, mp, ipst))
1581bd670b35SErik Nordmark 		return;
1582bd670b35SErik Nordmark 
1583bd670b35SErik Nordmark 	if (ire->ire_type & IRE_ONLINK) {
1584bd670b35SErik Nordmark 		/* Target is directly connected */
1585bd670b35SErik Nordmark 		v6targ = &ip6h->ip6_dst;
1586bd670b35SErik Nordmark 	} else {
1587bd670b35SErik Nordmark 		/* Determine the most specific IRE used to send the packets */
1588bd670b35SErik Nordmark 		nhop_ire = ire_nexthop(ire);
1589bd670b35SErik Nordmark 		if (nhop_ire == NULL)
1590bd670b35SErik Nordmark 			return;
1591bd670b35SErik Nordmark 
1592bd670b35SErik Nordmark 		/*
1593bd670b35SErik Nordmark 		 * We won't send redirects to a router
1594bd670b35SErik Nordmark 		 * that doesn't have a link local
1595bd670b35SErik Nordmark 		 * address, but will forward.
1596bd670b35SErik Nordmark 		 */
1597bd670b35SErik Nordmark 		if (!IN6_IS_ADDR_LINKLOCAL(&nhop_ire->ire_addr_v6)) {
1598bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
1599bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
1600bd670b35SErik Nordmark 			ire_refrele(nhop_ire);
1601bd670b35SErik Nordmark 			return;
1602bd670b35SErik Nordmark 		}
1603bd670b35SErik Nordmark 		v6targ = &nhop_ire->ire_addr_v6;
1604bd670b35SErik Nordmark 	}
1605bd670b35SErik Nordmark 	src_ire_v6 = ire_ftable_lookup_v6(&ip6h->ip6_src,
1606bd670b35SErik Nordmark 	    NULL, NULL, IRE_INTERFACE, ire->ire_ill, ALL_ZONES, NULL,
1607bd670b35SErik Nordmark 	    MATCH_IRE_ILL | MATCH_IRE_TYPE, 0, ipst, NULL);
1608bd670b35SErik Nordmark 
1609bd670b35SErik Nordmark 	if (src_ire_v6 == NULL) {
1610bd670b35SErik Nordmark 		if (nhop_ire != NULL)
1611bd670b35SErik Nordmark 			ire_refrele(nhop_ire);
1612bd670b35SErik Nordmark 		return;
1613bd670b35SErik Nordmark 	}
1614bd670b35SErik Nordmark 
1615bd670b35SErik Nordmark 	/*
1616bd670b35SErik Nordmark 	 * The source is directly connected.
1617bd670b35SErik Nordmark 	 */
1618bd670b35SErik Nordmark 	mp1 = copymsg(mp);
1619bd670b35SErik Nordmark 	if (mp1 != NULL)
1620bd670b35SErik Nordmark 		icmp_send_redirect_v6(mp1, v6targ, &ip6h->ip6_dst, ira);
1621bd670b35SErik Nordmark 
1622bd670b35SErik Nordmark 	if (nhop_ire != NULL)
1623bd670b35SErik Nordmark 		ire_refrele(nhop_ire);
1624bd670b35SErik Nordmark 	ire_refrele(src_ire_v6);
1625bd670b35SErik Nordmark }
1626bd670b35SErik Nordmark 
1627bd670b35SErik Nordmark /*
16287c478bd9Sstevel@tonic-gate  * Generate an ICMPv6 redirect message.
16297c478bd9Sstevel@tonic-gate  * Include target link layer address option if it exits.
16307c478bd9Sstevel@tonic-gate  * Always include redirect header.
16317c478bd9Sstevel@tonic-gate  */
16327c478bd9Sstevel@tonic-gate static void
icmp_send_redirect_v6(mblk_t * mp,in6_addr_t * targetp,in6_addr_t * dest,ip_recv_attr_t * ira)1633bd670b35SErik Nordmark icmp_send_redirect_v6(mblk_t *mp, in6_addr_t *targetp, in6_addr_t *dest,
1634bd670b35SErik Nordmark     ip_recv_attr_t *ira)
16357c478bd9Sstevel@tonic-gate {
16367c478bd9Sstevel@tonic-gate 	nd_redirect_t	*rd;
16377c478bd9Sstevel@tonic-gate 	nd_opt_rd_hdr_t	*rdh;
16387c478bd9Sstevel@tonic-gate 	uchar_t		*buf;
1639bd670b35SErik Nordmark 	ncec_t		*ncec = NULL;
16407c478bd9Sstevel@tonic-gate 	nd_opt_hdr_t	*opt;
16417c478bd9Sstevel@tonic-gate 	int		len;
16427c478bd9Sstevel@tonic-gate 	int		ll_opt_len = 0;
16437c478bd9Sstevel@tonic-gate 	int		max_redir_hdr_data_len;
16447c478bd9Sstevel@tonic-gate 	int		pkt_len;
16457c478bd9Sstevel@tonic-gate 	in6_addr_t	*srcp;
1646bd670b35SErik Nordmark 	ill_t		*ill;
1647bd670b35SErik Nordmark 	boolean_t	need_refrele;
1648bd670b35SErik Nordmark 	ip_stack_t	*ipst = ira->ira_ill->ill_ipst;
16497c478bd9Sstevel@tonic-gate 
1650bd670b35SErik Nordmark 	mp = icmp_pkt_err_ok_v6(mp, B_FALSE, ira);
16517c478bd9Sstevel@tonic-gate 	if (mp == NULL)
16527c478bd9Sstevel@tonic-gate 		return;
1653bd670b35SErik Nordmark 
1654bd670b35SErik Nordmark 	if (IS_UNDER_IPMP(ira->ira_ill)) {
1655bd670b35SErik Nordmark 		ill = ipmp_ill_hold_ipmp_ill(ira->ira_ill);
1656bd670b35SErik Nordmark 		if (ill == NULL) {
1657bd670b35SErik Nordmark 			ill = ira->ira_ill;
1658bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_icmp6_mib, ipv6IfIcmpInBadRedirects);
1659bd670b35SErik Nordmark 			ip_drop_output("no IPMP ill for sending redirect",
1660bd670b35SErik Nordmark 			    mp, ill);
1661bd670b35SErik Nordmark 			freemsg(mp);
1662bd670b35SErik Nordmark 			return;
1663bd670b35SErik Nordmark 		}
1664bd670b35SErik Nordmark 		need_refrele = B_TRUE;
1665bd670b35SErik Nordmark 	} else {
1666bd670b35SErik Nordmark 		ill = ira->ira_ill;
1667bd670b35SErik Nordmark 		need_refrele = B_FALSE;
1668bd670b35SErik Nordmark 	}
1669bd670b35SErik Nordmark 
1670bd670b35SErik Nordmark 	ncec = ncec_lookup_illgrp_v6(ill, targetp);
1671bd670b35SErik Nordmark 	if (ncec != NULL && ncec->ncec_state != ND_INCOMPLETE &&
1672bd670b35SErik Nordmark 	    ncec->ncec_lladdr != NULL) {
16737c478bd9Sstevel@tonic-gate 		ll_opt_len = (sizeof (nd_opt_hdr_t) +
16747c478bd9Sstevel@tonic-gate 		    ill->ill_phys_addr_length + 7)/8 * 8;
16757c478bd9Sstevel@tonic-gate 	}
16767c478bd9Sstevel@tonic-gate 	len = sizeof (nd_redirect_t) + sizeof (nd_opt_rd_hdr_t) + ll_opt_len;
16777c478bd9Sstevel@tonic-gate 	ASSERT(len % 4 == 0);
16787c478bd9Sstevel@tonic-gate 	buf = kmem_alloc(len, KM_NOSLEEP);
16797c478bd9Sstevel@tonic-gate 	if (buf == NULL) {
1680bd670b35SErik Nordmark 		if (ncec != NULL)
1681bd670b35SErik Nordmark 			ncec_refrele(ncec);
1682bd670b35SErik Nordmark 		if (need_refrele)
1683bd670b35SErik Nordmark 			ill_refrele(ill);
16847c478bd9Sstevel@tonic-gate 		freemsg(mp);
16857c478bd9Sstevel@tonic-gate 		return;
16867c478bd9Sstevel@tonic-gate 	}
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	rd = (nd_redirect_t *)buf;
16897c478bd9Sstevel@tonic-gate 	rd->nd_rd_type = (uint8_t)ND_REDIRECT;
16907c478bd9Sstevel@tonic-gate 	rd->nd_rd_code = 0;
16917c478bd9Sstevel@tonic-gate 	rd->nd_rd_reserved = 0;
16927c478bd9Sstevel@tonic-gate 	rd->nd_rd_target = *targetp;
16937c478bd9Sstevel@tonic-gate 	rd->nd_rd_dst = *dest;
16947c478bd9Sstevel@tonic-gate 
16957c478bd9Sstevel@tonic-gate 	opt = (nd_opt_hdr_t *)(buf + sizeof (nd_redirect_t));
1696bd670b35SErik Nordmark 	if (ncec != NULL && ll_opt_len != 0) {
16977c478bd9Sstevel@tonic-gate 		opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
16987c478bd9Sstevel@tonic-gate 		opt->nd_opt_len = ll_opt_len/8;
1699bd670b35SErik Nordmark 		bcopy((char *)ncec->ncec_lladdr, &opt[1],
17007c478bd9Sstevel@tonic-gate 		    ill->ill_phys_addr_length);
17017c478bd9Sstevel@tonic-gate 	}
1702bd670b35SErik Nordmark 	if (ncec != NULL)
1703bd670b35SErik Nordmark 		ncec_refrele(ncec);
17047c478bd9Sstevel@tonic-gate 	rdh = (nd_opt_rd_hdr_t *)(buf + sizeof (nd_redirect_t) + ll_opt_len);
17057c478bd9Sstevel@tonic-gate 	rdh->nd_opt_rh_type = (uint8_t)ND_OPT_REDIRECTED_HEADER;
17067c478bd9Sstevel@tonic-gate 	/* max_redir_hdr_data_len and nd_opt_rh_len must be multiple of 8 */
1707f4b3ec61Sdh155122 	max_redir_hdr_data_len =
1708f4b3ec61Sdh155122 	    (ipst->ips_ipv6_icmp_return - IPV6_HDR_LEN - len)/8*8;
17097c478bd9Sstevel@tonic-gate 	pkt_len = msgdsize(mp);
17107c478bd9Sstevel@tonic-gate 	/* Make sure mp is 8 byte aligned */
17117c478bd9Sstevel@tonic-gate 	if (pkt_len > max_redir_hdr_data_len) {
17127c478bd9Sstevel@tonic-gate 		rdh->nd_opt_rh_len = (max_redir_hdr_data_len +
17137c478bd9Sstevel@tonic-gate 		    sizeof (nd_opt_rd_hdr_t))/8;
17147c478bd9Sstevel@tonic-gate 		(void) adjmsg(mp, max_redir_hdr_data_len - pkt_len);
17157c478bd9Sstevel@tonic-gate 	} else {
17167c478bd9Sstevel@tonic-gate 		rdh->nd_opt_rh_len = (pkt_len + sizeof (nd_opt_rd_hdr_t))/8;
17177c478bd9Sstevel@tonic-gate 		(void) adjmsg(mp, -(pkt_len % 8));
17187c478bd9Sstevel@tonic-gate 	}
17197c478bd9Sstevel@tonic-gate 	rdh->nd_opt_rh_reserved1 = 0;
17207c478bd9Sstevel@tonic-gate 	rdh->nd_opt_rh_reserved2 = 0;
1721bd670b35SErik Nordmark 	/* ipif_v6lcl_addr contains the link-local source address */
1722bd670b35SErik Nordmark 	srcp = &ill->ill_ipif->ipif_v6lcl_addr;
17237c478bd9Sstevel@tonic-gate 
17245597b60aSnordmark 	/* Redirects sent by router, and router is global zone */
1725bd670b35SErik Nordmark 	ASSERT(ira->ira_zoneid == ALL_ZONES);
1726bd670b35SErik Nordmark 	ira->ira_zoneid = GLOBAL_ZONEID;
1727bd670b35SErik Nordmark 	icmp_pkt_v6(mp, buf, len, srcp, ira);
17287c478bd9Sstevel@tonic-gate 	kmem_free(buf, len);
1729bd670b35SErik Nordmark 	if (need_refrele)
1730bd670b35SErik Nordmark 		ill_refrele(ill);
17317c478bd9Sstevel@tonic-gate }
17327c478bd9Sstevel@tonic-gate 
17337c478bd9Sstevel@tonic-gate 
17347c478bd9Sstevel@tonic-gate /* Generate an ICMP time exceeded message.  (May be called as writer.) */
17357c478bd9Sstevel@tonic-gate void
icmp_time_exceeded_v6(mblk_t * mp,uint8_t code,boolean_t mcast_ok,ip_recv_attr_t * ira)1736bd670b35SErik Nordmark icmp_time_exceeded_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
1737bd670b35SErik Nordmark     ip_recv_attr_t *ira)
17387c478bd9Sstevel@tonic-gate {
17397c478bd9Sstevel@tonic-gate 	icmp6_t	icmp6;
17407c478bd9Sstevel@tonic-gate 
1741bd670b35SErik Nordmark 	mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
1742bd670b35SErik Nordmark 	if (mp == NULL)
17437c478bd9Sstevel@tonic-gate 		return;
1744bd670b35SErik Nordmark 
17457c478bd9Sstevel@tonic-gate 	bzero(&icmp6, sizeof (icmp6_t));
17467c478bd9Sstevel@tonic-gate 	icmp6.icmp6_type = ICMP6_TIME_EXCEEDED;
17477c478bd9Sstevel@tonic-gate 	icmp6.icmp6_code = code;
1748bd670b35SErik Nordmark 	icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17497c478bd9Sstevel@tonic-gate }
17507c478bd9Sstevel@tonic-gate 
17517c478bd9Sstevel@tonic-gate /*
17527c478bd9Sstevel@tonic-gate  * Generate an ICMP unreachable message.
1753bd670b35SErik Nordmark  * When called from ip_output side a minimal ip_recv_attr_t needs to be
1754bd670b35SErik Nordmark  * constructed by the caller.
17557c478bd9Sstevel@tonic-gate  */
17567c478bd9Sstevel@tonic-gate void
icmp_unreachable_v6(mblk_t * mp,uint8_t code,boolean_t mcast_ok,ip_recv_attr_t * ira)1757bd670b35SErik Nordmark icmp_unreachable_v6(mblk_t *mp, uint8_t code, boolean_t mcast_ok,
1758bd670b35SErik Nordmark     ip_recv_attr_t *ira)
17597c478bd9Sstevel@tonic-gate {
17607c478bd9Sstevel@tonic-gate 	icmp6_t	icmp6;
17617c478bd9Sstevel@tonic-gate 
1762bd670b35SErik Nordmark 	mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
1763bd670b35SErik Nordmark 	if (mp == NULL)
17647c478bd9Sstevel@tonic-gate 		return;
1765bd670b35SErik Nordmark 
17667c478bd9Sstevel@tonic-gate 	bzero(&icmp6, sizeof (icmp6_t));
17677c478bd9Sstevel@tonic-gate 	icmp6.icmp6_type = ICMP6_DST_UNREACH;
17687c478bd9Sstevel@tonic-gate 	icmp6.icmp6_code = code;
1769bd670b35SErik Nordmark 	icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17707c478bd9Sstevel@tonic-gate }
17717c478bd9Sstevel@tonic-gate 
17727c478bd9Sstevel@tonic-gate /*
17737c478bd9Sstevel@tonic-gate  * Generate an ICMP pkt too big message.
1774bd670b35SErik Nordmark  * When called from ip_output side a minimal ip_recv_attr_t needs to be
1775bd670b35SErik Nordmark  * constructed by the caller.
17767c478bd9Sstevel@tonic-gate  */
1777bd670b35SErik Nordmark void
icmp_pkt2big_v6(mblk_t * mp,uint32_t mtu,boolean_t mcast_ok,ip_recv_attr_t * ira)1778bd670b35SErik Nordmark icmp_pkt2big_v6(mblk_t *mp, uint32_t mtu, boolean_t mcast_ok,
1779bd670b35SErik Nordmark     ip_recv_attr_t *ira)
17807c478bd9Sstevel@tonic-gate {
17817c478bd9Sstevel@tonic-gate 	icmp6_t	icmp6;
17827c478bd9Sstevel@tonic-gate 
1783bd670b35SErik Nordmark 	mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
1784bd670b35SErik Nordmark 	if (mp == NULL)
17857c478bd9Sstevel@tonic-gate 		return;
1786bd670b35SErik Nordmark 
17877c478bd9Sstevel@tonic-gate 	bzero(&icmp6, sizeof (icmp6_t));
17887c478bd9Sstevel@tonic-gate 	icmp6.icmp6_type = ICMP6_PACKET_TOO_BIG;
17897c478bd9Sstevel@tonic-gate 	icmp6.icmp6_code = 0;
17907c478bd9Sstevel@tonic-gate 	icmp6.icmp6_mtu = htonl(mtu);
17917c478bd9Sstevel@tonic-gate 
1792bd670b35SErik Nordmark 	icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
17937c478bd9Sstevel@tonic-gate }
17947c478bd9Sstevel@tonic-gate 
17957c478bd9Sstevel@tonic-gate /*
17967c478bd9Sstevel@tonic-gate  * Generate an ICMP parameter problem message. (May be called as writer.)
17977c478bd9Sstevel@tonic-gate  * 'offset' is the offset from the beginning of the packet in error.
1798bd670b35SErik Nordmark  * When called from ip_output side a minimal ip_recv_attr_t needs to be
1799bd670b35SErik Nordmark  * constructed by the caller.
18007c478bd9Sstevel@tonic-gate  */
18017c478bd9Sstevel@tonic-gate static void
icmp_param_problem_v6(mblk_t * mp,uint8_t code,uint32_t offset,boolean_t mcast_ok,ip_recv_attr_t * ira)1802bd670b35SErik Nordmark icmp_param_problem_v6(mblk_t *mp, uint8_t code, uint32_t offset,
1803bd670b35SErik Nordmark     boolean_t mcast_ok, ip_recv_attr_t *ira)
18047c478bd9Sstevel@tonic-gate {
18057c478bd9Sstevel@tonic-gate 	icmp6_t	icmp6;
18067c478bd9Sstevel@tonic-gate 
1807bd670b35SErik Nordmark 	mp = icmp_pkt_err_ok_v6(mp, mcast_ok, ira);
1808bd670b35SErik Nordmark 	if (mp == NULL)
18097c478bd9Sstevel@tonic-gate 		return;
1810bd670b35SErik Nordmark 
18117c478bd9Sstevel@tonic-gate 	bzero((char *)&icmp6, sizeof (icmp6_t));
18127c478bd9Sstevel@tonic-gate 	icmp6.icmp6_type = ICMP6_PARAM_PROB;
18137c478bd9Sstevel@tonic-gate 	icmp6.icmp6_code = code;
18147c478bd9Sstevel@tonic-gate 	icmp6.icmp6_pptr = htonl(offset);
1815bd670b35SErik Nordmark 	icmp_pkt_v6(mp, &icmp6, sizeof (icmp6_t), NULL, ira);
18167c478bd9Sstevel@tonic-gate }
18177c478bd9Sstevel@tonic-gate 
1818bd670b35SErik Nordmark void
icmp_param_problem_nexthdr_v6(mblk_t * mp,boolean_t mcast_ok,ip_recv_attr_t * ira)1819bd670b35SErik Nordmark icmp_param_problem_nexthdr_v6(mblk_t *mp, boolean_t mcast_ok,
1820bd670b35SErik Nordmark     ip_recv_attr_t *ira)
18217c478bd9Sstevel@tonic-gate {
1822bd670b35SErik Nordmark 	ip6_t		*ip6h = (ip6_t *)mp->b_rptr;
1823bd670b35SErik Nordmark 	uint16_t	hdr_length;
1824bd670b35SErik Nordmark 	uint8_t		*nexthdrp;
1825bd670b35SErik Nordmark 	uint32_t	offset;
1826bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
1827de8c4a14SErik Nordmark 
1828bd670b35SErik Nordmark 	/* Determine the offset of the bad nexthdr value */
1829bd670b35SErik Nordmark 	if (!ip_hdr_length_nexthdr_v6(mp, ip6h,	&hdr_length, &nexthdrp)) {
1830bd670b35SErik Nordmark 		/* Malformed packet */
1831bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
1832bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInDiscards", mp, ill);
1833bd670b35SErik Nordmark 		freemsg(mp);
1834bd670b35SErik Nordmark 		return;
1835de8c4a14SErik Nordmark 	}
18367c478bd9Sstevel@tonic-gate 
1837bd670b35SErik Nordmark 	offset = nexthdrp - mp->b_rptr;
1838bd670b35SErik Nordmark 	icmp_param_problem_v6(mp, ICMP6_PARAMPROB_NEXTHEADER, offset,
1839bd670b35SErik Nordmark 	    mcast_ok, ira);
18407c478bd9Sstevel@tonic-gate }
18417c478bd9Sstevel@tonic-gate 
184245916cd2Sjpk /*
1843bd670b35SErik Nordmark  * Verify whether or not the IP address is a valid local address.
1844bd670b35SErik Nordmark  * Could be a unicast, including one for a down interface.
1845bd670b35SErik Nordmark  * If allow_mcbc then a multicast or broadcast address is also
1846bd670b35SErik Nordmark  * acceptable.
184745916cd2Sjpk  *
18487c478bd9Sstevel@tonic-gate  * In the case of a multicast address, however, the
18497c478bd9Sstevel@tonic-gate  * upper protocol is expected to reset the src address
1850bd670b35SErik Nordmark  * to zero when we return IPVL_MCAST so that
18517c478bd9Sstevel@tonic-gate  * no packets are emitted with multicast address as
18527c478bd9Sstevel@tonic-gate  * source address.
18537c478bd9Sstevel@tonic-gate  * The addresses valid for bind are:
18547c478bd9Sstevel@tonic-gate  *	(1) - in6addr_any
18557c478bd9Sstevel@tonic-gate  *	(2) - IP address of an UP interface
18567c478bd9Sstevel@tonic-gate  *	(3) - IP address of a DOWN interface
18577c478bd9Sstevel@tonic-gate  *	(4) - a multicast address. In this case
18587c478bd9Sstevel@tonic-gate  *	the conn will only receive packets destined to
18597c478bd9Sstevel@tonic-gate  *	the specified multicast address. Note: the
18607c478bd9Sstevel@tonic-gate  *	application still has to issue an
18617c478bd9Sstevel@tonic-gate  *	IPV6_JOIN_GROUP socket option.
18627c478bd9Sstevel@tonic-gate  *
18637c478bd9Sstevel@tonic-gate  * In all the above cases, the bound address must be valid in the current zone.
18647c478bd9Sstevel@tonic-gate  * When the address is loopback or multicast, there might be many matching IREs
18657c478bd9Sstevel@tonic-gate  * so bind has to look up based on the zone.
18667c478bd9Sstevel@tonic-gate  */
1867bd670b35SErik Nordmark ip_laddr_t
ip_laddr_verify_v6(const in6_addr_t * v6src,zoneid_t zoneid,ip_stack_t * ipst,boolean_t allow_mcbc,uint_t scopeid)1868bd670b35SErik Nordmark ip_laddr_verify_v6(const in6_addr_t *v6src, zoneid_t zoneid,
1869bd670b35SErik Nordmark     ip_stack_t *ipst, boolean_t allow_mcbc, uint_t scopeid)
18707c478bd9Sstevel@tonic-gate {
1871bd670b35SErik Nordmark 	ire_t		*src_ire;
1872bd670b35SErik Nordmark 	uint_t		match_flags;
1873bd670b35SErik Nordmark 	ill_t		*ill = NULL;
18747c478bd9Sstevel@tonic-gate 
1875bd670b35SErik Nordmark 	ASSERT(!IN6_IS_ADDR_V4MAPPED(v6src));
1876bd670b35SErik Nordmark 	ASSERT(!IN6_IS_ADDR_UNSPECIFIED(v6src));
18770f1702c5SYu Xiangning 
1878bd670b35SErik Nordmark 	match_flags = MATCH_IRE_ZONEONLY;
1879bd670b35SErik Nordmark 	if (scopeid != 0) {
1880bd670b35SErik Nordmark 		ill = ill_lookup_on_ifindex(scopeid, B_TRUE, ipst);
1881bd670b35SErik Nordmark 		if (ill == NULL)
1882bd670b35SErik Nordmark 			return (IPVL_BAD);
1883bd670b35SErik Nordmark 		match_flags |= MATCH_IRE_ILL;
1884bd670b35SErik Nordmark 	}
18857c478bd9Sstevel@tonic-gate 
1886bd670b35SErik Nordmark 	src_ire = ire_ftable_lookup_v6(v6src, NULL, NULL, 0,
1887bd670b35SErik Nordmark 	    ill, zoneid, NULL, match_flags, 0, ipst, NULL);
1888bd670b35SErik Nordmark 	if (ill != NULL)
1889bd670b35SErik Nordmark 		ill_refrele(ill);
18907c478bd9Sstevel@tonic-gate 
18917c478bd9Sstevel@tonic-gate 	/*
18927c478bd9Sstevel@tonic-gate 	 * If an address other than in6addr_any is requested,
18937c478bd9Sstevel@tonic-gate 	 * we verify that it is a valid address for bind
18947c478bd9Sstevel@tonic-gate 	 * Note: Following code is in if-else-if form for
18957c478bd9Sstevel@tonic-gate 	 * readability compared to a condition check.
18967c478bd9Sstevel@tonic-gate 	 */
1897bd670b35SErik Nordmark 	if (src_ire != NULL && (src_ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK))) {
18987c478bd9Sstevel@tonic-gate 		/*
18997c478bd9Sstevel@tonic-gate 		 * (2) Bind to address of local UP interface
19007c478bd9Sstevel@tonic-gate 		 */
1901bd670b35SErik Nordmark 		ire_refrele(src_ire);
1902bd670b35SErik Nordmark 		return (IPVL_UNICAST_UP);
19037c478bd9Sstevel@tonic-gate 	} else if (IN6_IS_ADDR_MULTICAST(v6src)) {
1904bd670b35SErik Nordmark 		/* (4) bind to multicast address. */
19057c478bd9Sstevel@tonic-gate 		if (src_ire != NULL)
19067c478bd9Sstevel@tonic-gate 			ire_refrele(src_ire);
19077c478bd9Sstevel@tonic-gate 
19087c478bd9Sstevel@tonic-gate 		/*
1909bd670b35SErik Nordmark 		 * Note: caller should take IPV6_MULTICAST_IF
1910bd670b35SErik Nordmark 		 * into account when selecting a real source address.
19117c478bd9Sstevel@tonic-gate 		 */
1912bd670b35SErik Nordmark 		if (allow_mcbc)
1913bd670b35SErik Nordmark 			return (IPVL_MCAST);
1914bd670b35SErik Nordmark 		else
1915bd670b35SErik Nordmark 			return (IPVL_BAD);
19167c478bd9Sstevel@tonic-gate 	} else {
19177c478bd9Sstevel@tonic-gate 		ipif_t *ipif;
19187c478bd9Sstevel@tonic-gate 
19197c478bd9Sstevel@tonic-gate 		/*
1920bd670b35SErik Nordmark 		 * (3) Bind to address of local DOWN interface?
1921bd670b35SErik Nordmark 		 * (ipif_lookup_addr() looks up all interfaces
1922bd670b35SErik Nordmark 		 * but we do not get here for UP interfaces
1923bd670b35SErik Nordmark 		 * - case (2) above)
19247c478bd9Sstevel@tonic-gate 		 */
1925bd670b35SErik Nordmark 		if (src_ire != NULL)
1926bd670b35SErik Nordmark 			ire_refrele(src_ire);
1927bd670b35SErik Nordmark 
1928bd670b35SErik Nordmark 		ipif = ipif_lookup_addr_v6(v6src, NULL, zoneid, ipst);
1929bd670b35SErik Nordmark 		if (ipif == NULL)
1930bd670b35SErik Nordmark 			return (IPVL_BAD);
1931bd670b35SErik Nordmark 
1932bd670b35SErik Nordmark 		/* Not a useful source? */
1933bd670b35SErik Nordmark 		if (ipif->ipif_flags & (IPIF_NOLOCAL | IPIF_ANYCAST)) {
19347c478bd9Sstevel@tonic-gate 			ipif_refrele(ipif);
1935bd670b35SErik Nordmark 			return (IPVL_BAD);
19367c478bd9Sstevel@tonic-gate 		}
19377c478bd9Sstevel@tonic-gate 		ipif_refrele(ipif);
1938bd670b35SErik Nordmark 		return (IPVL_UNICAST_DOWN);
1939bd670b35SErik Nordmark 	}
1940bd670b35SErik Nordmark }
1941bd670b35SErik Nordmark 
19427c478bd9Sstevel@tonic-gate /*
1943bd670b35SErik Nordmark  * Verify that both the source and destination addresses are valid.  If
1944bd670b35SErik Nordmark  * IPDF_VERIFY_DST is not set, then the destination address may be unreachable,
1945bd670b35SErik Nordmark  * i.e. have no route to it.  Protocols like TCP want to verify destination
1946bd670b35SErik Nordmark  * reachability, while tunnels do not.
19477c478bd9Sstevel@tonic-gate  *
1948bd670b35SErik Nordmark  * Determine the route, the interface, and (optionally) the source address
1949bd670b35SErik Nordmark  * to use to reach a given destination.
1950bd670b35SErik Nordmark  * Note that we allow connect to broadcast and multicast addresses when
1951bd670b35SErik Nordmark  * IPDF_ALLOW_MCBC is set.
1952bd670b35SErik Nordmark  * first_hop and dst_addr are normally the same, but if source routing
1953bd670b35SErik Nordmark  * they will differ; in that case the first_hop is what we'll use for the
1954bd670b35SErik Nordmark  * routing lookup but the dce and label checks will be done on dst_addr,
1955bd670b35SErik Nordmark  *
1956bd670b35SErik Nordmark  * If uinfo is set, then we fill in the best available information
1957bd670b35SErik Nordmark  * we have for the destination. This is based on (in priority order) any
1958bd670b35SErik Nordmark  * metrics and path MTU stored in a dce_t, route metrics, and finally the
1959*1eee170aSErik Nordmark  * ill_mtu/ill_mc_mtu.
1960bd670b35SErik Nordmark  *
1961bd670b35SErik Nordmark  * Tsol note: If we have a source route then dst_addr != firsthop. But we
1962bd670b35SErik Nordmark  * always do the label check on dst_addr.
1963bd670b35SErik Nordmark  *
1964bd670b35SErik Nordmark  * Assumes that the caller has set ixa_scopeid for link-local communication.
19657c478bd9Sstevel@tonic-gate  */
1966bd670b35SErik Nordmark int
ip_set_destination_v6(in6_addr_t * src_addrp,const in6_addr_t * dst_addr,const in6_addr_t * firsthop,ip_xmit_attr_t * ixa,iulp_t * uinfo,uint32_t flags,uint_t mac_mode)1967bd670b35SErik Nordmark ip_set_destination_v6(in6_addr_t *src_addrp, const in6_addr_t *dst_addr,
1968bd670b35SErik Nordmark     const in6_addr_t *firsthop, ip_xmit_attr_t *ixa, iulp_t *uinfo,
1969bd670b35SErik Nordmark     uint32_t flags, uint_t mac_mode)
1970bd670b35SErik Nordmark {
1971bd670b35SErik Nordmark 	ire_t		*ire;
1972bd670b35SErik Nordmark 	int		error = 0;
1973bd670b35SErik Nordmark 	in6_addr_t	setsrc;				/* RTF_SETSRC */
1974bd670b35SErik Nordmark 	zoneid_t	zoneid = ixa->ixa_zoneid;	/* Honors SO_ALLZONES */
1975bd670b35SErik Nordmark 	ip_stack_t	*ipst = ixa->ixa_ipst;
1976bd670b35SErik Nordmark 	dce_t		*dce;
1977bd670b35SErik Nordmark 	uint_t		pmtu;
1978bd670b35SErik Nordmark 	uint_t		ifindex;
1979bd670b35SErik Nordmark 	uint_t		generation;
1980bd670b35SErik Nordmark 	nce_t		*nce;
1981bd670b35SErik Nordmark 	ill_t		*ill = NULL;
1982bd670b35SErik Nordmark 	boolean_t	multirt = B_FALSE;
1983bd670b35SErik Nordmark 
1984bd670b35SErik Nordmark 	ASSERT(!IN6_IS_ADDR_V4MAPPED(dst_addr));
1985bd670b35SErik Nordmark 
1986bd670b35SErik Nordmark 	ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
1987bd670b35SErik Nordmark 
1988bd670b35SErik Nordmark 	/*
1989bd670b35SErik Nordmark 	 * We never send to zero; the ULPs map it to the loopback address.
1990bd670b35SErik Nordmark 	 * We can't allow it since we use zero to mean unitialized in some
1991bd670b35SErik Nordmark 	 * places.
1992bd670b35SErik Nordmark 	 */
1993bd670b35SErik Nordmark 	ASSERT(!IN6_IS_ADDR_UNSPECIFIED(dst_addr));
1994bd670b35SErik Nordmark 
1995bd670b35SErik Nordmark 	if (is_system_labeled()) {
1996bd670b35SErik Nordmark 		ts_label_t *tsl = NULL;
1997bd670b35SErik Nordmark 
1998bd670b35SErik Nordmark 		error = tsol_check_dest(ixa->ixa_tsl, dst_addr, IPV6_VERSION,
1999bd670b35SErik Nordmark 		    mac_mode, (flags & IPDF_ZONE_IS_GLOBAL) != 0, &tsl);
2000bd670b35SErik Nordmark 		if (error != 0)
2001bd670b35SErik Nordmark 			return (error);
2002bd670b35SErik Nordmark 		if (tsl != NULL) {
2003bd670b35SErik Nordmark 			/* Update the label */
2004bd670b35SErik Nordmark 			ip_xmit_attr_replace_tsl(ixa, tsl);
20057c478bd9Sstevel@tonic-gate 		}
20067c478bd9Sstevel@tonic-gate 	}
2007bd670b35SErik Nordmark 
2008bd670b35SErik Nordmark 	setsrc = ipv6_all_zeros;
2009bd670b35SErik Nordmark 	/*
2010bd670b35SErik Nordmark 	 * Select a route; For IPMP interfaces, we would only select
2011bd670b35SErik Nordmark 	 * a "hidden" route (i.e., going through a specific under_ill)
2012bd670b35SErik Nordmark 	 * if ixa_ifindex has been specified.
2013bd670b35SErik Nordmark 	 */
201444b099c4SSowmini Varadhan 	ire = ip_select_route_v6(firsthop, *src_addrp, ixa, &generation,
201544b099c4SSowmini Varadhan 	    &setsrc, &error, &multirt);
2016bd670b35SErik Nordmark 	ASSERT(ire != NULL);	/* IRE_NOROUTE if none found */
2017bd670b35SErik Nordmark 	if (error != 0)
20187c478bd9Sstevel@tonic-gate 		goto bad_addr;
20197c478bd9Sstevel@tonic-gate 
20207c478bd9Sstevel@tonic-gate 	/*
2021bd670b35SErik Nordmark 	 * ire can't be a broadcast or multicast unless IPDF_ALLOW_MCBC is set.
2022bd670b35SErik Nordmark 	 * If IPDF_VERIFY_DST is set, the destination must be reachable.
2023bd670b35SErik Nordmark 	 * Otherwise the destination needn't be reachable.
2024bd670b35SErik Nordmark 	 *
2025bd670b35SErik Nordmark 	 * If we match on a reject or black hole, then we've got a
2026bd670b35SErik Nordmark 	 * local failure.  May as well fail out the connect() attempt,
2027bd670b35SErik Nordmark 	 * since it's never going to succeed.
20287c478bd9Sstevel@tonic-gate 	 */
2029bd670b35SErik Nordmark 	if (ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE)) {
20309a09d68dSja97890 		/*
2031bd670b35SErik Nordmark 		 * If we're verifying destination reachability, we always want
2032bd670b35SErik Nordmark 		 * to complain here.
2033bd670b35SErik Nordmark 		 *
2034bd670b35SErik Nordmark 		 * If we're not verifying destination reachability but the
2035bd670b35SErik Nordmark 		 * destination has a route, we still want to fail on the
2036bd670b35SErik Nordmark 		 * temporary address and broadcast address tests.
2037bd670b35SErik Nordmark 		 *
2038bd670b35SErik Nordmark 		 * In both cases do we let the code continue so some reasonable
2039bd670b35SErik Nordmark 		 * information is returned to the caller. That enables the
2040bd670b35SErik Nordmark 		 * caller to use (and even cache) the IRE. conn_ip_ouput will
2041bd670b35SErik Nordmark 		 * use the generation mismatch path to check for the unreachable
2042bd670b35SErik Nordmark 		 * case thereby avoiding any specific check in the main path.
20439a09d68dSja97890 		 */
2044bd670b35SErik Nordmark 		ASSERT(generation == IRE_GENERATION_VERIFY);
2045bd670b35SErik Nordmark 		if (flags & IPDF_VERIFY_DST) {
20467c478bd9Sstevel@tonic-gate 			/*
2047bd670b35SErik Nordmark 			 * Set errno but continue to set up ixa_ire to be
2048bd670b35SErik Nordmark 			 * the RTF_REJECT|RTF_BLACKHOLE IRE.
2049bd670b35SErik Nordmark 			 * That allows callers to use ip_output to get an
2050bd670b35SErik Nordmark 			 * ICMP error back.
20517c478bd9Sstevel@tonic-gate 			 */
2052bd670b35SErik Nordmark 			if (!(ire->ire_type & IRE_HOST))
20537c478bd9Sstevel@tonic-gate 				error = ENETUNREACH;
20547c478bd9Sstevel@tonic-gate 			else
20557c478bd9Sstevel@tonic-gate 				error = EHOSTUNREACH;
20567c478bd9Sstevel@tonic-gate 		}
20577c478bd9Sstevel@tonic-gate 	}
2058bd670b35SErik Nordmark 
2059bd670b35SErik Nordmark 	if ((ire->ire_type & (IRE_BROADCAST|IRE_MULTICAST)) &&
2060bd670b35SErik Nordmark 	    !(flags & IPDF_ALLOW_MCBC)) {
2061bd670b35SErik Nordmark 		ire_refrele(ire);
2062bd670b35SErik Nordmark 		ire = ire_reject(ipst, B_FALSE);
2063bd670b35SErik Nordmark 		generation = IRE_GENERATION_VERIFY;
2064bd670b35SErik Nordmark 		error = ENETUNREACH;
2065bd670b35SErik Nordmark 	}
2066bd670b35SErik Nordmark 
2067bd670b35SErik Nordmark 	/* Cache things */
2068bd670b35SErik Nordmark 	if (ixa->ixa_ire != NULL)
2069bd670b35SErik Nordmark 		ire_refrele_notr(ixa->ixa_ire);
2070bd670b35SErik Nordmark #ifdef DEBUG
2071bd670b35SErik Nordmark 	ire_refhold_notr(ire);
2072bd670b35SErik Nordmark 	ire_refrele(ire);
2073bd670b35SErik Nordmark #endif
2074bd670b35SErik Nordmark 	ixa->ixa_ire = ire;
2075bd670b35SErik Nordmark 	ixa->ixa_ire_generation = generation;
2076bd670b35SErik Nordmark 
2077bd670b35SErik Nordmark 	/*
207853287767SSowmini Varadhan 	 * Ensure that ixa_dce is always set any time that ixa_ire is set,
207953287767SSowmini Varadhan 	 * since some callers will send a packet to conn_ip_output() even if
208053287767SSowmini Varadhan 	 * there's an error.
208153287767SSowmini Varadhan 	 */
208253287767SSowmini Varadhan 	ifindex = 0;
208353287767SSowmini Varadhan 	if (IN6_IS_ADDR_LINKSCOPE(dst_addr)) {
208453287767SSowmini Varadhan 		/* If we are creating a DCE we'd better have an ifindex */
208553287767SSowmini Varadhan 		if (ill != NULL)
208653287767SSowmini Varadhan 			ifindex = ill->ill_phyint->phyint_ifindex;
208753287767SSowmini Varadhan 		else
208853287767SSowmini Varadhan 			flags &= ~IPDF_UNIQUE_DCE;
208953287767SSowmini Varadhan 	}
209053287767SSowmini Varadhan 
209153287767SSowmini Varadhan 	if (flags & IPDF_UNIQUE_DCE) {
209253287767SSowmini Varadhan 		/* Fallback to the default dce if allocation fails */
209353287767SSowmini Varadhan 		dce = dce_lookup_and_add_v6(dst_addr, ifindex, ipst);
209453287767SSowmini Varadhan 		if (dce != NULL) {
209553287767SSowmini Varadhan 			generation = dce->dce_generation;
209653287767SSowmini Varadhan 		} else {
209753287767SSowmini Varadhan 			dce = dce_lookup_v6(dst_addr, ifindex, ipst,
209853287767SSowmini Varadhan 			    &generation);
209953287767SSowmini Varadhan 		}
210053287767SSowmini Varadhan 	} else {
210153287767SSowmini Varadhan 		dce = dce_lookup_v6(dst_addr, ifindex, ipst, &generation);
210253287767SSowmini Varadhan 	}
210353287767SSowmini Varadhan 	ASSERT(dce != NULL);
210453287767SSowmini Varadhan 	if (ixa->ixa_dce != NULL)
210553287767SSowmini Varadhan 		dce_refrele_notr(ixa->ixa_dce);
210653287767SSowmini Varadhan #ifdef DEBUG
210753287767SSowmini Varadhan 	dce_refhold_notr(dce);
210853287767SSowmini Varadhan 	dce_refrele(dce);
210953287767SSowmini Varadhan #endif
211053287767SSowmini Varadhan 	ixa->ixa_dce = dce;
211153287767SSowmini Varadhan 	ixa->ixa_dce_generation = generation;
211253287767SSowmini Varadhan 
211353287767SSowmini Varadhan 
211453287767SSowmini Varadhan 	/*
2115bd670b35SErik Nordmark 	 * For multicast with multirt we have a flag passed back from
2116bd670b35SErik Nordmark 	 * ire_lookup_multi_ill_v6 since we don't have an IRE for each
2117bd670b35SErik Nordmark 	 * possible multicast address.
2118bd670b35SErik Nordmark 	 * We also need a flag for multicast since we can't check
2119bd670b35SErik Nordmark 	 * whether RTF_MULTIRT is set in ixa_ire for multicast.
2120bd670b35SErik Nordmark 	 */
2121bd670b35SErik Nordmark 	if (multirt) {
2122bd670b35SErik Nordmark 		ixa->ixa_postfragfn = ip_postfrag_multirt_v6;
2123bd670b35SErik Nordmark 		ixa->ixa_flags |= IXAF_MULTIRT_MULTICAST;
21247c478bd9Sstevel@tonic-gate 	} else {
2125bd670b35SErik Nordmark 		ixa->ixa_postfragfn = ire->ire_postfragfn;
2126bd670b35SErik Nordmark 		ixa->ixa_flags &= ~IXAF_MULTIRT_MULTICAST;
21277c478bd9Sstevel@tonic-gate 	}
2128bd670b35SErik Nordmark 	if (!(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
2129bd670b35SErik Nordmark 		/* Get an nce to cache. */
2130bd670b35SErik Nordmark 		nce = ire_to_nce(ire, NULL, firsthop);
2131bd670b35SErik Nordmark 		if (nce == NULL) {
2132bd670b35SErik Nordmark 			/* Allocation failure? */
2133bd670b35SErik Nordmark 			ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
21347c478bd9Sstevel@tonic-gate 		} else {
2135bd670b35SErik Nordmark 			if (ixa->ixa_nce != NULL)
2136bd670b35SErik Nordmark 				nce_refrele(ixa->ixa_nce);
2137bd670b35SErik Nordmark 			ixa->ixa_nce = nce;
21387c478bd9Sstevel@tonic-gate 		}
21397c478bd9Sstevel@tonic-gate 	}
21407c478bd9Sstevel@tonic-gate 
21417c478bd9Sstevel@tonic-gate 	/*
21427c478bd9Sstevel@tonic-gate 	 * If the source address is a loopback address, the
21437c478bd9Sstevel@tonic-gate 	 * destination had best be local or multicast.
2144bd670b35SErik Nordmark 	 * If we are sending to an IRE_LOCAL using a loopback source then
2145bd670b35SErik Nordmark 	 * it had better be the same zoneid.
21467c478bd9Sstevel@tonic-gate 	 */
2147bd670b35SErik Nordmark 	if (IN6_IS_ADDR_LOOPBACK(src_addrp)) {
2148bd670b35SErik Nordmark 		if ((ire->ire_type & IRE_LOCAL) && ire->ire_zoneid != zoneid) {
2149bd670b35SErik Nordmark 			ire = NULL;	/* Stored in ixa_ire */
2150bd670b35SErik Nordmark 			error = EADDRNOTAVAIL;
21517c478bd9Sstevel@tonic-gate 			goto bad_addr;
21527c478bd9Sstevel@tonic-gate 		}
2153bd670b35SErik Nordmark 		if (!(ire->ire_type & (IRE_LOOPBACK|IRE_LOCAL|IRE_MULTICAST))) {
2154bd670b35SErik Nordmark 			ire = NULL;	/* Stored in ixa_ire */
2155bd670b35SErik Nordmark 			error = EADDRNOTAVAIL;
21567c478bd9Sstevel@tonic-gate 			goto bad_addr;
21577c478bd9Sstevel@tonic-gate 		}
21587c478bd9Sstevel@tonic-gate 	}
21597c478bd9Sstevel@tonic-gate 
21607c478bd9Sstevel@tonic-gate 	/*
2161bd670b35SErik Nordmark 	 * Does the caller want us to pick a source address?
21627c478bd9Sstevel@tonic-gate 	 */
2163bd670b35SErik Nordmark 	if (flags & IPDF_SELECT_SRC) {
2164bd670b35SErik Nordmark 		in6_addr_t	src_addr;
21657c478bd9Sstevel@tonic-gate 
2166188e1664SErik Nordmark 		/*
2167188e1664SErik Nordmark 		 * We use use ire_nexthop_ill to avoid the under ipmp
2168188e1664SErik Nordmark 		 * interface for source address selection. Note that for ipmp
2169188e1664SErik Nordmark 		 * probe packets, ixa_ifindex would have been specified, and
2170188e1664SErik Nordmark 		 * the ip_select_route() invocation would have picked an ire
2171188e1664SErik Nordmark 		 * will ire_ill pointing at an under interface.
2172188e1664SErik Nordmark 		 */
2173188e1664SErik Nordmark 		ill = ire_nexthop_ill(ire);
2174188e1664SErik Nordmark 
2175bd670b35SErik Nordmark 		/* If unreachable we have no ill but need some source */
2176bd670b35SErik Nordmark 		if (ill == NULL) {
2177bd670b35SErik Nordmark 			src_addr = ipv6_loopback;
2178bd670b35SErik Nordmark 			/* Make sure we look for a better source address */
2179bd670b35SErik Nordmark 			generation = SRC_GENERATION_VERIFY;
21800f1702c5SYu Xiangning 		} else {
2181bd670b35SErik Nordmark 			error = ip_select_source_v6(ill, &setsrc, dst_addr,
2182bd670b35SErik Nordmark 			    zoneid, ipst, B_FALSE, ixa->ixa_src_preferences,
2183bd670b35SErik Nordmark 			    &src_addr, &generation, NULL);
2184bd670b35SErik Nordmark 			if (error != 0) {
2185bd670b35SErik Nordmark 				ire = NULL;	/* Stored in ixa_ire */
21860f1702c5SYu Xiangning 				goto bad_addr;
21870f1702c5SYu Xiangning 			}
2188bd670b35SErik Nordmark 		}
21890f1702c5SYu Xiangning 
2190bd670b35SErik Nordmark 		/*
2191bd670b35SErik Nordmark 		 * We allow the source address to to down.
2192bd670b35SErik Nordmark 		 * However, we check that we don't use the loopback address
2193bd670b35SErik Nordmark 		 * as a source when sending out on the wire.
2194bd670b35SErik Nordmark 		 */
2195bd670b35SErik Nordmark 		if (IN6_IS_ADDR_LOOPBACK(&src_addr) &&
2196bd670b35SErik Nordmark 		    !(ire->ire_type & (IRE_LOCAL|IRE_LOOPBACK|IRE_MULTICAST)) &&
2197bd670b35SErik Nordmark 		    !(ire->ire_flags & (RTF_REJECT|RTF_BLACKHOLE))) {
2198bd670b35SErik Nordmark 			ire = NULL;	/* Stored in ixa_ire */
2199bd670b35SErik Nordmark 			error = EADDRNOTAVAIL;
22000f1702c5SYu Xiangning 			goto bad_addr;
22010f1702c5SYu Xiangning 		}
22020f1702c5SYu Xiangning 
2203bd670b35SErik Nordmark 		*src_addrp = src_addr;
2204bd670b35SErik Nordmark 		ixa->ixa_src_generation = generation;
22050f1702c5SYu Xiangning 	}
22060f1702c5SYu Xiangning 
22070f1702c5SYu Xiangning 	/*
2208bd670b35SErik Nordmark 	 * Make sure we don't leave an unreachable ixa_nce in place
2209bd670b35SErik Nordmark 	 * since ip_select_route is used when we unplumb i.e., remove
2210bd670b35SErik Nordmark 	 * references on ixa_ire, ixa_nce, and ixa_dce.
22117c478bd9Sstevel@tonic-gate 	 */
2212bd670b35SErik Nordmark 	nce = ixa->ixa_nce;
2213bd670b35SErik Nordmark 	if (nce != NULL && nce->nce_is_condemned) {
2214bd670b35SErik Nordmark 		nce_refrele(nce);
2215bd670b35SErik Nordmark 		ixa->ixa_nce = NULL;
2216bd670b35SErik Nordmark 		ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
22177c478bd9Sstevel@tonic-gate 	}
22187c478bd9Sstevel@tonic-gate 
2219bd670b35SErik Nordmark 	/*
2220bd670b35SErik Nordmark 	 * Note that IPv6 multicast supports PMTU discovery unlike IPv4
2221bd670b35SErik Nordmark 	 * multicast. But pmtu discovery is only enabled for connected
2222bd670b35SErik Nordmark 	 * sockets in general.
2223bd670b35SErik Nordmark 	 */
2224bd670b35SErik Nordmark 
2225bd670b35SErik Nordmark 	/*
2226bd670b35SErik Nordmark 	 * Set initial value for fragmentation limit.  Either conn_ip_output
2227bd670b35SErik Nordmark 	 * or ULP might updates it when there are routing changes.
2228bd670b35SErik Nordmark 	 * Handles a NULL ixa_ire->ire_ill or a NULL ixa_nce for RTF_REJECT.
2229bd670b35SErik Nordmark 	 */
2230bd670b35SErik Nordmark 	pmtu = ip_get_pmtu(ixa);
2231bd670b35SErik Nordmark 	ixa->ixa_fragsize = pmtu;
2232bd670b35SErik Nordmark 	/* Make sure ixa_fragsize and ixa_pmtu remain identical */
2233bd670b35SErik Nordmark 	if (ixa->ixa_flags & IXAF_VERIFY_PMTU)
2234bd670b35SErik Nordmark 		ixa->ixa_pmtu = pmtu;
2235bd670b35SErik Nordmark 
2236bd670b35SErik Nordmark 	/*
2237bd670b35SErik Nordmark 	 * Extract information useful for some transports.
2238bd670b35SErik Nordmark 	 * First we look for DCE metrics. Then we take what we have in
2239bd670b35SErik Nordmark 	 * the metrics in the route, where the offlink is used if we have
2240bd670b35SErik Nordmark 	 * one.
2241bd670b35SErik Nordmark 	 */
2242bd670b35SErik Nordmark 	if (uinfo != NULL) {
2243bd670b35SErik Nordmark 		bzero(uinfo, sizeof (*uinfo));
2244bd670b35SErik Nordmark 
2245bd670b35SErik Nordmark 		if (dce->dce_flags & DCEF_UINFO)
2246bd670b35SErik Nordmark 			*uinfo = dce->dce_uinfo;
2247bd670b35SErik Nordmark 
2248bd670b35SErik Nordmark 		rts_merge_metrics(uinfo, &ire->ire_metrics);
2249bd670b35SErik Nordmark 
2250bd670b35SErik Nordmark 		/* Allow ire_metrics to decrease the path MTU from above */
2251bd670b35SErik Nordmark 		if (uinfo->iulp_mtu == 0 || uinfo->iulp_mtu > pmtu)
2252bd670b35SErik Nordmark 			uinfo->iulp_mtu = pmtu;
2253bd670b35SErik Nordmark 
2254bd670b35SErik Nordmark 		uinfo->iulp_localnet = (ire->ire_type & IRE_ONLINK) != 0;
2255bd670b35SErik Nordmark 		uinfo->iulp_loopback = (ire->ire_type & IRE_LOOPBACK) != 0;
2256bd670b35SErik Nordmark 		uinfo->iulp_local = (ire->ire_type & IRE_LOCAL) != 0;
2257bd670b35SErik Nordmark 	}
2258bd670b35SErik Nordmark 
2259bd670b35SErik Nordmark 	if (ill != NULL)
2260bd670b35SErik Nordmark 		ill_refrele(ill);
2261bd670b35SErik Nordmark 
2262bd670b35SErik Nordmark 	return (error);
2263bd670b35SErik Nordmark 
2264bd670b35SErik Nordmark bad_addr:
2265bd670b35SErik Nordmark 	if (ire != NULL)
2266bd670b35SErik Nordmark 		ire_refrele(ire);
2267bd670b35SErik Nordmark 
2268bd670b35SErik Nordmark 	if (ill != NULL)
2269bd670b35SErik Nordmark 		ill_refrele(ill);
2270bd670b35SErik Nordmark 
2271bd670b35SErik Nordmark 	/*
2272bd670b35SErik Nordmark 	 * Make sure we don't leave an unreachable ixa_nce in place
2273bd670b35SErik Nordmark 	 * since ip_select_route is used when we unplumb i.e., remove
2274bd670b35SErik Nordmark 	 * references on ixa_ire, ixa_nce, and ixa_dce.
2275bd670b35SErik Nordmark 	 */
2276bd670b35SErik Nordmark 	nce = ixa->ixa_nce;
2277bd670b35SErik Nordmark 	if (nce != NULL && nce->nce_is_condemned) {
2278bd670b35SErik Nordmark 		nce_refrele(nce);
2279bd670b35SErik Nordmark 		ixa->ixa_nce = NULL;
2280bd670b35SErik Nordmark 		ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
2281bd670b35SErik Nordmark 	}
2282bd670b35SErik Nordmark 
2283bd670b35SErik Nordmark 	return (error);
22847c478bd9Sstevel@tonic-gate }
22857c478bd9Sstevel@tonic-gate 
22867c478bd9Sstevel@tonic-gate /*
22877c478bd9Sstevel@tonic-gate  * Handle protocols with which IP is less intimate.  There
22887c478bd9Sstevel@tonic-gate  * can be more than one stream bound to a particular
22897c478bd9Sstevel@tonic-gate  * protocol.  When this is the case, normally each one gets a copy
22907c478bd9Sstevel@tonic-gate  * of any incoming packets.
22917c478bd9Sstevel@tonic-gate  *
22927c478bd9Sstevel@tonic-gate  * Zones notes:
2293bd670b35SErik Nordmark  * Packets will be distributed to conns in all zones. This is really only
22947c478bd9Sstevel@tonic-gate  * useful for ICMPv6 as only applications in the global zone can create raw
22957c478bd9Sstevel@tonic-gate  * sockets for other protocols.
22967c478bd9Sstevel@tonic-gate  */
2297bd670b35SErik Nordmark void
ip_fanout_proto_v6(mblk_t * mp,ip6_t * ip6h,ip_recv_attr_t * ira)2298bd670b35SErik Nordmark ip_fanout_proto_v6(mblk_t *mp, ip6_t *ip6h, ip_recv_attr_t *ira)
22997c478bd9Sstevel@tonic-gate {
2300bd670b35SErik Nordmark 	mblk_t		*mp1;
2301bd670b35SErik Nordmark 	in6_addr_t	laddr = ip6h->ip6_dst;
23027c478bd9Sstevel@tonic-gate 	conn_t		*connp, *first_connp, *next_connp;
23037c478bd9Sstevel@tonic-gate 	connf_t		*connfp;
2304bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
2305bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
23067c478bd9Sstevel@tonic-gate 
2307bd670b35SErik Nordmark 	connfp = &ipst->ips_ipcl_proto_fanout_v6[ira->ira_protocol];
23087c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
23097c478bd9Sstevel@tonic-gate 	connp = connfp->connf_head;
23107c478bd9Sstevel@tonic-gate 	for (connp = connfp->connf_head; connp != NULL;
23117c478bd9Sstevel@tonic-gate 	    connp = connp->conn_next) {
2312bd670b35SErik Nordmark 		/* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */
2313bd670b35SErik Nordmark 		if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
2314bd670b35SErik Nordmark 		    (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
2315bd670b35SErik Nordmark 		    tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
23167c478bd9Sstevel@tonic-gate 			break;
23177c478bd9Sstevel@tonic-gate 	}
23187c478bd9Sstevel@tonic-gate 
23190f1702c5SYu Xiangning 	if (connp == NULL) {
23207c478bd9Sstevel@tonic-gate 		/*
23217c478bd9Sstevel@tonic-gate 		 * No one bound to this port.  Is
23227c478bd9Sstevel@tonic-gate 		 * there a client that wants all
23237c478bd9Sstevel@tonic-gate 		 * unclaimed datagrams?
23247c478bd9Sstevel@tonic-gate 		 */
23257c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
2326bd670b35SErik Nordmark 		ip_fanout_send_icmp_v6(mp, ICMP6_PARAM_PROB,
2327bd670b35SErik Nordmark 		    ICMP6_PARAMPROB_NEXTHEADER, ira);
23287c478bd9Sstevel@tonic-gate 		return;
23297c478bd9Sstevel@tonic-gate 	}
23307c478bd9Sstevel@tonic-gate 
2331bd670b35SErik Nordmark 	ASSERT(IPCL_IS_NONSTR(connp) || connp->conn_rq != NULL);
23320f1702c5SYu Xiangning 
23337c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
23347c478bd9Sstevel@tonic-gate 	first_connp = connp;
23357c478bd9Sstevel@tonic-gate 
23367c478bd9Sstevel@tonic-gate 	/*
23377c478bd9Sstevel@tonic-gate 	 * XXX: Fix the multiple protocol listeners case. We should not
2338bd670b35SErik Nordmark 	 * be walking the conn->conn_next list here.
23397c478bd9Sstevel@tonic-gate 	 */
23407c478bd9Sstevel@tonic-gate 	connp = connp->conn_next;
23417c478bd9Sstevel@tonic-gate 	for (;;) {
23427c478bd9Sstevel@tonic-gate 		while (connp != NULL) {
2343bd670b35SErik Nordmark 			/* Note: IPCL_PROTO_MATCH_V6 includes conn_wantpacket */
2344bd670b35SErik Nordmark 			if (IPCL_PROTO_MATCH_V6(connp, ira, ip6h) &&
2345bd670b35SErik Nordmark 			    (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
2346bd670b35SErik Nordmark 			    tsol_receive_local(mp, &laddr, IPV6_VERSION,
2347bd670b35SErik Nordmark 			    ira, connp)))
23487c478bd9Sstevel@tonic-gate 				break;
23497c478bd9Sstevel@tonic-gate 			connp = connp->conn_next;
23507c478bd9Sstevel@tonic-gate 		}
23517c478bd9Sstevel@tonic-gate 
2352bd670b35SErik Nordmark 		if (connp == NULL) {
2353bd670b35SErik Nordmark 			/* No more interested clients */
23547c478bd9Sstevel@tonic-gate 			connp = first_connp;
23557c478bd9Sstevel@tonic-gate 			break;
23567c478bd9Sstevel@tonic-gate 		}
2357bd670b35SErik Nordmark 		if (((mp1 = dupmsg(mp)) == NULL) &&
2358bd670b35SErik Nordmark 		    ((mp1 = copymsg(mp)) == NULL)) {
2359bd670b35SErik Nordmark 			/* Memory allocation failed */
2360bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
2361bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, ill);
2362bd670b35SErik Nordmark 			connp = first_connp;
2363bd670b35SErik Nordmark 			break;
2364bd670b35SErik Nordmark 		}
2365bd670b35SErik Nordmark 
23667c478bd9Sstevel@tonic-gate 		CONN_INC_REF(connp);
23677c478bd9Sstevel@tonic-gate 		mutex_exit(&connfp->connf_lock);
23687c478bd9Sstevel@tonic-gate 
2369bd670b35SErik Nordmark 		ip_fanout_proto_conn(connp, mp1, NULL, (ip6_t *)mp1->b_rptr,
2370bd670b35SErik Nordmark 		    ira);
23712b24ab6bSSebastien Roy 
23727c478bd9Sstevel@tonic-gate 		mutex_enter(&connfp->connf_lock);
23737c478bd9Sstevel@tonic-gate 		/* Follow the next pointer before releasing the conn. */
23747c478bd9Sstevel@tonic-gate 		next_connp = connp->conn_next;
23757c478bd9Sstevel@tonic-gate 		CONN_DEC_REF(connp);
23767c478bd9Sstevel@tonic-gate 		connp = next_connp;
23777c478bd9Sstevel@tonic-gate 	}
23787c478bd9Sstevel@tonic-gate 
23797c478bd9Sstevel@tonic-gate 	/* Last one.  Send it upstream. */
23807c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
23817c478bd9Sstevel@tonic-gate 
2382bd670b35SErik Nordmark 	ip_fanout_proto_conn(connp, mp, NULL, ip6h, ira);
23837c478bd9Sstevel@tonic-gate 
23847c478bd9Sstevel@tonic-gate 	CONN_DEC_REF(connp);
23857c478bd9Sstevel@tonic-gate }
23867c478bd9Sstevel@tonic-gate 
23877c478bd9Sstevel@tonic-gate /*
2388bd670b35SErik Nordmark  * Called when it is conceptually a ULP that would sent the packet
2389bd670b35SErik Nordmark  * e.g., port unreachable and nexthdr unknown. Check that the packet
2390bd670b35SErik Nordmark  * would have passed the IPsec global policy before sending the error.
2391bd670b35SErik Nordmark  *
2392bd670b35SErik Nordmark  * Send an ICMP error after patching up the packet appropriately.
2393bd670b35SErik Nordmark  * Uses ip_drop_input and bumps the appropriate MIB.
2394bd670b35SErik Nordmark  * For ICMP6_PARAMPROB_NEXTHEADER we determine the offset to use.
23957c478bd9Sstevel@tonic-gate  */
2396bd670b35SErik Nordmark void
ip_fanout_send_icmp_v6(mblk_t * mp,uint_t icmp_type,uint8_t icmp_code,ip_recv_attr_t * ira)2397bd670b35SErik Nordmark ip_fanout_send_icmp_v6(mblk_t *mp, uint_t icmp_type, uint8_t icmp_code,
2398bd670b35SErik Nordmark     ip_recv_attr_t *ira)
23997c478bd9Sstevel@tonic-gate {
24007c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h;
24017c478bd9Sstevel@tonic-gate 	boolean_t	secure;
2402bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
2403bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
2404bd670b35SErik Nordmark 	netstack_t	*ns = ipst->ips_netstack;
2405bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ns->netstack_ipsec;
24067c478bd9Sstevel@tonic-gate 
2407bd670b35SErik Nordmark 	secure = ira->ira_flags & IRAF_IPSEC_SECURE;
2408bd670b35SErik Nordmark 
24097c478bd9Sstevel@tonic-gate 	/*
24107c478bd9Sstevel@tonic-gate 	 * We are generating an icmp error for some inbound packet.
24117c478bd9Sstevel@tonic-gate 	 * Called from all ip_fanout_(udp, tcp, proto) functions.
24127c478bd9Sstevel@tonic-gate 	 * Before we generate an error, check with global policy
24137c478bd9Sstevel@tonic-gate 	 * to see whether this is allowed to enter the system. As
24147c478bd9Sstevel@tonic-gate 	 * there is no "conn", we are checking with global policy.
24157c478bd9Sstevel@tonic-gate 	 */
24167c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
2417f4b3ec61Sdh155122 	if (secure || ipss->ipsec_inbound_v6_policy_present) {
2418bd670b35SErik Nordmark 		mp = ipsec_check_global_policy(mp, NULL, NULL, ip6h, ira, ns);
2419bd670b35SErik Nordmark 		if (mp == NULL)
2420bd670b35SErik Nordmark 			return;
24217c478bd9Sstevel@tonic-gate 	}
24227c478bd9Sstevel@tonic-gate 
2423bd670b35SErik Nordmark 	/* We never send errors for protocols that we do implement */
2424bd670b35SErik Nordmark 	if (ira->ira_protocol == IPPROTO_ICMPV6) {
2425bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
2426bd670b35SErik Nordmark 		ip_drop_input("ip_fanout_send_icmp_v6", mp, ill);
2427bd670b35SErik Nordmark 		freemsg(mp);
2428bd670b35SErik Nordmark 		return;
2429bd670b35SErik Nordmark 	}
24307c478bd9Sstevel@tonic-gate 
24317c478bd9Sstevel@tonic-gate 	switch (icmp_type) {
24327c478bd9Sstevel@tonic-gate 	case ICMP6_DST_UNREACH:
2433bd670b35SErik Nordmark 		ASSERT(icmp_code == ICMP6_DST_UNREACH_NOPORT);
2434bd670b35SErik Nordmark 
2435bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, udpIfStatsNoPorts);
2436bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsNoPorts", mp, ill);
2437bd670b35SErik Nordmark 
2438bd670b35SErik Nordmark 		icmp_unreachable_v6(mp, icmp_code, B_FALSE, ira);
24397c478bd9Sstevel@tonic-gate 		break;
24407c478bd9Sstevel@tonic-gate 	case ICMP6_PARAM_PROB:
2441bd670b35SErik Nordmark 		ASSERT(icmp_code == ICMP6_PARAMPROB_NEXTHEADER);
2442bd670b35SErik Nordmark 
2443bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInUnknownProtos);
2444bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInUnknownProtos", mp, ill);
2445bd670b35SErik Nordmark 
2446bd670b35SErik Nordmark 		/* Let the system determine the offset for this one */
2447bd670b35SErik Nordmark 		icmp_param_problem_nexthdr_v6(mp, B_FALSE, ira);
24487c478bd9Sstevel@tonic-gate 		break;
24497c478bd9Sstevel@tonic-gate 	default:
24507c478bd9Sstevel@tonic-gate #ifdef DEBUG
24517c478bd9Sstevel@tonic-gate 		panic("ip_fanout_send_icmp_v6: wrong type");
24527c478bd9Sstevel@tonic-gate 		/*NOTREACHED*/
24537c478bd9Sstevel@tonic-gate #else
2454bd670b35SErik Nordmark 		freemsg(mp);
24557c478bd9Sstevel@tonic-gate 		break;
24567c478bd9Sstevel@tonic-gate #endif
24577c478bd9Sstevel@tonic-gate 	}
24587c478bd9Sstevel@tonic-gate }
24597c478bd9Sstevel@tonic-gate 
24607c478bd9Sstevel@tonic-gate /*
2461bd670b35SErik Nordmark  * Fanout for UDP packets that are multicast or ICMP errors.
2462bd670b35SErik Nordmark  * (Unicast fanout is handled in ip_input_v6.)
2463bd670b35SErik Nordmark  *
2464bd670b35SErik Nordmark  * If SO_REUSEADDR is set all multicast packets
2465bd670b35SErik Nordmark  * will be delivered to all conns bound to the same port.
2466bd670b35SErik Nordmark  *
24677c478bd9Sstevel@tonic-gate  * Fanout for UDP packets.
24687c478bd9Sstevel@tonic-gate  * The caller puts <fport, lport> in the ports parameter.
24697c478bd9Sstevel@tonic-gate  * ire_type must be IRE_BROADCAST for multicast and broadcast packets.
24707c478bd9Sstevel@tonic-gate  *
24717c478bd9Sstevel@tonic-gate  * If SO_REUSEADDR is set all multicast and broadcast packets
2472bd670b35SErik Nordmark  * will be delivered to all conns bound to the same port.
24737c478bd9Sstevel@tonic-gate  *
24747c478bd9Sstevel@tonic-gate  * Zones notes:
2475bd670b35SErik Nordmark  * Earlier in ip_input on a system with multiple shared-IP zones we
2476bd670b35SErik Nordmark  * duplicate the multicast and broadcast packets and send them up
2477bd670b35SErik Nordmark  * with each explicit zoneid that exists on that ill.
2478bd670b35SErik Nordmark  * This means that here we can match the zoneid with SO_ALLZONES being special.
24797c478bd9Sstevel@tonic-gate  */
2480bd670b35SErik Nordmark void
ip_fanout_udp_multi_v6(mblk_t * mp,ip6_t * ip6h,uint16_t lport,uint16_t fport,ip_recv_attr_t * ira)2481bd670b35SErik Nordmark ip_fanout_udp_multi_v6(mblk_t *mp, ip6_t *ip6h, uint16_t lport, uint16_t fport,
2482bd670b35SErik Nordmark     ip_recv_attr_t *ira)
24837c478bd9Sstevel@tonic-gate {
2484bd670b35SErik Nordmark 	in6_addr_t	laddr;
24857c478bd9Sstevel@tonic-gate 	conn_t		*connp;
24867c478bd9Sstevel@tonic-gate 	connf_t		*connfp;
2487bd670b35SErik Nordmark 	in6_addr_t	faddr;
2488bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
2489bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
24907c478bd9Sstevel@tonic-gate 
2491bd670b35SErik Nordmark 	ASSERT(ira->ira_flags & (IRAF_MULTIBROADCAST|IRAF_ICMP_ERROR));
24927c478bd9Sstevel@tonic-gate 
2493bd670b35SErik Nordmark 	laddr = ip6h->ip6_dst;
2494bd670b35SErik Nordmark 	faddr = ip6h->ip6_src;
249545916cd2Sjpk 
24967c478bd9Sstevel@tonic-gate 	/* Attempt to find a client stream based on destination port. */
2497bd670b35SErik Nordmark 	connfp = &ipst->ips_ipcl_udp_fanout[IPCL_UDP_HASH(lport, ipst)];
24987c478bd9Sstevel@tonic-gate 	mutex_enter(&connfp->connf_lock);
24997c478bd9Sstevel@tonic-gate 	connp = connfp->connf_head;
25007c478bd9Sstevel@tonic-gate 	while (connp != NULL) {
2501bd670b35SErik Nordmark 		if ((IPCL_UDP_MATCH_V6(connp, lport, laddr, fport, faddr)) &&
2502bd670b35SErik Nordmark 		    conn_wantpacket_v6(connp, ira, ip6h) &&
2503bd670b35SErik Nordmark 		    (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
2504bd670b35SErik Nordmark 		    tsol_receive_local(mp, &laddr, IPV6_VERSION, ira, connp)))
25057c478bd9Sstevel@tonic-gate 			break;
25067c478bd9Sstevel@tonic-gate 		connp = connp->conn_next;
25077c478bd9Sstevel@tonic-gate 	}
25087c478bd9Sstevel@tonic-gate 
2509bd670b35SErik Nordmark 	if (connp == NULL)
25107c478bd9Sstevel@tonic-gate 		goto notfound;
25117c478bd9Sstevel@tonic-gate 
25127c478bd9Sstevel@tonic-gate 	CONN_INC_REF(connp);
2513bd670b35SErik Nordmark 
2514bd670b35SErik Nordmark 	if (connp->conn_reuseaddr) {
2515bd670b35SErik Nordmark 		conn_t		*first_connp = connp;
2516bd670b35SErik Nordmark 		conn_t		*next_connp;
2517bd670b35SErik Nordmark 		mblk_t		*mp1;
2518bd670b35SErik Nordmark 
25197c478bd9Sstevel@tonic-gate 		connp = connp->conn_next;
25207c478bd9Sstevel@tonic-gate 		for (;;) {
25217c478bd9Sstevel@tonic-gate 			while (connp != NULL) {
2522bd670b35SErik Nordmark 				if (IPCL_UDP_MATCH_V6(connp, lport, laddr,
2523bd670b35SErik Nordmark 				    fport, faddr) &&
2524bd670b35SErik Nordmark 				    conn_wantpacket_v6(connp, ira, ip6h) &&
2525bd670b35SErik Nordmark 				    (!(ira->ira_flags & IRAF_SYSTEM_LABELED) ||
2526bd670b35SErik Nordmark 				    tsol_receive_local(mp, &laddr, IPV6_VERSION,
2527bd670b35SErik Nordmark 				    ira, connp)))
25287c478bd9Sstevel@tonic-gate 					break;
25297c478bd9Sstevel@tonic-gate 				connp = connp->conn_next;
25307c478bd9Sstevel@tonic-gate 			}
2531bd670b35SErik Nordmark 			if (connp == NULL) {
2532bd670b35SErik Nordmark 				/* No more interested clients */
2533bd670b35SErik Nordmark 				connp = first_connp;
25347c478bd9Sstevel@tonic-gate 				break;
25357c478bd9Sstevel@tonic-gate 			}
2536bd670b35SErik Nordmark 			if (((mp1 = dupmsg(mp)) == NULL) &&
2537bd670b35SErik Nordmark 			    ((mp1 = copymsg(mp)) == NULL)) {
2538bd670b35SErik Nordmark 				/* Memory allocation failed */
2539bd670b35SErik Nordmark 				BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
2540bd670b35SErik Nordmark 				ip_drop_input("ipIfStatsInDiscards", mp, ill);
2541bd670b35SErik Nordmark 				connp = first_connp;
2542bd670b35SErik Nordmark 				break;
2543bd670b35SErik Nordmark 			}
2544bd670b35SErik Nordmark 
25457c478bd9Sstevel@tonic-gate 			CONN_INC_REF(connp);
25467c478bd9Sstevel@tonic-gate 			mutex_exit(&connfp->connf_lock);
25477c478bd9Sstevel@tonic-gate 
2548bd670b35SErik Nordmark 			IP6_STAT(ipst, ip6_udp_fanmb);
2549bd670b35SErik Nordmark 			ip_fanout_udp_conn(connp, mp1, NULL,
2550bd670b35SErik Nordmark 			    (ip6_t *)mp1->b_rptr, ira);
2551ff550d0eSmasputra 
25527c478bd9Sstevel@tonic-gate 			mutex_enter(&connfp->connf_lock);
25537c478bd9Sstevel@tonic-gate 			/* Follow the next pointer before releasing the conn. */
2554bd670b35SErik Nordmark 			next_connp = connp->conn_next;
2555f4b3ec61Sdh155122 			IP6_STAT(ipst, ip6_udp_fanmb);
25567c478bd9Sstevel@tonic-gate 			CONN_DEC_REF(connp);
2557bd670b35SErik Nordmark 			connp = next_connp;
2558bd670b35SErik Nordmark 		}
25597c478bd9Sstevel@tonic-gate 	}
25607c478bd9Sstevel@tonic-gate 
25617c478bd9Sstevel@tonic-gate 	/* Last one.  Send it upstream. */
25627c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
25637c478bd9Sstevel@tonic-gate 
2564f4b3ec61Sdh155122 	IP6_STAT(ipst, ip6_udp_fanmb);
2565bd670b35SErik Nordmark 	ip_fanout_udp_conn(connp, mp, NULL, ip6h, ira);
25667c478bd9Sstevel@tonic-gate 	CONN_DEC_REF(connp);
25677c478bd9Sstevel@tonic-gate 	return;
25687c478bd9Sstevel@tonic-gate 
25697c478bd9Sstevel@tonic-gate notfound:
25707c478bd9Sstevel@tonic-gate 	mutex_exit(&connfp->connf_lock);
25717c478bd9Sstevel@tonic-gate 	/*
25727c478bd9Sstevel@tonic-gate 	 * No one bound to this port.  Is
25737c478bd9Sstevel@tonic-gate 	 * there a client that wants all
25747c478bd9Sstevel@tonic-gate 	 * unclaimed datagrams?
25757c478bd9Sstevel@tonic-gate 	 */
2576f4b3ec61Sdh155122 	if (ipst->ips_ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) {
2577bd670b35SErik Nordmark 		ASSERT(ira->ira_protocol == IPPROTO_UDP);
2578bd670b35SErik Nordmark 		ip_fanout_proto_v6(mp, ip6h, ira);
25797c478bd9Sstevel@tonic-gate 	} else {
2580bd670b35SErik Nordmark 		ip_fanout_send_icmp_v6(mp, ICMP6_DST_UNREACH,
2581bd670b35SErik Nordmark 		    ICMP6_DST_UNREACH_NOPORT, ira);
25827c478bd9Sstevel@tonic-gate 	}
25837c478bd9Sstevel@tonic-gate }
25847c478bd9Sstevel@tonic-gate 
25857c478bd9Sstevel@tonic-gate /*
25867c478bd9Sstevel@tonic-gate  * int ip_find_hdr_v6()
25877c478bd9Sstevel@tonic-gate  *
2588bd670b35SErik Nordmark  * This routine is used by the upper layer protocols, iptun, and IPsec:
25897c478bd9Sstevel@tonic-gate  * - Set extension header pointers to appropriate locations
25907c478bd9Sstevel@tonic-gate  * - Determine IPv6 header length and return it
25917c478bd9Sstevel@tonic-gate  * - Return a pointer to the last nexthdr value
25927c478bd9Sstevel@tonic-gate  *
25937c478bd9Sstevel@tonic-gate  * The caller must initialize ipp_fields.
2594bd670b35SErik Nordmark  * The upper layer protocols normally set label_separate which makes the
2595bd670b35SErik Nordmark  * routine put the TX label in ipp_label_v6. If this is not set then
2596bd670b35SErik Nordmark  * the hop-by-hop options including the label are placed in ipp_hopopts.
25977c478bd9Sstevel@tonic-gate  *
25987c478bd9Sstevel@tonic-gate  * NOTE: If multiple extension headers of the same type are present,
25997c478bd9Sstevel@tonic-gate  * ip_find_hdr_v6() will set the respective extension header pointers
26007c478bd9Sstevel@tonic-gate  * to the first one that it encounters in the IPv6 header.  It also
26017c478bd9Sstevel@tonic-gate  * skips fragment headers.  This routine deals with malformed packets
26027c478bd9Sstevel@tonic-gate  * of various sorts in which case the returned length is up to the
26037c478bd9Sstevel@tonic-gate  * malformed part.
26047c478bd9Sstevel@tonic-gate  */
26057c478bd9Sstevel@tonic-gate int
ip_find_hdr_v6(mblk_t * mp,ip6_t * ip6h,boolean_t label_separate,ip_pkt_t * ipp,uint8_t * nexthdrp)2606bd670b35SErik Nordmark ip_find_hdr_v6(mblk_t *mp, ip6_t *ip6h, boolean_t label_separate, ip_pkt_t *ipp,
2607bd670b35SErik Nordmark     uint8_t *nexthdrp)
26087c478bd9Sstevel@tonic-gate {
26097c478bd9Sstevel@tonic-gate 	uint_t	length, ehdrlen;
26107c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
26117c478bd9Sstevel@tonic-gate 	uint8_t *whereptr, *endptr;
26127c478bd9Sstevel@tonic-gate 	ip6_dest_t *tmpdstopts;
26137c478bd9Sstevel@tonic-gate 	ip6_rthdr_t *tmprthdr;
26147c478bd9Sstevel@tonic-gate 	ip6_hbh_t *tmphopopts;
26157c478bd9Sstevel@tonic-gate 	ip6_frag_t *tmpfraghdr;
26167c478bd9Sstevel@tonic-gate 
2617bd670b35SErik Nordmark 	ipp->ipp_fields |= IPPF_HOPLIMIT | IPPF_TCLASS | IPPF_ADDR;
2618bd670b35SErik Nordmark 	ipp->ipp_hoplimit = ip6h->ip6_hops;
2619bd670b35SErik Nordmark 	ipp->ipp_tclass = IPV6_FLOW_TCLASS(ip6h->ip6_flow);
2620bd670b35SErik Nordmark 	ipp->ipp_addr = ip6h->ip6_dst;
2621bd670b35SErik Nordmark 
26227c478bd9Sstevel@tonic-gate 	length = IPV6_HDR_LEN;
26237c478bd9Sstevel@tonic-gate 	whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
26247c478bd9Sstevel@tonic-gate 	endptr = mp->b_wptr;
26257c478bd9Sstevel@tonic-gate 
26267c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
26277c478bd9Sstevel@tonic-gate 	while (whereptr < endptr) {
26287c478bd9Sstevel@tonic-gate 		/* Is there enough left for len + nexthdr? */
26297c478bd9Sstevel@tonic-gate 		if (whereptr + MIN_EHDR_LEN > endptr)
26307c478bd9Sstevel@tonic-gate 			goto done;
26317c478bd9Sstevel@tonic-gate 
26327c478bd9Sstevel@tonic-gate 		switch (nexthdr) {
2633bd670b35SErik Nordmark 		case IPPROTO_HOPOPTS: {
2634bd670b35SErik Nordmark 			/* We check for any CIPSO */
2635bd670b35SErik Nordmark 			uchar_t *secopt;
2636bd670b35SErik Nordmark 			boolean_t hbh_needed;
2637bd670b35SErik Nordmark 			uchar_t *after_secopt;
2638bd670b35SErik Nordmark 
26397c478bd9Sstevel@tonic-gate 			tmphopopts = (ip6_hbh_t *)whereptr;
26407c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (tmphopopts->ip6h_len + 1);
26417c478bd9Sstevel@tonic-gate 			if ((uchar_t *)tmphopopts +  ehdrlen > endptr)
26427c478bd9Sstevel@tonic-gate 				goto done;
26437c478bd9Sstevel@tonic-gate 			nexthdr = tmphopopts->ip6h_nxt;
2644bd670b35SErik Nordmark 
2645bd670b35SErik Nordmark 			if (!label_separate) {
2646bd670b35SErik Nordmark 				secopt = NULL;
2647bd670b35SErik Nordmark 				after_secopt = whereptr;
2648bd670b35SErik Nordmark 			} else {
2649bd670b35SErik Nordmark 				/*
2650bd670b35SErik Nordmark 				 * We have dropped packets with bad options in
2651bd670b35SErik Nordmark 				 * ip6_input. No need to check return value
2652bd670b35SErik Nordmark 				 * here.
2653bd670b35SErik Nordmark 				 */
2654bd670b35SErik Nordmark 				(void) tsol_find_secopt_v6(whereptr, ehdrlen,
2655bd670b35SErik Nordmark 				    &secopt, &after_secopt, &hbh_needed);
2656bd670b35SErik Nordmark 			}
2657bd670b35SErik Nordmark 			if (secopt != NULL && after_secopt - whereptr > 0) {
2658bd670b35SErik Nordmark 				ipp->ipp_fields |= IPPF_LABEL_V6;
2659bd670b35SErik Nordmark 				ipp->ipp_label_v6 = secopt;
2660bd670b35SErik Nordmark 				ipp->ipp_label_len_v6 = after_secopt - whereptr;
2661bd670b35SErik Nordmark 			} else {
2662bd670b35SErik Nordmark 				ipp->ipp_label_len_v6 = 0;
2663bd670b35SErik Nordmark 				after_secopt = whereptr;
2664bd670b35SErik Nordmark 				hbh_needed = B_TRUE;
2665bd670b35SErik Nordmark 			}
26667c478bd9Sstevel@tonic-gate 			/* return only 1st hbh */
2667bd670b35SErik Nordmark 			if (hbh_needed && !(ipp->ipp_fields & IPPF_HOPOPTS)) {
26687c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_HOPOPTS;
2669bd670b35SErik Nordmark 				ipp->ipp_hopopts = (ip6_hbh_t *)after_secopt;
2670bd670b35SErik Nordmark 				ipp->ipp_hopoptslen = ehdrlen -
2671bd670b35SErik Nordmark 				    ipp->ipp_label_len_v6;
26727c478bd9Sstevel@tonic-gate 			}
26737c478bd9Sstevel@tonic-gate 			break;
2674bd670b35SErik Nordmark 		}
26757c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
26767c478bd9Sstevel@tonic-gate 			tmpdstopts = (ip6_dest_t *)whereptr;
26777c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (tmpdstopts->ip6d_len + 1);
26787c478bd9Sstevel@tonic-gate 			if ((uchar_t *)tmpdstopts +  ehdrlen > endptr)
26797c478bd9Sstevel@tonic-gate 				goto done;
26807c478bd9Sstevel@tonic-gate 			nexthdr = tmpdstopts->ip6d_nxt;
26817c478bd9Sstevel@tonic-gate 			/*
26827c478bd9Sstevel@tonic-gate 			 * ipp_dstopts is set to the destination header after a
26837c478bd9Sstevel@tonic-gate 			 * routing header.
26847c478bd9Sstevel@tonic-gate 			 * Assume it is a post-rthdr destination header
26857c478bd9Sstevel@tonic-gate 			 * and adjust when we find an rthdr.
26867c478bd9Sstevel@tonic-gate 			 */
26877c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_DSTOPTS)) {
26887c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_DSTOPTS;
26897c478bd9Sstevel@tonic-gate 				ipp->ipp_dstopts = tmpdstopts;
26907c478bd9Sstevel@tonic-gate 				ipp->ipp_dstoptslen = ehdrlen;
26917c478bd9Sstevel@tonic-gate 			}
26927c478bd9Sstevel@tonic-gate 			break;
26937c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
26947c478bd9Sstevel@tonic-gate 			tmprthdr = (ip6_rthdr_t *)whereptr;
26957c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (tmprthdr->ip6r_len + 1);
26967c478bd9Sstevel@tonic-gate 			if ((uchar_t *)tmprthdr +  ehdrlen > endptr)
26977c478bd9Sstevel@tonic-gate 				goto done;
26987c478bd9Sstevel@tonic-gate 			nexthdr = tmprthdr->ip6r_nxt;
26997c478bd9Sstevel@tonic-gate 			/* return only 1st rthdr */
27007c478bd9Sstevel@tonic-gate 			if (!(ipp->ipp_fields & IPPF_RTHDR)) {
27017c478bd9Sstevel@tonic-gate 				ipp->ipp_fields |= IPPF_RTHDR;
27027c478bd9Sstevel@tonic-gate 				ipp->ipp_rthdr = tmprthdr;
27037c478bd9Sstevel@tonic-gate 				ipp->ipp_rthdrlen = ehdrlen;
27047c478bd9Sstevel@tonic-gate 			}
27057c478bd9Sstevel@tonic-gate 			/*
27067c478bd9Sstevel@tonic-gate 			 * Make any destination header we've seen be a
27077c478bd9Sstevel@tonic-gate 			 * pre-rthdr destination header.
27087c478bd9Sstevel@tonic-gate 			 */
27097c478bd9Sstevel@tonic-gate 			if (ipp->ipp_fields & IPPF_DSTOPTS) {
27107c478bd9Sstevel@tonic-gate 				ipp->ipp_fields &= ~IPPF_DSTOPTS;
2711bd670b35SErik Nordmark 				ipp->ipp_fields |= IPPF_RTHDRDSTOPTS;
2712bd670b35SErik Nordmark 				ipp->ipp_rthdrdstopts = ipp->ipp_dstopts;
27137c478bd9Sstevel@tonic-gate 				ipp->ipp_dstopts = NULL;
2714bd670b35SErik Nordmark 				ipp->ipp_rthdrdstoptslen = ipp->ipp_dstoptslen;
27157c478bd9Sstevel@tonic-gate 				ipp->ipp_dstoptslen = 0;
27167c478bd9Sstevel@tonic-gate 			}
27177c478bd9Sstevel@tonic-gate 			break;
27187c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
27197c478bd9Sstevel@tonic-gate 			tmpfraghdr = (ip6_frag_t *)whereptr;
27207c478bd9Sstevel@tonic-gate 			ehdrlen = sizeof (ip6_frag_t);
27217c478bd9Sstevel@tonic-gate 			if ((uchar_t *)tmpfraghdr + ehdrlen > endptr)
27227c478bd9Sstevel@tonic-gate 				goto done;
27237c478bd9Sstevel@tonic-gate 			nexthdr = tmpfraghdr->ip6f_nxt;
27248810c16bSdanmcd 			if (!(ipp->ipp_fields & IPPF_FRAGHDR)) {
27258810c16bSdanmcd 				ipp->ipp_fields |= IPPF_FRAGHDR;
27268810c16bSdanmcd 				ipp->ipp_fraghdr = tmpfraghdr;
27278810c16bSdanmcd 				ipp->ipp_fraghdrlen = ehdrlen;
27288810c16bSdanmcd 			}
27297c478bd9Sstevel@tonic-gate 			break;
27307c478bd9Sstevel@tonic-gate 		case IPPROTO_NONE:
27317c478bd9Sstevel@tonic-gate 		default:
27327c478bd9Sstevel@tonic-gate 			goto done;
27337c478bd9Sstevel@tonic-gate 		}
27347c478bd9Sstevel@tonic-gate 		length += ehdrlen;
27357c478bd9Sstevel@tonic-gate 		whereptr += ehdrlen;
27367c478bd9Sstevel@tonic-gate 	}
27377c478bd9Sstevel@tonic-gate done:
27387c478bd9Sstevel@tonic-gate 	if (nexthdrp != NULL)
27397c478bd9Sstevel@tonic-gate 		*nexthdrp = nexthdr;
27407c478bd9Sstevel@tonic-gate 	return (length);
27417c478bd9Sstevel@tonic-gate }
27427c478bd9Sstevel@tonic-gate 
27437c478bd9Sstevel@tonic-gate /*
27447c478bd9Sstevel@tonic-gate  * Try to determine where and what are the IPv6 header length and
27457c478bd9Sstevel@tonic-gate  * pointer to nexthdr value for the upper layer protocol (or an
27467c478bd9Sstevel@tonic-gate  * unknown next hdr).
27477c478bd9Sstevel@tonic-gate  *
27487c478bd9Sstevel@tonic-gate  * Parameters returns a pointer to the nexthdr value;
27497c478bd9Sstevel@tonic-gate  * Must handle malformed packets of various sorts.
27507c478bd9Sstevel@tonic-gate  * Function returns failure for malformed cases.
27517c478bd9Sstevel@tonic-gate  */
27527c478bd9Sstevel@tonic-gate boolean_t
ip_hdr_length_nexthdr_v6(mblk_t * mp,ip6_t * ip6h,uint16_t * hdr_length_ptr,uint8_t ** nexthdrpp)27537c478bd9Sstevel@tonic-gate ip_hdr_length_nexthdr_v6(mblk_t *mp, ip6_t *ip6h, uint16_t *hdr_length_ptr,
27547c478bd9Sstevel@tonic-gate     uint8_t **nexthdrpp)
27557c478bd9Sstevel@tonic-gate {
27567c478bd9Sstevel@tonic-gate 	uint16_t length;
27577c478bd9Sstevel@tonic-gate 	uint_t	ehdrlen;
27587c478bd9Sstevel@tonic-gate 	uint8_t	*nexthdrp;
27597c478bd9Sstevel@tonic-gate 	uint8_t *whereptr;
27607c478bd9Sstevel@tonic-gate 	uint8_t *endptr;
27617c478bd9Sstevel@tonic-gate 	ip6_dest_t *desthdr;
27627c478bd9Sstevel@tonic-gate 	ip6_rthdr_t *rthdr;
27637c478bd9Sstevel@tonic-gate 	ip6_frag_t *fraghdr;
27647c478bd9Sstevel@tonic-gate 
2765bd670b35SErik Nordmark 	ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
27667c478bd9Sstevel@tonic-gate 	length = IPV6_HDR_LEN;
27677c478bd9Sstevel@tonic-gate 	whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
27687c478bd9Sstevel@tonic-gate 	endptr = mp->b_wptr;
27697c478bd9Sstevel@tonic-gate 
27707c478bd9Sstevel@tonic-gate 	nexthdrp = &ip6h->ip6_nxt;
27717c478bd9Sstevel@tonic-gate 	while (whereptr < endptr) {
27727c478bd9Sstevel@tonic-gate 		/* Is there enough left for len + nexthdr? */
27737c478bd9Sstevel@tonic-gate 		if (whereptr + MIN_EHDR_LEN > endptr)
27747c478bd9Sstevel@tonic-gate 			break;
27757c478bd9Sstevel@tonic-gate 
27767c478bd9Sstevel@tonic-gate 		switch (*nexthdrp) {
27777c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
27787c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
27797c478bd9Sstevel@tonic-gate 			/* Assumes the headers are identical for hbh and dst */
27807c478bd9Sstevel@tonic-gate 			desthdr = (ip6_dest_t *)whereptr;
27817c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (desthdr->ip6d_len + 1);
27827c478bd9Sstevel@tonic-gate 			if ((uchar_t *)desthdr +  ehdrlen > endptr)
27837c478bd9Sstevel@tonic-gate 				return (B_FALSE);
27847c478bd9Sstevel@tonic-gate 			nexthdrp = &desthdr->ip6d_nxt;
27857c478bd9Sstevel@tonic-gate 			break;
27867c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
27877c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr_t *)whereptr;
27887c478bd9Sstevel@tonic-gate 			ehdrlen =  8 * (rthdr->ip6r_len + 1);
27897c478bd9Sstevel@tonic-gate 			if ((uchar_t *)rthdr +  ehdrlen > endptr)
27907c478bd9Sstevel@tonic-gate 				return (B_FALSE);
27917c478bd9Sstevel@tonic-gate 			nexthdrp = &rthdr->ip6r_nxt;
27927c478bd9Sstevel@tonic-gate 			break;
27937c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
27947c478bd9Sstevel@tonic-gate 			fraghdr = (ip6_frag_t *)whereptr;
27957c478bd9Sstevel@tonic-gate 			ehdrlen = sizeof (ip6_frag_t);
27967c478bd9Sstevel@tonic-gate 			if ((uchar_t *)&fraghdr[1] > endptr)
27977c478bd9Sstevel@tonic-gate 				return (B_FALSE);
27987c478bd9Sstevel@tonic-gate 			nexthdrp = &fraghdr->ip6f_nxt;
27997c478bd9Sstevel@tonic-gate 			break;
28007c478bd9Sstevel@tonic-gate 		case IPPROTO_NONE:
28017c478bd9Sstevel@tonic-gate 			/* No next header means we're finished */
28027c478bd9Sstevel@tonic-gate 		default:
28037c478bd9Sstevel@tonic-gate 			*hdr_length_ptr = length;
28047c478bd9Sstevel@tonic-gate 			*nexthdrpp = nexthdrp;
28057c478bd9Sstevel@tonic-gate 			return (B_TRUE);
28067c478bd9Sstevel@tonic-gate 		}
28077c478bd9Sstevel@tonic-gate 		length += ehdrlen;
28087c478bd9Sstevel@tonic-gate 		whereptr += ehdrlen;
28097c478bd9Sstevel@tonic-gate 		*hdr_length_ptr = length;
28107c478bd9Sstevel@tonic-gate 		*nexthdrpp = nexthdrp;
28117c478bd9Sstevel@tonic-gate 	}
28127c478bd9Sstevel@tonic-gate 	switch (*nexthdrp) {
28137c478bd9Sstevel@tonic-gate 	case IPPROTO_HOPOPTS:
28147c478bd9Sstevel@tonic-gate 	case IPPROTO_DSTOPTS:
28157c478bd9Sstevel@tonic-gate 	case IPPROTO_ROUTING:
28167c478bd9Sstevel@tonic-gate 	case IPPROTO_FRAGMENT:
28177c478bd9Sstevel@tonic-gate 		/*
28187c478bd9Sstevel@tonic-gate 		 * If any know extension headers are still to be processed,
28197c478bd9Sstevel@tonic-gate 		 * the packet's malformed (or at least all the IP header(s) are
28207c478bd9Sstevel@tonic-gate 		 * not in the same mblk - and that should never happen.
28217c478bd9Sstevel@tonic-gate 		 */
28227c478bd9Sstevel@tonic-gate 		return (B_FALSE);
28237c478bd9Sstevel@tonic-gate 
28247c478bd9Sstevel@tonic-gate 	default:
28257c478bd9Sstevel@tonic-gate 		/*
28267c478bd9Sstevel@tonic-gate 		 * If we get here, we know that all of the IP headers were in
28277c478bd9Sstevel@tonic-gate 		 * the same mblk, even if the ULP header is in the next mblk.
28287c478bd9Sstevel@tonic-gate 		 */
28297c478bd9Sstevel@tonic-gate 		*hdr_length_ptr = length;
28307c478bd9Sstevel@tonic-gate 		*nexthdrpp = nexthdrp;
28317c478bd9Sstevel@tonic-gate 		return (B_TRUE);
28327c478bd9Sstevel@tonic-gate 	}
28337c478bd9Sstevel@tonic-gate }
28347c478bd9Sstevel@tonic-gate 
28357c478bd9Sstevel@tonic-gate /*
28367c478bd9Sstevel@tonic-gate  * Return the length of the IPv6 related headers (including extension headers)
28377c478bd9Sstevel@tonic-gate  * Returns a length even if the packet is malformed.
28387c478bd9Sstevel@tonic-gate  */
28397c478bd9Sstevel@tonic-gate int
ip_hdr_length_v6(mblk_t * mp,ip6_t * ip6h)28407c478bd9Sstevel@tonic-gate ip_hdr_length_v6(mblk_t *mp, ip6_t *ip6h)
28417c478bd9Sstevel@tonic-gate {
28427c478bd9Sstevel@tonic-gate 	uint16_t hdr_len;
28437c478bd9Sstevel@tonic-gate 	uint8_t	*nexthdrp;
28447c478bd9Sstevel@tonic-gate 
28457c478bd9Sstevel@tonic-gate 	(void) ip_hdr_length_nexthdr_v6(mp, ip6h, &hdr_len, &nexthdrp);
28467c478bd9Sstevel@tonic-gate 	return (hdr_len);
28477c478bd9Sstevel@tonic-gate }
28487c478bd9Sstevel@tonic-gate 
28497c478bd9Sstevel@tonic-gate /*
28507c478bd9Sstevel@tonic-gate  * Parse and process any hop-by-hop or destination options.
28517c478bd9Sstevel@tonic-gate  *
28527c478bd9Sstevel@tonic-gate  * Assumes that q is an ill read queue so that ICMP errors for link-local
28537c478bd9Sstevel@tonic-gate  * destinations are sent out the correct interface.
28547c478bd9Sstevel@tonic-gate  *
28557c478bd9Sstevel@tonic-gate  * Returns -1 if there was an error and mp has been consumed.
28567c478bd9Sstevel@tonic-gate  * Returns 0 if no special action is needed.
28577c478bd9Sstevel@tonic-gate  * Returns 1 if the packet contained a router alert option for this node
28587c478bd9Sstevel@tonic-gate  * which is verified to be "interesting/known" for our implementation.
28597c478bd9Sstevel@tonic-gate  *
28607c478bd9Sstevel@tonic-gate  * XXX Note: In future as more hbh or dest options are defined,
28617c478bd9Sstevel@tonic-gate  * it may be better to have different routines for hbh and dest
28627c478bd9Sstevel@tonic-gate  * options as opt_type fields other than IP6OPT_PAD1 and IP6OPT_PADN
28637c478bd9Sstevel@tonic-gate  * may have same value in different namespaces. Or is it same namespace ??
28647c478bd9Sstevel@tonic-gate  * Current code checks for each opt_type (other than pads) if it is in
28657c478bd9Sstevel@tonic-gate  * the expected  nexthdr (hbh or dest)
28667c478bd9Sstevel@tonic-gate  */
2867bd670b35SErik Nordmark int
ip_process_options_v6(mblk_t * mp,ip6_t * ip6h,uint8_t * optptr,uint_t optlen,uint8_t hdr_type,ip_recv_attr_t * ira)2868bd670b35SErik Nordmark ip_process_options_v6(mblk_t *mp, ip6_t *ip6h,
2869bd670b35SErik Nordmark     uint8_t *optptr, uint_t optlen, uint8_t hdr_type, ip_recv_attr_t *ira)
28707c478bd9Sstevel@tonic-gate {
28717c478bd9Sstevel@tonic-gate 	uint8_t opt_type;
28727c478bd9Sstevel@tonic-gate 	uint_t optused;
28737c478bd9Sstevel@tonic-gate 	int ret = 0;
287445916cd2Sjpk 	const char *errtype;
2875bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
2876bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
28777c478bd9Sstevel@tonic-gate 
28787c478bd9Sstevel@tonic-gate 	while (optlen != 0) {
28797c478bd9Sstevel@tonic-gate 		opt_type = *optptr;
28807c478bd9Sstevel@tonic-gate 		if (opt_type == IP6OPT_PAD1) {
28817c478bd9Sstevel@tonic-gate 			optused = 1;
28827c478bd9Sstevel@tonic-gate 		} else {
28837c478bd9Sstevel@tonic-gate 			if (optlen < 2)
28847c478bd9Sstevel@tonic-gate 				goto bad_opt;
288545916cd2Sjpk 			errtype = "malformed";
288645916cd2Sjpk 			if (opt_type == ip6opt_ls) {
288745916cd2Sjpk 				optused = 2 + optptr[1];
288845916cd2Sjpk 				if (optused > optlen)
288945916cd2Sjpk 					goto bad_opt;
289045916cd2Sjpk 			} else switch (opt_type) {
28917c478bd9Sstevel@tonic-gate 			case IP6OPT_PADN:
28927c478bd9Sstevel@tonic-gate 				/*
28937c478bd9Sstevel@tonic-gate 				 * Note:We don't verify that (N-2) pad octets
28947c478bd9Sstevel@tonic-gate 				 * are zero as required by spec. Adhere to
28957c478bd9Sstevel@tonic-gate 				 * "be liberal in what you accept..." part of
28967c478bd9Sstevel@tonic-gate 				 * implementation philosophy (RFC791,RFC1122)
28977c478bd9Sstevel@tonic-gate 				 */
28987c478bd9Sstevel@tonic-gate 				optused = 2 + optptr[1];
28997c478bd9Sstevel@tonic-gate 				if (optused > optlen)
29007c478bd9Sstevel@tonic-gate 					goto bad_opt;
29017c478bd9Sstevel@tonic-gate 				break;
29027c478bd9Sstevel@tonic-gate 
29037c478bd9Sstevel@tonic-gate 			case IP6OPT_JUMBO:
29047c478bd9Sstevel@tonic-gate 				if (hdr_type != IPPROTO_HOPOPTS)
29057c478bd9Sstevel@tonic-gate 					goto opt_error;
29067c478bd9Sstevel@tonic-gate 				goto opt_error; /* XXX Not implemented! */
29077c478bd9Sstevel@tonic-gate 
29087c478bd9Sstevel@tonic-gate 			case IP6OPT_ROUTER_ALERT: {
29097c478bd9Sstevel@tonic-gate 				struct ip6_opt_router *or;
29107c478bd9Sstevel@tonic-gate 
29117c478bd9Sstevel@tonic-gate 				if (hdr_type != IPPROTO_HOPOPTS)
29127c478bd9Sstevel@tonic-gate 					goto opt_error;
29137c478bd9Sstevel@tonic-gate 				optused = 2 + optptr[1];
29147c478bd9Sstevel@tonic-gate 				if (optused > optlen)
29157c478bd9Sstevel@tonic-gate 					goto bad_opt;
29167c478bd9Sstevel@tonic-gate 				or = (struct ip6_opt_router *)optptr;
29177c478bd9Sstevel@tonic-gate 				/* Check total length and alignment */
29187c478bd9Sstevel@tonic-gate 				if (optused != sizeof (*or) ||
29197c478bd9Sstevel@tonic-gate 				    ((uintptr_t)or->ip6or_value & 0x1) != 0)
29207c478bd9Sstevel@tonic-gate 					goto opt_error;
29217c478bd9Sstevel@tonic-gate 				/* Check value */
29227c478bd9Sstevel@tonic-gate 				switch (*((uint16_t *)or->ip6or_value)) {
29237c478bd9Sstevel@tonic-gate 				case IP6_ALERT_MLD:
29247c478bd9Sstevel@tonic-gate 				case IP6_ALERT_RSVP:
29257c478bd9Sstevel@tonic-gate 					ret = 1;
29267c478bd9Sstevel@tonic-gate 				}
29277c478bd9Sstevel@tonic-gate 				break;
29287c478bd9Sstevel@tonic-gate 			}
29297c478bd9Sstevel@tonic-gate 			case IP6OPT_HOME_ADDRESS: {
29307c478bd9Sstevel@tonic-gate 				/*
29317c478bd9Sstevel@tonic-gate 				 * Minimal support for the home address option
29327c478bd9Sstevel@tonic-gate 				 * (which is required by all IPv6 nodes).
29337c478bd9Sstevel@tonic-gate 				 * Implement by just swapping the home address
29347c478bd9Sstevel@tonic-gate 				 * and source address.
29357c478bd9Sstevel@tonic-gate 				 * XXX Note: this has IPsec implications since
29367c478bd9Sstevel@tonic-gate 				 * AH needs to take this into account.
29377c478bd9Sstevel@tonic-gate 				 * Also, when IPsec is used we need to ensure
29387c478bd9Sstevel@tonic-gate 				 * that this is only processed once
29397c478bd9Sstevel@tonic-gate 				 * in the received packet (to avoid swapping
29407c478bd9Sstevel@tonic-gate 				 * back and forth).
29417c478bd9Sstevel@tonic-gate 				 * NOTE:This option processing is considered
29427c478bd9Sstevel@tonic-gate 				 * to be unsafe and prone to a denial of
29437c478bd9Sstevel@tonic-gate 				 * service attack.
29447c478bd9Sstevel@tonic-gate 				 * The current processing is not safe even with
29457c478bd9Sstevel@tonic-gate 				 * IPsec secured IP packets. Since the home
29467c478bd9Sstevel@tonic-gate 				 * address option processing requirement still
29477c478bd9Sstevel@tonic-gate 				 * is in the IETF draft and in the process of
29487c478bd9Sstevel@tonic-gate 				 * being redefined for its usage, it has been
29497c478bd9Sstevel@tonic-gate 				 * decided to turn off the option by default.
29507c478bd9Sstevel@tonic-gate 				 * If this section of code needs to be executed,
29517c478bd9Sstevel@tonic-gate 				 * ndd variable ip6_ignore_home_address_opt
29527c478bd9Sstevel@tonic-gate 				 * should be set to 0 at the user's own risk.
29537c478bd9Sstevel@tonic-gate 				 */
29547c478bd9Sstevel@tonic-gate 				struct ip6_opt_home_address *oh;
29557c478bd9Sstevel@tonic-gate 				in6_addr_t tmp;
29567c478bd9Sstevel@tonic-gate 
2957f4b3ec61Sdh155122 				if (ipst->ips_ipv6_ignore_home_address_opt)
29587c478bd9Sstevel@tonic-gate 					goto opt_error;
29597c478bd9Sstevel@tonic-gate 
29607c478bd9Sstevel@tonic-gate 				if (hdr_type != IPPROTO_DSTOPTS)
29617c478bd9Sstevel@tonic-gate 					goto opt_error;
29627c478bd9Sstevel@tonic-gate 				optused = 2 + optptr[1];
29637c478bd9Sstevel@tonic-gate 				if (optused > optlen)
29647c478bd9Sstevel@tonic-gate 					goto bad_opt;
29657c478bd9Sstevel@tonic-gate 
29667c478bd9Sstevel@tonic-gate 				/*
29677c478bd9Sstevel@tonic-gate 				 * We did this dest. opt the first time
29687c478bd9Sstevel@tonic-gate 				 * around (i.e. before AH processing).
29697c478bd9Sstevel@tonic-gate 				 * If we've done AH... stop now.
29707c478bd9Sstevel@tonic-gate 				 */
2971bd670b35SErik Nordmark 				if ((ira->ira_flags & IRAF_IPSEC_SECURE) &&
2972bd670b35SErik Nordmark 				    ira->ira_ipsec_ah_sa != NULL)
29737c478bd9Sstevel@tonic-gate 					break;
29747c478bd9Sstevel@tonic-gate 
29757c478bd9Sstevel@tonic-gate 				oh = (struct ip6_opt_home_address *)optptr;
29767c478bd9Sstevel@tonic-gate 				/* Check total length and alignment */
29777c478bd9Sstevel@tonic-gate 				if (optused < sizeof (*oh) ||
29787c478bd9Sstevel@tonic-gate 				    ((uintptr_t)oh->ip6oh_addr & 0x7) != 0)
29797c478bd9Sstevel@tonic-gate 					goto opt_error;
29807c478bd9Sstevel@tonic-gate 				/* Swap ip6_src and the home address */
29817c478bd9Sstevel@tonic-gate 				tmp = ip6h->ip6_src;
29827c478bd9Sstevel@tonic-gate 				/* XXX Note: only 8 byte alignment option */
29837c478bd9Sstevel@tonic-gate 				ip6h->ip6_src = *(in6_addr_t *)oh->ip6oh_addr;
29847c478bd9Sstevel@tonic-gate 				*(in6_addr_t *)oh->ip6oh_addr = tmp;
29857c478bd9Sstevel@tonic-gate 				break;
29867c478bd9Sstevel@tonic-gate 			}
29877c478bd9Sstevel@tonic-gate 
29887c478bd9Sstevel@tonic-gate 			case IP6OPT_TUNNEL_LIMIT:
29897c478bd9Sstevel@tonic-gate 				if (hdr_type != IPPROTO_DSTOPTS) {
29907c478bd9Sstevel@tonic-gate 					goto opt_error;
29917c478bd9Sstevel@tonic-gate 				}
29927c478bd9Sstevel@tonic-gate 				optused = 2 + optptr[1];
29937c478bd9Sstevel@tonic-gate 				if (optused > optlen) {
29947c478bd9Sstevel@tonic-gate 					goto bad_opt;
29957c478bd9Sstevel@tonic-gate 				}
29967c478bd9Sstevel@tonic-gate 				if (optused != 3) {
29977c478bd9Sstevel@tonic-gate 					goto opt_error;
29987c478bd9Sstevel@tonic-gate 				}
29997c478bd9Sstevel@tonic-gate 				break;
30007c478bd9Sstevel@tonic-gate 
30017c478bd9Sstevel@tonic-gate 			default:
300245916cd2Sjpk 				errtype = "unknown";
300345916cd2Sjpk 				/* FALLTHROUGH */
30047c478bd9Sstevel@tonic-gate 			opt_error:
30055597b60aSnordmark 				/* Determine which zone should send error */
30067c478bd9Sstevel@tonic-gate 				switch (IP6OPT_TYPE(opt_type)) {
30077c478bd9Sstevel@tonic-gate 				case IP6OPT_TYPE_SKIP:
30087c478bd9Sstevel@tonic-gate 					optused = 2 + optptr[1];
30097c478bd9Sstevel@tonic-gate 					if (optused > optlen)
30107c478bd9Sstevel@tonic-gate 						goto bad_opt;
301145916cd2Sjpk 					ip1dbg(("ip_process_options_v6: %s "
301245916cd2Sjpk 					    "opt 0x%x skipped\n",
301345916cd2Sjpk 					    errtype, opt_type));
30147c478bd9Sstevel@tonic-gate 					break;
30157c478bd9Sstevel@tonic-gate 				case IP6OPT_TYPE_DISCARD:
301645916cd2Sjpk 					ip1dbg(("ip_process_options_v6: %s "
301745916cd2Sjpk 					    "opt 0x%x; packet dropped\n",
301845916cd2Sjpk 					    errtype, opt_type));
3019bd670b35SErik Nordmark 					BUMP_MIB(ill->ill_ip_mib,
3020bd670b35SErik Nordmark 					    ipIfStatsInHdrErrors);
3021bd670b35SErik Nordmark 					ip_drop_input("ipIfStatsInHdrErrors",
3022bd670b35SErik Nordmark 					    mp, ill);
3023bd670b35SErik Nordmark 					freemsg(mp);
30247c478bd9Sstevel@tonic-gate 					return (-1);
30257c478bd9Sstevel@tonic-gate 				case IP6OPT_TYPE_ICMP:
3026bd670b35SErik Nordmark 					BUMP_MIB(ill->ill_ip_mib,
3027bd670b35SErik Nordmark 					    ipIfStatsInHdrErrors);
3028bd670b35SErik Nordmark 					ip_drop_input("ipIfStatsInHdrErrors",
3029bd670b35SErik Nordmark 					    mp, ill);
3030bd670b35SErik Nordmark 					icmp_param_problem_v6(mp,
30317c478bd9Sstevel@tonic-gate 					    ICMP6_PARAMPROB_OPTION,
30327c478bd9Sstevel@tonic-gate 					    (uint32_t)(optptr -
30337c478bd9Sstevel@tonic-gate 					    (uint8_t *)ip6h),
3034bd670b35SErik Nordmark 					    B_FALSE, ira);
30357c478bd9Sstevel@tonic-gate 					return (-1);
30367c478bd9Sstevel@tonic-gate 				case IP6OPT_TYPE_FORCEICMP:
3037bd670b35SErik Nordmark 					BUMP_MIB(ill->ill_ip_mib,
3038bd670b35SErik Nordmark 					    ipIfStatsInHdrErrors);
3039bd670b35SErik Nordmark 					ip_drop_input("ipIfStatsInHdrErrors",
3040bd670b35SErik Nordmark 					    mp, ill);
3041bd670b35SErik Nordmark 					icmp_param_problem_v6(mp,
30427c478bd9Sstevel@tonic-gate 					    ICMP6_PARAMPROB_OPTION,
30437c478bd9Sstevel@tonic-gate 					    (uint32_t)(optptr -
30447c478bd9Sstevel@tonic-gate 					    (uint8_t *)ip6h),
3045bd670b35SErik Nordmark 					    B_TRUE, ira);
30467c478bd9Sstevel@tonic-gate 					return (-1);
304745916cd2Sjpk 				default:
304845916cd2Sjpk 					ASSERT(0);
30497c478bd9Sstevel@tonic-gate 				}
30507c478bd9Sstevel@tonic-gate 			}
30517c478bd9Sstevel@tonic-gate 		}
30527c478bd9Sstevel@tonic-gate 		optlen -= optused;
30537c478bd9Sstevel@tonic-gate 		optptr += optused;
30547c478bd9Sstevel@tonic-gate 	}
30557c478bd9Sstevel@tonic-gate 	return (ret);
30567c478bd9Sstevel@tonic-gate 
30577c478bd9Sstevel@tonic-gate bad_opt:
30585597b60aSnordmark 	/* Determine which zone should send error */
3059bd670b35SErik Nordmark 	ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
3060bd670b35SErik Nordmark 	icmp_param_problem_v6(mp, ICMP6_PARAMPROB_OPTION,
30617c478bd9Sstevel@tonic-gate 	    (uint32_t)(optptr - (uint8_t *)ip6h),
3062bd670b35SErik Nordmark 	    B_FALSE, ira);
30637c478bd9Sstevel@tonic-gate 	return (-1);
30647c478bd9Sstevel@tonic-gate }
30657c478bd9Sstevel@tonic-gate 
30667c478bd9Sstevel@tonic-gate /*
30677c478bd9Sstevel@tonic-gate  * Process a routing header that is not yet empty.
3068f0376ac1SBrian Utterback  * Because of RFC 5095, we now reject all route headers.
30697c478bd9Sstevel@tonic-gate  */
3070bd670b35SErik Nordmark void
ip_process_rthdr(mblk_t * mp,ip6_t * ip6h,ip6_rthdr_t * rth,ip_recv_attr_t * ira)3071bd670b35SErik Nordmark ip_process_rthdr(mblk_t *mp, ip6_t *ip6h, ip6_rthdr_t *rth,
3072bd670b35SErik Nordmark     ip_recv_attr_t *ira)
30737c478bd9Sstevel@tonic-gate {
3074bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
3075f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
30767c478bd9Sstevel@tonic-gate 
30777c478bd9Sstevel@tonic-gate 	ASSERT(rth->ip6r_segleft != 0);
30787c478bd9Sstevel@tonic-gate 
3079f4b3ec61Sdh155122 	if (!ipst->ips_ipv6_forward_src_routed) {
30807c478bd9Sstevel@tonic-gate 		/* XXX Check for source routed out same interface? */
30813173664eSapersson 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsForwProhibits);
30823173664eSapersson 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInAddrErrors);
3083bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInAddrErrors", mp, ill);
30847c478bd9Sstevel@tonic-gate 		freemsg(mp);
30857c478bd9Sstevel@tonic-gate 		return;
30867c478bd9Sstevel@tonic-gate 	}
3087bd670b35SErik Nordmark 
3088bd670b35SErik Nordmark 	ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
3089bd670b35SErik Nordmark 	icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
3090bd670b35SErik Nordmark 	    (uint32_t)((uchar_t *)&rth->ip6r_type - (uchar_t *)ip6h),
3091bd670b35SErik Nordmark 	    B_FALSE, ira);
30927c478bd9Sstevel@tonic-gate }
30937c478bd9Sstevel@tonic-gate 
30947c478bd9Sstevel@tonic-gate /*
30957c478bd9Sstevel@tonic-gate  * Read side put procedure for IPv6 module.
30967c478bd9Sstevel@tonic-gate  */
3097381a2a9aSdr146992 void
ip_rput_v6(queue_t * q,mblk_t * mp)30987c478bd9Sstevel@tonic-gate ip_rput_v6(queue_t *q, mblk_t *mp)
30997c478bd9Sstevel@tonic-gate {
31007c478bd9Sstevel@tonic-gate 	ill_t		*ill;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 	ill = (ill_t *)q->q_ptr;
3103bd670b35SErik Nordmark 	if (ill->ill_state_flags & (ILL_CONDEMNED | ILL_LL_SUBNET_PENDING)) {
31047c478bd9Sstevel@tonic-gate 		union DL_primitives *dl;
31057c478bd9Sstevel@tonic-gate 
31067c478bd9Sstevel@tonic-gate 		dl = (union DL_primitives *)mp->b_rptr;
31077c478bd9Sstevel@tonic-gate 		/*
31087c478bd9Sstevel@tonic-gate 		 * Things are opening or closing - only accept DLPI
31097c478bd9Sstevel@tonic-gate 		 * ack messages. If the stream is closing and ip_wsrv
31107c478bd9Sstevel@tonic-gate 		 * has completed, ip_close is out of the qwait, but has
31117c478bd9Sstevel@tonic-gate 		 * not yet completed qprocsoff. Don't proceed any further
31127c478bd9Sstevel@tonic-gate 		 * because the ill has been cleaned up and things hanging
31137c478bd9Sstevel@tonic-gate 		 * off the ill have been freed.
31147c478bd9Sstevel@tonic-gate 		 */
31157c478bd9Sstevel@tonic-gate 		if ((mp->b_datap->db_type != M_PCPROTO) ||
31167c478bd9Sstevel@tonic-gate 		    (dl->dl_primitive == DL_UNITDATA_IND)) {
3117ff550d0eSmasputra 			inet_freemsg(mp);
31187c478bd9Sstevel@tonic-gate 			return;
31197c478bd9Sstevel@tonic-gate 		}
31207c478bd9Sstevel@tonic-gate 	}
3121bd670b35SErik Nordmark 	if (DB_TYPE(mp) == M_DATA) {
3122bd670b35SErik Nordmark 		struct mac_header_info_s mhi;
31237c478bd9Sstevel@tonic-gate 
3124bd670b35SErik Nordmark 		ip_mdata_to_mhi(ill, mp, &mhi);
3125bd670b35SErik Nordmark 		ip_input_v6(ill, NULL, mp, &mhi);
31267c478bd9Sstevel@tonic-gate 	} else {
3127bd670b35SErik Nordmark 		ip_rput_notdata(ill, mp);
31287c478bd9Sstevel@tonic-gate 	}
31297c478bd9Sstevel@tonic-gate }
31307c478bd9Sstevel@tonic-gate 
31317c478bd9Sstevel@tonic-gate /*
31327c478bd9Sstevel@tonic-gate  * Walk through the IPv6 packet in mp and see if there's an AH header
31337c478bd9Sstevel@tonic-gate  * in it.  See if the AH header needs to get done before other headers in
31347c478bd9Sstevel@tonic-gate  * the packet.  (Worker function for ipsec_early_ah_v6().)
31357c478bd9Sstevel@tonic-gate  */
31367c478bd9Sstevel@tonic-gate #define	IPSEC_HDR_DONT_PROCESS	0
31377c478bd9Sstevel@tonic-gate #define	IPSEC_HDR_PROCESS	1
31385002eb58SBill Sommerfeld #define	IPSEC_MEMORY_ERROR	2 /* or malformed packet */
31397c478bd9Sstevel@tonic-gate static int
ipsec_needs_processing_v6(mblk_t * mp,uint8_t * nexthdr)31407c478bd9Sstevel@tonic-gate ipsec_needs_processing_v6(mblk_t *mp, uint8_t *nexthdr)
31417c478bd9Sstevel@tonic-gate {
31427c478bd9Sstevel@tonic-gate 	uint_t	length;
31437c478bd9Sstevel@tonic-gate 	uint_t	ehdrlen;
31447c478bd9Sstevel@tonic-gate 	uint8_t *whereptr;
31457c478bd9Sstevel@tonic-gate 	uint8_t *endptr;
31467c478bd9Sstevel@tonic-gate 	uint8_t *nexthdrp;
31477c478bd9Sstevel@tonic-gate 	ip6_dest_t *desthdr;
31487c478bd9Sstevel@tonic-gate 	ip6_rthdr_t *rthdr;
31497c478bd9Sstevel@tonic-gate 	ip6_t	*ip6h;
31507c478bd9Sstevel@tonic-gate 
31517c478bd9Sstevel@tonic-gate 	/*
31527c478bd9Sstevel@tonic-gate 	 * For now just pullup everything.  In general, the less pullups,
31537c478bd9Sstevel@tonic-gate 	 * the better, but there's so much squirrelling through anyway,
31547c478bd9Sstevel@tonic-gate 	 * it's just easier this way.
31557c478bd9Sstevel@tonic-gate 	 */
31567c478bd9Sstevel@tonic-gate 	if (!pullupmsg(mp, -1)) {
31577c478bd9Sstevel@tonic-gate 		return (IPSEC_MEMORY_ERROR);
31587c478bd9Sstevel@tonic-gate 	}
31597c478bd9Sstevel@tonic-gate 
31607c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
31617c478bd9Sstevel@tonic-gate 	length = IPV6_HDR_LEN;
31627c478bd9Sstevel@tonic-gate 	whereptr = ((uint8_t *)&ip6h[1]); /* point to next hdr */
31637c478bd9Sstevel@tonic-gate 	endptr = mp->b_wptr;
31647c478bd9Sstevel@tonic-gate 
31657c478bd9Sstevel@tonic-gate 	/*
31667c478bd9Sstevel@tonic-gate 	 * We can't just use the argument nexthdr in the place
31677c478bd9Sstevel@tonic-gate 	 * of nexthdrp becaue we don't dereference nexthdrp
31687c478bd9Sstevel@tonic-gate 	 * till we confirm whether it is a valid address.
31697c478bd9Sstevel@tonic-gate 	 */
31707c478bd9Sstevel@tonic-gate 	nexthdrp = &ip6h->ip6_nxt;
31717c478bd9Sstevel@tonic-gate 	while (whereptr < endptr) {
31727c478bd9Sstevel@tonic-gate 		/* Is there enough left for len + nexthdr? */
31737c478bd9Sstevel@tonic-gate 		if (whereptr + MIN_EHDR_LEN > endptr)
31747c478bd9Sstevel@tonic-gate 			return (IPSEC_MEMORY_ERROR);
31757c478bd9Sstevel@tonic-gate 
31767c478bd9Sstevel@tonic-gate 		switch (*nexthdrp) {
31777c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
31787c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
31797c478bd9Sstevel@tonic-gate 			/* Assumes the headers are identical for hbh and dst */
31807c478bd9Sstevel@tonic-gate 			desthdr = (ip6_dest_t *)whereptr;
31817c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (desthdr->ip6d_len + 1);
31827c478bd9Sstevel@tonic-gate 			if ((uchar_t *)desthdr +  ehdrlen > endptr)
31837c478bd9Sstevel@tonic-gate 				return (IPSEC_MEMORY_ERROR);
31847c478bd9Sstevel@tonic-gate 			/*
31855c0b7edeSseb 			 * Return DONT_PROCESS because the destination
31865c0b7edeSseb 			 * options header may be for each hop in a
31875c0b7edeSseb 			 * routing-header, and we only want AH if we're
31885c0b7edeSseb 			 * finished with routing headers.
31897c478bd9Sstevel@tonic-gate 			 */
31907c478bd9Sstevel@tonic-gate 			if (*nexthdrp == IPPROTO_DSTOPTS)
31917c478bd9Sstevel@tonic-gate 				return (IPSEC_HDR_DONT_PROCESS);
31927c478bd9Sstevel@tonic-gate 			nexthdrp = &desthdr->ip6d_nxt;
31937c478bd9Sstevel@tonic-gate 			break;
31947c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
31957c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr_t *)whereptr;
31967c478bd9Sstevel@tonic-gate 
31977c478bd9Sstevel@tonic-gate 			/*
31987c478bd9Sstevel@tonic-gate 			 * If there's more hops left on the routing header,
31997c478bd9Sstevel@tonic-gate 			 * return now with DON'T PROCESS.
32007c478bd9Sstevel@tonic-gate 			 */
32017c478bd9Sstevel@tonic-gate 			if (rthdr->ip6r_segleft > 0)
32027c478bd9Sstevel@tonic-gate 				return (IPSEC_HDR_DONT_PROCESS);
32037c478bd9Sstevel@tonic-gate 
32047c478bd9Sstevel@tonic-gate 			ehdrlen =  8 * (rthdr->ip6r_len + 1);
32057c478bd9Sstevel@tonic-gate 			if ((uchar_t *)rthdr +  ehdrlen > endptr)
32067c478bd9Sstevel@tonic-gate 				return (IPSEC_MEMORY_ERROR);
32077c478bd9Sstevel@tonic-gate 			nexthdrp = &rthdr->ip6r_nxt;
32087c478bd9Sstevel@tonic-gate 			break;
32097c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
32107c478bd9Sstevel@tonic-gate 			/* Wait for reassembly */
32117c478bd9Sstevel@tonic-gate 			return (IPSEC_HDR_DONT_PROCESS);
32127c478bd9Sstevel@tonic-gate 		case IPPROTO_AH:
32137c478bd9Sstevel@tonic-gate 			*nexthdr = IPPROTO_AH;
32147c478bd9Sstevel@tonic-gate 			return (IPSEC_HDR_PROCESS);
32157c478bd9Sstevel@tonic-gate 		case IPPROTO_NONE:
32167c478bd9Sstevel@tonic-gate 			/* No next header means we're finished */
32177c478bd9Sstevel@tonic-gate 		default:
32187c478bd9Sstevel@tonic-gate 			return (IPSEC_HDR_DONT_PROCESS);
32197c478bd9Sstevel@tonic-gate 		}
32207c478bd9Sstevel@tonic-gate 		length += ehdrlen;
32217c478bd9Sstevel@tonic-gate 		whereptr += ehdrlen;
32227c478bd9Sstevel@tonic-gate 	}
32235002eb58SBill Sommerfeld 	/*
32245002eb58SBill Sommerfeld 	 * Malformed/truncated packet.
32255002eb58SBill Sommerfeld 	 */
32265002eb58SBill Sommerfeld 	return (IPSEC_MEMORY_ERROR);
32277c478bd9Sstevel@tonic-gate }
32287c478bd9Sstevel@tonic-gate 
32297c478bd9Sstevel@tonic-gate /*
3230bd670b35SErik Nordmark  * Path for AH if options are present.
3231bd670b35SErik Nordmark  * Returns NULL if the mblk was consumed.
32327c478bd9Sstevel@tonic-gate  *
32337c478bd9Sstevel@tonic-gate  * Sometimes AH needs to be done before other IPv6 headers for security
32347c478bd9Sstevel@tonic-gate  * reasons.  This function (and its ipsec_needs_processing_v6() above)
32357c478bd9Sstevel@tonic-gate  * indicates if that is so, and fans out to the appropriate IPsec protocol
32367c478bd9Sstevel@tonic-gate  * for the datagram passed in.
32377c478bd9Sstevel@tonic-gate  */
3238bd670b35SErik Nordmark mblk_t *
ipsec_early_ah_v6(mblk_t * mp,ip_recv_attr_t * ira)3239bd670b35SErik Nordmark ipsec_early_ah_v6(mblk_t *mp, ip_recv_attr_t *ira)
32407c478bd9Sstevel@tonic-gate {
32417c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
32427c478bd9Sstevel@tonic-gate 	ah_t *ah;
3243bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
3244f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
3245bd670b35SErik Nordmark 	ipsec_stack_t	*ipss = ipst->ips_netstack->netstack_ipsec;
32467c478bd9Sstevel@tonic-gate 
3247bd670b35SErik Nordmark 	switch (ipsec_needs_processing_v6(mp, &nexthdr)) {
32487c478bd9Sstevel@tonic-gate 	case IPSEC_MEMORY_ERROR:
32493173664eSapersson 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
3250bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInDiscards", mp, ill);
3251bd670b35SErik Nordmark 		freemsg(mp);
3252bd670b35SErik Nordmark 		return (NULL);
32537c478bd9Sstevel@tonic-gate 	case IPSEC_HDR_DONT_PROCESS:
3254bd670b35SErik Nordmark 		return (mp);
32557c478bd9Sstevel@tonic-gate 	}
32567c478bd9Sstevel@tonic-gate 
32577c478bd9Sstevel@tonic-gate 	/* Default means send it to AH! */
32587c478bd9Sstevel@tonic-gate 	ASSERT(nexthdr == IPPROTO_AH);
32597c478bd9Sstevel@tonic-gate 
3260f4b3ec61Sdh155122 	if (!ipsec_loaded(ipss)) {
3261bd670b35SErik Nordmark 		ip_proto_not_sup(mp, ira);
3262bd670b35SErik Nordmark 		return (NULL);
32637c478bd9Sstevel@tonic-gate 	}
32647c478bd9Sstevel@tonic-gate 
3265bd670b35SErik Nordmark 	mp = ipsec_inbound_ah_sa(mp, ira, &ah);
3266bd670b35SErik Nordmark 	if (mp == NULL)
3267bd670b35SErik Nordmark 		return (NULL);
3268bd670b35SErik Nordmark 	ASSERT(ah != NULL);
3269bd670b35SErik Nordmark 	ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE);
3270bd670b35SErik Nordmark 	ASSERT(ira->ira_ipsec_ah_sa != NULL);
3271bd670b35SErik Nordmark 	ASSERT(ira->ira_ipsec_ah_sa->ipsa_input_func != NULL);
3272bd670b35SErik Nordmark 	mp = ira->ira_ipsec_ah_sa->ipsa_input_func(mp, ah, ira);
32737c478bd9Sstevel@tonic-gate 
3274bd670b35SErik Nordmark 	if (mp == NULL) {
3275bd670b35SErik Nordmark 		/*
3276bd670b35SErik Nordmark 		 * Either it failed or is pending. In the former case
3277bd670b35SErik Nordmark 		 * ipIfStatsInDiscards was increased.
3278bd670b35SErik Nordmark 		 */
3279bd670b35SErik Nordmark 		return (NULL);
3280bd670b35SErik Nordmark 	}
3281bd670b35SErik Nordmark 
32827c478bd9Sstevel@tonic-gate 	/* we're done with IPsec processing, send it up */
3283bd670b35SErik Nordmark 	ip_input_post_ipsec(mp, ira);
3284bd670b35SErik Nordmark 	return (NULL);
32857c478bd9Sstevel@tonic-gate }
32867c478bd9Sstevel@tonic-gate 
32877c478bd9Sstevel@tonic-gate /*
32887c478bd9Sstevel@tonic-gate  * Reassemble fragment.
32897c478bd9Sstevel@tonic-gate  * When it returns a completed message the first mblk will only contain
3290bd670b35SErik Nordmark  * the headers prior to the fragment header, with the nexthdr value updated
3291bd670b35SErik Nordmark  * to be the header after the fragment header.
32927c478bd9Sstevel@tonic-gate  */
3293bd670b35SErik Nordmark mblk_t *
ip_input_fragment_v6(mblk_t * mp,ip6_t * ip6h,ip6_frag_t * fraghdr,uint_t remlen,ip_recv_attr_t * ira)3294bd670b35SErik Nordmark ip_input_fragment_v6(mblk_t *mp, ip6_t *ip6h,
3295bd670b35SErik Nordmark     ip6_frag_t *fraghdr, uint_t remlen, ip_recv_attr_t *ira)
32967c478bd9Sstevel@tonic-gate {
32977c478bd9Sstevel@tonic-gate 	uint32_t	ident = ntohl(fraghdr->ip6f_ident);
32987c478bd9Sstevel@tonic-gate 	uint16_t	offset;
32997c478bd9Sstevel@tonic-gate 	boolean_t	more_frags;
33007c478bd9Sstevel@tonic-gate 	uint8_t		nexthdr = fraghdr->ip6f_nxt;
33017c478bd9Sstevel@tonic-gate 	in6_addr_t	*v6dst_ptr;
33027c478bd9Sstevel@tonic-gate 	in6_addr_t	*v6src_ptr;
33037c478bd9Sstevel@tonic-gate 	uint_t		end;
33047c478bd9Sstevel@tonic-gate 	uint_t		hdr_length;
33057c478bd9Sstevel@tonic-gate 	size_t		count;
33067c478bd9Sstevel@tonic-gate 	ipf_t		*ipf;
33077c478bd9Sstevel@tonic-gate 	ipf_t		**ipfp;
33087c478bd9Sstevel@tonic-gate 	ipfb_t		*ipfb;
33097c478bd9Sstevel@tonic-gate 	mblk_t		*mp1;
33107c478bd9Sstevel@tonic-gate 	uint8_t		ecn_info = 0;
33117c478bd9Sstevel@tonic-gate 	size_t		msg_len;
33127c478bd9Sstevel@tonic-gate 	mblk_t		*tail_mp;
33137c478bd9Sstevel@tonic-gate 	mblk_t		*t_mp;
33147c478bd9Sstevel@tonic-gate 	boolean_t	pruned = B_FALSE;
3315ff550d0eSmasputra 	uint32_t	sum_val;
3316ff550d0eSmasputra 	uint16_t	sum_flags;
3317bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_ill;
3318f4b3ec61Sdh155122 	ip_stack_t	*ipst = ill->ill_ipst;
3319bd670b35SErik Nordmark 	uint_t		prev_nexthdr_offset;
3320bd670b35SErik Nordmark 	uint8_t		prev_nexthdr;
3321bd670b35SErik Nordmark 	uint8_t		*ptr;
3322bd670b35SErik Nordmark 	uint32_t	packet_size;
3323ff550d0eSmasputra 
3324ff550d0eSmasputra 	/*
3325ff550d0eSmasputra 	 * We utilize hardware computed checksum info only for UDP since
3326ff550d0eSmasputra 	 * IP fragmentation is a normal occurence for the protocol.  In
3327ff550d0eSmasputra 	 * addition, checksum offload support for IP fragments carrying
3328ff550d0eSmasputra 	 * UDP payload is commonly implemented across network adapters.
3329ff550d0eSmasputra 	 */
3330bd670b35SErik Nordmark 	ASSERT(ira->ira_rill != NULL);
3331bd670b35SErik Nordmark 	if (nexthdr == IPPROTO_UDP && dohwcksum &&
3332bd670b35SErik Nordmark 	    ILL_HCKSUM_CAPABLE(ira->ira_rill) &&
3333ff550d0eSmasputra 	    (DB_CKSUMFLAGS(mp) & (HCK_FULLCKSUM | HCK_PARTIALCKSUM))) {
3334ff550d0eSmasputra 		mblk_t *mp1 = mp->b_cont;
3335ff550d0eSmasputra 		int32_t len;
3336ff550d0eSmasputra 
3337ff550d0eSmasputra 		/* Record checksum information from the packet */
3338ff550d0eSmasputra 		sum_val = (uint32_t)DB_CKSUM16(mp);
3339ff550d0eSmasputra 		sum_flags = DB_CKSUMFLAGS(mp);
3340ff550d0eSmasputra 
3341ff550d0eSmasputra 		/* fragmented payload offset from beginning of mblk */
3342ff550d0eSmasputra 		offset = (uint16_t)((uchar_t *)&fraghdr[1] - mp->b_rptr);
3343ff550d0eSmasputra 
3344ff550d0eSmasputra 		if ((sum_flags & HCK_PARTIALCKSUM) &&
3345ff550d0eSmasputra 		    (mp1 == NULL || mp1->b_cont == NULL) &&
3346bd670b35SErik Nordmark 		    offset >= DB_CKSUMSTART(mp) &&
3347bd670b35SErik Nordmark 		    ((len = offset - DB_CKSUMSTART(mp)) & 1) == 0) {
3348ff550d0eSmasputra 			uint32_t adj;
3349ff550d0eSmasputra 			/*
3350ff550d0eSmasputra 			 * Partial checksum has been calculated by hardware
3351ff550d0eSmasputra 			 * and attached to the packet; in addition, any
3352ff550d0eSmasputra 			 * prepended extraneous data is even byte aligned.
3353ff550d0eSmasputra 			 * If any such data exists, we adjust the checksum;
3354ff550d0eSmasputra 			 * this would also handle any postpended data.
3355ff550d0eSmasputra 			 */
3356ff550d0eSmasputra 			IP_ADJCKSUM_PARTIAL(mp->b_rptr + DB_CKSUMSTART(mp),
3357ff550d0eSmasputra 			    mp, mp1, len, adj);
3358ff550d0eSmasputra 
3359ff550d0eSmasputra 			/* One's complement subtract extraneous checksum */
3360ff550d0eSmasputra 			if (adj >= sum_val)
3361ff550d0eSmasputra 				sum_val = ~(adj - sum_val) & 0xFFFF;
3362ff550d0eSmasputra 			else
3363ff550d0eSmasputra 				sum_val -= adj;
3364ff550d0eSmasputra 		}
3365ff550d0eSmasputra 	} else {
3366ff550d0eSmasputra 		sum_val = 0;
3367ff550d0eSmasputra 		sum_flags = 0;
3368ff550d0eSmasputra 	}
3369ff550d0eSmasputra 
3370ff550d0eSmasputra 	/* Clear hardware checksumming flag */
3371ff550d0eSmasputra 	DB_CKSUMFLAGS(mp) = 0;
33727c478bd9Sstevel@tonic-gate 
33737c478bd9Sstevel@tonic-gate 	/*
3374bd670b35SErik Nordmark 	 * Determine the offset (from the begining of the IP header)
3375bd670b35SErik Nordmark 	 * of the nexthdr value which has IPPROTO_FRAGMENT. We use
3376bd670b35SErik Nordmark 	 * this when removing the fragment header from the packet.
3377bd670b35SErik Nordmark 	 * This packet consists of the IPv6 header, a potential
3378bd670b35SErik Nordmark 	 * hop-by-hop options header, a potential pre-routing-header
3379bd670b35SErik Nordmark 	 * destination options header, and a potential routing header.
3380bd670b35SErik Nordmark 	 */
3381bd670b35SErik Nordmark 	prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
3382bd670b35SErik Nordmark 	prev_nexthdr = ip6h->ip6_nxt;
3383bd670b35SErik Nordmark 	ptr = (uint8_t *)&ip6h[1];
3384bd670b35SErik Nordmark 
3385bd670b35SErik Nordmark 	if (prev_nexthdr == IPPROTO_HOPOPTS) {
3386bd670b35SErik Nordmark 		ip6_hbh_t	*hbh_hdr;
3387bd670b35SErik Nordmark 		uint_t		hdr_len;
3388bd670b35SErik Nordmark 
3389bd670b35SErik Nordmark 		hbh_hdr = (ip6_hbh_t *)ptr;
3390bd670b35SErik Nordmark 		hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
3391bd670b35SErik Nordmark 		prev_nexthdr = hbh_hdr->ip6h_nxt;
3392bd670b35SErik Nordmark 		prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
3393bd670b35SErik Nordmark 		    - (uint8_t *)ip6h;
3394bd670b35SErik Nordmark 		ptr += hdr_len;
3395bd670b35SErik Nordmark 	}
3396bd670b35SErik Nordmark 	if (prev_nexthdr == IPPROTO_DSTOPTS) {
3397bd670b35SErik Nordmark 		ip6_dest_t	*dest_hdr;
3398bd670b35SErik Nordmark 		uint_t		hdr_len;
3399bd670b35SErik Nordmark 
3400bd670b35SErik Nordmark 		dest_hdr = (ip6_dest_t *)ptr;
3401bd670b35SErik Nordmark 		hdr_len = 8 * (dest_hdr->ip6d_len + 1);
3402bd670b35SErik Nordmark 		prev_nexthdr = dest_hdr->ip6d_nxt;
3403bd670b35SErik Nordmark 		prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
3404bd670b35SErik Nordmark 		    - (uint8_t *)ip6h;
3405bd670b35SErik Nordmark 		ptr += hdr_len;
3406bd670b35SErik Nordmark 	}
3407bd670b35SErik Nordmark 	if (prev_nexthdr == IPPROTO_ROUTING) {
3408bd670b35SErik Nordmark 		ip6_rthdr_t	*rthdr;
3409bd670b35SErik Nordmark 		uint_t		hdr_len;
3410bd670b35SErik Nordmark 
3411bd670b35SErik Nordmark 		rthdr = (ip6_rthdr_t *)ptr;
3412bd670b35SErik Nordmark 		prev_nexthdr = rthdr->ip6r_nxt;
3413bd670b35SErik Nordmark 		prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
3414bd670b35SErik Nordmark 		    - (uint8_t *)ip6h;
3415bd670b35SErik Nordmark 		hdr_len = 8 * (rthdr->ip6r_len + 1);
3416bd670b35SErik Nordmark 		ptr += hdr_len;
3417bd670b35SErik Nordmark 	}
3418bd670b35SErik Nordmark 	if (prev_nexthdr != IPPROTO_FRAGMENT) {
3419bd670b35SErik Nordmark 		/* Can't handle other headers before the fragment header */
3420bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
3421bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
3422bd670b35SErik Nordmark 		freemsg(mp);
3423bd670b35SErik Nordmark 		return (NULL);
3424bd670b35SErik Nordmark 	}
3425bd670b35SErik Nordmark 
3426bd670b35SErik Nordmark 	/*
34277c478bd9Sstevel@tonic-gate 	 * Note: Fragment offset in header is in 8-octet units.
34287c478bd9Sstevel@tonic-gate 	 * Clearing least significant 3 bits not only extracts
34297c478bd9Sstevel@tonic-gate 	 * it but also gets it in units of octets.
34307c478bd9Sstevel@tonic-gate 	 */
34317c478bd9Sstevel@tonic-gate 	offset = ntohs(fraghdr->ip6f_offlg) & ~7;
34327c478bd9Sstevel@tonic-gate 	more_frags = (fraghdr->ip6f_offlg & IP6F_MORE_FRAG);
34337c478bd9Sstevel@tonic-gate 
34347c478bd9Sstevel@tonic-gate 	/*
34357c478bd9Sstevel@tonic-gate 	 * Is the more frags flag on and the payload length not a multiple
34367c478bd9Sstevel@tonic-gate 	 * of eight?
34377c478bd9Sstevel@tonic-gate 	 */
34387c478bd9Sstevel@tonic-gate 	if (more_frags && (ntohs(ip6h->ip6_plen) & 7)) {
3439bd670b35SErik Nordmark 		ip_drop_input("ICMP_PARAM_PROBLEM", mp, ill);
3440bd670b35SErik Nordmark 		icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
34417c478bd9Sstevel@tonic-gate 		    (uint32_t)((char *)&ip6h->ip6_plen -
3442bd670b35SErik Nordmark 		    (char *)ip6h), B_FALSE, ira);
34437c478bd9Sstevel@tonic-gate 		return (NULL);
34447c478bd9Sstevel@tonic-gate 	}
34457c478bd9Sstevel@tonic-gate 
34467c478bd9Sstevel@tonic-gate 	v6src_ptr = &ip6h->ip6_src;
34477c478bd9Sstevel@tonic-gate 	v6dst_ptr = &ip6h->ip6_dst;
34487c478bd9Sstevel@tonic-gate 	end = remlen;
34497c478bd9Sstevel@tonic-gate 
34507c478bd9Sstevel@tonic-gate 	hdr_length = (uint_t)((char *)&fraghdr[1] - (char *)ip6h);
34517c478bd9Sstevel@tonic-gate 	end += offset;
34527c478bd9Sstevel@tonic-gate 
34537c478bd9Sstevel@tonic-gate 	/*
34547c478bd9Sstevel@tonic-gate 	 * Would fragment cause reassembled packet to have a payload length
34557c478bd9Sstevel@tonic-gate 	 * greater than IP_MAXPACKET - the max payload size?
34567c478bd9Sstevel@tonic-gate 	 */
34577c478bd9Sstevel@tonic-gate 	if (end > IP_MAXPACKET) {
34583173664eSapersson 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
3459bd670b35SErik Nordmark 		ip_drop_input("Reassembled packet too large", mp, ill);
3460bd670b35SErik Nordmark 		icmp_param_problem_v6(mp, ICMP6_PARAMPROB_HEADER,
34617c478bd9Sstevel@tonic-gate 		    (uint32_t)((char *)&fraghdr->ip6f_offlg -
3462bd670b35SErik Nordmark 		    (char *)ip6h), B_FALSE, ira);
34637c478bd9Sstevel@tonic-gate 		return (NULL);
34647c478bd9Sstevel@tonic-gate 	}
34657c478bd9Sstevel@tonic-gate 
34667c478bd9Sstevel@tonic-gate 	/*
34677c478bd9Sstevel@tonic-gate 	 * This packet just has one fragment. Reassembly not
34687c478bd9Sstevel@tonic-gate 	 * needed.
34697c478bd9Sstevel@tonic-gate 	 */
34707c478bd9Sstevel@tonic-gate 	if (!more_frags && offset == 0) {
34717c478bd9Sstevel@tonic-gate 		goto reass_done;
34727c478bd9Sstevel@tonic-gate 	}
34737c478bd9Sstevel@tonic-gate 
34747c478bd9Sstevel@tonic-gate 	/*
34757c478bd9Sstevel@tonic-gate 	 * Drop the fragmented as early as possible, if
34767c478bd9Sstevel@tonic-gate 	 * we don't have resource(s) to re-assemble.
34777c478bd9Sstevel@tonic-gate 	 */
3478f4b3ec61Sdh155122 	if (ipst->ips_ip_reass_queue_bytes == 0) {
34797c478bd9Sstevel@tonic-gate 		freemsg(mp);
34807c478bd9Sstevel@tonic-gate 		return (NULL);
34817c478bd9Sstevel@tonic-gate 	}
34827c478bd9Sstevel@tonic-gate 
34837c478bd9Sstevel@tonic-gate 	/* Record the ECN field info. */
34847c478bd9Sstevel@tonic-gate 	ecn_info = (uint8_t)(ntohl(ip6h->ip6_vcf & htonl(~0xFFCFFFFF)) >> 20);
34857c478bd9Sstevel@tonic-gate 	/*
34867c478bd9Sstevel@tonic-gate 	 * If this is not the first fragment, dump the unfragmentable
34877c478bd9Sstevel@tonic-gate 	 * portion of the packet.
34887c478bd9Sstevel@tonic-gate 	 */
34897c478bd9Sstevel@tonic-gate 	if (offset)
34907c478bd9Sstevel@tonic-gate 		mp->b_rptr = (uchar_t *)&fraghdr[1];
34917c478bd9Sstevel@tonic-gate 
34927c478bd9Sstevel@tonic-gate 	/*
34937c478bd9Sstevel@tonic-gate 	 * Fragmentation reassembly.  Each ILL has a hash table for
34947c478bd9Sstevel@tonic-gate 	 * queueing packets undergoing reassembly for all IPIFs
34957c478bd9Sstevel@tonic-gate 	 * associated with the ILL.  The hash is based on the packet
34967c478bd9Sstevel@tonic-gate 	 * IP ident field.  The ILL frag hash table was allocated
34977c478bd9Sstevel@tonic-gate 	 * as a timer block at the time the ILL was created.  Whenever
34987c478bd9Sstevel@tonic-gate 	 * there is anything on the reassembly queue, the timer will
34997c478bd9Sstevel@tonic-gate 	 * be running.
35007c478bd9Sstevel@tonic-gate 	 */
3501bd670b35SErik Nordmark 	/* Handle vnic loopback of fragments */
3502bd670b35SErik Nordmark 	if (mp->b_datap->db_ref > 2)
3503bd670b35SErik Nordmark 		msg_len = 0;
3504bd670b35SErik Nordmark 	else
3505ff550d0eSmasputra 		msg_len = MBLKSIZE(mp);
3506bd670b35SErik Nordmark 
35077c478bd9Sstevel@tonic-gate 	tail_mp = mp;
35087c478bd9Sstevel@tonic-gate 	while (tail_mp->b_cont != NULL) {
35097c478bd9Sstevel@tonic-gate 		tail_mp = tail_mp->b_cont;
3510bd670b35SErik Nordmark 		if (tail_mp->b_datap->db_ref <= 2)
3511ff550d0eSmasputra 			msg_len += MBLKSIZE(tail_mp);
35127c478bd9Sstevel@tonic-gate 	}
35137c478bd9Sstevel@tonic-gate 	/*
35147c478bd9Sstevel@tonic-gate 	 * If the reassembly list for this ILL will get too big
35157c478bd9Sstevel@tonic-gate 	 * prune it.
35167c478bd9Sstevel@tonic-gate 	 */
35177c478bd9Sstevel@tonic-gate 
35187c478bd9Sstevel@tonic-gate 	if ((msg_len + sizeof (*ipf) + ill->ill_frag_count) >=
3519f4b3ec61Sdh155122 	    ipst->ips_ip_reass_queue_bytes) {
3520bd670b35SErik Nordmark 		DTRACE_PROBE3(ip_reass_queue_bytes, uint_t, msg_len,
3521bd670b35SErik Nordmark 		    uint_t, ill->ill_frag_count,
3522bd670b35SErik Nordmark 		    uint_t, ipst->ips_ip_reass_queue_bytes);
3523f4b3ec61Sdh155122 		ill_frag_prune(ill,
3524f4b3ec61Sdh155122 		    (ipst->ips_ip_reass_queue_bytes < msg_len) ? 0 :
3525f4b3ec61Sdh155122 		    (ipst->ips_ip_reass_queue_bytes - msg_len));
35267c478bd9Sstevel@tonic-gate 		pruned = B_TRUE;
35277c478bd9Sstevel@tonic-gate 	}
35287c478bd9Sstevel@tonic-gate 
35297c478bd9Sstevel@tonic-gate 	ipfb = &ill->ill_frag_hash_tbl[ILL_FRAG_HASH_V6(*v6src_ptr, ident)];
35307c478bd9Sstevel@tonic-gate 	mutex_enter(&ipfb->ipfb_lock);
35317c478bd9Sstevel@tonic-gate 
35327c478bd9Sstevel@tonic-gate 	ipfp = &ipfb->ipfb_ipf;
35337c478bd9Sstevel@tonic-gate 	/* Try to find an existing fragment queue for this packet. */
35347c478bd9Sstevel@tonic-gate 	for (;;) {
35357c478bd9Sstevel@tonic-gate 		ipf = ipfp[0];
35367c478bd9Sstevel@tonic-gate 		if (ipf) {
35377c478bd9Sstevel@tonic-gate 			/*
35387c478bd9Sstevel@tonic-gate 			 * It has to match on ident, source address, and
35397c478bd9Sstevel@tonic-gate 			 * dest address.
35407c478bd9Sstevel@tonic-gate 			 */
35417c478bd9Sstevel@tonic-gate 			if (ipf->ipf_ident == ident &&
35427c478bd9Sstevel@tonic-gate 			    IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6src, v6src_ptr) &&
35437c478bd9Sstevel@tonic-gate 			    IN6_ARE_ADDR_EQUAL(&ipf->ipf_v6dst, v6dst_ptr)) {
35447c478bd9Sstevel@tonic-gate 
35457c478bd9Sstevel@tonic-gate 				/*
35467c478bd9Sstevel@tonic-gate 				 * If we have received too many
35477c478bd9Sstevel@tonic-gate 				 * duplicate fragments for this packet
35487c478bd9Sstevel@tonic-gate 				 * free it.
35497c478bd9Sstevel@tonic-gate 				 */
35507c478bd9Sstevel@tonic-gate 				if (ipf->ipf_num_dups > ip_max_frag_dups) {
35517c478bd9Sstevel@tonic-gate 					ill_frag_free_pkts(ill, ipfb, ipf, 1);
35527c478bd9Sstevel@tonic-gate 					freemsg(mp);
35537c478bd9Sstevel@tonic-gate 					mutex_exit(&ipfb->ipfb_lock);
35547c478bd9Sstevel@tonic-gate 					return (NULL);
35557c478bd9Sstevel@tonic-gate 				}
35567c478bd9Sstevel@tonic-gate 
35577c478bd9Sstevel@tonic-gate 				break;
35587c478bd9Sstevel@tonic-gate 			}
35597c478bd9Sstevel@tonic-gate 			ipfp = &ipf->ipf_hash_next;
35607c478bd9Sstevel@tonic-gate 			continue;
35617c478bd9Sstevel@tonic-gate 		}
35627c478bd9Sstevel@tonic-gate 
35637c478bd9Sstevel@tonic-gate 
35647c478bd9Sstevel@tonic-gate 		/*
35657c478bd9Sstevel@tonic-gate 		 * If we pruned the list, do we want to store this new
35667c478bd9Sstevel@tonic-gate 		 * fragment?. We apply an optimization here based on the
35677c478bd9Sstevel@tonic-gate 		 * fact that most fragments will be received in order.
35687c478bd9Sstevel@tonic-gate 		 * So if the offset of this incoming fragment is zero,
35697c478bd9Sstevel@tonic-gate 		 * it is the first fragment of a new packet. We will
35707c478bd9Sstevel@tonic-gate 		 * keep it.  Otherwise drop the fragment, as we have
35717c478bd9Sstevel@tonic-gate 		 * probably pruned the packet already (since the
35727c478bd9Sstevel@tonic-gate 		 * packet cannot be found).
35737c478bd9Sstevel@tonic-gate 		 */
35747c478bd9Sstevel@tonic-gate 
35757c478bd9Sstevel@tonic-gate 		if (pruned && offset != 0) {
35767c478bd9Sstevel@tonic-gate 			mutex_exit(&ipfb->ipfb_lock);
35777c478bd9Sstevel@tonic-gate 			freemsg(mp);
35787c478bd9Sstevel@tonic-gate 			return (NULL);
35797c478bd9Sstevel@tonic-gate 		}
35807c478bd9Sstevel@tonic-gate 
35817c478bd9Sstevel@tonic-gate 		/* New guy.  Allocate a frag message. */
35827c478bd9Sstevel@tonic-gate 		mp1 = allocb(sizeof (*ipf), BPRI_MED);
35837c478bd9Sstevel@tonic-gate 		if (!mp1) {
35843173664eSapersson 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
3585bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, ill);
35867c478bd9Sstevel@tonic-gate 			freemsg(mp);
35877c478bd9Sstevel@tonic-gate 	partial_reass_done:
35887c478bd9Sstevel@tonic-gate 			mutex_exit(&ipfb->ipfb_lock);
35897c478bd9Sstevel@tonic-gate 			return (NULL);
35907c478bd9Sstevel@tonic-gate 		}
35917c478bd9Sstevel@tonic-gate 
3592f4b3ec61Sdh155122 		if (ipfb->ipfb_frag_pkts >= MAX_FRAG_PKTS(ipst))  {
35937c478bd9Sstevel@tonic-gate 			/*
35947c478bd9Sstevel@tonic-gate 			 * Too many fragmented packets in this hash bucket.
35957c478bd9Sstevel@tonic-gate 			 * Free the oldest.
35967c478bd9Sstevel@tonic-gate 			 */
35977c478bd9Sstevel@tonic-gate 			ill_frag_free_pkts(ill, ipfb, ipfb->ipfb_ipf, 1);
35987c478bd9Sstevel@tonic-gate 		}
35997c478bd9Sstevel@tonic-gate 
36007c478bd9Sstevel@tonic-gate 		mp1->b_cont = mp;
36017c478bd9Sstevel@tonic-gate 
36027c478bd9Sstevel@tonic-gate 		/* Initialize the fragment header. */
36037c478bd9Sstevel@tonic-gate 		ipf = (ipf_t *)mp1->b_rptr;
36047c478bd9Sstevel@tonic-gate 		ipf->ipf_mp = mp1;
36057c478bd9Sstevel@tonic-gate 		ipf->ipf_ptphn = ipfp;
36067c478bd9Sstevel@tonic-gate 		ipfp[0] = ipf;
36077c478bd9Sstevel@tonic-gate 		ipf->ipf_hash_next = NULL;
36087c478bd9Sstevel@tonic-gate 		ipf->ipf_ident = ident;
36097c478bd9Sstevel@tonic-gate 		ipf->ipf_v6src = *v6src_ptr;
36107c478bd9Sstevel@tonic-gate 		ipf->ipf_v6dst = *v6dst_ptr;
36117c478bd9Sstevel@tonic-gate 		/* Record reassembly start time. */
36127c478bd9Sstevel@tonic-gate 		ipf->ipf_timestamp = gethrestime_sec();
36137c478bd9Sstevel@tonic-gate 		/* Record ipf generation and account for frag header */
36147c478bd9Sstevel@tonic-gate 		ipf->ipf_gen = ill->ill_ipf_gen++;
3615ff550d0eSmasputra 		ipf->ipf_count = MBLKSIZE(mp1);
36167c478bd9Sstevel@tonic-gate 		ipf->ipf_protocol = nexthdr;
36177c478bd9Sstevel@tonic-gate 		ipf->ipf_nf_hdr_len = 0;
36187c478bd9Sstevel@tonic-gate 		ipf->ipf_prev_nexthdr_offset = 0;
36197c478bd9Sstevel@tonic-gate 		ipf->ipf_last_frag_seen = B_FALSE;
36207c478bd9Sstevel@tonic-gate 		ipf->ipf_ecn = ecn_info;
36217c478bd9Sstevel@tonic-gate 		ipf->ipf_num_dups = 0;
36227c478bd9Sstevel@tonic-gate 		ipfb->ipfb_frag_pkts++;
3623ff550d0eSmasputra 		ipf->ipf_checksum = 0;
3624ff550d0eSmasputra 		ipf->ipf_checksum_flags = 0;
3625ff550d0eSmasputra 
3626ff550d0eSmasputra 		/* Store checksum value in fragment header */
3627ff550d0eSmasputra 		if (sum_flags != 0) {
3628ff550d0eSmasputra 			sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3629ff550d0eSmasputra 			sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3630ff550d0eSmasputra 			ipf->ipf_checksum = sum_val;
3631ff550d0eSmasputra 			ipf->ipf_checksum_flags = sum_flags;
3632ff550d0eSmasputra 		}
36337c478bd9Sstevel@tonic-gate 
36347c478bd9Sstevel@tonic-gate 		/*
36357c478bd9Sstevel@tonic-gate 		 * We handle reassembly two ways.  In the easy case,
36367c478bd9Sstevel@tonic-gate 		 * where all the fragments show up in order, we do
36377c478bd9Sstevel@tonic-gate 		 * minimal bookkeeping, and just clip new pieces on
36387c478bd9Sstevel@tonic-gate 		 * the end.  If we ever see a hole, then we go off
36397c478bd9Sstevel@tonic-gate 		 * to ip_reassemble which has to mark the pieces and
36407c478bd9Sstevel@tonic-gate 		 * keep track of the number of holes, etc.  Obviously,
36417c478bd9Sstevel@tonic-gate 		 * the point of having both mechanisms is so we can
36427c478bd9Sstevel@tonic-gate 		 * handle the easy case as efficiently as possible.
36437c478bd9Sstevel@tonic-gate 		 */
36447c478bd9Sstevel@tonic-gate 		if (offset == 0) {
36457c478bd9Sstevel@tonic-gate 			/* Easy case, in-order reassembly so far. */
36467c478bd9Sstevel@tonic-gate 			/* Update the byte count */
36477c478bd9Sstevel@tonic-gate 			ipf->ipf_count += msg_len;
36487c478bd9Sstevel@tonic-gate 			ipf->ipf_tail_mp = tail_mp;
36497c478bd9Sstevel@tonic-gate 			/*
36507c478bd9Sstevel@tonic-gate 			 * Keep track of next expected offset in
36517c478bd9Sstevel@tonic-gate 			 * ipf_end.
36527c478bd9Sstevel@tonic-gate 			 */
36537c478bd9Sstevel@tonic-gate 			ipf->ipf_end = end;
36547c478bd9Sstevel@tonic-gate 			ipf->ipf_nf_hdr_len = hdr_length;
3655bd670b35SErik Nordmark 			ipf->ipf_prev_nexthdr_offset = prev_nexthdr_offset;
36567c478bd9Sstevel@tonic-gate 		} else {
36577c478bd9Sstevel@tonic-gate 			/* Hard case, hole at the beginning. */
36587c478bd9Sstevel@tonic-gate 			ipf->ipf_tail_mp = NULL;
36597c478bd9Sstevel@tonic-gate 			/*
36607c478bd9Sstevel@tonic-gate 			 * ipf_end == 0 means that we have given up
36617c478bd9Sstevel@tonic-gate 			 * on easy reassembly.
36627c478bd9Sstevel@tonic-gate 			 */
36637c478bd9Sstevel@tonic-gate 			ipf->ipf_end = 0;
3664ff550d0eSmasputra 
3665ff550d0eSmasputra 			/* Forget checksum offload from now on */
3666ff550d0eSmasputra 			ipf->ipf_checksum_flags = 0;
3667ff550d0eSmasputra 
36687c478bd9Sstevel@tonic-gate 			/*
36697c478bd9Sstevel@tonic-gate 			 * ipf_hole_cnt is set by ip_reassemble.
36707c478bd9Sstevel@tonic-gate 			 * ipf_count is updated by ip_reassemble.
36717c478bd9Sstevel@tonic-gate 			 * No need to check for return value here
36727c478bd9Sstevel@tonic-gate 			 * as we don't expect reassembly to complete or
36737c478bd9Sstevel@tonic-gate 			 * fail for the first fragment itself.
36747c478bd9Sstevel@tonic-gate 			 */
36757c478bd9Sstevel@tonic-gate 			(void) ip_reassemble(mp, ipf, offset, more_frags, ill,
36767c478bd9Sstevel@tonic-gate 			    msg_len);
36777c478bd9Sstevel@tonic-gate 		}
36787c478bd9Sstevel@tonic-gate 		/* Update per ipfb and ill byte counts */
36797c478bd9Sstevel@tonic-gate 		ipfb->ipfb_count += ipf->ipf_count;
36807c478bd9Sstevel@tonic-gate 		ASSERT(ipfb->ipfb_count > 0);	/* Wraparound */
36810a5d959fSgeorges 		atomic_add_32(&ill->ill_frag_count, ipf->ipf_count);
36827c478bd9Sstevel@tonic-gate 		/* If the frag timer wasn't already going, start it. */
36837c478bd9Sstevel@tonic-gate 		mutex_enter(&ill->ill_lock);
36847c478bd9Sstevel@tonic-gate 		ill_frag_timer_start(ill);
36857c478bd9Sstevel@tonic-gate 		mutex_exit(&ill->ill_lock);
36867c478bd9Sstevel@tonic-gate 		goto partial_reass_done;
36877c478bd9Sstevel@tonic-gate 	}
36887c478bd9Sstevel@tonic-gate 
36897c478bd9Sstevel@tonic-gate 	/*
3690ff550d0eSmasputra 	 * If the packet's flag has changed (it could be coming up
3691ff550d0eSmasputra 	 * from an interface different than the previous, therefore
3692ff550d0eSmasputra 	 * possibly different checksum capability), then forget about
3693ff550d0eSmasputra 	 * any stored checksum states.  Otherwise add the value to
3694ff550d0eSmasputra 	 * the existing one stored in the fragment header.
3695ff550d0eSmasputra 	 */
3696ff550d0eSmasputra 	if (sum_flags != 0 && sum_flags == ipf->ipf_checksum_flags) {
3697ff550d0eSmasputra 		sum_val += ipf->ipf_checksum;
3698ff550d0eSmasputra 		sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3699ff550d0eSmasputra 		sum_val = (sum_val & 0xFFFF) + (sum_val >> 16);
3700ff550d0eSmasputra 		ipf->ipf_checksum = sum_val;
3701ff550d0eSmasputra 	} else if (ipf->ipf_checksum_flags != 0) {
3702ff550d0eSmasputra 		/* Forget checksum offload from now on */
3703ff550d0eSmasputra 		ipf->ipf_checksum_flags = 0;
3704ff550d0eSmasputra 	}
3705ff550d0eSmasputra 
3706ff550d0eSmasputra 	/*
37077c478bd9Sstevel@tonic-gate 	 * We have a new piece of a datagram which is already being
37087c478bd9Sstevel@tonic-gate 	 * reassembled.  Update the ECN info if all IP fragments
37097c478bd9Sstevel@tonic-gate 	 * are ECN capable.  If there is one which is not, clear
37107c478bd9Sstevel@tonic-gate 	 * all the info.  If there is at least one which has CE
37117c478bd9Sstevel@tonic-gate 	 * code point, IP needs to report that up to transport.
37127c478bd9Sstevel@tonic-gate 	 */
37137c478bd9Sstevel@tonic-gate 	if (ecn_info != IPH_ECN_NECT && ipf->ipf_ecn != IPH_ECN_NECT) {
37147c478bd9Sstevel@tonic-gate 		if (ecn_info == IPH_ECN_CE)
37157c478bd9Sstevel@tonic-gate 			ipf->ipf_ecn = IPH_ECN_CE;
37167c478bd9Sstevel@tonic-gate 	} else {
37177c478bd9Sstevel@tonic-gate 		ipf->ipf_ecn = IPH_ECN_NECT;
37187c478bd9Sstevel@tonic-gate 	}
37197c478bd9Sstevel@tonic-gate 
37207c478bd9Sstevel@tonic-gate 	if (offset && ipf->ipf_end == offset) {
37217c478bd9Sstevel@tonic-gate 		/* The new fragment fits at the end */
37227c478bd9Sstevel@tonic-gate 		ipf->ipf_tail_mp->b_cont = mp;
37237c478bd9Sstevel@tonic-gate 		/* Update the byte count */
37247c478bd9Sstevel@tonic-gate 		ipf->ipf_count += msg_len;
37257c478bd9Sstevel@tonic-gate 		/* Update per ipfb and ill byte counts */
37267c478bd9Sstevel@tonic-gate 		ipfb->ipfb_count += msg_len;
37277c478bd9Sstevel@tonic-gate 		ASSERT(ipfb->ipfb_count > 0);	/* Wraparound */
37280a5d959fSgeorges 		atomic_add_32(&ill->ill_frag_count, msg_len);
37297c478bd9Sstevel@tonic-gate 		if (more_frags) {
37307c478bd9Sstevel@tonic-gate 			/* More to come. */
37317c478bd9Sstevel@tonic-gate 			ipf->ipf_end = end;
37327c478bd9Sstevel@tonic-gate 			ipf->ipf_tail_mp = tail_mp;
37337c478bd9Sstevel@tonic-gate 			goto partial_reass_done;
37347c478bd9Sstevel@tonic-gate 		}
37357c478bd9Sstevel@tonic-gate 	} else {
37367c478bd9Sstevel@tonic-gate 		/*
37377c478bd9Sstevel@tonic-gate 		 * Go do the hard cases.
37387c478bd9Sstevel@tonic-gate 		 * Call ip_reassemble().
37397c478bd9Sstevel@tonic-gate 		 */
37407c478bd9Sstevel@tonic-gate 		int ret;
37417c478bd9Sstevel@tonic-gate 
37427c478bd9Sstevel@tonic-gate 		if (offset == 0) {
37437c478bd9Sstevel@tonic-gate 			if (ipf->ipf_prev_nexthdr_offset == 0) {
37447c478bd9Sstevel@tonic-gate 				ipf->ipf_nf_hdr_len = hdr_length;
37457c478bd9Sstevel@tonic-gate 				ipf->ipf_prev_nexthdr_offset =
3746bd670b35SErik Nordmark 				    prev_nexthdr_offset;
37477c478bd9Sstevel@tonic-gate 			}
37487c478bd9Sstevel@tonic-gate 		}
37497c478bd9Sstevel@tonic-gate 		/* Save current byte count */
37507c478bd9Sstevel@tonic-gate 		count = ipf->ipf_count;
37517c478bd9Sstevel@tonic-gate 		ret = ip_reassemble(mp, ipf, offset, more_frags, ill, msg_len);
37527c478bd9Sstevel@tonic-gate 
37537c478bd9Sstevel@tonic-gate 		/* Count of bytes added and subtracted (freeb()ed) */
37547c478bd9Sstevel@tonic-gate 		count = ipf->ipf_count - count;
37557c478bd9Sstevel@tonic-gate 		if (count) {
37567c478bd9Sstevel@tonic-gate 			/* Update per ipfb and ill byte counts */
37577c478bd9Sstevel@tonic-gate 			ipfb->ipfb_count += count;
37587c478bd9Sstevel@tonic-gate 			ASSERT(ipfb->ipfb_count > 0);	/* Wraparound */
37590a5d959fSgeorges 			atomic_add_32(&ill->ill_frag_count, count);
37607c478bd9Sstevel@tonic-gate 		}
37617c478bd9Sstevel@tonic-gate 		if (ret == IP_REASS_PARTIAL) {
37627c478bd9Sstevel@tonic-gate 			goto partial_reass_done;
37637c478bd9Sstevel@tonic-gate 		} else if (ret == IP_REASS_FAILED) {
37647c478bd9Sstevel@tonic-gate 			/* Reassembly failed. Free up all resources */
37657c478bd9Sstevel@tonic-gate 			ill_frag_free_pkts(ill, ipfb, ipf, 1);
37667c478bd9Sstevel@tonic-gate 			for (t_mp = mp; t_mp != NULL; t_mp = t_mp->b_cont) {
37677c478bd9Sstevel@tonic-gate 				IP_REASS_SET_START(t_mp, 0);
37687c478bd9Sstevel@tonic-gate 				IP_REASS_SET_END(t_mp, 0);
37697c478bd9Sstevel@tonic-gate 			}
37707c478bd9Sstevel@tonic-gate 			freemsg(mp);
37717c478bd9Sstevel@tonic-gate 			goto partial_reass_done;
37727c478bd9Sstevel@tonic-gate 		}
37737c478bd9Sstevel@tonic-gate 
37747c478bd9Sstevel@tonic-gate 		/* We will reach here iff 'ret' is IP_REASS_COMPLETE */
37757c478bd9Sstevel@tonic-gate 	}
37767c478bd9Sstevel@tonic-gate 	/*
37777c478bd9Sstevel@tonic-gate 	 * We have completed reassembly.  Unhook the frag header from
37787c478bd9Sstevel@tonic-gate 	 * the reassembly list.
37797c478bd9Sstevel@tonic-gate 	 *
37807c478bd9Sstevel@tonic-gate 	 * Grab the unfragmentable header length next header value out
37817c478bd9Sstevel@tonic-gate 	 * of the first fragment
37827c478bd9Sstevel@tonic-gate 	 */
37837c478bd9Sstevel@tonic-gate 	ASSERT(ipf->ipf_nf_hdr_len != 0);
37847c478bd9Sstevel@tonic-gate 	hdr_length = ipf->ipf_nf_hdr_len;
37857c478bd9Sstevel@tonic-gate 
37867c478bd9Sstevel@tonic-gate 	/*
37877c478bd9Sstevel@tonic-gate 	 * Before we free the frag header, record the ECN info
37887c478bd9Sstevel@tonic-gate 	 * to report back to the transport.
37897c478bd9Sstevel@tonic-gate 	 */
37907c478bd9Sstevel@tonic-gate 	ecn_info = ipf->ipf_ecn;
37917c478bd9Sstevel@tonic-gate 
37927c478bd9Sstevel@tonic-gate 	/*
37937c478bd9Sstevel@tonic-gate 	 * Store the nextheader field in the header preceding the fragment
37947c478bd9Sstevel@tonic-gate 	 * header
37957c478bd9Sstevel@tonic-gate 	 */
37967c478bd9Sstevel@tonic-gate 	nexthdr = ipf->ipf_protocol;
3797bd670b35SErik Nordmark 	prev_nexthdr_offset = ipf->ipf_prev_nexthdr_offset;
37987c478bd9Sstevel@tonic-gate 	ipfp = ipf->ipf_ptphn;
3799ff550d0eSmasputra 
3800ff550d0eSmasputra 	/* We need to supply these to caller */
3801ff550d0eSmasputra 	if ((sum_flags = ipf->ipf_checksum_flags) != 0)
3802ff550d0eSmasputra 		sum_val = ipf->ipf_checksum;
3803ff550d0eSmasputra 	else
3804ff550d0eSmasputra 		sum_val = 0;
3805ff550d0eSmasputra 
38067c478bd9Sstevel@tonic-gate 	mp1 = ipf->ipf_mp;
38077c478bd9Sstevel@tonic-gate 	count = ipf->ipf_count;
38087c478bd9Sstevel@tonic-gate 	ipf = ipf->ipf_hash_next;
38097c478bd9Sstevel@tonic-gate 	if (ipf)
38107c478bd9Sstevel@tonic-gate 		ipf->ipf_ptphn = ipfp;
38117c478bd9Sstevel@tonic-gate 	ipfp[0] = ipf;
38120a5d959fSgeorges 	atomic_add_32(&ill->ill_frag_count, -count);
38137c478bd9Sstevel@tonic-gate 	ASSERT(ipfb->ipfb_count >= count);
38147c478bd9Sstevel@tonic-gate 	ipfb->ipfb_count -= count;
38157c478bd9Sstevel@tonic-gate 	ipfb->ipfb_frag_pkts--;
38167c478bd9Sstevel@tonic-gate 	mutex_exit(&ipfb->ipfb_lock);
38177c478bd9Sstevel@tonic-gate 	/* Ditch the frag header. */
38187c478bd9Sstevel@tonic-gate 	mp = mp1->b_cont;
38197c478bd9Sstevel@tonic-gate 	freeb(mp1);
38207c478bd9Sstevel@tonic-gate 
38217c478bd9Sstevel@tonic-gate 	/*
38227c478bd9Sstevel@tonic-gate 	 * Make sure the packet is good by doing some sanity
38237c478bd9Sstevel@tonic-gate 	 * check. If bad we can silentely drop the packet.
38247c478bd9Sstevel@tonic-gate 	 */
38257c478bd9Sstevel@tonic-gate reass_done:
38267c478bd9Sstevel@tonic-gate 	if (hdr_length < sizeof (ip6_frag_t)) {
38273173664eSapersson 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsInHdrErrors);
3828bd670b35SErik Nordmark 		ip_drop_input("ipIfStatsInHdrErrors", mp, ill);
3829bd670b35SErik Nordmark 		ip1dbg(("ip_input_fragment_v6: bad packet\n"));
38307c478bd9Sstevel@tonic-gate 		freemsg(mp);
38317c478bd9Sstevel@tonic-gate 		return (NULL);
38327c478bd9Sstevel@tonic-gate 	}
38337c478bd9Sstevel@tonic-gate 
38347c478bd9Sstevel@tonic-gate 	/*
38357c478bd9Sstevel@tonic-gate 	 * Remove the fragment header from the initial header by
38367c478bd9Sstevel@tonic-gate 	 * splitting the mblk into the non-fragmentable header and
38377c478bd9Sstevel@tonic-gate 	 * everthing after the fragment extension header.  This has the
38387c478bd9Sstevel@tonic-gate 	 * side effect of putting all the headers that need destination
38397c478bd9Sstevel@tonic-gate 	 * processing into the b_cont block-- on return this fact is
38407c478bd9Sstevel@tonic-gate 	 * used in order to avoid having to look at the extensions
38417c478bd9Sstevel@tonic-gate 	 * already processed.
38427c478bd9Sstevel@tonic-gate 	 *
38437c478bd9Sstevel@tonic-gate 	 * Note that this code assumes that the unfragmentable portion
38447c478bd9Sstevel@tonic-gate 	 * of the header is in the first mblk and increments
38457c478bd9Sstevel@tonic-gate 	 * the read pointer past it.  If this assumption is broken
38467c478bd9Sstevel@tonic-gate 	 * this code fails badly.
38477c478bd9Sstevel@tonic-gate 	 */
38487c478bd9Sstevel@tonic-gate 	if (mp->b_rptr + hdr_length != mp->b_wptr) {
38497c478bd9Sstevel@tonic-gate 		mblk_t *nmp;
38507c478bd9Sstevel@tonic-gate 
38517c478bd9Sstevel@tonic-gate 		if (!(nmp = dupb(mp))) {
3852bd670b35SErik Nordmark 			ip1dbg(("ip_input_fragment_v6: dupb failed\n"));
38533173664eSapersson 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
3854bd670b35SErik Nordmark 			ip_drop_input("ipIfStatsInDiscards", mp, ill);
38557c478bd9Sstevel@tonic-gate 			freemsg(mp);
38567c478bd9Sstevel@tonic-gate 			return (NULL);
38577c478bd9Sstevel@tonic-gate 		}
38587c478bd9Sstevel@tonic-gate 		nmp->b_cont = mp->b_cont;
38597c478bd9Sstevel@tonic-gate 		mp->b_cont = nmp;
38607c478bd9Sstevel@tonic-gate 		nmp->b_rptr += hdr_length;
38617c478bd9Sstevel@tonic-gate 	}
38627c478bd9Sstevel@tonic-gate 	mp->b_wptr = mp->b_rptr + hdr_length - sizeof (ip6_frag_t);
38637c478bd9Sstevel@tonic-gate 
38647c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
3865bd670b35SErik Nordmark 	((char *)ip6h)[prev_nexthdr_offset] = nexthdr;
38667c478bd9Sstevel@tonic-gate 
38677c478bd9Sstevel@tonic-gate 	/* Restore original IP length in header. */
3868bd670b35SErik Nordmark 	packet_size = msgdsize(mp);
3869bd670b35SErik Nordmark 	ip6h->ip6_plen = htons((uint16_t)(packet_size - IPV6_HDR_LEN));
38707c478bd9Sstevel@tonic-gate 	/* Record the ECN info. */
38717c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf &= htonl(0xFFCFFFFF);
38727c478bd9Sstevel@tonic-gate 	ip6h->ip6_vcf |= htonl(ecn_info << 20);
38737c478bd9Sstevel@tonic-gate 
3874bd670b35SErik Nordmark 	/* Update the receive attributes */
3875bd670b35SErik Nordmark 	ira->ira_pktlen = packet_size;
3876bd670b35SErik Nordmark 	ira->ira_ip_hdr_length = hdr_length - sizeof (ip6_frag_t);
3877bd670b35SErik Nordmark 	ira->ira_protocol = nexthdr;
3878bd670b35SErik Nordmark 
3879bd670b35SErik Nordmark 	/* Reassembly is successful; set checksum information in packet */
3880bd670b35SErik Nordmark 	DB_CKSUM16(mp) = (uint16_t)sum_val;
3881bd670b35SErik Nordmark 	DB_CKSUMFLAGS(mp) = sum_flags;
3882bd670b35SErik Nordmark 	DB_CKSUMSTART(mp) = ira->ira_ip_hdr_length;
3883ff550d0eSmasputra 
38847c478bd9Sstevel@tonic-gate 	return (mp);
38857c478bd9Sstevel@tonic-gate }
38867c478bd9Sstevel@tonic-gate 
38877c478bd9Sstevel@tonic-gate /*
388805d90a32SDan McDonald  * Given an mblk and a ptr, find the destination address in an IPv6 routing
388905d90a32SDan McDonald  * header.
389005d90a32SDan McDonald  */
389105d90a32SDan McDonald static in6_addr_t
pluck_out_dst(const mblk_t * mp,uint8_t * whereptr,in6_addr_t oldrv)3892bd670b35SErik Nordmark pluck_out_dst(const mblk_t *mp, uint8_t *whereptr, in6_addr_t oldrv)
389305d90a32SDan McDonald {
389405d90a32SDan McDonald 	ip6_rthdr0_t *rt0;
389505d90a32SDan McDonald 	int segleft, numaddr;
389605d90a32SDan McDonald 	in6_addr_t *ap, rv = oldrv;
389705d90a32SDan McDonald 
389805d90a32SDan McDonald 	rt0 = (ip6_rthdr0_t *)whereptr;
389905d90a32SDan McDonald 	if (rt0->ip6r0_type != 0 && rt0->ip6r0_type != 2) {
390005d90a32SDan McDonald 		DTRACE_PROBE2(pluck_out_dst_unknown_type, mblk_t *, mp,
390105d90a32SDan McDonald 		    uint8_t *, whereptr);
390205d90a32SDan McDonald 		return (rv);
390305d90a32SDan McDonald 	}
390405d90a32SDan McDonald 	segleft = rt0->ip6r0_segleft;
390505d90a32SDan McDonald 	numaddr = rt0->ip6r0_len / 2;
390605d90a32SDan McDonald 
390705d90a32SDan McDonald 	if ((rt0->ip6r0_len & 0x1) ||
3908bd670b35SErik Nordmark 	    (mp != NULL && whereptr + (rt0->ip6r0_len + 1) * 8 > mp->b_wptr) ||
390905d90a32SDan McDonald 	    (segleft > rt0->ip6r0_len / 2)) {
391005d90a32SDan McDonald 		/*
391105d90a32SDan McDonald 		 * Corrupt packet.  Either the routing header length is odd
391205d90a32SDan McDonald 		 * (can't happen) or mismatched compared to the packet, or the
391305d90a32SDan McDonald 		 * number of addresses is.  Return what we can.  This will
391405d90a32SDan McDonald 		 * only be a problem on forwarded packets that get squeezed
391505d90a32SDan McDonald 		 * through an outbound tunnel enforcing IPsec Tunnel Mode.
391605d90a32SDan McDonald 		 */
391705d90a32SDan McDonald 		DTRACE_PROBE2(pluck_out_dst_badpkt, mblk_t *, mp, uint8_t *,
391805d90a32SDan McDonald 		    whereptr);
391905d90a32SDan McDonald 		return (rv);
392005d90a32SDan McDonald 	}
392105d90a32SDan McDonald 
392205d90a32SDan McDonald 	if (segleft != 0) {
392305d90a32SDan McDonald 		ap = (in6_addr_t *)((char *)rt0 + sizeof (*rt0));
392405d90a32SDan McDonald 		rv = ap[numaddr - 1];
392505d90a32SDan McDonald 	}
392605d90a32SDan McDonald 
392705d90a32SDan McDonald 	return (rv);
392805d90a32SDan McDonald }
392905d90a32SDan McDonald 
393005d90a32SDan McDonald /*
39317c478bd9Sstevel@tonic-gate  * Walk through the options to see if there is a routing header.
39327c478bd9Sstevel@tonic-gate  * If present get the destination which is the last address of
39337c478bd9Sstevel@tonic-gate  * the option.
3934bd670b35SErik Nordmark  * mp needs to be provided in cases when the extension headers might span
3935bd670b35SErik Nordmark  * b_cont; mp is never modified by this function.
39367c478bd9Sstevel@tonic-gate  */
39377c478bd9Sstevel@tonic-gate in6_addr_t
ip_get_dst_v6(ip6_t * ip6h,const mblk_t * mp,boolean_t * is_fragment)3938bd670b35SErik Nordmark ip_get_dst_v6(ip6_t *ip6h, const mblk_t *mp, boolean_t *is_fragment)
39397c478bd9Sstevel@tonic-gate {
3940bd670b35SErik Nordmark 	const mblk_t *current_mp = mp;
39417c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
39427c478bd9Sstevel@tonic-gate 	uint8_t *whereptr;
39437c478bd9Sstevel@tonic-gate 	int ehdrlen;
394405d90a32SDan McDonald 	in6_addr_t rv;
39453062294fSDan McDonald 
39463062294fSDan McDonald 	whereptr = (uint8_t *)ip6h;
39473062294fSDan McDonald 	ehdrlen = sizeof (ip6_t);
39483062294fSDan McDonald 
394905d90a32SDan McDonald 	/* We assume at least the IPv6 base header is within one mblk. */
3950bd670b35SErik Nordmark 	ASSERT(mp == NULL ||
3951bd670b35SErik Nordmark 	    (mp->b_rptr <= whereptr && mp->b_wptr >= whereptr + ehdrlen));
395205d90a32SDan McDonald 
395305d90a32SDan McDonald 	rv = ip6h->ip6_dst;
395405d90a32SDan McDonald 	nexthdr = ip6h->ip6_nxt;
395505d90a32SDan McDonald 	if (is_fragment != NULL)
395605d90a32SDan McDonald 		*is_fragment = B_FALSE;
395705d90a32SDan McDonald 
395805d90a32SDan McDonald 	/*
395905d90a32SDan McDonald 	 * We also assume (thanks to ipsec_tun_outbound()'s pullup) that
396005d90a32SDan McDonald 	 * no extension headers will be split across mblks.
396105d90a32SDan McDonald 	 */
396205d90a32SDan McDonald 
396305d90a32SDan McDonald 	while (nexthdr == IPPROTO_HOPOPTS || nexthdr == IPPROTO_DSTOPTS ||
396405d90a32SDan McDonald 	    nexthdr == IPPROTO_ROUTING) {
396505d90a32SDan McDonald 		if (nexthdr == IPPROTO_ROUTING)
396605d90a32SDan McDonald 			rv = pluck_out_dst(current_mp, whereptr, rv);
396705d90a32SDan McDonald 
396805d90a32SDan McDonald 		/*
396905d90a32SDan McDonald 		 * All IPv6 extension headers have the next-header in byte
397005d90a32SDan McDonald 		 * 0, and the (length - 8) in 8-byte-words.
397105d90a32SDan McDonald 		 */
3972bd670b35SErik Nordmark 		while (current_mp != NULL &&
3973bd670b35SErik Nordmark 		    whereptr + ehdrlen >= current_mp->b_wptr) {
39743062294fSDan McDonald 			ehdrlen -= (current_mp->b_wptr - whereptr);
39753062294fSDan McDonald 			current_mp = current_mp->b_cont;
39763062294fSDan McDonald 			if (current_mp == NULL) {
39773062294fSDan McDonald 				/* Bad packet.  Return what we can. */
397805d90a32SDan McDonald 				DTRACE_PROBE3(ip_get_dst_v6_badpkt, mblk_t *,
397905d90a32SDan McDonald 				    mp, mblk_t *, current_mp, ip6_t *, ip6h);
39803062294fSDan McDonald 				goto done;
39813062294fSDan McDonald 			}
39823062294fSDan McDonald 			whereptr = current_mp->b_rptr;
39833062294fSDan McDonald 		}
39843062294fSDan McDonald 		whereptr += ehdrlen;
39853062294fSDan McDonald 
398605d90a32SDan McDonald 		nexthdr = *whereptr;
3987bd670b35SErik Nordmark 		ASSERT(current_mp == NULL || whereptr + 1 < current_mp->b_wptr);
398805d90a32SDan McDonald 		ehdrlen = (*(whereptr + 1) + 1) * 8;
39897c478bd9Sstevel@tonic-gate 	}
39907c478bd9Sstevel@tonic-gate 
39917c478bd9Sstevel@tonic-gate done:
399205d90a32SDan McDonald 	if (nexthdr == IPPROTO_FRAGMENT && is_fragment != NULL)
399305d90a32SDan McDonald 		*is_fragment = B_TRUE;
39947c478bd9Sstevel@tonic-gate 	return (rv);
39957c478bd9Sstevel@tonic-gate }
39967c478bd9Sstevel@tonic-gate 
39977c478bd9Sstevel@tonic-gate /*
39987c478bd9Sstevel@tonic-gate  * ip_source_routed_v6:
3999bd670b35SErik Nordmark  * This function is called by redirect code (called from ip_input_v6) to
40007c478bd9Sstevel@tonic-gate  * know whether this packet is source routed through this node i.e
40017c478bd9Sstevel@tonic-gate  * whether this node (router) is part of the journey. This
40027c478bd9Sstevel@tonic-gate  * function is called under two cases :
40037c478bd9Sstevel@tonic-gate  *
40047c478bd9Sstevel@tonic-gate  * case 1 : Routing header was processed by this node and
40057c478bd9Sstevel@tonic-gate  *	    ip_process_rthdr replaced ip6_dst with the next hop
40067c478bd9Sstevel@tonic-gate  *	    and we are forwarding the packet to the next hop.
40077c478bd9Sstevel@tonic-gate  *
40087c478bd9Sstevel@tonic-gate  * case 2 : Routing header was not processed by this node and we
40097c478bd9Sstevel@tonic-gate  *	    are just forwarding the packet.
40107c478bd9Sstevel@tonic-gate  *
40117c478bd9Sstevel@tonic-gate  * For case (1) we don't want to send redirects. For case(2) we
40127c478bd9Sstevel@tonic-gate  * want to send redirects.
40137c478bd9Sstevel@tonic-gate  */
40147c478bd9Sstevel@tonic-gate static boolean_t
ip_source_routed_v6(ip6_t * ip6h,mblk_t * mp,ip_stack_t * ipst)4015f4b3ec61Sdh155122 ip_source_routed_v6(ip6_t *ip6h, mblk_t *mp, ip_stack_t *ipst)
40167c478bd9Sstevel@tonic-gate {
40177c478bd9Sstevel@tonic-gate 	uint8_t		nexthdr;
40187c478bd9Sstevel@tonic-gate 	in6_addr_t	*addrptr;
40197c478bd9Sstevel@tonic-gate 	ip6_rthdr0_t	*rthdr;
40207c478bd9Sstevel@tonic-gate 	uint8_t		numaddr;
40217c478bd9Sstevel@tonic-gate 	ip6_hbh_t	*hbhhdr;
40227c478bd9Sstevel@tonic-gate 	uint_t		ehdrlen;
40237c478bd9Sstevel@tonic-gate 	uint8_t		*byteptr;
40247c478bd9Sstevel@tonic-gate 
40257c478bd9Sstevel@tonic-gate 	ip2dbg(("ip_source_routed_v6\n"));
40267c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
40277c478bd9Sstevel@tonic-gate 	ehdrlen = IPV6_HDR_LEN;
40287c478bd9Sstevel@tonic-gate 
40297c478bd9Sstevel@tonic-gate 	/* if a routing hdr is preceeded by HOPOPT or DSTOPT */
40307c478bd9Sstevel@tonic-gate 	while (nexthdr == IPPROTO_HOPOPTS ||
40317c478bd9Sstevel@tonic-gate 	    nexthdr == IPPROTO_DSTOPTS) {
40327c478bd9Sstevel@tonic-gate 		byteptr = (uint8_t *)ip6h + ehdrlen;
40337c478bd9Sstevel@tonic-gate 		/*
40347c478bd9Sstevel@tonic-gate 		 * Check if we have already processed
40357c478bd9Sstevel@tonic-gate 		 * packets or we are just a forwarding
40367c478bd9Sstevel@tonic-gate 		 * router which only pulled up msgs up
40377c478bd9Sstevel@tonic-gate 		 * to IPV6HDR and  one HBH ext header
40387c478bd9Sstevel@tonic-gate 		 */
40397c478bd9Sstevel@tonic-gate 		if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
40407c478bd9Sstevel@tonic-gate 			ip2dbg(("ip_source_routed_v6: Extension"
40417c478bd9Sstevel@tonic-gate 			    " headers not processed\n"));
40427c478bd9Sstevel@tonic-gate 			return (B_FALSE);
40437c478bd9Sstevel@tonic-gate 		}
40447c478bd9Sstevel@tonic-gate 		hbhhdr = (ip6_hbh_t *)byteptr;
40457c478bd9Sstevel@tonic-gate 		nexthdr = hbhhdr->ip6h_nxt;
40467c478bd9Sstevel@tonic-gate 		ehdrlen = ehdrlen + 8 * (hbhhdr->ip6h_len + 1);
40477c478bd9Sstevel@tonic-gate 	}
40487c478bd9Sstevel@tonic-gate 	switch (nexthdr) {
40497c478bd9Sstevel@tonic-gate 	case IPPROTO_ROUTING:
40507c478bd9Sstevel@tonic-gate 		byteptr = (uint8_t *)ip6h + ehdrlen;
40517c478bd9Sstevel@tonic-gate 		/*
40527c478bd9Sstevel@tonic-gate 		 * If for some reason, we haven't pulled up
40537c478bd9Sstevel@tonic-gate 		 * the routing hdr data mblk, then we must
40547c478bd9Sstevel@tonic-gate 		 * not have processed it at all. So for sure
40557c478bd9Sstevel@tonic-gate 		 * we are not part of the source routed journey.
40567c478bd9Sstevel@tonic-gate 		 */
40577c478bd9Sstevel@tonic-gate 		if (byteptr + MIN_EHDR_LEN > mp->b_wptr) {
40587c478bd9Sstevel@tonic-gate 			ip2dbg(("ip_source_routed_v6: Routing"
40597c478bd9Sstevel@tonic-gate 			    " header not processed\n"));
40607c478bd9Sstevel@tonic-gate 			return (B_FALSE);
40617c478bd9Sstevel@tonic-gate 		}
40627c478bd9Sstevel@tonic-gate 		rthdr = (ip6_rthdr0_t *)byteptr;
40637c478bd9Sstevel@tonic-gate 		/*
40647c478bd9Sstevel@tonic-gate 		 * Either we are an intermediate router or the
40657c478bd9Sstevel@tonic-gate 		 * last hop before destination and we have
40667c478bd9Sstevel@tonic-gate 		 * already processed the routing header.
40677c478bd9Sstevel@tonic-gate 		 * If segment_left is greater than or equal to zero,
40687c478bd9Sstevel@tonic-gate 		 * then we must be the (numaddr - segleft) entry
40697c478bd9Sstevel@tonic-gate 		 * of the routing header. Although ip6r0_segleft
40707c478bd9Sstevel@tonic-gate 		 * is a unit8_t variable, we still check for zero
40717c478bd9Sstevel@tonic-gate 		 * or greater value, if in case the data type
40727c478bd9Sstevel@tonic-gate 		 * is changed someday in future.
40737c478bd9Sstevel@tonic-gate 		 */
40747c478bd9Sstevel@tonic-gate 		if (rthdr->ip6r0_segleft > 0 ||
40757c478bd9Sstevel@tonic-gate 		    rthdr->ip6r0_segleft == 0) {
40767c478bd9Sstevel@tonic-gate 			numaddr = rthdr->ip6r0_len / 2;
40777c478bd9Sstevel@tonic-gate 			addrptr = (in6_addr_t *)((char *)rthdr +
40787c478bd9Sstevel@tonic-gate 			    sizeof (*rthdr));
40797c478bd9Sstevel@tonic-gate 			addrptr += (numaddr - (rthdr->ip6r0_segleft + 1));
40807c478bd9Sstevel@tonic-gate 			if (addrptr != NULL) {
4081bd670b35SErik Nordmark 				if (ip_type_v6(addrptr, ipst) == IRE_LOCAL)
40827c478bd9Sstevel@tonic-gate 					return (B_TRUE);
4083bd670b35SErik Nordmark 				ip1dbg(("ip_source_routed_v6: Not local\n"));
40847c478bd9Sstevel@tonic-gate 			}
40857c478bd9Sstevel@tonic-gate 		}
40867c478bd9Sstevel@tonic-gate 	/* FALLTHRU */
40877c478bd9Sstevel@tonic-gate 	default:
40887c478bd9Sstevel@tonic-gate 		ip2dbg(("ip_source_routed_v6: Not source routed here\n"));
40897c478bd9Sstevel@tonic-gate 		return (B_FALSE);
40907c478bd9Sstevel@tonic-gate 	}
40917c478bd9Sstevel@tonic-gate }
40927c478bd9Sstevel@tonic-gate 
40937c478bd9Sstevel@tonic-gate /*
40947c478bd9Sstevel@tonic-gate  * IPv6 fragmentation.  Essentially the same as IPv4 fragmentation.
40957c478bd9Sstevel@tonic-gate  * We have not optimized this in terms of number of mblks
40967c478bd9Sstevel@tonic-gate  * allocated. For instance, for each fragment sent we always allocate a
40977c478bd9Sstevel@tonic-gate  * mblk to hold the IPv6 header and fragment header.
40987c478bd9Sstevel@tonic-gate  *
4099bd670b35SErik Nordmark  * Assumes that all the extension headers are contained in the first mblk
4100bd670b35SErik Nordmark  * and that the fragment header has has already been added by calling
4101bd670b35SErik Nordmark  * ip_fraghdr_add_v6.
41027c478bd9Sstevel@tonic-gate  */
4103bd670b35SErik Nordmark int
ip_fragment_v6(mblk_t * mp,nce_t * nce,iaflags_t ixaflags,uint_t pkt_len,uint32_t max_frag,uint32_t xmit_hint,zoneid_t szone,zoneid_t nolzid,pfirepostfrag_t postfragfn,uintptr_t * ixa_cookie)4104bd670b35SErik Nordmark ip_fragment_v6(mblk_t *mp, nce_t *nce, iaflags_t ixaflags, uint_t pkt_len,
4105bd670b35SErik Nordmark     uint32_t max_frag, uint32_t xmit_hint, zoneid_t szone, zoneid_t nolzid,
4106bd670b35SErik Nordmark     pfirepostfrag_t postfragfn, uintptr_t *ixa_cookie)
41077c478bd9Sstevel@tonic-gate {
41087c478bd9Sstevel@tonic-gate 	ip6_t		*ip6h = (ip6_t *)mp->b_rptr;
41097c478bd9Sstevel@tonic-gate 	ip6_t		*fip6h;
41107c478bd9Sstevel@tonic-gate 	mblk_t		*hmp;
41117c478bd9Sstevel@tonic-gate 	mblk_t		*hmp0;
41127c478bd9Sstevel@tonic-gate 	mblk_t		*dmp;
41137c478bd9Sstevel@tonic-gate 	ip6_frag_t	*fraghdr;
41147c478bd9Sstevel@tonic-gate 	size_t		unfragmentable_len;
41157c478bd9Sstevel@tonic-gate 	size_t		mlen;
41167c478bd9Sstevel@tonic-gate 	size_t		max_chunk;
41177c478bd9Sstevel@tonic-gate 	uint16_t	off_flags;
41187c478bd9Sstevel@tonic-gate 	uint16_t	offset = 0;
4119bd670b35SErik Nordmark 	ill_t		*ill = nce->nce_ill;
4120bd670b35SErik Nordmark 	uint8_t		nexthdr;
4121bd670b35SErik Nordmark 	uint8_t		*ptr;
4122bd670b35SErik Nordmark 	ip_stack_t	*ipst = ill->ill_ipst;
4123bd670b35SErik Nordmark 	uint_t		priority = mp->b_band;
4124bd670b35SErik Nordmark 	int		error = 0;
4125bd670b35SErik Nordmark 
4126bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragReqds);
4127bd670b35SErik Nordmark 	if (max_frag == 0) {
4128bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
4129bd670b35SErik Nordmark 		ip_drop_output("FragFails: zero max_frag", mp, ill);
4130bd670b35SErik Nordmark 		freemsg(mp);
4131bd670b35SErik Nordmark 		return (EINVAL);
4132bd670b35SErik Nordmark 	}
4133bd670b35SErik Nordmark 
4134bd670b35SErik Nordmark 	/*
4135bd670b35SErik Nordmark 	 * Caller should have added fraghdr_t to pkt_len, and also
4136bd670b35SErik Nordmark 	 * updated ip6_plen.
4137bd670b35SErik Nordmark 	 */
4138bd670b35SErik Nordmark 	ASSERT(ntohs(ip6h->ip6_plen) + IPV6_HDR_LEN == pkt_len);
4139bd670b35SErik Nordmark 	ASSERT(msgdsize(mp) == pkt_len);
4140bd670b35SErik Nordmark 
4141bd670b35SErik Nordmark 	/*
4142bd670b35SErik Nordmark 	 * Determine the length of the unfragmentable portion of this
4143bd670b35SErik Nordmark 	 * datagram.  This consists of the IPv6 header, a potential
4144bd670b35SErik Nordmark 	 * hop-by-hop options header, a potential pre-routing-header
4145bd670b35SErik Nordmark 	 * destination options header, and a potential routing header.
4146bd670b35SErik Nordmark 	 */
4147bd670b35SErik Nordmark 	nexthdr = ip6h->ip6_nxt;
4148bd670b35SErik Nordmark 	ptr = (uint8_t *)&ip6h[1];
4149bd670b35SErik Nordmark 
4150bd670b35SErik Nordmark 	if (nexthdr == IPPROTO_HOPOPTS) {
4151bd670b35SErik Nordmark 		ip6_hbh_t	*hbh_hdr;
4152bd670b35SErik Nordmark 		uint_t		hdr_len;
4153bd670b35SErik Nordmark 
4154bd670b35SErik Nordmark 		hbh_hdr = (ip6_hbh_t *)ptr;
4155bd670b35SErik Nordmark 		hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
4156bd670b35SErik Nordmark 		nexthdr = hbh_hdr->ip6h_nxt;
4157bd670b35SErik Nordmark 		ptr += hdr_len;
4158bd670b35SErik Nordmark 	}
4159bd670b35SErik Nordmark 	if (nexthdr == IPPROTO_DSTOPTS) {
4160bd670b35SErik Nordmark 		ip6_dest_t	*dest_hdr;
4161bd670b35SErik Nordmark 		uint_t		hdr_len;
4162bd670b35SErik Nordmark 
4163bd670b35SErik Nordmark 		dest_hdr = (ip6_dest_t *)ptr;
4164bd670b35SErik Nordmark 		if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
4165bd670b35SErik Nordmark 			hdr_len = 8 * (dest_hdr->ip6d_len + 1);
4166bd670b35SErik Nordmark 			nexthdr = dest_hdr->ip6d_nxt;
4167bd670b35SErik Nordmark 			ptr += hdr_len;
4168bd670b35SErik Nordmark 		}
4169bd670b35SErik Nordmark 	}
4170bd670b35SErik Nordmark 	if (nexthdr == IPPROTO_ROUTING) {
4171bd670b35SErik Nordmark 		ip6_rthdr_t	*rthdr;
4172bd670b35SErik Nordmark 		uint_t		hdr_len;
4173bd670b35SErik Nordmark 
4174bd670b35SErik Nordmark 		rthdr = (ip6_rthdr_t *)ptr;
4175bd670b35SErik Nordmark 		nexthdr = rthdr->ip6r_nxt;
4176bd670b35SErik Nordmark 		hdr_len = 8 * (rthdr->ip6r_len + 1);
4177bd670b35SErik Nordmark 		ptr += hdr_len;
4178bd670b35SErik Nordmark 	}
4179bd670b35SErik Nordmark 	if (nexthdr != IPPROTO_FRAGMENT) {
4180bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
4181bd670b35SErik Nordmark 		ip_drop_output("FragFails: bad nexthdr", mp, ill);
4182bd670b35SErik Nordmark 		freemsg(mp);
4183bd670b35SErik Nordmark 		return (EINVAL);
4184bd670b35SErik Nordmark 	}
4185bd670b35SErik Nordmark 	unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
4186bd670b35SErik Nordmark 	unfragmentable_len += sizeof (ip6_frag_t);
4187bd670b35SErik Nordmark 
4188bd670b35SErik Nordmark 	max_chunk = (max_frag - unfragmentable_len) & ~7;
4189bd670b35SErik Nordmark 
4190bd670b35SErik Nordmark 	/*
4191bd670b35SErik Nordmark 	 * Allocate an mblk with enough room for the link-layer
4192bd670b35SErik Nordmark 	 * header and the unfragmentable part of the datagram, which includes
4193bd670b35SErik Nordmark 	 * the fragment header.  This (or a copy) will be used as the
4194bd670b35SErik Nordmark 	 * first mblk for each fragment we send.
4195bd670b35SErik Nordmark 	 */
4196bd670b35SErik Nordmark 	hmp = allocb_tmpl(unfragmentable_len + ipst->ips_ip_wroff_extra, mp);
4197bd670b35SErik Nordmark 	if (hmp == NULL) {
4198bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
4199bd670b35SErik Nordmark 		ip_drop_output("FragFails: no hmp", mp, ill);
4200bd670b35SErik Nordmark 		freemsg(mp);
4201bd670b35SErik Nordmark 		return (ENOBUFS);
4202bd670b35SErik Nordmark 	}
4203bd670b35SErik Nordmark 	hmp->b_rptr += ipst->ips_ip_wroff_extra;
4204bd670b35SErik Nordmark 	hmp->b_wptr = hmp->b_rptr + unfragmentable_len;
4205bd670b35SErik Nordmark 
4206bd670b35SErik Nordmark 	fip6h = (ip6_t *)hmp->b_rptr;
4207bd670b35SErik Nordmark 	bcopy(ip6h, fip6h, unfragmentable_len);
4208bd670b35SErik Nordmark 
4209bd670b35SErik Nordmark 	/*
4210bd670b35SErik Nordmark 	 * pkt_len is set to the total length of the fragmentable data in this
4211bd670b35SErik Nordmark 	 * datagram.  For each fragment sent, we will decrement pkt_len
4212bd670b35SErik Nordmark 	 * by the amount of fragmentable data sent in that fragment
4213bd670b35SErik Nordmark 	 * until len reaches zero.
4214bd670b35SErik Nordmark 	 */
4215bd670b35SErik Nordmark 	pkt_len -= unfragmentable_len;
4216bd670b35SErik Nordmark 
4217bd670b35SErik Nordmark 	/*
4218bd670b35SErik Nordmark 	 * Move read ptr past unfragmentable portion, we don't want this part
4219bd670b35SErik Nordmark 	 * of the data in our fragments.
4220bd670b35SErik Nordmark 	 */
4221bd670b35SErik Nordmark 	mp->b_rptr += unfragmentable_len;
4222bd670b35SErik Nordmark 	if (mp->b_rptr == mp->b_wptr) {
4223bd670b35SErik Nordmark 		mblk_t *mp1 = mp->b_cont;
4224bd670b35SErik Nordmark 		freeb(mp);
4225bd670b35SErik Nordmark 		mp = mp1;
4226bd670b35SErik Nordmark 	}
4227bd670b35SErik Nordmark 
4228bd670b35SErik Nordmark 	while (pkt_len != 0) {
4229bd670b35SErik Nordmark 		mlen = MIN(pkt_len, max_chunk);
4230bd670b35SErik Nordmark 		pkt_len -= mlen;
4231bd670b35SErik Nordmark 		if (pkt_len != 0) {
4232bd670b35SErik Nordmark 			/* Not last */
4233bd670b35SErik Nordmark 			hmp0 = copyb(hmp);
4234bd670b35SErik Nordmark 			if (hmp0 == NULL) {
4235bd670b35SErik Nordmark 				BUMP_MIB(ill->ill_ip_mib,
4236bd670b35SErik Nordmark 				    ipIfStatsOutFragFails);
4237bd670b35SErik Nordmark 				ip_drop_output("FragFails: copyb failed",
4238bd670b35SErik Nordmark 				    mp, ill);
4239bd670b35SErik Nordmark 				freeb(hmp);
4240bd670b35SErik Nordmark 				freemsg(mp);
4241bd670b35SErik Nordmark 				ip1dbg(("ip_fragment_v6: copyb failed\n"));
4242bd670b35SErik Nordmark 				return (ENOBUFS);
4243bd670b35SErik Nordmark 			}
4244bd670b35SErik Nordmark 			off_flags = IP6F_MORE_FRAG;
4245bd670b35SErik Nordmark 		} else {
4246bd670b35SErik Nordmark 			/* Last fragment */
4247bd670b35SErik Nordmark 			hmp0 = hmp;
4248bd670b35SErik Nordmark 			hmp = NULL;
4249bd670b35SErik Nordmark 			off_flags = 0;
4250bd670b35SErik Nordmark 		}
4251bd670b35SErik Nordmark 		fip6h = (ip6_t *)(hmp0->b_rptr);
4252bd670b35SErik Nordmark 		fraghdr = (ip6_frag_t *)(hmp0->b_rptr + unfragmentable_len -
4253bd670b35SErik Nordmark 		    sizeof (ip6_frag_t));
4254bd670b35SErik Nordmark 
4255bd670b35SErik Nordmark 		fip6h->ip6_plen = htons((uint16_t)(mlen +
4256bd670b35SErik Nordmark 		    unfragmentable_len - IPV6_HDR_LEN));
4257bd670b35SErik Nordmark 		/*
4258bd670b35SErik Nordmark 		 * Note: Optimization alert.
4259bd670b35SErik Nordmark 		 * In IPv6 (and IPv4) protocol header, Fragment Offset
4260bd670b35SErik Nordmark 		 * ("offset") is 13 bits wide and in 8-octet units.
4261bd670b35SErik Nordmark 		 * In IPv6 protocol header (unlike IPv4) in a 16 bit field,
4262bd670b35SErik Nordmark 		 * it occupies the most significant 13 bits.
4263bd670b35SErik Nordmark 		 * (least significant 13 bits in IPv4).
4264bd670b35SErik Nordmark 		 * We do not do any shifts here. Not shifting is same effect
4265bd670b35SErik Nordmark 		 * as taking offset value in octet units, dividing by 8 and
4266bd670b35SErik Nordmark 		 * then shifting 3 bits left to line it up in place in proper
4267bd670b35SErik Nordmark 		 * place protocol header.
4268bd670b35SErik Nordmark 		 */
4269bd670b35SErik Nordmark 		fraghdr->ip6f_offlg = htons(offset) | off_flags;
4270bd670b35SErik Nordmark 
4271bd670b35SErik Nordmark 		if (!(dmp = ip_carve_mp(&mp, mlen))) {
4272bd670b35SErik Nordmark 			/* mp has already been freed by ip_carve_mp() */
4273bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
4274bd670b35SErik Nordmark 			ip_drop_output("FragFails: could not carve mp",
4275bd670b35SErik Nordmark 			    hmp0, ill);
4276bd670b35SErik Nordmark 			if (hmp != NULL)
4277bd670b35SErik Nordmark 				freeb(hmp);
4278bd670b35SErik Nordmark 			freeb(hmp0);
4279bd670b35SErik Nordmark 			ip1dbg(("ip_carve_mp: failed\n"));
4280bd670b35SErik Nordmark 			return (ENOBUFS);
4281bd670b35SErik Nordmark 		}
4282bd670b35SErik Nordmark 		hmp0->b_cont = dmp;
4283bd670b35SErik Nordmark 		/* Get the priority marking, if any */
4284bd670b35SErik Nordmark 		hmp0->b_band = priority;
4285bd670b35SErik Nordmark 
4286bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragCreates);
4287bd670b35SErik Nordmark 
4288bd670b35SErik Nordmark 		error = postfragfn(hmp0, nce, ixaflags,
4289bd670b35SErik Nordmark 		    mlen + unfragmentable_len, xmit_hint, szone, nolzid,
4290bd670b35SErik Nordmark 		    ixa_cookie);
4291bd670b35SErik Nordmark 		if (error != 0 && error != EWOULDBLOCK && hmp != NULL) {
4292bd670b35SErik Nordmark 			/* No point in sending the other fragments */
4293bd670b35SErik Nordmark 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragFails);
4294bd670b35SErik Nordmark 			ip_drop_output("FragFails: postfragfn failed",
4295bd670b35SErik Nordmark 			    hmp, ill);
4296bd670b35SErik Nordmark 			freeb(hmp);
4297bd670b35SErik Nordmark 			freemsg(mp);
4298bd670b35SErik Nordmark 			return (error);
4299bd670b35SErik Nordmark 		}
4300bd670b35SErik Nordmark 		/* No need to redo state machine in loop */
4301bd670b35SErik Nordmark 		ixaflags &= ~IXAF_REACH_CONF;
4302bd670b35SErik Nordmark 
4303bd670b35SErik Nordmark 		offset += mlen;
4304bd670b35SErik Nordmark 	}
4305bd670b35SErik Nordmark 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutFragOKs);
4306bd670b35SErik Nordmark 	return (error);
4307bd670b35SErik Nordmark }
4308bd670b35SErik Nordmark 
4309bd670b35SErik Nordmark /*
4310bd670b35SErik Nordmark  * Add a fragment header to an IPv6 packet.
4311bd670b35SErik Nordmark  * Assumes that all the extension headers are contained in the first mblk.
4312bd670b35SErik Nordmark  *
4313bd670b35SErik Nordmark  * The fragment header is inserted after an hop-by-hop options header
4314bd670b35SErik Nordmark  * and after [an optional destinations header followed by] a routing header.
4315bd670b35SErik Nordmark  */
4316bd670b35SErik Nordmark mblk_t *
ip_fraghdr_add_v6(mblk_t * mp,uint32_t ident,ip_xmit_attr_t * ixa)4317bd670b35SErik Nordmark ip_fraghdr_add_v6(mblk_t *mp, uint32_t ident, ip_xmit_attr_t *ixa)
4318bd670b35SErik Nordmark {
4319bd670b35SErik Nordmark 	ip6_t		*ip6h = (ip6_t *)mp->b_rptr;
4320bd670b35SErik Nordmark 	ip6_t		*fip6h;
4321bd670b35SErik Nordmark 	mblk_t		*hmp;
4322bd670b35SErik Nordmark 	ip6_frag_t	*fraghdr;
4323bd670b35SErik Nordmark 	size_t		unfragmentable_len;
43247c478bd9Sstevel@tonic-gate 	uint8_t		nexthdr;
43257c478bd9Sstevel@tonic-gate 	uint_t		prev_nexthdr_offset;
43267c478bd9Sstevel@tonic-gate 	uint8_t		*ptr;
4327bd670b35SErik Nordmark 	uint_t		priority = mp->b_band;
4328bd670b35SErik Nordmark 	ip_stack_t	*ipst = ixa->ixa_ipst;
43293173664eSapersson 
43307c478bd9Sstevel@tonic-gate 	/*
43317c478bd9Sstevel@tonic-gate 	 * Determine the length of the unfragmentable portion of this
43327c478bd9Sstevel@tonic-gate 	 * datagram.  This consists of the IPv6 header, a potential
43337c478bd9Sstevel@tonic-gate 	 * hop-by-hop options header, a potential pre-routing-header
43347c478bd9Sstevel@tonic-gate 	 * destination options header, and a potential routing header.
43357c478bd9Sstevel@tonic-gate 	 */
43367c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
43377c478bd9Sstevel@tonic-gate 	prev_nexthdr_offset = (uint8_t *)&ip6h->ip6_nxt - (uint8_t *)ip6h;
43387c478bd9Sstevel@tonic-gate 	ptr = (uint8_t *)&ip6h[1];
43397c478bd9Sstevel@tonic-gate 
43407c478bd9Sstevel@tonic-gate 	if (nexthdr == IPPROTO_HOPOPTS) {
43417c478bd9Sstevel@tonic-gate 		ip6_hbh_t	*hbh_hdr;
43427c478bd9Sstevel@tonic-gate 		uint_t		hdr_len;
43437c478bd9Sstevel@tonic-gate 
43447c478bd9Sstevel@tonic-gate 		hbh_hdr = (ip6_hbh_t *)ptr;
43457c478bd9Sstevel@tonic-gate 		hdr_len = 8 * (hbh_hdr->ip6h_len + 1);
43467c478bd9Sstevel@tonic-gate 		nexthdr = hbh_hdr->ip6h_nxt;
43477c478bd9Sstevel@tonic-gate 		prev_nexthdr_offset = (uint8_t *)&hbh_hdr->ip6h_nxt
43487c478bd9Sstevel@tonic-gate 		    - (uint8_t *)ip6h;
43497c478bd9Sstevel@tonic-gate 		ptr += hdr_len;
43507c478bd9Sstevel@tonic-gate 	}
43517c478bd9Sstevel@tonic-gate 	if (nexthdr == IPPROTO_DSTOPTS) {
43527c478bd9Sstevel@tonic-gate 		ip6_dest_t	*dest_hdr;
43537c478bd9Sstevel@tonic-gate 		uint_t		hdr_len;
43547c478bd9Sstevel@tonic-gate 
43557c478bd9Sstevel@tonic-gate 		dest_hdr = (ip6_dest_t *)ptr;
43567c478bd9Sstevel@tonic-gate 		if (dest_hdr->ip6d_nxt == IPPROTO_ROUTING) {
43577c478bd9Sstevel@tonic-gate 			hdr_len = 8 * (dest_hdr->ip6d_len + 1);
43587c478bd9Sstevel@tonic-gate 			nexthdr = dest_hdr->ip6d_nxt;
43597c478bd9Sstevel@tonic-gate 			prev_nexthdr_offset = (uint8_t *)&dest_hdr->ip6d_nxt
43607c478bd9Sstevel@tonic-gate 			    - (uint8_t *)ip6h;
43617c478bd9Sstevel@tonic-gate 			ptr += hdr_len;
43627c478bd9Sstevel@tonic-gate 		}
43637c478bd9Sstevel@tonic-gate 	}
43647c478bd9Sstevel@tonic-gate 	if (nexthdr == IPPROTO_ROUTING) {
43657c478bd9Sstevel@tonic-gate 		ip6_rthdr_t	*rthdr;
43667c478bd9Sstevel@tonic-gate 		uint_t		hdr_len;
43677c478bd9Sstevel@tonic-gate 
43687c478bd9Sstevel@tonic-gate 		rthdr = (ip6_rthdr_t *)ptr;
43697c478bd9Sstevel@tonic-gate 		nexthdr = rthdr->ip6r_nxt;
43707c478bd9Sstevel@tonic-gate 		prev_nexthdr_offset = (uint8_t *)&rthdr->ip6r_nxt
43717c478bd9Sstevel@tonic-gate 		    - (uint8_t *)ip6h;
43727c478bd9Sstevel@tonic-gate 		hdr_len = 8 * (rthdr->ip6r_len + 1);
43737c478bd9Sstevel@tonic-gate 		ptr += hdr_len;
43747c478bd9Sstevel@tonic-gate 	}
43757c478bd9Sstevel@tonic-gate 	unfragmentable_len = (uint_t)(ptr - (uint8_t *)ip6h);
43767c478bd9Sstevel@tonic-gate 
43777c478bd9Sstevel@tonic-gate 	/*
43787c478bd9Sstevel@tonic-gate 	 * Allocate an mblk with enough room for the link-layer
43797c478bd9Sstevel@tonic-gate 	 * header, the unfragmentable part of the datagram, and the
4380bd670b35SErik Nordmark 	 * fragment header.
43817c478bd9Sstevel@tonic-gate 	 */
4382de8c4a14SErik Nordmark 	hmp = allocb_tmpl(unfragmentable_len + sizeof (ip6_frag_t) +
4383de8c4a14SErik Nordmark 	    ipst->ips_ip_wroff_extra, mp);
43847c478bd9Sstevel@tonic-gate 	if (hmp == NULL) {
4385bd670b35SErik Nordmark 		ill_t *ill = ixa->ixa_nce->nce_ill;
4386bd670b35SErik Nordmark 
4387bd670b35SErik Nordmark 		BUMP_MIB(ill->ill_ip_mib, ipIfStatsOutDiscards);
4388bd670b35SErik Nordmark 		ip_drop_output("ipIfStatsOutDiscards: allocb failure", mp, ill);
43897c478bd9Sstevel@tonic-gate 		freemsg(mp);
4390bd670b35SErik Nordmark 		return (NULL);
43917c478bd9Sstevel@tonic-gate 	}
4392f4b3ec61Sdh155122 	hmp->b_rptr += ipst->ips_ip_wroff_extra;
43937c478bd9Sstevel@tonic-gate 	hmp->b_wptr = hmp->b_rptr + unfragmentable_len + sizeof (ip6_frag_t);
43947c478bd9Sstevel@tonic-gate 
43957c478bd9Sstevel@tonic-gate 	fip6h = (ip6_t *)hmp->b_rptr;
43967c478bd9Sstevel@tonic-gate 	fraghdr = (ip6_frag_t *)(hmp->b_rptr + unfragmentable_len);
43977c478bd9Sstevel@tonic-gate 
43987c478bd9Sstevel@tonic-gate 	bcopy(ip6h, fip6h, unfragmentable_len);
4399bd670b35SErik Nordmark 	fip6h->ip6_plen = htons(ntohs(fip6h->ip6_plen) + sizeof (ip6_frag_t));
44007c478bd9Sstevel@tonic-gate 	hmp->b_rptr[prev_nexthdr_offset] = IPPROTO_FRAGMENT;
44017c478bd9Sstevel@tonic-gate 
44027c478bd9Sstevel@tonic-gate 	fraghdr->ip6f_nxt = nexthdr;
44037c478bd9Sstevel@tonic-gate 	fraghdr->ip6f_reserved = 0;
4404ff550d0eSmasputra 	fraghdr->ip6f_offlg = 0;
44057c478bd9Sstevel@tonic-gate 	fraghdr->ip6f_ident = htonl(ident);
44067c478bd9Sstevel@tonic-gate 
4407bd670b35SErik Nordmark 	/* Get the priority marking, if any */
4408bd670b35SErik Nordmark 	hmp->b_band = priority;
44097c478bd9Sstevel@tonic-gate 
44107c478bd9Sstevel@tonic-gate 	/*
44117c478bd9Sstevel@tonic-gate 	 * Move read ptr past unfragmentable portion, we don't want this part
44127c478bd9Sstevel@tonic-gate 	 * of the data in our fragments.
44137c478bd9Sstevel@tonic-gate 	 */
44147c478bd9Sstevel@tonic-gate 	mp->b_rptr += unfragmentable_len;
4415bd670b35SErik Nordmark 	hmp->b_cont = mp;
4416bd670b35SErik Nordmark 	return (hmp);
44177c478bd9Sstevel@tonic-gate }
44187c478bd9Sstevel@tonic-gate 
44197c478bd9Sstevel@tonic-gate /*
44207c478bd9Sstevel@tonic-gate  * Determine if the ill and multicast aspects of that packets
44217c478bd9Sstevel@tonic-gate  * "matches" the conn.
44227c478bd9Sstevel@tonic-gate  */
44237c478bd9Sstevel@tonic-gate boolean_t
conn_wantpacket_v6(conn_t * connp,ip_recv_attr_t * ira,ip6_t * ip6h)4424bd670b35SErik Nordmark conn_wantpacket_v6(conn_t *connp, ip_recv_attr_t *ira, ip6_t *ip6h)
44257c478bd9Sstevel@tonic-gate {
4426bd670b35SErik Nordmark 	ill_t		*ill = ira->ira_rill;
4427bd670b35SErik Nordmark 	zoneid_t	zoneid = ira->ira_zoneid;
4428bd670b35SErik Nordmark 	uint_t		in_ifindex;
44297c478bd9Sstevel@tonic-gate 	in6_addr_t	*v6dst_ptr = &ip6h->ip6_dst;
44307c478bd9Sstevel@tonic-gate 	in6_addr_t	*v6src_ptr = &ip6h->ip6_src;
44317c478bd9Sstevel@tonic-gate 
44327c478bd9Sstevel@tonic-gate 	/*
4433bd670b35SErik Nordmark 	 * conn_incoming_ifindex is set by IPV6_BOUND_IF and as link-local
4434bd670b35SErik Nordmark 	 * scopeid. This is used to limit
4435bd670b35SErik Nordmark 	 * unicast and multicast reception to conn_incoming_ifindex.
44367c478bd9Sstevel@tonic-gate 	 * conn_wantpacket_v6 is called both for unicast and
4437bd670b35SErik Nordmark 	 * multicast packets.
44387c478bd9Sstevel@tonic-gate 	 */
4439bd670b35SErik Nordmark 	in_ifindex = connp->conn_incoming_ifindex;
4440bd670b35SErik Nordmark 
4441bd670b35SErik Nordmark 	/* mpathd can bind to the under IPMP interface, which we allow */
4442bd670b35SErik Nordmark 	if (in_ifindex != 0 && in_ifindex != ill->ill_phyint->phyint_ifindex) {
4443bd670b35SErik Nordmark 		if (!IS_UNDER_IPMP(ill))
44447c478bd9Sstevel@tonic-gate 			return (B_FALSE);
4445bd670b35SErik Nordmark 
4446bd670b35SErik Nordmark 		if (in_ifindex != ipmp_ill_get_ipmp_ifindex(ill))
4447e11c3f44Smeem 			return (B_FALSE);
4448e11c3f44Smeem 	}
4449bd670b35SErik Nordmark 
4450bd670b35SErik Nordmark 	if (!IPCL_ZONE_MATCH(connp, zoneid))
4451bd670b35SErik Nordmark 		return (B_FALSE);
4452bd670b35SErik Nordmark 
4453bd670b35SErik Nordmark 	if (!(ira->ira_flags & IRAF_MULTICAST))
4454bd670b35SErik Nordmark 		return (B_TRUE);
44557c478bd9Sstevel@tonic-gate 
44567c478bd9Sstevel@tonic-gate 	if (connp->conn_multi_router)
44577c478bd9Sstevel@tonic-gate 		return (B_TRUE);
44587c478bd9Sstevel@tonic-gate 
4459bd670b35SErik Nordmark 	if (ira->ira_protocol == IPPROTO_RSVP)
4460bd670b35SErik Nordmark 		return (B_TRUE);
44617c478bd9Sstevel@tonic-gate 
4462bd670b35SErik Nordmark 	return (conn_hasmembers_ill_withsrc_v6(connp, v6dst_ptr, v6src_ptr,
4463bd670b35SErik Nordmark 	    ira->ira_ill));
44647c478bd9Sstevel@tonic-gate }
44657c478bd9Sstevel@tonic-gate 
44667c478bd9Sstevel@tonic-gate /*
44677c478bd9Sstevel@tonic-gate  * pr_addr_dbg function provides the needed buffer space to call
44687c478bd9Sstevel@tonic-gate  * inet_ntop() function's 3rd argument. This function should be
44697c478bd9Sstevel@tonic-gate  * used by any kernel routine which wants to save INET6_ADDRSTRLEN
44707c478bd9Sstevel@tonic-gate  * stack buffer space in it's own stack frame. This function uses
44717c478bd9Sstevel@tonic-gate  * a buffer from it's own stack and prints the information.
44727c478bd9Sstevel@tonic-gate  * Example: pr_addr_dbg("func: no route for %s\n ", AF_INET, addr)
44737c478bd9Sstevel@tonic-gate  *
44747c478bd9Sstevel@tonic-gate  * Note:    This function can call inet_ntop() once.
44757c478bd9Sstevel@tonic-gate  */
44767c478bd9Sstevel@tonic-gate void
pr_addr_dbg(char * fmt1,int af,const void * addr)44777c478bd9Sstevel@tonic-gate pr_addr_dbg(char *fmt1, int af, const void *addr)
44787c478bd9Sstevel@tonic-gate {
44797c478bd9Sstevel@tonic-gate 	char	buf[INET6_ADDRSTRLEN];
44807c478bd9Sstevel@tonic-gate 
44817c478bd9Sstevel@tonic-gate 	if (fmt1 == NULL) {
44827c478bd9Sstevel@tonic-gate 		ip0dbg(("pr_addr_dbg: Wrong arguments\n"));
44837c478bd9Sstevel@tonic-gate 		return;
44847c478bd9Sstevel@tonic-gate 	}
44857c478bd9Sstevel@tonic-gate 
44867c478bd9Sstevel@tonic-gate 	/*
44877c478bd9Sstevel@tonic-gate 	 * This does not compare debug level and just prints
44887c478bd9Sstevel@tonic-gate 	 * out. Thus it is the responsibility of the caller
44897c478bd9Sstevel@tonic-gate 	 * to check the appropriate debug-level before calling
44907c478bd9Sstevel@tonic-gate 	 * this function.
44917c478bd9Sstevel@tonic-gate 	 */
44927c478bd9Sstevel@tonic-gate 	if (ip_debug > 0) {
44937c478bd9Sstevel@tonic-gate 		printf(fmt1, inet_ntop(af, addr, buf, sizeof (buf)));
44947c478bd9Sstevel@tonic-gate 	}
44957c478bd9Sstevel@tonic-gate 
44967c478bd9Sstevel@tonic-gate 
44977c478bd9Sstevel@tonic-gate }
44987c478bd9Sstevel@tonic-gate 
44997c478bd9Sstevel@tonic-gate 
45007c478bd9Sstevel@tonic-gate /*
4501bd670b35SErik Nordmark  * Return the length in bytes of the IPv6 headers (base header
4502bd670b35SErik Nordmark  * extension headers) that will be needed based on the
4503bd670b35SErik Nordmark  * ip_pkt_t structure passed by the caller.
45047c478bd9Sstevel@tonic-gate  *
45057c478bd9Sstevel@tonic-gate  * The returned length does not include the length of the upper level
45067c478bd9Sstevel@tonic-gate  * protocol (ULP) header.
45077c478bd9Sstevel@tonic-gate  */
45087c478bd9Sstevel@tonic-gate int
ip_total_hdrs_len_v6(const ip_pkt_t * ipp)4509bd670b35SErik Nordmark ip_total_hdrs_len_v6(const ip_pkt_t *ipp)
45107c478bd9Sstevel@tonic-gate {
45117c478bd9Sstevel@tonic-gate 	int len;
45127c478bd9Sstevel@tonic-gate 
45137c478bd9Sstevel@tonic-gate 	len = IPV6_HDR_LEN;
4514bd670b35SErik Nordmark 
4515bd670b35SErik Nordmark 	/*
4516bd670b35SErik Nordmark 	 * If there's a security label here, then we ignore any hop-by-hop
4517bd670b35SErik Nordmark 	 * options the user may try to set.
4518bd670b35SErik Nordmark 	 */
4519bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_LABEL_V6) {
4520bd670b35SErik Nordmark 		uint_t hopoptslen;
4521bd670b35SErik Nordmark 		/*
4522bd670b35SErik Nordmark 		 * Note that ipp_label_len_v6 is just the option - not
4523bd670b35SErik Nordmark 		 * the hopopts extension header. It also needs to be padded
4524bd670b35SErik Nordmark 		 * to a multiple of 8 bytes.
4525bd670b35SErik Nordmark 		 */
4526bd670b35SErik Nordmark 		ASSERT(ipp->ipp_label_len_v6 != 0);
4527bd670b35SErik Nordmark 		hopoptslen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
4528bd670b35SErik Nordmark 		hopoptslen = (hopoptslen + 7)/8 * 8;
4529bd670b35SErik Nordmark 		len += hopoptslen;
4530bd670b35SErik Nordmark 	} else if (ipp->ipp_fields & IPPF_HOPOPTS) {
45317c478bd9Sstevel@tonic-gate 		ASSERT(ipp->ipp_hopoptslen != 0);
45327c478bd9Sstevel@tonic-gate 		len += ipp->ipp_hopoptslen;
45337c478bd9Sstevel@tonic-gate 	}
4534bd670b35SErik Nordmark 
45357c478bd9Sstevel@tonic-gate 	/*
45367c478bd9Sstevel@tonic-gate 	 * En-route destination options
45377c478bd9Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
45387c478bd9Sstevel@tonic-gate 	 */
4539bd670b35SErik Nordmark 	if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
4540bd670b35SErik Nordmark 	    (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
4541bd670b35SErik Nordmark 		ASSERT(ipp->ipp_rthdrdstoptslen != 0);
4542bd670b35SErik Nordmark 		len += ipp->ipp_rthdrdstoptslen;
4543bd670b35SErik Nordmark 	}
4544bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_RTHDR) {
4545bd670b35SErik Nordmark 		ASSERT(ipp->ipp_rthdrlen != 0);
4546bd670b35SErik Nordmark 		len += ipp->ipp_rthdrlen;
45477c478bd9Sstevel@tonic-gate 	}
45487c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_DSTOPTS) {
45497c478bd9Sstevel@tonic-gate 		ASSERT(ipp->ipp_dstoptslen != 0);
45507c478bd9Sstevel@tonic-gate 		len += ipp->ipp_dstoptslen;
45517c478bd9Sstevel@tonic-gate 	}
45527c478bd9Sstevel@tonic-gate 	return (len);
45537c478bd9Sstevel@tonic-gate }
45547c478bd9Sstevel@tonic-gate 
45557c478bd9Sstevel@tonic-gate /*
45567c478bd9Sstevel@tonic-gate  * All-purpose routine to build a header chain of an IPv6 header
4557bd670b35SErik Nordmark  * followed by any required extension headers and a proto header.
45587c478bd9Sstevel@tonic-gate  *
4559bd670b35SErik Nordmark  * The caller has to set the source and destination address as well as
4560bd670b35SErik Nordmark  * ip6_plen. The caller has to massage any routing header and compensate
4561bd670b35SErik Nordmark  * for the ULP pseudo-header checksum due to the source route.
45627c478bd9Sstevel@tonic-gate  *
4563bd670b35SErik Nordmark  * The extension headers will all be fully filled in.
45647c478bd9Sstevel@tonic-gate  */
45657c478bd9Sstevel@tonic-gate void
ip_build_hdrs_v6(uchar_t * buf,uint_t buf_len,const ip_pkt_t * ipp,uint8_t protocol,uint32_t flowinfo)4566bd670b35SErik Nordmark ip_build_hdrs_v6(uchar_t *buf, uint_t buf_len, const ip_pkt_t *ipp,
4567bd670b35SErik Nordmark     uint8_t protocol, uint32_t flowinfo)
45687c478bd9Sstevel@tonic-gate {
45697c478bd9Sstevel@tonic-gate 	uint8_t *nxthdr_ptr;
45707c478bd9Sstevel@tonic-gate 	uint8_t *cp;
4571bd670b35SErik Nordmark 	ip6_t	*ip6h = (ip6_t *)buf;
45727c478bd9Sstevel@tonic-gate 
45737c478bd9Sstevel@tonic-gate 	/* Initialize IPv6 header */
4574bd670b35SErik Nordmark 	ip6h->ip6_vcf =
4575bd670b35SErik Nordmark 	    (IPV6_DEFAULT_VERS_AND_FLOW & IPV6_VERS_AND_FLOW_MASK) |
4576bd670b35SErik Nordmark 	    (flowinfo & ~IPV6_VERS_AND_FLOW_MASK);
4577bd670b35SErik Nordmark 
45787c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_TCLASS) {
4579bd670b35SErik Nordmark 		/* Overrides the class part of flowinfo */
4580bd670b35SErik Nordmark 		ip6h->ip6_vcf = IPV6_TCLASS_FLOW(ip6h->ip6_vcf,
4581bd670b35SErik Nordmark 		    ipp->ipp_tclass);
45827c478bd9Sstevel@tonic-gate 	}
4583bd670b35SErik Nordmark 
4584bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_HOPLIMIT)
4585bd670b35SErik Nordmark 		ip6h->ip6_hops = ipp->ipp_hoplimit;
4586bd670b35SErik Nordmark 	else
4587bd670b35SErik Nordmark 		ip6h->ip6_hops = ipp->ipp_unicast_hops;
4588bd670b35SErik Nordmark 
4589bd670b35SErik Nordmark 	if ((ipp->ipp_fields & IPPF_ADDR) &&
4590bd670b35SErik Nordmark 	    !IN6_IS_ADDR_V4MAPPED(&ipp->ipp_addr))
45917c478bd9Sstevel@tonic-gate 		ip6h->ip6_src = ipp->ipp_addr;
45927c478bd9Sstevel@tonic-gate 
45937c478bd9Sstevel@tonic-gate 	nxthdr_ptr = (uint8_t *)&ip6h->ip6_nxt;
45947c478bd9Sstevel@tonic-gate 	cp = (uint8_t *)&ip6h[1];
45957c478bd9Sstevel@tonic-gate 	/*
45967c478bd9Sstevel@tonic-gate 	 * Here's where we have to start stringing together
45977c478bd9Sstevel@tonic-gate 	 * any extension headers in the right order:
45987c478bd9Sstevel@tonic-gate 	 * Hop-by-hop, destination, routing, and final destination opts.
45997c478bd9Sstevel@tonic-gate 	 */
4600bd670b35SErik Nordmark 	/*
4601bd670b35SErik Nordmark 	 * If there's a security label here, then we ignore any hop-by-hop
4602bd670b35SErik Nordmark 	 * options the user may try to set.
4603bd670b35SErik Nordmark 	 */
4604bd670b35SErik Nordmark 	if (ipp->ipp_fields & IPPF_LABEL_V6) {
4605bd670b35SErik Nordmark 		/*
4606bd670b35SErik Nordmark 		 * Hop-by-hop options with the label.
4607bd670b35SErik Nordmark 		 * Note that ipp_label_v6 is just the option - not
4608bd670b35SErik Nordmark 		 * the hopopts extension header. It also needs to be padded
4609bd670b35SErik Nordmark 		 * to a multiple of 8 bytes.
4610bd670b35SErik Nordmark 		 */
4611bd670b35SErik Nordmark 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
4612bd670b35SErik Nordmark 		uint_t hopoptslen;
4613bd670b35SErik Nordmark 		uint_t padlen;
4614bd670b35SErik Nordmark 
4615bd670b35SErik Nordmark 		padlen = ipp->ipp_label_len_v6 + sizeof (ip6_hbh_t);
4616bd670b35SErik Nordmark 		hopoptslen = (padlen + 7)/8 * 8;
4617bd670b35SErik Nordmark 		padlen = hopoptslen - padlen;
4618bd670b35SErik Nordmark 
4619bd670b35SErik Nordmark 		*nxthdr_ptr = IPPROTO_HOPOPTS;
4620bd670b35SErik Nordmark 		nxthdr_ptr = &hbh->ip6h_nxt;
4621bd670b35SErik Nordmark 		hbh->ip6h_len = hopoptslen/8 - 1;
4622bd670b35SErik Nordmark 		cp += sizeof (ip6_hbh_t);
4623bd670b35SErik Nordmark 		bcopy(ipp->ipp_label_v6, cp, ipp->ipp_label_len_v6);
4624bd670b35SErik Nordmark 		cp += ipp->ipp_label_len_v6;
4625bd670b35SErik Nordmark 
4626bd670b35SErik Nordmark 		ASSERT(padlen <= 7);
4627bd670b35SErik Nordmark 		switch (padlen) {
4628bd670b35SErik Nordmark 		case 0:
4629bd670b35SErik Nordmark 			break;
4630bd670b35SErik Nordmark 		case 1:
4631bd670b35SErik Nordmark 			cp[0] = IP6OPT_PAD1;
4632bd670b35SErik Nordmark 			break;
4633bd670b35SErik Nordmark 		default:
4634bd670b35SErik Nordmark 			cp[0] = IP6OPT_PADN;
4635bd670b35SErik Nordmark 			cp[1] = padlen - 2;
4636bd670b35SErik Nordmark 			bzero(&cp[2], padlen - 2);
4637bd670b35SErik Nordmark 			break;
4638bd670b35SErik Nordmark 		}
4639bd670b35SErik Nordmark 		cp += padlen;
4640bd670b35SErik Nordmark 	} else if (ipp->ipp_fields & IPPF_HOPOPTS) {
46417c478bd9Sstevel@tonic-gate 		/* Hop-by-hop options */
46427c478bd9Sstevel@tonic-gate 		ip6_hbh_t *hbh = (ip6_hbh_t *)cp;
46437c478bd9Sstevel@tonic-gate 
46447c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_HOPOPTS;
46457c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &hbh->ip6h_nxt;
46467c478bd9Sstevel@tonic-gate 
46477c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_hopopts, cp, ipp->ipp_hopoptslen);
46487c478bd9Sstevel@tonic-gate 		cp += ipp->ipp_hopoptslen;
46497c478bd9Sstevel@tonic-gate 	}
46507c478bd9Sstevel@tonic-gate 	/*
46517c478bd9Sstevel@tonic-gate 	 * En-route destination options
46527c478bd9Sstevel@tonic-gate 	 * Only do them if there's a routing header as well
46537c478bd9Sstevel@tonic-gate 	 */
4654bd670b35SErik Nordmark 	if ((ipp->ipp_fields & (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) ==
4655bd670b35SErik Nordmark 	    (IPPF_RTHDRDSTOPTS|IPPF_RTHDR)) {
46567c478bd9Sstevel@tonic-gate 		ip6_dest_t *dst = (ip6_dest_t *)cp;
46577c478bd9Sstevel@tonic-gate 
46587c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
46597c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dst->ip6d_nxt;
46607c478bd9Sstevel@tonic-gate 
4661bd670b35SErik Nordmark 		bcopy(ipp->ipp_rthdrdstopts, cp, ipp->ipp_rthdrdstoptslen);
4662bd670b35SErik Nordmark 		cp += ipp->ipp_rthdrdstoptslen;
46637c478bd9Sstevel@tonic-gate 	}
46647c478bd9Sstevel@tonic-gate 	/*
46657c478bd9Sstevel@tonic-gate 	 * Routing header next
46667c478bd9Sstevel@tonic-gate 	 */
46677c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_RTHDR) {
46687c478bd9Sstevel@tonic-gate 		ip6_rthdr_t *rt = (ip6_rthdr_t *)cp;
46697c478bd9Sstevel@tonic-gate 
46707c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_ROUTING;
46717c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &rt->ip6r_nxt;
46727c478bd9Sstevel@tonic-gate 
46737c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_rthdr, cp, ipp->ipp_rthdrlen);
46747c478bd9Sstevel@tonic-gate 		cp += ipp->ipp_rthdrlen;
46757c478bd9Sstevel@tonic-gate 	}
46767c478bd9Sstevel@tonic-gate 	/*
46777c478bd9Sstevel@tonic-gate 	 * Do ultimate destination options
46787c478bd9Sstevel@tonic-gate 	 */
46797c478bd9Sstevel@tonic-gate 	if (ipp->ipp_fields & IPPF_DSTOPTS) {
46807c478bd9Sstevel@tonic-gate 		ip6_dest_t *dest = (ip6_dest_t *)cp;
46817c478bd9Sstevel@tonic-gate 
46827c478bd9Sstevel@tonic-gate 		*nxthdr_ptr = IPPROTO_DSTOPTS;
46837c478bd9Sstevel@tonic-gate 		nxthdr_ptr = &dest->ip6d_nxt;
46847c478bd9Sstevel@tonic-gate 
46857c478bd9Sstevel@tonic-gate 		bcopy(ipp->ipp_dstopts, cp, ipp->ipp_dstoptslen);
46867c478bd9Sstevel@tonic-gate 		cp += ipp->ipp_dstoptslen;
46877c478bd9Sstevel@tonic-gate 	}
46887c478bd9Sstevel@tonic-gate 	/*
46897c478bd9Sstevel@tonic-gate 	 * Now set the last header pointer to the proto passed in
46907c478bd9Sstevel@tonic-gate 	 */
46917c478bd9Sstevel@tonic-gate 	*nxthdr_ptr = protocol;
4692bd670b35SErik Nordmark 	ASSERT((int)(cp - buf) == buf_len);
46937c478bd9Sstevel@tonic-gate }
46947c478bd9Sstevel@tonic-gate 
46957c478bd9Sstevel@tonic-gate /*
46967c478bd9Sstevel@tonic-gate  * Return a pointer to the routing header extension header
46977c478bd9Sstevel@tonic-gate  * in the IPv6 header(s) chain passed in.
46987c478bd9Sstevel@tonic-gate  * If none found, return NULL
46997c478bd9Sstevel@tonic-gate  * Assumes that all extension headers are in same mblk as the v6 header
47007c478bd9Sstevel@tonic-gate  */
47017c478bd9Sstevel@tonic-gate ip6_rthdr_t *
ip_find_rthdr_v6(ip6_t * ip6h,uint8_t * endptr)47027c478bd9Sstevel@tonic-gate ip_find_rthdr_v6(ip6_t *ip6h, uint8_t *endptr)
47037c478bd9Sstevel@tonic-gate {
47047c478bd9Sstevel@tonic-gate 	ip6_dest_t	*desthdr;
47057c478bd9Sstevel@tonic-gate 	ip6_frag_t	*fraghdr;
47067c478bd9Sstevel@tonic-gate 	uint_t		hdrlen;
47077c478bd9Sstevel@tonic-gate 	uint8_t		nexthdr;
47087c478bd9Sstevel@tonic-gate 	uint8_t		*ptr = (uint8_t *)&ip6h[1];
47097c478bd9Sstevel@tonic-gate 
47107c478bd9Sstevel@tonic-gate 	if (ip6h->ip6_nxt == IPPROTO_ROUTING)
47117c478bd9Sstevel@tonic-gate 		return ((ip6_rthdr_t *)ptr);
47127c478bd9Sstevel@tonic-gate 
47137c478bd9Sstevel@tonic-gate 	/*
47147c478bd9Sstevel@tonic-gate 	 * The routing header will precede all extension headers
47157c478bd9Sstevel@tonic-gate 	 * other than the hop-by-hop and destination options
47167c478bd9Sstevel@tonic-gate 	 * extension headers, so if we see anything other than those,
47177c478bd9Sstevel@tonic-gate 	 * we're done and didn't find it.
47187c478bd9Sstevel@tonic-gate 	 * We could see a destination options header alone but no
47197c478bd9Sstevel@tonic-gate 	 * routing header, in which case we'll return NULL as soon as
47207c478bd9Sstevel@tonic-gate 	 * we see anything after that.
47217c478bd9Sstevel@tonic-gate 	 * Hop-by-hop and destination option headers are identical,
47227c478bd9Sstevel@tonic-gate 	 * so we can use either one we want as a template.
47237c478bd9Sstevel@tonic-gate 	 */
47247c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
47257c478bd9Sstevel@tonic-gate 	while (ptr < endptr) {
47267c478bd9Sstevel@tonic-gate 		/* Is there enough left for len + nexthdr? */
47277c478bd9Sstevel@tonic-gate 		if (ptr + MIN_EHDR_LEN > endptr)
47287c478bd9Sstevel@tonic-gate 			return (NULL);
47297c478bd9Sstevel@tonic-gate 
47307c478bd9Sstevel@tonic-gate 		switch (nexthdr) {
47317c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
47327c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
47337c478bd9Sstevel@tonic-gate 			/* Assumes the headers are identical for hbh and dst */
47347c478bd9Sstevel@tonic-gate 			desthdr = (ip6_dest_t *)ptr;
47357c478bd9Sstevel@tonic-gate 			hdrlen = 8 * (desthdr->ip6d_len + 1);
47367c478bd9Sstevel@tonic-gate 			nexthdr = desthdr->ip6d_nxt;
47377c478bd9Sstevel@tonic-gate 			break;
47387c478bd9Sstevel@tonic-gate 
47397c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
47407c478bd9Sstevel@tonic-gate 			return ((ip6_rthdr_t *)ptr);
47417c478bd9Sstevel@tonic-gate 
47427c478bd9Sstevel@tonic-gate 		case IPPROTO_FRAGMENT:
47437c478bd9Sstevel@tonic-gate 			fraghdr = (ip6_frag_t *)ptr;
47447c478bd9Sstevel@tonic-gate 			hdrlen = sizeof (ip6_frag_t);
47457c478bd9Sstevel@tonic-gate 			nexthdr = fraghdr->ip6f_nxt;
47467c478bd9Sstevel@tonic-gate 			break;
47477c478bd9Sstevel@tonic-gate 
47487c478bd9Sstevel@tonic-gate 		default:
47497c478bd9Sstevel@tonic-gate 			return (NULL);
47507c478bd9Sstevel@tonic-gate 		}
47517c478bd9Sstevel@tonic-gate 		ptr += hdrlen;
47527c478bd9Sstevel@tonic-gate 	}
47537c478bd9Sstevel@tonic-gate 	return (NULL);
47547c478bd9Sstevel@tonic-gate }
47557c478bd9Sstevel@tonic-gate 
47567c478bd9Sstevel@tonic-gate /*
47577c478bd9Sstevel@tonic-gate  * Called for source-routed packets originating on this node.
47587c478bd9Sstevel@tonic-gate  * Manipulates the original routing header by moving every entry up
47597c478bd9Sstevel@tonic-gate  * one slot, placing the first entry in the v6 header's v6_dst field,
47607c478bd9Sstevel@tonic-gate  * and placing the ultimate destination in the routing header's last
47617c478bd9Sstevel@tonic-gate  * slot.
47627c478bd9Sstevel@tonic-gate  *
47637c478bd9Sstevel@tonic-gate  * Returns the checksum diference between the ultimate destination
47647c478bd9Sstevel@tonic-gate  * (last hop in the routing header when the packet is sent) and
47657c478bd9Sstevel@tonic-gate  * the first hop (ip6_dst when the packet is sent)
47667c478bd9Sstevel@tonic-gate  */
4767f4b3ec61Sdh155122 /* ARGSUSED2 */
47687c478bd9Sstevel@tonic-gate uint32_t
ip_massage_options_v6(ip6_t * ip6h,ip6_rthdr_t * rth,netstack_t * ns)4769f4b3ec61Sdh155122 ip_massage_options_v6(ip6_t *ip6h, ip6_rthdr_t *rth, netstack_t *ns)
47707c478bd9Sstevel@tonic-gate {
47717c478bd9Sstevel@tonic-gate 	uint_t		numaddr;
47727c478bd9Sstevel@tonic-gate 	uint_t		i;
47737c478bd9Sstevel@tonic-gate 	in6_addr_t	*addrptr;
47747c478bd9Sstevel@tonic-gate 	in6_addr_t	tmp;
47757c478bd9Sstevel@tonic-gate 	ip6_rthdr0_t	*rthdr = (ip6_rthdr0_t *)rth;
47767c478bd9Sstevel@tonic-gate 	uint32_t	cksm;
47777c478bd9Sstevel@tonic-gate 	uint32_t	addrsum = 0;
47787c478bd9Sstevel@tonic-gate 	uint16_t	*ptr;
47797c478bd9Sstevel@tonic-gate 
47807c478bd9Sstevel@tonic-gate 	/*
47817c478bd9Sstevel@tonic-gate 	 * Perform any processing needed for source routing.
47827c478bd9Sstevel@tonic-gate 	 * We know that all extension headers will be in the same mblk
47837c478bd9Sstevel@tonic-gate 	 * as the IPv6 header.
47847c478bd9Sstevel@tonic-gate 	 */
47857c478bd9Sstevel@tonic-gate 
47867c478bd9Sstevel@tonic-gate 	/*
47877c478bd9Sstevel@tonic-gate 	 * If no segments left in header, or the header length field is zero,
47887c478bd9Sstevel@tonic-gate 	 * don't move hop addresses around;
47897c478bd9Sstevel@tonic-gate 	 * Checksum difference is zero.
47907c478bd9Sstevel@tonic-gate 	 */
47917c478bd9Sstevel@tonic-gate 	if ((rthdr->ip6r0_segleft == 0) || (rthdr->ip6r0_len == 0))
47927c478bd9Sstevel@tonic-gate 		return (0);
47937c478bd9Sstevel@tonic-gate 
47947c478bd9Sstevel@tonic-gate 	ptr = (uint16_t *)&ip6h->ip6_dst;
47957c478bd9Sstevel@tonic-gate 	cksm = 0;
47967c478bd9Sstevel@tonic-gate 	for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
47977c478bd9Sstevel@tonic-gate 		cksm += ptr[i];
47987c478bd9Sstevel@tonic-gate 	}
47997c478bd9Sstevel@tonic-gate 	cksm = (cksm & 0xFFFF) + (cksm >> 16);
48007c478bd9Sstevel@tonic-gate 
48017c478bd9Sstevel@tonic-gate 	/*
48027c478bd9Sstevel@tonic-gate 	 * Here's where the fun begins - we have to
48037c478bd9Sstevel@tonic-gate 	 * move all addresses up one spot, take the
48047c478bd9Sstevel@tonic-gate 	 * first hop and make it our first ip6_dst,
48057c478bd9Sstevel@tonic-gate 	 * and place the ultimate destination in the
48067c478bd9Sstevel@tonic-gate 	 * newly-opened last slot.
48077c478bd9Sstevel@tonic-gate 	 */
48087c478bd9Sstevel@tonic-gate 	addrptr = (in6_addr_t *)((char *)rthdr + sizeof (*rthdr));
48097c478bd9Sstevel@tonic-gate 	numaddr = rthdr->ip6r0_len / 2;
48107c478bd9Sstevel@tonic-gate 	tmp = *addrptr;
48117c478bd9Sstevel@tonic-gate 	for (i = 0; i < (numaddr - 1); addrptr++, i++) {
48127c478bd9Sstevel@tonic-gate 		*addrptr = addrptr[1];
48137c478bd9Sstevel@tonic-gate 	}
48147c478bd9Sstevel@tonic-gate 	*addrptr = ip6h->ip6_dst;
48157c478bd9Sstevel@tonic-gate 	ip6h->ip6_dst = tmp;
48167c478bd9Sstevel@tonic-gate 
48177c478bd9Sstevel@tonic-gate 	/*
48187c478bd9Sstevel@tonic-gate 	 * From the checksummed ultimate destination subtract the checksummed
48197c478bd9Sstevel@tonic-gate 	 * current ip6_dst (the first hop address). Return that number.
48207c478bd9Sstevel@tonic-gate 	 * (In the v4 case, the second part of this is done in each routine
48217c478bd9Sstevel@tonic-gate 	 *  that calls ip_massage_options(). We do it all in this one place
48227c478bd9Sstevel@tonic-gate 	 *  for v6).
48237c478bd9Sstevel@tonic-gate 	 */
48247c478bd9Sstevel@tonic-gate 	ptr = (uint16_t *)&ip6h->ip6_dst;
48257c478bd9Sstevel@tonic-gate 	for (i = 0; i < (sizeof (in6_addr_t) / sizeof (uint16_t)); i++) {
48267c478bd9Sstevel@tonic-gate 		addrsum += ptr[i];
48277c478bd9Sstevel@tonic-gate 	}
48287c478bd9Sstevel@tonic-gate 	cksm -= ((addrsum >> 16) + (addrsum & 0xFFFF));
48297c478bd9Sstevel@tonic-gate 	if ((int)cksm < 0)
48307c478bd9Sstevel@tonic-gate 		cksm--;
48317c478bd9Sstevel@tonic-gate 	cksm = (cksm & 0xFFFF) + (cksm >> 16);
48327c478bd9Sstevel@tonic-gate 
48337c478bd9Sstevel@tonic-gate 	return (cksm);
48347c478bd9Sstevel@tonic-gate }
48357c478bd9Sstevel@tonic-gate 
48367c478bd9Sstevel@tonic-gate void
ip6_kstat_init(netstackid_t stackid,ip6_stat_t * ip6_statisticsp)4837f4b3ec61Sdh155122 *ip6_kstat_init(netstackid_t stackid, ip6_stat_t *ip6_statisticsp)
48387c478bd9Sstevel@tonic-gate {
4839f4b3ec61Sdh155122 	kstat_t *ksp;
4840f4b3ec61Sdh155122 
4841f4b3ec61Sdh155122 	ip6_stat_t template = {
4842f4b3ec61Sdh155122 		{ "ip6_udp_fannorm", 	KSTAT_DATA_UINT64 },
4843f4b3ec61Sdh155122 		{ "ip6_udp_fanmb", 	KSTAT_DATA_UINT64 },
4844bd670b35SErik Nordmark 		{ "ip6_recv_pullup", 		KSTAT_DATA_UINT64 },
4845bd670b35SErik Nordmark 		{ "ip6_db_ref",			KSTAT_DATA_UINT64 },
4846bd670b35SErik Nordmark 		{ "ip6_notaligned",		KSTAT_DATA_UINT64 },
4847bd670b35SErik Nordmark 		{ "ip6_multimblk",		KSTAT_DATA_UINT64 },
4848bd670b35SErik Nordmark 		{ "ipsec_proto_ahesp",		KSTAT_DATA_UINT64 },
4849f4b3ec61Sdh155122 		{ "ip6_out_sw_cksum",			KSTAT_DATA_UINT64 },
4850bd670b35SErik Nordmark 		{ "ip6_out_sw_cksum_bytes",		KSTAT_DATA_UINT64 },
4851f4b3ec61Sdh155122 		{ "ip6_in_sw_cksum",			KSTAT_DATA_UINT64 },
4852f4b3ec61Sdh155122 		{ "ip6_tcp_in_full_hw_cksum_err",	KSTAT_DATA_UINT64 },
4853f4b3ec61Sdh155122 		{ "ip6_tcp_in_part_hw_cksum_err",	KSTAT_DATA_UINT64 },
4854f4b3ec61Sdh155122 		{ "ip6_tcp_in_sw_cksum_err",		KSTAT_DATA_UINT64 },
4855f4b3ec61Sdh155122 		{ "ip6_udp_in_full_hw_cksum_err",	KSTAT_DATA_UINT64 },
4856f4b3ec61Sdh155122 		{ "ip6_udp_in_part_hw_cksum_err",	KSTAT_DATA_UINT64 },
4857f4b3ec61Sdh155122 		{ "ip6_udp_in_sw_cksum_err",		KSTAT_DATA_UINT64 },
4858f4b3ec61Sdh155122 	};
4859f4b3ec61Sdh155122 	ksp = kstat_create_netstack("ip", 0, "ip6stat", "net",
4860f4b3ec61Sdh155122 	    KSTAT_TYPE_NAMED, sizeof (template) / sizeof (kstat_named_t),
4861f4b3ec61Sdh155122 	    KSTAT_FLAG_VIRTUAL, stackid);
4862f4b3ec61Sdh155122 
4863f4b3ec61Sdh155122 	if (ksp == NULL)
4864f4b3ec61Sdh155122 		return (NULL);
4865f4b3ec61Sdh155122 
4866f4b3ec61Sdh155122 	bcopy(&template, ip6_statisticsp, sizeof (template));
4867f4b3ec61Sdh155122 	ksp->ks_data = (void *)ip6_statisticsp;
4868f4b3ec61Sdh155122 	ksp->ks_private = (void *)(uintptr_t)stackid;
4869f4b3ec61Sdh155122 
4870f4b3ec61Sdh155122 	kstat_install(ksp);
4871f4b3ec61Sdh155122 	return (ksp);
4872f4b3ec61Sdh155122 }
4873f4b3ec61Sdh155122 
4874f4b3ec61Sdh155122 void
ip6_kstat_fini(netstackid_t stackid,kstat_t * ksp)4875f4b3ec61Sdh155122 ip6_kstat_fini(netstackid_t stackid, kstat_t *ksp)
4876f4b3ec61Sdh155122 {
4877f4b3ec61Sdh155122 	if (ksp != NULL) {
4878f4b3ec61Sdh155122 		ASSERT(stackid == (netstackid_t)(uintptr_t)ksp->ks_private);
4879f4b3ec61Sdh155122 		kstat_delete_netstack(ksp, stackid);
48807c478bd9Sstevel@tonic-gate 	}
48817c478bd9Sstevel@tonic-gate }
48827c478bd9Sstevel@tonic-gate 
48837c478bd9Sstevel@tonic-gate /*
48847c478bd9Sstevel@tonic-gate  * The following two functions set and get the value for the
48857c478bd9Sstevel@tonic-gate  * IPV6_SRC_PREFERENCES socket option.
48867c478bd9Sstevel@tonic-gate  */
48877c478bd9Sstevel@tonic-gate int
ip6_set_src_preferences(ip_xmit_attr_t * ixa,uint32_t prefs)4888bd670b35SErik Nordmark ip6_set_src_preferences(ip_xmit_attr_t *ixa, uint32_t prefs)
48897c478bd9Sstevel@tonic-gate {
48907c478bd9Sstevel@tonic-gate 	/*
48917c478bd9Sstevel@tonic-gate 	 * We only support preferences that are covered by
48927c478bd9Sstevel@tonic-gate 	 * IPV6_PREFER_SRC_MASK.
48937c478bd9Sstevel@tonic-gate 	 */
48947c478bd9Sstevel@tonic-gate 	if (prefs & ~IPV6_PREFER_SRC_MASK)
48957c478bd9Sstevel@tonic-gate 		return (EINVAL);
48967c478bd9Sstevel@tonic-gate 
48977c478bd9Sstevel@tonic-gate 	/*
48987c478bd9Sstevel@tonic-gate 	 * Look for conflicting preferences or default preferences.  If
48997c478bd9Sstevel@tonic-gate 	 * both bits of a related pair are clear, the application wants the
49007c478bd9Sstevel@tonic-gate 	 * system's default value for that pair.  Both bits in a pair can't
49017c478bd9Sstevel@tonic-gate 	 * be set.
49027c478bd9Sstevel@tonic-gate 	 */
49037c478bd9Sstevel@tonic-gate 	if ((prefs & IPV6_PREFER_SRC_MIPMASK) == 0) {
49047c478bd9Sstevel@tonic-gate 		prefs |= IPV6_PREFER_SRC_MIPDEFAULT;
49057c478bd9Sstevel@tonic-gate 	} else if ((prefs & IPV6_PREFER_SRC_MIPMASK) ==
49067c478bd9Sstevel@tonic-gate 	    IPV6_PREFER_SRC_MIPMASK) {
49077c478bd9Sstevel@tonic-gate 		return (EINVAL);
49087c478bd9Sstevel@tonic-gate 	}
49097c478bd9Sstevel@tonic-gate 	if ((prefs & IPV6_PREFER_SRC_TMPMASK) == 0) {
49107c478bd9Sstevel@tonic-gate 		prefs |= IPV6_PREFER_SRC_TMPDEFAULT;
49117c478bd9Sstevel@tonic-gate 	} else if ((prefs & IPV6_PREFER_SRC_TMPMASK) ==
49127c478bd9Sstevel@tonic-gate 	    IPV6_PREFER_SRC_TMPMASK) {
49137c478bd9Sstevel@tonic-gate 		return (EINVAL);
49147c478bd9Sstevel@tonic-gate 	}
49157c478bd9Sstevel@tonic-gate 	if ((prefs & IPV6_PREFER_SRC_CGAMASK) == 0) {
49167c478bd9Sstevel@tonic-gate 		prefs |= IPV6_PREFER_SRC_CGADEFAULT;
49177c478bd9Sstevel@tonic-gate 	} else if ((prefs & IPV6_PREFER_SRC_CGAMASK) ==
49187c478bd9Sstevel@tonic-gate 	    IPV6_PREFER_SRC_CGAMASK) {
49197c478bd9Sstevel@tonic-gate 		return (EINVAL);
49207c478bd9Sstevel@tonic-gate 	}
49217c478bd9Sstevel@tonic-gate 
4922bd670b35SErik Nordmark 	ixa->ixa_src_preferences = prefs;
49237c478bd9Sstevel@tonic-gate 	return (0);
49247c478bd9Sstevel@tonic-gate }
49257c478bd9Sstevel@tonic-gate 
49267c478bd9Sstevel@tonic-gate size_t
ip6_get_src_preferences(ip_xmit_attr_t * ixa,uint32_t * val)4927bd670b35SErik Nordmark ip6_get_src_preferences(ip_xmit_attr_t *ixa, uint32_t *val)
49287c478bd9Sstevel@tonic-gate {
4929bd670b35SErik Nordmark 	*val = ixa->ixa_src_preferences;
4930bd670b35SErik Nordmark 	return (sizeof (ixa->ixa_src_preferences));
49317c478bd9Sstevel@tonic-gate }
49327c478bd9Sstevel@tonic-gate 
49337c478bd9Sstevel@tonic-gate /*
49347c478bd9Sstevel@tonic-gate  * Get the size of the IP options (including the IP headers size)
49357c478bd9Sstevel@tonic-gate  * without including the AH header's size. If till_ah is B_FALSE,
49367c478bd9Sstevel@tonic-gate  * and if AH header is present, dest options beyond AH header will
49377c478bd9Sstevel@tonic-gate  * also be included in the returned size.
49387c478bd9Sstevel@tonic-gate  */
49397c478bd9Sstevel@tonic-gate int
ipsec_ah_get_hdr_size_v6(mblk_t * mp,boolean_t till_ah)49407c478bd9Sstevel@tonic-gate ipsec_ah_get_hdr_size_v6(mblk_t *mp, boolean_t till_ah)
49417c478bd9Sstevel@tonic-gate {
49427c478bd9Sstevel@tonic-gate 	ip6_t *ip6h;
49437c478bd9Sstevel@tonic-gate 	uint8_t nexthdr;
49447c478bd9Sstevel@tonic-gate 	uint8_t *whereptr;
49457c478bd9Sstevel@tonic-gate 	ip6_hbh_t *hbhhdr;
49467c478bd9Sstevel@tonic-gate 	ip6_dest_t *dsthdr;
49477c478bd9Sstevel@tonic-gate 	ip6_rthdr_t *rthdr;
49487c478bd9Sstevel@tonic-gate 	int ehdrlen;
49497c478bd9Sstevel@tonic-gate 	int size;
49507c478bd9Sstevel@tonic-gate 	ah_t *ah;
49517c478bd9Sstevel@tonic-gate 
49527c478bd9Sstevel@tonic-gate 	ip6h = (ip6_t *)mp->b_rptr;
49537c478bd9Sstevel@tonic-gate 	size = IPV6_HDR_LEN;
49547c478bd9Sstevel@tonic-gate 	nexthdr = ip6h->ip6_nxt;
49557c478bd9Sstevel@tonic-gate 	whereptr = (uint8_t *)&ip6h[1];
49567c478bd9Sstevel@tonic-gate 	for (;;) {
49577c478bd9Sstevel@tonic-gate 		/* Assume IP has already stripped it */
4958bd670b35SErik Nordmark 		ASSERT(nexthdr != IPPROTO_FRAGMENT);
49597c478bd9Sstevel@tonic-gate 		switch (nexthdr) {
49607c478bd9Sstevel@tonic-gate 		case IPPROTO_HOPOPTS:
49617c478bd9Sstevel@tonic-gate 			hbhhdr = (ip6_hbh_t *)whereptr;
49627c478bd9Sstevel@tonic-gate 			nexthdr = hbhhdr->ip6h_nxt;
49637c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (hbhhdr->ip6h_len + 1);
49647c478bd9Sstevel@tonic-gate 			break;
49657c478bd9Sstevel@tonic-gate 		case IPPROTO_DSTOPTS:
49667c478bd9Sstevel@tonic-gate 			dsthdr = (ip6_dest_t *)whereptr;
49677c478bd9Sstevel@tonic-gate 			nexthdr = dsthdr->ip6d_nxt;
49687c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (dsthdr->ip6d_len + 1);
49697c478bd9Sstevel@tonic-gate 			break;
49707c478bd9Sstevel@tonic-gate 		case IPPROTO_ROUTING:
49717c478bd9Sstevel@tonic-gate 			rthdr = (ip6_rthdr_t *)whereptr;
49727c478bd9Sstevel@tonic-gate 			nexthdr = rthdr->ip6r_nxt;
49737c478bd9Sstevel@tonic-gate 			ehdrlen = 8 * (rthdr->ip6r_len + 1);
49747c478bd9Sstevel@tonic-gate 			break;
49757c478bd9Sstevel@tonic-gate 		default :
49767c478bd9Sstevel@tonic-gate 			if (till_ah) {
49777c478bd9Sstevel@tonic-gate 				ASSERT(nexthdr == IPPROTO_AH);
49787c478bd9Sstevel@tonic-gate 				return (size);
49797c478bd9Sstevel@tonic-gate 			}
49807c478bd9Sstevel@tonic-gate 			/*
49817c478bd9Sstevel@tonic-gate 			 * If we don't have a AH header to traverse,
49827c478bd9Sstevel@tonic-gate 			 * return now. This happens normally for
49837c478bd9Sstevel@tonic-gate 			 * outbound datagrams where we have not inserted
49847c478bd9Sstevel@tonic-gate 			 * the AH header.
49857c478bd9Sstevel@tonic-gate 			 */
49867c478bd9Sstevel@tonic-gate 			if (nexthdr != IPPROTO_AH) {
49877c478bd9Sstevel@tonic-gate 				return (size);
49887c478bd9Sstevel@tonic-gate 			}
49897c478bd9Sstevel@tonic-gate 
49907c478bd9Sstevel@tonic-gate 			/*
49917c478bd9Sstevel@tonic-gate 			 * We don't include the AH header's size
49927c478bd9Sstevel@tonic-gate 			 * to be symmetrical with other cases where
49937c478bd9Sstevel@tonic-gate 			 * we either don't have a AH header (outbound)
49947c478bd9Sstevel@tonic-gate 			 * or peek into the AH header yet (inbound and
49957c478bd9Sstevel@tonic-gate 			 * not pulled up yet).
49967c478bd9Sstevel@tonic-gate 			 */
49977c478bd9Sstevel@tonic-gate 			ah = (ah_t *)whereptr;
49987c478bd9Sstevel@tonic-gate 			nexthdr = ah->ah_nexthdr;
49997c478bd9Sstevel@tonic-gate 			ehdrlen = (ah->ah_length << 2) + 8;
50007c478bd9Sstevel@tonic-gate 
50017c478bd9Sstevel@tonic-gate 			if (nexthdr == IPPROTO_DSTOPTS) {
50027c478bd9Sstevel@tonic-gate 				if (whereptr + ehdrlen >= mp->b_wptr) {
50037c478bd9Sstevel@tonic-gate 					/*
50047c478bd9Sstevel@tonic-gate 					 * The destination options header
50057c478bd9Sstevel@tonic-gate 					 * is not part of the first mblk.
50067c478bd9Sstevel@tonic-gate 					 */
50077c478bd9Sstevel@tonic-gate 					whereptr = mp->b_cont->b_rptr;
50087c478bd9Sstevel@tonic-gate 				} else {
50097c478bd9Sstevel@tonic-gate 					whereptr += ehdrlen;
50107c478bd9Sstevel@tonic-gate 				}
50117c478bd9Sstevel@tonic-gate 
50127c478bd9Sstevel@tonic-gate 				dsthdr = (ip6_dest_t *)whereptr;
50137c478bd9Sstevel@tonic-gate 				ehdrlen = 8 * (dsthdr->ip6d_len + 1);
50147c478bd9Sstevel@tonic-gate 				size += ehdrlen;
50157c478bd9Sstevel@tonic-gate 			}
50167c478bd9Sstevel@tonic-gate 			return (size);
50177c478bd9Sstevel@tonic-gate 		}
50187c478bd9Sstevel@tonic-gate 		whereptr += ehdrlen;
50197c478bd9Sstevel@tonic-gate 		size += ehdrlen;
50207c478bd9Sstevel@tonic-gate 	}
50217c478bd9Sstevel@tonic-gate }
5022e11c3f44Smeem 
5023e11c3f44Smeem /*
5024e11c3f44Smeem  * Utility routine that checks if `v6srcp' is a valid address on underlying
5025e11c3f44Smeem  * interface `ill'.  If `ipifp' is non-NULL, it's set to a held ipif
5026e11c3f44Smeem  * associated with `v6srcp' on success.  NOTE: if this is not called from
5027e11c3f44Smeem  * inside the IPSQ (ill_g_lock is not held), `ill' may be removed from the
5028e11c3f44Smeem  * group during or after this lookup.
5029e11c3f44Smeem  */
5030bd670b35SErik Nordmark boolean_t
ipif_lookup_testaddr_v6(ill_t * ill,const in6_addr_t * v6srcp,ipif_t ** ipifp)5031e11c3f44Smeem ipif_lookup_testaddr_v6(ill_t *ill, const in6_addr_t *v6srcp, ipif_t **ipifp)
5032e11c3f44Smeem {
5033e11c3f44Smeem 	ipif_t *ipif;
5034e11c3f44Smeem 
5035bd670b35SErik Nordmark 
5036e11c3f44Smeem 	ipif = ipif_lookup_addr_exact_v6(v6srcp, ill, ill->ill_ipst);
5037e11c3f44Smeem 	if (ipif != NULL) {
5038e11c3f44Smeem 		if (ipifp != NULL)
5039e11c3f44Smeem 			*ipifp = ipif;
5040e11c3f44Smeem 		else
5041e11c3f44Smeem 			ipif_refrele(ipif);
5042e11c3f44Smeem 		return (B_TRUE);
5043e11c3f44Smeem 	}
5044e11c3f44Smeem 
5045e11c3f44Smeem 	if (ip_debug > 2) {
5046e11c3f44Smeem 		pr_addr_dbg("ipif_lookup_testaddr_v6: cannot find ipif for "
5047e11c3f44Smeem 		    "src %s\n", AF_INET6, v6srcp);
5048e11c3f44Smeem 	}
5049e11c3f44Smeem 	return (B_FALSE);
5050e11c3f44Smeem }
5051