PSARC 2006/466 IP_PKTINFO Socket Option
authorrshoaib
Fri, 22 Dec 2006 01:03:02 -0800
changeset 3318 fcaac7018af6
parent 3317 e378dabfd171
child 3319 f3f3ca757d7e
PSARC 2006/466 IP_PKTINFO Socket Option 4773220 Provide API to set source address of UDP/IPv4 datagrams
usr/src/uts/common/inet/ip.h
usr/src/uts/common/inet/ip/icmp.c
usr/src/uts/common/inet/ip/icmp_opt_data.c
usr/src/uts/common/inet/ip/ip.c
usr/src/uts/common/inet/ip/ip6.c
usr/src/uts/common/inet/ip/ip_opt_data.c
usr/src/uts/common/inet/ipclassifier.h
usr/src/uts/common/inet/rawip_impl.h
usr/src/uts/common/inet/sctp/sctp_hash.c
usr/src/uts/common/inet/sctp/sctp_input.c
usr/src/uts/common/inet/sctp/sctp_opt_data.c
usr/src/uts/common/inet/tcp/tcp.c
usr/src/uts/common/inet/udp/udp.c
usr/src/uts/common/inet/udp/udp_opt_data.c
usr/src/uts/common/inet/udp_impl.h
usr/src/uts/common/netinet/in.h
--- a/usr/src/uts/common/inet/ip.h	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip.h	Fri Dec 22 01:03:02 2006 -0800
@@ -314,7 +314,7 @@
 #define	IP_FF_RAWIP		0x08	/* Use rawip mib variable */
 #define	IP_FF_SRC_QUENCH	0x10	/* OK to send ICMP_SOURCE_QUENCH */
 #define	IP_FF_SYN_ADDIRE	0x20	/* Add IRE if TCP syn packet */
-#define	IP_FF_IP6INFO		0x80	/* Add ip6i_t if needed */
+#define	IP_FF_IPINFO		0x80	/* Used for both V4 and V6 */
 #define	IP_FF_SEND_SLLA		0x100	/* Send source link layer info ? */
 #define	IPV6_REACHABILITY_CONFIRMATION	0x200	/* Flags for ip_xmit_v6 */
 #define	IP_FF_NO_MCAST_LOOP	0x400	/* No multicasts for sending zone */
@@ -2662,23 +2662,52 @@
 extern void ip6_pkt_free(ip6_pkt_t *);	/* free storage inside ip6_pkt_t */
 
 /*
- * This structure is used to convey information from IP and the ULP.
- * Currently used for the IP_RECVSLLA and IP_RECVIF options. The
- * type of information field is set to IN_PKTINFO (i.e inbound pkt info)
+ * This struct is used by ULP_opt_set() functions to return value of IPv4
+ * ancillary options. Currently this is only used by udp and icmp and only
+ * IP_PKTINFO option is supported.
+ */
+typedef struct ip4_pkt_s {
+	uint_t		ip4_ill_index;	/* interface index */
+	ipaddr_t	ip4_addr;	/* source address */
+} ip4_pkt_t;
+
+/*
+ * Used by ULP's to pass options info to ip_output
+ * currently only IP_PKTINFO is supported.
  */
-typedef struct in_pktinfo {
-	uint32_t		in_pkt_ulp_type;	/* type of info sent */
-							/* to UDP */
-	uint32_t		in_pkt_flags;	/* what is sent up by IP */
-	uint32_t		in_pkt_ifindex;	/* inbound interface index */
-	struct sockaddr_dl	in_pkt_slla;	/* has source link layer addr */
-} in_pktinfo_t;
+typedef struct ip_opt_info_s {
+	uint_t ip_opt_ill_index;
+	uint_t ip_opt_flags;
+} ip_opt_info_t;
+
+/*
+ * value for ip_opt_flags
+ */
+#define	IP_VERIFY_SRC	0x1
+
+/*
+ * This structure is used to convey information from IP and the ULP.
+ * Currently used for the IP_RECVSLLA, IP_RECVIF and IP_RECVPKTINFO options.
+ * The type of information field is set to IN_PKTINFO (i.e inbound pkt info)
+ */
+typedef struct ip_pktinfo {
+	uint32_t		ip_pkt_ulp_type;	/* type of info sent */
+	uint32_t		ip_pkt_flags;	/* what is sent up by IP */
+	uint32_t		ip_pkt_ifindex;	/* inbound interface index */
+	struct sockaddr_dl	ip_pkt_slla;	/* has source link layer addr */
+	struct in_addr		ip_pkt_match_addr; /* matched address */
+} ip_pktinfo_t;
 
 /*
  * flags to tell UDP what IP is sending; in_pkt_flags
  */
 #define	IPF_RECVIF	0x01	/* inbound interface index */
 #define	IPF_RECVSLLA	0x02	/* source link layer address */
+/*
+ * Inbound interface index + matched address.
+ * Used only by IPV4.
+ */
+#define	IPF_RECVADDR	0x04
 
 /* ipp_fields values */
 #define	IPPF_IFINDEX	0x0001	/* Part of in6_pktinfo: ifindex */
@@ -3141,7 +3170,7 @@
 extern boolean_t icmp_err_rate_limit(void);
 extern void	icmp_time_exceeded(queue_t *, mblk_t *, uint8_t, zoneid_t);
 extern void	icmp_unreachable(queue_t *, mblk_t *, uint8_t, zoneid_t);
