diff options
Diffstat (limited to 'usr/src/uts/common/inet/ip/spd.c')
-rw-r--r-- | usr/src/uts/common/inet/ip/spd.c | 2213 |
1 files changed, 893 insertions, 1320 deletions
diff --git a/usr/src/uts/common/inet/ip/spd.c b/usr/src/uts/common/inet/ip/spd.c index 37a9f47432..e6903cefc2 100644 --- a/usr/src/uts/common/inet/ip/spd.c +++ b/usr/src/uts/common/inet/ip/spd.c @@ -37,6 +37,7 @@ #include <sys/strsubr.h> #include <sys/strsun.h> #include <sys/strlog.h> +#include <sys/strsun.h> #include <sys/cmn_err.h> #include <sys/zone.h> @@ -59,7 +60,6 @@ #include <net/pfkeyv2.h> #include <net/pfpolicy.h> -#include <inet/ipsec_info.h> #include <inet/sadb.h> #include <inet/ipsec_impl.h> @@ -75,16 +75,8 @@ static void ipsec_update_present_flags(ipsec_stack_t *); static ipsec_act_t *ipsec_act_wildcard_expand(ipsec_act_t *, uint_t *, netstack_t *); -static void ipsec_out_free(void *); -static void ipsec_in_free(void *); -static mblk_t *ipsec_attach_global_policy(mblk_t **, conn_t *, - ipsec_selector_t *, netstack_t *); -static mblk_t *ipsec_apply_global_policy(mblk_t *, conn_t *, - ipsec_selector_t *, netstack_t *); static mblk_t *ipsec_check_ipsecin_policy(mblk_t *, ipsec_policy_t *, - ipha_t *, ip6_t *, uint64_t, netstack_t *); -static void ipsec_in_release_refs(ipsec_in_t *); -static void ipsec_out_release_refs(ipsec_out_t *); + ipha_t *, ip6_t *, uint64_t, ip_recv_attr_t *, netstack_t *); static void ipsec_action_free_table(ipsec_action_t *); static void ipsec_action_reclaim(void *); static void ipsec_action_reclaim_stack(netstack_t *); @@ -105,9 +97,9 @@ typedef enum { SELRET_NOMEM, SELRET_BADPKT, SELRET_SUCCESS, SELRET_TUNFRAG} static selret_t ipsec_init_inbound_sel(ipsec_selector_t *, mblk_t *, ipha_t *, ip6_t *, uint8_t); -static boolean_t ipsec_check_ipsecin_action(struct ipsec_in_s *, mblk_t *, +static boolean_t ipsec_check_ipsecin_action(ip_recv_attr_t *, mblk_t *, struct ipsec_action_s *, ipha_t *ipha, ip6_t *ip6h, const char **, - kstat_named_t **); + kstat_named_t **, netstack_t *); static void ipsec_unregister_prov_update(void); static void ipsec_prov_update_callback_stack(uint32_t, void *, netstack_t *); static boolean_t ipsec_compare_action(ipsec_policy_t *, ipsec_policy_t *); @@ -117,15 +109,13 @@ static void ipsec_kstat_destroy(ipsec_stack_t *); static int ipsec_free_tables(ipsec_stack_t *); static int tunnel_compare(const void *, const void *); static void ipsec_freemsg_chain(mblk_t *); -static void ip_drop_packet_chain(mblk_t *, boolean_t, ill_t *, ire_t *, +static void ip_drop_packet_chain(mblk_t *, boolean_t, ill_t *, struct kstat_named *, ipdropper_t *); static boolean_t ipsec_kstat_init(ipsec_stack_t *); static void ipsec_kstat_destroy(ipsec_stack_t *); static int ipsec_free_tables(ipsec_stack_t *); static int tunnel_compare(const void *, const void *); static void ipsec_freemsg_chain(mblk_t *); -static void ip_drop_packet_chain(mblk_t *, boolean_t, ill_t *, ire_t *, - struct kstat_named *, ipdropper_t *); /* * Selector hash table is statically sized at module load time. @@ -150,16 +140,15 @@ static crypto_notify_handle_t prov_update_handle = NULL; static kmem_cache_t *ipsec_action_cache; static kmem_cache_t *ipsec_sel_cache; static kmem_cache_t *ipsec_pol_cache; -static kmem_cache_t *ipsec_info_cache; /* Frag cache prototypes */ -static void ipsec_fragcache_clean(ipsec_fragcache_t *); +static void ipsec_fragcache_clean(ipsec_fragcache_t *, ipsec_stack_t *); static ipsec_fragcache_entry_t *fragcache_delentry(int, - ipsec_fragcache_entry_t *, ipsec_fragcache_t *); + ipsec_fragcache_entry_t *, ipsec_fragcache_t *, ipsec_stack_t *); boolean_t ipsec_fragcache_init(ipsec_fragcache_t *); -void ipsec_fragcache_uninit(ipsec_fragcache_t *); -mblk_t *ipsec_fragcache_add(ipsec_fragcache_t *, mblk_t *, mblk_t *, int, - ipsec_stack_t *); +void ipsec_fragcache_uninit(ipsec_fragcache_t *, ipsec_stack_t *ipss); +mblk_t *ipsec_fragcache_add(ipsec_fragcache_t *, mblk_t *, mblk_t *, + int, ipsec_stack_t *); int ipsec_hdr_pullup_needed = 0; int ipsec_weird_null_inbound_policy = 0; @@ -240,23 +229,28 @@ ipsec_freemsg_chain(mblk_t *mp) ASSERT(mp->b_prev == NULL); mpnext = mp->b_next; mp->b_next = NULL; - freemsg(mp); /* Always works, even if NULL */ + freemsg(mp); mp = mpnext; } } -/* ip_drop all messages in an mblk chain */ +/* + * ip_drop all messages in an mblk chain + * Can handle a b_next chain of ip_recv_attr_t mblks, or just a b_next chain + * of data. + */ static void -ip_drop_packet_chain(mblk_t *mp, boolean_t inbound, ill_t *arriving, - ire_t *outbound_ire, struct kstat_named *counter, ipdropper_t *who_called) +ip_drop_packet_chain(mblk_t *mp, boolean_t inbound, ill_t *ill, + struct kstat_named *counter, ipdropper_t *who_called) { mblk_t *mpnext; while (mp != NULL) { ASSERT(mp->b_prev == NULL); mpnext = mp->b_next; mp->b_next = NULL; - ip_drop_packet(mp, inbound, arriving, outbound_ire, counter, - who_called); + if (ip_recv_attr_is_mblk(mp)) + mp = ip_recv_attr_free_mblk(mp); + ip_drop_packet(mp, inbound, ill, counter, who_called); mp = mpnext; } } @@ -287,7 +281,7 @@ ipsec_policy_cmpbyid(const void *a, const void *b) * ipsl_sel (selector set), so an entry with a NULL ipsp_sel is not * actually in-tree but rather a template node being used in * an avl_find query; see ipsec_policy_delete(). This gives us - * a placeholder in the ordering just before the the first entry with + * a placeholder in the ordering just before the first entry with * a key >= the one we're looking for, so we can walk forward from * that point to get the remaining entries with the same id. */ @@ -443,7 +437,6 @@ ipsec_policy_g_destroy(void) kmem_cache_destroy(ipsec_action_cache); kmem_cache_destroy(ipsec_sel_cache); kmem_cache_destroy(ipsec_pol_cache); - kmem_cache_destroy(ipsec_info_cache); ipsec_unregister_prov_update(); @@ -693,9 +686,6 @@ ipsec_policy_g_init(void) ipsec_pol_cache = kmem_cache_create("ipsec_policy", sizeof (ipsec_policy_t), _POINTER_ALIGNMENT, NULL, NULL, NULL, NULL, NULL, 0); - ipsec_info_cache = kmem_cache_create("ipsec_info", - sizeof (ipsec_info_t), _POINTER_ALIGNMENT, NULL, NULL, - NULL, NULL, NULL, 0); /* * We want to be informed each time a stack is created or @@ -920,6 +910,7 @@ ipsec_copy_policy(const ipsec_policy_t *src) src->ipsp_sel->ipsl_refs++; HASH_NULL(dst, ipsp_hash); + dst->ipsp_netstack = src->ipsp_netstack; dst->ipsp_refs = 1; dst->ipsp_sel = src->ipsp_sel; dst->ipsp_act = src->ipsp_act; @@ -1469,7 +1460,7 @@ ipsec_req_from_conn(conn_t *connp, ipsec_req_t *req, int af) bzero(req, sizeof (*req)); - mutex_enter(&connp->conn_lock); + ASSERT(MUTEX_HELD(&connp->conn_lock)); ipl = connp->conn_latch; /* @@ -1478,20 +1469,20 @@ ipsec_req_from_conn(conn_t *connp, ipsec_req_t *req, int af) * look at configured policy. */ if (ipl != NULL) { - if (ipl->ipl_in_action != NULL) { - rv = ipsec_req_from_act(ipl->ipl_in_action, req); + if (connp->conn_latch_in_action != NULL) { + rv = ipsec_req_from_act(connp->conn_latch_in_action, + req); goto done; } - if (ipl->ipl_in_policy != NULL) { - rv = ipsec_req_from_act(ipl->ipl_in_policy->ipsp_act, - req); + if (connp->conn_latch_in_policy != NULL) { + rv = ipsec_req_from_act( + connp->conn_latch_in_policy->ipsp_act, req); goto done; } } if (connp->conn_policy != NULL) rv = ipsec_req_from_head(connp->conn_policy, req, af); done: - mutex_exit(&connp->conn_lock); return (rv); } @@ -1502,66 +1493,18 @@ ipsec_actvec_free(ipsec_act_t *act, uint_t nact) } /* - * When outbound policy is not cached, look it up the hard way and attach - * an ipsec_out_t to the packet.. - */ -static mblk_t * -ipsec_attach_global_policy(mblk_t **mp, conn_t *connp, ipsec_selector_t *sel, - netstack_t *ns) -{ - ipsec_policy_t *p; - - p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, NULL, sel, ns); - - if (p == NULL) - return (NULL); - return (ipsec_attach_ipsec_out(mp, connp, p, sel->ips_protocol, ns)); -} - -/* - * We have an ipsec_out already, but don't have cached policy; fill it in - * with the right actions. - */ -static mblk_t * -ipsec_apply_global_policy(mblk_t *ipsec_mp, conn_t *connp, - ipsec_selector_t *sel, netstack_t *ns) -{ - ipsec_out_t *io; - ipsec_policy_t *p; - - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); - ASSERT(ipsec_mp->b_cont->b_datap->db_type == M_DATA); - - io = (ipsec_out_t *)ipsec_mp->b_rptr; - - if (io->ipsec_out_policy == NULL) { - p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, io, sel, ns); - io->ipsec_out_policy = p; - } - return (ipsec_mp); -} - - -/* * Consumes a reference to ipsp. */ static mblk_t * -ipsec_check_loopback_policy(mblk_t *first_mp, boolean_t mctl_present, +ipsec_check_loopback_policy(mblk_t *data_mp, ip_recv_attr_t *ira, ipsec_policy_t *ipsp) { - mblk_t *ipsec_mp; - ipsec_in_t *ii; - netstack_t *ns; - - if (!mctl_present) - return (first_mp); + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) + return (data_mp); - ipsec_mp = first_mp; + ASSERT(ira->ira_flags & IRAF_LOOPBACK); - ii = (ipsec_in_t *)ipsec_mp->b_rptr; - ns = ii->ipsec_in_ns; - ASSERT(ii->ipsec_in_loopback); - IPPOL_REFRELE(ipsp, ns); + IPPOL_REFRELE(ipsp); /* * We should do an actual policy check here. Revisit this @@ -1569,7 +1512,7 @@ ipsec_check_loopback_policy(mblk_t *first_mp, boolean_t mctl_present, * get there.) */ - return (first_mp); + return (data_mp); } /* @@ -1577,20 +1520,19 @@ ipsec_check_loopback_policy(mblk_t *first_mp, boolean_t mctl_present, * expected by the SAs it traversed on the way in. */ static boolean_t -ipsec_check_ipsecin_unique(ipsec_in_t *ii, const char **reason, - kstat_named_t **counter, uint64_t pkt_unique) +ipsec_check_ipsecin_unique(ip_recv_attr_t *ira, const char **reason, + kstat_named_t **counter, uint64_t pkt_unique, netstack_t *ns) { uint64_t ah_mask, esp_mask; ipsa_t *ah_assoc; ipsa_t *esp_assoc; - netstack_t *ns = ii->ipsec_in_ns; ipsec_stack_t *ipss = ns->netstack_ipsec; - ASSERT(ii->ipsec_in_secure); - ASSERT(!ii->ipsec_in_loopback); + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); + ASSERT(!(ira->ira_flags & IRAF_LOOPBACK)); - ah_assoc = ii->ipsec_in_ah_sa; - esp_assoc = ii->ipsec_in_esp_sa; + ah_assoc = ira->ira_ipsec_ah_sa; + esp_assoc = ira->ira_ipsec_esp_sa; ASSERT((ah_assoc != NULL) || (esp_assoc != NULL)); ah_mask = (ah_assoc != NULL) ? ah_assoc->ipsa_unique_mask : 0; @@ -1621,30 +1563,30 @@ ipsec_check_ipsecin_unique(ipsec_in_t *ii, const char **reason, } static boolean_t -ipsec_check_ipsecin_action(ipsec_in_t *ii, mblk_t *mp, ipsec_action_t *ap, - ipha_t *ipha, ip6_t *ip6h, const char **reason, kstat_named_t **counter) +ipsec_check_ipsecin_action(ip_recv_attr_t *ira, mblk_t *mp, ipsec_action_t *ap, + ipha_t *ipha, ip6_t *ip6h, const char **reason, kstat_named_t **counter, + netstack_t *ns) { boolean_t ret = B_TRUE; ipsec_prot_t *ipp; ipsa_t *ah_assoc; ipsa_t *esp_assoc; boolean_t decaps; - netstack_t *ns = ii->ipsec_in_ns; ipsec_stack_t *ipss = ns->netstack_ipsec; ASSERT((ipha == NULL && ip6h != NULL) || (ip6h == NULL && ipha != NULL)); - if (ii->ipsec_in_loopback) { + if (ira->ira_flags & IRAF_LOOPBACK) { /* * Besides accepting pointer-equivalent actions, we also * accept any ICMP errors we generated for ourselves, * regardless of policy. If we do not wish to make this * assumption in the future, check here, and where - * icmp_loopback is initialized in ip.c and ip6.c. (Look for - * ipsec_out_icmp_loopback.) + * IXAF_TRUSTED_ICMP is initialized in ip.c and ip6.c. */ - if (ap == ii->ipsec_in_action || ii->ipsec_in_icmp_loopback) + if (ap == ira->ira_ipsec_action || + (ira->ira_flags & IRAF_TRUSTED_ICMP)) return (B_TRUE); /* Deep compare necessary here?? */ @@ -1652,12 +1594,13 @@ ipsec_check_ipsecin_action(ipsec_in_t *ii, mblk_t *mp, ipsec_action_t *ap, *reason = "loopback policy mismatch"; return (B_FALSE); } - ASSERT(!ii->ipsec_in_icmp_loopback); + ASSERT(!(ira->ira_flags & IRAF_TRUSTED_ICMP)); + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); - ah_assoc = ii->ipsec_in_ah_sa; - esp_assoc = ii->ipsec_in_esp_sa; + ah_assoc = ira->ira_ipsec_ah_sa; + esp_assoc = ira->ira_ipsec_esp_sa; - decaps = ii->ipsec_in_decaps; + decaps = (ira->ira_flags & IRAF_IPSEC_DECAPS); switch (ap->ipa_act.ipa_type) { case IPSEC_ACT_DISCARD: @@ -1744,10 +1687,10 @@ ipsec_check_ipsecin_action(ipsec_in_t *ii, mblk_t *mp, ipsec_action_t *ap, } } } else if (esp_assoc != NULL) { - /* - * Don't allow this. Check IPSEC NOTE above - * ip_fanout_proto(). - */ + /* + * Don't allow this. Check IPSEC NOTE above + * ip_fanout_proto(). + */ *counter = DROPPER(ipss, ipds_spd_got_esp); *reason = "unexpected ESP"; ret = B_FALSE; @@ -1777,17 +1720,18 @@ ipsec_check_ipsecin_action(ipsec_in_t *ii, mblk_t *mp, ipsec_action_t *ap, ret = B_FALSE; break; } - if (ii->ipsec_in_action != NULL) { + if (ira->ira_ipsec_action != NULL) { /* * This can happen if we do a double policy-check on * a packet * XXX XXX should fix this case! */ - IPACT_REFRELE(ii->ipsec_in_action); + IPACT_REFRELE(ira->ira_ipsec_action); } - ASSERT(ii->ipsec_in_action == NULL); + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); + ASSERT(ira->ira_ipsec_action == NULL); IPACT_REFHOLD(ap); - ii->ipsec_in_action = ap; + ira->ira_ipsec_action = ap; break; /* from switch */ } return (ret); @@ -1818,9 +1762,9 @@ static uint64_t conn_to_unique(conn_t *connp, mblk_t *data_mp, ipha_t *ipha, ip6_t *ip6h) { ipsec_selector_t sel; - uint8_t ulp = connp->conn_ulp; + uint8_t ulp = connp->conn_proto; - ASSERT(connp->conn_latch->ipl_in_policy != NULL); + ASSERT(connp->conn_latch_in_policy != NULL); if ((ulp == IPPROTO_TCP || ulp == IPPROTO_UDP || ulp == IPPROTO_SCTP) && (connp->conn_fport == 0 || connp->conn_lport == 0)) { @@ -1839,46 +1783,51 @@ conn_to_unique(conn_t *connp, mblk_t *data_mp, ipha_t *ipha, ip6_t *ip6h) SELRET_SUCCESS) { ASSERT(sel.ips_local_port == connp->conn_lport); ASSERT(sel.ips_remote_port == connp->conn_fport); - ASSERT(sel.ips_protocol == connp->conn_ulp); + ASSERT(sel.ips_protocol == connp->conn_proto); } - ASSERT(connp->conn_ulp != 0); + ASSERT(connp->conn_proto != 0); #endif return (SA_UNIQUE_ID(connp->conn_fport, connp->conn_lport, ulp, 0)); } /* - * Called to check policy on a latched connection, both from this file - * and from tcp.c + * Called to check policy on a latched connection. + * Note that we don't dereference conn_latch or conn_ihere since the conn might + * be closing. The caller passes a held ipsec_latch_t instead. */ -boolean_t -ipsec_check_ipsecin_latch(ipsec_in_t *ii, mblk_t *mp, ipsec_latch_t *ipl, - ipha_t *ipha, ip6_t *ip6h, const char **reason, kstat_named_t **counter, - conn_t *connp) +static boolean_t +ipsec_check_ipsecin_latch(ip_recv_attr_t *ira, mblk_t *mp, ipsec_latch_t *ipl, + ipsec_action_t *ap, ipha_t *ipha, ip6_t *ip6h, const char **reason, + kstat_named_t **counter, conn_t *connp, netstack_t *ns) { - netstack_t *ns = ii->ipsec_in_ns; ipsec_stack_t *ipss = ns->netstack_ipsec; ASSERT(ipl->ipl_ids_latched == B_TRUE); + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); - if (!ii->ipsec_in_loopback) { + if (!(ira->ira_flags & IRAF_LOOPBACK)) { /* * Over loopback, there aren't real security associations, * so there are neither identities nor "unique" values * for us to check the packet against. */ - if ((ii->ipsec_in_ah_sa != NULL) && - (!spd_match_inbound_ids(ipl, ii->ipsec_in_ah_sa))) { - *counter = DROPPER(ipss, ipds_spd_ah_badid); - *reason = "AH identity mismatch"; - return (B_FALSE); + if (ira->ira_ipsec_ah_sa != NULL) { + if (!spd_match_inbound_ids(ipl, + ira->ira_ipsec_ah_sa)) { + *counter = DROPPER(ipss, ipds_spd_ah_badid); + *reason = "AH identity mismatch"; + return (B_FALSE); + } } - if ((ii->ipsec_in_esp_sa != NULL) && - (!spd_match_inbound_ids(ipl, ii->ipsec_in_esp_sa))) { - *counter = DROPPER(ipss, ipds_spd_esp_badid); - *reason = "ESP identity mismatch"; - return (B_FALSE); + if (ira->ira_ipsec_esp_sa != NULL) { + if (!spd_match_inbound_ids(ipl, + ira->ira_ipsec_esp_sa)) { + *counter = DROPPER(ipss, ipds_spd_esp_badid); + *reason = "ESP identity mismatch"; + return (B_FALSE); + } } /* @@ -1886,14 +1835,13 @@ ipsec_check_ipsecin_latch(ipsec_in_t *ii, mblk_t *mp, ipsec_latch_t *ipl, * In DEBUG kernels (see conn_to_unique()'s implementation), * verify this even if it REALLY slows things down. */ - if (!ipsec_check_ipsecin_unique(ii, reason, counter, - conn_to_unique(connp, mp, ipha, ip6h))) { + if (!ipsec_check_ipsecin_unique(ira, reason, counter, + conn_to_unique(connp, mp, ipha, ip6h), ns)) { return (B_FALSE); } } - - return (ipsec_check_ipsecin_action(ii, mp, ipl->ipl_in_action, - ipha, ip6h, reason, counter)); + return (ipsec_check_ipsecin_action(ira, mp, ap, ipha, ip6h, reason, + counter, ns)); } /* @@ -1903,52 +1851,48 @@ ipsec_check_ipsecin_latch(ipsec_in_t *ii, mblk_t *mp, ipsec_latch_t *ipl, * Called from ipsec_check_global_policy, and ipsec_check_inbound_policy. * * Consumes a reference to ipsp. + * Returns the mblk if ok. */ static mblk_t * -ipsec_check_ipsecin_policy(mblk_t *first_mp, ipsec_policy_t *ipsp, - ipha_t *ipha, ip6_t *ip6h, uint64_t pkt_unique, netstack_t *ns) +ipsec_check_ipsecin_policy(mblk_t *data_mp, ipsec_policy_t *ipsp, + ipha_t *ipha, ip6_t *ip6h, uint64_t pkt_unique, ip_recv_attr_t *ira, + netstack_t *ns) { - ipsec_in_t *ii; ipsec_action_t *ap; const char *reason = "no policy actions found"; - mblk_t *data_mp, *ipsec_mp; - ipsec_stack_t *ipss = ns->netstack_ipsec; ip_stack_t *ipst = ns->netstack_ip; + ipsec_stack_t *ipss = ns->netstack_ipsec; kstat_named_t *counter; counter = DROPPER(ipss, ipds_spd_got_secure); - data_mp = first_mp->b_cont; - ipsec_mp = first_mp; - ASSERT(ipsp != NULL); ASSERT((ipha == NULL && ip6h != NULL) || (ip6h == NULL && ipha != NULL)); - ii = (ipsec_in_t *)ipsec_mp->b_rptr; + if (ira->ira_flags & IRAF_LOOPBACK) + return (ipsec_check_loopback_policy(data_mp, ira, ipsp)); - if (ii->ipsec_in_loopback) - return (ipsec_check_loopback_policy(first_mp, B_TRUE, ipsp)); - ASSERT(ii->ipsec_in_type == IPSEC_IN); - ASSERT(ii->ipsec_in_secure); + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); - if (ii->ipsec_in_action != NULL) { + if (ira->ira_ipsec_action != NULL) { /* * this can happen if we do a double policy-check on a packet * Would be nice to be able to delete this test.. */ - IPACT_REFRELE(ii->ipsec_in_action); + IPACT_REFRELE(ira->ira_ipsec_action); } - ASSERT(ii->ipsec_in_action == NULL); + ASSERT(ira->ira_ipsec_action == NULL); - if (!SA_IDS_MATCH(ii->ipsec_in_ah_sa, ii->ipsec_in_esp_sa)) { + if (!SA_IDS_MATCH(ira->ira_ipsec_ah_sa, ira->ira_ipsec_esp_sa)) { reason = "inbound AH and ESP identities differ"; counter = DROPPER(ipss, ipds_spd_ahesp_diffid); goto drop; } - if (!ipsec_check_ipsecin_unique(ii, &reason, &counter, pkt_unique)) + if (!ipsec_check_ipsecin_unique(ira, &reason, &counter, pkt_unique, + ns)) goto drop; /* @@ -1957,21 +1901,21 @@ ipsec_check_ipsecin_policy(mblk_t *first_mp, ipsec_policy_t *ipsp, */ for (ap = ipsp->ipsp_act; ap != NULL; ap = ap->ipa_next) { - if (ipsec_check_ipsecin_action(ii, data_mp, ap, - ipha, ip6h, &reason, &counter)) { + if (ipsec_check_ipsecin_action(ira, data_mp, ap, + ipha, ip6h, &reason, &counter, ns)) { BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - IPPOL_REFRELE(ipsp, ns); - return (first_mp); + IPPOL_REFRELE(ipsp); + return (data_mp); } } drop: ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, SL_ERROR|SL_WARN|SL_CONSOLE, "ipsec inbound policy mismatch: %s, packet dropped\n", reason); - IPPOL_REFRELE(ipsp, ns); - ASSERT(ii->ipsec_in_action == NULL); + IPPOL_REFRELE(ipsp); + ASSERT(ira->ira_ipsec_action == NULL); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); - ip_drop_packet(first_mp, B_TRUE, NULL, NULL, counter, + ip_drop_packet(data_mp, B_TRUE, NULL, counter, &ipss->ipsec_spd_dropper); return (NULL); } @@ -2075,7 +2019,7 @@ ipsec_find_policy_chain(ipsec_policy_t *best, ipsec_policy_t *chain, */ ipsec_policy_t * ipsec_find_policy_head(ipsec_policy_t *best, ipsec_policy_head_t *head, - int direction, ipsec_selector_t *sel, netstack_t *ns) + int direction, ipsec_selector_t *sel) { ipsec_policy_t *curbest; ipsec_policy_root_t *root; @@ -2121,7 +2065,7 @@ ipsec_find_policy_head(ipsec_policy_t *best, ipsec_policy_head_t *head, IPPOL_REFHOLD(curbest); if (best != NULL) { - IPPOL_REFRELE(best, ns); + IPPOL_REFRELE(best); } } @@ -2139,20 +2083,17 @@ ipsec_find_policy_head(ipsec_policy_t *best, ipsec_policy_head_t *head, * reference when done. */ ipsec_policy_t * -ipsec_find_policy(int direction, conn_t *connp, ipsec_out_t *io, - ipsec_selector_t *sel, netstack_t *ns) +ipsec_find_policy(int direction, const conn_t *connp, ipsec_selector_t *sel, + netstack_t *ns) { ipsec_policy_t *p; ipsec_stack_t *ipss = ns->netstack_ipsec; p = ipsec_find_policy_head(NULL, &ipss->ipsec_system_policy, - direction, sel, ns); + direction, sel); if ((connp != NULL) && (connp->conn_policy != NULL)) { p = ipsec_find_policy_head(p, connp->conn_policy, - direction, sel, ns); - } else if ((io != NULL) && (io->ipsec_out_polhead != NULL)) { - p = ipsec_find_policy_head(p, io->ipsec_out_polhead, - direction, sel, ns); + direction, sel); } return (p); @@ -2172,21 +2113,16 @@ ipsec_find_policy(int direction, conn_t *connp, ipsec_out_t *io, * floor. */ mblk_t * -ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, - ipha_t *ipha, ip6_t *ip6h, boolean_t mctl_present, netstack_t *ns) +ipsec_check_global_policy(mblk_t *data_mp, conn_t *connp, + ipha_t *ipha, ip6_t *ip6h, ip_recv_attr_t *ira, netstack_t *ns) { ipsec_policy_t *p; ipsec_selector_t sel; - mblk_t *data_mp, *ipsec_mp; boolean_t policy_present; kstat_named_t *counter; - ipsec_in_t *ii = NULL; uint64_t pkt_unique; - ipsec_stack_t *ipss = ns->netstack_ipsec; ip_stack_t *ipst = ns->netstack_ip; - - data_mp = mctl_present ? first_mp->b_cont : first_mp; - ipsec_mp = mctl_present ? first_mp : NULL; + ipsec_stack_t *ipss = ns->netstack_ipsec; sel.ips_is_icmp_inv_acq = 0; @@ -2203,13 +2139,7 @@ ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, * No global policy and no per-socket policy; * just pass it back (but we shouldn't get here in that case) */ - return (first_mp); - } - - if (ipsec_mp != NULL) { - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); - ii = (ipsec_in_t *)(ipsec_mp->b_rptr); - ASSERT(ii->ipsec_in_type == IPSEC_IN); + return (data_mp); } /* @@ -2217,32 +2147,11 @@ ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, * Otherwise consult system policy. */ if ((connp != NULL) && (connp->conn_latch != NULL)) { - p = connp->conn_latch->ipl_in_policy; + p = connp->conn_latch_in_policy; if (p != NULL) { IPPOL_REFHOLD(p); } /* - * The caller may have mistakenly assigned an ip6i_t as the - * ip6h for this packet, so take that corner-case into - * account. - */ - if (ip6h != NULL && ip6h->ip6_nxt == IPPROTO_RAW) { - ip6h++; - /* First check for bizarro split-mblk headers. */ - if ((uintptr_t)ip6h > (uintptr_t)data_mp->b_wptr || - ((uintptr_t)ip6h) + sizeof (ip6_t) > - (uintptr_t)data_mp->b_wptr) { - ipsec_log_policy_failure(IPSEC_POLICY_MISMATCH, - "ipsec_check_global_policy", ipha, ip6h, - B_TRUE, ns); - counter = DROPPER(ipss, ipds_spd_nomem); - goto fail; - } - /* Next, see if ip6i is at the end of an mblk. */ - if (ip6h == (ip6_t *)data_mp->b_wptr) - ip6h = (ip6_t *)data_mp->b_cont->b_rptr; - } - /* * Fudge sel for UNIQUE_ID setting below. */ pkt_unique = conn_to_unique(connp, data_mp, ipha, ip6h); @@ -2271,20 +2180,19 @@ ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, * local policy alone. */ - p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, NULL, &sel, - ns); + p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, &sel, ns); pkt_unique = SA_UNIQUE_ID(sel.ips_remote_port, sel.ips_local_port, sel.ips_protocol, 0); } if (p == NULL) { - if (ipsec_mp == NULL) { + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { /* * We have no policy; default to succeeding. * XXX paranoid system design doesn't do this. */ BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - return (first_mp); + return (data_mp); } else { counter = DROPPER(ipss, ipds_spd_got_secure); ipsec_log_policy_failure(IPSEC_POLICY_NOT_NEEDED, @@ -2293,16 +2201,16 @@ ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, goto fail; } } - if ((ii != NULL) && (ii->ipsec_in_secure)) { - return (ipsec_check_ipsecin_policy(ipsec_mp, p, ipha, ip6h, - pkt_unique, ns)); + if (ira->ira_flags & IRAF_IPSEC_SECURE) { + return (ipsec_check_ipsecin_policy(data_mp, p, ipha, ip6h, + pkt_unique, ira, ns)); } if (p->ipsp_act->ipa_allow_clear) { BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - IPPOL_REFRELE(p, ns); - return (first_mp); + IPPOL_REFRELE(p); + return (data_mp); } - IPPOL_REFRELE(p, ns); + IPPOL_REFRELE(p); /* * If we reach here, we will drop the packet because it failed the * global policy check because the packet was cleartext, and it @@ -2313,7 +2221,7 @@ ipsec_check_global_policy(mblk_t *first_mp, conn_t *connp, counter = DROPPER(ipss, ipds_spd_got_clear); fail: - ip_drop_packet(first_mp, B_TRUE, NULL, NULL, counter, + ip_drop_packet(data_mp, B_TRUE, NULL, counter, &ipss->ipsec_spd_dropper); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); return (NULL); @@ -2435,7 +2343,7 @@ ipsec_inbound_accept_clear(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h) case ICMP_FRAGMENTATION_NEEDED: /* * Be in sync with icmp_inbound, where we have - * already set ire_max_frag. + * already set dce_pmtu */ #ifdef FRAGCACHE_DEBUG cmn_err(CE_WARN, "ICMP frag needed\n"); @@ -2496,27 +2404,44 @@ ipsec_latch_ids(ipsec_latch_t *ipl, ipsid_t *local, ipsid_t *remote) } void -ipsec_latch_inbound(ipsec_latch_t *ipl, ipsec_in_t *ii) +ipsec_latch_inbound(conn_t *connp, ip_recv_attr_t *ira) { ipsa_t *sa; + ipsec_latch_t *ipl = connp->conn_latch; if (!ipl->ipl_ids_latched) { ipsid_t *local = NULL; ipsid_t *remote = NULL; - if (!ii->ipsec_in_loopback) { - if (ii->ipsec_in_esp_sa != NULL) - sa = ii->ipsec_in_esp_sa; + if (!(ira->ira_flags & IRAF_LOOPBACK)) { + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); + if (ira->ira_ipsec_esp_sa != NULL) + sa = ira->ira_ipsec_esp_sa; else - sa = ii->ipsec_in_ah_sa; + sa = ira->ira_ipsec_ah_sa; ASSERT(sa != NULL); local = sa->ipsa_dst_cid; remote = sa->ipsa_src_cid; } ipsec_latch_ids(ipl, local, remote); } - ipl->ipl_in_action = ii->ipsec_in_action; - IPACT_REFHOLD(ipl->ipl_in_action); + if (ira->ira_flags & IRAF_IPSEC_SECURE) { + if (connp->conn_latch_in_action != NULL) { + /* + * Previously cached action. This is probably + * harmless, but in DEBUG kernels, check for + * action equality. + * + * Preserve the existing action to preserve latch + * invariance. + */ + ASSERT(connp->conn_latch_in_action == + ira->ira_ipsec_action); + return; + } + connp->conn_latch_in_action = ira->ira_ipsec_action; + IPACT_REFHOLD(connp->conn_latch_in_action); + } } /* @@ -2527,27 +2452,25 @@ ipsec_latch_inbound(ipsec_latch_t *ipl, ipsec_in_t *ii) * see also ipsec_check_ipsecin_latch() and ipsec_check_global_policy() */ mblk_t * -ipsec_check_inbound_policy(mblk_t *first_mp, conn_t *connp, - ipha_t *ipha, ip6_t *ip6h, boolean_t mctl_present) +ipsec_check_inbound_policy(mblk_t *mp, conn_t *connp, + ipha_t *ipha, ip6_t *ip6h, ip_recv_attr_t *ira) { - ipsec_in_t *ii; - boolean_t ret; - mblk_t *mp = mctl_present ? first_mp->b_cont : first_mp; - mblk_t *ipsec_mp = mctl_present ? first_mp : NULL; - ipsec_latch_t *ipl; - uint64_t unique_id; + boolean_t ret; + ipsec_latch_t *ipl; + ipsec_action_t *ap; + uint64_t unique_id; ipsec_stack_t *ipss; ip_stack_t *ipst; netstack_t *ns; ipsec_policy_head_t *policy_head; + ipsec_policy_t *p = NULL; ASSERT(connp != NULL); ns = connp->conn_netstack; ipss = ns->netstack_ipsec; ipst = ns->netstack_ip; - if (ipsec_mp == NULL) { -clear: + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { /* * This is the case where the incoming datagram is * cleartext and we need to see whether this client @@ -2559,49 +2482,49 @@ clear: mutex_enter(&connp->conn_lock); if (connp->conn_state_flags & CONN_CONDEMNED) { mutex_exit(&connp->conn_lock); - ip_drop_packet(first_mp, B_TRUE, NULL, - NULL, DROPPER(ipss, ipds_spd_got_clear), + ip_drop_packet(mp, B_TRUE, NULL, + DROPPER(ipss, ipds_spd_got_clear), &ipss->ipsec_spd_dropper); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); return (NULL); } - if ((ipl = connp->conn_latch) != NULL) { + if (connp->conn_latch != NULL) { /* Hold a reference in case the conn is closing */ - IPLATCH_REFHOLD(ipl); + p = connp->conn_latch_in_policy; + if (p != NULL) + IPPOL_REFHOLD(p); mutex_exit(&connp->conn_lock); /* * Policy is cached in the conn. */ - if ((ipl->ipl_in_policy != NULL) && - (!ipl->ipl_in_policy->ipsp_act->ipa_allow_clear)) { + if (p != NULL && !p->ipsp_act->ipa_allow_clear) { ret = ipsec_inbound_accept_clear(mp, ipha, ip6h); if (ret) { BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - IPLATCH_REFRELE(ipl, ns); - return (first_mp); + IPPOL_REFRELE(p); + return (mp); } else { ipsec_log_policy_failure( IPSEC_POLICY_MISMATCH, "ipsec_check_inbound_policy", ipha, ip6h, B_FALSE, ns); - ip_drop_packet(first_mp, B_TRUE, NULL, - NULL, + ip_drop_packet(mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_got_clear), &ipss->ipsec_spd_dropper); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); - IPLATCH_REFRELE(ipl, ns); + IPPOL_REFRELE(p); return (NULL); } } else { BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - IPLATCH_REFRELE(ipl, ns); - return (first_mp); + if (p != NULL) + IPPOL_REFRELE(p); + return (mp); } } else { - uchar_t db_type; policy_head = connp->conn_policy; /* Hold a reference in case the conn is closing */ @@ -2611,50 +2534,22 @@ clear: /* * As this is a non-hardbound connection we need * to look at both per-socket policy and global - * policy. As this is cleartext, mark the mp as - * M_DATA in case if it is an ICMP error being - * reported before calling ipsec_check_global_policy - * so that it does not mistake it for IPSEC_IN. + * policy. */ - db_type = mp->b_datap->db_type; - mp->b_datap->db_type = M_DATA; - first_mp = ipsec_check_global_policy(first_mp, connp, - ipha, ip6h, mctl_present, ns); + mp = ipsec_check_global_policy(mp, connp, + ipha, ip6h, ira, ns); if (policy_head != NULL) IPPH_REFRELE(policy_head, ns); - if (first_mp != NULL) - mp->b_datap->db_type = db_type; - return (first_mp); + return (mp); } } - /* - * If it is inbound check whether the attached message - * is secure or not. We have a special case for ICMP, - * where we have a IPSEC_IN message and the attached - * message is not secure. See icmp_inbound_error_fanout - * for details. - */ - ASSERT(ipsec_mp != NULL); - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); - ii = (ipsec_in_t *)ipsec_mp->b_rptr; - - if (!ii->ipsec_in_secure) - goto clear; - - /* - * mp->b_cont could be either a M_CTL message - * for icmp errors being sent up or a M_DATA message. - */ - ASSERT(mp->b_datap->db_type == M_CTL || mp->b_datap->db_type == M_DATA); - - ASSERT(ii->ipsec_in_type == IPSEC_IN); mutex_enter(&connp->conn_lock); /* Connection is closing */ if (connp->conn_state_flags & CONN_CONDEMNED) { mutex_exit(&connp->conn_lock); - ip_drop_packet(first_mp, B_TRUE, NULL, - NULL, DROPPER(ipss, ipds_spd_got_clear), + ip_drop_packet(mp, B_TRUE, NULL, + DROPPER(ipss, ipds_spd_got_clear), &ipss->ipsec_spd_dropper); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); return (NULL); @@ -2679,58 +2574,64 @@ clear: * policy. It will check against conn or global * depending on whichever is stronger. */ - retmp = ipsec_check_global_policy(first_mp, connp, - ipha, ip6h, mctl_present, ns); + retmp = ipsec_check_global_policy(mp, connp, + ipha, ip6h, ira, ns); if (policy_head != NULL) IPPH_REFRELE(policy_head, ns); return (retmp); } IPLATCH_REFHOLD(ipl); + /* Hold reference on conn_latch_in_action in case conn is closing */ + ap = connp->conn_latch_in_action; + if (ap != NULL) + IPACT_REFHOLD(ap); mutex_exit(&connp->conn_lock); - if (ipl->ipl_in_action != NULL) { + if (ap != NULL) { /* Policy is cached & latched; fast(er) path */ const char *reason; kstat_named_t *counter; - if (ipsec_check_ipsecin_latch(ii, mp, ipl, - ipha, ip6h, &reason, &counter, connp)) { + if (ipsec_check_ipsecin_latch(ira, mp, ipl, ap, + ipha, ip6h, &reason, &counter, connp, ns)) { BUMP_MIB(&ipst->ips_ip_mib, ipsecInSucceeded); - IPLATCH_REFRELE(ipl, ns); - return (first_mp); + IPLATCH_REFRELE(ipl); + IPACT_REFRELE(ap); + return (mp); } ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, SL_ERROR|SL_WARN|SL_CONSOLE, "ipsec inbound policy mismatch: %s, packet dropped\n", reason); - ip_drop_packet(first_mp, B_TRUE, NULL, NULL, counter, + ip_drop_packet(mp, B_TRUE, NULL, counter, &ipss->ipsec_spd_dropper); BUMP_MIB(&ipst->ips_ip_mib, ipsecInFailed); - IPLATCH_REFRELE(ipl, ns); + IPLATCH_REFRELE(ipl); + IPACT_REFRELE(ap); return (NULL); - } else if (ipl->ipl_in_policy == NULL) { + } + if ((p = connp->conn_latch_in_policy) == NULL) { ipsec_weird_null_inbound_policy++; - IPLATCH_REFRELE(ipl, ns); - return (first_mp); + IPLATCH_REFRELE(ipl); + return (mp); } unique_id = conn_to_unique(connp, mp, ipha, ip6h); - IPPOL_REFHOLD(ipl->ipl_in_policy); - first_mp = ipsec_check_ipsecin_policy(first_mp, ipl->ipl_in_policy, - ipha, ip6h, unique_id, ns); + IPPOL_REFHOLD(p); + mp = ipsec_check_ipsecin_policy(mp, p, ipha, ip6h, unique_id, ira, ns); /* * NOTE: ipsecIn{Failed,Succeeeded} bumped by * ipsec_check_ipsecin_policy(). */ - if (first_mp != NULL) - ipsec_latch_inbound(ipl, ii); - IPLATCH_REFRELE(ipl, ns); - return (first_mp); + if (mp != NULL) + ipsec_latch_inbound(connp, ira); + IPLATCH_REFRELE(ipl); + return (mp); } /* - * Handle all sorts of cases like tunnel-mode, ICMP, and ip6i prepending. + * Handle all sorts of cases like tunnel-mode and ICMP. */ static int prepended_length(mblk_t *mp, uintptr_t hptr) @@ -2779,19 +2680,24 @@ prepended_length(mblk_t *mp, uintptr_t hptr) * should put this packet in a fragment-gathering queue. * Only returned if SEL_TUNNEL_MODE and SEL_PORT_POLICY * is set. + * + * Note that ipha/ip6h can be in a different mblk (mp->b_cont) in the case + * of tunneled packets. + * Also, mp->b_rptr can be an ICMP error where ipha/ip6h is the packet in + * error past the ICMP error. */ static selret_t ipsec_init_inbound_sel(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, uint8_t sel_flags) { uint16_t *ports; - int outer_hdr_len = 0; /* For ICMP, tunnel-mode, or ip6i cases... */ + int outer_hdr_len = 0; /* For ICMP or tunnel-mode cases... */ ushort_t hdr_len; mblk_t *spare_mp = NULL; uint8_t *nexthdrp, *transportp; uint8_t nexthdr; uint8_t icmp_proto; - ip6_pkt_t ipp; + ip_pkt_t ipp; boolean_t port_policy_present = (sel_flags & SEL_PORT_POLICY); boolean_t is_icmp = (sel_flags & SEL_IS_ICMP); boolean_t tunnel_mode = (sel_flags & SEL_TUNNEL_MODE); @@ -2802,44 +2708,14 @@ ipsec_init_inbound_sel(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, if (ip6h != NULL) { outer_hdr_len = prepended_length(mp, (uintptr_t)ip6h); - nexthdr = ip6h->ip6_nxt; - - /* - * The caller may have mistakenly assigned an ip6i_t as the - * ip6h for this packet, so take that corner-case into - * account. - */ - if (nexthdr == IPPROTO_RAW) { - ip6h++; - /* First check for bizarro split-mblk headers. */ - if ((uintptr_t)ip6h > (uintptr_t)mp->b_wptr || - ((uintptr_t)ip6h) + sizeof (ip6_t) > - (uintptr_t)mp->b_wptr) { - return (SELRET_BADPKT); - } - /* Next, see if ip6i is at the end of an mblk. */ - if (ip6h == (ip6_t *)mp->b_wptr) - ip6h = (ip6_t *)mp->b_cont->b_rptr; - - nexthdr = ip6h->ip6_nxt; - - /* - * Finally, if we haven't adjusted for ip6i, do so - * now. ip6i_t structs are prepended, so an ICMP - * or tunnel packet would just be overwritten. - */ - if (outer_hdr_len == 0) - outer_hdr_len = sizeof (ip6i_t); - } - icmp_proto = IPPROTO_ICMPV6; sel->ips_isv4 = B_FALSE; sel->ips_local_addr_v6 = ip6h->ip6_dst; sel->ips_remote_addr_v6 = ip6h->ip6_src; bzero(&ipp, sizeof (ipp)); - (void) ip_find_hdr_v6(mp, ip6h, &ipp, NULL); + (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &ipp, NULL); switch (nexthdr) { case IPPROTO_HOPOPTS: @@ -2852,7 +2728,6 @@ ipsec_init_inbound_sel(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, */ if ((spare_mp = msgpullup(mp, -1)) == NULL) return (SELRET_NOMEM); - if (!ip_hdr_length_nexthdr_v6(spare_mp, (ip6_t *)(spare_mp->b_rptr + outer_hdr_len), &hdr_len, &nexthdrp)) { @@ -2930,6 +2805,10 @@ ipsec_init_inbound_sel(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, return (SELRET_SUCCESS); } +/* + * This is called with a b_next chain of messages from the fragcache code, + * hence it needs to discard a chain on error. + */ static boolean_t ipsec_init_outbound_ports(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, int outer_hdr_len, ipsec_stack_t *ipss) @@ -2967,7 +2846,7 @@ ipsec_init_outbound_ports(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, &hdr_len, &nexthdrp)) { /* Always works, even if NULL. */ ipsec_freemsg_chain(spare_mp); - ip_drop_packet_chain(mp, B_FALSE, NULL, NULL, + ip_drop_packet_chain(mp, B_FALSE, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); return (B_FALSE); @@ -3005,7 +2884,7 @@ ipsec_init_outbound_ports(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, */ if (spare_mp == NULL && (spare_mp = msgpullup(mp, -1)) == NULL) { - ip_drop_packet_chain(mp, B_FALSE, NULL, NULL, + ip_drop_packet_chain(mp, B_FALSE, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); return (B_FALSE); @@ -3029,13 +2908,68 @@ ipsec_init_outbound_ports(ipsec_selector_t *sel, mblk_t *mp, ipha_t *ipha, } /* + * Prepend an mblk with a ipsec_crypto_t to the message chain. + * Frees the argument and returns NULL should the allocation fail. + * Returns the pointer to the crypto data part. + */ +mblk_t * +ipsec_add_crypto_data(mblk_t *data_mp, ipsec_crypto_t **icp) +{ + mblk_t *mp; + + mp = allocb(sizeof (ipsec_crypto_t), BPRI_MED); + if (mp == NULL) { + freemsg(data_mp); + return (NULL); + } + bzero(mp->b_rptr, sizeof (ipsec_crypto_t)); + mp->b_wptr += sizeof (ipsec_crypto_t); + mp->b_cont = data_mp; + mp->b_datap->db_type = M_EVENT; /* For ASSERT */ + *icp = (ipsec_crypto_t *)mp->b_rptr; + return (mp); +} + +/* + * Remove what was prepended above. Return b_cont and a pointer to the + * crypto data. + * The caller must call ipsec_free_crypto_data for mblk once it is done + * with the crypto data. + */ +mblk_t * +ipsec_remove_crypto_data(mblk_t *crypto_mp, ipsec_crypto_t **icp) +{ + ASSERT(crypto_mp->b_datap->db_type == M_EVENT); + ASSERT(MBLKL(crypto_mp) == sizeof (ipsec_crypto_t)); + + *icp = (ipsec_crypto_t *)crypto_mp->b_rptr; + return (crypto_mp->b_cont); +} + +/* + * Free what was prepended above. Return b_cont. + */ +mblk_t * +ipsec_free_crypto_data(mblk_t *crypto_mp) +{ + mblk_t *mp; + + ASSERT(crypto_mp->b_datap->db_type == M_EVENT); + ASSERT(MBLKL(crypto_mp) == sizeof (ipsec_crypto_t)); + + mp = crypto_mp->b_cont; + freeb(crypto_mp); + return (mp); +} + +/* * Create an ipsec_action_t based on the way an inbound packet was protected. * Used to reflect traffic back to a sender. * * We don't bother interning the action into the hash table. */ ipsec_action_t * -ipsec_in_to_out_action(ipsec_in_t *ii) +ipsec_in_to_out_action(ip_recv_attr_t *ira) { ipsa_t *ah_assoc, *esp_assoc; uint_t auth_alg = 0, encr_alg = 0, espa_alg = 0; @@ -3057,10 +2991,12 @@ ipsec_in_to_out_action(ipsec_in_t *ii) */ ap->ipa_act.ipa_type = IPSEC_ACT_APPLY; ap->ipa_act.ipa_log = 0; - ah_assoc = ii->ipsec_in_ah_sa; + ASSERT(ira->ira_flags & IRAF_IPSEC_SECURE); + + ah_assoc = ira->ira_ipsec_ah_sa; ap->ipa_act.ipa_apply.ipp_use_ah = (ah_assoc != NULL); - esp_assoc = ii->ipsec_in_esp_sa; + esp_assoc = ira->ira_ipsec_esp_sa; ap->ipa_act.ipa_apply.ipp_use_esp = (esp_assoc != NULL); if (esp_assoc != NULL) { @@ -3074,7 +3010,8 @@ ipsec_in_to_out_action(ipsec_in_t *ii) ap->ipa_act.ipa_apply.ipp_encr_alg = (uint8_t)encr_alg; ap->ipa_act.ipa_apply.ipp_auth_alg = (uint8_t)auth_alg; ap->ipa_act.ipa_apply.ipp_esp_auth_alg = (uint8_t)espa_alg; - ap->ipa_act.ipa_apply.ipp_use_se = ii->ipsec_in_decaps; + ap->ipa_act.ipa_apply.ipp_use_se = + !!(ira->ira_flags & IRAF_IPSEC_DECAPS); unique = B_FALSE; if (esp_assoc != NULL) { @@ -3104,7 +3041,7 @@ ipsec_in_to_out_action(ipsec_in_t *ii) ap->ipa_act.ipa_apply.ipp_use_unique = unique; ap->ipa_want_unique = unique; ap->ipa_allow_clear = B_FALSE; - ap->ipa_want_se = ii->ipsec_in_decaps; + ap->ipa_want_se = !!(ira->ira_flags & IRAF_IPSEC_DECAPS); ap->ipa_want_ah = (ah_assoc != NULL); ap->ipa_want_esp = (esp_assoc != NULL); @@ -3500,13 +3437,14 @@ ipsec_sel_rel(ipsec_sel_t **spp, netstack_t *ns) * Free a policy rule which we know is no longer being referenced. */ void -ipsec_policy_free(ipsec_policy_t *ipp, netstack_t *ns) +ipsec_policy_free(ipsec_policy_t *ipp) { ASSERT(ipp->ipsp_refs == 0); ASSERT(ipp->ipsp_sel != NULL); ASSERT(ipp->ipsp_act != NULL); + ASSERT(ipp->ipsp_netstack != NULL); - ipsec_sel_rel(&ipp->ipsp_sel, ns); + ipsec_sel_rel(&ipp->ipsp_sel, ipp->ipsp_netstack); IPACT_REFRELE(ipp->ipsp_act); kmem_cache_free(ipsec_pol_cache, ipp); } @@ -3544,6 +3482,7 @@ ipsec_policy_create(ipsec_selkey_t *keys, const ipsec_act_t *a, HASH_NULL(ipp, ipsp_hash); + ipp->ipsp_netstack = ns; /* Needed for ipsec_policy_free */ ipp->ipsp_refs = 1; /* caller's reference */ ipp->ipsp_sel = sp; ipp->ipsp_act = ap; @@ -3613,7 +3552,7 @@ ipsec_policy_delete(ipsec_policy_head_t *php, ipsec_selkey_t *keys, int dir, continue; } - IPPOL_UNCHAIN(php, ip, ns); + IPPOL_UNCHAIN(php, ip); php->iph_gen++; ipsec_update_present_flags(ns->netstack_ipsec); @@ -3664,7 +3603,7 @@ ipsec_policy_delete_index(ipsec_policy_head_t *php, uint64_t policy_index, break; } - IPPOL_UNCHAIN(php, ip, ns); + IPPOL_UNCHAIN(php, ip); found = B_TRUE; } @@ -3897,8 +3836,7 @@ ipsec_enter_policy(ipsec_policy_head_t *php, ipsec_policy_t *ipp, int direction, } static void -ipsec_ipr_flush(ipsec_policy_head_t *php, ipsec_policy_root_t *ipr, - netstack_t *ns) +ipsec_ipr_flush(ipsec_policy_head_t *php, ipsec_policy_root_t *ipr) { ipsec_policy_t *ip, *nip; int af, chain, nchain; @@ -3906,7 +3844,7 @@ ipsec_ipr_flush(ipsec_policy_head_t *php, ipsec_policy_root_t *ipr, for (af = 0; af < IPSEC_NAF; af++) { for (ip = ipr->ipr_nonhash[af]; ip != NULL; ip = nip) { nip = ip->ipsp_hash.hash_next; - IPPOL_UNCHAIN(php, ip, ns); + IPPOL_UNCHAIN(php, ip); } ipr->ipr_nonhash[af] = NULL; } @@ -3916,7 +3854,7 @@ ipsec_ipr_flush(ipsec_policy_head_t *php, ipsec_policy_root_t *ipr, for (ip = ipr->ipr_hash[chain].hash_head; ip != NULL; ip = nip) { nip = ip->ipsp_hash.hash_next; - IPPOL_UNCHAIN(php, ip, ns); + IPPOL_UNCHAIN(php, ip); } ipr->ipr_hash[chain].hash_head = NULL; } @@ -3954,8 +3892,9 @@ ipsec_polhead_flush(ipsec_policy_head_t *php, netstack_t *ns) ASSERT(RW_WRITE_HELD(&php->iph_lock)); for (dir = 0; dir < IPSEC_NTYPES; dir++) - ipsec_ipr_flush(php, &php->iph_root[dir], ns); + ipsec_ipr_flush(php, &php->iph_root[dir]); + php->iph_gen++; ipsec_update_present_flags(ns->netstack_ipsec); } @@ -4066,727 +4005,219 @@ ipsec_polhead_split(ipsec_policy_head_t *php, netstack_t *ns) * * NOTE2: This function is called by cleartext cases, so it needs to be * in IP proper. + * + * Note: the caller has moved other parts of ira into ixa already. */ boolean_t -ipsec_in_to_out(mblk_t *ipsec_mp, ipha_t *ipha, ip6_t *ip6h, zoneid_t zoneid) -{ - ipsec_in_t *ii; - ipsec_out_t *io; - boolean_t v4; - mblk_t *mp; - boolean_t secure; - uint_t ifindex; +ipsec_in_to_out(ip_recv_attr_t *ira, ip_xmit_attr_t *ixa, mblk_t *data_mp, + ipha_t *ipha, ip6_t *ip6h) +{ ipsec_selector_t sel; - ipsec_action_t *reflect_action = NULL; - netstack_t *ns; - - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); + ipsec_action_t *reflect_action = NULL; + netstack_t *ns = ixa->ixa_ipst->ips_netstack; bzero((void*)&sel, sizeof (sel)); - ii = (ipsec_in_t *)ipsec_mp->b_rptr; - - mp = ipsec_mp->b_cont; - ASSERT(mp != NULL); - - if (ii->ipsec_in_action != NULL) { + if (ira->ira_ipsec_action != NULL) { /* transfer reference.. */ - reflect_action = ii->ipsec_in_action; - ii->ipsec_in_action = NULL; - } else if (!ii->ipsec_in_loopback) - reflect_action = ipsec_in_to_out_action(ii); - secure = ii->ipsec_in_secure; - ifindex = ii->ipsec_in_ill_index; - ns = ii->ipsec_in_ns; - v4 = ii->ipsec_in_v4; - - ipsec_in_release_refs(ii); /* No netstack_rele/hold needed */ - - /* - * Use the global zone's id if we don't have a specific zone - * identified. This is likely to happen when the received packet's - * destination is a Trusted Extensions all-zones address. We did - * not copy the zoneid from ii->ipsec_in_zone id because that - * information represents the zoneid we started input processing - * with. The caller should have a better idea of which zone the - * received packet was destined for. - */ - - if (zoneid == ALL_ZONES) - zoneid = GLOBAL_ZONEID; + reflect_action = ira->ira_ipsec_action; + ira->ira_ipsec_action = NULL; + } else if (!(ira->ira_flags & IRAF_LOOPBACK)) + reflect_action = ipsec_in_to_out_action(ira); /* * The caller is going to send the datagram out which might - * go on the wire or delivered locally through ip_wput_local. + * go on the wire or delivered locally through ire_send_local. * * 1) If it goes out on the wire, new associations will be * obtained. - * 2) If it is delivered locally, ip_wput_local will convert - * this IPSEC_OUT to a IPSEC_IN looking at the requests. + * 2) If it is delivered locally, ire_send_local will convert + * this ip_xmit_attr_t back to a ip_recv_attr_t looking at the + * requests. */ + ixa->ixa_ipsec_action = reflect_action; - io = (ipsec_out_t *)ipsec_mp->b_rptr; - bzero(io, sizeof (ipsec_out_t)); - io->ipsec_out_type = IPSEC_OUT; - io->ipsec_out_len = sizeof (ipsec_out_t); - io->ipsec_out_frtn.free_func = ipsec_out_free; - io->ipsec_out_frtn.free_arg = (char *)io; - io->ipsec_out_act = reflect_action; - - if (!ipsec_init_outbound_ports(&sel, mp, ipha, ip6h, 0, - ns->netstack_ipsec)) + if (!ipsec_init_outbound_ports(&sel, data_mp, ipha, ip6h, 0, + ns->netstack_ipsec)) { + /* Note: data_mp already consumed and ip_drop_packet done */ return (B_FALSE); - - io->ipsec_out_src_port = sel.ips_local_port; - io->ipsec_out_dst_port = sel.ips_remote_port; - io->ipsec_out_proto = sel.ips_protocol; - io->ipsec_out_icmp_type = sel.ips_icmp_type; - io->ipsec_out_icmp_code = sel.ips_icmp_code; + } + ixa->ixa_ipsec_src_port = sel.ips_local_port; + ixa->ixa_ipsec_dst_port = sel.ips_remote_port; + ixa->ixa_ipsec_proto = sel.ips_protocol; + ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; + ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; /* * Don't use global policy for this, as we want * to use the same protection that was applied to the inbound packet. + * Thus we set IXAF_NO_IPSEC is it arrived in the clear to make + * it be sent in the clear. */ - io->ipsec_out_use_global_policy = B_FALSE; - io->ipsec_out_proc_begin = B_FALSE; - io->ipsec_out_secure = secure; - io->ipsec_out_v4 = v4; - io->ipsec_out_ill_index = ifindex; - io->ipsec_out_zoneid = zoneid; - io->ipsec_out_ns = ns; /* No netstack_hold */ + if (ira->ira_flags & IRAF_IPSEC_SECURE) + ixa->ixa_flags |= IXAF_IPSEC_SECURE; + else + ixa->ixa_flags |= IXAF_NO_IPSEC; return (B_TRUE); } -mblk_t * -ipsec_in_tag(mblk_t *mp, mblk_t *cont, netstack_t *ns) -{ - ipsec_in_t *ii = (ipsec_in_t *)mp->b_rptr; - ipsec_in_t *nii; - mblk_t *nmp; - frtn_t nfrtn; - ipsec_stack_t *ipss = ns->netstack_ipsec; - - ASSERT(ii->ipsec_in_type == IPSEC_IN); - ASSERT(ii->ipsec_in_len == sizeof (ipsec_in_t)); - - nmp = ipsec_in_alloc(ii->ipsec_in_v4, ns); - if (nmp == NULL) { - ip_drop_packet_chain(cont, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - return (NULL); - } - - ASSERT(nmp->b_datap->db_type == M_CTL); - ASSERT(nmp->b_wptr == (nmp->b_rptr + sizeof (ipsec_info_t))); - - /* - * Bump refcounts. - */ - if (ii->ipsec_in_ah_sa != NULL) - IPSA_REFHOLD(ii->ipsec_in_ah_sa); - if (ii->ipsec_in_esp_sa != NULL) - IPSA_REFHOLD(ii->ipsec_in_esp_sa); - if (ii->ipsec_in_policy != NULL) - IPPH_REFHOLD(ii->ipsec_in_policy); - - /* - * Copy everything, but preserve the free routine provided by - * ipsec_in_alloc(). - */ - nii = (ipsec_in_t *)nmp->b_rptr; - nfrtn = nii->ipsec_in_frtn; - bcopy(ii, nii, sizeof (*ii)); - nii->ipsec_in_frtn = nfrtn; - - nmp->b_cont = cont; - - return (nmp); -} - -mblk_t * -ipsec_out_tag(mblk_t *mp, mblk_t *cont, netstack_t *ns) -{ - ipsec_out_t *io = (ipsec_out_t *)mp->b_rptr; - ipsec_out_t *nio; - mblk_t *nmp; - frtn_t nfrtn; - ipsec_stack_t *ipss = ns->netstack_ipsec; - - ASSERT(io->ipsec_out_type == IPSEC_OUT); - ASSERT(io->ipsec_out_len == sizeof (ipsec_out_t)); - - nmp = ipsec_alloc_ipsec_out(ns); - if (nmp == NULL) { - ip_drop_packet_chain(cont, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - return (NULL); - } - ASSERT(nmp->b_datap->db_type == M_CTL); - ASSERT(nmp->b_wptr == (nmp->b_rptr + sizeof (ipsec_info_t))); - - /* - * Bump refcounts. - */ - if (io->ipsec_out_ah_sa != NULL) - IPSA_REFHOLD(io->ipsec_out_ah_sa); - if (io->ipsec_out_esp_sa != NULL) - IPSA_REFHOLD(io->ipsec_out_esp_sa); - if (io->ipsec_out_polhead != NULL) - IPPH_REFHOLD(io->ipsec_out_polhead); - if (io->ipsec_out_policy != NULL) - IPPOL_REFHOLD(io->ipsec_out_policy); - if (io->ipsec_out_act != NULL) - IPACT_REFHOLD(io->ipsec_out_act); - if (io->ipsec_out_latch != NULL) - IPLATCH_REFHOLD(io->ipsec_out_latch); - if (io->ipsec_out_cred != NULL) - crhold(io->ipsec_out_cred); - - /* - * Copy everything, but preserve the free routine provided by - * ipsec_alloc_ipsec_out(). - */ - nio = (ipsec_out_t *)nmp->b_rptr; - nfrtn = nio->ipsec_out_frtn; - bcopy(io, nio, sizeof (*io)); - nio->ipsec_out_frtn = nfrtn; - - nmp->b_cont = cont; - - return (nmp); -} - -static void -ipsec_out_release_refs(ipsec_out_t *io) +void +ipsec_out_release_refs(ip_xmit_attr_t *ixa) { - netstack_t *ns = io->ipsec_out_ns; - - ASSERT(io->ipsec_out_type == IPSEC_OUT); - ASSERT(io->ipsec_out_len == sizeof (ipsec_out_t)); - ASSERT(io->ipsec_out_ns != NULL); + if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) + return; - /* Note: IPSA_REFRELE is multi-line macro */ - if (io->ipsec_out_ah_sa != NULL) - IPSA_REFRELE(io->ipsec_out_ah_sa); - if (io->ipsec_out_esp_sa != NULL) - IPSA_REFRELE(io->ipsec_out_esp_sa); - if (io->ipsec_out_polhead != NULL) - IPPH_REFRELE(io->ipsec_out_polhead, ns); - if (io->ipsec_out_policy != NULL) - IPPOL_REFRELE(io->ipsec_out_policy, ns); - if (io->ipsec_out_act != NULL) - IPACT_REFRELE(io->ipsec_out_act); - if (io->ipsec_out_cred != NULL) { - crfree(io->ipsec_out_cred); - io->ipsec_out_cred = NULL; + if (ixa->ixa_ipsec_ah_sa != NULL) { + IPSA_REFRELE(ixa->ixa_ipsec_ah_sa); + ixa->ixa_ipsec_ah_sa = NULL; } - if (io->ipsec_out_latch) { - IPLATCH_REFRELE(io->ipsec_out_latch, ns); - io->ipsec_out_latch = NULL; + if (ixa->ixa_ipsec_esp_sa != NULL) { + IPSA_REFRELE(ixa->ixa_ipsec_esp_sa); + ixa->ixa_ipsec_esp_sa = NULL; } -} - -static void -ipsec_out_free(void *arg) -{ - ipsec_out_t *io = (ipsec_out_t *)arg; - ipsec_out_release_refs(io); - kmem_cache_free(ipsec_info_cache, arg); -} - -static void -ipsec_in_release_refs(ipsec_in_t *ii) -{ - netstack_t *ns = ii->ipsec_in_ns; - - ASSERT(ii->ipsec_in_ns != NULL); - - /* Note: IPSA_REFRELE is multi-line macro */ - if (ii->ipsec_in_ah_sa != NULL) - IPSA_REFRELE(ii->ipsec_in_ah_sa); - if (ii->ipsec_in_esp_sa != NULL) - IPSA_REFRELE(ii->ipsec_in_esp_sa); - if (ii->ipsec_in_policy != NULL) - IPPH_REFRELE(ii->ipsec_in_policy, ns); - if (ii->ipsec_in_da != NULL) { - freeb(ii->ipsec_in_da); - ii->ipsec_in_da = NULL; + if (ixa->ixa_ipsec_policy != NULL) { + IPPOL_REFRELE(ixa->ixa_ipsec_policy); + ixa->ixa_ipsec_policy = NULL; } -} - -static void -ipsec_in_free(void *arg) -{ - ipsec_in_t *ii = (ipsec_in_t *)arg; - ipsec_in_release_refs(ii); - kmem_cache_free(ipsec_info_cache, arg); -} - -/* - * This is called only for outbound datagrams if the datagram needs to - * go out secure. A NULL mp can be passed to get an ipsec_out. This - * facility is used by ip_unbind. - * - * NOTE : o As the data part could be modified by ipsec_out_process etc. - * we can't make it fast by calling a dup. - */ -mblk_t * -ipsec_alloc_ipsec_out(netstack_t *ns) -{ - mblk_t *ipsec_mp; - ipsec_out_t *io = kmem_cache_alloc(ipsec_info_cache, KM_NOSLEEP); - - if (io == NULL) - return (NULL); - - bzero(io, sizeof (ipsec_out_t)); - - io->ipsec_out_type = IPSEC_OUT; - io->ipsec_out_len = sizeof (ipsec_out_t); - io->ipsec_out_frtn.free_func = ipsec_out_free; - io->ipsec_out_frtn.free_arg = (char *)io; - - /* - * Set the zoneid to ALL_ZONES which is used as an invalid value. Code - * using ipsec_out_zoneid should assert that the zoneid has been set to - * a sane value. - */ - io->ipsec_out_zoneid = ALL_ZONES; - io->ipsec_out_ns = ns; /* No netstack_hold */ - - ipsec_mp = desballoc((uint8_t *)io, sizeof (ipsec_info_t), BPRI_HI, - &io->ipsec_out_frtn); - if (ipsec_mp == NULL) { - ipsec_out_free(io); - - return (NULL); + if (ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(ixa->ixa_ipsec_action); + ixa->ixa_ipsec_action = NULL; } - ipsec_mp->b_datap->db_type = M_CTL; - ipsec_mp->b_wptr = ipsec_mp->b_rptr + sizeof (ipsec_info_t); - - return (ipsec_mp); -} - -/* - * Attach an IPSEC_OUT; use pol for policy if it is non-null. - * Otherwise initialize using conn. - * - * If pol is non-null, we consume a reference to it. - */ -mblk_t * -ipsec_attach_ipsec_out(mblk_t **mp, conn_t *connp, ipsec_policy_t *pol, - uint8_t proto, netstack_t *ns) -{ - mblk_t *ipsec_mp; - ipsec_stack_t *ipss = ns->netstack_ipsec; - - ASSERT((pol != NULL) || (connp != NULL)); - - ipsec_mp = ipsec_alloc_ipsec_out(ns); - if (ipsec_mp == NULL) { - ipsec_rl_strlog(ns, IP_MOD_ID, 0, 0, SL_ERROR|SL_NOTE, - "ipsec_attach_ipsec_out: Allocation failure\n"); - ip_drop_packet(*mp, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - *mp = NULL; - return (NULL); + if (ixa->ixa_ipsec_latch) { + IPLATCH_REFRELE(ixa->ixa_ipsec_latch); + ixa->ixa_ipsec_latch = NULL; } - ipsec_mp->b_cont = *mp; - /* - * If *mp is NULL, ipsec_init_ipsec_out() won't/should not be using it. - */ - return (ipsec_init_ipsec_out(ipsec_mp, mp, connp, pol, proto, ns)); + /* Clear the soft references to the SAs */ + ixa->ixa_ipsec_ref[0].ipsr_sa = NULL; + ixa->ixa_ipsec_ref[0].ipsr_bucket = NULL; + ixa->ixa_ipsec_ref[0].ipsr_gen = 0; + ixa->ixa_ipsec_ref[1].ipsr_sa = NULL; + ixa->ixa_ipsec_ref[1].ipsr_bucket = NULL; + ixa->ixa_ipsec_ref[1].ipsr_gen = 0; + ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; } -/* - * Initialize the IPSEC_OUT (ipsec_mp) using pol if it is non-null. - * Otherwise initialize using conn. - * - * If pol is non-null, we consume a reference to it. - */ -mblk_t * -ipsec_init_ipsec_out(mblk_t *ipsec_mp, mblk_t **mp, conn_t *connp, - ipsec_policy_t *pol, uint8_t proto, netstack_t *ns) +void +ipsec_in_release_refs(ip_recv_attr_t *ira) { - ipsec_out_t *io; - ipsec_policy_t *p; - ipha_t *ipha; - ip6_t *ip6h; - ipsec_stack_t *ipss = ns->netstack_ipsec; - - ASSERT(ipsec_mp->b_cont == *mp); - - ASSERT((pol != NULL) || (connp != NULL)); - - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); - ASSERT(ipsec_mp->b_wptr == (ipsec_mp->b_rptr + sizeof (ipsec_info_t))); - io = (ipsec_out_t *)ipsec_mp->b_rptr; - ASSERT(io->ipsec_out_type == IPSEC_OUT); - ASSERT(io->ipsec_out_len == sizeof (ipsec_out_t)); - io->ipsec_out_latch = NULL; - /* - * Set the zoneid when we have the connp. - * Otherwise, we're called from ip_wput_attach_policy() who will take - * care of setting the zoneid. - */ - if (connp != NULL) - io->ipsec_out_zoneid = connp->conn_zoneid; - - io->ipsec_out_ns = ns; /* No netstack_hold */ - - if (*mp != NULL) { - ipha = (ipha_t *)(*mp)->b_rptr; - if (IPH_HDR_VERSION(ipha) == IP_VERSION) { - io->ipsec_out_v4 = B_TRUE; - ip6h = NULL; - } else { - io->ipsec_out_v4 = B_FALSE; - ip6h = (ip6_t *)ipha; - ipha = NULL; - } - } else { - ASSERT(connp != NULL && connp->conn_policy_cached); - ip6h = NULL; - ipha = NULL; - io->ipsec_out_v4 = !connp->conn_pkt_isv6; - } - - p = NULL; - - /* - * Take latched policies over global policy. Check here again for - * this, in case we had conn_latch set while the packet was flying - * around in IP. - */ - if (connp != NULL && connp->conn_latch != NULL) { - ASSERT(ns == connp->conn_netstack); - p = connp->conn_latch->ipl_out_policy; - io->ipsec_out_latch = connp->conn_latch; - IPLATCH_REFHOLD(connp->conn_latch); - if (p != NULL) { - IPPOL_REFHOLD(p); - } - io->ipsec_out_src_port = connp->conn_lport; - io->ipsec_out_dst_port = connp->conn_fport; - io->ipsec_out_icmp_type = io->ipsec_out_icmp_code = 0; - if (pol != NULL) - IPPOL_REFRELE(pol, ns); - } else if (pol != NULL) { - ipsec_selector_t sel; - - bzero((void*)&sel, sizeof (sel)); - - p = pol; - /* - * conn does not have the port information. Get - * it from the packet. - */ + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) + return; - if (!ipsec_init_outbound_ports(&sel, *mp, ipha, ip6h, 0, - ns->netstack_ipsec)) { - /* Callee did ip_drop_packet() on *mp. */ - *mp = NULL; - freeb(ipsec_mp); - return (NULL); - } - io->ipsec_out_src_port = sel.ips_local_port; - io->ipsec_out_dst_port = sel.ips_remote_port; - io->ipsec_out_icmp_type = sel.ips_icmp_type; - io->ipsec_out_icmp_code = sel.ips_icmp_code; + if (ira->ira_ipsec_ah_sa != NULL) { + IPSA_REFRELE(ira->ira_ipsec_ah_sa); + ira->ira_ipsec_ah_sa = NULL; } - - io->ipsec_out_proto = proto; - io->ipsec_out_use_global_policy = B_TRUE; - io->ipsec_out_secure = (p != NULL); - io->ipsec_out_policy = p; - - if (p == NULL) { - if (connp->conn_policy != NULL) { - io->ipsec_out_secure = B_TRUE; - ASSERT(io->ipsec_out_latch == NULL); - ASSERT(io->ipsec_out_use_global_policy == B_TRUE); - io->ipsec_out_need_policy = B_TRUE; - ASSERT(io->ipsec_out_polhead == NULL); - IPPH_REFHOLD(connp->conn_policy); - io->ipsec_out_polhead = connp->conn_policy; - } - } else { - /* Handle explicit drop action. */ - if (p->ipsp_act->ipa_act.ipa_type == IPSEC_ACT_DISCARD || - p->ipsp_act->ipa_act.ipa_type == IPSEC_ACT_REJECT) { - ip_drop_packet(ipsec_mp, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_explicit), - &ipss->ipsec_spd_dropper); - *mp = NULL; - ipsec_mp = NULL; - } + if (ira->ira_ipsec_esp_sa != NULL) { + IPSA_REFRELE(ira->ira_ipsec_esp_sa); + ira->ira_ipsec_esp_sa = NULL; } - - return (ipsec_mp); + ira->ira_flags &= ~IRAF_IPSEC_SECURE; } /* - * Allocate an IPSEC_IN mblk. This will be prepended to an inbound datagram - * and keep track of what-if-any IPsec processing will be applied to the - * datagram. - */ -mblk_t * -ipsec_in_alloc(boolean_t isv4, netstack_t *ns) -{ - mblk_t *ipsec_in; - ipsec_in_t *ii = kmem_cache_alloc(ipsec_info_cache, KM_NOSLEEP); - - if (ii == NULL) - return (NULL); - - bzero(ii, sizeof (ipsec_info_t)); - ii->ipsec_in_type = IPSEC_IN; - ii->ipsec_in_len = sizeof (ipsec_in_t); - - ii->ipsec_in_v4 = isv4; - ii->ipsec_in_secure = B_TRUE; - ii->ipsec_in_ns = ns; /* No netstack_hold */ - ii->ipsec_in_stackid = ns->netstack_stackid; - - ii->ipsec_in_frtn.free_func = ipsec_in_free; - ii->ipsec_in_frtn.free_arg = (char *)ii; - - ii->ipsec_in_zoneid = ALL_ZONES; /* default for received packets */ - - ipsec_in = desballoc((uint8_t *)ii, sizeof (ipsec_info_t), BPRI_HI, - &ii->ipsec_in_frtn); - if (ipsec_in == NULL) { - ip1dbg(("ipsec_in_alloc: IPSEC_IN allocation failure.\n")); - ipsec_in_free(ii); - return (NULL); - } - - ipsec_in->b_datap->db_type = M_CTL; - ipsec_in->b_wptr += sizeof (ipsec_info_t); - - return (ipsec_in); -} - -/* - * This is called from ip_wput_local when a packet which needs - * security is looped back, to convert the IPSEC_OUT to a IPSEC_IN - * before fanout, where the policy check happens. In most of the - * cases, IPSEC processing has *never* been done. There is one case - * (ip_wput_ire_fragmentit -> ip_wput_frag -> icmp_frag_needed) where - * the packet is destined for localhost, IPSEC processing has already - * been done. + * This is called from ire_send_local when a packet + * is looped back. We setup the ip_recv_attr_t "borrowing" the references + * held by the callers. + * Note that we don't do any IPsec but we carry the actions and IPSEC flags + * across so that the fanout policy checks see that IPsec was applied. * - * Future: This could happen after SA selection has occurred for - * outbound.. which will tell us who the src and dst identities are.. - * Then it's just a matter of splicing the ah/esp SA pointers from the - * ipsec_out_t to the ipsec_in_t. + * The caller should do ipsec_in_release_refs() on the ira by calling + * ira_cleanup(). */ void -ipsec_out_to_in(mblk_t *ipsec_mp) +ipsec_out_to_in(ip_xmit_attr_t *ixa, ill_t *ill, ip_recv_attr_t *ira) { - ipsec_in_t *ii; - ipsec_out_t *io; ipsec_policy_t *pol; ipsec_action_t *act; - boolean_t v4, icmp_loopback; - zoneid_t zoneid; - netstack_t *ns; - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); + /* Non-IPsec operations */ + ira->ira_free_flags = 0; + ira->ira_zoneid = ixa->ixa_zoneid; + ira->ira_cred = ixa->ixa_cred; + ira->ira_cpid = ixa->ixa_cpid; + ira->ira_tsl = ixa->ixa_tsl; + ira->ira_ill = ira->ira_rill = ill; + ira->ira_flags = ixa->ixa_flags & IAF_MASK; + ira->ira_no_loop_zoneid = ixa->ixa_no_loop_zoneid; + ira->ira_pktlen = ixa->ixa_pktlen; + ira->ira_ip_hdr_length = ixa->ixa_ip_hdr_length; + ira->ira_protocol = ixa->ixa_protocol; + ira->ira_mhip = NULL; + + ira->ira_flags |= IRAF_LOOPBACK | IRAF_L2SRC_LOOPBACK; + + ira->ira_sqp = ixa->ixa_sqp; + ira->ira_ring = NULL; + + ira->ira_ruifindex = ill->ill_phyint->phyint_ifindex; + ira->ira_rifindex = ira->ira_ruifindex; + + if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) + return; - io = (ipsec_out_t *)ipsec_mp->b_rptr; + ira->ira_flags |= IRAF_IPSEC_SECURE; - v4 = io->ipsec_out_v4; - zoneid = io->ipsec_out_zoneid; - icmp_loopback = io->ipsec_out_icmp_loopback; - ns = io->ipsec_out_ns; + ira->ira_ipsec_ah_sa = NULL; + ira->ira_ipsec_esp_sa = NULL; - act = io->ipsec_out_act; + act = ixa->ixa_ipsec_action; if (act == NULL) { - pol = io->ipsec_out_policy; + pol = ixa->ixa_ipsec_policy; if (pol != NULL) { act = pol->ipsp_act; IPACT_REFHOLD(act); } } - io->ipsec_out_act = NULL; - - ipsec_out_release_refs(io); /* No netstack_rele/hold needed */ - - ii = (ipsec_in_t *)ipsec_mp->b_rptr; - bzero(ii, sizeof (ipsec_in_t)); - ii->ipsec_in_type = IPSEC_IN; - ii->ipsec_in_len = sizeof (ipsec_in_t); - ii->ipsec_in_loopback = B_TRUE; - ii->ipsec_in_ns = ns; /* No netstack_hold */ - - ii->ipsec_in_frtn.free_func = ipsec_in_free; - ii->ipsec_in_frtn.free_arg = (char *)ii; - ii->ipsec_in_action = act; - ii->ipsec_in_zoneid = zoneid; - - /* - * In most of the cases, we can't look at the ipsec_out_XXX_sa - * because this never went through IPSEC processing. So, look at - * the requests and infer whether it would have gone through - * IPSEC processing or not. Initialize the "done" fields with - * the requests. The possible values for "done" fields are : - * - * 1) zero, indicates that a particular preference was never - * requested. - * 2) non-zero, indicates that it could be IPSEC_PREF_REQUIRED/ - * IPSEC_PREF_NEVER. If IPSEC_REQ_DONE is set, it means that - * IPSEC processing has been completed. - */ - ii->ipsec_in_secure = B_TRUE; - ii->ipsec_in_v4 = v4; - ii->ipsec_in_icmp_loopback = icmp_loopback; + ixa->ixa_ipsec_action = NULL; + ira->ira_ipsec_action = act; } /* - * Consults global policy to see whether this datagram should - * go out secure. If so it attaches a ipsec_mp in front and - * returns. + * Consults global policy and per-socket policy to see whether this datagram + * should go out secure. If so it updates the ip_xmit_attr_t + * Should not be used when connecting, since then we want to latch the policy. + * + * If connp is NULL we just look at the global policy. + * + * Returns NULL if the packet was dropped, in which case the MIB has + * been incremented and ip_drop_packet done. */ mblk_t * -ip_wput_attach_policy(mblk_t *ipsec_mp, ipha_t *ipha, ip6_t *ip6h, ire_t *ire, - conn_t *connp, boolean_t unspec_src, zoneid_t zoneid) +ip_output_attach_policy(mblk_t *mp, ipha_t *ipha, ip6_t *ip6h, + const conn_t *connp, ip_xmit_attr_t *ixa) { - mblk_t *mp; - ipsec_out_t *io = NULL; ipsec_selector_t sel; - uint_t ill_index; - boolean_t conn_dontroutex; - boolean_t conn_multicast_loopx; - boolean_t policy_present; - ip_stack_t *ipst = ire->ire_ipst; + boolean_t policy_present; + ip_stack_t *ipst = ixa->ixa_ipst; netstack_t *ns = ipst->ips_netstack; ipsec_stack_t *ipss = ns->netstack_ipsec; + ipsec_policy_t *p; + ixa->ixa_ipsec_policy_gen = ipss->ipsec_system_policy.iph_gen; ASSERT((ipha != NULL && ip6h == NULL) || (ip6h != NULL && ipha == NULL)); - bzero((void*)&sel, sizeof (sel)); - if (ipha != NULL) policy_present = ipss->ipsec_outbound_v4_policy_present; else policy_present = ipss->ipsec_outbound_v6_policy_present; - /* - * Fast Path to see if there is any policy. - */ - if (!policy_present) { - if (ipsec_mp->b_datap->db_type == M_CTL) { - io = (ipsec_out_t *)ipsec_mp->b_rptr; - if (!io->ipsec_out_secure) { - /* - * If there is no global policy and ip_wput - * or ip_wput_multicast has attached this mp - * for multicast case, free the ipsec_mp and - * return the original mp. - */ - mp = ipsec_mp->b_cont; - freeb(ipsec_mp); - ipsec_mp = mp; - io = NULL; - } - ASSERT(io == NULL || !io->ipsec_out_tunnel); - } - if (((io == NULL) || (io->ipsec_out_polhead == NULL)) && - ((connp == NULL) || (connp->conn_policy == NULL))) - return (ipsec_mp); - } - ill_index = 0; - conn_multicast_loopx = conn_dontroutex = B_FALSE; - mp = ipsec_mp; - if (ipsec_mp->b_datap->db_type == M_CTL) { - mp = ipsec_mp->b_cont; - /* - * This is a connection where we have some per-socket - * policy or ip_wput has attached an ipsec_mp for - * the multicast datagram. - */ - io = (ipsec_out_t *)ipsec_mp->b_rptr; - if (!io->ipsec_out_secure) { - /* - * This ipsec_mp was allocated in ip_wput or - * ip_wput_multicast so that we will know the - * value of ill_index, conn_dontroute, - * conn_multicast_loop in the multicast case if - * we inherit global policy here. - */ - ill_index = io->ipsec_out_ill_index; - conn_dontroutex = io->ipsec_out_dontroute; - conn_multicast_loopx = io->ipsec_out_multicast_loop; - freeb(ipsec_mp); - ipsec_mp = mp; - io = NULL; - } - ASSERT(io == NULL || !io->ipsec_out_tunnel); - } + if (!policy_present && (connp == NULL || connp->conn_policy == NULL)) + return (mp); + + bzero((void*)&sel, sizeof (sel)); if (ipha != NULL) { - sel.ips_local_addr_v4 = (ipha->ipha_src != 0 ? - ipha->ipha_src : ire->ire_src_addr); + sel.ips_local_addr_v4 = ipha->ipha_src; sel.ips_remote_addr_v4 = ip_get_dst(ipha); - sel.ips_protocol = (uint8_t)ipha->ipha_protocol; sel.ips_isv4 = B_TRUE; } else { - ushort_t hdr_len; - uint8_t *nexthdrp; - boolean_t is_fragment; - sel.ips_isv4 = B_FALSE; - if (IN6_IS_ADDR_UNSPECIFIED(&ip6h->ip6_src)) { - if (!unspec_src) - sel.ips_local_addr_v6 = ire->ire_src_addr_v6; - } else { - sel.ips_local_addr_v6 = ip6h->ip6_src; - } - - sel.ips_remote_addr_v6 = ip_get_dst_v6(ip6h, mp, &is_fragment); - if (is_fragment) { - /* - * It's a packet fragment for a packet that - * we have already processed (since IPsec processing - * is done before fragmentation), so we don't - * have to do policy checks again. Fragments can - * come back to us for processing if they have - * been queued up due to flow control. - */ - if (ipsec_mp->b_datap->db_type == M_CTL) { - mp = ipsec_mp->b_cont; - freeb(ipsec_mp); - ipsec_mp = mp; - } - return (ipsec_mp); - } - - /* IPv6 common-case. */ - sel.ips_protocol = ip6h->ip6_nxt; - switch (ip6h->ip6_nxt) { - case IPPROTO_TCP: - case IPPROTO_UDP: - case IPPROTO_SCTP: - case IPPROTO_ICMPV6: - break; - default: - if (!ip_hdr_length_nexthdr_v6(mp, ip6h, - &hdr_len, &nexthdrp)) { - BUMP_MIB(&ipst->ips_ip6_mib, - ipIfStatsOutDiscards); - freemsg(ipsec_mp); /* Not IPsec-related drop. */ - return (NULL); - } - sel.ips_protocol = *nexthdrp; - break; - } + sel.ips_local_addr_v6 = ip6h->ip6_src; + sel.ips_remote_addr_v6 = ip_get_dst_v6(ip6h, mp, NULL); } + sel.ips_protocol = ixa->ixa_protocol; if (!ipsec_init_outbound_ports(&sel, mp, ipha, ip6h, 0, ipss)) { if (ipha != NULL) { @@ -4794,65 +4225,36 @@ ip_wput_attach_policy(mblk_t *ipsec_mp, ipha_t *ipha, ip6_t *ip6h, ire_t *ire, } else { BUMP_MIB(&ipst->ips_ip6_mib, ipIfStatsOutDiscards); } - - /* Callee dropped the packet. */ + /* Note: mp already consumed and ip_drop_packet done */ return (NULL); } - if (io != NULL) { - /* - * We seem to have some local policy (we already have - * an ipsec_out). Look at global policy and see - * whether we have to inherit or not. - */ - io->ipsec_out_need_policy = B_FALSE; - ipsec_mp = ipsec_apply_global_policy(ipsec_mp, connp, - &sel, ns); - ASSERT((io->ipsec_out_policy != NULL) || - (io->ipsec_out_act != NULL)); - ASSERT(io->ipsec_out_need_policy == B_FALSE); - return (ipsec_mp); + ASSERT(ixa->ixa_ipsec_policy == NULL); + p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); + ixa->ixa_ipsec_policy = p; + if (p != NULL) { + ixa->ixa_flags |= IXAF_IPSEC_SECURE; + if (connp == NULL || connp->conn_policy == NULL) + ixa->ixa_flags |= IXAF_IPSEC_GLOBAL_POLICY; + } else { + ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; } - /* - * We pass in a pointer to a pointer because mp can become - * NULL due to allocation failures or explicit drops. Callers - * of this function should assume a NULL mp means the packet - * was dropped. - */ - ipsec_mp = ipsec_attach_global_policy(&mp, connp, &sel, ns); - if (ipsec_mp == NULL) - return (mp); /* * Copy the right port information. */ - ASSERT(ipsec_mp->b_datap->db_type == M_CTL); - io = (ipsec_out_t *)ipsec_mp->b_rptr; - - ASSERT(io->ipsec_out_need_policy == B_FALSE); - ASSERT((io->ipsec_out_policy != NULL) || - (io->ipsec_out_act != NULL)); - io->ipsec_out_src_port = sel.ips_local_port; - io->ipsec_out_dst_port = sel.ips_remote_port; - io->ipsec_out_icmp_type = sel.ips_icmp_type; - io->ipsec_out_icmp_code = sel.ips_icmp_code; - /* - * Set ill_index, conn_dontroute and conn_multicast_loop - * for multicast datagrams. - */ - io->ipsec_out_ill_index = ill_index; - io->ipsec_out_dontroute = conn_dontroutex; - io->ipsec_out_multicast_loop = conn_multicast_loopx; - - if (zoneid == ALL_ZONES) - zoneid = GLOBAL_ZONEID; - io->ipsec_out_zoneid = zoneid; - return (ipsec_mp); + ixa->ixa_ipsec_src_port = sel.ips_local_port; + ixa->ixa_ipsec_dst_port = sel.ips_remote_port; + ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; + ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; + ixa->ixa_ipsec_proto = sel.ips_protocol; + return (mp); } /* * When appropriate, this function caches inbound and outbound policy - * for this connection. + * for this connection. The outbound policy is stored in conn_ixa. + * Note that it can not be used for SCTP since conn_faddr isn't set for SCTP. * * XXX need to work out more details about per-interface policy and * caching here! @@ -4866,20 +4268,38 @@ ipsec_conn_cache_policy(conn_t *connp, boolean_t isv4) netstack_t *ns = connp->conn_netstack; ipsec_stack_t *ipss = ns->netstack_ipsec; + connp->conn_ixa->ixa_ipsec_policy_gen = + ipss->ipsec_system_policy.iph_gen; /* * There is no policy latching for ICMP sockets because we can't * decide on which policy to use until we see the packet and get * type/code selectors. */ - if (connp->conn_ulp == IPPROTO_ICMP || - connp->conn_ulp == IPPROTO_ICMPV6) { + if (connp->conn_proto == IPPROTO_ICMP || + connp->conn_proto == IPPROTO_ICMPV6) { connp->conn_in_enforce_policy = connp->conn_out_enforce_policy = B_TRUE; if (connp->conn_latch != NULL) { - IPLATCH_REFRELE(connp->conn_latch, ns); + IPLATCH_REFRELE(connp->conn_latch); connp->conn_latch = NULL; } - connp->conn_flags |= IPCL_CHECK_POLICY; + if (connp->conn_latch_in_policy != NULL) { + IPPOL_REFRELE(connp->conn_latch_in_policy); + connp->conn_latch_in_policy = NULL; + } + if (connp->conn_latch_in_action != NULL) { + IPACT_REFRELE(connp->conn_latch_in_action); + connp->conn_latch_in_action = NULL; + } + if (connp->conn_ixa->ixa_ipsec_policy != NULL) { + IPPOL_REFRELE(connp->conn_ixa->ixa_ipsec_policy); + connp->conn_ixa->ixa_ipsec_policy = NULL; + } + if (connp->conn_ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(connp->conn_ixa->ixa_ipsec_action); + connp->conn_ixa->ixa_ipsec_action = NULL; + } + connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; return (0); } @@ -4898,38 +4318,57 @@ ipsec_conn_cache_policy(conn_t *connp, boolean_t isv4) return (ENOMEM); } - sel.ips_protocol = connp->conn_ulp; + bzero((void*)&sel, sizeof (sel)); + + sel.ips_protocol = connp->conn_proto; sel.ips_local_port = connp->conn_lport; sel.ips_remote_port = connp->conn_fport; sel.ips_is_icmp_inv_acq = 0; sel.ips_isv4 = isv4; if (isv4) { - sel.ips_local_addr_v4 = connp->conn_src; - sel.ips_remote_addr_v4 = connp->conn_rem; + sel.ips_local_addr_v4 = connp->conn_laddr_v4; + sel.ips_remote_addr_v4 = connp->conn_faddr_v4; } else { - sel.ips_local_addr_v6 = connp->conn_srcv6; - sel.ips_remote_addr_v6 = connp->conn_remv6; + sel.ips_local_addr_v6 = connp->conn_laddr_v6; + sel.ips_remote_addr_v6 = connp->conn_faddr_v6; } - p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, NULL, &sel, - ns); - if (connp->conn_latch->ipl_in_policy != NULL) - IPPOL_REFRELE(connp->conn_latch->ipl_in_policy, ns); - connp->conn_latch->ipl_in_policy = p; + p = ipsec_find_policy(IPSEC_TYPE_INBOUND, connp, &sel, ns); + if (connp->conn_latch_in_policy != NULL) + IPPOL_REFRELE(connp->conn_latch_in_policy); + connp->conn_latch_in_policy = p; connp->conn_in_enforce_policy = (p != NULL); - p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, NULL, &sel, - ns); - if (connp->conn_latch->ipl_out_policy != NULL) - IPPOL_REFRELE(connp->conn_latch->ipl_out_policy, ns); - connp->conn_latch->ipl_out_policy = p; + p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); + if (connp->conn_ixa->ixa_ipsec_policy != NULL) + IPPOL_REFRELE(connp->conn_ixa->ixa_ipsec_policy); + connp->conn_ixa->ixa_ipsec_policy = p; connp->conn_out_enforce_policy = (p != NULL); - + if (p != NULL) { + connp->conn_ixa->ixa_flags |= IXAF_IPSEC_SECURE; + if (connp->conn_policy == NULL) { + connp->conn_ixa->ixa_flags |= + IXAF_IPSEC_GLOBAL_POLICY; + } + } else { + connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; + } /* Clear the latched actions too, in case we're recaching. */ - if (connp->conn_latch->ipl_out_action != NULL) - IPACT_REFRELE(connp->conn_latch->ipl_out_action); - if (connp->conn_latch->ipl_in_action != NULL) - IPACT_REFRELE(connp->conn_latch->ipl_in_action); + if (connp->conn_ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(connp->conn_ixa->ixa_ipsec_action); + connp->conn_ixa->ixa_ipsec_action = NULL; + } + if (connp->conn_latch_in_action != NULL) { + IPACT_REFRELE(connp->conn_latch_in_action); + connp->conn_latch_in_action = NULL; + } + connp->conn_ixa->ixa_ipsec_src_port = sel.ips_local_port; + connp->conn_ixa->ixa_ipsec_dst_port = sel.ips_remote_port; + connp->conn_ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; + connp->conn_ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; + connp->conn_ixa->ixa_ipsec_proto = sel.ips_protocol; + } else { + connp->conn_ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; } /* @@ -4945,28 +4384,125 @@ ipsec_conn_cache_policy(conn_t *connp, boolean_t isv4) * global policy (because conn_policy_cached is already set). */ connp->conn_policy_cached = B_TRUE; - if (connp->conn_in_enforce_policy) - connp->conn_flags |= IPCL_CHECK_POLICY; return (0); } +/* + * When appropriate, this function caches outbound policy for faddr/fport. + * It is used when we are not connected i.e., when we can not latch the + * policy. + */ void -iplatch_free(ipsec_latch_t *ipl, netstack_t *ns) -{ - if (ipl->ipl_out_policy != NULL) - IPPOL_REFRELE(ipl->ipl_out_policy, ns); - if (ipl->ipl_in_policy != NULL) - IPPOL_REFRELE(ipl->ipl_in_policy, ns); - if (ipl->ipl_in_action != NULL) - IPACT_REFRELE(ipl->ipl_in_action); - if (ipl->ipl_out_action != NULL) - IPACT_REFRELE(ipl->ipl_out_action); +ipsec_cache_outbound_policy(const conn_t *connp, const in6_addr_t *v6src, + const in6_addr_t *v6dst, in_port_t dstport, ip_xmit_attr_t *ixa) +{ + boolean_t isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0; + boolean_t global_policy_present; + netstack_t *ns = connp->conn_netstack; + ipsec_stack_t *ipss = ns->netstack_ipsec; + + ixa->ixa_ipsec_policy_gen = ipss->ipsec_system_policy.iph_gen; + + /* + * There is no policy caching for ICMP sockets because we can't + * decide on which policy to use until we see the packet and get + * type/code selectors. + */ + if (connp->conn_proto == IPPROTO_ICMP || + connp->conn_proto == IPPROTO_ICMPV6) { + ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; + if (ixa->ixa_ipsec_policy != NULL) { + IPPOL_REFRELE(ixa->ixa_ipsec_policy); + ixa->ixa_ipsec_policy = NULL; + } + if (ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(ixa->ixa_ipsec_action); + ixa->ixa_ipsec_action = NULL; + } + return; + } + + global_policy_present = isv4 ? + (ipss->ipsec_outbound_v4_policy_present || + ipss->ipsec_inbound_v4_policy_present) : + (ipss->ipsec_outbound_v6_policy_present || + ipss->ipsec_inbound_v6_policy_present); + + if ((connp->conn_policy != NULL) || global_policy_present) { + ipsec_selector_t sel; + ipsec_policy_t *p; + + bzero((void*)&sel, sizeof (sel)); + + sel.ips_protocol = connp->conn_proto; + sel.ips_local_port = connp->conn_lport; + sel.ips_remote_port = dstport; + sel.ips_is_icmp_inv_acq = 0; + sel.ips_isv4 = isv4; + if (isv4) { + IN6_V4MAPPED_TO_IPADDR(v6src, sel.ips_local_addr_v4); + IN6_V4MAPPED_TO_IPADDR(v6dst, sel.ips_remote_addr_v4); + } else { + sel.ips_local_addr_v6 = *v6src; + sel.ips_remote_addr_v6 = *v6dst; + } + + p = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, &sel, ns); + if (ixa->ixa_ipsec_policy != NULL) + IPPOL_REFRELE(ixa->ixa_ipsec_policy); + ixa->ixa_ipsec_policy = p; + if (p != NULL) { + ixa->ixa_flags |= IXAF_IPSEC_SECURE; + if (connp->conn_policy == NULL) + ixa->ixa_flags |= IXAF_IPSEC_GLOBAL_POLICY; + } else { + ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; + } + /* Clear the latched actions too, in case we're recaching. */ + if (ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(ixa->ixa_ipsec_action); + ixa->ixa_ipsec_action = NULL; + } + + ixa->ixa_ipsec_src_port = sel.ips_local_port; + ixa->ixa_ipsec_dst_port = sel.ips_remote_port; + ixa->ixa_ipsec_icmp_type = sel.ips_icmp_type; + ixa->ixa_ipsec_icmp_code = sel.ips_icmp_code; + ixa->ixa_ipsec_proto = sel.ips_protocol; + } else { + ixa->ixa_flags &= ~IXAF_IPSEC_SECURE; + if (ixa->ixa_ipsec_policy != NULL) { + IPPOL_REFRELE(ixa->ixa_ipsec_policy); + ixa->ixa_ipsec_policy = NULL; + } + if (ixa->ixa_ipsec_action != NULL) { + IPACT_REFRELE(ixa->ixa_ipsec_action); + ixa->ixa_ipsec_action = NULL; + } + } +} + +/* + * Returns B_FALSE if the policy has gone stale. + */ +boolean_t +ipsec_outbound_policy_current(ip_xmit_attr_t *ixa) +{ + ipsec_stack_t *ipss = ixa->ixa_ipst->ips_netstack->netstack_ipsec; + + if (!(ixa->ixa_flags & IXAF_IPSEC_GLOBAL_POLICY)) + return (B_TRUE); + + return (ixa->ixa_ipsec_policy_gen == ipss->ipsec_system_policy.iph_gen); +} + +void +iplatch_free(ipsec_latch_t *ipl) +{ if (ipl->ipl_local_cid != NULL) IPSID_REFRELE(ipl->ipl_local_cid); if (ipl->ipl_remote_cid != NULL) IPSID_REFRELE(ipl->ipl_remote_cid); - if (ipl->ipl_local_id != NULL) - crfree(ipl->ipl_local_id); mutex_destroy(&ipl->ipl_lock); kmem_free(ipl, sizeof (*ipl)); } @@ -5622,18 +5158,19 @@ ipsec_unregister_prov_update(void) * SAs are available. If there's no per-tunnel policy, or a match comes back * with no match, then still return the packet and have global policy take * a crack at it in IP. + * This updates the ip_xmit_attr with the IPsec policy. * * Remember -> we can be forwarding packets. Keep that in mind w.r.t. * inner-packet contents. */ mblk_t * ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, - ip6_t *inner_ipv6, ipha_t *outer_ipv4, ip6_t *outer_ipv6, int outer_hdr_len) + ip6_t *inner_ipv6, ipha_t *outer_ipv4, ip6_t *outer_ipv6, int outer_hdr_len, + ip_xmit_attr_t *ixa) { ipsec_policy_head_t *polhead; ipsec_selector_t sel; - mblk_t *ipsec_mp, *ipsec_mp_head, *nmp; - ipsec_out_t *io; + mblk_t *nmp; boolean_t is_fragment; ipsec_policy_t *pol; ipsec_tun_pol_t *itp = iptun->iptun_itp; @@ -5644,6 +5181,15 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, outer_ipv4 != NULL && outer_ipv6 == NULL); /* We take care of inners in a bit. */ + /* Are the IPsec fields initialized at all? */ + if (!(ixa->ixa_flags & IXAF_IPSEC_SECURE)) { + ASSERT(ixa->ixa_ipsec_policy == NULL); + ASSERT(ixa->ixa_ipsec_latch == NULL); + ASSERT(ixa->ixa_ipsec_action == NULL); + ASSERT(ixa->ixa_ipsec_ah_sa == NULL); + ASSERT(ixa->ixa_ipsec_esp_sa == NULL); + } + ASSERT(itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)); polhead = itp->itp_policy; @@ -5675,7 +5221,7 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, if (mp->b_cont != NULL) { nmp = msgpullup(mp, -1); if (nmp == NULL) { - ip_drop_packet(mp, B_FALSE, NULL, NULL, + ip_drop_packet(mp, B_FALSE, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); return (NULL); @@ -5734,8 +5280,8 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, ip6h = (ip6_t *)mp->b_rptr; if (!ip_hdr_length_nexthdr_v6(mp, ip6h, &ip6_hdr_length, &v6_proto_p)) { - ip_drop_packet_chain(mp, B_FALSE, - NULL, NULL, DROPPER(ipss, + ip_drop_packet_chain(mp, B_FALSE, NULL, + DROPPER(ipss, ipds_spd_malformed_packet), &ipss->ipsec_spd_dropper); return (NULL); @@ -5761,8 +5307,8 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, sel.ips_remote_addr_v6 = inner_ipv6->ip6_dst; if (!ip_hdr_length_nexthdr_v6(mp, inner_ipv6, &ip6_hdr_length, &v6_proto_p)) { - ip_drop_packet_chain(mp, B_FALSE, - NULL, NULL, DROPPER(ipss, + ip_drop_packet_chain(mp, B_FALSE, NULL, + DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); return (NULL); @@ -5802,8 +5348,7 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, /* Success so far! */ } rw_enter(&polhead->iph_lock, RW_READER); - pol = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_OUTBOUND, - &sel, ns); + pol = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_OUTBOUND, &sel); rw_exit(&polhead->iph_lock); if (pol == NULL) { /* @@ -5825,7 +5370,7 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, cmn_err(CE_WARN, "ipsec_tun_outbound(): No matching tunnel " "per-port policy\n"); #endif - ip_drop_packet_chain(mp, B_FALSE, NULL, NULL, + ip_drop_packet_chain(mp, B_FALSE, NULL, DROPPER(ipss, ipds_spd_explicit), &ipss->ipsec_spd_dropper); return (NULL); @@ -5835,101 +5380,65 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, cmn_err(CE_WARN, "Having matching tunnel per-port policy\n"); #endif - /* Construct an IPSEC_OUT message. */ - ipsec_mp = ipsec_mp_head = ipsec_alloc_ipsec_out(ns); - if (ipsec_mp == NULL) { - IPPOL_REFRELE(pol, ns); - ip_drop_packet(mp, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - return (NULL); - } - ipsec_mp->b_cont = mp; - io = (ipsec_out_t *)ipsec_mp->b_rptr; - IPPH_REFHOLD(polhead); /* - * NOTE: free() function of ipsec_out mblk will release polhead and - * pol references. + * NOTE: ixa_cleanup() function will release pol references. */ - io->ipsec_out_polhead = polhead; - io->ipsec_out_policy = pol; + ixa->ixa_ipsec_policy = pol; /* * NOTE: There is a subtle difference between iptun_zoneid and * iptun_connp->conn_zoneid explained in iptun_conn_create(). When * interacting with the ip module, we must use conn_zoneid. */ - io->ipsec_out_zoneid = iptun->iptun_connp->conn_zoneid; - io->ipsec_out_v4 = (outer_ipv4 != NULL); - io->ipsec_out_secure = B_TRUE; + ixa->ixa_zoneid = iptun->iptun_connp->conn_zoneid; + + ASSERT((outer_ipv4 != NULL) ? (ixa->ixa_flags & IXAF_IS_IPV4) : + !(ixa->ixa_flags & IXAF_IS_IPV4)); + ASSERT(ixa->ixa_ipsec_policy != NULL); + ixa->ixa_flags |= IXAF_IPSEC_SECURE; if (!(itp->itp_flags & ITPF_P_TUNNEL)) { /* Set up transport mode for tunnelled packets. */ - io->ipsec_out_proto = (inner_ipv4 != NULL) ? IPPROTO_ENCAP : + ixa->ixa_ipsec_proto = (inner_ipv4 != NULL) ? IPPROTO_ENCAP : IPPROTO_IPV6; - return (ipsec_mp); + return (mp); } /* Fill in tunnel-mode goodies here. */ - io->ipsec_out_tunnel = B_TRUE; + ixa->ixa_flags |= IXAF_IPSEC_TUNNEL; /* XXX Do I need to fill in all of the goodies here? */ if (inner_ipv4) { - io->ipsec_out_inaf = AF_INET; - io->ipsec_out_insrc[0] = + ixa->ixa_ipsec_inaf = AF_INET; + ixa->ixa_ipsec_insrc[0] = pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v4; - io->ipsec_out_indst[0] = + ixa->ixa_ipsec_indst[0] = pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v4; } else { - io->ipsec_out_inaf = AF_INET6; - io->ipsec_out_insrc[0] = + ixa->ixa_ipsec_inaf = AF_INET6; + ixa->ixa_ipsec_insrc[0] = pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[0]; - io->ipsec_out_insrc[1] = + ixa->ixa_ipsec_insrc[1] = pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[1]; - io->ipsec_out_insrc[2] = + ixa->ixa_ipsec_insrc[2] = pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[2]; - io->ipsec_out_insrc[3] = + ixa->ixa_ipsec_insrc[3] = pol->ipsp_sel->ipsl_key.ipsl_local.ipsad_v6.s6_addr32[3]; - io->ipsec_out_indst[0] = + ixa->ixa_ipsec_indst[0] = pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[0]; - io->ipsec_out_indst[1] = + ixa->ixa_ipsec_indst[1] = pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[1]; - io->ipsec_out_indst[2] = + ixa->ixa_ipsec_indst[2] = pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[2]; - io->ipsec_out_indst[3] = + ixa->ixa_ipsec_indst[3] = pol->ipsp_sel->ipsl_key.ipsl_remote.ipsad_v6.s6_addr32[3]; } - io->ipsec_out_insrcpfx = pol->ipsp_sel->ipsl_key.ipsl_local_pfxlen; - io->ipsec_out_indstpfx = pol->ipsp_sel->ipsl_key.ipsl_remote_pfxlen; + ixa->ixa_ipsec_insrcpfx = pol->ipsp_sel->ipsl_key.ipsl_local_pfxlen; + ixa->ixa_ipsec_indstpfx = pol->ipsp_sel->ipsl_key.ipsl_remote_pfxlen; /* NOTE: These are used for transport mode too. */ - io->ipsec_out_src_port = pol->ipsp_sel->ipsl_key.ipsl_lport; - io->ipsec_out_dst_port = pol->ipsp_sel->ipsl_key.ipsl_rport; - io->ipsec_out_proto = pol->ipsp_sel->ipsl_key.ipsl_proto; + ixa->ixa_ipsec_src_port = pol->ipsp_sel->ipsl_key.ipsl_lport; + ixa->ixa_ipsec_dst_port = pol->ipsp_sel->ipsl_key.ipsl_rport; + ixa->ixa_ipsec_proto = pol->ipsp_sel->ipsl_key.ipsl_proto; - /* - * The mp pointer still valid - * Add ipsec_out to each fragment. - * The fragment head already has one - */ - nmp = mp->b_next; - mp->b_next = NULL; - mp = nmp; - ASSERT(ipsec_mp != NULL); - while (mp != NULL) { - nmp = mp->b_next; - ipsec_mp->b_next = ipsec_out_tag(ipsec_mp_head, mp, ns); - if (ipsec_mp->b_next == NULL) { - ip_drop_packet_chain(ipsec_mp_head, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - ip_drop_packet_chain(mp, B_FALSE, NULL, NULL, - DROPPER(ipss, ipds_spd_nomem), - &ipss->ipsec_spd_dropper); - return (NULL); - } - ipsec_mp = ipsec_mp->b_next; - mp->b_next = NULL; - mp = nmp; - } - return (ipsec_mp_head); + return (mp); } /* @@ -5937,16 +5446,28 @@ ipsec_tun_outbound(mblk_t *mp, iptun_t *iptun, ipha_t *inner_ipv4, * calls ip_drop_packet() for me on NULL returns. */ mblk_t * -ipsec_check_ipsecin_policy_reasm(mblk_t *ipsec_mp, ipsec_policy_t *pol, +ipsec_check_ipsecin_policy_reasm(mblk_t *attr_mp, ipsec_policy_t *pol, ipha_t *inner_ipv4, ip6_t *inner_ipv6, uint64_t pkt_unique, netstack_t *ns) { - /* Assume ipsec_mp is a chain of b_next-linked IPSEC_IN M_CTLs. */ + /* Assume attr_mp is a chain of b_next-linked ip_recv_attr mblk. */ mblk_t *data_chain = NULL, *data_tail = NULL; - mblk_t *ii_next; - - while (ipsec_mp != NULL) { - ii_next = ipsec_mp->b_next; - ipsec_mp->b_next = NULL; /* No tripping asserts. */ + mblk_t *next; + mblk_t *data_mp; + ip_recv_attr_t iras; + + while (attr_mp != NULL) { + ASSERT(ip_recv_attr_is_mblk(attr_mp)); + next = attr_mp->b_next; + attr_mp->b_next = NULL; /* No tripping asserts. */ + + data_mp = attr_mp->b_cont; + attr_mp->b_cont = NULL; + if (!ip_recv_attr_from_mblk(attr_mp, &iras)) { + /* The ill or ip_stack_t disappeared on us */ + freemsg(data_mp); /* ip_drop_packet?? */ + ira_cleanup(&iras, B_TRUE); + goto fail; + } /* * Need IPPOL_REFHOLD(pol) for extras because @@ -5954,67 +5475,67 @@ ipsec_check_ipsecin_policy_reasm(mblk_t *ipsec_mp, ipsec_policy_t *pol, */ IPPOL_REFHOLD(pol); - if (ipsec_check_ipsecin_policy(ipsec_mp, pol, inner_ipv4, - inner_ipv6, pkt_unique, ns) != NULL) { - if (data_tail == NULL) { - /* First one */ - data_chain = data_tail = ipsec_mp->b_cont; - } else { - data_tail->b_next = ipsec_mp->b_cont; - data_tail = data_tail->b_next; - } - freeb(ipsec_mp); + data_mp = ipsec_check_ipsecin_policy(data_mp, pol, inner_ipv4, + inner_ipv6, pkt_unique, &iras, ns); + ira_cleanup(&iras, B_TRUE); + + if (data_mp == NULL) + goto fail; + + if (data_tail == NULL) { + /* First one */ + data_chain = data_tail = data_mp; } else { - /* - * ipsec_check_ipsecin_policy() freed ipsec_mp - * already. Need to get rid of any extra pol - * references, and any remaining bits as well. - */ - IPPOL_REFRELE(pol, ns); - ipsec_freemsg_chain(data_chain); - ipsec_freemsg_chain(ii_next); /* ipdrop stats? */ - return (NULL); + data_tail->b_next = data_mp; + data_tail = data_mp; } - ipsec_mp = ii_next; + attr_mp = next; } /* * One last release because either the loop bumped it up, or we never * called ipsec_check_ipsecin_policy(). */ - IPPOL_REFRELE(pol, ns); + IPPOL_REFRELE(pol); /* data_chain is ready for return to tun module. */ return (data_chain); -} +fail: + /* + * Need to get rid of any extra pol + * references, and any remaining bits as well. + */ + IPPOL_REFRELE(pol); + ipsec_freemsg_chain(data_chain); + ipsec_freemsg_chain(next); /* ipdrop stats? */ + return (NULL); +} /* - * Returns B_TRUE if the inbound packet passed an IPsec policy check. Returns - * B_FALSE if it failed or if it is a fragment needing its friends before a + * Return a message if the inbound packet passed an IPsec policy check. Returns + * NULL if it failed or if it is a fragment needing its friends before a * policy check can be performed. * - * Expects a non-NULL *data_mp, an optional ipsec_mp, and a non-NULL polhead. - * data_mp may be reassigned with a b_next chain of packets if fragments + * Expects a non-NULL data_mp, and a non-NULL polhead. + * The returned mblk may be a b_next chain of packets if fragments * neeeded to be collected for a proper policy check. * - * Always frees ipsec_mp, but only frees data_mp if returns B_FALSE. This - * function calls ip_drop_packet() on data_mp if need be. + * This function calls ip_drop_packet() on data_mp if need be. * * NOTE: outer_hdr_len is signed. If it's a negative value, the caller * is inspecting an ICMP packet. */ -boolean_t -ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, +mblk_t * +ipsec_tun_inbound(ip_recv_attr_t *ira, mblk_t *data_mp, ipsec_tun_pol_t *itp, ipha_t *inner_ipv4, ip6_t *inner_ipv6, ipha_t *outer_ipv4, ip6_t *outer_ipv6, int outer_hdr_len, netstack_t *ns) { ipsec_policy_head_t *polhead; ipsec_selector_t sel; - mblk_t *message = (ipsec_mp == NULL) ? *data_mp : ipsec_mp; ipsec_policy_t *pol; uint16_t tmpport; selret_t rc; - boolean_t retval, port_policy_present, is_icmp, global_present; + boolean_t port_policy_present, is_icmp, global_present; in6_addr_t tmpaddr; ipaddr_t tmp4; uint8_t flags, *inner_hdr; @@ -6032,7 +5553,6 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, ASSERT(inner_ipv4 != NULL && inner_ipv6 == NULL || inner_ipv4 == NULL && inner_ipv6 != NULL); - ASSERT(message == *data_mp || message->b_cont == *data_mp); if (outer_hdr_len < 0) { outer_hdr_len = (-outer_hdr_len); @@ -6042,6 +5562,8 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, } if (itp != NULL && (itp->itp_flags & ITPF_P_ACTIVE)) { + mblk_t *mp = data_mp; + polhead = itp->itp_policy; /* * We need to perform full Tunnel-Mode enforcement, @@ -6061,53 +5583,66 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, flags = ((port_policy_present ? SEL_PORT_POLICY : SEL_NONE) | (is_icmp ? SEL_IS_ICMP : SEL_NONE) | SEL_TUNNEL_MODE); - rc = ipsec_init_inbound_sel(&sel, *data_mp, inner_ipv4, + rc = ipsec_init_inbound_sel(&sel, data_mp, inner_ipv4, inner_ipv6, flags); switch (rc) { case SELRET_NOMEM: - ip_drop_packet(message, B_TRUE, NULL, NULL, + ip_drop_packet(data_mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); - return (B_FALSE); + return (NULL); case SELRET_TUNFRAG: /* * At this point, if we're cleartext, we don't want * to go there. */ - if (ipsec_mp == NULL) { - ip_drop_packet(*data_mp, B_TRUE, NULL, NULL, + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { + ip_drop_packet(data_mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_got_clear), &ipss->ipsec_spd_dropper); - *data_mp = NULL; - return (B_FALSE); + return (NULL); + } + /* + * If we need to queue the packet. First we + * get an mblk with the attributes. ipsec_fragcache_add + * will prepend that to the queued data and return + * a list of b_next messages each of which starts with + * the attribute mblk. + */ + mp = ip_recv_attr_to_mblk(ira); + if (mp == NULL) { + ip_drop_packet(data_mp, B_TRUE, NULL, + DROPPER(ipss, ipds_spd_nomem), + &ipss->ipsec_spd_dropper); + return (NULL); } - ASSERT(((ipsec_in_t *)ipsec_mp->b_rptr)-> - ipsec_in_secure); - message = ipsec_fragcache_add(&itp->itp_fragcache, - ipsec_mp, *data_mp, outer_hdr_len, ipss); + mp = ipsec_fragcache_add(&itp->itp_fragcache, + mp, data_mp, outer_hdr_len, ipss); - if (message == NULL) { + if (mp == NULL) { /* * Data is cached, fragment chain is not - * complete. I consume ipsec_mp and data_mp + * complete. */ - return (B_FALSE); + return (NULL); } /* * If we get here, we have a full fragment chain. * Reacquire headers and selectors from first fragment. */ - inner_hdr = message->b_cont->b_rptr; + ASSERT(ip_recv_attr_is_mblk(mp)); + data_mp = mp->b_cont; + inner_hdr = data_mp->b_rptr; if (outer_ipv4 != NULL) { inner_hdr += IPH_HDR_LENGTH( - (ipha_t *)message->b_cont->b_rptr); + (ipha_t *)data_mp->b_rptr); } else { - inner_hdr += ip_hdr_length_v6(message->b_cont, - (ip6_t *)message->b_cont->b_rptr); + inner_hdr += ip_hdr_length_v6(data_mp, + (ip6_t *)data_mp->b_rptr); } - ASSERT(inner_hdr <= message->b_cont->b_wptr); + ASSERT(inner_hdr <= data_mp->b_wptr); if (inner_ipv4 != NULL) { inner_ipv4 = (ipha_t *)inner_hdr; @@ -6121,7 +5656,7 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, * Use SEL_TUNNEL_MODE to take into account the outer * header. Use SEL_POST_FRAG so we always get ports. */ - rc = ipsec_init_inbound_sel(&sel, message->b_cont, + rc = ipsec_init_inbound_sel(&sel, data_mp, inner_ipv4, inner_ipv6, SEL_TUNNEL_MODE | SEL_POST_FRAG); switch (rc) { @@ -6132,17 +5667,15 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, */ break; case SELRET_NOMEM: - ip_drop_packet_chain(message, B_TRUE, - NULL, NULL, + ip_drop_packet_chain(mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); - return (B_FALSE); + return (NULL); case SELRET_BADPKT: - ip_drop_packet_chain(message, B_TRUE, - NULL, NULL, + ip_drop_packet_chain(mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); - return (B_FALSE); + return (NULL); case SELRET_TUNFRAG: cmn_err(CE_WARN, "(TUNFRAG on 2nd call...)"); /* FALLTHRU */ @@ -6151,7 +5684,7 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, " returns bizarro 0x%x", rc); /* Guaranteed panic! */ ASSERT(rc == SELRET_NOMEM); - return (B_FALSE); + return (NULL); } /* FALLTHRU */ case SELRET_SUCCESS: @@ -6174,7 +5707,7 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, "ipsec_init_inbound_sel() returns bizarro 0x%x", rc); ASSERT(rc == SELRET_NOMEM); /* Guaranteed panic! */ - return (B_FALSE); + return (NULL); } if (is_icmp) { @@ -6192,42 +5725,54 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, /* find_policy_head() */ rw_enter(&polhead->iph_lock, RW_READER); pol = ipsec_find_policy_head(NULL, polhead, IPSEC_TYPE_INBOUND, - &sel, ns); + &sel); rw_exit(&polhead->iph_lock); if (pol != NULL) { - if (ipsec_mp == NULL || - !((ipsec_in_t *)ipsec_mp->b_rptr)-> - ipsec_in_secure) { - retval = pol->ipsp_act->ipa_allow_clear; - if (!retval) { + uint64_t pkt_unique; + + if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) { + if (!pol->ipsp_act->ipa_allow_clear) { /* * XXX should never get here with * tunnel reassembled fragments? */ - ASSERT(message->b_next == NULL); - ip_drop_packet(message, B_TRUE, NULL, - NULL, + ASSERT(mp == data_mp); + ip_drop_packet(data_mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_got_clear), &ipss->ipsec_spd_dropper); - } else if (ipsec_mp != NULL) { - freeb(ipsec_mp); + IPPOL_REFRELE(pol); + return (NULL); + } else { + IPPOL_REFRELE(pol); + return (mp); } - - IPPOL_REFRELE(pol, ns); - return (retval); } + pkt_unique = SA_UNIQUE_ID(sel.ips_remote_port, + sel.ips_local_port, + (inner_ipv4 == NULL) ? IPPROTO_IPV6 : + IPPROTO_ENCAP, sel.ips_protocol); + /* * NOTE: The following releases pol's reference and * calls ip_drop_packet() for me on NULL returns. * * "sel" is still good here, so let's use it! */ - *data_mp = ipsec_check_ipsecin_policy_reasm(message, - pol, inner_ipv4, inner_ipv6, SA_UNIQUE_ID( - sel.ips_remote_port, sel.ips_local_port, - (inner_ipv4 == NULL) ? IPPROTO_IPV6 : - IPPROTO_ENCAP, sel.ips_protocol), ns); - return (*data_mp != NULL); + if (data_mp == mp) { + /* A single packet without attributes */ + data_mp = ipsec_check_ipsecin_policy(data_mp, + pol, inner_ipv4, inner_ipv6, pkt_unique, + ira, ns); + } else { + /* + * We pass in the b_next chain of attr_mp's + * and get back a b_next chain of data_mp's. + */ + data_mp = ipsec_check_ipsecin_policy_reasm(mp, + pol, inner_ipv4, inner_ipv6, pkt_unique, + ns); + } + return (data_mp); } /* @@ -6237,11 +5782,10 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, * a new-style tunnel-mode tunnel. */ if ((itp->itp_flags & ITPF_P_TUNNEL) && !is_icmp) { - ip_drop_packet_chain(message, B_TRUE, NULL, - NULL, + ip_drop_packet_chain(data_mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_explicit), &ipss->ipsec_spd_dropper); - return (B_FALSE); + return (NULL); } } @@ -6251,24 +5795,22 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, * tunnel-mode tunnel, which either returns with a pass, or gets * hit by the ip_drop_packet_chain() call right above here. */ + ASSERT(data_mp->b_next == NULL); /* If no per-tunnel security, check global policy now. */ - if (ipsec_mp != NULL && !global_present) { - if (((ipsec_in_t *)(ipsec_mp->b_rptr))-> - ipsec_in_icmp_loopback) { + if ((ira->ira_flags & IRAF_IPSEC_SECURE) && !global_present) { + if (ira->ira_flags & IRAF_TRUSTED_ICMP) { /* - * This is an ICMP message with an ipsec_mp - * attached. We should accept it. + * This is an ICMP message that was geenrated locally. + * We should accept it. */ - if (ipsec_mp != NULL) - freeb(ipsec_mp); - return (B_TRUE); + return (data_mp); } - ip_drop_packet(ipsec_mp, B_TRUE, NULL, NULL, + ip_drop_packet(data_mp, B_TRUE, NULL, DROPPER(ipss, ipds_spd_got_secure), &ipss->ipsec_spd_dropper); - return (B_FALSE); + return (NULL); } if (is_icmp) { @@ -6294,11 +5836,10 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, } } - /* NOTE: Frees message if it returns NULL. */ - if (ipsec_check_global_policy(message, NULL, outer_ipv4, outer_ipv6, - (ipsec_mp != NULL), ns) == NULL) { - return (B_FALSE); - } + data_mp = ipsec_check_global_policy(data_mp, NULL, outer_ipv4, + outer_ipv6, ira, ns); + if (data_mp == NULL) + return (NULL); if (is_icmp) { /* Set things back to normal. */ @@ -6314,14 +5855,11 @@ ipsec_tun_inbound(mblk_t *ipsec_mp, mblk_t **data_mp, ipsec_tun_pol_t *itp, } } - if (ipsec_mp != NULL) - freeb(ipsec_mp); - /* * At this point, we pretend it's a cleartext accepted * packet. */ - return (B_TRUE); + return (data_mp); } /* @@ -6365,7 +5903,7 @@ itp_unlink(ipsec_tun_pol_t *node, netstack_t *ns) rw_enter(&ipss->ipsec_tunnel_policy_lock, RW_WRITER); ipss->ipsec_tunnel_policy_gen++; - ipsec_fragcache_uninit(&node->itp_fragcache); + ipsec_fragcache_uninit(&node->itp_fragcache, ipss); avl_remove(&ipss->ipsec_tunnel_policies, node); rw_exit(&ipss->ipsec_tunnel_policy_lock); ITP_REFRELE(node, ns); @@ -6615,7 +6153,7 @@ ipsec_fragcache_init(ipsec_fragcache_t *frag) } void -ipsec_fragcache_uninit(ipsec_fragcache_t *frag) +ipsec_fragcache_uninit(ipsec_fragcache_t *frag, ipsec_stack_t *ipss) { ipsec_fragcache_entry_t *fep; int i; @@ -6627,7 +6165,7 @@ ipsec_fragcache_uninit(ipsec_fragcache_t *frag) fep = (frag->itpf_ptr)[i]; while (fep != NULL) { /* Returned fep is next in chain or NULL */ - fep = fragcache_delentry(i, fep, frag); + fep = fragcache_delentry(i, fep, frag, ipss); } } /* @@ -6658,10 +6196,12 @@ ipsec_fragcache_uninit(ipsec_fragcache_t *frag) /* * Add a fragment to the fragment cache. Consumes mp if NULL is returned. * Returns mp if a whole fragment has been assembled, NULL otherwise + * The returned mp could be a b_next chain of fragments. + * + * The iramp argument is set on inbound; NULL if outbound. */ - mblk_t * -ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, +ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *iramp, mblk_t *mp, int outer_hdr_len, ipsec_stack_t *ipss) { boolean_t is_v4; @@ -6672,7 +6212,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, uint8_t v6_proto; uint8_t *v6_proto_p; uint16_t ip6_hdr_length; - ip6_pkt_t ipp; + ip_pkt_t ipp; ip6_frag_t *fraghdr; ipsec_fragcache_entry_t *fep; int i; @@ -6680,10 +6220,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, int firstbyte, lastbyte; int offset; int last; - boolean_t inbound = (ipsec_mp != NULL); - mblk_t *first_mp = inbound ? ipsec_mp : mp; - - ASSERT(first_mp == mp || first_mp->b_cont == mp); + boolean_t inbound = (iramp != NULL); /* * You're on the slow path, so insure that every packet in the @@ -6692,14 +6229,14 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (mp->b_cont != NULL) { nmp = msgpullup(mp, -1); if (nmp == NULL) { - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } freemsg(mp); - if (ipsec_mp != NULL) - ipsec_mp->b_cont = nmp; mp = nmp; } @@ -6721,9 +6258,11 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, * If it fails we have a malformed packet */ mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_malformed_packet), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } else { v6_proto = *v6_proto_p; @@ -6731,16 +6270,18 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, bzero(&ipp, sizeof (ipp)); - (void) ip_find_hdr_v6(mp, ip6h, &ipp, NULL); + (void) ip_find_hdr_v6(mp, ip6h, B_FALSE, &ipp, NULL); if (!(ipp.ipp_fields & IPPF_FRAGHDR)) { /* * We think this is a fragment, but didn't find * a fragment header. Something is wrong. */ mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } fraghdr = ipp.ipp_fraghdr; @@ -6759,7 +6300,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, */ itpf_time = gethrestime_sec(); if (itpf_time >= frag->itpf_expire_hint) - ipsec_fragcache_clean(frag); + ipsec_fragcache_clean(frag, ipss); /* Lookup to see if there is an existing entry */ @@ -6814,11 +6355,13 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, /* check for bogus fragments and delete the entry */ if (firstbyte > 0 && firstbyte <= 8) { if (fep != NULL) - (void) fragcache_delentry(i, fep, frag); + (void) fragcache_delentry(i, fep, frag, ipss); mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } @@ -6826,12 +6369,14 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (fep == NULL) { if (frag->itpf_freelist == NULL) { /* see if there is some space */ - ipsec_fragcache_clean(frag); + ipsec_fragcache_clean(frag, ipss); if (frag->itpf_freelist == NULL) { mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } } @@ -6879,7 +6424,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, ipha_t *niph; ipha_t *oniph; ip6_t *nip6h; - ip6_pkt_t nipp; + ip_pkt_t nipp; ip6_frag_t *nfraghdr; uint16_t nip6_hdr_length; uint8_t *nv6_proto_p; @@ -6929,14 +6474,17 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (!ip_hdr_length_nexthdr_v6(ndata_mp, nip6h, &nip6_hdr_length, &nv6_proto_p)) { mutex_exit(&frag->itpf_lock); - ip_drop_packet_chain(nmp, inbound, NULL, NULL, + ip_drop_packet_chain(nmp, inbound, NULL, DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); ipsec_freemsg_chain(ndata_mp); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } bzero(&nipp, sizeof (nipp)); - (void) ip_find_hdr_v6(ndata_mp, nip6h, &nipp, NULL); + (void) ip_find_hdr_v6(ndata_mp, nip6h, B_FALSE, &nipp, + NULL); nfraghdr = nipp.ipp_fraghdr; nfirstbyte = ntohs(nfraghdr->ip6f_offlg & IP6F_OFF_MASK); @@ -6968,11 +6516,13 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (bcmp(data, ndata, MIN(lastbyte, nlastbyte) - firstbyte)) { /* Overlapping data does not match */ - (void) fragcache_delentry(i, fep, frag); + (void) fragcache_delentry(i, fep, frag, ipss); mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_overlap_frag), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } /* Part of defense for jolt2.c fragmentation attack */ @@ -6987,9 +6537,11 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, * ---------- ------ */ mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_evil_frag), &ipss->ipsec_spd_dropper); + if (inbound) + (void) ip_recv_attr_free_mblk(iramp); return (NULL); } @@ -7027,12 +6579,17 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (bcmp(data, ndata, MIN(lastbyte, nlastbyte) - nfirstbyte)) { /* Overlap mismatch */ - (void) fragcache_delentry(i, fep, frag); + (void) fragcache_delentry(i, fep, frag, + ipss); mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, - NULL, DROPPER(ipss, + ip_drop_packet(mp, inbound, NULL, + DROPPER(ipss, ipds_spd_overlap_frag), &ipss->ipsec_spd_dropper); + if (inbound) { + (void) ip_recv_attr_free_mblk( + iramp); + } return (NULL); } } @@ -7046,21 +6603,31 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, prevmp = nmp; } - first_mp->b_next = nmp; + /* Prepend the attributes before we link it in */ + if (iramp != NULL) { + ASSERT(iramp->b_cont == NULL); + iramp->b_cont = mp; + mp = iramp; + iramp = NULL; + } + mp->b_next = nmp; if (prevmp == NULL) { - fep->itpfe_fraglist = first_mp; + fep->itpfe_fraglist = mp; } else { - prevmp->b_next = first_mp; + prevmp->b_next = mp; } if (last) fep->itpfe_last = 1; /* Part of defense for jolt2.c fragmentation attack */ if (++(fep->itpfe_depth) > IPSEC_MAX_FRAGS) { - (void) fragcache_delentry(i, fep, frag); + (void) fragcache_delentry(i, fep, frag, ipss); mutex_exit(&frag->itpf_lock); - ip_drop_packet(first_mp, inbound, NULL, NULL, + if (inbound) + mp = ip_recv_attr_free_mblk(mp); + + ip_drop_packet(mp, inbound, NULL, DROPPER(ipss, ipds_spd_max_frags), &ipss->ipsec_spd_dropper); return (NULL); @@ -7078,7 +6645,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, #ifdef FRAGCACHE_DEBUG cmn_err(CE_WARN, "Last fragment cached.\n"); - cmn_err(CE_WARN, "mp = %p, first_mp = %p.\n", mp, first_mp); + cmn_err(CE_WARN, "mp = %p\n", mp); #endif offset = 0; @@ -7118,14 +6685,15 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, if (!ip_hdr_length_nexthdr_v6(data_mp, ip6h, &ip6_hdr_length, &v6_proto_p)) { mutex_exit(&frag->itpf_lock); - ip_drop_packet_chain(mp, inbound, NULL, NULL, + ip_drop_packet_chain(mp, inbound, NULL, DROPPER(ipss, ipds_spd_malformed_frag), &ipss->ipsec_spd_dropper); return (NULL); } v6_proto = *v6_proto_p; bzero(&ipp, sizeof (ipp)); - (void) ip_find_hdr_v6(data_mp, ip6h, &ipp, NULL); + (void) ip_find_hdr_v6(data_mp, ip6h, B_FALSE, &ipp, + NULL); fraghdr = ipp.ipp_fraghdr; firstbyte = ntohs(fraghdr->ip6f_offlg & IP6F_OFF_MASK); @@ -7163,7 +6731,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, (!is_v4 && !(fraghdr->ip6f_offlg & IP6F_MORE_FRAG))) { mp = fep->itpfe_fraglist; fep->itpfe_fraglist = NULL; - (void) fragcache_delentry(i, fep, frag); + (void) fragcache_delentry(i, fep, frag, ipss); mutex_exit(&frag->itpf_lock); if ((is_v4 && (firstbyte + ntohs(iph->ipha_length) > @@ -7171,7 +6739,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, ntohs(ip6h->ip6_plen) > 65535))) { /* It is an invalid "ping-o-death" packet */ /* Discard it */ - ip_drop_packet_chain(mp, inbound, NULL, NULL, + ip_drop_packet_chain(mp, inbound, NULL, DROPPER(ipss, ipds_spd_evil_frag), &ipss->ipsec_spd_dropper); return (NULL); @@ -7181,7 +6749,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, "mp->b_next = %p", mp, mp->b_next); #endif /* - * For inbound case, mp has ipsec_in b_next'd chain + * For inbound case, mp has attrmp b_next'd chain * For outbound case, it is just data mp chain */ return (mp); @@ -7202,7 +6770,7 @@ ipsec_fragcache_add(ipsec_fragcache_t *frag, mblk_t *ipsec_mp, mblk_t *mp, } static void -ipsec_fragcache_clean(ipsec_fragcache_t *frag) +ipsec_fragcache_clean(ipsec_fragcache_t *frag, ipsec_stack_t *ipss) { ipsec_fragcache_entry_t *fep; int i; @@ -7221,7 +6789,7 @@ ipsec_fragcache_clean(ipsec_fragcache_t *frag) while (fep) { if (fep->itpfe_exp < itpf_time) { /* found */ - fep = fragcache_delentry(i, fep, frag); + fep = fragcache_delentry(i, fep, frag, ipss); } else { if (fep->itpfe_exp < earlyexp) { earlyfep = fep; @@ -7237,12 +6805,12 @@ ipsec_fragcache_clean(ipsec_fragcache_t *frag) /* if (!found) */ if (frag->itpf_freelist == NULL) - (void) fragcache_delentry(earlyi, earlyfep, frag); + (void) fragcache_delentry(earlyi, earlyfep, frag, ipss); } static ipsec_fragcache_entry_t * fragcache_delentry(int slot, ipsec_fragcache_entry_t *fep, - ipsec_fragcache_t *frag) + ipsec_fragcache_t *frag, ipsec_stack_t *ipss) { ipsec_fragcache_entry_t *targp; ipsec_fragcache_entry_t *nextp = fep->itpfe_next; @@ -7250,7 +6818,12 @@ fragcache_delentry(int slot, ipsec_fragcache_entry_t *fep, ASSERT(MUTEX_HELD(&frag->itpf_lock)); /* Free up any fragment list still in cache entry */ - ipsec_freemsg_chain(fep->itpfe_fraglist); + if (fep->itpfe_fraglist != NULL) { + ip_drop_packet_chain(fep->itpfe_fraglist, + ip_recv_attr_is_mblk(fep->itpfe_fraglist), NULL, + DROPPER(ipss, ipds_spd_nomem), &ipss->ipsec_spd_dropper); + } + fep->itpfe_fraglist = NULL; targp = (frag->itpf_ptr)[slot]; ASSERT(targp != 0); |