10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51659Smarkfen * Common Development and Distribution License (the "License").
61659Smarkfen * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*11446Sdanmcd@sun.com * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #include <sys/types.h>
270Sstevel@tonic-gate #include <sys/stream.h>
2810934Ssommerfeld@sun.com #include <sys/strsubr.h>
290Sstevel@tonic-gate #include <sys/sunddi.h>
303055Sdanmcd #include <sys/ddi.h>
310Sstevel@tonic-gate #include <sys/strlog.h>
320Sstevel@tonic-gate
330Sstevel@tonic-gate #include <inet/common.h>
340Sstevel@tonic-gate #include <inet/mib2.h>
350Sstevel@tonic-gate #include <inet/ip.h>
360Sstevel@tonic-gate #include <inet/ip6.h>
370Sstevel@tonic-gate
380Sstevel@tonic-gate #include <net/pfkeyv2.h>
390Sstevel@tonic-gate #include <inet/sadb.h>
400Sstevel@tonic-gate #include <inet/ipsec_impl.h>
413448Sdh155122 #include <inet/ipdrop.h>
420Sstevel@tonic-gate #include <inet/ipsecesp.h>
430Sstevel@tonic-gate #include <inet/ipsecah.h>
440Sstevel@tonic-gate #include <sys/kstat.h>
450Sstevel@tonic-gate
460Sstevel@tonic-gate /*
470Sstevel@tonic-gate * Returns B_TRUE if the identities in the SA match the identities
480Sstevel@tonic-gate * in the "latch" structure.
490Sstevel@tonic-gate */
500Sstevel@tonic-gate
510Sstevel@tonic-gate static boolean_t
ipsec_match_outbound_ids(ipsec_latch_t * ipl,ipsa_t * sa)520Sstevel@tonic-gate ipsec_match_outbound_ids(ipsec_latch_t *ipl, ipsa_t *sa)
530Sstevel@tonic-gate {
540Sstevel@tonic-gate ASSERT(ipl->ipl_ids_latched == B_TRUE);
550Sstevel@tonic-gate return ipsid_equal(ipl->ipl_local_cid, sa->ipsa_src_cid) &&
560Sstevel@tonic-gate ipsid_equal(ipl->ipl_remote_cid, sa->ipsa_dst_cid);
570Sstevel@tonic-gate }
580Sstevel@tonic-gate
5911042SErik.Nordmark@Sun.COM /* l1 is packet label; l2 is SA label */
6010934Ssommerfeld@sun.com boolean_t
ipsec_label_match(ts_label_t * l1,ts_label_t * l2)6111042SErik.Nordmark@Sun.COM ipsec_label_match(ts_label_t *l1, ts_label_t *l2)
6210934Ssommerfeld@sun.com {
6310934Ssommerfeld@sun.com if (!is_system_labeled())
6410934Ssommerfeld@sun.com return (B_TRUE);
6510934Ssommerfeld@sun.com
6610934Ssommerfeld@sun.com /*
6711042SErik.Nordmark@Sun.COM * Check for NULL label. Unlabeled SA (l2) always matches;
6810934Ssommerfeld@sun.com * unlabeled user with labeled SA always fails
6910934Ssommerfeld@sun.com */
7011042SErik.Nordmark@Sun.COM if (l2 == NULL)
7110934Ssommerfeld@sun.com return (B_TRUE);
7210934Ssommerfeld@sun.com
7310934Ssommerfeld@sun.com if (l1 == NULL)
7410934Ssommerfeld@sun.com return (B_FALSE);
7510934Ssommerfeld@sun.com
7610934Ssommerfeld@sun.com /* Simple IPsec MLS policy: labels must be equal */
7710934Ssommerfeld@sun.com /* In future will need bit in policy saying whether this is the case */
7810934Ssommerfeld@sun.com
7910934Ssommerfeld@sun.com /*
8010934Ssommerfeld@sun.com * label_equal() checks DOI and label contents. We should be
8110934Ssommerfeld@sun.com * good to go with this check.
8210934Ssommerfeld@sun.com */
8310934Ssommerfeld@sun.com return (label_equal(l1, l2));
8410934Ssommerfeld@sun.com }
8510934Ssommerfeld@sun.com
8610934Ssommerfeld@sun.com
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate * Look up a security association based on the unique ID generated by IP and
893055Sdanmcd * transport or tunnel information, such as ports and upper-layer protocol,
903055Sdanmcd * and the inner and outer address(es). Used for uniqueness testing and
913055Sdanmcd * outbound packets. The outer source address may be ignored.
920Sstevel@tonic-gate *
930Sstevel@tonic-gate * I expect an SA hash bucket, and that its per-bucket mutex is held.
940Sstevel@tonic-gate * The SA ptr I return will have its reference count incremented by one.
950Sstevel@tonic-gate */
960Sstevel@tonic-gate ipsa_t *
ipsec_getassocbyconn(isaf_t * bucket,ip_xmit_attr_t * ixa,uint32_t * src,uint32_t * dst,sa_family_t af,uint8_t protocol,ts_label_t * tsl)9711042SErik.Nordmark@Sun.COM ipsec_getassocbyconn(isaf_t *bucket, ip_xmit_attr_t *ixa, uint32_t *src,
9811042SErik.Nordmark@Sun.COM uint32_t *dst, sa_family_t af, uint8_t protocol, ts_label_t *tsl)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate ipsa_t *retval, *candidate;
1010Sstevel@tonic-gate ipsec_action_t *candact;
1020Sstevel@tonic-gate boolean_t need_unique;
10311042SErik.Nordmark@Sun.COM boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL);
1040Sstevel@tonic-gate uint64_t unique_id;
1050Sstevel@tonic-gate uint32_t old_flags, excludeflags;
10611042SErik.Nordmark@Sun.COM ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
10711042SErik.Nordmark@Sun.COM ipsec_action_t *actlist = ixa->ixa_ipsec_action;
1080Sstevel@tonic-gate ipsec_action_t *act;
10911042SErik.Nordmark@Sun.COM ipsec_latch_t *ipl = ixa->ixa_ipsec_latch;
1100Sstevel@tonic-gate ipsa_ref_t *ipr = NULL;
11111042SErik.Nordmark@Sun.COM sa_family_t inaf = ixa->ixa_ipsec_inaf;
11211042SErik.Nordmark@Sun.COM uint32_t *insrc = ixa->ixa_ipsec_insrc;
11311042SErik.Nordmark@Sun.COM uint32_t *indst = ixa->ixa_ipsec_indst;
11411042SErik.Nordmark@Sun.COM uint8_t insrcpfx = ixa->ixa_ipsec_insrcpfx;
11511042SErik.Nordmark@Sun.COM uint8_t indstpfx = ixa->ixa_ipsec_indstpfx;
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate ASSERT(MUTEX_HELD(&bucket->isaf_lock));
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate /*
12011042SErik.Nordmark@Sun.COM * Caller must set ip_xmit_attr_t structure such that we know
1213055Sdanmcd * whether this is tunnel mode or transport mode based on
12211042SErik.Nordmark@Sun.COM * IXAF_IPSEC_TUNNEL. If this flag is set, we assume that
1233055Sdanmcd * there are valid inner src and destination addresses to compare.
1243055Sdanmcd */
1253055Sdanmcd
1263055Sdanmcd /*
1270Sstevel@tonic-gate * Fast path: do we have a latch structure, is it for this bucket,
1280Sstevel@tonic-gate * and does the generation number match? If so, refhold and return.
1290Sstevel@tonic-gate */
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate if (ipl != NULL) {
1320Sstevel@tonic-gate ASSERT((protocol == IPPROTO_AH) || (protocol == IPPROTO_ESP));
13311042SErik.Nordmark@Sun.COM ipr = &ixa->ixa_ipsec_ref[protocol - IPPROTO_ESP];
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate retval = ipr->ipsr_sa;
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate /*
1380Sstevel@tonic-gate * NOTE: The isaf_gen check (incremented upon
1390Sstevel@tonic-gate * sadb_unlinkassoc()) protects against retval being a freed
1400Sstevel@tonic-gate * SA. (We're exploiting short-circuit evaluation.)
1410Sstevel@tonic-gate */
1420Sstevel@tonic-gate if ((bucket == ipr->ipsr_bucket) &&
1430Sstevel@tonic-gate (bucket->isaf_gen == ipr->ipsr_gen) &&
1440Sstevel@tonic-gate (retval->ipsa_state != IPSA_STATE_DEAD) &&
1450Sstevel@tonic-gate !(retval->ipsa_flags & IPSA_F_CINVALID)) {
1460Sstevel@tonic-gate IPSA_REFHOLD(retval);
1470Sstevel@tonic-gate return (retval);
1480Sstevel@tonic-gate }
1490Sstevel@tonic-gate }
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate ASSERT((pp != NULL) || (actlist != NULL));
1520Sstevel@tonic-gate if (actlist == NULL)
1530Sstevel@tonic-gate actlist = pp->ipsp_act;
1540Sstevel@tonic-gate ASSERT(actlist != NULL);
1550Sstevel@tonic-gate
1560Sstevel@tonic-gate need_unique = actlist->ipa_want_unique;
15711042SErik.Nordmark@Sun.COM unique_id = SA_FORM_UNIQUE_ID(ixa);
1580Sstevel@tonic-gate
1590Sstevel@tonic-gate /*
1600Sstevel@tonic-gate * Precompute mask for SA flags comparison: If we need a
1610Sstevel@tonic-gate * unique SA and an SA has already been used, or if the SA has
1620Sstevel@tonic-gate * a unique value which doesn't match, we aren't interested in
1630Sstevel@tonic-gate * the SA..
1640Sstevel@tonic-gate */
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate excludeflags = IPSA_F_UNIQUE;
1670Sstevel@tonic-gate if (need_unique)
1680Sstevel@tonic-gate excludeflags |= IPSA_F_USED;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate /*
1710Sstevel@tonic-gate * Walk the hash bucket, matching on:
1720Sstevel@tonic-gate *
1730Sstevel@tonic-gate * - unique_id
1740Sstevel@tonic-gate * - destination
1750Sstevel@tonic-gate * - source
1760Sstevel@tonic-gate * - algorithms
1773055Sdanmcd * - inner dst
1783055Sdanmcd * - inner src
1790Sstevel@tonic-gate * - <MORE TBD>
1800Sstevel@tonic-gate *
1810Sstevel@tonic-gate * Make sure that wildcard sources are inserted at the end of the hash
1820Sstevel@tonic-gate * bucket.
1830Sstevel@tonic-gate *
1840Sstevel@tonic-gate * DEFINITIONS: A _shared_ SA is one with unique_id == 0 and USED.
1850Sstevel@tonic-gate * An _unused_ SA is one with unique_id == 0 and not USED.
1860Sstevel@tonic-gate * A _unique_ SA is one with unique_id != 0 and USED.
1870Sstevel@tonic-gate * An SA with unique_id != 0 and not USED never happens.
1880Sstevel@tonic-gate */
1890Sstevel@tonic-gate
1900Sstevel@tonic-gate candidate = NULL;
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate for (retval = bucket->isaf_ipsa; retval != NULL;
1930Sstevel@tonic-gate retval = retval->ipsa_next) {
1940Sstevel@tonic-gate ASSERT((candidate == NULL) ||
1950Sstevel@tonic-gate MUTEX_HELD(&candidate->ipsa_lock));
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate /*
1980Sstevel@tonic-gate * Q: Should I lock this SA?
1990Sstevel@tonic-gate * A: For now, yes. I change and use too many fields in here
2000Sstevel@tonic-gate * (e.g. unique_id) that I may be racing with other threads.
2010Sstevel@tonic-gate * Also, the refcnt needs to be bumped up.
2020Sstevel@tonic-gate */
2030Sstevel@tonic-gate
2040Sstevel@tonic-gate mutex_enter(&retval->ipsa_lock);
2050Sstevel@tonic-gate
2060Sstevel@tonic-gate /* My apologies for the use of goto instead of continue. */
2073055Sdanmcd
2083055Sdanmcd /* Outer destination address */
2090Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
2100Sstevel@tonic-gate goto next_ipsa; /* Destination mismatch. */
2113055Sdanmcd
2123055Sdanmcd /* Outer source address */
2130Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) &&
2140Sstevel@tonic-gate !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af))
2150Sstevel@tonic-gate goto next_ipsa; /* Specific source and not matched. */
2160Sstevel@tonic-gate
2173055Sdanmcd if (tunnel_mode) {
2183055Sdanmcd /* Check tunnel mode */
2193055Sdanmcd if (!(retval->ipsa_flags & IPSA_F_TUNNEL))
2203055Sdanmcd goto next_ipsa; /* Not tunnel mode SA */
2213055Sdanmcd
2223055Sdanmcd /* Inner destination address */
2233055Sdanmcd if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innerdst, inaf)) {
2243055Sdanmcd if (!ip_addr_match((uint8_t *)indst,
2253055Sdanmcd min(indstpfx, retval->ipsa_innerdstpfx),
2263055Sdanmcd (in6_addr_t *)retval->ipsa_innerdst))
2273055Sdanmcd goto next_ipsa; /* not matched. */
2283055Sdanmcd }
2293055Sdanmcd
2303055Sdanmcd /* Inner source address */
2313055Sdanmcd if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innersrc, inaf)) {
2323055Sdanmcd if (!ip_addr_match((uint8_t *)insrc,
2333055Sdanmcd min(insrcpfx, retval->ipsa_innersrcpfx),
2343055Sdanmcd (in6_addr_t *)retval->ipsa_innersrc))
2353055Sdanmcd goto next_ipsa; /* not matched. */
2363055Sdanmcd }
2373055Sdanmcd } else {
2383055Sdanmcd /* Check transport mode */
2393055Sdanmcd if (retval->ipsa_flags & IPSA_F_TUNNEL)
2403055Sdanmcd goto next_ipsa; /* Not transport mode SA */
2413055Sdanmcd
2423055Sdanmcd /*
2433055Sdanmcd * TODO - If we ever do RFC 3884's dream of transport-
2443055Sdanmcd * mode SAs with inner IP address selectors, we need
2453055Sdanmcd * to put some code here.
2463055Sdanmcd */
2473055Sdanmcd }
2483055Sdanmcd
2490Sstevel@tonic-gate /*
2500Sstevel@tonic-gate * XXX should be able to use cached/latched action
2510Sstevel@tonic-gate * to dodge this loop
2520Sstevel@tonic-gate */
2530Sstevel@tonic-gate for (act = actlist; act != NULL; act = act->ipa_next) {
2540Sstevel@tonic-gate ipsec_act_t *ap = &act->ipa_act;
2550Sstevel@tonic-gate if (ap->ipa_type != IPSEC_POLICY_APPLY)
2560Sstevel@tonic-gate continue;
2570Sstevel@tonic-gate
2580Sstevel@tonic-gate /*
2590Sstevel@tonic-gate * XXX ugly. should be better way to do this test
2600Sstevel@tonic-gate */
2610Sstevel@tonic-gate if (protocol == IPPROTO_AH) {
2620Sstevel@tonic-gate if (!(ap->ipa_apply.ipp_use_ah))
2630Sstevel@tonic-gate continue;
2640Sstevel@tonic-gate if (ap->ipa_apply.ipp_auth_alg !=
2650Sstevel@tonic-gate retval->ipsa_auth_alg)
2660Sstevel@tonic-gate continue;
2670Sstevel@tonic-gate if (ap->ipa_apply.ipp_ah_minbits >
2687749SThejaswini.Singarajipura@Sun.COM retval->ipsa_authkeybits)
2690Sstevel@tonic-gate continue;
2700Sstevel@tonic-gate } else {
2710Sstevel@tonic-gate if (!(ap->ipa_apply.ipp_use_esp))
2720Sstevel@tonic-gate continue;
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate if ((ap->ipa_apply.ipp_encr_alg !=
2750Sstevel@tonic-gate retval->ipsa_encr_alg))
2760Sstevel@tonic-gate continue;
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate if (ap->ipa_apply.ipp_espe_minbits >
2790Sstevel@tonic-gate retval->ipsa_encrkeybits)
2800Sstevel@tonic-gate continue;
2810Sstevel@tonic-gate
2820Sstevel@tonic-gate if (ap->ipa_apply.ipp_esp_auth_alg != 0) {
2830Sstevel@tonic-gate if (ap->ipa_apply.ipp_esp_auth_alg !=
2840Sstevel@tonic-gate retval->ipsa_auth_alg)
2850Sstevel@tonic-gate continue;
2860Sstevel@tonic-gate if (ap->ipa_apply.ipp_espa_minbits >
2870Sstevel@tonic-gate retval->ipsa_authkeybits)
2880Sstevel@tonic-gate continue;
2890Sstevel@tonic-gate }
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate * Check key mgmt proto, cookie
2940Sstevel@tonic-gate */
2950Sstevel@tonic-gate if ((ap->ipa_apply.ipp_km_proto != 0) &&
2960Sstevel@tonic-gate (retval->ipsa_kmp != 0) &&
2970Sstevel@tonic-gate (ap->ipa_apply.ipp_km_proto != retval->ipsa_kmp))
2980Sstevel@tonic-gate continue;
2990Sstevel@tonic-gate
3000Sstevel@tonic-gate if ((ap->ipa_apply.ipp_km_cookie != 0) &&
3010Sstevel@tonic-gate (retval->ipsa_kmc != 0) &&
3020Sstevel@tonic-gate (ap->ipa_apply.ipp_km_cookie != retval->ipsa_kmc))
3030Sstevel@tonic-gate continue;
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate break;
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate if (act == NULL)
3080Sstevel@tonic-gate goto next_ipsa; /* nothing matched */
3090Sstevel@tonic-gate
3100Sstevel@tonic-gate /*
3110Sstevel@tonic-gate * Do identities match?
3120Sstevel@tonic-gate */
3130Sstevel@tonic-gate if (ipl && ipl->ipl_ids_latched &&
3140Sstevel@tonic-gate !ipsec_match_outbound_ids(ipl, retval))
3150Sstevel@tonic-gate goto next_ipsa;
3160Sstevel@tonic-gate
3170Sstevel@tonic-gate /*
31810934Ssommerfeld@sun.com * Do labels match?
31910934Ssommerfeld@sun.com */
32011042SErik.Nordmark@Sun.COM if (!ipsec_label_match(tsl, retval->ipsa_tsl))
32110934Ssommerfeld@sun.com goto next_ipsa;
32210934Ssommerfeld@sun.com
32310934Ssommerfeld@sun.com /*
3240Sstevel@tonic-gate * At this point, we know that we have at least a match on:
3250Sstevel@tonic-gate *
3260Sstevel@tonic-gate * - dest
3270Sstevel@tonic-gate * - source (if source is specified, i.e. non-zeroes)
3283055Sdanmcd * - inner dest (if specified)
3293055Sdanmcd * - inner source (if specified)
3300Sstevel@tonic-gate * - auth alg (if auth alg is specified, i.e. non-zero)
3310Sstevel@tonic-gate * - encrypt. alg (if encrypt. alg is specified, i.e. non-zero)
3320Sstevel@tonic-gate * and we know that the SA keylengths are appropriate.
3330Sstevel@tonic-gate *
3340Sstevel@tonic-gate * (Keep in mind known-src SAs are hit before zero-src SAs,
3350Sstevel@tonic-gate * thanks to sadb_insertassoc().)
3360Sstevel@tonic-gate * If we need a unique asssociation, optimally we have
3370Sstevel@tonic-gate * ipsa_unique_id == unique_id, otherwise NOT USED
3380Sstevel@tonic-gate * is held in reserve (stored in candidate).
3390Sstevel@tonic-gate *
3400Sstevel@tonic-gate * For those stored in candidate, take best-match (i.e. given
3410Sstevel@tonic-gate * a choice, candidate should have non-zero ipsa_src).
3420Sstevel@tonic-gate */
3430Sstevel@tonic-gate
3440Sstevel@tonic-gate /*
3450Sstevel@tonic-gate * If SA has a unique value which matches, we're all set...
3460Sstevel@tonic-gate * "key management knows best"
3470Sstevel@tonic-gate */
3480Sstevel@tonic-gate if ((retval->ipsa_flags & IPSA_F_UNIQUE) &&
3490Sstevel@tonic-gate ((unique_id & retval->ipsa_unique_mask) ==
3507749SThejaswini.Singarajipura@Sun.COM retval->ipsa_unique_id))
3510Sstevel@tonic-gate break;
3520Sstevel@tonic-gate
3530Sstevel@tonic-gate /*
3540Sstevel@tonic-gate * If we need a unique SA and this SA has already been used,
3550Sstevel@tonic-gate * or if the SA has a unique value which doesn't match,
3560Sstevel@tonic-gate * this isn't for us.
3570Sstevel@tonic-gate */
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate if (retval->ipsa_flags & excludeflags)
3600Sstevel@tonic-gate goto next_ipsa;
3610Sstevel@tonic-gate
3620Sstevel@tonic-gate
3630Sstevel@tonic-gate /*
3640Sstevel@tonic-gate * I found a candidate..
3650Sstevel@tonic-gate */
3660Sstevel@tonic-gate if (candidate == NULL) {
3670Sstevel@tonic-gate /*
3680Sstevel@tonic-gate * and didn't already have one..
3690Sstevel@tonic-gate */
3700Sstevel@tonic-gate candidate = retval;
3710Sstevel@tonic-gate candact = act;
3720Sstevel@tonic-gate continue;
3730Sstevel@tonic-gate } else {
3740Sstevel@tonic-gate /*
3750Sstevel@tonic-gate * If candidate's source address is zero and
3760Sstevel@tonic-gate * the current match (i.e. retval) address is
3770Sstevel@tonic-gate * not zero, we have a better candidate..
3780Sstevel@tonic-gate */
3790Sstevel@tonic-gate if (IPSA_IS_ADDR_UNSPEC(candidate->ipsa_srcaddr, af) &&
3800Sstevel@tonic-gate !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
3810Sstevel@tonic-gate mutex_exit(&candidate->ipsa_lock);
3820Sstevel@tonic-gate candidate = retval;
3830Sstevel@tonic-gate candact = act;
3840Sstevel@tonic-gate continue;
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate next_ipsa:
3880Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock);
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate ASSERT((retval == NULL) || MUTEX_HELD(&retval->ipsa_lock));
3910Sstevel@tonic-gate ASSERT((candidate == NULL) || MUTEX_HELD(&candidate->ipsa_lock));
3920Sstevel@tonic-gate ASSERT((retval == NULL) || (act != NULL));
3930Sstevel@tonic-gate ASSERT((candidate == NULL) || (candact != NULL));
3940Sstevel@tonic-gate
3950Sstevel@tonic-gate /* Let caller react to a lookup failure when it gets NULL. */
3960Sstevel@tonic-gate if (retval == NULL && candidate == NULL)
3970Sstevel@tonic-gate return (NULL);
3980Sstevel@tonic-gate
3990Sstevel@tonic-gate if (retval == NULL) {
4000Sstevel@tonic-gate ASSERT(MUTEX_HELD(&candidate->ipsa_lock));
4010Sstevel@tonic-gate retval = candidate;
4020Sstevel@tonic-gate act = candact;
4030Sstevel@tonic-gate } else if (candidate != NULL) {
4040Sstevel@tonic-gate mutex_exit(&candidate->ipsa_lock);
4050Sstevel@tonic-gate }
4060Sstevel@tonic-gate ASSERT(MUTEX_HELD(&retval->ipsa_lock));
4070Sstevel@tonic-gate ASSERT(act != NULL);
4080Sstevel@tonic-gate
4090Sstevel@tonic-gate /*
4100Sstevel@tonic-gate * Even though I hold the mutex, since the reference counter is an
4110Sstevel@tonic-gate * atomic operation, I really have to use the IPSA_REFHOLD macro.
4120Sstevel@tonic-gate */
4130Sstevel@tonic-gate IPSA_REFHOLD(retval);
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate /*
4160Sstevel@tonic-gate * This association is no longer unused.
4170Sstevel@tonic-gate */
4180Sstevel@tonic-gate old_flags = retval->ipsa_flags;
4190Sstevel@tonic-gate retval->ipsa_flags |= IPSA_F_USED;
4200Sstevel@tonic-gate
4210Sstevel@tonic-gate /*
4220Sstevel@tonic-gate * Cache a reference to this SA for the fast path.
4230Sstevel@tonic-gate */
4240Sstevel@tonic-gate if (ipr != NULL) {
4250Sstevel@tonic-gate ipr->ipsr_bucket = bucket;
4260Sstevel@tonic-gate ipr->ipsr_gen = bucket->isaf_gen;
4270Sstevel@tonic-gate ipr->ipsr_sa = retval;
4280Sstevel@tonic-gate /* I'm now caching, so the cache-invalid flag goes away! */
4290Sstevel@tonic-gate retval->ipsa_flags &= ~IPSA_F_CINVALID;
4300Sstevel@tonic-gate }
4310Sstevel@tonic-gate /*
4320Sstevel@tonic-gate * Latch various things while we're here..
4330Sstevel@tonic-gate */
4340Sstevel@tonic-gate if (ipl != NULL) {
4350Sstevel@tonic-gate if (!ipl->ipl_ids_latched) {
4360Sstevel@tonic-gate ipsec_latch_ids(ipl,
4370Sstevel@tonic-gate retval->ipsa_src_cid, retval->ipsa_dst_cid);
4380Sstevel@tonic-gate }
43911042SErik.Nordmark@Sun.COM if (ixa->ixa_ipsec_action == NULL) {
4400Sstevel@tonic-gate IPACT_REFHOLD(act);
44111042SErik.Nordmark@Sun.COM ixa->ixa_ipsec_action = act;
4420Sstevel@tonic-gate }
4430Sstevel@tonic-gate }
4440Sstevel@tonic-gate
4450Sstevel@tonic-gate /*
4460Sstevel@tonic-gate * Set the uniqueness only first time.
4470Sstevel@tonic-gate */
4480Sstevel@tonic-gate if (need_unique && !(old_flags & IPSA_F_USED)) {
4490Sstevel@tonic-gate if (retval->ipsa_unique_id == 0) {
4500Sstevel@tonic-gate ASSERT((retval->ipsa_flags & IPSA_F_UNIQUE) == 0);
4510Sstevel@tonic-gate /*
4520Sstevel@tonic-gate * From now on, only this src, dst[ports, addr],
4530Sstevel@tonic-gate * proto, should use it.
4540Sstevel@tonic-gate */
4550Sstevel@tonic-gate retval->ipsa_flags |= IPSA_F_UNIQUE;
4560Sstevel@tonic-gate retval->ipsa_unique_id = unique_id;
4570Sstevel@tonic-gate retval->ipsa_unique_mask = SA_UNIQUE_MASK(
45811042SErik.Nordmark@Sun.COM ixa->ixa_ipsec_src_port, ixa->ixa_ipsec_dst_port,
4593055Sdanmcd protocol, 0);
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate
4620Sstevel@tonic-gate /*
4630Sstevel@tonic-gate * Set the source address and adjust the hash
4640Sstevel@tonic-gate * buckets only if src_addr is zero.
4650Sstevel@tonic-gate */
4660Sstevel@tonic-gate if (IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
4670Sstevel@tonic-gate /*
4680Sstevel@tonic-gate * sadb_unlinkassoc() will decrement the refcnt. Bump
4690Sstevel@tonic-gate * up when we have the lock so that we don't have to
4700Sstevel@tonic-gate * acquire locks when we come back from
4710Sstevel@tonic-gate * sadb_insertassoc().
4720Sstevel@tonic-gate *
4730Sstevel@tonic-gate * We don't need to bump the bucket's gen since
4740Sstevel@tonic-gate * we aren't moving to a new bucket.
4750Sstevel@tonic-gate */
4760Sstevel@tonic-gate IPSA_REFHOLD(retval);
4770Sstevel@tonic-gate IPSA_COPY_ADDR(retval->ipsa_srcaddr, src, af);
4780Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock);
4790Sstevel@tonic-gate sadb_unlinkassoc(retval);
4800Sstevel@tonic-gate /*
4810Sstevel@tonic-gate * Since the bucket lock is held, we know
4820Sstevel@tonic-gate * sadb_insertassoc() will succeed.
4830Sstevel@tonic-gate */
4840Sstevel@tonic-gate #ifdef DEBUG
4850Sstevel@tonic-gate if (sadb_insertassoc(retval, bucket) != 0) {
4860Sstevel@tonic-gate cmn_err(CE_PANIC,
4870Sstevel@tonic-gate "sadb_insertassoc() failed in "
4880Sstevel@tonic-gate "ipsec_getassocbyconn().\n");
4890Sstevel@tonic-gate }
4900Sstevel@tonic-gate #else /* non-DEBUG */
4910Sstevel@tonic-gate (void) sadb_insertassoc(retval, bucket);
4920Sstevel@tonic-gate #endif /* DEBUG */
4930Sstevel@tonic-gate return (retval);
4940Sstevel@tonic-gate }
4950Sstevel@tonic-gate }
4960Sstevel@tonic-gate mutex_exit(&retval->ipsa_lock);
4970Sstevel@tonic-gate
4980Sstevel@tonic-gate return (retval);
4990Sstevel@tonic-gate }
5000Sstevel@tonic-gate
5010Sstevel@tonic-gate /*
5020Sstevel@tonic-gate * Look up a security association based on the security parameters index (SPI)
5030Sstevel@tonic-gate * and address(es). This is used for inbound packets and general SA lookups
5040Sstevel@tonic-gate * (even in outbound SA tables). The source address may be ignored. Return
5050Sstevel@tonic-gate * NULL if no association is available. If an SA is found, return it, with
5060Sstevel@tonic-gate * its refcnt incremented. The caller must REFRELE after using the SA.
5070Sstevel@tonic-gate * The hash bucket must be locked down before calling.
5080Sstevel@tonic-gate */
5090Sstevel@tonic-gate ipsa_t *
ipsec_getassocbyspi(isaf_t * bucket,uint32_t spi,uint32_t * src,uint32_t * dst,sa_family_t af)5100Sstevel@tonic-gate ipsec_getassocbyspi(isaf_t *bucket, uint32_t spi, uint32_t *src, uint32_t *dst,
5110Sstevel@tonic-gate sa_family_t af)
5120Sstevel@tonic-gate {
5130Sstevel@tonic-gate ipsa_t *retval;
5140Sstevel@tonic-gate
5150Sstevel@tonic-gate ASSERT(MUTEX_HELD(&bucket->isaf_lock));
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate /*
5180Sstevel@tonic-gate * Walk the hash bucket, matching exactly on SPI, then destination,
5190Sstevel@tonic-gate * then source.
5200Sstevel@tonic-gate *
5210Sstevel@tonic-gate * Per-SA locking doesn't need to happen, because I'm only matching
5220Sstevel@tonic-gate * on addresses. Addresses are only changed during insertion/deletion
5230Sstevel@tonic-gate * from the hash bucket. Since the hash bucket lock is held, we don't
5240Sstevel@tonic-gate * need to worry about addresses changing.
5250Sstevel@tonic-gate */
5260Sstevel@tonic-gate
5270Sstevel@tonic-gate for (retval = bucket->isaf_ipsa; retval != NULL;
5280Sstevel@tonic-gate retval = retval->ipsa_next) {
5290Sstevel@tonic-gate if (retval->ipsa_spi != spi)
5300Sstevel@tonic-gate continue;
5310Sstevel@tonic-gate if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
5320Sstevel@tonic-gate continue;
5330Sstevel@tonic-gate
5340Sstevel@tonic-gate /*
5350Sstevel@tonic-gate * Assume that wildcard source addresses are inserted at the
5360Sstevel@tonic-gate * end of the hash bucket. (See sadb_insertassoc().)
5370Sstevel@tonic-gate * The following check for source addresses is a weak form
5380Sstevel@tonic-gate * of access control/source identity verification. If an
5390Sstevel@tonic-gate * SA has a source address, I only match an all-zeroes
5400Sstevel@tonic-gate * source address, or that particular one. If the SA has
5410Sstevel@tonic-gate * an all-zeroes source, then I match regardless.
5420Sstevel@tonic-gate *
5430Sstevel@tonic-gate * There is a weakness here in that a packet with all-zeroes
5440Sstevel@tonic-gate * for an address will match regardless of the source address
5450Sstevel@tonic-gate * stored in the packet.
546691Ssommerfe *
547691Ssommerfe * Note that port-level packet selectors, if present,
548691Ssommerfe * are checked in ipsec_check_ipsecin_unique().
5490Sstevel@tonic-gate */
5500Sstevel@tonic-gate if (IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) ||
5510Sstevel@tonic-gate IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af) ||
5520Sstevel@tonic-gate IPSA_IS_ADDR_UNSPEC(src, af))
5530Sstevel@tonic-gate break;
5540Sstevel@tonic-gate }
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate if (retval != NULL) {
5570Sstevel@tonic-gate /*
5580Sstevel@tonic-gate * Just refhold the return value. The caller will then
5590Sstevel@tonic-gate * make the appropriate calls to set the USED flag.
5600Sstevel@tonic-gate */
5610Sstevel@tonic-gate IPSA_REFHOLD(retval);
5620Sstevel@tonic-gate }
5630Sstevel@tonic-gate
5640Sstevel@tonic-gate return (retval);
5650Sstevel@tonic-gate }
5660Sstevel@tonic-gate
5670Sstevel@tonic-gate boolean_t
ipsec_outbound_sa(mblk_t * data_mp,ip_xmit_attr_t * ixa,uint_t proto)56811042SErik.Nordmark@Sun.COM ipsec_outbound_sa(mblk_t *data_mp, ip_xmit_attr_t *ixa, uint_t proto)
5690Sstevel@tonic-gate {
5700Sstevel@tonic-gate ipaddr_t dst;
5710Sstevel@tonic-gate uint32_t *dst_ptr, *src_ptr;
5720Sstevel@tonic-gate isaf_t *bucket;
5730Sstevel@tonic-gate ipsa_t *assoc;
57411042SErik.Nordmark@Sun.COM ip_pkt_t ipp;
5750Sstevel@tonic-gate in6_addr_t dst6;
5760Sstevel@tonic-gate ipsa_t **sa;
5770Sstevel@tonic-gate sadbp_t *sadbp;
578564Ssommerfe sadb_t *sp;
5790Sstevel@tonic-gate sa_family_t af;
58011042SErik.Nordmark@Sun.COM ip_stack_t *ipst = ixa->ixa_ipst;
58111042SErik.Nordmark@Sun.COM netstack_t *ns = ipst->ips_netstack;
5820Sstevel@tonic-gate
58311042SErik.Nordmark@Sun.COM ASSERT(ixa->ixa_flags & IXAF_IPSEC_SECURE);
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate if (proto == IPPROTO_ESP) {
5863448Sdh155122 ipsecesp_stack_t *espstack;
5873448Sdh155122
5883448Sdh155122 espstack = ns->netstack_ipsecesp;
58911042SErik.Nordmark@Sun.COM sa = &ixa->ixa_ipsec_esp_sa;
5903448Sdh155122 sadbp = &espstack->esp_sadb;
5910Sstevel@tonic-gate } else {
5923448Sdh155122 ipsecah_stack_t *ahstack;
5933448Sdh155122
5940Sstevel@tonic-gate ASSERT(proto == IPPROTO_AH);
5953448Sdh155122 ahstack = ns->netstack_ipsecah;
59611042SErik.Nordmark@Sun.COM sa = &ixa->ixa_ipsec_ah_sa;
5973448Sdh155122 sadbp = &ahstack->ah_sadb;
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate
6000Sstevel@tonic-gate ASSERT(*sa == NULL);
6010Sstevel@tonic-gate
60211042SErik.Nordmark@Sun.COM if (ixa->ixa_flags & IXAF_IS_IPV4) {
6030Sstevel@tonic-gate ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
6040Sstevel@tonic-gate
6050Sstevel@tonic-gate ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
6060Sstevel@tonic-gate dst = ip_get_dst(ipha);
607564Ssommerfe sp = &sadbp->s_v4;
6080Sstevel@tonic-gate af = AF_INET;
6090Sstevel@tonic-gate
6100Sstevel@tonic-gate /*
6110Sstevel@tonic-gate * NOTE:Getting the outbound association is considerably
6120Sstevel@tonic-gate * painful. ipsec_getassocbyconn() will require more
6130Sstevel@tonic-gate * parameters as policy implementations mature.
6140Sstevel@tonic-gate */
615564Ssommerfe bucket = OUTBOUND_BUCKET_V4(sp, dst);
6160Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src;
6170Sstevel@tonic-gate dst_ptr = (uint32_t *)&dst;
6180Sstevel@tonic-gate } else {
6190Sstevel@tonic-gate ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
6200Sstevel@tonic-gate
6210Sstevel@tonic-gate ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
6228705Sdanmcd@sun.com dst6 = ip_get_dst_v6(ip6h, data_mp, NULL);
6230Sstevel@tonic-gate af = AF_INET6;
6240Sstevel@tonic-gate
6250Sstevel@tonic-gate bzero(&ipp, sizeof (ipp));
626564Ssommerfe sp = &sadbp->s_v6;
6270Sstevel@tonic-gate
6280Sstevel@tonic-gate /* Same NOTE: applies here! */
629564Ssommerfe bucket = OUTBOUND_BUCKET_V6(sp, dst6);
6300Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src;
6310Sstevel@tonic-gate dst_ptr = (uint32_t *)&dst6;
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate
6340Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
63511042SErik.Nordmark@Sun.COM assoc = ipsec_getassocbyconn(bucket, ixa, src_ptr, dst_ptr, af,
63611042SErik.Nordmark@Sun.COM proto, ixa->ixa_tsl);
6370Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
6380Sstevel@tonic-gate
6390Sstevel@tonic-gate if (assoc == NULL)
6400Sstevel@tonic-gate return (B_FALSE);
6410Sstevel@tonic-gate
6420Sstevel@tonic-gate if (assoc->ipsa_state == IPSA_STATE_DEAD) {
6430Sstevel@tonic-gate IPSA_REFRELE(assoc);
6440Sstevel@tonic-gate return (B_FALSE);
6450Sstevel@tonic-gate }
6460Sstevel@tonic-gate
6470Sstevel@tonic-gate ASSERT(assoc->ipsa_state != IPSA_STATE_LARVAL);
6480Sstevel@tonic-gate
6490Sstevel@tonic-gate *sa = assoc;
6500Sstevel@tonic-gate return (B_TRUE);
6510Sstevel@tonic-gate }
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate /*
6540Sstevel@tonic-gate * Inbound IPsec SA selection.
65511042SErik.Nordmark@Sun.COM * Can return a pulled up mblk.
65611042SErik.Nordmark@Sun.COM * When it returns non-NULL ahp is updated
6570Sstevel@tonic-gate */
65811042SErik.Nordmark@Sun.COM mblk_t *
ipsec_inbound_ah_sa(mblk_t * mp,ip_recv_attr_t * ira,ah_t ** ahp)65911042SErik.Nordmark@Sun.COM ipsec_inbound_ah_sa(mblk_t *mp, ip_recv_attr_t *ira, ah_t **ahp)
6600Sstevel@tonic-gate {
6610Sstevel@tonic-gate ipha_t *ipha;
6620Sstevel@tonic-gate ipsa_t *assoc;
6630Sstevel@tonic-gate ah_t *ah;
6640Sstevel@tonic-gate isaf_t *hptr;
6650Sstevel@tonic-gate boolean_t isv6;
6660Sstevel@tonic-gate ip6_t *ip6h;
6670Sstevel@tonic-gate int ah_offset;
6680Sstevel@tonic-gate uint32_t *src_ptr, *dst_ptr;
6690Sstevel@tonic-gate int pullup_len;
670564Ssommerfe sadb_t *sp;
6710Sstevel@tonic-gate sa_family_t af;
67211042SErik.Nordmark@Sun.COM netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
6733448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec;
6743448Sdh155122 ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
6750Sstevel@tonic-gate
6763448Sdh155122 IP_AH_BUMP_STAT(ipss, in_requests);
6770Sstevel@tonic-gate
67811042SErik.Nordmark@Sun.COM isv6 = !(ira->ira_flags & IRAF_IS_IPV4);
6790Sstevel@tonic-gate if (isv6) {
6800Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
6810Sstevel@tonic-gate ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE);
6820Sstevel@tonic-gate } else {
6830Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr;
6840Sstevel@tonic-gate ASSERT(ipha->ipha_protocol == IPPROTO_AH);
6850Sstevel@tonic-gate ah_offset = ipha->ipha_version_and_hdr_length -
6860Sstevel@tonic-gate (uint8_t)((IP_VERSION << 4));
6870Sstevel@tonic-gate ah_offset <<= 2;
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate
6900Sstevel@tonic-gate /*
6910Sstevel@tonic-gate * We assume that the IP header is pulled up until
6920Sstevel@tonic-gate * the options. We need to see whether we have the
6930Sstevel@tonic-gate * AH header in the same mblk or not.
6940Sstevel@tonic-gate */
6950Sstevel@tonic-gate pullup_len = ah_offset + sizeof (ah_t);
6960Sstevel@tonic-gate if (mp->b_rptr + pullup_len > mp->b_wptr) {
6970Sstevel@tonic-gate if (!pullupmsg(mp, pullup_len)) {
6983448Sdh155122 ipsec_rl_strlog(ns, ip_mod_info.mi_idnum, 0, 0,
6991659Smarkfen SL_WARN | SL_ERROR,
7000Sstevel@tonic-gate "ipsec_inbound_ah_sa: Small AH header\n");
7013448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards);
70211042SErik.Nordmark@Sun.COM ip_drop_packet(mp, B_TRUE, ira->ira_ill,
7033448Sdh155122 DROPPER(ipss, ipds_ah_bad_length),
7043448Sdh155122 &ipss->ipsec_dropper);
7050Sstevel@tonic-gate return (NULL);
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate if (isv6)
7080Sstevel@tonic-gate ip6h = (ip6_t *)mp->b_rptr;
7090Sstevel@tonic-gate else
7100Sstevel@tonic-gate ipha = (ipha_t *)mp->b_rptr;
7110Sstevel@tonic-gate }
7120Sstevel@tonic-gate
7130Sstevel@tonic-gate ah = (ah_t *)(mp->b_rptr + ah_offset);
7140Sstevel@tonic-gate
7150Sstevel@tonic-gate if (isv6) {
7160Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src;
7170Sstevel@tonic-gate dst_ptr = (uint32_t *)&ip6h->ip6_dst;
7183448Sdh155122 sp = &ahstack->ah_sadb.s_v6;
7190Sstevel@tonic-gate af = AF_INET6;
7200Sstevel@tonic-gate } else {
7210Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src;
7220Sstevel@tonic-gate dst_ptr = (uint32_t *)&ipha->ipha_dst;
7233448Sdh155122 sp = &ahstack->ah_sadb.s_v4;
7240Sstevel@tonic-gate af = AF_INET;
7250Sstevel@tonic-gate }
7260Sstevel@tonic-gate
727564Ssommerfe hptr = INBOUND_BUCKET(sp, ah->ah_spi);
7280Sstevel@tonic-gate mutex_enter(&hptr->isaf_lock);
7290Sstevel@tonic-gate assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, src_ptr, dst_ptr, af);
7300Sstevel@tonic-gate mutex_exit(&hptr->isaf_lock);
7310Sstevel@tonic-gate
7327749SThejaswini.Singarajipura@Sun.COM if (assoc == NULL || assoc->ipsa_state == IPSA_STATE_DEAD ||
7337749SThejaswini.Singarajipura@Sun.COM assoc->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) {
7343448Sdh155122 IP_AH_BUMP_STAT(ipss, lookup_failure);
7353448Sdh155122 IP_AH_BUMP_STAT(ipss, in_discards);
73611042SErik.Nordmark@Sun.COM ipsecah_in_assocfailure(mp, 0,
7370Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN,
7380Sstevel@tonic-gate "ipsec_inbound_ah_sa: No association found for "
7390Sstevel@tonic-gate "spi 0x%x, dst addr %s\n",
74011042SErik.Nordmark@Sun.COM ah->ah_spi, dst_ptr, af, ira);
7410Sstevel@tonic-gate if (assoc != NULL) {
7420Sstevel@tonic-gate IPSA_REFRELE(assoc);
7430Sstevel@tonic-gate }
7440Sstevel@tonic-gate return (NULL);
7450Sstevel@tonic-gate }
7460Sstevel@tonic-gate
747*11446Sdanmcd@sun.com if (assoc->ipsa_state == IPSA_STATE_LARVAL) {
7480Sstevel@tonic-gate /* Not fully baked; swap the packet under a rock until then */
749*11446Sdanmcd@sun.com
750*11446Sdanmcd@sun.com mp = sadb_set_lpkt(assoc, mp, ira);
751*11446Sdanmcd@sun.com if (mp == NULL) {
752*11446Sdanmcd@sun.com IPSA_REFRELE(assoc);
753*11446Sdanmcd@sun.com return (NULL);
754*11446Sdanmcd@sun.com }
755*11446Sdanmcd@sun.com /* Looks like the SA is no longer LARVAL. */
7560Sstevel@tonic-gate }
7570Sstevel@tonic-gate
75811042SErik.Nordmark@Sun.COM /* Are the IPsec fields initialized at all? */
75911042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
76011042SErik.Nordmark@Sun.COM ira->ira_ipsec_action = NULL;
76111042SErik.Nordmark@Sun.COM ira->ira_ipsec_ah_sa = NULL;
76211042SErik.Nordmark@Sun.COM ira->ira_ipsec_esp_sa = NULL;
76311042SErik.Nordmark@Sun.COM }
76411042SErik.Nordmark@Sun.COM
7650Sstevel@tonic-gate /*
7660Sstevel@tonic-gate * Save a reference to the association so that it can
7670Sstevel@tonic-gate * be retrieved after execution. We free any AH SA reference
7680Sstevel@tonic-gate * already there (innermost SA "wins". The reference to
7690Sstevel@tonic-gate * the SA will also be used later when doing the policy checks.
7700Sstevel@tonic-gate */
77111042SErik.Nordmark@Sun.COM if (ira->ira_ipsec_ah_sa != NULL) {
77211042SErik.Nordmark@Sun.COM IPSA_REFRELE(ira->ira_ipsec_ah_sa);
7730Sstevel@tonic-gate }
77411042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_IPSEC_SECURE;
77511042SErik.Nordmark@Sun.COM ira->ira_ipsec_ah_sa = assoc;
7760Sstevel@tonic-gate
77711042SErik.Nordmark@Sun.COM *ahp = ah;
77811042SErik.Nordmark@Sun.COM return (mp);
7790Sstevel@tonic-gate }
7800Sstevel@tonic-gate
78111042SErik.Nordmark@Sun.COM /*
78211042SErik.Nordmark@Sun.COM * Can return a pulled up mblk.
78311042SErik.Nordmark@Sun.COM * When it returns non-NULL esphp is updated
78411042SErik.Nordmark@Sun.COM */
78511042SErik.Nordmark@Sun.COM mblk_t *
ipsec_inbound_esp_sa(mblk_t * data_mp,ip_recv_attr_t * ira,esph_t ** esphp)78611042SErik.Nordmark@Sun.COM ipsec_inbound_esp_sa(mblk_t *data_mp, ip_recv_attr_t *ira, esph_t **esphp)
7870Sstevel@tonic-gate {
78811042SErik.Nordmark@Sun.COM mblk_t *placeholder;
7890Sstevel@tonic-gate uint32_t *src_ptr, *dst_ptr;
7900Sstevel@tonic-gate ipha_t *ipha;
7910Sstevel@tonic-gate ip6_t *ip6h;
7920Sstevel@tonic-gate esph_t *esph;
7930Sstevel@tonic-gate ipsa_t *ipsa;
7940Sstevel@tonic-gate isaf_t *bucket;
7950Sstevel@tonic-gate uint_t preamble;
7960Sstevel@tonic-gate sa_family_t af;
7970Sstevel@tonic-gate boolean_t isv6;
798564Ssommerfe sadb_t *sp;
79911042SErik.Nordmark@Sun.COM netstack_t *ns = ira->ira_ill->ill_ipst->ips_netstack;
8003448Sdh155122 ipsec_stack_t *ipss = ns->netstack_ipsec;
8013448Sdh155122 ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
8020Sstevel@tonic-gate
8033448Sdh155122 IP_ESP_BUMP_STAT(ipss, in_requests);
8040Sstevel@tonic-gate
80511042SErik.Nordmark@Sun.COM isv6 = !(ira->ira_flags & IRAF_IS_IPV4);
8060Sstevel@tonic-gate if (isv6) {
8070Sstevel@tonic-gate ip6h = (ip6_t *)data_mp->b_rptr;
8080Sstevel@tonic-gate } else {
8090Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr;
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate /*
8130Sstevel@tonic-gate * Put all data into one mblk if it's not there already.
8140Sstevel@tonic-gate * XXX This is probably bad long-term. Figure out better ways of doing
8150Sstevel@tonic-gate * this. Much of the inbound path depends on all of the data being
8160Sstevel@tonic-gate * in one mblk.
8170Sstevel@tonic-gate *
8180Sstevel@tonic-gate * XXX Jumbogram issues will have to be dealt with here.
8190Sstevel@tonic-gate * If the plen is 0, we'll have to scan for a HBH header with the
8200Sstevel@tonic-gate * actual packet length.
8210Sstevel@tonic-gate */
8220Sstevel@tonic-gate if (data_mp->b_datap->db_ref > 1 ||
82311042SErik.Nordmark@Sun.COM (data_mp->b_wptr - data_mp->b_rptr) < ira->ira_pktlen) {
8240Sstevel@tonic-gate placeholder = msgpullup(data_mp, -1);
8250Sstevel@tonic-gate if (placeholder == NULL) {
8263448Sdh155122 IP_ESP_BUMP_STAT(ipss, in_discards);
82711042SErik.Nordmark@Sun.COM ip_drop_packet(data_mp, B_TRUE, ira->ira_ill,
8283448Sdh155122 DROPPER(ipss, ipds_esp_nomem),
8293448Sdh155122 &ipss->ipsec_dropper);
8300Sstevel@tonic-gate return (NULL);
8310Sstevel@tonic-gate } else {
8320Sstevel@tonic-gate /* Reset packet with new pulled up mblk. */
8330Sstevel@tonic-gate freemsg(data_mp);
8340Sstevel@tonic-gate data_mp = placeholder;
8350Sstevel@tonic-gate }
8360Sstevel@tonic-gate }
8370Sstevel@tonic-gate
8380Sstevel@tonic-gate /*
8390Sstevel@tonic-gate * Find the ESP header, point the address pointers at the appropriate
8400Sstevel@tonic-gate * IPv4/IPv6 places.
8410Sstevel@tonic-gate */
8420Sstevel@tonic-gate if (isv6) {
8430Sstevel@tonic-gate ip6h = (ip6_t *)data_mp->b_rptr;
8440Sstevel@tonic-gate src_ptr = (uint32_t *)&ip6h->ip6_src;
8450Sstevel@tonic-gate dst_ptr = (uint32_t *)&ip6h->ip6_dst;
8460Sstevel@tonic-gate if (ip6h->ip6_nxt != IPPROTO_ESP) {
8470Sstevel@tonic-gate /* There are options that need to be processed. */
8480Sstevel@tonic-gate preamble = ip_hdr_length_v6(data_mp, ip6h);
8490Sstevel@tonic-gate } else {
8500Sstevel@tonic-gate preamble = sizeof (ip6_t);
8510Sstevel@tonic-gate }
8520Sstevel@tonic-gate
8533448Sdh155122 sp = &espstack->esp_sadb.s_v6;
8540Sstevel@tonic-gate af = AF_INET6;
8550Sstevel@tonic-gate } else {
8560Sstevel@tonic-gate ipha = (ipha_t *)data_mp->b_rptr;
8570Sstevel@tonic-gate src_ptr = (uint32_t *)&ipha->ipha_src;
8580Sstevel@tonic-gate dst_ptr = (uint32_t *)&ipha->ipha_dst;
8590Sstevel@tonic-gate preamble = IPH_HDR_LENGTH(ipha);
8600Sstevel@tonic-gate
8613448Sdh155122 sp = &espstack->esp_sadb.s_v4;
8620Sstevel@tonic-gate af = AF_INET;
8630Sstevel@tonic-gate }
8640Sstevel@tonic-gate
8650Sstevel@tonic-gate esph = (esph_t *)(data_mp->b_rptr + preamble);
8660Sstevel@tonic-gate
8670Sstevel@tonic-gate /* Since hash is common on inbound (SPI value), hash here. */
868564Ssommerfe bucket = INBOUND_BUCKET(sp, esph->esph_spi);
8690Sstevel@tonic-gate mutex_enter(&bucket->isaf_lock);
8700Sstevel@tonic-gate ipsa = ipsec_getassocbyspi(bucket, esph->esph_spi, src_ptr, dst_ptr,
8710Sstevel@tonic-gate af);
8720Sstevel@tonic-gate mutex_exit(&bucket->isaf_lock);
8730Sstevel@tonic-gate
8747749SThejaswini.Singarajipura@Sun.COM if (ipsa == NULL || ipsa->ipsa_state == IPSA_STATE_DEAD ||
8757749SThejaswini.Singarajipura@Sun.COM ipsa->ipsa_state == IPSA_STATE_ACTIVE_ELSEWHERE) {
8760Sstevel@tonic-gate /* This is a loggable error! AUDIT ME! */
8773448Sdh155122 IP_ESP_BUMP_STAT(ipss, lookup_failure);
8783448Sdh155122 IP_ESP_BUMP_STAT(ipss, in_discards);
87911042SErik.Nordmark@Sun.COM ipsecesp_in_assocfailure(data_mp, 0,
8800Sstevel@tonic-gate SL_ERROR | SL_CONSOLE | SL_WARN,
8810Sstevel@tonic-gate "ipsec_inbound_esp_sa: No association found for "
8820Sstevel@tonic-gate "spi 0x%x, dst addr %s\n",
88311042SErik.Nordmark@Sun.COM esph->esph_spi, dst_ptr, af, ira);
8840Sstevel@tonic-gate if (ipsa != NULL) {
8850Sstevel@tonic-gate IPSA_REFRELE(ipsa);
8860Sstevel@tonic-gate }
8870Sstevel@tonic-gate return (NULL);
8880Sstevel@tonic-gate }
8890Sstevel@tonic-gate
890*11446Sdanmcd@sun.com if (ipsa->ipsa_state == IPSA_STATE_LARVAL) {
8910Sstevel@tonic-gate /* Not fully baked; swap the packet under a rock until then */
892*11446Sdanmcd@sun.com
893*11446Sdanmcd@sun.com data_mp = sadb_set_lpkt(ipsa, data_mp, ira);
894*11446Sdanmcd@sun.com if (data_mp == NULL) {
895*11446Sdanmcd@sun.com IPSA_REFRELE(ipsa);
896*11446Sdanmcd@sun.com return (NULL);
897*11446Sdanmcd@sun.com }
898*11446Sdanmcd@sun.com /* Looks like the SA is no longer LARVAL. */
8990Sstevel@tonic-gate }
9000Sstevel@tonic-gate
90111042SErik.Nordmark@Sun.COM /* Are the IPsec fields initialized at all? */
90211042SErik.Nordmark@Sun.COM if (!(ira->ira_flags & IRAF_IPSEC_SECURE)) {
90311042SErik.Nordmark@Sun.COM ira->ira_ipsec_action = NULL;
90411042SErik.Nordmark@Sun.COM ira->ira_ipsec_ah_sa = NULL;
90511042SErik.Nordmark@Sun.COM ira->ira_ipsec_esp_sa = NULL;
90611042SErik.Nordmark@Sun.COM }
90711042SErik.Nordmark@Sun.COM
9080Sstevel@tonic-gate /*
9090Sstevel@tonic-gate * Save a reference to the association so that it can
9100Sstevel@tonic-gate * be retrieved after execution. We free any AH SA reference
9110Sstevel@tonic-gate * already there (innermost SA "wins". The reference to
9120Sstevel@tonic-gate * the SA will also be used later when doing the policy checks.
9130Sstevel@tonic-gate */
91411042SErik.Nordmark@Sun.COM if (ira->ira_ipsec_esp_sa != NULL) {
91511042SErik.Nordmark@Sun.COM IPSA_REFRELE(ira->ira_ipsec_esp_sa);
9160Sstevel@tonic-gate }
91711042SErik.Nordmark@Sun.COM ira->ira_flags |= IRAF_IPSEC_SECURE;
91811042SErik.Nordmark@Sun.COM ira->ira_ipsec_esp_sa = ipsa;
9190Sstevel@tonic-gate
92011042SErik.Nordmark@Sun.COM *esphp = esph;
92111042SErik.Nordmark@Sun.COM return (data_mp);
9220Sstevel@tonic-gate }
923