PSARC 2006/466 IP_PKTINFO Socket Option
4773220 Provide API to set source address of UDP/IPv4 datagrams
diff --git a/usr/src/uts/common/inet/ip.h b/usr/src/uts/common/inet/ip.h
index 2aea221..e7036a1 100644
--- a/usr/src/uts/common/inet/ip.h
+++ b/usr/src/uts/common/inet/ip.h
@@ -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 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 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 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
diff --git a/usr/src/uts/common/inet/ip/icmp.c b/usr/src/uts/common/inet/ip/icmp.c
index d79cfce..198c361 100644
--- a/usr/src/uts/common/inet/ip/icmp.c
+++ b/usr/src/uts/common/inet/ip/icmp.c
@@ -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
}
diff --git a/usr/src/uts/common/inet/ip/icmp_opt_data.c b/usr/src/uts/common/inet/ip/icmp_opt_data.c
index 41d3278..0231f83 100644
--- a/usr/src/uts/common/inet/ip/icmp_opt_data.c
+++ b/usr/src/uts/common/inet/ip/icmp_opt_data.c
@@ -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),
diff --git a/usr/src/uts/common/inet/ip/ip.c b/usr/src/uts/common/inet/ip/ip.c
index d73a8db..b7f95b1 100644
--- a/usr/src/uts/common/inet/ip/ip.c
+++ b/usr/src/uts/common/inet/ip/ip.c
@@ -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 */
+ 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)
- pinfo->in_pkt_ifindex = ill->ill_phyint->phyint_ifindex;
+ 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"));
+ ill_refrele(xmit_ill);
+ if (need_decref)
+ CONN_DEC_REF(connp);
+ return;
}
+ ipif_refrele(ipif);
} else {
- freemsg(first_mp);
+ goto drop_pkt;
}
- 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;
} 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) {
diff --git a/usr/src/uts/common/inet/ip/ip6.c b/usr/src/uts/common/inet/ip/ip6.c
index 0b89c57..582165e 100644
--- a/usr/src/uts/common/inet/ip/ip6.c
+++ b/usr/src/uts/common/inet/ip/ip6.c
@@ -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
diff --git a/usr/src/uts/common/inet/ip/ip_opt_data.c b/usr/src/uts/common/inet/ip/ip_opt_data.c
index 86e146a..2462004 100644
--- a/usr/src/uts/common/inet/ip/ip_opt_data.c
+++ b/usr/src/uts/common/inet/ip/ip_opt_data.c
@@ -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,
diff --git a/usr/src/uts/common/inet/ipclassifier.h b/usr/src/uts/common/inet/ipclassifier.h
index 48e5fc8..cd85aae 100644
--- a/usr/src/uts/common/inet/ipclassifier.h
+++ b/usr/src/uts/common/inet/ipclassifier.h
@@ -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 */
diff --git a/usr/src/uts/common/inet/rawip_impl.h b/usr/src/uts/common/inet/rawip_impl.h
index 8e61fc9..b0029b9 100644
--- a/usr/src/uts/common/inet/rawip_impl.h
+++ b/usr/src/uts/common/inet/rawip_impl.h
@@ -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 */
diff --git a/usr/src/uts/common/inet/sctp/sctp_hash.c b/usr/src/uts/common/inet/sctp/sctp_hash.c
index 9007550..7c37295 100644
--- a/usr/src/uts/common/inet/sctp/sctp_hash.c
+++ b/usr/src/uts/common/inet/sctp/sctp_hash.c
@@ -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);
}
diff --git a/usr/src/uts/common/inet/sctp/sctp_input.c b/usr/src/uts/common/inet/sctp/sctp_input.c
index 420d175..b19e21a 100644
--- a/usr/src/uts/common/inet/sctp/sctp_input.c
+++ b/usr/src/uts/common/inet/sctp/sctp_input.c
@@ -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 {
diff --git a/usr/src/uts/common/inet/sctp/sctp_opt_data.c b/usr/src/uts/common/inet/sctp/sctp_opt_data.c
index c670fec..28d38c8 100644
--- a/usr/src/uts/common/inet/sctp/sctp_opt_data.c
+++ b/usr/src/uts/common/inet/sctp/sctp_opt_data.c
@@ -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)) {
diff --git a/usr/src/uts/common/inet/tcp/tcp.c b/usr/src/uts/common/inet/tcp/tcp.c
index fee934b..18bd9f9 100644
--- a/usr/src/uts/common/inet/tcp/tcp.c
+++ b/usr/src/uts/common/inet/tcp/tcp.c
@@ -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;
diff --git a/usr/src/uts/common/inet/udp/udp.c b/usr/src/uts/common/inet/udp/udp.c
index 2470df6..aa08b7c 100644
--- a/usr/src/uts/common/inet/udp/udp.c
+++ b/usr/src/uts/common/inet/udp/udp.c
@@ -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;
+ 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);
+ 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;
diff --git a/usr/src/uts/common/inet/udp/udp_opt_data.c b/usr/src/uts/common/inet/udp/udp_opt_data.c
index 1d2ccf7..a40fa54 100644
--- a/usr/src/uts/common/inet/udp/udp_opt_data.c
+++ b/usr/src/uts/common/inet/udp/udp_opt_data.c
@@ -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 */ },
diff --git a/usr/src/uts/common/inet/udp_impl.h b/usr/src/uts/common/inet/udp_impl.h
index db6506f..b5c2282 100644
--- a/usr/src/uts/common/inet/udp_impl.h
+++ b/usr/src/uts/common/inet/udp_impl.h
@@ -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;
diff --git a/usr/src/uts/common/netinet/in.h b/usr/src/uts/common/netinet/in.h
index f6284c4..45d0d70 100644
--- a/usr/src/uts/common/netinet/in.h
+++ b/usr/src/uts/common/netinet/in.h
@@ -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 {