-extern mblk_t	*ip_add_info(mblk_t *, ill_t *, uint_t);
+extern mblk_t	*ip_add_info(mblk_t *, ill_t *, uint_t, zoneid_t);
 extern mblk_t	*ip_bind_v4(queue_t *, mblk_t *, conn_t *);
 extern int	ip_bind_connected(conn_t *, mblk_t *, ipaddr_t *, uint16_t,
     ipaddr_t, uint16_t, boolean_t, boolean_t, boolean_t,
@@ -3181,6 +3210,8 @@
 extern void	ip_trash_timer_expire(void *);
 extern void	ip_wput(queue_t *, mblk_t *);
 extern void	ip_output(void *, mblk_t *, void *, int);
+extern void	ip_output_options(void *, mblk_t *, void *, int,
+    ip_opt_info_t *);
 extern void	ip_wput_md(queue_t *, mblk_t *, conn_t *);
 
 extern void	ip_wput_ire(queue_t *, mblk_t *, ire_t *, conn_t *, int,
@@ -3538,6 +3569,13 @@
 #define	SQTAG_TCP_KSSL_INPUT		36
 #define	SQTAG_TCP_DROP_Q0		37
 
+#define	NOT_OVER_IP(ip_wq)	\
+	(ip_wq->q_next != NULL ||	\
+	    (ip_wq->q_qinfo->qi_minfo->mi_idname) == NULL ||	\
+	    strcmp(ip_wq->q_qinfo->qi_minfo->mi_idname,	\
+	    IP_MOD_NAME) != 0 ||	\
+	    ip_wq->q_qinfo->qi_minfo->mi_idnum != IP_MOD_ID)
+
 #endif	/* _KERNEL */
 
 #ifdef	__cplusplus
--- a/usr/src/uts/common/inet/ip/icmp.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip/icmp.c	Fri Dec 22 01:03:02 2006 -0800
@@ -76,6 +76,11 @@
 #include <sys/tsol/label.h>
 #include <sys/tsol/tnet.h>
 
+#include <inet/ip_ire.h>
+#include <inet/ip_if.h>
+
+#include <inet/ip_impl.h>
+
 #define	ICMP6 "icmp6"
 major_t	ICMP6_MAJ;
 
@@ -1624,6 +1629,17 @@
 		case IP_RECVIF:
 			*ptr = icmp->icmp_recvif;
 			break;	/* goto sizeof (int) option return */
+		case IP_RECVPKTINFO:
+			/*
+			 * This also handles IP_PKTINFO.
+			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
+			 * Differentiation is based on the size of the argument
+			 * passed in.
+			 * This option is handled in IP which will return an
+			 * error for IP_PKTINFO as it's not supported as a
+			 * sticky option.
+			 */
+			return (-EINVAL);
 		/*
 		 * Cannot "get" the value of following options
 		 * at this level. Action is same as "default" to
@@ -1709,7 +1725,7 @@
 			/* cannot "get" the value for these */
 			return (-1);
 		case IPV6_RECVPKTINFO:
-			*i1 = icmp->icmp_ipv6_recvpktinfo;
+			*i1 = icmp->icmp_ip_recvpktinfo;
 			break;
 		case IPV6_RECVTCLASS:
 			*i1 = icmp->icmp_ipv6_recvtclass;
@@ -2138,6 +2154,57 @@
 			if (!checkonly)
 				icmp->icmp_recvif = onoff;
 			break;
+
+		case IP_PKTINFO: {
+			/*
+			 * This also handles IP_RECVPKTINFO.
+			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
+			 * Differentiation is based on the size of the argument
+			 * passed in.
+			 */
+			struct in_pktinfo *pktinfop;
+			ip4_pkt_t *attr_pktinfop;
+
+			if (checkonly)
+				break;
+
+			if (inlen == sizeof (int)) {
+				/*
+				 * This is IP_RECVPKTINFO option.
+				 * Keep a local copy of wether this option is
+				 * set or not and pass it down to IP for
+				 * processing.
+				 */
+				icmp->icmp_ip_recvpktinfo = onoff;
+				return (-EINVAL);
+			}
+
+
+			if (inlen != sizeof (struct in_pktinfo))
+				return (EINVAL);
+
+			if ((attr_pktinfop = (ip4_pkt_t *)thisdg_attrs)
+			    == NULL) {
+				/*
+				 * sticky option is not supported
+				 */
+				return (EINVAL);
+			}
+
+			pktinfop = (struct in_pktinfo *)invalp;
+
+			/*
+			 * Atleast one of the values should be specified
+			 */
+			if (pktinfop->ipi_ifindex == 0 &&
+			    pktinfop->ipi_spec_dst.s_addr == INADDR_ANY) {
+				return (EINVAL);
+			}
+
+			attr_pktinfop->ip4_addr = pktinfop->ipi_spec_dst.s_addr;
+			attr_pktinfop->ip4_ill_index = pktinfop->ipi_ifindex;
+		}
+			break;
 		case IP_ADD_MEMBERSHIP:
 		case IP_DROP_MEMBERSHIP:
 		case IP_BLOCK_SOURCE:
@@ -2319,7 +2386,7 @@
 		 */
 		case IPV6_RECVPKTINFO:
 			if (!checkonly)
-				icmp->icmp_ipv6_recvpktinfo = onoff;
+				icmp->icmp_ip_recvpktinfo = onoff;
 			break;
 		case IPV6_RECVPATHMTU:
 			if (!checkonly)
@@ -2879,7 +2946,7 @@
 	ip6_pkt_t		ipp;
 	uint8_t			nexthdr;
 	boolean_t		recvif = B_FALSE;
-	in_pktinfo_t		*pinfo;
+	ip_pktinfo_t		*pinfo = NULL;
 	mblk_t			*options_mp = NULL;
 	uint_t			icmp_opt = 0;
 	boolean_t		icmp_ipv6_recvhoplimit = B_FALSE;
@@ -2901,9 +2968,10 @@
 			freeb(mp);
 			mp = mp1;
 		} else {
-			pinfo = (in_pktinfo_t *)mp->b_rptr;
-			if ((icmp->icmp_recvif != 0) &&
-			    (pinfo->in_pkt_ulp_type == IN_PKTINFO)) {
+			pinfo = (ip_pktinfo_t *)mp->b_rptr;
+			if ((icmp->icmp_recvif != 0 ||
+			    icmp->icmp_ip_recvpktinfo) &&
+			    (pinfo->ip_pkt_ulp_type == IN_PKTINFO)) {
 				/*
 				 * IP has passed the options in mp and the
 				 * actual data is in b_cont.
@@ -3100,10 +3168,18 @@
 	if (icmp->icmp_family == AF_INET) {
 		ASSERT(ipvers == IPV4_VERSION);
 		udi_size =  sizeof (struct T_unitdata_ind) + sizeof (sin_t);
-		if (recvif) {
+		if (icmp->icmp_recvif && recvif &&
+		    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 			udi_size += sizeof (struct T_opthdr) +
 			    sizeof (uint_t);
 		}
+
+		if (icmp->icmp_ip_recvpktinfo && recvif &&
+		    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+			udi_size += sizeof (struct T_opthdr) +
+			    sizeof (struct in_pktinfo);
+		}
+
 		/*
 		 * If SO_TIMESTAMP is set allocate the appropriate sized
 		 * buffer. Since gethrestime() expects a pointer aligned
@@ -3146,7 +3222,8 @@
 			char *dstopt;
 
 			dstopt = (char *)&sin[1];
-			if (recvif) {
+			if (icmp->icmp_recvif && recvif &&
+			    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 
 				struct T_opthdr *toh;
 				uint_t		*dstptr;
@@ -3159,7 +3236,7 @@
 				toh->status = 0;
 				dstopt += sizeof (struct T_opthdr);
 				dstptr = (uint_t *)dstopt;
-				*dstptr = pinfo->in_pkt_ifindex;
+				*dstptr = pinfo->ip_pkt_ifindex;
 				dstopt += sizeof (uint_t);
 				freeb(options_mp);
 				udi_size -= toh->len;
@@ -3178,7 +3255,29 @@
 				dstopt = (char *)P2ROUNDUP((intptr_t)dstopt,
 				    sizeof (intptr_t));
 				gethrestime((timestruc_t *)dstopt);
-				dstopt += sizeof (timestruc_t);
+				dstopt = (char *)toh + toh->len;
+				udi_size -= toh->len;
+			}
+			if (icmp->icmp_ip_recvpktinfo && recvif &&
+			    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+				struct	T_opthdr *toh;
+				struct	in_pktinfo *pktinfop;
+
+				toh = (struct T_opthdr *)dstopt;
+				toh->level = IPPROTO_IP;
+				toh->name = IP_PKTINFO;
+				toh->len = sizeof (struct T_opthdr) +
+				    sizeof (in_pktinfo_t);
+				toh->status = 0;
+				dstopt += sizeof (struct T_opthdr);
+				pktinfop = (struct in_pktinfo *)dstopt;
+				pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex;
+				pktinfop->ipi_spec_dst =
+				    pinfo->ip_pkt_match_addr;
+
+				pktinfop->ipi_addr.s_addr = ipha->ipha_dst;
+
+				dstopt += sizeof (struct in_pktinfo);
 				udi_size -= toh->len;
 			}
 
@@ -3395,7 +3494,7 @@
 			    ipp.ipp_rthdrlen;
 			icmp_opt |= IPPF_RTHDR;
 		}
-		if (icmp->icmp_ipv6_recvpktinfo &&
+		if (icmp->icmp_ip_recvpktinfo &&
 		    (ipp.ipp_fields & IPPF_IFINDEX)) {
 			udi_size += sizeof (struct T_opthdr) +
 			    sizeof (struct in6_pktinfo);
@@ -3827,14 +3926,18 @@
  * IPPROTO_IGMP).
  */
 static void
-icmp_wput_hdrincl(queue_t *q, mblk_t *mp, icmp_t *icmp)
+icmp_wput_hdrincl(queue_t *q, mblk_t *mp, icmp_t *icmp, ip4_pkt_t *pktinfop,
+boolean_t use_putnext)
 {
 	ipha_t	*ipha;
 	int	ip_hdr_length;
 	int	tp_hdr_len;
 	mblk_t	*mp1;
 	uint_t	pkt_len;
-
+	ip_opt_info_t optinfo;
+
+	optinfo.ip_opt_flags = 0;
+	optinfo.ip_opt_ill_index = 0;
 	ipha = (ipha_t *)mp->b_rptr;
 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
 	if ((mp->b_wptr - mp->b_rptr) < IP_SIMPLE_HDR_LENGTH) {
@@ -3931,8 +4034,30 @@
 		 */
 		(void) ip_massage_options(ipha);
 	}
+
+	if (pktinfop != NULL) {
+		/*
+		 * Over write the source address provided in the header
+		 */
+		if (pktinfop->ip4_addr != INADDR_ANY) {
+			ipha->ipha_src = pktinfop->ip4_addr;
+			optinfo.ip_opt_flags = IP_VERIFY_SRC;
+			ASSERT(use_putnext == B_FALSE);
+		}
+
+		if (pktinfop->ip4_ill_index != 0) {
+			optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
+			ASSERT(use_putnext == B_FALSE);
+		}
+	}
+
 	mblk_setcred(mp, icmp->icmp_credp);
-	putnext(q, mp);
+	if (use_putnext) {
+		putnext(q, mp);
+	} else {
+		ip_output_options(Q_TO_CONN(q->q_next), mp, q->q_next, IP_WPUT,
+		    &optinfo);
+	}
 }
 
 static boolean_t
@@ -3979,6 +4104,11 @@
 	sin6_t	*sin6;
 	sin_t	*sin;
 	ipaddr_t	v4dst;
+	ip4_pkt_t	pktinfo;
+	ip4_pkt_t	*pktinfop = &pktinfo;
+	ip_opt_info_t	optinfo;
+	queue_t		*ip_wq;
+	boolean_t	use_putnext = B_TRUE;
 
 	icmp = (icmp_t *)q->q_ptr;
 	if (icmp->icmp_restricted) {
@@ -4011,7 +4141,7 @@
 			    !icmp_update_label(q, icmp, mp, ipha->ipha_dst)) {
 				return;
 			}
-			icmp_wput_hdrincl(q, mp, icmp);
+			icmp_wput_hdrincl(q, mp, icmp, NULL, use_putnext);
 			return;
 		}
 		freemsg(mp);
@@ -4033,6 +4163,8 @@
 
 	/* Handle T_UNITDATA_REQ messages here. */
 
+
+
 	if (icmp->icmp_state == TS_UNBND) {
 		/* If a port has not been bound to the stream, fail. */
 		BUMP_MIB(&rawip_mib, rawipOutErrors);
@@ -4094,25 +4226,47 @@
 		ASSERT(0);
 	}
 
+	pktinfop->ip4_ill_index = 0;
+	pktinfop->ip4_addr = INADDR_ANY;
+	optinfo.ip_opt_flags = 0;
+	optinfo.ip_opt_ill_index = 0;
+
+
 	/*
 	 * If options passed in, feed it for verification and handling
 	 */
 	if (tudr->OPT_length != 0) {
 		int error;
 
+		error = 0;
 		if (icmp_unitdata_opt_process(q, mp, &error,
-		    (uchar_t *)0) < 0) {
+		    (void *)pktinfop) < 0) {
 			/* failure */
 			BUMP_MIB(&rawip_mib, rawipOutErrors);
 			icmp_ud_err(q, mp, error);
 			return;
 		}
+		ASSERT(error == 0);
 		/*
 		 * Note: Success in processing options.
 		 * mp option buffer represented by
 		 * OPT_length/offset now potentially modified
 		 * and contain option setting results
 		 */
+
+		if (pktinfop->ip4_ill_index != 0 ||
+		    pktinfop->ip4_addr != INADDR_ANY) {
+			/*
+			 * PKTINFO option is supported only when ICMP is
+			 * over IP.
+			 */
+			ip_wq = WR(q)->q_next;
+			if (NOT_OVER_IP(ip_wq)) {
+				icmp_ud_err(q, mp, EINVAL);
+				return;
+			}
+			use_putnext = B_FALSE;
+		}
 	}
 
 	if (v4dst == INADDR_ANY)
@@ -4129,10 +4283,11 @@
 	/* Protocol 255 contains full IP headers */
 	if (icmp->icmp_hdrincl) {
 		freeb(mp);
-		icmp_wput_hdrincl(q, mp1, icmp);
+		icmp_wput_hdrincl(q, mp1, icmp, pktinfop, use_putnext);
 		return;
 	}
 
+
 	/* Add an IP header */
 	ip_hdr_length = IP_SIMPLE_HDR_LENGTH + icmp->icmp_ip_snd_options_len;
 	ipha = (ipha_t *)&mp1->b_rptr[-ip_hdr_length];
@@ -4165,13 +4320,27 @@
 	/* Set ttl and protocol */
 	*(uint16_t *)&ipha->ipha_ttl = (icmp->icmp_proto << 8) | icmp->icmp_ttl;
 #endif
-	/*
-	 * Copy our address into the packet.  If this is zero,
-	 * ip will fill in the real source address.
-	 */
-	IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src, ipha->ipha_src);
+	if (pktinfop->ip4_addr != INADDR_ANY) {
+		ASSERT(use_putnext == B_FALSE);
+		ipha->ipha_src = pktinfop->ip4_addr;
+		optinfo.ip_opt_flags = IP_VERIFY_SRC;
+	} else {
+
+		/*
+		 * Copy our address into the packet.  If this is zero,
+		 * ip will fill in the real source address.
+		 */
+		IN6_V4MAPPED_TO_IPADDR(&icmp->icmp_v6src, ipha->ipha_src);
+	}
+
 	ipha->ipha_fragment_offset_and_flags = 0;
 
+	if (pktinfop->ip4_ill_index != 0) {
+		optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
+		ASSERT(use_putnext == B_FALSE);
+	}
+
+
 	/*
 	 * For the socket of SOCK_RAW type, the checksum is provided in the
 	 * pre-built packet. We set the ipha_ident field to IP_HDR_INCLUDED to
@@ -4221,10 +4390,16 @@
 		 */
 		(void) ip_massage_options(ipha);
 	}
+
 	freeb(mp);
 	BUMP_MIB(&rawip_mib, rawipOutDatagrams);
 	mblk_setcred(mp1, icmp->icmp_credp);
-	putnext(q, mp1);
+	if (use_putnext) {
+		putnext(q, mp1);
+	} else {
+		ip_output_options(Q_TO_CONN(q->q_next), mp1, q->q_next, IP_WPUT,
+		    &optinfo);
+	}
 #undef	ipha
 #undef tudr
 }
--- a/usr/src/uts/common/inet/ip/icmp_opt_data.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip/icmp_opt_data.c	Fri Dec 22 01:03:02 2006 -0800
@@ -150,8 +150,12 @@
 
 { IP_RECVIF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT, sizeof (int), 0 },
 
+{ IP_PKTINFO, IPPROTO_IP, OA_RW, OA_RW, OP_NP,
+	(OP_PASSNEXT|OP_NODEFAULT|OP_VARLEN),
+	sizeof (struct in_pktinfo), -1 /* not initialized */ },
+
 { IP_NEXTHOP, IPPROTO_IP, OA_RW, OA_RW, OP_CONFIG, OP_PASSNEXT,
-	sizeof (in_addr_t),	-1 /* not initialized */ },
+	sizeof (in_addr_t), -1 /* not initialized */ },
 
 { MRT_INIT, IPPROTO_IP, 0, OA_X, OP_CONFIG,
 	(OP_PASSNEXT|OP_NODEFAULT), sizeof (int),
--- a/usr/src/uts/common/inet/ip/ip.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip/ip.c	Fri Dec 22 01:03:02 2006 -0800
@@ -746,7 +746,7 @@
 void		ip_newroute(queue_t *, mblk_t *, ipaddr_t, ill_t *, conn_t *,
 		    zoneid_t);
 static void	ip_newroute_ipif(queue_t *, mblk_t *, ipif_t *, ipaddr_t,
-		    conn_t *, uint32_t, zoneid_t);
+		    conn_t *, uint32_t, zoneid_t, ip_opt_info_t *);
 char		*ip_nv_lookup(nv_t *, int);
 static boolean_t	ip_check_for_ipsec_opt(queue_t *, mblk_t *);
 static int	ip_param_get(queue_t *, mblk_t *, caddr_t, cred_t *);
@@ -4249,27 +4249,79 @@
  * application.
  */
 mblk_t *
