xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_sadb.c (revision 3055:e5701846929e)
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 /*
221659Smarkfen  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/types.h>
290Sstevel@tonic-gate #include <sys/stream.h>
300Sstevel@tonic-gate #include <sys/sunddi.h>
31*3055Sdanmcd #include <sys/ddi.h>
320Sstevel@tonic-gate #include <sys/strlog.h>
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #include <inet/common.h>
350Sstevel@tonic-gate #include <inet/mib2.h>
360Sstevel@tonic-gate #include <inet/ip.h>
370Sstevel@tonic-gate #include <inet/ip6.h>
380Sstevel@tonic-gate #include <inet/ipdrop.h>
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #include <net/pfkeyv2.h>
410Sstevel@tonic-gate #include <inet/ipsec_info.h>
420Sstevel@tonic-gate #include <inet/sadb.h>
430Sstevel@tonic-gate #include <inet/ipsec_impl.h>
440Sstevel@tonic-gate #include <inet/ipsecesp.h>
450Sstevel@tonic-gate #include <inet/ipsecah.h>
460Sstevel@tonic-gate #include <sys/kstat.h>
470Sstevel@tonic-gate 
480Sstevel@tonic-gate /* stats */
490Sstevel@tonic-gate static kstat_t *ipsec_ksp;
500Sstevel@tonic-gate ipsec_kstats_t *ipsec_kstats;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /* The IPsec SADBs for AH and ESP */
530Sstevel@tonic-gate sadbp_t ah_sadb, esp_sadb;
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /* Packet dropper for IP IPsec processing failures */
560Sstevel@tonic-gate extern ipdropper_t ip_dropper;
570Sstevel@tonic-gate 
580Sstevel@tonic-gate void
590Sstevel@tonic-gate ipsec_kstat_init(void)
600Sstevel@tonic-gate {
610Sstevel@tonic-gate 	ipsec_ksp = kstat_create("ip", 0, "ipsec_stat", "net",
620Sstevel@tonic-gate 	    KSTAT_TYPE_NAMED, sizeof (*ipsec_kstats) / sizeof (kstat_named_t),
630Sstevel@tonic-gate 	    KSTAT_FLAG_PERSISTENT);
640Sstevel@tonic-gate 
650Sstevel@tonic-gate 	ASSERT(ipsec_ksp != NULL);
660Sstevel@tonic-gate 
670Sstevel@tonic-gate 	ipsec_kstats = ipsec_ksp->ks_data;
680Sstevel@tonic-gate 
690Sstevel@tonic-gate #define	KI(x) kstat_named_init(&ipsec_kstats->x, #x, KSTAT_DATA_UINT64)
700Sstevel@tonic-gate 	KI(esp_stat_in_requests);
710Sstevel@tonic-gate 	KI(esp_stat_in_discards);
720Sstevel@tonic-gate 	KI(esp_stat_lookup_failure);
730Sstevel@tonic-gate 	KI(ah_stat_in_requests);
740Sstevel@tonic-gate 	KI(ah_stat_in_discards);
750Sstevel@tonic-gate 	KI(ah_stat_lookup_failure);
761659Smarkfen 	KI(sadb_acquire_maxpackets);
771659Smarkfen 	KI(sadb_acquire_qhiwater);
780Sstevel@tonic-gate #undef KI
790Sstevel@tonic-gate 
800Sstevel@tonic-gate 	kstat_install(ipsec_ksp);
810Sstevel@tonic-gate }
820Sstevel@tonic-gate 
830Sstevel@tonic-gate void
840Sstevel@tonic-gate ipsec_kstat_destroy(void)
850Sstevel@tonic-gate {
860Sstevel@tonic-gate 	kstat_delete(ipsec_ksp);
870Sstevel@tonic-gate }
880Sstevel@tonic-gate 
890Sstevel@tonic-gate /*
900Sstevel@tonic-gate  * Returns B_TRUE if the identities in the SA match the identities
910Sstevel@tonic-gate  * in the "latch" structure.
920Sstevel@tonic-gate  */
930Sstevel@tonic-gate 
940Sstevel@tonic-gate static boolean_t
950Sstevel@tonic-gate ipsec_match_outbound_ids(ipsec_latch_t *ipl, ipsa_t *sa)
960Sstevel@tonic-gate {
970Sstevel@tonic-gate 	ASSERT(ipl->ipl_ids_latched == B_TRUE);
980Sstevel@tonic-gate 	return ipsid_equal(ipl->ipl_local_cid, sa->ipsa_src_cid) &&
990Sstevel@tonic-gate 	    ipsid_equal(ipl->ipl_remote_cid, sa->ipsa_dst_cid);
1000Sstevel@tonic-gate }
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate /*
1030Sstevel@tonic-gate  * Look up a security association based on the unique ID generated by IP and
104*3055Sdanmcd  * transport or tunnel information, such as ports and upper-layer protocol,
105*3055Sdanmcd  * and the inner and outer address(es).	 Used for uniqueness testing and
106*3055Sdanmcd  * outbound packets.  The outer source address may be ignored.
1070Sstevel@tonic-gate  *
1080Sstevel@tonic-gate  * I expect an SA hash bucket, and that its per-bucket mutex is held.
1090Sstevel@tonic-gate  * The SA ptr I return will have its reference count incremented by one.
1100Sstevel@tonic-gate  */
1110Sstevel@tonic-gate ipsa_t *
1120Sstevel@tonic-gate ipsec_getassocbyconn(isaf_t *bucket, ipsec_out_t *io, uint32_t *src,
1130Sstevel@tonic-gate     uint32_t *dst, sa_family_t af, uint8_t protocol)
1140Sstevel@tonic-gate {
1150Sstevel@tonic-gate 	ipsa_t *retval, *candidate;
1160Sstevel@tonic-gate 	ipsec_action_t *candact;
1170Sstevel@tonic-gate 	boolean_t need_unique;
118*3055Sdanmcd 	boolean_t tunnel_mode = io->ipsec_out_tunnel;
1190Sstevel@tonic-gate 	uint64_t unique_id;
1200Sstevel@tonic-gate 	uint32_t old_flags, excludeflags;
1210Sstevel@tonic-gate 	ipsec_policy_t *pp = io->ipsec_out_policy;
1220Sstevel@tonic-gate 	ipsec_action_t *actlist = io->ipsec_out_act;
1230Sstevel@tonic-gate 	ipsec_action_t *act;
1240Sstevel@tonic-gate 	ipsec_latch_t *ipl = io->ipsec_out_latch;
1250Sstevel@tonic-gate 	ipsa_ref_t *ipr = NULL;
126*3055Sdanmcd 	sa_family_t inaf = io->ipsec_out_inaf;
127*3055Sdanmcd 	uint32_t *insrc = io->ipsec_out_insrc;
128*3055Sdanmcd 	uint32_t *indst = io->ipsec_out_indst;
129*3055Sdanmcd 	uint8_t insrcpfx = io->ipsec_out_insrcpfx;
130*3055Sdanmcd 	uint8_t indstpfx = io->ipsec_out_indstpfx;
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	/*
135*3055Sdanmcd 	 * Caller must set ipsec_out_t structure such that we know
136*3055Sdanmcd 	 * whether this is tunnel mode or transport mode based on
137*3055Sdanmcd 	 * io->ipsec_out_tunnel.  If this flag is set, we assume that
138*3055Sdanmcd 	 * there are valid inner src and destination addresses to compare.
139*3055Sdanmcd 	 */
140*3055Sdanmcd 
141*3055Sdanmcd 	/*
1420Sstevel@tonic-gate 	 * Fast path: do we have a latch structure, is it for this bucket,
1430Sstevel@tonic-gate 	 * and does the generation number match?  If so, refhold and return.
1440Sstevel@tonic-gate 	 */
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	if (ipl != NULL) {
1470Sstevel@tonic-gate 		ASSERT((protocol == IPPROTO_AH) || (protocol == IPPROTO_ESP));
1480Sstevel@tonic-gate 		ipr = &ipl->ipl_ref[protocol - IPPROTO_ESP];
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 		retval = ipr->ipsr_sa;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 		/*
1530Sstevel@tonic-gate 		 * NOTE: The isaf_gen check (incremented upon
1540Sstevel@tonic-gate 		 * sadb_unlinkassoc()) protects against retval being a freed
1550Sstevel@tonic-gate 		 * SA.  (We're exploiting short-circuit evaluation.)
1560Sstevel@tonic-gate 		 */
1570Sstevel@tonic-gate 		if ((bucket == ipr->ipsr_bucket) &&
1580Sstevel@tonic-gate 		    (bucket->isaf_gen == ipr->ipsr_gen) &&
1590Sstevel@tonic-gate 		    (retval->ipsa_state != IPSA_STATE_DEAD) &&
1600Sstevel@tonic-gate 		    !(retval->ipsa_flags & IPSA_F_CINVALID)) {
1610Sstevel@tonic-gate 			IPSA_REFHOLD(retval);
1620Sstevel@tonic-gate 			return (retval);
1630Sstevel@tonic-gate 		}
1640Sstevel@tonic-gate 	}
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	ASSERT((pp != NULL) || (actlist != NULL));
1670Sstevel@tonic-gate 	if (actlist == NULL)
1680Sstevel@tonic-gate 		actlist = pp->ipsp_act;
1690Sstevel@tonic-gate 	ASSERT(actlist != NULL);
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	need_unique = actlist->ipa_want_unique;
1720Sstevel@tonic-gate 	unique_id = SA_FORM_UNIQUE_ID(io);
1730Sstevel@tonic-gate 
1740Sstevel@tonic-gate 	/*
1750Sstevel@tonic-gate 	 * Precompute mask for SA flags comparison: If we need a
1760Sstevel@tonic-gate 	 * unique SA and an SA has already been used, or if the SA has
1770Sstevel@tonic-gate 	 * a unique value which doesn't match, we aren't interested in
1780Sstevel@tonic-gate 	 * the SA..
1790Sstevel@tonic-gate 	 */
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 	excludeflags = IPSA_F_UNIQUE;
1820Sstevel@tonic-gate 	if (need_unique)
1830Sstevel@tonic-gate 		excludeflags |= IPSA_F_USED;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 	/*
1860Sstevel@tonic-gate 	 * Walk the hash bucket, matching on:
1870Sstevel@tonic-gate 	 *
1880Sstevel@tonic-gate 	 * - unique_id
1890Sstevel@tonic-gate 	 * - destination
1900Sstevel@tonic-gate 	 * - source
1910Sstevel@tonic-gate 	 * - algorithms
192*3055Sdanmcd 	 * - inner dst
193*3055Sdanmcd 	 * - inner src
1940Sstevel@tonic-gate 	 * - <MORE TBD>
1950Sstevel@tonic-gate 	 *
1960Sstevel@tonic-gate 	 * Make sure that wildcard sources are inserted at the end of the hash
1970Sstevel@tonic-gate 	 * bucket.
1980Sstevel@tonic-gate 	 *
1990Sstevel@tonic-gate 	 * DEFINITIONS:	A _shared_ SA is one with unique_id == 0 and USED.
2000Sstevel@tonic-gate 	 *		An _unused_ SA is one with unique_id == 0 and not USED.
2010Sstevel@tonic-gate 	 *		A _unique_ SA is one with unique_id != 0 and USED.
2020Sstevel@tonic-gate 	 *		An SA with unique_id != 0 and not USED never happens.
2030Sstevel@tonic-gate 	 */
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	candidate = NULL;
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 	for (retval = bucket->isaf_ipsa; retval != NULL;
2080Sstevel@tonic-gate 	    retval = retval->ipsa_next) {
2090Sstevel@tonic-gate 		ASSERT((candidate == NULL) ||
2100Sstevel@tonic-gate 		    MUTEX_HELD(&candidate->ipsa_lock));
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 		/*
2130Sstevel@tonic-gate 		 * Q: Should I lock this SA?
2140Sstevel@tonic-gate 		 * A: For now, yes.  I change and use too many fields in here
2150Sstevel@tonic-gate 		 *    (e.g. unique_id) that I may be racing with other threads.
2160Sstevel@tonic-gate 		 *    Also, the refcnt needs to be bumped up.
2170Sstevel@tonic-gate 		 */
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 		mutex_enter(&retval->ipsa_lock);
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 		/* My apologies for the use of goto instead of continue. */
222*3055Sdanmcd 
223*3055Sdanmcd 		/* Outer destination address */
2240Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
2250Sstevel@tonic-gate 			goto next_ipsa;	/* Destination mismatch. */
226*3055Sdanmcd 
227*3055Sdanmcd 		/* Outer source address */
2280Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) &&
2290Sstevel@tonic-gate 		    !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af))
2300Sstevel@tonic-gate 			goto next_ipsa;	/* Specific source and not matched. */
2310Sstevel@tonic-gate 
232*3055Sdanmcd 		if (tunnel_mode) {
233*3055Sdanmcd 			/* Check tunnel mode */
234*3055Sdanmcd 			if (!(retval->ipsa_flags & IPSA_F_TUNNEL))
235*3055Sdanmcd 				goto next_ipsa; /* Not tunnel mode SA */
236*3055Sdanmcd 
237*3055Sdanmcd 			/* Inner destination address */
238*3055Sdanmcd 			if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innerdst, inaf)) {
239*3055Sdanmcd 				if (!ip_addr_match((uint8_t *)indst,
240*3055Sdanmcd 				    min(indstpfx, retval->ipsa_innerdstpfx),
241*3055Sdanmcd 				    (in6_addr_t *)retval->ipsa_innerdst))
242*3055Sdanmcd 					goto next_ipsa; /* not matched. */
243*3055Sdanmcd 			}
244*3055Sdanmcd 
245*3055Sdanmcd 			/* Inner source address */
246*3055Sdanmcd 			if (!IPSA_IS_ADDR_UNSPEC(retval->ipsa_innersrc, inaf)) {
247*3055Sdanmcd 				if (!ip_addr_match((uint8_t *)insrc,
248*3055Sdanmcd 				    min(insrcpfx, retval->ipsa_innersrcpfx),
249*3055Sdanmcd 				    (in6_addr_t *)retval->ipsa_innersrc))
250*3055Sdanmcd 					goto next_ipsa; /* not matched. */
251*3055Sdanmcd 			}
252*3055Sdanmcd 		} else {
253*3055Sdanmcd 			/* Check transport mode */
254*3055Sdanmcd 			if (retval->ipsa_flags & IPSA_F_TUNNEL)
255*3055Sdanmcd 				goto next_ipsa; /* Not transport mode SA */
256*3055Sdanmcd 
257*3055Sdanmcd 			/*
258*3055Sdanmcd 			 * TODO - If we ever do RFC 3884's dream of transport-
259*3055Sdanmcd 			 * mode SAs with inner IP address selectors, we need
260*3055Sdanmcd 			 * to put some code here.
261*3055Sdanmcd 			 */
262*3055Sdanmcd 		}
263*3055Sdanmcd 
2640Sstevel@tonic-gate 		/*
2650Sstevel@tonic-gate 		 * XXX should be able to use cached/latched action
2660Sstevel@tonic-gate 		 * to dodge this loop
2670Sstevel@tonic-gate 		 */
2680Sstevel@tonic-gate 		for (act = actlist; act != NULL; act = act->ipa_next) {
2690Sstevel@tonic-gate 			ipsec_act_t *ap = &act->ipa_act;
2700Sstevel@tonic-gate 			if (ap->ipa_type != IPSEC_POLICY_APPLY)
2710Sstevel@tonic-gate 				continue;
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 			/*
2740Sstevel@tonic-gate 			 * XXX ugly.  should be better way to do this test
2750Sstevel@tonic-gate 			 */
2760Sstevel@tonic-gate 			if (protocol == IPPROTO_AH) {
2770Sstevel@tonic-gate 				if (!(ap->ipa_apply.ipp_use_ah))
2780Sstevel@tonic-gate 					continue;
2790Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_auth_alg !=
2800Sstevel@tonic-gate 				    retval->ipsa_auth_alg)
2810Sstevel@tonic-gate 					continue;
2820Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_ah_minbits >
2830Sstevel@tonic-gate 					retval->ipsa_authkeybits)
2840Sstevel@tonic-gate 					continue;
2850Sstevel@tonic-gate 			} else {
2860Sstevel@tonic-gate 				if (!(ap->ipa_apply.ipp_use_esp))
2870Sstevel@tonic-gate 					continue;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 				if ((ap->ipa_apply.ipp_encr_alg !=
2900Sstevel@tonic-gate 				    retval->ipsa_encr_alg))
2910Sstevel@tonic-gate 					continue;
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_espe_minbits >
2940Sstevel@tonic-gate 				    retval->ipsa_encrkeybits)
2950Sstevel@tonic-gate 					continue;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_esp_auth_alg != 0) {
2980Sstevel@tonic-gate 					if (ap->ipa_apply.ipp_esp_auth_alg !=
2990Sstevel@tonic-gate 					    retval->ipsa_auth_alg)
3000Sstevel@tonic-gate 						continue;
3010Sstevel@tonic-gate 					if (ap->ipa_apply.ipp_espa_minbits >
3020Sstevel@tonic-gate 					    retval->ipsa_authkeybits)
3030Sstevel@tonic-gate 						continue;
3040Sstevel@tonic-gate 				}
3050Sstevel@tonic-gate 			}
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 			/*
3080Sstevel@tonic-gate 			 * Check key mgmt proto, cookie
3090Sstevel@tonic-gate 			 */
3100Sstevel@tonic-gate 			if ((ap->ipa_apply.ipp_km_proto != 0) &&
3110Sstevel@tonic-gate 			    (retval->ipsa_kmp != 0) &&
3120Sstevel@tonic-gate 			    (ap->ipa_apply.ipp_km_proto != retval->ipsa_kmp))
3130Sstevel@tonic-gate 				continue;
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 			if ((ap->ipa_apply.ipp_km_cookie != 0) &&
3160Sstevel@tonic-gate 			    (retval->ipsa_kmc != 0) &&
3170Sstevel@tonic-gate 			    (ap->ipa_apply.ipp_km_cookie != retval->ipsa_kmc))
3180Sstevel@tonic-gate 				continue;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 			break;
3210Sstevel@tonic-gate 		}
3220Sstevel@tonic-gate 		if (act == NULL)
3230Sstevel@tonic-gate 			goto next_ipsa;	/* nothing matched */
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 		/*
3260Sstevel@tonic-gate 		 * Do identities match?
3270Sstevel@tonic-gate 		 */
3280Sstevel@tonic-gate 		if (ipl && ipl->ipl_ids_latched &&
3290Sstevel@tonic-gate 		    !ipsec_match_outbound_ids(ipl, retval))
3300Sstevel@tonic-gate 			goto next_ipsa;
3310Sstevel@tonic-gate 
3320Sstevel@tonic-gate 		/*
3330Sstevel@tonic-gate 		 * At this point, we know that we have at least a match on:
3340Sstevel@tonic-gate 		 *
3350Sstevel@tonic-gate 		 * - dest
3360Sstevel@tonic-gate 		 * - source (if source is specified, i.e. non-zeroes)
337*3055Sdanmcd 		 * - inner dest (if specified)
338*3055Sdanmcd 		 * - inner source (if specified)
3390Sstevel@tonic-gate 		 * - auth alg (if auth alg is specified, i.e. non-zero)
3400Sstevel@tonic-gate 		 * - encrypt. alg (if encrypt. alg is specified, i.e. non-zero)
3410Sstevel@tonic-gate 		 * and we know that the SA keylengths are appropriate.
3420Sstevel@tonic-gate 		 *
3430Sstevel@tonic-gate 		 * (Keep in mind known-src SAs are hit before zero-src SAs,
3440Sstevel@tonic-gate 		 * thanks to sadb_insertassoc().)
3450Sstevel@tonic-gate 		 * If we need a unique asssociation, optimally we have
3460Sstevel@tonic-gate 		 * ipsa_unique_id == unique_id, otherwise NOT USED
3470Sstevel@tonic-gate 		 * is held in reserve (stored in candidate).
3480Sstevel@tonic-gate 		 *
3490Sstevel@tonic-gate 		 * For those stored in candidate, take best-match (i.e. given
3500Sstevel@tonic-gate 		 * a choice, candidate should have non-zero ipsa_src).
3510Sstevel@tonic-gate 		 */
3520Sstevel@tonic-gate 
3530Sstevel@tonic-gate 		/*
3540Sstevel@tonic-gate 		 * If SA has a unique value which matches, we're all set...
3550Sstevel@tonic-gate 		 * "key management knows best"
3560Sstevel@tonic-gate 		 */
3570Sstevel@tonic-gate 		if ((retval->ipsa_flags & IPSA_F_UNIQUE) &&
3580Sstevel@tonic-gate 		    ((unique_id & retval->ipsa_unique_mask) ==
3590Sstevel@tonic-gate 			retval->ipsa_unique_id))
3600Sstevel@tonic-gate 			break;
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 		/*
3630Sstevel@tonic-gate 		 * If we need a unique SA and this SA has already been used,
3640Sstevel@tonic-gate 		 * or if the SA has a unique value which doesn't match,
3650Sstevel@tonic-gate 		 * this isn't for us.
3660Sstevel@tonic-gate 		 */
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 		if (retval->ipsa_flags & excludeflags)
3690Sstevel@tonic-gate 			goto next_ipsa;
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 
3720Sstevel@tonic-gate 		/*
3730Sstevel@tonic-gate 		 * I found a candidate..
3740Sstevel@tonic-gate 		 */
3750Sstevel@tonic-gate 		if (candidate == NULL) {
3760Sstevel@tonic-gate 			/*
3770Sstevel@tonic-gate 			 * and didn't already have one..
3780Sstevel@tonic-gate 			 */
3790Sstevel@tonic-gate 			candidate = retval;
3800Sstevel@tonic-gate 			candact = act;
3810Sstevel@tonic-gate 			continue;
3820Sstevel@tonic-gate 		} else {
3830Sstevel@tonic-gate 			/*
3840Sstevel@tonic-gate 			 * If candidate's source address is zero and
3850Sstevel@tonic-gate 			 * the current match (i.e. retval) address is
3860Sstevel@tonic-gate 			 * not zero, we have a better candidate..
3870Sstevel@tonic-gate 			 */
3880Sstevel@tonic-gate 			if (IPSA_IS_ADDR_UNSPEC(candidate->ipsa_srcaddr, af) &&
3890Sstevel@tonic-gate 			    !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
3900Sstevel@tonic-gate 				mutex_exit(&candidate->ipsa_lock);
3910Sstevel@tonic-gate 				candidate = retval;
3920Sstevel@tonic-gate 				candact = act;
3930Sstevel@tonic-gate 				continue;
3940Sstevel@tonic-gate 			}
3950Sstevel@tonic-gate 		}
3960Sstevel@tonic-gate next_ipsa:
3970Sstevel@tonic-gate 		mutex_exit(&retval->ipsa_lock);
3980Sstevel@tonic-gate 	}
3990Sstevel@tonic-gate 	ASSERT((retval == NULL) || MUTEX_HELD(&retval->ipsa_lock));
4000Sstevel@tonic-gate 	ASSERT((candidate == NULL) || MUTEX_HELD(&candidate->ipsa_lock));
4010Sstevel@tonic-gate 	ASSERT((retval == NULL) || (act != NULL));
4020Sstevel@tonic-gate 	ASSERT((candidate == NULL) || (candact != NULL));
4030Sstevel@tonic-gate 
4040Sstevel@tonic-gate 	/* Let caller react to a lookup failure when it gets NULL. */
4050Sstevel@tonic-gate 	if (retval == NULL && candidate == NULL)
4060Sstevel@tonic-gate 		return (NULL);
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate 	if (retval == NULL) {
4090Sstevel@tonic-gate 		ASSERT(MUTEX_HELD(&candidate->ipsa_lock));
4100Sstevel@tonic-gate 		retval = candidate;
4110Sstevel@tonic-gate 		act = candact;
4120Sstevel@tonic-gate 	} else if (candidate != NULL) {
4130Sstevel@tonic-gate 		mutex_exit(&candidate->ipsa_lock);
4140Sstevel@tonic-gate 	}
4150Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&retval->ipsa_lock));
4160Sstevel@tonic-gate 	ASSERT(act != NULL);
4170Sstevel@tonic-gate 
4180Sstevel@tonic-gate 	/*
4190Sstevel@tonic-gate 	 * Even though I hold the mutex, since the reference counter is an
4200Sstevel@tonic-gate 	 * atomic operation, I really have to use the IPSA_REFHOLD macro.
4210Sstevel@tonic-gate 	 */
4220Sstevel@tonic-gate 	IPSA_REFHOLD(retval);
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate 	/*
4250Sstevel@tonic-gate 	 * This association is no longer unused.
4260Sstevel@tonic-gate 	 */
4270Sstevel@tonic-gate 	old_flags = retval->ipsa_flags;
4280Sstevel@tonic-gate 	retval->ipsa_flags |= IPSA_F_USED;
4290Sstevel@tonic-gate 
4300Sstevel@tonic-gate 	/*
4310Sstevel@tonic-gate 	 * Cache a reference to this SA for the fast path.
4320Sstevel@tonic-gate 	 */
4330Sstevel@tonic-gate 	if (ipr != NULL) {
4340Sstevel@tonic-gate 		ipr->ipsr_bucket = bucket;
4350Sstevel@tonic-gate 		ipr->ipsr_gen = bucket->isaf_gen;
4360Sstevel@tonic-gate 		ipr->ipsr_sa = retval;
4370Sstevel@tonic-gate 		/* I'm now caching, so the cache-invalid flag goes away! */
4380Sstevel@tonic-gate 		retval->ipsa_flags &= ~IPSA_F_CINVALID;
4390Sstevel@tonic-gate 	}
4400Sstevel@tonic-gate 	/*
4410Sstevel@tonic-gate 	 * Latch various things while we're here..
4420Sstevel@tonic-gate 	 */
4430Sstevel@tonic-gate 	if (ipl != NULL) {
4440Sstevel@tonic-gate 		if (!ipl->ipl_ids_latched) {
4450Sstevel@tonic-gate 			ipsec_latch_ids(ipl,
4460Sstevel@tonic-gate 			    retval->ipsa_src_cid, retval->ipsa_dst_cid);
4470Sstevel@tonic-gate 		}
4480Sstevel@tonic-gate 		if (!ipl->ipl_out_action_latched) {
4490Sstevel@tonic-gate 			IPACT_REFHOLD(act);
4500Sstevel@tonic-gate 			ipl->ipl_out_action = act;
4510Sstevel@tonic-gate 			ipl->ipl_out_action_latched = B_TRUE;
4520Sstevel@tonic-gate 		}
4530Sstevel@tonic-gate 	}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 	/*
4560Sstevel@tonic-gate 	 * Set the uniqueness only first time.
4570Sstevel@tonic-gate 	 */
4580Sstevel@tonic-gate 	if (need_unique && !(old_flags & IPSA_F_USED)) {
4590Sstevel@tonic-gate 		if (retval->ipsa_unique_id == 0) {
4600Sstevel@tonic-gate 			ASSERT((retval->ipsa_flags & IPSA_F_UNIQUE) == 0);
4610Sstevel@tonic-gate 			/*
4620Sstevel@tonic-gate 			 * From now on, only this src, dst[ports, addr],
4630Sstevel@tonic-gate 			 * proto, should use it.
4640Sstevel@tonic-gate 			 */
4650Sstevel@tonic-gate 			retval->ipsa_flags |= IPSA_F_UNIQUE;
4660Sstevel@tonic-gate 			retval->ipsa_unique_id = unique_id;
4670Sstevel@tonic-gate 			retval->ipsa_unique_mask = SA_UNIQUE_MASK(
4680Sstevel@tonic-gate 			    io->ipsec_out_src_port, io->ipsec_out_dst_port,
469*3055Sdanmcd 			    protocol, 0);
4700Sstevel@tonic-gate 		}
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate 		/*
4730Sstevel@tonic-gate 		 * Set the source address and adjust the hash
4740Sstevel@tonic-gate 		 * buckets only if src_addr is zero.
4750Sstevel@tonic-gate 		 */
4760Sstevel@tonic-gate 		if (IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
4770Sstevel@tonic-gate 			/*
4780Sstevel@tonic-gate 			 * sadb_unlinkassoc() will decrement the refcnt.  Bump
4790Sstevel@tonic-gate 			 * up when we have the lock so that we don't have to
4800Sstevel@tonic-gate 			 * acquire locks when we come back from
4810Sstevel@tonic-gate 			 * sadb_insertassoc().
4820Sstevel@tonic-gate 			 *
4830Sstevel@tonic-gate 			 * We don't need to bump the bucket's gen since
4840Sstevel@tonic-gate 			 * we aren't moving to a new bucket.
4850Sstevel@tonic-gate 			 */
4860Sstevel@tonic-gate 			IPSA_REFHOLD(retval);
4870Sstevel@tonic-gate 			IPSA_COPY_ADDR(retval->ipsa_srcaddr, src, af);
4880Sstevel@tonic-gate 			mutex_exit(&retval->ipsa_lock);
4890Sstevel@tonic-gate 			sadb_unlinkassoc(retval);
4900Sstevel@tonic-gate 			/*
4910Sstevel@tonic-gate 			 * Since the bucket lock is held, we know
4920Sstevel@tonic-gate 			 * sadb_insertassoc() will succeed.
4930Sstevel@tonic-gate 			 */
4940Sstevel@tonic-gate #ifdef DEBUG
4950Sstevel@tonic-gate 			if (sadb_insertassoc(retval, bucket) != 0) {
4960Sstevel@tonic-gate 				cmn_err(CE_PANIC,
4970Sstevel@tonic-gate 				    "sadb_insertassoc() failed in "
4980Sstevel@tonic-gate 				    "ipsec_getassocbyconn().\n");
4990Sstevel@tonic-gate 			}
5000Sstevel@tonic-gate #else	/* non-DEBUG */
5010Sstevel@tonic-gate 			(void) sadb_insertassoc(retval, bucket);
5020Sstevel@tonic-gate #endif	/* DEBUG */
5030Sstevel@tonic-gate 			return (retval);
5040Sstevel@tonic-gate 		}
5050Sstevel@tonic-gate 	}
5060Sstevel@tonic-gate 	mutex_exit(&retval->ipsa_lock);
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	return (retval);
5090Sstevel@tonic-gate }
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate /*
5120Sstevel@tonic-gate  * Look up a security association based on the security parameters index (SPI)
5130Sstevel@tonic-gate  * and address(es).  This is used for inbound packets and general SA lookups
5140Sstevel@tonic-gate  * (even in outbound SA tables).  The source address may be ignored.  Return
5150Sstevel@tonic-gate  * NULL if no association is available.	 If an SA is found, return it, with
5160Sstevel@tonic-gate  * its refcnt incremented.  The caller must REFRELE after using the SA.
5170Sstevel@tonic-gate  * The hash bucket must be locked down before calling.
5180Sstevel@tonic-gate  */
5190Sstevel@tonic-gate ipsa_t *
5200Sstevel@tonic-gate ipsec_getassocbyspi(isaf_t *bucket, uint32_t spi, uint32_t *src, uint32_t *dst,
5210Sstevel@tonic-gate     sa_family_t af)
5220Sstevel@tonic-gate {
5230Sstevel@tonic-gate 	ipsa_t *retval;
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate 	/*
5280Sstevel@tonic-gate 	 * Walk the hash bucket, matching exactly on SPI, then destination,
5290Sstevel@tonic-gate 	 * then source.
5300Sstevel@tonic-gate 	 *
5310Sstevel@tonic-gate 	 * Per-SA locking doesn't need to happen, because I'm only matching
5320Sstevel@tonic-gate 	 * on addresses.  Addresses are only changed during insertion/deletion
5330Sstevel@tonic-gate 	 * from the hash bucket.  Since the hash bucket lock is held, we don't
5340Sstevel@tonic-gate 	 * need to worry about addresses changing.
5350Sstevel@tonic-gate 	 */
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 	for (retval = bucket->isaf_ipsa; retval != NULL;
5380Sstevel@tonic-gate 	    retval = retval->ipsa_next) {
5390Sstevel@tonic-gate 		if (retval->ipsa_spi != spi)
5400Sstevel@tonic-gate 			continue;
5410Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
5420Sstevel@tonic-gate 			continue;
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 		/*
5450Sstevel@tonic-gate 		 * Assume that wildcard source addresses are inserted at the
5460Sstevel@tonic-gate 		 * end of the hash bucket.  (See sadb_insertassoc().)
5470Sstevel@tonic-gate 		 * The following check for source addresses is a weak form
5480Sstevel@tonic-gate 		 * of access control/source identity verification.  If an
5490Sstevel@tonic-gate 		 * SA has a source address, I only match an all-zeroes
5500Sstevel@tonic-gate 		 * source address, or that particular one.  If the SA has
5510Sstevel@tonic-gate 		 * an all-zeroes source, then I match regardless.
5520Sstevel@tonic-gate 		 *
5530Sstevel@tonic-gate 		 * There is a weakness here in that a packet with all-zeroes
5540Sstevel@tonic-gate 		 * for an address will match regardless of the source address
5550Sstevel@tonic-gate 		 * stored in the packet.
556691Ssommerfe 		 *
557691Ssommerfe 		 * Note that port-level packet selectors, if present,
558691Ssommerfe 		 * are checked in ipsec_check_ipsecin_unique().
5590Sstevel@tonic-gate 		 */
5600Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) ||
5610Sstevel@tonic-gate 		    IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af) ||
5620Sstevel@tonic-gate 		    IPSA_IS_ADDR_UNSPEC(src, af))
5630Sstevel@tonic-gate 			break;
5640Sstevel@tonic-gate 	}
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	if (retval != NULL) {
5670Sstevel@tonic-gate 		/*
5680Sstevel@tonic-gate 		 * Just refhold the return value.  The caller will then
5690Sstevel@tonic-gate 		 * make the appropriate calls to set the USED flag.
5700Sstevel@tonic-gate 		 */
5710Sstevel@tonic-gate 		IPSA_REFHOLD(retval);
5720Sstevel@tonic-gate 	}
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	return (retval);
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate boolean_t
5780Sstevel@tonic-gate ipsec_outbound_sa(mblk_t *mp, uint_t proto)
5790Sstevel@tonic-gate {
5800Sstevel@tonic-gate 	mblk_t *data_mp;
5810Sstevel@tonic-gate 	ipsec_out_t *io;
5820Sstevel@tonic-gate 	ipaddr_t dst;
5830Sstevel@tonic-gate 	uint32_t *dst_ptr, *src_ptr;
5840Sstevel@tonic-gate 	isaf_t *bucket;
5850Sstevel@tonic-gate 	ipsa_t *assoc;
5860Sstevel@tonic-gate 	ip6_pkt_t ipp;
5870Sstevel@tonic-gate 	in6_addr_t dst6;
5880Sstevel@tonic-gate 	ipsa_t **sa;
5890Sstevel@tonic-gate 	sadbp_t *sadbp;
590564Ssommerfe 	sadb_t *sp;
5910Sstevel@tonic-gate 	sa_family_t af;
5920Sstevel@tonic-gate 
5930Sstevel@tonic-gate 	data_mp = mp->b_cont;
5940Sstevel@tonic-gate 	io = (ipsec_out_t *)mp->b_rptr;
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate 	if (proto == IPPROTO_ESP) {
5970Sstevel@tonic-gate 		sa = &io->ipsec_out_esp_sa;
5980Sstevel@tonic-gate 		sadbp = &esp_sadb;
5990Sstevel@tonic-gate 	} else {
6000Sstevel@tonic-gate 		ASSERT(proto == IPPROTO_AH);
6010Sstevel@tonic-gate 		sa = &io->ipsec_out_ah_sa;
6020Sstevel@tonic-gate 		sadbp = &ah_sadb;
6030Sstevel@tonic-gate 	}
6040Sstevel@tonic-gate 
6050Sstevel@tonic-gate 	ASSERT(*sa == NULL);
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	if (io->ipsec_out_v4) {
6080Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
6110Sstevel@tonic-gate 		dst = ip_get_dst(ipha);
612564Ssommerfe 		sp = &sadbp->s_v4;
6130Sstevel@tonic-gate 		af = AF_INET;
6140Sstevel@tonic-gate 
6150Sstevel@tonic-gate 		/*
6160Sstevel@tonic-gate 		 * NOTE:Getting the outbound association is considerably
6170Sstevel@tonic-gate 		 *	painful.  ipsec_getassocbyconn() will require more
6180Sstevel@tonic-gate 		 *	parameters as policy implementations mature.
6190Sstevel@tonic-gate 		 */
620564Ssommerfe 		bucket = OUTBOUND_BUCKET_V4(sp, dst);
6210Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
6220Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&dst;
6230Sstevel@tonic-gate 	} else {
6240Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
6250Sstevel@tonic-gate 
6260Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
6270Sstevel@tonic-gate 		dst6 = ip_get_dst_v6(ip6h, NULL);
6280Sstevel@tonic-gate 		af = AF_INET6;
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 		bzero(&ipp, sizeof (ipp));
631564Ssommerfe 		sp = &sadbp->s_v6;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 		/* Same NOTE: applies here! */
634564Ssommerfe 		bucket = OUTBOUND_BUCKET_V6(sp, dst6);
6350Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
6360Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&dst6;
6370Sstevel@tonic-gate 	}
6380Sstevel@tonic-gate 
6390Sstevel@tonic-gate 	mutex_enter(&bucket->isaf_lock);
6400Sstevel@tonic-gate 	assoc = ipsec_getassocbyconn(bucket, io, src_ptr, dst_ptr, af, proto);
6410Sstevel@tonic-gate 	mutex_exit(&bucket->isaf_lock);
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	if (assoc == NULL)
6440Sstevel@tonic-gate 		return (B_FALSE);
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_DEAD) {
6470Sstevel@tonic-gate 		IPSA_REFRELE(assoc);
6480Sstevel@tonic-gate 		return (B_FALSE);
6490Sstevel@tonic-gate 	}
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	ASSERT(assoc->ipsa_state != IPSA_STATE_LARVAL);
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	*sa = assoc;
6540Sstevel@tonic-gate 	return (B_TRUE);
6550Sstevel@tonic-gate }
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate /*
6580Sstevel@tonic-gate  * Inbound IPsec SA selection.
6590Sstevel@tonic-gate  */
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate ah_t *
6620Sstevel@tonic-gate ipsec_inbound_ah_sa(mblk_t *mp)
6630Sstevel@tonic-gate {
6640Sstevel@tonic-gate 	mblk_t *ipsec_in;
6650Sstevel@tonic-gate 	ipha_t *ipha;
6660Sstevel@tonic-gate 	ipsa_t 	*assoc;
6670Sstevel@tonic-gate 	ah_t *ah;
6680Sstevel@tonic-gate 	isaf_t *hptr;
6690Sstevel@tonic-gate 	ipsec_in_t *ii;
6700Sstevel@tonic-gate 	boolean_t isv6;
6710Sstevel@tonic-gate 	ip6_t *ip6h;
6720Sstevel@tonic-gate 	int ah_offset;
6730Sstevel@tonic-gate 	uint32_t *src_ptr, *dst_ptr;
6740Sstevel@tonic-gate 	int pullup_len;
675564Ssommerfe 	sadb_t *sp;
6760Sstevel@tonic-gate 	sa_family_t af;
6770Sstevel@tonic-gate 
6780Sstevel@tonic-gate 	IP_AH_BUMP_STAT(in_requests);
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_CTL);
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	ipsec_in = mp;
6830Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in->b_rptr;
6840Sstevel@tonic-gate 	mp = mp->b_cont;
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_DATA);
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	isv6 = !ii->ipsec_in_v4;
6890Sstevel@tonic-gate 	if (isv6) {
6900Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
6910Sstevel@tonic-gate 		ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE);
6920Sstevel@tonic-gate 	} else {
6930Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
6940Sstevel@tonic-gate 		ASSERT(ipha->ipha_protocol == IPPROTO_AH);
6950Sstevel@tonic-gate 		ah_offset = ipha->ipha_version_and_hdr_length -
6960Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
6970Sstevel@tonic-gate 		ah_offset <<= 2;
6980Sstevel@tonic-gate 	}
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate 	/*
7010Sstevel@tonic-gate 	 * We assume that the IP header is pulled up until
7020Sstevel@tonic-gate 	 * the options. We need to see whether we have the
7030Sstevel@tonic-gate 	 * AH header in the same mblk or not.
7040Sstevel@tonic-gate 	 */
7050Sstevel@tonic-gate 	pullup_len = ah_offset + sizeof (ah_t);
7060Sstevel@tonic-gate 	if (mp->b_rptr + pullup_len > mp->b_wptr) {
7070Sstevel@tonic-gate 		if (!pullupmsg(mp, pullup_len)) {
7081659Smarkfen 			ipsec_rl_strlog(ip_mod_info.mi_idnum, 0, 0,
7091659Smarkfen 			    SL_WARN | SL_ERROR,
7100Sstevel@tonic-gate 			    "ipsec_inbound_ah_sa: Small AH header\n");
7110Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
7120Sstevel@tonic-gate 			ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL,
7130Sstevel@tonic-gate 			    &ipdrops_ah_bad_length, &ip_dropper);
7140Sstevel@tonic-gate 			return (NULL);
7150Sstevel@tonic-gate 		}
7160Sstevel@tonic-gate 		if (isv6)
7170Sstevel@tonic-gate 			ip6h = (ip6_t *)mp->b_rptr;
7180Sstevel@tonic-gate 		else
7190Sstevel@tonic-gate 			ipha = (ipha_t *)mp->b_rptr;
7200Sstevel@tonic-gate 	}
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 	ah = (ah_t *)(mp->b_rptr + ah_offset);
7230Sstevel@tonic-gate 
7240Sstevel@tonic-gate 	if (isv6) {
7250Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
7260Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ip6h->ip6_dst;
727564Ssommerfe 		sp = &ah_sadb.s_v6;
7280Sstevel@tonic-gate 		af = AF_INET6;
7290Sstevel@tonic-gate 	} else {
7300Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
7310Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ipha->ipha_dst;
732564Ssommerfe 		sp = &ah_sadb.s_v4;
7330Sstevel@tonic-gate 		af = AF_INET;
7340Sstevel@tonic-gate 	}
7350Sstevel@tonic-gate 
736564Ssommerfe 	hptr = INBOUND_BUCKET(sp, ah->ah_spi);
7370Sstevel@tonic-gate 	mutex_enter(&hptr->isaf_lock);
7380Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, src_ptr, dst_ptr, af);
7390Sstevel@tonic-gate 	mutex_exit(&hptr->isaf_lock);
7400Sstevel@tonic-gate 
7410Sstevel@tonic-gate 	if (assoc == NULL || assoc->ipsa_state == IPSA_STATE_DEAD) {
7420Sstevel@tonic-gate 		IP_AH_BUMP_STAT(lookup_failure);
7430Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
7440Sstevel@tonic-gate 		ipsecah_in_assocfailure(ipsec_in, 0,
7450Sstevel@tonic-gate 		    SL_ERROR | SL_CONSOLE | SL_WARN,
7460Sstevel@tonic-gate 		    "ipsec_inbound_ah_sa: No association found for "
7470Sstevel@tonic-gate 		    "spi 0x%x, dst addr %s\n",
7480Sstevel@tonic-gate 		    ah->ah_spi, dst_ptr, af);
7490Sstevel@tonic-gate 		if (assoc != NULL) {
7500Sstevel@tonic-gate 			IPSA_REFRELE(assoc);
7510Sstevel@tonic-gate 		}
7520Sstevel@tonic-gate 		return (NULL);
7530Sstevel@tonic-gate 	}
7540Sstevel@tonic-gate 
7550Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_LARVAL) {
7560Sstevel@tonic-gate 		/* Not fully baked; swap the packet under a rock until then */
7570Sstevel@tonic-gate 		sadb_set_lpkt(assoc, ipsec_in);
7580Sstevel@tonic-gate 		IPSA_REFRELE(assoc);
7590Sstevel@tonic-gate 		return (NULL);
7600Sstevel@tonic-gate 	}
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 	/*
7630Sstevel@tonic-gate 	 * Save a reference to the association so that it can
7640Sstevel@tonic-gate 	 * be retrieved after execution. We free any AH SA reference
7650Sstevel@tonic-gate 	 * already there (innermost SA "wins". The reference to
7660Sstevel@tonic-gate 	 * the SA will also be used later when doing the policy checks.
7670Sstevel@tonic-gate 	 */
7680Sstevel@tonic-gate 	if (ii->ipsec_in_ah_sa != NULL) {
7690Sstevel@tonic-gate 		IPSA_REFRELE(ii->ipsec_in_ah_sa);
7700Sstevel@tonic-gate 	}
7710Sstevel@tonic-gate 	ii->ipsec_in_ah_sa = assoc;
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	return (ah);
7740Sstevel@tonic-gate }
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate esph_t *
7770Sstevel@tonic-gate ipsec_inbound_esp_sa(mblk_t *ipsec_in_mp)
7780Sstevel@tonic-gate {
7790Sstevel@tonic-gate 	mblk_t *data_mp, *placeholder;
7800Sstevel@tonic-gate 	uint32_t *src_ptr, *dst_ptr;
7810Sstevel@tonic-gate 	ipsec_in_t *ii;
7820Sstevel@tonic-gate 	ipha_t *ipha;
7830Sstevel@tonic-gate 	ip6_t *ip6h;
7840Sstevel@tonic-gate 	esph_t *esph;
7850Sstevel@tonic-gate 	ipsa_t *ipsa;
7860Sstevel@tonic-gate 	isaf_t *bucket;
7870Sstevel@tonic-gate 	uint_t preamble;
7880Sstevel@tonic-gate 	sa_family_t af;
7890Sstevel@tonic-gate 	boolean_t isv6;
790564Ssommerfe 	sadb_t *sp;
7910Sstevel@tonic-gate 
7920Sstevel@tonic-gate 	IP_ESP_BUMP_STAT(in_requests);
7930Sstevel@tonic-gate 	ASSERT(ipsec_in_mp->b_datap->db_type == M_CTL);
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	/* We have IPSEC_IN already! */
7960Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in_mp->b_rptr;
7970Sstevel@tonic-gate 	data_mp = ipsec_in_mp->b_cont;
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate 	ASSERT(ii->ipsec_in_type == IPSEC_IN);
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate 	isv6 = !ii->ipsec_in_v4;
8020Sstevel@tonic-gate 	if (isv6) {
8030Sstevel@tonic-gate 		ip6h = (ip6_t *)data_mp->b_rptr;
8040Sstevel@tonic-gate 	} else {
8050Sstevel@tonic-gate 		ipha = (ipha_t *)data_mp->b_rptr;
8060Sstevel@tonic-gate 	}
8070Sstevel@tonic-gate 
8080Sstevel@tonic-gate 	/*
8090Sstevel@tonic-gate 	 * Put all data into one mblk if it's not there already.
8100Sstevel@tonic-gate 	 * XXX This is probably bad long-term.  Figure out better ways of doing
8110Sstevel@tonic-gate 	 * this.  Much of the inbound path depends on all of the data being
8120Sstevel@tonic-gate 	 * in one mblk.
8130Sstevel@tonic-gate 	 *
8140Sstevel@tonic-gate 	 * XXX Jumbogram issues will have to be dealt with here.
8150Sstevel@tonic-gate 	 * If the plen is 0, we'll have to scan for a HBH header with the
8160Sstevel@tonic-gate 	 * actual packet length.
8170Sstevel@tonic-gate 	 */
8180Sstevel@tonic-gate 	if (data_mp->b_datap->db_ref > 1 ||
8190Sstevel@tonic-gate 	    (data_mp->b_wptr - data_mp->b_rptr) <
8200Sstevel@tonic-gate 	    (isv6 ? (ntohs(ip6h->ip6_plen) + sizeof (ip6_t))
8210Sstevel@tonic-gate 		: ntohs(ipha->ipha_length))) {
8220Sstevel@tonic-gate 		placeholder = msgpullup(data_mp, -1);
8230Sstevel@tonic-gate 		if (placeholder == NULL) {
8240Sstevel@tonic-gate 			IP_ESP_BUMP_STAT(in_discards);
8250Sstevel@tonic-gate 			/*
8260Sstevel@tonic-gate 			 * TODO: Extract inbound interface from the IPSEC_IN
8270Sstevel@tonic-gate 			 * message's ii->ipsec_in_rill_index.
8280Sstevel@tonic-gate 			 */
8290Sstevel@tonic-gate 			ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL,
8300Sstevel@tonic-gate 			    &ipdrops_esp_nomem, &ip_dropper);
8310Sstevel@tonic-gate 			return (NULL);
8320Sstevel@tonic-gate 		} else {
8330Sstevel@tonic-gate 			/* Reset packet with new pulled up mblk. */
8340Sstevel@tonic-gate 			freemsg(data_mp);
8350Sstevel@tonic-gate 			data_mp = placeholder;
8360Sstevel@tonic-gate 			ipsec_in_mp->b_cont = data_mp;
8370Sstevel@tonic-gate 		}
8380Sstevel@tonic-gate 	}
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 	/*
8410Sstevel@tonic-gate 	 * Find the ESP header, point the address pointers at the appropriate
8420Sstevel@tonic-gate 	 * IPv4/IPv6 places.
8430Sstevel@tonic-gate 	 */
8440Sstevel@tonic-gate 	if (isv6) {
8450Sstevel@tonic-gate 		ip6h = (ip6_t *)data_mp->b_rptr;
8460Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
8470Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ip6h->ip6_dst;
8480Sstevel@tonic-gate 		if (ip6h->ip6_nxt != IPPROTO_ESP) {
8490Sstevel@tonic-gate 			/* There are options that need to be processed. */
8500Sstevel@tonic-gate 			preamble = ip_hdr_length_v6(data_mp, ip6h);
8510Sstevel@tonic-gate 		} else {
8520Sstevel@tonic-gate 			preamble = sizeof (ip6_t);
8530Sstevel@tonic-gate 		}
8540Sstevel@tonic-gate 
855564Ssommerfe 		sp = &esp_sadb.s_v6;
8560Sstevel@tonic-gate 		af = AF_INET6;
8570Sstevel@tonic-gate 	} else {
8580Sstevel@tonic-gate 		ipha = (ipha_t *)data_mp->b_rptr;
8590Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
8600Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ipha->ipha_dst;
8610Sstevel@tonic-gate 		preamble = IPH_HDR_LENGTH(ipha);
8620Sstevel@tonic-gate 
863564Ssommerfe 		sp = &esp_sadb.s_v4;
8640Sstevel@tonic-gate 		af = AF_INET;
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 	esph = (esph_t *)(data_mp->b_rptr + preamble);
8680Sstevel@tonic-gate 
8690Sstevel@tonic-gate 	/* Since hash is common on inbound (SPI value), hash here. */
870564Ssommerfe 	bucket = INBOUND_BUCKET(sp, esph->esph_spi);
8710Sstevel@tonic-gate 	mutex_enter(&bucket->isaf_lock);
8720Sstevel@tonic-gate 	ipsa = ipsec_getassocbyspi(bucket, esph->esph_spi, src_ptr, dst_ptr,
8730Sstevel@tonic-gate 	    af);
8740Sstevel@tonic-gate 	mutex_exit(&bucket->isaf_lock);
8750Sstevel@tonic-gate 
8760Sstevel@tonic-gate 	if (ipsa == NULL || ipsa->ipsa_state == IPSA_STATE_DEAD) {
8770Sstevel@tonic-gate 		/*  This is a loggable error!  AUDIT ME! */
8780Sstevel@tonic-gate 		IP_ESP_BUMP_STAT(lookup_failure);
8790Sstevel@tonic-gate 		IP_ESP_BUMP_STAT(in_discards);
8800Sstevel@tonic-gate 		ipsecesp_in_assocfailure(ipsec_in_mp, 0,
8810Sstevel@tonic-gate 		    SL_ERROR | SL_CONSOLE | SL_WARN,
8820Sstevel@tonic-gate 		    "ipsec_inbound_esp_sa: No association found for "
8830Sstevel@tonic-gate 		    "spi 0x%x, dst addr %s\n",
8840Sstevel@tonic-gate 		    esph->esph_spi, dst_ptr, af);
8850Sstevel@tonic-gate 		if (ipsa != NULL) {
8860Sstevel@tonic-gate 			IPSA_REFRELE(ipsa);
8870Sstevel@tonic-gate 		}
8880Sstevel@tonic-gate 		return (NULL);
8890Sstevel@tonic-gate 	}
8900Sstevel@tonic-gate 
8910Sstevel@tonic-gate 	if (ipsa->ipsa_state == IPSA_STATE_LARVAL) {
8920Sstevel@tonic-gate 		/* Not fully baked; swap the packet under a rock until then */
8930Sstevel@tonic-gate 		sadb_set_lpkt(ipsa, ipsec_in_mp);
8940Sstevel@tonic-gate 		IPSA_REFRELE(ipsa);
8950Sstevel@tonic-gate 		return (NULL);
8960Sstevel@tonic-gate 	}
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	/*
8990Sstevel@tonic-gate 	 * Save a reference to the association so that it can
9000Sstevel@tonic-gate 	 * be retrieved after execution. We free any AH SA reference
9010Sstevel@tonic-gate 	 * already there (innermost SA "wins". The reference to
9020Sstevel@tonic-gate 	 * the SA will also be used later when doing the policy checks.
9030Sstevel@tonic-gate 	 */
9040Sstevel@tonic-gate 	if (ii->ipsec_in_esp_sa != NULL) {
9050Sstevel@tonic-gate 		IPSA_REFRELE(ii->ipsec_in_esp_sa);
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate 	ii->ipsec_in_esp_sa = ipsa;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 	return (esph);
9100Sstevel@tonic-gate }
911