#include <sys/types.h>
#include <sys/stream.h>
#include <sys/strsun.h>
#include <sys/zone.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/cmn_err.h>
#include <sys/debug.h>
#include <sys/atomic.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/kmem.h>
#include <sys/sdt.h>
#include <sys/socket.h>
#include <sys/mac.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <net/route.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <net/if_dl.h>
#include <inet/common.h>
#include <inet/mi.h>
#include <inet/mib2.h>
#include <inet/nd.h>
#include <inet/arp.h>
#include <inet/snmpcom.h>
#include <inet/kstatcom.h>
#include <netinet/igmp_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
#include <netinet/sctp.h>
#include <inet/ip.h>
#include <inet/ip_impl.h>
#include <inet/ip6.h>
#include <inet/ip6_asp.h>
#include <inet/tcp.h>
#include <inet/ip_multi.h>
#include <inet/ip_if.h>
#include <inet/ip_ire.h>
#include <inet/ip_ftable.h>
#include <inet/ip_rts.h>
#include <inet/optcom.h>
#include <inet/ip_ndp.h>
#include <inet/ip_listutils.h>
#include <netinet/igmp.h>
#include <netinet/ip_mroute.h>
#include <inet/ipp_common.h>
#include <net/pfkeyv2.h>
#include <inet/sadb.h>
#include <inet/ipsec_impl.h>
#include <inet/ipdrop.h>
#include <inet/ip_netinfo.h>
#include <sys/squeue_impl.h>
#include <sys/squeue.h>
#include <inet/ipclassifier.h>
#include <inet/sctp_ip.h>
#include <inet/sctp/sctp_impl.h>
#include <inet/udp_impl.h>
#include <sys/sunddi.h>
#include <sys/tsol/label.h>
#include <sys/tsol/tnet.h>
#define IXA_REFRELE(ixa) \
{ \
if (atomic_dec_32_nv(&(ixa)->ixa_refcnt) == 0) \
ixa_inactive(ixa); \
}
#define IXA_REFHOLD(ixa) \
{ \
ASSERT3U((ixa)->ixa_refcnt, !=, 0); \
atomic_inc_32(&(ixa)->ixa_refcnt); \
}
typedef struct ixamblk_s {
boolean_t ixm_inbound;
iaflags_t ixm_flags;
netstackid_t ixm_stackid;
uint_t ixm_ifindex;
in6_addr_t ixm_nceaddr_v6;
#define ixm_nceaddr_v4 V4_PART_OF_V6(ixm_nceaddr_v6)
uint32_t ixm_fragsize;
uint_t ixm_pktlen;
uint16_t ixm_ip_hdr_length;
uint8_t ixm_protocol;
pfirepostfrag_t ixm_postfragfn;
zoneid_t ixm_zoneid;
zoneid_t ixm_no_loop_zoneid;
uint_t ixm_scopeid;
uint32_t ixm_ident;
uint32_t ixm_xmit_hint;
uint64_t ixm_conn_id;
cred_t *ixm_cred;
pid_t ixm_cpid;
ts_label_t *ixm_tsl;
ipsec_latch_t *ixm_ipsec_latch;
struct ipsa_s *ixm_ipsec_ah_sa;
struct ipsa_s *ixm_ipsec_esp_sa;
struct ipsec_policy_s *ixm_ipsec_policy;
struct ipsec_action_s *ixm_ipsec_action;
ipsa_ref_t ixm_ipsec_ref[2];
uint16_t ixm_ipsec_src_port;
uint16_t ixm_ipsec_dst_port;
uint8_t ixm_ipsec_icmp_type;
uint8_t ixm_ipsec_icmp_code;
sa_family_t ixm_ipsec_inaf;
uint32_t ixm_ipsec_insrc[IXA_MAX_ADDRLEN];
uint32_t ixm_ipsec_indst[IXA_MAX_ADDRLEN];
uint8_t ixm_ipsec_insrcpfx;
uint8_t ixm_ipsec_indstpfx;
uint8_t ixm_ipsec_proto;
} ixamblk_t;
typedef struct iramblk_s {
boolean_t irm_inbound;
iaflags_t irm_flags;
netstackid_t irm_stackid;
uint_t irm_ifindex;
uint_t irm_rifindex;
uint_t irm_ruifindex;
uint_t irm_pktlen;
uint16_t irm_ip_hdr_length;
uint8_t irm_protocol;
uint8_t irm_ttl;
zoneid_t irm_zoneid;
squeue_t *irm_sqp;
ill_rx_ring_t *irm_ring;
ipaddr_t irm_mroute_tunnel;
zoneid_t irm_no_loop_zoneid;
uint32_t irm_esp_udp_ports;
char irm_l2src[IRA_L2SRC_SIZE];
cred_t *irm_cred;
pid_t irm_cpid;
ts_label_t *irm_tsl;
struct ipsa_s *irm_ipsec_ah_sa;
struct ipsa_s *irm_ipsec_esp_sa;
struct ipsec_action_s *irm_ipsec_action;
} iramblk_t;
mblk_t *
ip_xmit_attr_to_mblk(ip_xmit_attr_t *ixa)
{
mblk_t *ixamp;
ixamblk_t *ixm;
nce_t *nce = ixa->ixa_nce;
ASSERT(nce != NULL);
ixamp = allocb(sizeof (*ixm), BPRI_MED);
if (ixamp == NULL)
return (NULL);
ixamp->b_datap->db_type = M_BREAK;
ixamp->b_wptr += sizeof (*ixm);
ixm = (ixamblk_t *)ixamp->b_rptr;
bzero(ixm, sizeof (*ixm));
ixm->ixm_inbound = B_FALSE;
ixm->ixm_flags = ixa->ixa_flags;
ixm->ixm_stackid = ixa->ixa_ipst->ips_netstack->netstack_stackid;
ixm->ixm_ifindex = nce->nce_ill->ill_phyint->phyint_ifindex;
ixm->ixm_nceaddr_v6 = nce->nce_addr;
ixm->ixm_fragsize = ixa->ixa_fragsize;
ixm->ixm_pktlen = ixa->ixa_pktlen;
ixm->ixm_ip_hdr_length = ixa->ixa_ip_hdr_length;
ixm->ixm_protocol = ixa->ixa_protocol;
ixm->ixm_postfragfn = ixa->ixa_postfragfn;
ixm->ixm_zoneid = ixa->ixa_zoneid;
ixm->ixm_no_loop_zoneid = ixa->ixa_no_loop_zoneid;
ixm->ixm_scopeid = ixa->ixa_scopeid;
ixm->ixm_ident = ixa->ixa_ident;
ixm->ixm_xmit_hint = ixa->ixa_xmit_hint;
if (ixa->ixa_tsl != NULL) {
ixm->ixm_tsl = ixa->ixa_tsl;
label_hold(ixm->ixm_tsl);
}
if (ixa->ixa_cred != NULL) {
ixm->ixm_cred = ixa->ixa_cred;
crhold(ixa->ixa_cred);
}
ixm->ixm_cpid = ixa->ixa_cpid;
ixm->ixm_conn_id = ixa->ixa_conn_id;
if (ixa->ixa_flags & IXAF_IPSEC_SECURE) {
if (ixa->ixa_ipsec_ah_sa != NULL) {
ixm->ixm_ipsec_ah_sa = ixa->ixa_ipsec_ah_sa;
IPSA_REFHOLD(ixa->ixa_ipsec_ah_sa);
}
if (ixa->ixa_ipsec_esp_sa != NULL) {
ixm->ixm_ipsec_esp_sa = ixa->ixa_ipsec_esp_sa;
IPSA_REFHOLD(ixa->ixa_ipsec_esp_sa);
}
if (ixa->ixa_ipsec_policy != NULL) {
ixm->ixm_ipsec_policy = ixa->ixa_ipsec_policy;
IPPOL_REFHOLD(ixa->ixa_ipsec_policy);
}
if (ixa->ixa_ipsec_action != NULL) {
ixm->ixm_ipsec_action = ixa->ixa_ipsec_action;
IPACT_REFHOLD(ixa->ixa_ipsec_action);
}
if (ixa->ixa_ipsec_latch != NULL) {
ixm->ixm_ipsec_latch = ixa->ixa_ipsec_latch;
IPLATCH_REFHOLD(ixa->ixa_ipsec_latch);
}
ixm->ixm_ipsec_ref[0] = ixa->ixa_ipsec_ref[0];
ixm->ixm_ipsec_ref[1] = ixa->ixa_ipsec_ref[1];
ixm->ixm_ipsec_src_port = ixa->ixa_ipsec_src_port;
ixm->ixm_ipsec_dst_port = ixa->ixa_ipsec_dst_port;
ixm->ixm_ipsec_icmp_type = ixa->ixa_ipsec_icmp_type;
ixm->ixm_ipsec_icmp_code = ixa->ixa_ipsec_icmp_code;
ixm->ixm_ipsec_inaf = ixa->ixa_ipsec_inaf;
ixm->ixm_ipsec_insrc[0] = ixa->ixa_ipsec_insrc[0];
ixm->ixm_ipsec_insrc[1] = ixa->ixa_ipsec_insrc[1];
ixm->ixm_ipsec_insrc[2] = ixa->ixa_ipsec_insrc[2];
ixm->ixm_ipsec_insrc[3] = ixa->ixa_ipsec_insrc[3];
ixm->ixm_ipsec_indst[0] = ixa->ixa_ipsec_indst[0];
ixm->ixm_ipsec_indst[1] = ixa->ixa_ipsec_indst[1];
ixm->ixm_ipsec_indst[2] = ixa->ixa_ipsec_indst[2];
ixm->ixm_ipsec_indst[3] = ixa->ixa_ipsec_indst[3];
ixm->ixm_ipsec_insrcpfx = ixa->ixa_ipsec_insrcpfx;
ixm->ixm_ipsec_indstpfx = ixa->ixa_ipsec_indstpfx;
ixm->ixm_ipsec_proto = ixa->ixa_ipsec_proto;
}
return (ixamp);
}
boolean_t
ip_xmit_attr_from_mblk(mblk_t *ixamp, ip_xmit_attr_t *ixa)
{
ixamblk_t *ixm;
netstack_t *ns;
ip_stack_t *ipst;
ill_t *ill;
nce_t *nce;
bzero(ixa, sizeof (*ixa));
ASSERT(DB_TYPE(ixamp) == M_BREAK);
ASSERT(ixamp->b_cont == NULL);
ixm = (ixamblk_t *)ixamp->b_rptr;
ASSERT(!ixm->ixm_inbound);
ns = netstack_find_by_stackid(ixm->ixm_stackid);
if (ns == NULL) {
(void) ip_xmit_attr_free_mblk(ixamp);
return (B_FALSE);
}
ipst = ns->netstack_ip;
ill = ill_lookup_on_ifindex(ixm->ixm_ifindex,
!(ixm->ixm_flags & IXAF_IS_IPV4), ipst);
netstack_rele(ns);
if (ill == NULL) {
(void) ip_xmit_attr_free_mblk(ixamp);
return (B_FALSE);
}
if (ixm->ixm_flags & IXAF_IS_IPV4) {
nce = nce_lookup_v4(ill, &ixm->ixm_nceaddr_v4);
} else {
nce = nce_lookup_v6(ill, &ixm->ixm_nceaddr_v6);
}
ill_refrele(ill);
if (nce == NULL) {
(void) ip_xmit_attr_free_mblk(ixamp);
return (B_FALSE);
}
ixa->ixa_flags = ixm->ixm_flags;
ixa->ixa_refcnt = 1;
ixa->ixa_ipst = ipst;
ixa->ixa_fragsize = ixm->ixm_fragsize;
ixa->ixa_pktlen = ixm->ixm_pktlen;
ixa->ixa_ip_hdr_length = ixm->ixm_ip_hdr_length;
ixa->ixa_protocol = ixm->ixm_protocol;
ixa->ixa_nce = nce;
ixa->ixa_postfragfn = ixm->ixm_postfragfn;
ixa->ixa_zoneid = ixm->ixm_zoneid;
ixa->ixa_no_loop_zoneid = ixm->ixm_no_loop_zoneid;
ixa->ixa_scopeid = ixm->ixm_scopeid;
ixa->ixa_ident = ixm->ixm_ident;
ixa->ixa_xmit_hint = ixm->ixm_xmit_hint;
if (ixm->ixm_tsl != NULL) {
ixa->ixa_tsl = ixm->ixm_tsl;
ixa->ixa_free_flags |= IXA_FREE_TSL;
ixm->ixm_tsl = NULL;
}
if (ixm->ixm_cred != NULL) {
ixa->ixa_cred = ixm->ixm_cred;
ixa->ixa_free_flags |= IXA_FREE_CRED;
ixm->ixm_cred = NULL;
}
ixa->ixa_cpid = ixm->ixm_cpid;
ixa->ixa_conn_id = ixm->ixm_conn_id;
ixa->ixa_ipsec_ah_sa = ixm->ixm_ipsec_ah_sa;
ixa->ixa_ipsec_esp_sa = ixm->ixm_ipsec_esp_sa;
ixa->ixa_ipsec_policy = ixm->ixm_ipsec_policy;
ixa->ixa_ipsec_action = ixm->ixm_ipsec_action;
ixa->ixa_ipsec_latch = ixm->ixm_ipsec_latch;
ixa->ixa_ipsec_ref[0] = ixm->ixm_ipsec_ref[0];
ixa->ixa_ipsec_ref[1] = ixm->ixm_ipsec_ref[1];
ixa->ixa_ipsec_src_port = ixm->ixm_ipsec_src_port;
ixa->ixa_ipsec_dst_port = ixm->ixm_ipsec_dst_port;
ixa->ixa_ipsec_icmp_type = ixm->ixm_ipsec_icmp_type;
ixa->ixa_ipsec_icmp_code = ixm->ixm_ipsec_icmp_code;
ixa->ixa_ipsec_inaf = ixm->ixm_ipsec_inaf;
ixa->ixa_ipsec_insrc[0] = ixm->ixm_ipsec_insrc[0];
ixa->ixa_ipsec_insrc[1] = ixm->ixm_ipsec_insrc[1];
ixa->ixa_ipsec_insrc[2] = ixm->ixm_ipsec_insrc[2];
ixa->ixa_ipsec_insrc[3] = ixm->ixm_ipsec_insrc[3];
ixa->ixa_ipsec_indst[0] = ixm->ixm_ipsec_indst[0];
ixa->ixa_ipsec_indst[1] = ixm->ixm_ipsec_indst[1];
ixa->ixa_ipsec_indst[2] = ixm->ixm_ipsec_indst[2];
ixa->ixa_ipsec_indst[3] = ixm->ixm_ipsec_indst[3];
ixa->ixa_ipsec_insrcpfx = ixm->ixm_ipsec_insrcpfx;
ixa->ixa_ipsec_indstpfx = ixm->ixm_ipsec_indstpfx;
ixa->ixa_ipsec_proto = ixm->ixm_ipsec_proto;
freeb(ixamp);
return (B_TRUE);
}
mblk_t *
ip_xmit_attr_free_mblk(mblk_t *ixamp)
{
ixamblk_t *ixm;
mblk_t *mp;
ASSERT(DB_TYPE(ixamp) == M_BREAK);
mp = ixamp->b_cont;
ixm = (ixamblk_t *)ixamp->b_rptr;
ASSERT(!ixm->ixm_inbound);
if (ixm->ixm_ipsec_ah_sa != NULL) {
IPSA_REFRELE(ixm->ixm_ipsec_ah_sa);
ixm->ixm_ipsec_ah_sa = NULL;
}
if (ixm->ixm_ipsec_esp_sa != NULL) {
IPSA_REFRELE(ixm->ixm_ipsec_esp_sa);
ixm->ixm_ipsec_esp_sa = NULL;
}
if (ixm->ixm_ipsec_policy != NULL) {
IPPOL_REFRELE(ixm->ixm_ipsec_policy);
ixm->ixm_ipsec_policy = NULL;
}
if (ixm->ixm_ipsec_action != NULL) {
IPACT_REFRELE(ixm->ixm_ipsec_action);
ixm->ixm_ipsec_action = NULL;
}
if (ixm->ixm_ipsec_latch) {
IPLATCH_REFRELE(ixm->ixm_ipsec_latch);
ixm->ixm_ipsec_latch = NULL;
}
if (ixm->ixm_tsl != NULL) {
label_rele(ixm->ixm_tsl);
ixm->ixm_tsl = NULL;
}
if (ixm->ixm_cred != NULL) {
crfree(ixm->ixm_cred);
ixm->ixm_cred = NULL;
}
freeb(ixamp);
return (mp);
}
mblk_t *
ip_recv_attr_to_mblk(ip_recv_attr_t *ira)
{
mblk_t *iramp;
iramblk_t *irm;
ill_t *ill = ira->ira_ill;
ASSERT(ira->ira_ill != NULL || ira->ira_ruifindex != 0);
iramp = allocb(sizeof (*irm), BPRI_MED);
if (iramp == NULL)
return (NULL);
iramp->b_datap->db_type = M_BREAK;
iramp->b_wptr += sizeof (*irm);
irm = (iramblk_t *)iramp->b_rptr;
bzero(irm, sizeof (*irm));
irm->irm_inbound = B_TRUE;
irm->irm_flags = ira->ira_flags;
if (ill != NULL) {
irm->irm_stackid =
ill->ill_ipst->ips_netstack->netstack_stackid;
irm->irm_ifindex = ira->ira_ill->ill_phyint->phyint_ifindex;
ASSERT(ira->ira_rill->ill_phyint->phyint_ifindex ==
ira->ira_rifindex);
} else {
irm->irm_stackid = -1;
}
irm->irm_rifindex = ira->ira_rifindex;
irm->irm_ruifindex = ira->ira_ruifindex;
irm->irm_pktlen = ira->ira_pktlen;
irm->irm_ip_hdr_length = ira->ira_ip_hdr_length;
irm->irm_protocol = ira->ira_protocol;
irm->irm_ttl = ira->ira_ttl;
irm->irm_sqp = ira->ira_sqp;
irm->irm_ring = ira->ira_ring;
irm->irm_zoneid = ira->ira_zoneid;
irm->irm_mroute_tunnel = ira->ira_mroute_tunnel;
irm->irm_no_loop_zoneid = ira->ira_no_loop_zoneid;
irm->irm_esp_udp_ports = ira->ira_esp_udp_ports;
if (ira->ira_tsl != NULL) {
irm->irm_tsl = ira->ira_tsl;
label_hold(irm->irm_tsl);
}
if (ira->ira_cred != NULL) {
irm->irm_cred = ira->ira_cred;
crhold(ira->ira_cred);
}
irm->irm_cpid = ira->ira_cpid;
if (ira->ira_flags & IRAF_L2SRC_SET)
bcopy(ira->ira_l2src, irm->irm_l2src, IRA_L2SRC_SIZE);
if (ira->ira_flags & IRAF_IPSEC_SECURE) {
if (ira->ira_ipsec_ah_sa != NULL) {
irm->irm_ipsec_ah_sa = ira->ira_ipsec_ah_sa;
IPSA_REFHOLD(ira->ira_ipsec_ah_sa);
}
if (ira->ira_ipsec_esp_sa != NULL) {
irm->irm_ipsec_esp_sa = ira->ira_ipsec_esp_sa;
IPSA_REFHOLD(ira->ira_ipsec_esp_sa);
}
if (ira->ira_ipsec_action != NULL) {
irm->irm_ipsec_action = ira->ira_ipsec_action;
IPACT_REFHOLD(ira->ira_ipsec_action);
}
}
return (iramp);
}
boolean_t
ip_recv_attr_from_mblk(mblk_t *iramp, ip_recv_attr_t *ira)
{
iramblk_t *irm;
netstack_t *ns;
ip_stack_t *ipst = NULL;
ill_t *ill = NULL, *rill = NULL;
bzero(ira, sizeof (*ira));
ASSERT(DB_TYPE(iramp) == M_BREAK);
ASSERT(iramp->b_cont == NULL);
irm = (iramblk_t *)iramp->b_rptr;
ASSERT(irm->irm_inbound);
if (irm->irm_stackid != -1) {
ns = netstack_find_by_stackid(irm->irm_stackid);
if (ns == NULL) {
(void) ip_recv_attr_free_mblk(iramp);
return (B_FALSE);
}
ipst = ns->netstack_ip;
ill = ill_lookup_on_ifindex(irm->irm_ifindex,
!(irm->irm_flags & IRAF_IS_IPV4), ipst);
if (irm->irm_ifindex == irm->irm_rifindex) {
rill = ill;
} else {
rill = ill_lookup_on_ifindex(irm->irm_rifindex,
!(irm->irm_flags & IRAF_IS_IPV4), ipst);
}
netstack_rele(ns);
if (ill == NULL || rill == NULL) {
if (ill != NULL)
ill_refrele(ill);
if (rill != NULL && rill != ill)
ill_refrele(rill);
(void) ip_recv_attr_free_mblk(iramp);
return (B_FALSE);
}
}
ira->ira_flags = irm->irm_flags;
ira->ira_ill = ill;
ira->ira_rill = rill;
ira->ira_rifindex = irm->irm_rifindex;
ira->ira_ruifindex = irm->irm_ruifindex;
ira->ira_pktlen = irm->irm_pktlen;
ira->ira_ip_hdr_length = irm->irm_ip_hdr_length;
ira->ira_protocol = irm->irm_protocol;
ira->ira_ttl = irm->irm_ttl;
ira->ira_sqp = irm->irm_sqp;
ira->ira_ring = irm->irm_ring;
ira->ira_zoneid = irm->irm_zoneid;
ira->ira_mroute_tunnel = irm->irm_mroute_tunnel;
ira->ira_no_loop_zoneid = irm->irm_no_loop_zoneid;
ira->ira_esp_udp_ports = irm->irm_esp_udp_ports;
if (irm->irm_tsl != NULL) {
ira->ira_tsl = irm->irm_tsl;
ira->ira_free_flags |= IRA_FREE_TSL;
irm->irm_tsl = NULL;
}
if (irm->irm_cred != NULL) {
ira->ira_cred = irm->irm_cred;
ira->ira_free_flags |= IRA_FREE_CRED;
irm->irm_cred = NULL;
}
ira->ira_cpid = irm->irm_cpid;
if (ira->ira_flags & IRAF_L2SRC_SET)
bcopy(irm->irm_l2src, ira->ira_l2src, IRA_L2SRC_SIZE);
ira->ira_ipsec_ah_sa = irm->irm_ipsec_ah_sa;
ira->ira_ipsec_esp_sa = irm->irm_ipsec_esp_sa;
ira->ira_ipsec_action = irm->irm_ipsec_action;
freeb(iramp);
return (B_TRUE);
}
mblk_t *
ip_recv_attr_free_mblk(mblk_t *iramp)
{
iramblk_t *irm;
mblk_t *mp;
ASSERT(DB_TYPE(iramp) == M_BREAK);
mp = iramp->b_cont;
irm = (iramblk_t *)iramp->b_rptr;
ASSERT(irm->irm_inbound);
if (irm->irm_ipsec_ah_sa != NULL) {
IPSA_REFRELE(irm->irm_ipsec_ah_sa);
irm->irm_ipsec_ah_sa = NULL;
}
if (irm->irm_ipsec_esp_sa != NULL) {
IPSA_REFRELE(irm->irm_ipsec_esp_sa);
irm->irm_ipsec_esp_sa = NULL;
}
if (irm->irm_ipsec_action != NULL) {
IPACT_REFRELE(irm->irm_ipsec_action);
irm->irm_ipsec_action = NULL;
}
if (irm->irm_tsl != NULL) {
label_rele(irm->irm_tsl);
irm->irm_tsl = NULL;
}
if (irm->irm_cred != NULL) {
crfree(irm->irm_cred);
irm->irm_cred = NULL;
}
freeb(iramp);
return (mp);
}
boolean_t
ip_recv_attr_is_mblk(mblk_t *mp)
{
if (mp->b_wptr == NULL || mp->b_wptr == (uchar_t *)-1)
return (B_FALSE);
#ifdef DEBUG
iramblk_t *irm;
if (DB_TYPE(mp) != M_BREAK)
return (B_FALSE);
irm = (iramblk_t *)mp->b_rptr;
ASSERT(irm->irm_inbound);
return (B_TRUE);
#else
return (DB_TYPE(mp) == M_BREAK);
#endif
}
static ip_xmit_attr_t *
conn_get_ixa_impl(conn_t *connp, boolean_t replace, int kmflag)
{
ip_xmit_attr_t *oldixa;
ip_xmit_attr_t *ixa;
mutex_enter(&connp->conn_lock);
oldixa = connp->conn_ixa;
ASSERT3U(oldixa->ixa_refcnt, >=, 1);
if (atomic_inc_32_nv(&oldixa->ixa_refcnt) == 2) {
mutex_exit(&connp->conn_lock);
return (oldixa);
}
ixa = kmem_alloc(sizeof (*ixa), kmflag);
if (ixa == NULL) {
mutex_exit(&connp->conn_lock);
IXA_REFRELE(oldixa);
return (NULL);
}
ixa_safe_copy(oldixa, ixa);
if (replace) {
ixa->ixa_refcnt++;
connp->conn_ixa = ixa;
mutex_exit(&connp->conn_lock);
IXA_REFRELE(oldixa);
} else {
mutex_exit(&connp->conn_lock);
}
IXA_REFRELE(oldixa);
return (ixa);
}
ip_xmit_attr_t *
conn_get_ixa(conn_t *connp, boolean_t replace)
{
return (conn_get_ixa_impl(connp, replace, KM_NOSLEEP));
}
ip_xmit_attr_t *
conn_get_ixa_tryhard(conn_t *connp, boolean_t replace)
{
return (conn_get_ixa_impl(connp, replace, KM_SLEEP));
}
ip_xmit_attr_t *
conn_replace_ixa(conn_t *connp, ip_xmit_attr_t *ixa)
{
ip_xmit_attr_t *oldixa;
ASSERT(MUTEX_HELD(&connp->conn_lock));
oldixa = connp->conn_ixa;
IXA_REFHOLD(ixa);
ixa->ixa_conn_id = oldixa->ixa_conn_id;
connp->conn_ixa = ixa;
return (oldixa);
}
ip_xmit_attr_t *
conn_get_ixa_exclusive(conn_t *connp)
{
ip_xmit_attr_t *oldixa;
ip_xmit_attr_t *ixa;
ixa = kmem_alloc(sizeof (*ixa), KM_NOSLEEP_LAZY);
if (ixa == NULL)
return (NULL);
mutex_enter(&connp->conn_lock);
oldixa = connp->conn_ixa;
IXA_REFHOLD(oldixa);
ixa_safe_copy(oldixa, ixa);
mutex_exit(&connp->conn_lock);
IXA_REFRELE(oldixa);
return (ixa);
}
void
ixa_safe_copy(ip_xmit_attr_t *src, ip_xmit_attr_t *ixa)
{
bcopy(src, ixa, sizeof (*ixa));
ixa->ixa_refcnt = 1;
ixa->ixa_ire = NULL;
ixa->ixa_nce = NULL;
ixa->ixa_dce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
#ifdef DEBUG
ixa->ixa_curthread = NULL;
#endif
ixa->ixa_flags &= ~IXAF_IPSEC_SECURE;
ixa->ixa_ipsec_latch = NULL;
ixa->ixa_ipsec_ah_sa = NULL;
ixa->ixa_ipsec_esp_sa = NULL;
ixa->ixa_ipsec_policy = NULL;
ixa->ixa_ipsec_action = NULL;
if (ixa->ixa_free_flags & IXA_FREE_TSL)
label_hold(ixa->ixa_tsl);
if (ixa->ixa_free_flags & IXA_FREE_CRED)
crhold(ixa->ixa_cred);
ixa->ixa_tcpcleanup = IXATC_IDLE;
}
ip_xmit_attr_t *
ip_xmit_attr_duplicate(ip_xmit_attr_t *src_ixa)
{
ip_xmit_attr_t *ixa;
ixa = kmem_alloc(sizeof (*ixa), KM_NOSLEEP);
if (ixa == NULL)
return (NULL);
bcopy(src_ixa, ixa, sizeof (*ixa));
ixa->ixa_refcnt = 1;
if (ixa->ixa_ire != NULL)
ire_refhold_notr(ixa->ixa_ire);
if (ixa->ixa_nce != NULL)
nce_refhold(ixa->ixa_nce);
if (ixa->ixa_dce != NULL)
dce_refhold_notr(ixa->ixa_dce);
#ifdef DEBUG
ixa->ixa_curthread = NULL;
#endif
if (ixa->ixa_ipsec_latch != NULL)
IPLATCH_REFHOLD(ixa->ixa_ipsec_latch);
if (ixa->ixa_ipsec_ah_sa != NULL)
IPSA_REFHOLD(ixa->ixa_ipsec_ah_sa);
if (ixa->ixa_ipsec_esp_sa != NULL)
IPSA_REFHOLD(ixa->ixa_ipsec_esp_sa);
if (ixa->ixa_ipsec_policy != NULL)
IPPOL_REFHOLD(ixa->ixa_ipsec_policy);
if (ixa->ixa_ipsec_action != NULL)
IPACT_REFHOLD(ixa->ixa_ipsec_action);
if (ixa->ixa_tsl != NULL) {
label_hold(ixa->ixa_tsl);
ixa->ixa_free_flags |= IXA_FREE_TSL;
}
if (ixa->ixa_cred != NULL) {
crhold(ixa->ixa_cred);
ixa->ixa_free_flags |= IXA_FREE_CRED;
}
return (ixa);
}
void
ip_xmit_attr_replace_tsl(ip_xmit_attr_t *ixa, ts_label_t *tsl)
{
ASSERT(tsl != NULL);
if (ixa->ixa_free_flags & IXA_FREE_TSL) {
ASSERT(ixa->ixa_tsl != NULL);
label_rele(ixa->ixa_tsl);
} else {
ixa->ixa_free_flags |= IXA_FREE_TSL;
}
ixa->ixa_tsl = tsl;
}
boolean_t
ip_recv_attr_replace_label(ip_recv_attr_t *ira, ts_label_t *tsl)
{
cred_t *newcr;
if (ira->ira_free_flags & IRA_FREE_TSL) {
ASSERT(ira->ira_tsl != NULL);
label_rele(ira->ira_tsl);
}
label_hold(tsl);
ira->ira_tsl = tsl;
ira->ira_free_flags |= IRA_FREE_TSL;
if (ira->ira_flags & IRAF_TX_SHARED_ADDR)
ira->ira_zoneid = ALL_ZONES;
newcr = copycred_from_tslabel(ira->ira_cred, ira->ira_tsl, KM_NOSLEEP);
if (newcr == NULL)
return (B_FALSE);
if (ira->ira_free_flags & IRA_FREE_CRED)
crfree(ira->ira_cred);
ira->ira_cred = newcr;
ira->ira_free_flags |= IRA_FREE_CRED;
return (B_TRUE);
}
void
ip_xmit_attr_restore_tsl(ip_xmit_attr_t *ixa, cred_t *cr)
{
if (!is_system_labeled())
return;
if (ixa->ixa_free_flags & IXA_FREE_TSL) {
ASSERT(ixa->ixa_tsl != NULL);
label_rele(ixa->ixa_tsl);
ixa->ixa_free_flags &= ~IXA_FREE_TSL;
}
ixa->ixa_tsl = crgetlabel(cr);
}
void
ixa_refrele(ip_xmit_attr_t *ixa)
{
IXA_REFRELE(ixa);
}
void
ixa_inactive(ip_xmit_attr_t *ixa)
{
ASSERT(ixa->ixa_refcnt == 0);
ixa_cleanup(ixa);
kmem_free(ixa, sizeof (*ixa));
}
void
ixa_cleanup(ip_xmit_attr_t *ixa)
{
if (ixa->ixa_ire != NULL) {
ire_refrele_notr(ixa->ixa_ire);
ixa->ixa_ire = NULL;
}
if (ixa->ixa_dce != NULL) {
dce_refrele_notr(ixa->ixa_dce);
ixa->ixa_dce = NULL;
}
if (ixa->ixa_nce != NULL) {
nce_refrele(ixa->ixa_nce);
ixa->ixa_nce = NULL;
}
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
if (ixa->ixa_flags & IXAF_IPSEC_SECURE) {
ipsec_out_release_refs(ixa);
}
if (ixa->ixa_free_flags & IXA_FREE_TSL) {
ASSERT(ixa->ixa_tsl != NULL);
label_rele(ixa->ixa_tsl);
ixa->ixa_free_flags &= ~IXA_FREE_TSL;
}
ixa->ixa_tsl = NULL;
if (ixa->ixa_free_flags & IXA_FREE_CRED) {
ASSERT(ixa->ixa_cred != NULL);
crfree(ixa->ixa_cred);
ixa->ixa_free_flags &= ~IXA_FREE_CRED;
}
ixa->ixa_cred = NULL;
ixa->ixa_src_preferences = 0;
ixa->ixa_ifindex = 0;
ixa->ixa_multicast_ifindex = 0;
ixa->ixa_multicast_ifaddr = INADDR_ANY;
}
void
ira_cleanup(ip_recv_attr_t *ira, boolean_t refrele_ill)
{
if (ira->ira_ill != NULL) {
if (ira->ira_rill != ira->ira_ill) {
ill_refrele(ira->ira_rill);
}
if (refrele_ill)
ill_refrele(ira->ira_ill);
}
if (ira->ira_flags & IRAF_IPSEC_SECURE) {
ipsec_in_release_refs(ira);
}
if (ira->ira_free_flags & IRA_FREE_TSL) {
ASSERT(ira->ira_tsl != NULL);
label_rele(ira->ira_tsl);
ira->ira_free_flags &= ~IRA_FREE_TSL;
}
ira->ira_tsl = NULL;
if (ira->ira_free_flags & IRA_FREE_CRED) {
ASSERT(ira->ira_cred != NULL);
crfree(ira->ira_cred);
ira->ira_free_flags &= ~IRA_FREE_CRED;
}
ira->ira_cred = NULL;
}
static void
ixa_cleanup_stale(ip_xmit_attr_t *ixa)
{
ire_t *ire;
nce_t *nce;
dce_t *dce;
ire = ixa->ixa_ire;
nce = ixa->ixa_nce;
dce = ixa->ixa_dce;
if (ire != NULL && IRE_IS_CONDEMNED(ire)) {
ire_refrele_notr(ire);
ire = ire_blackhole(ixa->ixa_ipst,
!(ixa->ixa_flags & IXAF_IS_IPV4));
ASSERT(ire != NULL);
#ifdef DEBUG
ire_refhold_notr(ire);
ire_refrele(ire);
#endif
ixa->ixa_ire = ire;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
if (nce != NULL && nce->nce_is_condemned) {
nce_refrele(nce);
ixa->ixa_nce = NULL;
ixa->ixa_ire_generation = IRE_GENERATION_VERIFY;
}
if (dce != NULL && DCE_IS_CONDEMNED(dce)) {
dce_refrele_notr(dce);
dce = dce_get_default(ixa->ixa_ipst);
ASSERT(dce != NULL);
#ifdef DEBUG
dce_refhold_notr(dce);
dce_refrele(dce);
#endif
ixa->ixa_dce = dce;
ixa->ixa_dce_generation = DCE_GENERATION_VERIFY;
}
}
static mblk_t *
tcp_ixa_cleanup_getmblk(conn_t *connp)
{
tcp_stack_t *tcps = connp->conn_netstack->netstack_tcp;
int need_retry;
mblk_t *mp;
mutex_enter(&tcps->tcps_ixa_cleanup_lock);
do {
need_retry = 0;
while (connp->conn_ixa->ixa_tcpcleanup != IXATC_IDLE) {
cv_wait(&tcps->tcps_ixa_cleanup_done_cv,
&tcps->tcps_ixa_cleanup_lock);
}
while ((mp = tcps->tcps_ixa_cleanup_mp) == NULL) {
need_retry = 1;
cv_wait(&tcps->tcps_ixa_cleanup_ready_cv,
&tcps->tcps_ixa_cleanup_lock);
}
} while (need_retry);
ASSERT(MUTEX_HELD(&tcps->tcps_ixa_cleanup_lock));
ASSERT(connp->conn_ixa->ixa_tcpcleanup == IXATC_IDLE);
ASSERT(tcps->tcps_ixa_cleanup_mp == mp);
ASSERT(mp != NULL);
connp->conn_ixa->ixa_tcpcleanup = IXATC_INPROGRESS;
tcps->tcps_ixa_cleanup_mp = NULL;
mutex_exit(&tcps->tcps_ixa_cleanup_lock);
return (mp);
}
static void
tcp_ixa_cleanup(void *arg, mblk_t *mp, void *arg2,
ip_recv_attr_t *dummy)
{
conn_t *connp = (conn_t *)arg;
tcp_stack_t *tcps;
tcps = connp->conn_netstack->netstack_tcp;
ixa_cleanup_stale(connp->conn_ixa);
mutex_enter(&tcps->tcps_ixa_cleanup_lock);
ASSERT(tcps->tcps_ixa_cleanup_mp == NULL);
connp->conn_ixa->ixa_tcpcleanup = IXATC_COMPLETE;
tcps->tcps_ixa_cleanup_mp = mp;
cv_signal(&tcps->tcps_ixa_cleanup_ready_cv);
cv_broadcast(&tcps->tcps_ixa_cleanup_done_cv);
mutex_exit(&tcps->tcps_ixa_cleanup_lock);
}
static void
tcp_ixa_cleanup_wait_and_finish(conn_t *connp)
{
tcp_stack_t *tcps = connp->conn_netstack->netstack_tcp;
mutex_enter(&tcps->tcps_ixa_cleanup_lock);
ASSERT(connp->conn_ixa->ixa_tcpcleanup != IXATC_IDLE);
while (connp->conn_ixa->ixa_tcpcleanup == IXATC_INPROGRESS) {
cv_wait(&tcps->tcps_ixa_cleanup_done_cv,
&tcps->tcps_ixa_cleanup_lock);
}
ASSERT(connp->conn_ixa->ixa_tcpcleanup == IXATC_COMPLETE);
connp->conn_ixa->ixa_tcpcleanup = IXATC_IDLE;
cv_broadcast(&tcps->tcps_ixa_cleanup_done_cv);
mutex_exit(&tcps->tcps_ixa_cleanup_lock);
}
void
conn_ixa_cleanup(conn_t *connp, void *arg)
{
boolean_t tryhard = (boolean_t)arg;
if (IPCL_IS_TCP(connp)) {
mblk_t *mp;
mp = tcp_ixa_cleanup_getmblk(connp);
if (connp->conn_sqp->sq_run == curthread) {
tcp_ixa_cleanup(connp, mp, NULL, NULL);
} else {
CONN_INC_REF(connp);
SQUEUE_ENTER_ONE(connp->conn_sqp, mp, tcp_ixa_cleanup,
connp, NULL, SQ_PROCESS, SQTAG_TCP_IXA_CLEANUP);
}
tcp_ixa_cleanup_wait_and_finish(connp);
} else if (IPCL_IS_SCTP(connp)) {
sctp_t *sctp;
sctp_faddr_t *fp;
sctp = CONN2SCTP(connp);
RUN_SCTP(sctp);
ixa_cleanup_stale(connp->conn_ixa);
for (fp = sctp->sctp_faddrs; fp != NULL; fp = fp->sf_next)
ixa_cleanup_stale(fp->sf_ixa);
WAKE_SCTP(sctp);
} else {
ip_xmit_attr_t *ixa;
if (tryhard) {
ixa = conn_get_ixa_tryhard(connp, B_TRUE);
ASSERT(ixa != NULL);
} else {
ixa = conn_get_ixa(connp, B_TRUE);
if (ixa == NULL) {
DTRACE_PROBE1(conn__ixa__cleanup__bail,
conn_t *, connp);
return;
}
}
ixa_cleanup_stale(ixa);
IXA_REFRELE(ixa);
}
}
boolean_t
ixa_check_drain_insert(conn_t *connp, ip_xmit_attr_t *ixa)
{
uintptr_t cookie = ixa->ixa_cookie;
ill_dld_direct_t *idd;
idl_tx_list_t *idl_txl;
ill_t *ill = ixa->ixa_nce->nce_ill;
boolean_t inserted = B_FALSE;
idd = &(ill)->ill_dld_capab->idc_direct;
idl_txl = &ixa->ixa_ipst->ips_idl_tx_list[IDLHASHINDEX(cookie)];
mutex_enter(&idl_txl->txl_lock);
if (cookie == 0)
goto tryinsert;
ASSERT(ILL_DIRECT_CAPABLE(ill));
if (idd->idd_tx_fctl_df(idd->idd_tx_fctl_dh, cookie) == 0) {
DTRACE_PROBE1(ill__tx__not__blocked, uintptr_t, cookie);
} else if (idl_txl->txl_cookie != (uintptr_t)NULL &&
idl_txl->txl_cookie != ixa->ixa_cookie) {
DTRACE_PROBE2(ill__tx__cookie__collision, uintptr_t, cookie,
uintptr_t, idl_txl->txl_cookie);
} else {
tryinsert: mutex_enter(&connp->conn_lock);
if (connp->conn_blocked) {
DTRACE_PROBE1(ill__tx__conn__already__blocked,
conn_t *, connp);
mutex_exit(&connp->conn_lock);
} else {
connp->conn_blocked = B_TRUE;
mutex_exit(&connp->conn_lock);
idl_txl->txl_cookie = cookie;
conn_drain_insert(connp, idl_txl);
if (!IPCL_IS_NONSTR(connp))
noenable(connp->conn_wq);
inserted = B_TRUE;
}
}
mutex_exit(&idl_txl->txl_lock);
return (inserted);
}