-ip_add_info(mblk_t *data_mp, ill_t *ill, uint_t flags)
+ip_add_info(mblk_t *data_mp, ill_t *ill, uint_t flags, zoneid_t zoneid)
 {
 	mblk_t		*mp;
-	in_pktinfo_t	*pinfo;
+	ip_pktinfo_t	*pinfo;
 	ipha_t *ipha;
 	struct ether_header *pether;
 
-	mp = allocb(sizeof (in_pktinfo_t), BPRI_MED);
+	mp = allocb(sizeof (ip_pktinfo_t), BPRI_MED);
 	if (mp == NULL) {
 		ip1dbg(("ip_add_info: allocation failure.\n"));
 		return (data_mp);
 	}
 
 	ipha	= (ipha_t *)data_mp->b_rptr;
-	pinfo = (in_pktinfo_t *)mp->b_rptr;
-	bzero(pinfo, sizeof (in_pktinfo_t));
-	pinfo->in_pkt_flags = (uchar_t)flags;
-	pinfo->in_pkt_ulp_type = IN_PKTINFO;	/* Tell ULP what type of info */
-
-	if (flags & IPF_RECVIF)
-		pinfo->in_pkt_ifindex = ill->ill_phyint->phyint_ifindex;
+	pinfo = (ip_pktinfo_t *)mp->b_rptr;
+	bzero(pinfo, sizeof (ip_pktinfo_t));
+	pinfo->ip_pkt_flags = (uchar_t)flags;
+	pinfo->ip_pkt_ulp_type = IN_PKTINFO;	/* Tell ULP what type of info */
+
+	if (flags & (IPF_RECVIF | IPF_RECVADDR))
+		pinfo->ip_pkt_ifindex = ill->ill_phyint->phyint_ifindex;
+	if (flags & IPF_RECVADDR) {
+		ipif_t	*ipif;
+		ire_t	*ire;
+
+		/*
+		 * Only valid for V4
+		 */
+		ASSERT((ipha->ipha_version_and_hdr_length & 0xf0) ==
+		    (IPV4_VERSION << 4));
+
+		ipif = ipif_get_next_ipif(NULL, ill);
+		if (ipif != NULL) {
+			/*
+			 * Since a decision has already been made to deliver the
+			 * packet, there is no need to test for SECATTR and
+			 * ZONEONLY.
+			 */
+			ire = ire_ctable_lookup(ipha->ipha_dst, 0, 0, ipif,
+			    zoneid, NULL, MATCH_IRE_ILL_GROUP);
+			if (ire == NULL) {
+				/*
+				 * packet must have come on a different
+				 * interface.
+				 * Since a decision has already been made to
+				 * deliver the packet, there is no need to test
+				 * for SECATTR and ZONEONLY.
+				 */
+				ire = ire_ctable_lookup(ipha->ipha_dst, 0, 0,
+				    ipif, zoneid, NULL, NULL);
+			}
+
+			if (ire == NULL) {
+				/*
+				 * This is either a multicast packet or
+				 * the address has been removed since
+				 * the packet was received.
+				 * Return INADDR_ANY so that normal source
+				 * selection occurs for the response.
+				 */
+
+				pinfo->ip_pkt_match_addr.s_addr = INADDR_ANY;
+			} else {
+				ASSERT(ire->ire_type != IRE_CACHE);
+				pinfo->ip_pkt_match_addr.s_addr =
+				    ire->ire_src_addr;
+				ire_refrele(ire);
+			}
+			ipif_refrele(ipif);
+		} else {
+			pinfo->ip_pkt_match_addr.s_addr = INADDR_ANY;
+		}
+	}
 
 	pether = (struct ether_header *)((char *)ipha
 	    - sizeof (struct ether_header));
@@ -4284,19 +4336,19 @@
 	    (ill->ill_type == IFT_ETHER) &&
 	    (ill->ill_net_type == IRE_IF_RESOLVER)) {
 
-		pinfo->in_pkt_slla.sdl_type = IFT_ETHER;
+		pinfo->ip_pkt_slla.sdl_type = IFT_ETHER;
 		bcopy((uchar_t *)pether->ether_shost.ether_addr_octet,
-		    (uchar_t *)pinfo->in_pkt_slla.sdl_data, ETHERADDRL);
+		    (uchar_t *)pinfo->ip_pkt_slla.sdl_data, ETHERADDRL);
 	} else {
 		/*
 		 * Clear the bit. Indicate to upper layer that IP is not
 		 * sending this ancillary info.
 		 */
-		pinfo->in_pkt_flags = pinfo->in_pkt_flags & ~IPF_RECVSLLA;
+		pinfo->ip_pkt_flags = pinfo->ip_pkt_flags & ~IPF_RECVSLLA;
 	}
 
 	mp->b_datap->db_type = M_CTL;
-	mp->b_wptr += sizeof (in_pktinfo_t);
+	mp->b_wptr += sizeof (ip_pktinfo_t);
 	mp->b_cont = data_mp;
 
 	return (mp);
@@ -6372,6 +6424,7 @@
 				    mctl_present);
 			}
 			if (first_mp1 != NULL) {
+				int in_flags = 0;
 				/*
 				 * ip_fanout_proto also gets called from
 				 * icmp_inbound_error_fanout, in which case
@@ -6381,7 +6434,28 @@
 				 * inbound iface index for ICMP error msgs,
 				 * then this can be changed.
 				 */
-				if ((connp->conn_recvif != 0) &&
+				if (connp->conn_recvif)
+					in_flags = IPF_RECVIF;
+				/*
+				 * The ULP may support IP_RECVPKTINFO for both
+				 * IP v4 and v6 so pass the appropriate argument
+				 * based on conn IP version.
+				 */
+				if (connp->conn_ip_recvpktinfo) {
+					if (connp->conn_af_isv6) {
+						/*
+						 * V6 only needs index
+						 */
+						in_flags |= IPF_RECVIF;
+					} else {
+						/*
+						 * V4 needs index +
+						 * matching address.
+						 */
+						in_flags |= IPF_RECVADDR;
+					}
+				}
+				if ((in_flags != 0) &&
 				    (mp->b_datap->db_type != M_CTL)) {
 					/*
 					 * the actual data will be
@@ -6392,7 +6466,7 @@
 					 */
 					ASSERT(recv_ill != NULL);
 					mp1 = ip_add_info(mp1, recv_ill,
-						IPF_RECVIF);
+					    in_flags, IPCL_ZONEID(connp));
 				}
 				BUMP_MIB(mibptr, ipIfStatsHCInDelivers);
 				if (mctl_present)
@@ -6455,6 +6529,8 @@
 		}
 
 		if (first_mp != NULL) {
+			int in_flags = 0;
+
 			/*
 			 * ip_fanout_proto also gets called
 			 * from icmp_inbound_error_fanout, in
@@ -6465,8 +6541,25 @@
 			 * index for ICMP error msgs, then this
 			 * can be changed
 			 */
-			if ((connp->conn_recvif != 0) &&
+			if (connp->conn_recvif)
+				in_flags = IPF_RECVIF;
+			if (connp->conn_ip_recvpktinfo) {
+				if (connp->conn_af_isv6) {
+					/*
+					 * V6 only needs index
+					 */
+					in_flags |= IPF_RECVIF;
+				} else {
+					/*
+					 * V4 needs index +
+					 * matching address.
+					 */
+					in_flags |= IPF_RECVADDR;
+				}
+			}
+			if ((in_flags != 0) &&
 			    (mp->b_datap->db_type != M_CTL)) {
+
 				/*
 				 * the actual data will be contained in
 				 * b_cont upon successful return
@@ -6474,7 +6567,8 @@
 				 * mblk is returned
 				 */
 				ASSERT(recv_ill != NULL);
-				mp = ip_add_info(mp, recv_ill, IPF_RECVIF);
+				mp = ip_add_info(mp, recv_ill,
+				    in_flags, IPCL_ZONEID(connp));
 			}
 			BUMP_MIB(mibptr, ipIfStatsHCInDelivers);
 			putnext(rq, mp);
@@ -6651,12 +6745,16 @@
 
 
 
-	/* Handle IPv6 socket options. */
+	/* Handle socket options. */
 	if (!syn_present &&
-	    connp->conn_ipv6_recvpktinfo && (flags & IP_FF_IP6INFO)) {
+	    connp->conn_ip_recvpktinfo && (flags & IP_FF_IPINFO)) {
 		/* Add header */
 		ASSERT(recv_ill != NULL);
-		mp = ip_add_info(mp, recv_ill, IPF_RECVIF);
+		/*
+		 * Since tcp does not support IP_RECVPKTINFO for V4, only pass
+		 * IPF_RECVIF.
+		 */
+		mp = ip_add_info(mp, recv_ill, IPF_RECVIF, IPCL_ZONEID(connp));
 		if (mp == NULL) {
 			BUMP_MIB(recv_ill->ill_ip_mib, ipIfStatsInDiscards);
 			CONN_DEC_REF(connp);
@@ -6729,15 +6827,30 @@
 	if (mctl_present)
 		freeb(first_mp);
 
+	/* Handle options. */
 	if (connp->conn_recvif)
 		in_flags = IPF_RECVIF;
+	/*
+	 * UDP supports IP_RECVPKTINFO option for both v4 and v6 so the flag
+	 * passed to ip_add_info is based on IP version of connp.
+	 */
+	if (connp->conn_ip_recvpktinfo && (flags & IP_FF_IPINFO)) {
+		if (connp->conn_af_isv6) {
+			/*
+			 * V6 only needs index
+			 */
+			in_flags |= IPF_RECVIF;
+		} else {
+			/*
+			 * V4 needs index + matching address.
+			 */
+			in_flags |= IPF_RECVADDR;
+		}
+	}
+
 	if (connp->conn_recvslla && !(flags & IP_FF_SEND_SLLA))
 		in_flags |= IPF_RECVSLLA;
 
-	/* Handle IPv6 options. */
-	if (connp->conn_ipv6_recvpktinfo && (flags & IP_FF_IP6INFO))
-		in_flags |= IPF_RECVIF;
-
 	/*
 	 * Initiate IPPF processing here, if needed. Note first_mp won't be
 	 * freed if the packet is dropped. The caller will do so.
@@ -6757,7 +6870,7 @@
 		 * else original mblk is returned
 		 */
 		ASSERT(recv_ill != NULL);
-		mp = ip_add_info(mp, recv_ill, in_flags);
+		mp = ip_add_info(mp, recv_ill, in_flags, IPCL_ZONEID(connp));
 	}
 	BUMP_MIB(ill->ill_ip_mib, ipIfStatsHCInDelivers);
 	/* Send it upstream */
@@ -8888,6 +9001,8 @@
 	icmp_unreachable(q, first_mp, ICMP_HOST_UNREACHABLE, zoneid);
 }
 
+ip_opt_info_t zero_info;
+
 /*
  * IPv4 -
  * ip_newroute_ipif is called by ip_wput_multicast and
@@ -8916,7 +9031,7 @@
  */
 static void
 ip_newroute_ipif(queue_t *q, mblk_t *mp, ipif_t *ipif, ipaddr_t dst,
-    conn_t *connp, uint32_t flags, zoneid_t zoneid)
+    conn_t *connp, uint32_t flags, zoneid_t zoneid, ip_opt_info_t *infop)
 {
 	areq_t	*areq;
 	ire_t	*ire = NULL;
@@ -9140,7 +9255,8 @@
 		 * route optimization.
 		 */
 		if (CLASSD(ipha_dst) && (connp == NULL ||
-		    connp->conn_xmit_if_ill == NULL)) {
+		    connp->conn_xmit_if_ill == NULL) &&
+		    infop->ip_opt_ill_index == 0) {
 			/* ipif_to_ire returns an held ire */
 			ire = ipif_to_ire(ipif);
 			if (ire == NULL)
@@ -9186,7 +9302,8 @@
 		} else {
 			ASSERT((connp == NULL) ||
 			    (connp->conn_xmit_if_ill != NULL) ||
-			    (connp->conn_dontroute));
+			    (connp->conn_dontroute) ||
+			    infop->ip_opt_ill_index != 0);
 			/*
 			 * The only ways we can come here are:
 			 * 1) IP_XMIT_IF socket option is set
@@ -9194,6 +9311,7 @@
 			 *    ip_mrtun_forward() routine and it needs
 			 *    to go through the specified ill.
 			 * 3) SO_DONTROUTE socket option is set
+			 * 4) IP_PKTINFO option is passed in as ancillary data.
 			 * In all cases, the new ire will not be added
 			 * into cache table.
 			 */
@@ -10946,6 +11064,13 @@
 				mutex_exit(&connp->conn_lock);
 			}
 			break;	/* goto sizeof (int) option return */
+		case IP_RECVPKTINFO:
+			if (!checkonly) {
+				mutex_enter(&connp->conn_lock);
+				connp->conn_ip_recvpktinfo = *i1 ? 1 : 0;
+				mutex_exit(&connp->conn_lock);
+			}
+			break;	/* goto sizeof (int) option return */
 		case IP_RECVSLLA:
 			/* Retrieve the source link layer address */
 			if (!checkonly) {
@@ -11242,7 +11367,7 @@
 		case IPV6_RECVPKTINFO:
 			if (!checkonly) {
 				mutex_enter(&connp->conn_lock);
-				connp->conn_ipv6_recvpktinfo = *i1 ? 1 : 0;
+				connp->conn_ip_recvpktinfo = *i1 ? 1 : 0;
 				mutex_exit(&connp->conn_lock);
 			}
 			break;	/* goto sizeof (int) option return */
@@ -11493,6 +11618,9 @@
 				return (sizeof (ipaddr_t));
 			} else
 				return (0);
+		case IP_RECVPKTINFO:
+			*(int *)ptr = connp->conn_ip_recvpktinfo ? 1: 0;
+			return (sizeof (int));
 		default:
 			break;
 		}
@@ -12017,11 +12145,28 @@
 	 * IP_RECVSLLA and/or IPV6_RECVPKTINFO options are not set
 	 */
 	if (connp->conn_recvif || connp->conn_recvslla ||
-	    connp->conn_ipv6_recvpktinfo) {
-		if (connp->conn_recvif ||
-		    connp->conn_ipv6_recvpktinfo) {
+	    connp->conn_ip_recvpktinfo) {
+		if (connp->conn_recvif) {
 			in_flags = IPF_RECVIF;
 		}
+		/*
+		 * UDP supports IP_RECVPKTINFO option for both v4 and v6
+		 * so the flag passed to ip_add_info is based on IP version
+		 * of connp.
+		 */
+		if (connp->conn_ip_recvpktinfo) {
+			if (connp->conn_af_isv6) {
+				/*
+				 * V6 only needs index
+				 */
+				in_flags |= IPF_RECVIF;
+			} else {
+				/*
+				 * V4 needs index + matching address.
+				 */
+				in_flags |= IPF_RECVADDR;
+			}
+		}
 		if (connp->conn_recvslla) {
 			in_flags |= IPF_RECVSLLA;
 		}
@@ -12036,7 +12181,7 @@
 		 * If the call fails then the original mblk is
 		 * returned.
 		 */
-		*mpp = ip_add_info(*mpp, ill, in_flags);
+		*mpp = ip_add_info(*mpp, ill, in_flags, IPCL_ZONEID(connp));
 	}
 
 	return (B_TRUE);
@@ -12780,7 +12925,7 @@
 
 	ip_fanout_udp(q, first_mp, ill, ipha, *(uint32_t *)up,
 	    (ire->ire_type == IRE_BROADCAST),
-	    IP_FF_SEND_ICMP | IP_FF_CKSUM | IP_FF_IP6INFO,
+	    IP_FF_SEND_ICMP | IP_FF_CKSUM | IP_FF_IPINFO,
 	    mctl_present, B_TRUE, recv_ill, ire->ire_zoneid);
 
 slow_done:
@@ -13074,8 +13219,13 @@
 
 	}
 
-	if (!syn_present && connp->conn_ipv6_recvpktinfo) {
-		mp = ip_add_info(mp, recv_ill, flags);
+	if (!syn_present && connp->conn_ip_recvpktinfo) {
+		/*
+		 * TCP does not support IP_RECVPKTINFO for v4 so lets
+		 * make sure IPF_RECVIF is passed to ip_add_info.
+		 */
+		mp = ip_add_info(mp, recv_ill, flags|IPF_RECVIF,
+		    IPCL_ZONEID(connp));
 		if (mp == NULL) {
 			BUMP_MIB(ill->ill_ip_mib, ipIfStatsInDiscards);
 			CONN_DEC_REF(connp);
@@ -16534,7 +16684,7 @@
 		mp->b_prev = NULL;
 		mp->b_next = mp;
 		ip_newroute_ipif(ipif->ipif_ill->ill_wq, mp, ipif, dst,
-		    NULL, 0, GLOBAL_ZONEID);
+		    NULL, 0, GLOBAL_ZONEID, &zero_info);
 	} else {
 		ip_rput_forward(ire, (ipha_t *)mp->b_rptr, mp, NULL);
 		IRE_REFRELE(ire);
@@ -19877,6 +20027,13 @@
 void
 ip_output(void *arg, mblk_t *mp, void *arg2, int caller)
 {
+	ip_output_options(arg, mp, arg2, caller, &zero_info);
+}
+
+void
+ip_output_options(void *arg, mblk_t *mp, void *arg2, int caller,
+    ip_opt_info_t *infop)
+{
 	conn_t		*connp = NULL;
 	queue_t		*q = (queue_t *)arg2;
 	ipha_t		*ipha;
@@ -19992,6 +20149,48 @@
 		ipha->ipha_length = htons(iplen);
 	}
 
+	ASSERT(infop != NULL);
+
+	if (infop->ip_opt_flags & IP_VERIFY_SRC) {
+		/*
+		 * IP_PKTINFO ancillary option is present.
+		 * IPCL_ZONEID is used to honor IP_ALLZONES option which
+		 * allows using address of any zone as the source address.
+		 */
+		ire = ire_ctable_lookup(ipha->ipha_src, 0,
+		    (IRE_LOCAL|IRE_LOOPBACK), NULL, IPCL_ZONEID(connp),
+		    NULL, MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY);
+		if (ire == NULL)
+			goto drop_pkt;
+		ire_refrele(ire);
+		ire = NULL;
+	}
+
+	/*
+	 * IP_DONTFAILOVER_IF and IP_XMIT_IF have precedence over
+	 * ill index passed in IP_PKTINFO.
+	 */
+	if (infop->ip_opt_ill_index != 0 &&
+	    connp->conn_xmit_if_ill == NULL &&
+	    connp->conn_nofailover_ill == NULL) {
+
+		xmit_ill = ill_lookup_on_ifindex(
+		    infop->ip_opt_ill_index, B_FALSE, NULL, NULL, NULL, NULL);
+
+		if (xmit_ill == NULL || IS_VNI(xmit_ill))
+			goto drop_pkt;
+		/*
+		 * check that there is an ipif belonging
+		 * to our zone. IPCL_ZONEID is not used because
+		 * IP_ALLZONES option is valid only when the ill is
+		 * accessible from all zones i.e has a valid ipif in
+		 * all zones.
+		 */
+		if (!ipif_lookup_zoneid_group(xmit_ill, zoneid, 0, NULL)) {
+			goto drop_pkt;
+		}
+	}
+
 	/*
 	 * If there is a policy, try to attach an ipsec_out in
 	 * the front. At the end, first_mp either points to a
@@ -20036,10 +20235,19 @@
 		}
 	}
 
+
 	/* is packet multicast? */
 	if (CLASSD(dst))
 		goto multicast;
 
+	/*
+	 * If xmit_ill is set above due to index passed in ip_pkt_info. It
+	 * takes precedence over conn_dontroute and conn_nexthop_set
+	 */
+	if (xmit_ill != NULL) {
+		goto send_from_ill;
+	}
+
 	if ((connp->conn_dontroute) || (connp->conn_xmit_if_ill != NULL) ||
 	    (connp->conn_nexthop_set)) {
 		/*
@@ -20657,7 +20865,6 @@
 
 	    multicast:
 		ASSERT(first_mp != NULL);
-		ASSERT(xmit_ill == NULL);
 		ip2dbg(("ip_wput: CLASSD\n"));
 		if (connp == NULL) {
 			/*
@@ -20702,18 +20909,22 @@
 			    ntohl(dst), ill->ill_name));
 		} else {
 			/*
-			 * If both IP_MULTICAST_IF and IP_XMIT_IF are set,
-			 * IP_XMIT_IF is honoured.
+			 * The order of precedence is IP_XMIT_IF, IP_PKTINFO
+			 * and IP_MULTICAST_IF.
 			 * Block comment above this function explains the
 			 * locking mechanism used here
 			 */
-			xmit_ill = conn_get_held_ill(connp,
-			    &connp->conn_xmit_if_ill, &err);
-			if (err == ILL_LOOKUP_FAILED) {
-				ip1dbg(("ip_wput: No ill for IP_XMIT_IF\n"));
-				BUMP_MIB(&ip_mib, ipIfStatsOutNoRoutes);
-				goto drop_pkt;
-			}
+			if (xmit_ill == NULL) {
+				xmit_ill = conn_get_held_ill(connp,
+				    &connp->conn_xmit_if_ill, &err);
+				if (err == ILL_LOOKUP_FAILED) {
+					ip1dbg(("ip_wput: No ill for "
+					    "IP_XMIT_IF\n"));
+					BUMP_MIB(&ip_mib, ipIfStatsOutNoRoutes);
+					goto drop_pkt;
+				}
+			}
+
 			if (xmit_ill == NULL) {
 				ipif = conn_get_held_ipif(connp,
 				    &connp->conn_multicast_ipif, &err);
@@ -20904,7 +21115,7 @@
 			if (xmit_ill == NULL ||
 			    xmit_ill->ill_ipif_up_count > 0) {
 				ip_newroute_ipif(q, first_mp, ipif, dst, connp,
-				    setsrc | RTF_MULTIRT, zoneid);
+				    setsrc | RTF_MULTIRT, zoneid, infop);
 				TRACE_2(TR_FAC_IP, TR_IP_WPUT_END,
 				    "ip_wput_end: q %p (%S)", q, "noire");
 			} else {
@@ -21069,30 +21280,56 @@
 				}
 			}
 			/*
-			 * could be SO_DONTROUTE case also.
+			 * Could be SO_DONTROUTE case also.
 			 * check at least one interface is UP as
-			 * spcified by this ILL, and then call
-			 * ip_newroute_ipif()
+			 * specified by this ILL
 			 */
 			if (xmit_ill->ill_ipif_up_count > 0) {
 				ipif_t *ipif;
 
 				ipif = ipif_get_next_ipif(NULL, xmit_ill);
-				if (ipif != NULL) {
+				if (ipif == NULL) {
+					ip1dbg(("ip_output: "
+					    "xmit_ill NULL ipif\n"));
+					goto drop_pkt;
+				}
+				/*
+				 * Look for a ire that is part of the group,
+				 * if found use it else call ip_newroute_ipif.
+				 * IPCL_ZONEID is not used for matching because
+				 * IP_ALLZONES option is valid only when the
+				 * ill is accessible from all zones i.e has a
+				 * valid ipif in all zones.
+				 */
+				match_flags = MATCH_IRE_ILL_GROUP |
+				    MATCH_IRE_SECATTR;
+				ire = ire_ctable_lookup(dst, 0, 0, ipif, zoneid,
+				    MBLK_GETLABEL(mp), match_flags);
+				/*
+				 * If an ire exists use it or else create
+				 * an ire but don't add it to the cache.
+				 * Adding an ire may cause issues with
+				 * asymmetric routing.
+				 * In case of multiroute always act as if
+				 * ire does not exist.
+				 */
+				if (ire == NULL ||
+				    ire->ire_flags & RTF_MULTIRT) {
+					if (ire != NULL)
+						ire_refrele(ire);
 					ip_newroute_ipif(q, first_mp, ipif,
-					    dst, connp, 0, zoneid);
+					    dst, connp, 0, zoneid, infop);
 					ipif_refrele(ipif);
 					ip1dbg(("ip_wput: ip_unicast_if\n"));
-				}
-			} else {
-				freemsg(first_mp);
-			}
-			ill_refrele(xmit_ill);
-			TRACE_2(TR_FAC_IP, TR_IP_WPUT_END,
-			    "ip_wput_end: q %p (%S)", q, "unicast_if");
-			if (need_decref)
-				CONN_DEC_REF(connp);
-			return;
+					ill_refrele(xmit_ill);
+					if (need_decref)
+						CONN_DEC_REF(connp);
+					return;
+				}
+				ipif_refrele(ipif);
+			} else {
+				goto drop_pkt;
+			}
 		} else if (ip_nexthop || (connp != NULL &&
 		    (connp->conn_nexthop_set)) && !ignore_nexthop) {
 			if (!ip_nexthop) {
@@ -21233,8 +21470,9 @@
 		if (CLASSD(dst)) {
 			ipif_t *ipif = ipif_lookup_group(dst, zoneid);
 			if (ipif) {
+				ASSERT(infop->ip_opt_ill_index == 0);
 				ip_newroute_ipif(q, copy_mp, ipif, dst, connp,
-				    RTF_SETSRC | RTF_MULTIRT, zoneid);
+				    RTF_SETSRC | RTF_MULTIRT, zoneid, infop);
 				ipif_refrele(ipif);
 			} else {
 				MULTIRT_DEBUG_UNTAG(copy_mp);
@@ -24873,7 +25111,7 @@
 		ip_fanout_udp(q, first_mp, ill, ipha, ports,
 		    (ire_type == IRE_BROADCAST),
 		    fanout_flags | IP_FF_SEND_ICMP | IP_FF_HDR_COMPLETE |
-		    IP_FF_SEND_SLLA | IP_FF_IP6INFO, mctl_present, B_FALSE,
+		    IP_FF_SEND_SLLA | IP_FF_IPINFO, mctl_present, B_FALSE,
 		    ill, zoneid);
 		TRACE_2(TR_FAC_IP, TR_IP_WPUT_LOCAL_END,
 		    "ip_wput_local_end: q %p (%S)", q, "ip_fanout_udp");
@@ -24905,7 +25143,7 @@
 		    <= mp->b_wptr);
 		ip_fanout_tcp(q, first_mp, ill, ipha,
 		    fanout_flags | IP_FF_SEND_ICMP | IP_FF_HDR_COMPLETE |
-		    IP_FF_SYN_ADDIRE | IP_FF_IP6INFO,
+		    IP_FF_SYN_ADDIRE | IP_FF_IPINFO,
 		    mctl_present, B_FALSE, zoneid);
 		TRACE_2(TR_FAC_IP, TR_IP_WPUT_LOCAL_END,
 		    "ip_wput_local_end: q %p (%S)", q, "ip_fanout_tcp");
@@ -24918,7 +25156,7 @@
 		bcopy(rptr + IPH_HDR_LENGTH(ipha), &ports, sizeof (ports));
 		ip_fanout_sctp(first_mp, ill, ipha, ports,
 		    fanout_flags | IP_FF_SEND_ICMP | IP_FF_HDR_COMPLETE |
-		    IP_FF_IP6INFO,
+		    IP_FF_IPINFO,
 		    mctl_present, B_FALSE, 0, zoneid);
 		return;
 	}
@@ -25127,7 +25365,7 @@
 		mp->b_prev = NULL;
 		mp->b_next = NULL;
 		ip_newroute_ipif(q, first_mp, ipif, dst, NULL, RTF_SETSRC,
-		    zoneid);
+		    zoneid, &zero_info);
 		return;
 	}
 
@@ -25695,7 +25933,7 @@
 		 * Also handle RTF_MULTIRT routes.
 		 */
 		ip_newroute_ipif(q, ipsec_mp, ipif, dst, NULL, RTF_MULTIRT,
-		    zoneid);
+		    zoneid, &zero_info);
 	} else {
 		if (attach_if) {
 			ire = ire_ctable_lookup(dst, 0, 0, ill->ill_ipif,
@@ -29174,19 +29412,24 @@
 	}
 
 	if (connp->conn_recvif || connp->conn_recvslla ||
-	    ((connp->conn_ipv6_recvpktinfo ||
+	    ((connp->conn_ip_recvpktinfo ||
 	    (!isv4 && IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src))) &&
-	    (flags & IP_FF_IP6INFO))) {
+	    (flags & IP_FF_IPINFO))) {
 		int in_flags = 0;
 
-		if (connp->conn_recvif || connp->conn_ipv6_recvpktinfo) {
+		/*
+		 * Since sctp does not support IP_RECVPKTINFO for v4, only pass
+		 * IPF_RECVIF.
+		 */
+		if (connp->conn_recvif || connp->conn_ip_recvpktinfo) {
 			in_flags = IPF_RECVIF;
 		}
 		if (connp->conn_recvslla) {
 			in_flags |= IPF_RECVSLLA;
 		}
 		if (isv4) {
-			mp = ip_add_info(mp, recv_ill, in_flags);
+			mp = ip_add_info(mp, recv_ill, in_flags,
+			    IPCL_ZONEID(connp));
 		} else {
 			mp = ip_add_info_v6(mp, recv_ill, &ip6h->ip6_dst);
 			if (mp == NULL) {
--- a/usr/src/uts/common/inet/ip/ip6.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip/ip6.c	Fri Dec 22 01:03:02 2006 -0800
@@ -3332,9 +3332,9 @@
 		 * For link-local always add ifindex so that transport can set
 		 * sin6_scope_id. Avoid it for ICMP error fanout.
 		 */
-		if ((connp->conn_ipv6_recvpktinfo ||
+		if ((connp->conn_ip_recvpktinfo ||
 		    IN6_IS_ADDR_LINKLOCAL(&src)) &&
-		    (flags & IP_FF_IP6INFO)) {
+		    (flags & IP_FF_IPINFO)) {
 			/* Add header */
 			mp1 = ip_add_info_v6(mp1, inill, &dst);
 		}
@@ -3399,8 +3399,8 @@
 	 * For link-local always add ifindex so that transport can set
 	 * sin6_scope_id. Avoid it for ICMP error fanout.
 	 */
-	if ((connp->conn_ipv6_recvpktinfo || IN6_IS_ADDR_LINKLOCAL(&src)) &&
-	    (flags & IP_FF_IP6INFO)) {
+	if ((connp->conn_ip_recvpktinfo || IN6_IS_ADDR_LINKLOCAL(&src)) &&
+	    (flags & IP_FF_IPINFO)) {
 		/* Add header */
 		mp = ip_add_info_v6(mp, inill, &dst);
 		if (mp == NULL) {
@@ -3708,9 +3708,9 @@
 	 * For link-local always add ifindex so that TCP can bind to that
 	 * interface. Avoid it for ICMP error fanout.
 	 */
-	if (!syn_present && ((connp->conn_ipv6_recvpktinfo ||
+	if (!syn_present && ((connp->conn_ip_recvpktinfo ||
 	    IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src)) &&
-	    (flags & IP_FF_IP6INFO))) {
+	    (flags & IP_FF_IPINFO))) {
 		/* Add header */
 		mp = ip_add_info_v6(mp, inill, &ip6h->ip6_dst);
 		if (mp == NULL) {
@@ -3856,9 +3856,9 @@
 		 * transport can set sin6_scope_id. Avoid it for
 		 * ICMP error fanout.
 		 */
-		if ((connp->conn_ipv6_recvpktinfo ||
+		if ((connp->conn_ip_recvpktinfo ||
 		    IN6_IS_ADDR_LINKLOCAL(&src)) &&
-		    (flags & IP_FF_IP6INFO)) {
+		    (flags & IP_FF_IPINFO)) {
 				/* Add header */
 			mp = ip_add_info_v6(mp, inill, &dst);
 			if (mp == NULL) {
@@ -3937,9 +3937,9 @@
 		 * can set sin6_scope_id. Avoid it for ICMP error
 		 * fanout.
 		 */
-		if ((connp->conn_ipv6_recvpktinfo ||
+		if ((connp->conn_ip_recvpktinfo ||
 		    IN6_IS_ADDR_LINKLOCAL(&src)) &&
-		    (flags & IP_FF_IP6INFO)) {
+		    (flags & IP_FF_IPINFO)) {
 			/* Add header */
 			mp1 = ip_add_info_v6(mp1, inill, &dst);
 		}
@@ -4007,8 +4007,8 @@
 	 * For link-local always add ifindex so that transport can set
 	 * sin6_scope_id. Avoid it for ICMP error fanout.
 	 */
-	if ((connp->conn_ipv6_recvpktinfo ||
-	    IN6_IS_ADDR_LINKLOCAL(&src)) && (flags & IP_FF_IP6INFO)) {
+	if ((connp->conn_ip_recvpktinfo ||
+	    IN6_IS_ADDR_LINKLOCAL(&src)) && (flags & IP_FF_IPINFO)) {
 		/* Add header */
 		mp = ip_add_info_v6(mp, inill, &dst);
 		if (mp == NULL) {
@@ -4056,7 +4056,7 @@
 	 */
 	if (ipcl_proto_fanout_v6[IPPROTO_UDP].connf_head != NULL) {
 		ip_fanout_proto_v6(q, first_mp, ip6h, ill, inill, IPPROTO_UDP,
-		    0, flags | IP_FF_RAWIP | IP_FF_IP6INFO, mctl_present,
+		    0, flags | IP_FF_RAWIP | IP_FF_IPINFO, mctl_present,
 		    zoneid);
 	} else {
 		if (ip_fanout_send_icmp_v6(q, first_mp, flags,
@@ -7885,7 +7885,7 @@
 tcp_fanout:
 			ip_fanout_tcp_v6(q, first_mp, ip6h, ill, inill,
 			    (flags|IP_FF_SEND_ICMP|IP_FF_SYN_ADDIRE|
-			    IP_FF_IP6INFO), hdr_len, mctl_present, zoneid);
+			    IP_FF_IPINFO), hdr_len, mctl_present, zoneid);
 			return;
 		}
 		case IPPROTO_SCTP:
@@ -7927,7 +7927,7 @@
 				ip_fanout_sctp_raw(first_mp, ill,
 				    (ipha_t *)ip6h, B_FALSE, ports,
 				    mctl_present,
-				    (flags|IP_FF_SEND_ICMP|IP_FF_IP6INFO),
+				    (flags|IP_FF_SEND_ICMP|IP_FF_IPINFO),
 				    B_TRUE, ipif_id, zoneid);
 				return;
 			}
@@ -8124,7 +8124,7 @@
 			/*
 			 * Handle protocols with which IPv6 is less intimate.
 			 */
-			uint_t proto_flags = IP_FF_RAWIP|IP_FF_IP6INFO;
+			uint_t proto_flags = IP_FF_RAWIP|IP_FF_IPINFO;
 
 			if (hada_mp != NULL) {
 				ip0dbg(("default hada drop\n"));
@@ -8483,7 +8483,7 @@
 		    UDP_PORTS_OFFSET);
 		IP6_STAT(ip6_udp_slow_path);
 		ip_fanout_udp_v6(q, first_mp, ip6h, ports, ill, inill,
-		    (flags|IP_FF_SEND_ICMP|IP_FF_IP6INFO), mctl_present,
+		    (flags|IP_FF_SEND_ICMP|IP_FF_IPINFO), mctl_present,
 		    zoneid);
 		return;
 	}
@@ -8505,7 +8505,7 @@
 		}
 	}
 
-	if (connp->conn_ipv6_recvpktinfo ||
+	if (connp->conn_ip_recvpktinfo ||
 	    IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src)) {
 		mp = ip_add_info_v6(mp, inill, &ip6h->ip6_dst);
 		if (mp == NULL) {
@@ -9668,9 +9668,13 @@
 
 			ASSERT(!IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src));
 			if (secpolicy_net_rawaccess(cr) != 0) {
+				/*
+				 * Use IPCL_ZONEID to honor SO_ALLZONES.
+				 */
 				ire = ire_route_lookup_v6(&ip6h->ip6_src,
 				    0, 0, (IRE_LOCAL|IRE_LOOPBACK), NULL,
-				    NULL, zoneid, NULL,
+				    NULL, connp != NULL ?
+				    IPCL_ZONEID(connp) : zoneid, NULL,
 				    MATCH_IRE_TYPE | MATCH_IRE_ZONEONLY);
 				if (ire == NULL) {
 					if (do_outrequests)
@@ -10683,7 +10687,7 @@
 			    TCP_PORTS_OFFSET);
 			ip_fanout_tcp_v6(q, first_mp, ip6h, ill, ill,
 			    fanout_flags|IP_FF_SEND_ICMP|IP_FF_SYN_ADDIRE|
-			    IP_FF_IP6INFO|IP6_NO_IPPOLICY|IP_FF_LOOPBACK,
+			    IP_FF_IPINFO|IP6_NO_IPPOLICY|IP_FF_LOOPBACK,
 			    hdr_length, mctl_present, ire->ire_zoneid);
 			return;
 
@@ -10691,7 +10695,7 @@
 			ports = *(uint32_t *)(mp->b_rptr + hdr_length +
 			    UDP_PORTS_OFFSET);
 			ip_fanout_udp_v6(q, first_mp, ip6h, ports, ill, ill,
-			    fanout_flags|IP_FF_SEND_ICMP|IP_FF_IP6INFO|
+			    fanout_flags|IP_FF_SEND_ICMP|IP_FF_IPINFO|
 			    IP6_NO_IPPOLICY, mctl_present, ire->ire_zoneid);
 			return;
 
@@ -10701,7 +10705,7 @@
 
 			ports = *(uint32_t *)(mp->b_rptr + hdr_length);
 			ip_fanout_sctp(mp, ill, (ipha_t *)ip6h, ports,
-			    fanout_flags|IP_FF_SEND_ICMP|IP_FF_IP6INFO,
+			    fanout_flags|IP_FF_SEND_ICMP|IP_FF_IPINFO,
 			    mctl_present, IP6_NO_IPPOLICY, ipif_seqid,
 			    ire->ire_zoneid);
 			return;
@@ -10788,7 +10792,7 @@
 			/*
 			 * Handle protocols with which IPv6 is less intimate.
 			 */
-			fanout_flags |= IP_FF_RAWIP|IP_FF_IP6INFO;
+			fanout_flags |= IP_FF_RAWIP|IP_FF_IPINFO;
 
 			/*
 			 * Enable sending ICMP for "Unknown" nexthdr
--- a/usr/src/uts/common/inet/ip/ip_opt_data.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ip/ip_opt_data.c	Fri Dec 22 01:03:02 2006 -0800
@@ -112,6 +112,8 @@
 
 { IP_RECVIF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT, sizeof (int), 0 },
 
+{ IP_PKTINFO, IPPROTO_IP, OA_RW, OA_RW, OP_NP, 0, sizeof (int), 0 },
+
 { IP_RECVSLLA, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT, sizeof (int), 0 },
 
 { IP_BOUND_IF, IPPROTO_IP, OA_RW, OA_RW, OP_NP, OP_PASSNEXT,
--- a/usr/src/uts/common/inet/ipclassifier.h	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/ipclassifier.h	Fri Dec 22 01:03:02 2006 -0800
@@ -153,7 +153,7 @@
 		conn_out_enforce_policy : 1,	/* Enforce Policy on outbound */
 		conn_af_isv6 : 1,		/* ip address family ver 6 */
 		conn_pkt_isv6 : 1,		/* ip packet format ver 6 */
-		conn_ipv6_recvpktinfo : 1,	/* IPV6_RECVPKTINFO option */
+		conn_ip_recvpktinfo : 1,	/* IPV*_RECVPKTINFO option */
 
 		conn_ipv6_recvhoplimit : 1,	/* IPV6_RECVHOPLIMIT option */
 		conn_ipv6_recvhopopts : 1,	/* IPV6_RECVHOPOPTS option */
--- a/usr/src/uts/common/inet/rawip_impl.h	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/rawip_impl.h	Fri Dec 22 01:03:02 2006 -0800
@@ -84,7 +84,7 @@
 	    icmp_raw_checksum : 1,	/* raw checksum per IPV6_CHECKSUM */
 	    icmp_no_tp_cksum : 1,	/* icmp_proto is UDP or TCP */
 
-	    icmp_ipv6_recvpktinfo : 1,	/* IPV6_RECVPKTINFO option  */
+	    icmp_ip_recvpktinfo : 1,	/* IPV[4,6]_RECVPKTINFO option  */
 	    icmp_ipv6_recvhoplimit : 1,	/* IPV6_RECVHOPLIMIT option */
 	    icmp_ipv6_recvhopopts : 1,	/* IPV6_RECVHOPOPTS option */
 	    icmp_ipv6_recvdstopts : 1,	/* IPV6_RECVDSTOPTS option */
--- a/usr/src/uts/common/inet/sctp/sctp_hash.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/sctp/sctp_hash.c	Fri Dec 22 01:03:02 2006 -0800
@@ -519,17 +519,18 @@
 	}
 
 	if (connp->conn_recvif || connp->conn_recvslla ||
-	    connp->conn_ipv6_recvpktinfo) {
+	    connp->conn_ip_recvpktinfo) {
 		int in_flags = 0;
 
-		if (connp->conn_recvif || connp->conn_ipv6_recvpktinfo) {
+		if (connp->conn_recvif || connp->conn_ip_recvpktinfo) {
 			in_flags = IPF_RECVIF;
 		}
 		if (connp->conn_recvslla) {
 			in_flags |= IPF_RECVSLLA;
 		}
 		if (isv4) {
-			mp = ip_add_info(mp, recv_ill, in_flags);
+			mp = ip_add_info(mp, recv_ill, in_flags,
+			    IPCL_ZONEID(connp));
 		} else {
 			mp = ip_add_info_v6(mp, recv_ill, &ip6h->ip6_dst);
 		}
--- a/usr/src/uts/common/inet/sctp/sctp_input.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c	Fri Dec 22 01:03:02 2006 -0800
@@ -3051,7 +3051,7 @@
 /* ARGSUSED */
 static sctp_hdr_t *
 find_sctp_hdrs(mblk_t *mp, in6_addr_t *src, in6_addr_t *dst,
-    uint_t *ifindex, uint_t *ip_hdr_len, ip6_pkt_t *ipp, in_pktinfo_t *pinfo)
+    uint_t *ifindex, uint_t *ip_hdr_len, ip6_pkt_t *ipp, ip_pktinfo_t *pinfo)
 {
 	uchar_t	*rptr;
 	ipha_t	*ip4h;
@@ -3067,9 +3067,9 @@
 
 		ipp->ipp_fields |= IPPF_HOPLIMIT;
 		ipp->ipp_hoplimit = ((ipha_t *)rptr)->ipha_ttl;
-		if (pinfo != NULL && (pinfo->in_pkt_flags & IPF_RECVIF)) {
+		if (pinfo != NULL && (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 			ipp->ipp_fields |= IPPF_IFINDEX;
-			ipp->ipp_ifindex = pinfo->in_pkt_ifindex;
+			ipp->ipp_ifindex = pinfo->ip_pkt_ifindex;
 		}
 	} else {
 		ASSERT(IPH_HDR_VERSION(rptr) == IPV6_VERSION);
@@ -3172,7 +3172,7 @@
 	uint_t			ifindex;
 	ip6_pkt_t		ipp;
 	ssize_t			mlen;
-	in_pktinfo_t		*pinfo = NULL;
+	ip_pktinfo_t		*pinfo = NULL;
 	mblk_t			*first_mp;
 
 	BUMP_MIB(&sctp_mib, sctpOutOfBlue);
@@ -3325,17 +3325,18 @@
 	}
 
 	if (connp->conn_recvif || connp->conn_recvslla ||
-	    connp->conn_ipv6_recvpktinfo) {
+	    connp->conn_ip_recvpktinfo) {
 		int in_flags = 0;
 
-		if (connp->conn_recvif || connp->conn_ipv6_recvpktinfo) {
+		if (connp->conn_recvif || connp->conn_ip_recvpktinfo) {
 			in_flags = IPF_RECVIF;
 		}
 		if (connp->conn_recvslla) {
 			in_flags |= IPF_RECVSLLA;
 		}
 		if (isv4) {
-			mp = ip_add_info(mp, recv_ill, in_flags);
+			mp = ip_add_info(mp, recv_ill, in_flags,
+			    IPCL_ZONEID(connp));
 		} else {
 			mp = ip_add_info_v6(mp, recv_ill,
 			    &(((ip6_t *)ipha)->ip6_dst));
@@ -3426,16 +3427,16 @@
 	int			recv_adaption;
 	boolean_t		wake_eager = B_FALSE;
 	mblk_t			*pinfo_mp;
-	in_pktinfo_t		*pinfo = NULL;
+	ip_pktinfo_t		*pinfo = NULL;
 	in6_addr_t		peer_src;
 	int64_t			now;
 
 	if (DB_TYPE(mp) != M_DATA) {
 		ASSERT(DB_TYPE(mp) == M_CTL);
-		if (MBLKL(mp) == sizeof (in_pktinfo_t) &&
-		    ((in_pktinfo_t *)mp->b_rptr)->in_pkt_ulp_type ==
+		if (MBLKL(mp) == sizeof (ip_pktinfo_t) &&
+		    ((ip_pktinfo_t *)mp->b_rptr)->ip_pkt_ulp_type ==
 		    IN_PKTINFO) {
-			pinfo = (in_pktinfo_t *)mp->b_rptr;
+			pinfo = (ip_pktinfo_t *)mp->b_rptr;
 			pinfo_mp = mp;
 			mp = mp->b_cont;
 		} else {
--- a/usr/src/uts/common/inet/sctp/sctp_opt_data.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/sctp/sctp_opt_data.c	Fri Dec 22 01:03:02 2006 -0800
@@ -1502,7 +1502,7 @@
 				    ~SCTP_IPV6_RECVPKTINFO;
 			/* Send it with the next msg */
 			sctp->sctp_recvifindex = 0;
-			connp->conn_ipv6_recvpktinfo = onoff;
+			connp->conn_ip_recvpktinfo = onoff;
 			break;
 		case IPV6_RECVHOPLIMIT:
 			if (inlen < sizeof (int32_t)) {
--- a/usr/src/uts/common/inet/tcp/tcp.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/tcp/tcp.c	Fri Dec 22 01:03:02 2006 -0800
@@ -12540,7 +12540,7 @@
 tcp_find_pktinfo(tcp_t *tcp, mblk_t *mp, uint_t *ipversp, uint_t *ip_hdr_lenp,
     uint_t *ifindexp, ip6_pkt_t *ippp)
 {
-	in_pktinfo_t	*pinfo;
+	ip_pktinfo_t	*pinfo;
 	ip6_t		*ip6h;
 	uchar_t		*rptr;
 	mblk_t		*first_mp = mp;
@@ -12591,13 +12591,13 @@
 		 */
 		if ((tcp->tcp_ipv6_recvancillary & TCP_IPV6_RECVPKTINFO) &&
 		    mctl_present) {
-			pinfo = (in_pktinfo_t *)first_mp->b_rptr;
-			if ((MBLKL(first_mp) == sizeof (in_pktinfo_t)) &&
-			    (pinfo->in_pkt_ulp_type == IN_PKTINFO) &&
-			    (pinfo->in_pkt_flags & IPF_RECVIF)) {
+			pinfo = (ip_pktinfo_t *)first_mp->b_rptr;
+			if ((MBLKL(first_mp) == sizeof (ip_pktinfo_t)) &&
+			    (pinfo->ip_pkt_ulp_type == IN_PKTINFO) &&
+			    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 				ipp.ipp_fields |= IPPF_IFINDEX;
-				ipp.ipp_ifindex = pinfo->in_pkt_ifindex;
-				ifindex = pinfo->in_pkt_ifindex;
+				ipp.ipp_ifindex = pinfo->ip_pkt_ifindex;
+				ifindex = pinfo->ip_pkt_ifindex;
 			}
 			freeb(first_mp);
 			mctl_present = B_FALSE;
--- a/usr/src/uts/common/inet/udp/udp.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/udp/udp.c	Fri Dec 22 01:03:02 2006 -0800
@@ -267,7 +267,12 @@
 
 /* Option processing attrs */
 typedef struct udpattrs_s {
-	ip6_pkt_t	*udpattr_ipp;
+	union {
+		ip6_pkt_t	*udpattr_ipp6;	/* For V6 */
+		ip4_pkt_t 	*udpattr_ipp4;	/* For V4 */
+	} udpattr_ippu;
+#define	udpattr_ipp6 udpattr_ippu.udpattr_ipp6
+#define	udpattr_ipp4 udpattr_ippu.udpattr_ipp4
 	mblk_t		*udpattr_mb;
 	boolean_t	udpattr_credset;
 } udpattrs_t;
@@ -2887,7 +2892,6 @@
 	conn_t *connp;
 	zoneid_t zoneid = getzoneid();
 	queue_t	*ip_wq;
-	char	*name;
 
 	TRACE_1(TR_FAC_UDP, TR_UDP_OPEN, "udp_open: q %p", q);
 
@@ -2916,10 +2920,7 @@
 	 * sake of MIB browsers and fail everything else.
 	 */
 	ip_wq = WR(q)->q_next;
-	if (ip_wq->q_next != NULL ||
-	    (name = ip_wq->q_qinfo->qi_minfo->mi_idname) == NULL ||
-	    strcmp(name, IP_MOD_NAME) != 0 ||
-	    ip_wq->q_qinfo->qi_minfo->mi_idnum != IP_MOD_ID) {
+	if (NOT_OVER_IP(ip_wq)) {
 		/* Support just SNMP for MIB browsers */
 		connp = ipcl_conn_create(IPCL_IPCCONN, KM_SLEEP);
 		connp->conn_rq = q;
@@ -3166,7 +3167,16 @@
 			*i1 = (int)udp->udp_ttl;
 			break;	/* goto sizeof (int) option return */
 		case IP_NEXTHOP:
-			/* Handled at IP level */
+		case IP_RECVPKTINFO:
+			/*
+			 * This also handles IP_PKTINFO.
+			 * IP_PKTINFO and IP_RECVPKTINFO have the same value.
+			 * Differentiation is based on the size of the argument
+			 * passed in.
+			 * This option is handled in IP which will return an
+			 * error for IP_PKTINFO as it's not supported as a
+			 * sticky option.
+			 */
 			return (-EINVAL);
 		case IP_MULTICAST_IF:
 			/* 0 address if not set */
@@ -3257,7 +3267,7 @@
 			*i1 = udp->udp_unspec_source;
 			break;	/* goto sizeof (int) option return */
 		case IPV6_RECVPKTINFO:
-			*i1 = udp->udp_ipv6_recvpktinfo;
+			*i1 = udp->udp_ip_recvpktinfo;
 			break;	/* goto sizeof (int) option return */
 		case IPV6_RECVTCLASS:
 			*i1 = udp->udp_ipv6_recvtclass;
@@ -3658,6 +3668,58 @@
 			if (!checkonly)
 				udp->udp_recvttl = onoff;
 			break;
+		case IP_PKTINFO: {
+			/*
+			 * This also handles IP_RECVPKTINFO.
+			 * IP_PKTINFO and IP_RECVPKTINFO have same value.
+			 * Differentiation is based on the size of the
+			 * argument passed in.
+			 */
+			struct in_pktinfo *pktinfop;
+			ip4_pkt_t *attr_pktinfop;
+
+			if (checkonly)
+				break;
+
+			if (inlen == sizeof (int)) {
+				/*
+				 * This is IP_RECVPKTINFO option.
+				 * Keep a local copy of whether this option is
+				 * set or not and pass it down to IP for
+				 * processing.
+				 */
+
+				udp->udp_ip_recvpktinfo = onoff;
+				return (-EINVAL);
+			}
+
+			if (attrs == NULL ||
+			    (attr_pktinfop = attrs->udpattr_ipp4) == NULL) {
+				/*
+				 * sticky option or no buffer to return
+				 * the results.
+				 */
+				return (EINVAL);
+			}
+
+			if (inlen != sizeof (struct in_pktinfo))
+				return (EINVAL);
+
+			pktinfop = (struct in_pktinfo *)invalp;
+
+			/*
+			 * At least one of the values should be specified
+			 */
+			if (pktinfop->ipi_ifindex == 0 &&
+			    pktinfop->ipi_spec_dst.s_addr == INADDR_ANY) {
+				return (EINVAL);
+			}
+
+			attr_pktinfop->ip4_addr = pktinfop->ipi_spec_dst.s_addr;
+			attr_pktinfop->ip4_ill_index = pktinfop->ipi_ifindex;
+
+			break;
+		}
 		case IP_ADD_MEMBERSHIP:
 		case IP_DROP_MEMBERSHIP:
 		case IP_BLOCK_SOURCE:
@@ -3707,7 +3769,8 @@
 		 * Deal with both sticky options and ancillary data
 		 */
 		sticky = B_FALSE;
-		if (attrs == NULL || (ipp = attrs->udpattr_ipp) == NULL) {
+		if (attrs == NULL || (ipp = attrs->udpattr_ipp6) ==
+		    NULL) {
 			/* sticky options, or none */
 			ipp = &udp->udp_sticky_ipp;
 			sticky = B_TRUE;
@@ -3801,7 +3864,7 @@
 		 */
 		case IPV6_RECVPKTINFO:
 			if (!checkonly)
-				udp->udp_ipv6_recvpktinfo = onoff;
+				udp->udp_ip_recvpktinfo = onoff;
 			break;
 		case IPV6_RECVTCLASS:
 			if (!checkonly) {
@@ -4492,7 +4555,7 @@
 	ip6i_t			*ip6i;
 	mblk_t			*mp1;
 	mblk_t			*options_mp = NULL;
-	in_pktinfo_t		*pinfo = NULL;
+	ip_pktinfo_t		*pinfo = NULL;
 	cred_t			*cr = NULL;
 	queue_t			*q = connp->conn_rq;
 	pid_t			cpid;
@@ -4512,15 +4575,15 @@
 	 * a valid ICMP message
 	 */
 	if (DB_TYPE(mp) == M_CTL) {
-		if (MBLKL(mp) == sizeof (in_pktinfo_t) &&
-		    ((in_pktinfo_t *)mp->b_rptr)->in_pkt_ulp_type ==
+		if (MBLKL(mp) == sizeof (ip_pktinfo_t) &&
+		    ((ip_pktinfo_t *)mp->b_rptr)->ip_pkt_ulp_type ==
 		    IN_PKTINFO) {
 			/*
-			 * IP_RECVIF or IP_RECVSLLA information has been
-			 * appended to the packet by IP. We need to
+			 * IP_RECVIF or IP_RECVSLLA or IPF_RECVADDR information
+			 * has been appended to the packet by IP. We need to
 			 * extract the mblk and adjust the rptr
 			 */
-			pinfo = (in_pktinfo_t *)mp->b_rptr;
+			pinfo = (ip_pktinfo_t *)mp->b_rptr;
 			options_mp = mp;
 			mp = mp->b_cont;
 			rptr = mp->b_rptr;
@@ -4589,10 +4652,10 @@
 
 		/* Handle IPV6_RECVHOPLIMIT. */
 		if ((udp->udp_family == AF_INET6) && (pinfo != NULL) &&
-		    udp->udp_ipv6_recvpktinfo) {
-			if (pinfo->in_pkt_flags & IPF_RECVIF) {
+		    udp->udp_ip_recvpktinfo) {
+			if (pinfo->ip_pkt_flags & IPF_RECVIF) {
 				ipp.ipp_fields |= IPPF_IFINDEX;
-				ipp.ipp_ifindex = pinfo->in_pkt_ifindex;
+				ipp.ipp_ifindex = pinfo->ip_pkt_ifindex;
 			}
 		}
 		break;
@@ -4691,18 +4754,25 @@
 			UDP_STAT(udp_in_recvdstaddr);
 		}
 
+		if (udp->udp_ip_recvpktinfo && (pinfo != NULL) &&
+		    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+			udi_size += sizeof (struct T_opthdr) +
+			    sizeof (struct in_pktinfo);
+			UDP_STAT(udp_ip_recvpktinfo);
+		}
+
 		/*
 		 * If the IP_RECVSLLA or the IP_RECVIF is set then allocate
 		 * space accordingly
 		 */
 		if (udp->udp_recvif && (pinfo != NULL) &&
-		    (pinfo->in_pkt_flags & IPF_RECVIF)) {
+		    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 			udi_size += sizeof (struct T_opthdr) + sizeof (uint_t);
 			UDP_STAT(udp_in_recvif);
 		}
 
 		if (udp->udp_recvslla && (pinfo != NULL) &&
-		    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
+		    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
 			udi_size += sizeof (struct T_opthdr) +
 			    sizeof (struct sockaddr_dl);
 			UDP_STAT(udp_in_recvslla);
@@ -4794,8 +4864,31 @@
 				udi_size -= toh->len;
 			}
 
+			if (udp->udp_ip_recvpktinfo && (pinfo != NULL) &&
+			    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+				struct T_opthdr *toh;
+				struct in_pktinfo *pktinfop;
+
+				toh = (struct T_opthdr *)dstopt;
+				toh->level = IPPROTO_IP;
+				toh->name = IP_PKTINFO;
+				toh->len = sizeof (struct T_opthdr) +
+				    sizeof (*pktinfop);
+				toh->status = 0;
+				dstopt += sizeof (struct T_opthdr);
+				pktinfop = (struct in_pktinfo *)dstopt;
+				pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex;
+				pktinfop->ipi_spec_dst =
+				    pinfo->ip_pkt_match_addr;
+				pktinfop->ipi_addr.s_addr =
+				    ((ipha_t *)rptr)->ipha_dst;
+
+				dstopt += sizeof (struct in_pktinfo);
+				udi_size -= toh->len;
+			}
+
 			if (udp->udp_recvslla && (pinfo != NULL) &&
-			    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
+			    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
 
 				struct T_opthdr *toh;
 				struct sockaddr_dl	*dstptr;
@@ -4808,14 +4901,14 @@
 				toh->status = 0;
 				dstopt += sizeof (struct T_opthdr);
 				dstptr = (struct sockaddr_dl *)dstopt;
-				bcopy(&pinfo->in_pkt_slla, dstptr,
+				bcopy(&pinfo->ip_pkt_slla, dstptr,
 				    sizeof (struct sockaddr_dl));
 				dstopt = (char *)toh + toh->len;
 				udi_size -= toh->len;
 			}
 
 			if (udp->udp_recvif && (pinfo != NULL) &&
-			    (pinfo->in_pkt_flags & IPF_RECVIF)) {
+			    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 
 				struct T_opthdr *toh;
 				uint_t		*dstptr;
@@ -4828,7 +4921,7 @@
 				toh->status = 0;
 				dstopt += sizeof (struct T_opthdr);
 				dstptr = (uint_t *)dstopt;
-				*dstptr = pinfo->in_pkt_ifindex;
+				*dstptr = pinfo->ip_pkt_ifindex;
 				dstopt = (char *)toh + toh->len;
 				udi_size -= toh->len;
 			}
@@ -4941,7 +5034,7 @@
 				    ipp.ipp_rthdrlen;
 				UDP_STAT(udp_in_recvrthdr);
 			}
-			if (udp->udp_ipv6_recvpktinfo &&
+			if (udp->udp_ip_recvpktinfo &&
 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
 				udi_size += sizeof (struct T_opthdr) +
 				    sizeof (struct in6_pktinfo);
@@ -5019,7 +5112,7 @@
 			uchar_t *dstopt;
 
 			dstopt = (uchar_t *)&sin6[1];
-			if (udp->udp_ipv6_recvpktinfo &&
+			if (udp->udp_ip_recvpktinfo &&
 			    (ipp.ipp_fields & IPPF_IFINDEX)) {
 				struct T_opthdr *toh;
 				struct in6_pktinfo *pkti;
@@ -5221,7 +5314,7 @@
 	sin_t			*sin;
 	struct T_error_ack	*tea;
 	mblk_t			*options_mp = NULL;
-	in_pktinfo_t		*pinfo;
+	ip_pktinfo_t		*pinfo;
 	boolean_t		recv_on = B_FALSE;
 	cred_t			*cr = NULL;
 	udp_t			*udp = Q_TO_UDP(q);
@@ -5241,7 +5334,7 @@
 		 */
 		recv_on = B_TRUE;
 		options_mp = mp;
-		pinfo = (in_pktinfo_t *)options_mp->b_rptr;
+		pinfo = (ip_pktinfo_t *)options_mp->b_rptr;
 
 		/*
 		 * The actual data is in mp->b_cont
@@ -5393,6 +5486,14 @@
 		udi_size += sizeof (struct T_opthdr) + sizeof (struct in_addr);
 		UDP_STAT(udp_in_recvdstaddr);
 	}
+
+	if (udp->udp_ip_recvpktinfo && recv_on &&
+	    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+		udi_size += sizeof (struct T_opthdr) +
+		    sizeof (struct in_pktinfo);
+		UDP_STAT(udp_ip_recvpktinfo);
+	}
+
 	if (udp->udp_recvopts && opt_len > 0) {
 		udi_size += sizeof (struct T_opthdr) + opt_len;
 		UDP_STAT(udp_in_recvopts);
@@ -5403,13 +5504,13 @@
 	 * space accordingly
 	 */
 	if (udp->udp_recvif && recv_on &&
-	    (pinfo->in_pkt_flags & IPF_RECVIF)) {
+	    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 		udi_size += sizeof (struct T_opthdr) + sizeof (uint_t);
 		UDP_STAT(udp_in_recvif);
 	}
 
 	if (udp->udp_recvslla && recv_on &&
-	    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
+	    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
 		udi_size += sizeof (struct T_opthdr) +
 		    sizeof (struct sockaddr_dl);
 		UDP_STAT(udp_in_recvslla);
@@ -5499,9 +5600,31 @@
 			dstopt += opt_len;
 			udi_size -= toh->len;
 		}
+		if (udp->udp_ip_recvpktinfo && recv_on &&
+		    (pinfo->ip_pkt_flags & IPF_RECVADDR)) {
+
+			struct T_opthdr *toh;
+			struct in_pktinfo *pktinfop;
+
+			toh = (struct T_opthdr *)dstopt;
+			toh->level = IPPROTO_IP;
+			toh->name = IP_PKTINFO;
+			toh->len = sizeof (struct T_opthdr) +
+			sizeof (*pktinfop);
+			toh->status = 0;
+			dstopt += sizeof (struct T_opthdr);
+			pktinfop = (struct in_pktinfo *)dstopt;
+			pktinfop->ipi_ifindex = pinfo->ip_pkt_ifindex;
+			pktinfop->ipi_spec_dst = pinfo->ip_pkt_match_addr;
+
+			pktinfop->ipi_addr.s_addr = ((ipha_t *)rptr)->ipha_dst;
+
+			dstopt += sizeof (struct in_pktinfo);
+			udi_size -= toh->len;
+		}
 
 		if (udp->udp_recvslla && recv_on &&
-		    (pinfo->in_pkt_flags & IPF_RECVSLLA)) {
+		    (pinfo->ip_pkt_flags & IPF_RECVSLLA)) {
 
 			struct T_opthdr *toh;
 			struct sockaddr_dl	*dstptr;
@@ -5514,14 +5637,14 @@
 			toh->status = 0;
 			dstopt += sizeof (struct T_opthdr);
 			dstptr = (struct sockaddr_dl *)dstopt;
-			bcopy(&pinfo->in_pkt_slla, dstptr,
+			bcopy(&pinfo->ip_pkt_slla, dstptr,
 			    sizeof (struct sockaddr_dl));
 			dstopt += sizeof (struct sockaddr_dl);
 			udi_size -= toh->len;
 		}
 
 		if (udp->udp_recvif && recv_on &&
-		    (pinfo->in_pkt_flags & IPF_RECVIF)) {
+		    (pinfo->ip_pkt_flags & IPF_RECVIF)) {
 
 			struct T_opthdr *toh;
 			uint_t		*dstptr;
@@ -5534,7 +5657,7 @@
 			toh->status = 0;
 			dstopt += sizeof (struct T_opthdr);
 			dstptr = (uint_t *)dstopt;
-			*dstptr = pinfo->in_pkt_ifindex;
+			*dstptr = pinfo->ip_pkt_ifindex;
 			dstopt += sizeof (uint_t);
 			udi_size -= toh->len;
 		}
@@ -6269,8 +6392,16 @@
 	udpattrs_t	attrs;
 	uchar_t	ip_snd_opt[IP_MAX_OPT_LENGTH];
 	uint32_t	ip_snd_opt_len = 0;
+	ip4_pkt_t  pktinfo;
+	ip4_pkt_t  *pktinfop = &pktinfo;
+	ip_opt_info_t optinfo;
+
 
 	*error = 0;
+	pktinfop->ip4_ill_index = 0;
+	pktinfop->ip4_addr = INADDR_ANY;
+	optinfo.ip_opt_flags = 0;
+	optinfo.ip_opt_ill_index = 0;
 
 	if (v4dst == INADDR_ANY)
 		v4dst = htonl(INADDR_LOOPBACK);
@@ -6282,7 +6413,7 @@
 	if (DB_TYPE(mp) != M_DATA) {
 		mp1 = mp->b_cont;
 		if (((struct T_unitdata_req *)mp->b_rptr)->OPT_length != 0) {
-			attrs.udpattr_ipp = NULL;
+			attrs.udpattr_ipp4 = pktinfop;
 			attrs.udpattr_mb = mp;
 			if (udp_unitdata_opt_process(q, mp, error, &attrs) < 0)
 				goto done;
@@ -6371,17 +6502,27 @@
 	/* Set ttl and protocol */
 	*(uint16_t *)&ipha->ipha_ttl = (IPPROTO_UDP << 8) | udp->udp_ttl;
 #endif
-	/*
-	 * Copy our address into the packet.  If this is zero,
-	 * first look at __sin6_src_id for a hint. If we leave the source
-	 * as INADDR_ANY then ip will fill in the real source address.
-	 */
-	IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, ipha->ipha_src);
-	if (srcid != 0 && ipha->ipha_src == INADDR_ANY) {
-		in6_addr_t v6src;
-
-		ip_srcid_find_id(srcid, &v6src, connp->conn_zoneid);
-		IN6_V4MAPPED_TO_IPADDR(&v6src, ipha->ipha_src);
+	if (pktinfop->ip4_addr != INADDR_ANY) {
+		ipha->ipha_src = pktinfop->ip4_addr;
+		optinfo.ip_opt_flags = IP_VERIFY_SRC;
+	} else {
+		/*
+		 * Copy our address into the packet.  If this is zero,
+		 * first look at __sin6_src_id for a hint. If we leave the
+		 * source as INADDR_ANY then ip will fill in the real source
+		 * address.
+		 */
+		IN6_V4MAPPED_TO_IPADDR(&udp->udp_v6src, ipha->ipha_src);
+		if (srcid != 0 && ipha->ipha_src == INADDR_ANY) {
+			in6_addr_t v6src;
+
+			ip_srcid_find_id(srcid, &v6src, connp->conn_zoneid);
+			IN6_V4MAPPED_TO_IPADDR(&v6src, ipha->ipha_src);
+		}
+	}
+
+	if (pktinfop->ip4_ill_index != 0) {
+		optinfo.ip_opt_ill_index = pktinfop->ip4_ill_index;
 	}
 
 	ipha->ipha_fragment_offset_and_flags = 0;
@@ -6477,6 +6618,7 @@
 			ip_len <<= 16;
 #endif
 	}
+
 	/* Set UDP length and checksum */
 	*((uint32_t *)&udpha->uha_length) = ip_len;
 	if (DB_CRED(mp) != NULL)
@@ -6500,11 +6642,13 @@
 	    CONN_OUTBOUND_POLICY_PRESENT(connp) ||
 	    connp->conn_dontroute || connp->conn_xmit_if_ill != NULL ||
 	    connp->conn_nofailover_ill != NULL ||
-	    connp->conn_outgoing_ill != NULL ||
+	    connp->conn_outgoing_ill != NULL || optinfo.ip_opt_flags != 0 ||
+	    optinfo.ip_opt_ill_index != 0 ||
 	    ipha->ipha_version_and_hdr_length != IP_SIMPLE_HDR_VERSION ||
 	    IPP_ENABLED(IPP_LOCAL_OUT) || ip_g_mrouter != NULL) {
 		UDP_STAT(udp_ip_send);
-		ip_output(connp, mp1, connp->conn_wq, IP_WPUT);
+		ip_output_options(connp, mp1, connp->conn_wq, IP_WPUT,
+		    &optinfo);
 	} else {
 		udp_send_data(udp, connp->conn_wq, mp1, ipha);
 	}
@@ -7168,7 +7312,7 @@
 	if (DB_TYPE(mp) != M_DATA) {
 		mp1 = mp->b_cont;
 		if (((struct T_unitdata_req *)mp->b_rptr)->OPT_length != 0) {
-			attrs.udpattr_ipp = ipp;
+			attrs.udpattr_ipp6 = ipp;
 			attrs.udpattr_mb = mp;
 			if (udp_unitdata_opt_process(q, mp, error, &attrs) < 0)
 				goto done;
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c	Fri Dec 22 01:03:02 2006 -0800
@@ -138,6 +138,9 @@
 { IP_UNSPEC_SRC, IPPROTO_IP, OA_R, OA_RW, OP_RAW, OP_PASSNEXT,
 	sizeof (int), 0 },
 
+{ IP_PKTINFO, IPPROTO_IP, OA_RW, OA_RW, OP_NP,
+	(OP_PASSNEXT|OP_NODEFAULT|OP_VARLEN),
+	sizeof (struct in_pktinfo), -1 /* not initialized */ },
 { IP_NEXTHOP, IPPROTO_IP, OA_RW, OA_RW, OP_CONFIG, OP_PASSNEXT,
 	sizeof (in_addr_t),	-1 /* not initialized  */ },
 
--- a/usr/src/uts/common/inet/udp_impl.h	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/inet/udp_impl.h	Fri Dec 22 01:03:02 2006 -0800
@@ -99,7 +99,7 @@
 
 		udp_discon_pending : 1,	/* T_DISCON_REQ in progress */
 		udp_unspec_source : 1,	/* IP*_UNSPEC_SRC option */
-		udp_ipv6_recvpktinfo : 1,	/* IPV6_RECVPKTINFO option  */
+		udp_ip_recvpktinfo : 1,	/* IPV[4,6]_RECVPKTINFO option  */
 		udp_ipv6_recvhoplimit : 1,	/* IPV6_RECVHOPLIMIT option */
 
 		udp_ipv6_recvhopopts : 1,	/* IPV6_RECVHOPOPTS option */
@@ -219,6 +219,7 @@
 	kstat_named_t	udp_in_recvpktinfo;
 	kstat_named_t	udp_in_recvtclass;
 	kstat_named_t	udp_in_timestamp;
+	kstat_named_t	udp_ip_recvpktinfo;
 #ifdef DEBUG
 	kstat_named_t	udp_data_conn;
 	kstat_named_t	udp_data_notconn;
--- a/usr/src/uts/common/netinet/in.h	Fri Dec 22 00:49:41 2006 -0800
+++ b/usr/src/uts/common/netinet/in.h	Fri Dec 22 01:03:02 2006 -0800
@@ -829,6 +829,12 @@
 #define	IP_ADD_SOURCE_MEMBERSHIP  0x17	/* add  mcast group/source pair	   */
 #define	IP_DROP_SOURCE_MEMBERSHIP 0x18	/* drop mcast group/source pair	   */
 #define	IP_NEXTHOP		0x19	/* send directly to next hop	   */
+/*
+ * IP_PKTINFO and IP_RECVPKTINFO have same value. Size of argument passed in
+ * is used to differentiate b/w the two.
+ */
+#define	IP_PKTINFO		0x1a	/* specify src address and/or index */
+#define	IP_RECVPKTINFO		0x1a	/* recv dest/matched addr and index */
 
 #if !defined(_XPG4_2) || defined(__EXTENSIONS__)
 /*
@@ -1029,6 +1035,15 @@
 #define	MCAST_EXCLUDE	2
 
 /*
+ * Argument struct for IP_PKTINFO option
+ */
+typedef struct in_pktinfo {
+	unsigned int		ipi_ifindex;	/* send/recv interface index */
+	struct in_addr		ipi_spec_dst;	/* matched source address */
+	struct in_addr		ipi_addr;	/* src/dst address in IP hdr */
+} in_pktinfo_t;
+
+/*
  * Argument struct for IPV6_PKTINFO option
  */
 struct in6_pktinfo {