xref: /onnv-gate/usr/src/uts/common/inet/ip/ip_sadb.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/stream.h>
31*0Sstevel@tonic-gate #include <sys/sunddi.h>
32*0Sstevel@tonic-gate #include <sys/strlog.h>
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include <inet/common.h>
35*0Sstevel@tonic-gate #include <inet/mib2.h>
36*0Sstevel@tonic-gate #include <inet/ip.h>
37*0Sstevel@tonic-gate #include <inet/ip6.h>
38*0Sstevel@tonic-gate #include <inet/ipdrop.h>
39*0Sstevel@tonic-gate 
40*0Sstevel@tonic-gate #include <net/pfkeyv2.h>
41*0Sstevel@tonic-gate #include <inet/ipsec_info.h>
42*0Sstevel@tonic-gate #include <inet/sadb.h>
43*0Sstevel@tonic-gate #include <inet/ipsec_impl.h>
44*0Sstevel@tonic-gate #include <inet/ipsecesp.h>
45*0Sstevel@tonic-gate #include <inet/ipsecah.h>
46*0Sstevel@tonic-gate #include <sys/kstat.h>
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate /* stats */
49*0Sstevel@tonic-gate static kstat_t *ipsec_ksp;
50*0Sstevel@tonic-gate ipsec_kstats_t *ipsec_kstats;
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate /* The IPsec SADBs for AH and ESP */
53*0Sstevel@tonic-gate sadbp_t ah_sadb, esp_sadb;
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate /* Packet dropper for IP IPsec processing failures */
56*0Sstevel@tonic-gate extern ipdropper_t ip_dropper;
57*0Sstevel@tonic-gate 
58*0Sstevel@tonic-gate void
59*0Sstevel@tonic-gate ipsec_kstat_init(void)
60*0Sstevel@tonic-gate {
61*0Sstevel@tonic-gate 	ipsec_ksp = kstat_create("ip", 0, "ipsec_stat", "net",
62*0Sstevel@tonic-gate 	    KSTAT_TYPE_NAMED, sizeof (*ipsec_kstats) / sizeof (kstat_named_t),
63*0Sstevel@tonic-gate 	    KSTAT_FLAG_PERSISTENT);
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate 	ASSERT(ipsec_ksp != NULL);
66*0Sstevel@tonic-gate 
67*0Sstevel@tonic-gate 	ipsec_kstats = ipsec_ksp->ks_data;
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate #define	KI(x) kstat_named_init(&ipsec_kstats->x, #x, KSTAT_DATA_UINT64)
70*0Sstevel@tonic-gate 	KI(esp_stat_in_requests);
71*0Sstevel@tonic-gate 	KI(esp_stat_in_discards);
72*0Sstevel@tonic-gate 	KI(esp_stat_lookup_failure);
73*0Sstevel@tonic-gate 	KI(ah_stat_in_requests);
74*0Sstevel@tonic-gate 	KI(ah_stat_in_discards);
75*0Sstevel@tonic-gate 	KI(ah_stat_lookup_failure);
76*0Sstevel@tonic-gate #undef KI
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate 	kstat_install(ipsec_ksp);
79*0Sstevel@tonic-gate }
80*0Sstevel@tonic-gate 
81*0Sstevel@tonic-gate void
82*0Sstevel@tonic-gate ipsec_kstat_destroy(void)
83*0Sstevel@tonic-gate {
84*0Sstevel@tonic-gate 	kstat_delete(ipsec_ksp);
85*0Sstevel@tonic-gate }
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate /*
88*0Sstevel@tonic-gate  * Returns B_TRUE if the identities in the SA match the identities
89*0Sstevel@tonic-gate  * in the "latch" structure.
90*0Sstevel@tonic-gate  */
91*0Sstevel@tonic-gate 
92*0Sstevel@tonic-gate static boolean_t
93*0Sstevel@tonic-gate ipsec_match_outbound_ids(ipsec_latch_t *ipl, ipsa_t *sa)
94*0Sstevel@tonic-gate {
95*0Sstevel@tonic-gate 	ASSERT(ipl->ipl_ids_latched == B_TRUE);
96*0Sstevel@tonic-gate 	return ipsid_equal(ipl->ipl_local_cid, sa->ipsa_src_cid) &&
97*0Sstevel@tonic-gate 	    ipsid_equal(ipl->ipl_remote_cid, sa->ipsa_dst_cid);
98*0Sstevel@tonic-gate }
99*0Sstevel@tonic-gate 
100*0Sstevel@tonic-gate /*
101*0Sstevel@tonic-gate  * Look up a security association based on the unique ID generated by IP and
102*0Sstevel@tonic-gate  * transport information, such as ports and upper-layer protocol, and the
103*0Sstevel@tonic-gate  * address(es).	 Used for uniqueness testing and outbound packets.  The
104*0Sstevel@tonic-gate  * source address may be ignored.
105*0Sstevel@tonic-gate  *
106*0Sstevel@tonic-gate  * I expect an SA hash bucket, and that its per-bucket mutex is held.
107*0Sstevel@tonic-gate  * The SA ptr I return will have its reference count incremented by one.
108*0Sstevel@tonic-gate  */
109*0Sstevel@tonic-gate ipsa_t *
110*0Sstevel@tonic-gate ipsec_getassocbyconn(isaf_t *bucket, ipsec_out_t *io, uint32_t *src,
111*0Sstevel@tonic-gate     uint32_t *dst, sa_family_t af, uint8_t protocol)
112*0Sstevel@tonic-gate {
113*0Sstevel@tonic-gate 	ipsa_t *retval, *candidate;
114*0Sstevel@tonic-gate 	ipsec_action_t *candact;
115*0Sstevel@tonic-gate 	boolean_t need_unique;
116*0Sstevel@tonic-gate 	uint64_t unique_id;
117*0Sstevel@tonic-gate 	uint32_t old_flags, excludeflags;
118*0Sstevel@tonic-gate 	ipsec_policy_t *pp = io->ipsec_out_policy;
119*0Sstevel@tonic-gate 	ipsec_action_t *actlist = io->ipsec_out_act;
120*0Sstevel@tonic-gate 	ipsec_action_t *act;
121*0Sstevel@tonic-gate 	ipsec_latch_t *ipl = io->ipsec_out_latch;
122*0Sstevel@tonic-gate 	ipsa_ref_t *ipr = NULL;
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
125*0Sstevel@tonic-gate 
126*0Sstevel@tonic-gate 	/*
127*0Sstevel@tonic-gate 	 * Fast path: do we have a latch structure, is it for this bucket,
128*0Sstevel@tonic-gate 	 * and does the generation number match?  If so, refhold and return.
129*0Sstevel@tonic-gate 	 */
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	if (ipl != NULL) {
132*0Sstevel@tonic-gate 		ASSERT((protocol == IPPROTO_AH) || (protocol == IPPROTO_ESP));
133*0Sstevel@tonic-gate 		ipr = &ipl->ipl_ref[protocol - IPPROTO_ESP];
134*0Sstevel@tonic-gate 
135*0Sstevel@tonic-gate 		retval = ipr->ipsr_sa;
136*0Sstevel@tonic-gate 
137*0Sstevel@tonic-gate 		/*
138*0Sstevel@tonic-gate 		 * NOTE: The isaf_gen check (incremented upon
139*0Sstevel@tonic-gate 		 * sadb_unlinkassoc()) protects against retval being a freed
140*0Sstevel@tonic-gate 		 * SA.  (We're exploiting short-circuit evaluation.)
141*0Sstevel@tonic-gate 		 */
142*0Sstevel@tonic-gate 		if ((bucket == ipr->ipsr_bucket) &&
143*0Sstevel@tonic-gate 		    (bucket->isaf_gen == ipr->ipsr_gen) &&
144*0Sstevel@tonic-gate 		    (retval->ipsa_state != IPSA_STATE_DEAD) &&
145*0Sstevel@tonic-gate 		    !(retval->ipsa_flags & IPSA_F_CINVALID)) {
146*0Sstevel@tonic-gate 			IPSA_REFHOLD(retval);
147*0Sstevel@tonic-gate 			return (retval);
148*0Sstevel@tonic-gate 		}
149*0Sstevel@tonic-gate 	}
150*0Sstevel@tonic-gate 
151*0Sstevel@tonic-gate 	ASSERT((pp != NULL) || (actlist != NULL));
152*0Sstevel@tonic-gate 	if (actlist == NULL)
153*0Sstevel@tonic-gate 		actlist = pp->ipsp_act;
154*0Sstevel@tonic-gate 	ASSERT(actlist != NULL);
155*0Sstevel@tonic-gate 
156*0Sstevel@tonic-gate 	need_unique = actlist->ipa_want_unique;
157*0Sstevel@tonic-gate 	unique_id = SA_FORM_UNIQUE_ID(io);
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate 	/*
160*0Sstevel@tonic-gate 	 * Precompute mask for SA flags comparison: If we need a
161*0Sstevel@tonic-gate 	 * unique SA and an SA has already been used, or if the SA has
162*0Sstevel@tonic-gate 	 * a unique value which doesn't match, we aren't interested in
163*0Sstevel@tonic-gate 	 * the SA..
164*0Sstevel@tonic-gate 	 */
165*0Sstevel@tonic-gate 
166*0Sstevel@tonic-gate 	excludeflags = IPSA_F_UNIQUE;
167*0Sstevel@tonic-gate 	if (need_unique)
168*0Sstevel@tonic-gate 		excludeflags |= IPSA_F_USED;
169*0Sstevel@tonic-gate 
170*0Sstevel@tonic-gate 	/*
171*0Sstevel@tonic-gate 	 * Walk the hash bucket, matching on:
172*0Sstevel@tonic-gate 	 *
173*0Sstevel@tonic-gate 	 * - unique_id
174*0Sstevel@tonic-gate 	 * - destination
175*0Sstevel@tonic-gate 	 * - source
176*0Sstevel@tonic-gate 	 * - algorithms
177*0Sstevel@tonic-gate 	 * - <MORE TBD>
178*0Sstevel@tonic-gate 	 *
179*0Sstevel@tonic-gate 	 * Make sure that wildcard sources are inserted at the end of the hash
180*0Sstevel@tonic-gate 	 * bucket.
181*0Sstevel@tonic-gate 	 *
182*0Sstevel@tonic-gate 	 * DEFINITIONS:	A _shared_ SA is one with unique_id == 0 and USED.
183*0Sstevel@tonic-gate 	 *		An _unused_ SA is one with unique_id == 0 and not USED.
184*0Sstevel@tonic-gate 	 *		A _unique_ SA is one with unique_id != 0 and USED.
185*0Sstevel@tonic-gate 	 *		An SA with unique_id != 0 and not USED never happens.
186*0Sstevel@tonic-gate 	 */
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	candidate = NULL;
189*0Sstevel@tonic-gate 
190*0Sstevel@tonic-gate 	for (retval = bucket->isaf_ipsa; retval != NULL;
191*0Sstevel@tonic-gate 	    retval = retval->ipsa_next) {
192*0Sstevel@tonic-gate 		ASSERT((candidate == NULL) ||
193*0Sstevel@tonic-gate 		    MUTEX_HELD(&candidate->ipsa_lock));
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate 		/*
196*0Sstevel@tonic-gate 		 * Q: Should I lock this SA?
197*0Sstevel@tonic-gate 		 * A: For now, yes.  I change and use too many fields in here
198*0Sstevel@tonic-gate 		 *    (e.g. unique_id) that I may be racing with other threads.
199*0Sstevel@tonic-gate 		 *    Also, the refcnt needs to be bumped up.
200*0Sstevel@tonic-gate 		 */
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 		mutex_enter(&retval->ipsa_lock);
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 		/* My apologies for the use of goto instead of continue. */
205*0Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
206*0Sstevel@tonic-gate 			goto next_ipsa;	/* Destination mismatch. */
207*0Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) &&
208*0Sstevel@tonic-gate 		    !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af))
209*0Sstevel@tonic-gate 			goto next_ipsa;	/* Specific source and not matched. */
210*0Sstevel@tonic-gate 
211*0Sstevel@tonic-gate 		/*
212*0Sstevel@tonic-gate 		 * XXX should be able to use cached/latched action
213*0Sstevel@tonic-gate 		 * to dodge this loop
214*0Sstevel@tonic-gate 		 */
215*0Sstevel@tonic-gate 		for (act = actlist; act != NULL; act = act->ipa_next) {
216*0Sstevel@tonic-gate 			ipsec_act_t *ap = &act->ipa_act;
217*0Sstevel@tonic-gate 			if (ap->ipa_type != IPSEC_POLICY_APPLY)
218*0Sstevel@tonic-gate 				continue;
219*0Sstevel@tonic-gate 
220*0Sstevel@tonic-gate 			/*
221*0Sstevel@tonic-gate 			 * XXX ugly.  should be better way to do this test
222*0Sstevel@tonic-gate 			 */
223*0Sstevel@tonic-gate 			if (protocol == IPPROTO_AH) {
224*0Sstevel@tonic-gate 				if (!(ap->ipa_apply.ipp_use_ah))
225*0Sstevel@tonic-gate 					continue;
226*0Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_auth_alg !=
227*0Sstevel@tonic-gate 				    retval->ipsa_auth_alg)
228*0Sstevel@tonic-gate 					continue;
229*0Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_ah_minbits >
230*0Sstevel@tonic-gate 					retval->ipsa_authkeybits)
231*0Sstevel@tonic-gate 					continue;
232*0Sstevel@tonic-gate 			} else {
233*0Sstevel@tonic-gate 				if (!(ap->ipa_apply.ipp_use_esp))
234*0Sstevel@tonic-gate 					continue;
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 				if ((ap->ipa_apply.ipp_encr_alg !=
237*0Sstevel@tonic-gate 				    retval->ipsa_encr_alg))
238*0Sstevel@tonic-gate 					continue;
239*0Sstevel@tonic-gate 
240*0Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_espe_minbits >
241*0Sstevel@tonic-gate 				    retval->ipsa_encrkeybits)
242*0Sstevel@tonic-gate 					continue;
243*0Sstevel@tonic-gate 
244*0Sstevel@tonic-gate 				if (ap->ipa_apply.ipp_esp_auth_alg != 0) {
245*0Sstevel@tonic-gate 					if (ap->ipa_apply.ipp_esp_auth_alg !=
246*0Sstevel@tonic-gate 					    retval->ipsa_auth_alg)
247*0Sstevel@tonic-gate 						continue;
248*0Sstevel@tonic-gate 					if (ap->ipa_apply.ipp_espa_minbits >
249*0Sstevel@tonic-gate 					    retval->ipsa_authkeybits)
250*0Sstevel@tonic-gate 						continue;
251*0Sstevel@tonic-gate 				}
252*0Sstevel@tonic-gate 			}
253*0Sstevel@tonic-gate 
254*0Sstevel@tonic-gate 			/*
255*0Sstevel@tonic-gate 			 * Check key mgmt proto, cookie
256*0Sstevel@tonic-gate 			 */
257*0Sstevel@tonic-gate 			if ((ap->ipa_apply.ipp_km_proto != 0) &&
258*0Sstevel@tonic-gate 			    (retval->ipsa_kmp != 0) &&
259*0Sstevel@tonic-gate 			    (ap->ipa_apply.ipp_km_proto != retval->ipsa_kmp))
260*0Sstevel@tonic-gate 				continue;
261*0Sstevel@tonic-gate 
262*0Sstevel@tonic-gate 			if ((ap->ipa_apply.ipp_km_cookie != 0) &&
263*0Sstevel@tonic-gate 			    (retval->ipsa_kmc != 0) &&
264*0Sstevel@tonic-gate 			    (ap->ipa_apply.ipp_km_cookie != retval->ipsa_kmc))
265*0Sstevel@tonic-gate 				continue;
266*0Sstevel@tonic-gate 
267*0Sstevel@tonic-gate 			break;
268*0Sstevel@tonic-gate 		}
269*0Sstevel@tonic-gate 		if (act == NULL)
270*0Sstevel@tonic-gate 			goto next_ipsa;	/* nothing matched */
271*0Sstevel@tonic-gate 
272*0Sstevel@tonic-gate 		/*
273*0Sstevel@tonic-gate 		 * Do identities match?
274*0Sstevel@tonic-gate 		 */
275*0Sstevel@tonic-gate 		if (ipl && ipl->ipl_ids_latched &&
276*0Sstevel@tonic-gate 		    !ipsec_match_outbound_ids(ipl, retval))
277*0Sstevel@tonic-gate 			goto next_ipsa;
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 		/*
280*0Sstevel@tonic-gate 		 * At this point, we know that we have at least a match on:
281*0Sstevel@tonic-gate 		 *
282*0Sstevel@tonic-gate 		 * - dest
283*0Sstevel@tonic-gate 		 * - source (if source is specified, i.e. non-zeroes)
284*0Sstevel@tonic-gate 		 * - auth alg (if auth alg is specified, i.e. non-zero)
285*0Sstevel@tonic-gate 		 * - encrypt. alg (if encrypt. alg is specified, i.e. non-zero)
286*0Sstevel@tonic-gate 		 * and we know that the SA keylengths are appropriate.
287*0Sstevel@tonic-gate 		 *
288*0Sstevel@tonic-gate 		 * (Keep in mind known-src SAs are hit before zero-src SAs,
289*0Sstevel@tonic-gate 		 * thanks to sadb_insertassoc().)
290*0Sstevel@tonic-gate 		 * If we need a unique asssociation, optimally we have
291*0Sstevel@tonic-gate 		 * ipsa_unique_id == unique_id, otherwise NOT USED
292*0Sstevel@tonic-gate 		 * is held in reserve (stored in candidate).
293*0Sstevel@tonic-gate 		 *
294*0Sstevel@tonic-gate 		 * For those stored in candidate, take best-match (i.e. given
295*0Sstevel@tonic-gate 		 * a choice, candidate should have non-zero ipsa_src).
296*0Sstevel@tonic-gate 		 */
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 		/*
299*0Sstevel@tonic-gate 		 * If SA has a unique value which matches, we're all set...
300*0Sstevel@tonic-gate 		 * "key management knows best"
301*0Sstevel@tonic-gate 		 */
302*0Sstevel@tonic-gate 		if ((retval->ipsa_flags & IPSA_F_UNIQUE) &&
303*0Sstevel@tonic-gate 		    ((unique_id & retval->ipsa_unique_mask) ==
304*0Sstevel@tonic-gate 			retval->ipsa_unique_id))
305*0Sstevel@tonic-gate 			break;
306*0Sstevel@tonic-gate 
307*0Sstevel@tonic-gate 		/*
308*0Sstevel@tonic-gate 		 * If we need a unique SA and this SA has already been used,
309*0Sstevel@tonic-gate 		 * or if the SA has a unique value which doesn't match,
310*0Sstevel@tonic-gate 		 * this isn't for us.
311*0Sstevel@tonic-gate 		 */
312*0Sstevel@tonic-gate 
313*0Sstevel@tonic-gate 		if (retval->ipsa_flags & excludeflags)
314*0Sstevel@tonic-gate 			goto next_ipsa;
315*0Sstevel@tonic-gate 
316*0Sstevel@tonic-gate 
317*0Sstevel@tonic-gate 		/*
318*0Sstevel@tonic-gate 		 * I found a candidate..
319*0Sstevel@tonic-gate 		 */
320*0Sstevel@tonic-gate 		if (candidate == NULL) {
321*0Sstevel@tonic-gate 			/*
322*0Sstevel@tonic-gate 			 * and didn't already have one..
323*0Sstevel@tonic-gate 			 */
324*0Sstevel@tonic-gate 			candidate = retval;
325*0Sstevel@tonic-gate 			candact = act;
326*0Sstevel@tonic-gate 			continue;
327*0Sstevel@tonic-gate 		} else {
328*0Sstevel@tonic-gate 			/*
329*0Sstevel@tonic-gate 			 * If candidate's source address is zero and
330*0Sstevel@tonic-gate 			 * the current match (i.e. retval) address is
331*0Sstevel@tonic-gate 			 * not zero, we have a better candidate..
332*0Sstevel@tonic-gate 			 */
333*0Sstevel@tonic-gate 			if (IPSA_IS_ADDR_UNSPEC(candidate->ipsa_srcaddr, af) &&
334*0Sstevel@tonic-gate 			    !IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
335*0Sstevel@tonic-gate 				mutex_exit(&candidate->ipsa_lock);
336*0Sstevel@tonic-gate 				candidate = retval;
337*0Sstevel@tonic-gate 				candact = act;
338*0Sstevel@tonic-gate 				continue;
339*0Sstevel@tonic-gate 			}
340*0Sstevel@tonic-gate 		}
341*0Sstevel@tonic-gate next_ipsa:
342*0Sstevel@tonic-gate 		mutex_exit(&retval->ipsa_lock);
343*0Sstevel@tonic-gate 	}
344*0Sstevel@tonic-gate 	ASSERT((retval == NULL) || MUTEX_HELD(&retval->ipsa_lock));
345*0Sstevel@tonic-gate 	ASSERT((candidate == NULL) || MUTEX_HELD(&candidate->ipsa_lock));
346*0Sstevel@tonic-gate 	ASSERT((retval == NULL) || (act != NULL));
347*0Sstevel@tonic-gate 	ASSERT((candidate == NULL) || (candact != NULL));
348*0Sstevel@tonic-gate 
349*0Sstevel@tonic-gate 	/* Let caller react to a lookup failure when it gets NULL. */
350*0Sstevel@tonic-gate 	if (retval == NULL && candidate == NULL)
351*0Sstevel@tonic-gate 		return (NULL);
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 	if (retval == NULL) {
354*0Sstevel@tonic-gate 		ASSERT(MUTEX_HELD(&candidate->ipsa_lock));
355*0Sstevel@tonic-gate 		retval = candidate;
356*0Sstevel@tonic-gate 		act = candact;
357*0Sstevel@tonic-gate 	} else if (candidate != NULL) {
358*0Sstevel@tonic-gate 		mutex_exit(&candidate->ipsa_lock);
359*0Sstevel@tonic-gate 	}
360*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&retval->ipsa_lock));
361*0Sstevel@tonic-gate 	ASSERT(act != NULL);
362*0Sstevel@tonic-gate 
363*0Sstevel@tonic-gate 	/*
364*0Sstevel@tonic-gate 	 * Even though I hold the mutex, since the reference counter is an
365*0Sstevel@tonic-gate 	 * atomic operation, I really have to use the IPSA_REFHOLD macro.
366*0Sstevel@tonic-gate 	 */
367*0Sstevel@tonic-gate 	IPSA_REFHOLD(retval);
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate 	/*
370*0Sstevel@tonic-gate 	 * This association is no longer unused.
371*0Sstevel@tonic-gate 	 */
372*0Sstevel@tonic-gate 	old_flags = retval->ipsa_flags;
373*0Sstevel@tonic-gate 	retval->ipsa_flags |= IPSA_F_USED;
374*0Sstevel@tonic-gate 
375*0Sstevel@tonic-gate 	/*
376*0Sstevel@tonic-gate 	 * Cache a reference to this SA for the fast path.
377*0Sstevel@tonic-gate 	 */
378*0Sstevel@tonic-gate 	if (ipr != NULL) {
379*0Sstevel@tonic-gate 		ipr->ipsr_bucket = bucket;
380*0Sstevel@tonic-gate 		ipr->ipsr_gen = bucket->isaf_gen;
381*0Sstevel@tonic-gate 		ipr->ipsr_sa = retval;
382*0Sstevel@tonic-gate 		/* I'm now caching, so the cache-invalid flag goes away! */
383*0Sstevel@tonic-gate 		retval->ipsa_flags &= ~IPSA_F_CINVALID;
384*0Sstevel@tonic-gate 	}
385*0Sstevel@tonic-gate 	/*
386*0Sstevel@tonic-gate 	 * Latch various things while we're here..
387*0Sstevel@tonic-gate 	 */
388*0Sstevel@tonic-gate 	if (ipl != NULL) {
389*0Sstevel@tonic-gate 		if (!ipl->ipl_ids_latched) {
390*0Sstevel@tonic-gate 			ipsec_latch_ids(ipl,
391*0Sstevel@tonic-gate 			    retval->ipsa_src_cid, retval->ipsa_dst_cid);
392*0Sstevel@tonic-gate 		}
393*0Sstevel@tonic-gate 		if (!ipl->ipl_out_action_latched) {
394*0Sstevel@tonic-gate 			IPACT_REFHOLD(act);
395*0Sstevel@tonic-gate 			ipl->ipl_out_action = act;
396*0Sstevel@tonic-gate 			ipl->ipl_out_action_latched = B_TRUE;
397*0Sstevel@tonic-gate 		}
398*0Sstevel@tonic-gate 	}
399*0Sstevel@tonic-gate 
400*0Sstevel@tonic-gate 	/*
401*0Sstevel@tonic-gate 	 * Set the uniqueness only first time.
402*0Sstevel@tonic-gate 	 */
403*0Sstevel@tonic-gate 	if (need_unique && !(old_flags & IPSA_F_USED)) {
404*0Sstevel@tonic-gate 		if (retval->ipsa_unique_id == 0) {
405*0Sstevel@tonic-gate 			ASSERT((retval->ipsa_flags & IPSA_F_UNIQUE) == 0);
406*0Sstevel@tonic-gate 			/*
407*0Sstevel@tonic-gate 			 * From now on, only this src, dst[ports, addr],
408*0Sstevel@tonic-gate 			 * proto, should use it.
409*0Sstevel@tonic-gate 			 */
410*0Sstevel@tonic-gate 			retval->ipsa_flags |= IPSA_F_UNIQUE;
411*0Sstevel@tonic-gate 			retval->ipsa_unique_id = unique_id;
412*0Sstevel@tonic-gate 			retval->ipsa_unique_mask = SA_UNIQUE_MASK(
413*0Sstevel@tonic-gate 			    io->ipsec_out_src_port, io->ipsec_out_dst_port,
414*0Sstevel@tonic-gate 			    protocol);
415*0Sstevel@tonic-gate 		}
416*0Sstevel@tonic-gate 
417*0Sstevel@tonic-gate 		/*
418*0Sstevel@tonic-gate 		 * Set the source address and adjust the hash
419*0Sstevel@tonic-gate 		 * buckets only if src_addr is zero.
420*0Sstevel@tonic-gate 		 */
421*0Sstevel@tonic-gate 		if (IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af)) {
422*0Sstevel@tonic-gate 			/*
423*0Sstevel@tonic-gate 			 * sadb_unlinkassoc() will decrement the refcnt.  Bump
424*0Sstevel@tonic-gate 			 * up when we have the lock so that we don't have to
425*0Sstevel@tonic-gate 			 * acquire locks when we come back from
426*0Sstevel@tonic-gate 			 * sadb_insertassoc().
427*0Sstevel@tonic-gate 			 *
428*0Sstevel@tonic-gate 			 * We don't need to bump the bucket's gen since
429*0Sstevel@tonic-gate 			 * we aren't moving to a new bucket.
430*0Sstevel@tonic-gate 			 */
431*0Sstevel@tonic-gate 			IPSA_REFHOLD(retval);
432*0Sstevel@tonic-gate 			IPSA_COPY_ADDR(retval->ipsa_srcaddr, src, af);
433*0Sstevel@tonic-gate 			mutex_exit(&retval->ipsa_lock);
434*0Sstevel@tonic-gate 			sadb_unlinkassoc(retval);
435*0Sstevel@tonic-gate 			/*
436*0Sstevel@tonic-gate 			 * Since the bucket lock is held, we know
437*0Sstevel@tonic-gate 			 * sadb_insertassoc() will succeed.
438*0Sstevel@tonic-gate 			 */
439*0Sstevel@tonic-gate #ifdef DEBUG
440*0Sstevel@tonic-gate 			if (sadb_insertassoc(retval, bucket) != 0) {
441*0Sstevel@tonic-gate 				cmn_err(CE_PANIC,
442*0Sstevel@tonic-gate 				    "sadb_insertassoc() failed in "
443*0Sstevel@tonic-gate 				    "ipsec_getassocbyconn().\n");
444*0Sstevel@tonic-gate 			}
445*0Sstevel@tonic-gate #else	/* non-DEBUG */
446*0Sstevel@tonic-gate 			(void) sadb_insertassoc(retval, bucket);
447*0Sstevel@tonic-gate #endif	/* DEBUG */
448*0Sstevel@tonic-gate 			return (retval);
449*0Sstevel@tonic-gate 		}
450*0Sstevel@tonic-gate 	}
451*0Sstevel@tonic-gate 	mutex_exit(&retval->ipsa_lock);
452*0Sstevel@tonic-gate 
453*0Sstevel@tonic-gate 	return (retval);
454*0Sstevel@tonic-gate }
455*0Sstevel@tonic-gate 
456*0Sstevel@tonic-gate /*
457*0Sstevel@tonic-gate  * Look up a security association based on the security parameters index (SPI)
458*0Sstevel@tonic-gate  * and address(es).  This is used for inbound packets and general SA lookups
459*0Sstevel@tonic-gate  * (even in outbound SA tables).  The source address may be ignored.  Return
460*0Sstevel@tonic-gate  * NULL if no association is available.	 If an SA is found, return it, with
461*0Sstevel@tonic-gate  * its refcnt incremented.  The caller must REFRELE after using the SA.
462*0Sstevel@tonic-gate  * The hash bucket must be locked down before calling.
463*0Sstevel@tonic-gate  */
464*0Sstevel@tonic-gate ipsa_t *
465*0Sstevel@tonic-gate ipsec_getassocbyspi(isaf_t *bucket, uint32_t spi, uint32_t *src, uint32_t *dst,
466*0Sstevel@tonic-gate     sa_family_t af)
467*0Sstevel@tonic-gate {
468*0Sstevel@tonic-gate 	ipsa_t *retval;
469*0Sstevel@tonic-gate 
470*0Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&bucket->isaf_lock));
471*0Sstevel@tonic-gate 
472*0Sstevel@tonic-gate 	/*
473*0Sstevel@tonic-gate 	 * Walk the hash bucket, matching exactly on SPI, then destination,
474*0Sstevel@tonic-gate 	 * then source.
475*0Sstevel@tonic-gate 	 *
476*0Sstevel@tonic-gate 	 * Per-SA locking doesn't need to happen, because I'm only matching
477*0Sstevel@tonic-gate 	 * on addresses.  Addresses are only changed during insertion/deletion
478*0Sstevel@tonic-gate 	 * from the hash bucket.  Since the hash bucket lock is held, we don't
479*0Sstevel@tonic-gate 	 * need to worry about addresses changing.
480*0Sstevel@tonic-gate 	 */
481*0Sstevel@tonic-gate 
482*0Sstevel@tonic-gate 	for (retval = bucket->isaf_ipsa; retval != NULL;
483*0Sstevel@tonic-gate 	    retval = retval->ipsa_next) {
484*0Sstevel@tonic-gate 		if (retval->ipsa_spi != spi)
485*0Sstevel@tonic-gate 			continue;
486*0Sstevel@tonic-gate 		if (!IPSA_ARE_ADDR_EQUAL(dst, retval->ipsa_dstaddr, af))
487*0Sstevel@tonic-gate 			continue;
488*0Sstevel@tonic-gate 
489*0Sstevel@tonic-gate 		/*
490*0Sstevel@tonic-gate 		 * Assume that wildcard source addresses are inserted at the
491*0Sstevel@tonic-gate 		 * end of the hash bucket.  (See sadb_insertassoc().)
492*0Sstevel@tonic-gate 		 * The following check for source addresses is a weak form
493*0Sstevel@tonic-gate 		 * of access control/source identity verification.  If an
494*0Sstevel@tonic-gate 		 * SA has a source address, I only match an all-zeroes
495*0Sstevel@tonic-gate 		 * source address, or that particular one.  If the SA has
496*0Sstevel@tonic-gate 		 * an all-zeroes source, then I match regardless.
497*0Sstevel@tonic-gate 		 *
498*0Sstevel@tonic-gate 		 * There is a weakness here in that a packet with all-zeroes
499*0Sstevel@tonic-gate 		 * for an address will match regardless of the source address
500*0Sstevel@tonic-gate 		 * stored in the packet.
501*0Sstevel@tonic-gate 		 */
502*0Sstevel@tonic-gate 		if (IPSA_ARE_ADDR_EQUAL(src, retval->ipsa_srcaddr, af) ||
503*0Sstevel@tonic-gate 		    IPSA_IS_ADDR_UNSPEC(retval->ipsa_srcaddr, af) ||
504*0Sstevel@tonic-gate 		    IPSA_IS_ADDR_UNSPEC(src, af))
505*0Sstevel@tonic-gate 			break;
506*0Sstevel@tonic-gate 	}
507*0Sstevel@tonic-gate 
508*0Sstevel@tonic-gate 	if (retval != NULL) {
509*0Sstevel@tonic-gate 		/*
510*0Sstevel@tonic-gate 		 * Just refhold the return value.  The caller will then
511*0Sstevel@tonic-gate 		 * make the appropriate calls to set the USED flag.
512*0Sstevel@tonic-gate 		 */
513*0Sstevel@tonic-gate 		IPSA_REFHOLD(retval);
514*0Sstevel@tonic-gate 	}
515*0Sstevel@tonic-gate 
516*0Sstevel@tonic-gate 	return (retval);
517*0Sstevel@tonic-gate }
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate boolean_t
520*0Sstevel@tonic-gate ipsec_outbound_sa(mblk_t *mp, uint_t proto)
521*0Sstevel@tonic-gate {
522*0Sstevel@tonic-gate 	mblk_t *data_mp;
523*0Sstevel@tonic-gate 	ipsec_out_t *io;
524*0Sstevel@tonic-gate 	ipaddr_t dst;
525*0Sstevel@tonic-gate 	uint32_t *dst_ptr, *src_ptr;
526*0Sstevel@tonic-gate 	isaf_t *bucket;
527*0Sstevel@tonic-gate 	ipsa_t *assoc;
528*0Sstevel@tonic-gate 	ip6_pkt_t ipp;
529*0Sstevel@tonic-gate 	in6_addr_t dst6;
530*0Sstevel@tonic-gate 	ipsa_t **sa;
531*0Sstevel@tonic-gate 	sadbp_t *sadbp;
532*0Sstevel@tonic-gate 	sa_family_t af;
533*0Sstevel@tonic-gate 
534*0Sstevel@tonic-gate 	data_mp = mp->b_cont;
535*0Sstevel@tonic-gate 	io = (ipsec_out_t *)mp->b_rptr;
536*0Sstevel@tonic-gate 
537*0Sstevel@tonic-gate 	if (proto == IPPROTO_ESP) {
538*0Sstevel@tonic-gate 		sa = &io->ipsec_out_esp_sa;
539*0Sstevel@tonic-gate 		sadbp = &esp_sadb;
540*0Sstevel@tonic-gate 	} else {
541*0Sstevel@tonic-gate 		ASSERT(proto == IPPROTO_AH);
542*0Sstevel@tonic-gate 		sa = &io->ipsec_out_ah_sa;
543*0Sstevel@tonic-gate 		sadbp = &ah_sadb;
544*0Sstevel@tonic-gate 	}
545*0Sstevel@tonic-gate 
546*0Sstevel@tonic-gate 	ASSERT(*sa == NULL);
547*0Sstevel@tonic-gate 
548*0Sstevel@tonic-gate 	if (io->ipsec_out_v4) {
549*0Sstevel@tonic-gate 		ipha_t *ipha = (ipha_t *)data_mp->b_rptr;
550*0Sstevel@tonic-gate 
551*0Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ipha) == IPV4_VERSION);
552*0Sstevel@tonic-gate 		dst = ip_get_dst(ipha);
553*0Sstevel@tonic-gate 		af = AF_INET;
554*0Sstevel@tonic-gate 
555*0Sstevel@tonic-gate 		/*
556*0Sstevel@tonic-gate 		 * NOTE:Getting the outbound association is considerably
557*0Sstevel@tonic-gate 		 *	painful.  ipsec_getassocbyconn() will require more
558*0Sstevel@tonic-gate 		 *	parameters as policy implementations mature.
559*0Sstevel@tonic-gate 		 */
560*0Sstevel@tonic-gate 		bucket = &sadbp->s_v4.sdb_of[OUTBOUND_HASH_V4(dst)];
561*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
562*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&dst;
563*0Sstevel@tonic-gate 	} else {
564*0Sstevel@tonic-gate 		ip6_t *ip6h = (ip6_t *)data_mp->b_rptr;
565*0Sstevel@tonic-gate 
566*0Sstevel@tonic-gate 		ASSERT(IPH_HDR_VERSION(ip6h) == IPV6_VERSION);
567*0Sstevel@tonic-gate 		dst6 = ip_get_dst_v6(ip6h, NULL);
568*0Sstevel@tonic-gate 		af = AF_INET6;
569*0Sstevel@tonic-gate 
570*0Sstevel@tonic-gate 		bzero(&ipp, sizeof (ipp));
571*0Sstevel@tonic-gate 
572*0Sstevel@tonic-gate 		/* Same NOTE: applies here! */
573*0Sstevel@tonic-gate 		bucket = &sadbp->s_v6.sdb_of[OUTBOUND_HASH_V6(dst6)];
574*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
575*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&dst6;
576*0Sstevel@tonic-gate 	}
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	mutex_enter(&bucket->isaf_lock);
579*0Sstevel@tonic-gate 	assoc = ipsec_getassocbyconn(bucket, io, src_ptr, dst_ptr, af, proto);
580*0Sstevel@tonic-gate 	mutex_exit(&bucket->isaf_lock);
581*0Sstevel@tonic-gate 
582*0Sstevel@tonic-gate 	if (assoc == NULL)
583*0Sstevel@tonic-gate 		return (B_FALSE);
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_DEAD) {
586*0Sstevel@tonic-gate 		IPSA_REFRELE(assoc);
587*0Sstevel@tonic-gate 		return (B_FALSE);
588*0Sstevel@tonic-gate 	}
589*0Sstevel@tonic-gate 
590*0Sstevel@tonic-gate 	ASSERT(assoc->ipsa_state != IPSA_STATE_LARVAL);
591*0Sstevel@tonic-gate 
592*0Sstevel@tonic-gate 	*sa = assoc;
593*0Sstevel@tonic-gate 	return (B_TRUE);
594*0Sstevel@tonic-gate }
595*0Sstevel@tonic-gate 
596*0Sstevel@tonic-gate /*
597*0Sstevel@tonic-gate  * Inbound IPsec SA selection.
598*0Sstevel@tonic-gate  */
599*0Sstevel@tonic-gate 
600*0Sstevel@tonic-gate ah_t *
601*0Sstevel@tonic-gate ipsec_inbound_ah_sa(mblk_t *mp)
602*0Sstevel@tonic-gate {
603*0Sstevel@tonic-gate 	mblk_t *ipsec_in;
604*0Sstevel@tonic-gate 	ipha_t *ipha;
605*0Sstevel@tonic-gate 	ipsa_t 	*assoc;
606*0Sstevel@tonic-gate 	ah_t *ah;
607*0Sstevel@tonic-gate 	isaf_t *hptr;
608*0Sstevel@tonic-gate 	ipsec_in_t *ii;
609*0Sstevel@tonic-gate 	boolean_t isv6;
610*0Sstevel@tonic-gate 	ip6_t *ip6h;
611*0Sstevel@tonic-gate 	int ah_offset;
612*0Sstevel@tonic-gate 	uint32_t *src_ptr, *dst_ptr;
613*0Sstevel@tonic-gate 	int pullup_len;
614*0Sstevel@tonic-gate 	sa_family_t af;
615*0Sstevel@tonic-gate 
616*0Sstevel@tonic-gate 	IP_AH_BUMP_STAT(in_requests);
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_CTL);
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 	ipsec_in = mp;
621*0Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in->b_rptr;
622*0Sstevel@tonic-gate 	mp = mp->b_cont;
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	ASSERT(mp->b_datap->db_type == M_DATA);
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	isv6 = !ii->ipsec_in_v4;
627*0Sstevel@tonic-gate 	if (isv6) {
628*0Sstevel@tonic-gate 		ip6h = (ip6_t *)mp->b_rptr;
629*0Sstevel@tonic-gate 		ah_offset = ipsec_ah_get_hdr_size_v6(mp, B_TRUE);
630*0Sstevel@tonic-gate 	} else {
631*0Sstevel@tonic-gate 		ipha = (ipha_t *)mp->b_rptr;
632*0Sstevel@tonic-gate 		ASSERT(ipha->ipha_protocol == IPPROTO_AH);
633*0Sstevel@tonic-gate 		ah_offset = ipha->ipha_version_and_hdr_length -
634*0Sstevel@tonic-gate 		    (uint8_t)((IP_VERSION << 4));
635*0Sstevel@tonic-gate 		ah_offset <<= 2;
636*0Sstevel@tonic-gate 	}
637*0Sstevel@tonic-gate 
638*0Sstevel@tonic-gate 	/*
639*0Sstevel@tonic-gate 	 * We assume that the IP header is pulled up until
640*0Sstevel@tonic-gate 	 * the options. We need to see whether we have the
641*0Sstevel@tonic-gate 	 * AH header in the same mblk or not.
642*0Sstevel@tonic-gate 	 */
643*0Sstevel@tonic-gate 	pullup_len = ah_offset + sizeof (ah_t);
644*0Sstevel@tonic-gate 	if (mp->b_rptr + pullup_len > mp->b_wptr) {
645*0Sstevel@tonic-gate 		if (!pullupmsg(mp, pullup_len)) {
646*0Sstevel@tonic-gate 			ipsecah_rl_strlog(0,  SL_WARN | SL_ERROR,
647*0Sstevel@tonic-gate 			    "ipsec_inbound_ah_sa: Small AH header\n");
648*0Sstevel@tonic-gate 			IP_AH_BUMP_STAT(in_discards);
649*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_in, B_TRUE, NULL, NULL,
650*0Sstevel@tonic-gate 			    &ipdrops_ah_bad_length, &ip_dropper);
651*0Sstevel@tonic-gate 			return (NULL);
652*0Sstevel@tonic-gate 		}
653*0Sstevel@tonic-gate 		if (isv6)
654*0Sstevel@tonic-gate 			ip6h = (ip6_t *)mp->b_rptr;
655*0Sstevel@tonic-gate 		else
656*0Sstevel@tonic-gate 			ipha = (ipha_t *)mp->b_rptr;
657*0Sstevel@tonic-gate 	}
658*0Sstevel@tonic-gate 
659*0Sstevel@tonic-gate 	ah = (ah_t *)(mp->b_rptr + ah_offset);
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate 	if (isv6) {
662*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
663*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ip6h->ip6_dst;
664*0Sstevel@tonic-gate 		hptr = ah_sadb.s_v6.sdb_if;
665*0Sstevel@tonic-gate 		af = AF_INET6;
666*0Sstevel@tonic-gate 	} else {
667*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
668*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ipha->ipha_dst;
669*0Sstevel@tonic-gate 		hptr = ah_sadb.s_v4.sdb_if;
670*0Sstevel@tonic-gate 		af = AF_INET;
671*0Sstevel@tonic-gate 	}
672*0Sstevel@tonic-gate 
673*0Sstevel@tonic-gate 	hptr += INBOUND_HASH(ah->ah_spi);
674*0Sstevel@tonic-gate 	mutex_enter(&hptr->isaf_lock);
675*0Sstevel@tonic-gate 	assoc = ipsec_getassocbyspi(hptr, ah->ah_spi, src_ptr, dst_ptr, af);
676*0Sstevel@tonic-gate 	mutex_exit(&hptr->isaf_lock);
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate 	if (assoc == NULL || assoc->ipsa_state == IPSA_STATE_DEAD) {
679*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(lookup_failure);
680*0Sstevel@tonic-gate 		IP_AH_BUMP_STAT(in_discards);
681*0Sstevel@tonic-gate 		ipsecah_in_assocfailure(ipsec_in, 0,
682*0Sstevel@tonic-gate 		    SL_ERROR | SL_CONSOLE | SL_WARN,
683*0Sstevel@tonic-gate 		    "ipsec_inbound_ah_sa: No association found for "
684*0Sstevel@tonic-gate 		    "spi 0x%x, dst addr %s\n",
685*0Sstevel@tonic-gate 		    ah->ah_spi, dst_ptr, af);
686*0Sstevel@tonic-gate 		if (assoc != NULL) {
687*0Sstevel@tonic-gate 			IPSA_REFRELE(assoc);
688*0Sstevel@tonic-gate 		}
689*0Sstevel@tonic-gate 		return (NULL);
690*0Sstevel@tonic-gate 	}
691*0Sstevel@tonic-gate 
692*0Sstevel@tonic-gate 	if (assoc->ipsa_state == IPSA_STATE_LARVAL) {
693*0Sstevel@tonic-gate 		/* Not fully baked; swap the packet under a rock until then */
694*0Sstevel@tonic-gate 		sadb_set_lpkt(assoc, ipsec_in);
695*0Sstevel@tonic-gate 		IPSA_REFRELE(assoc);
696*0Sstevel@tonic-gate 		return (NULL);
697*0Sstevel@tonic-gate 	}
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	/*
700*0Sstevel@tonic-gate 	 * Save a reference to the association so that it can
701*0Sstevel@tonic-gate 	 * be retrieved after execution. We free any AH SA reference
702*0Sstevel@tonic-gate 	 * already there (innermost SA "wins". The reference to
703*0Sstevel@tonic-gate 	 * the SA will also be used later when doing the policy checks.
704*0Sstevel@tonic-gate 	 */
705*0Sstevel@tonic-gate 	if (ii->ipsec_in_ah_sa != NULL) {
706*0Sstevel@tonic-gate 		IPSA_REFRELE(ii->ipsec_in_ah_sa);
707*0Sstevel@tonic-gate 	}
708*0Sstevel@tonic-gate 	ii->ipsec_in_ah_sa = assoc;
709*0Sstevel@tonic-gate 
710*0Sstevel@tonic-gate 	return (ah);
711*0Sstevel@tonic-gate }
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate esph_t *
714*0Sstevel@tonic-gate ipsec_inbound_esp_sa(mblk_t *ipsec_in_mp)
715*0Sstevel@tonic-gate {
716*0Sstevel@tonic-gate 	mblk_t *data_mp, *placeholder;
717*0Sstevel@tonic-gate 	uint32_t *src_ptr, *dst_ptr;
718*0Sstevel@tonic-gate 	ipsec_in_t *ii;
719*0Sstevel@tonic-gate 	ipha_t *ipha;
720*0Sstevel@tonic-gate 	ip6_t *ip6h;
721*0Sstevel@tonic-gate 	esph_t *esph;
722*0Sstevel@tonic-gate 	ipsa_t *ipsa;
723*0Sstevel@tonic-gate 	isaf_t *bucket;
724*0Sstevel@tonic-gate 	uint_t preamble;
725*0Sstevel@tonic-gate 	sa_family_t af;
726*0Sstevel@tonic-gate 	boolean_t isv6;
727*0Sstevel@tonic-gate 
728*0Sstevel@tonic-gate 	IP_ESP_BUMP_STAT(in_requests);
729*0Sstevel@tonic-gate 	ASSERT(ipsec_in_mp->b_datap->db_type == M_CTL);
730*0Sstevel@tonic-gate 
731*0Sstevel@tonic-gate 	/* We have IPSEC_IN already! */
732*0Sstevel@tonic-gate 	ii = (ipsec_in_t *)ipsec_in_mp->b_rptr;
733*0Sstevel@tonic-gate 	data_mp = ipsec_in_mp->b_cont;
734*0Sstevel@tonic-gate 
735*0Sstevel@tonic-gate 	ASSERT(ii->ipsec_in_type == IPSEC_IN);
736*0Sstevel@tonic-gate 
737*0Sstevel@tonic-gate 	isv6 = !ii->ipsec_in_v4;
738*0Sstevel@tonic-gate 	if (isv6) {
739*0Sstevel@tonic-gate 		ip6h = (ip6_t *)data_mp->b_rptr;
740*0Sstevel@tonic-gate 	} else {
741*0Sstevel@tonic-gate 		ipha = (ipha_t *)data_mp->b_rptr;
742*0Sstevel@tonic-gate 	}
743*0Sstevel@tonic-gate 
744*0Sstevel@tonic-gate 	/*
745*0Sstevel@tonic-gate 	 * Put all data into one mblk if it's not there already.
746*0Sstevel@tonic-gate 	 * XXX This is probably bad long-term.  Figure out better ways of doing
747*0Sstevel@tonic-gate 	 * this.  Much of the inbound path depends on all of the data being
748*0Sstevel@tonic-gate 	 * in one mblk.
749*0Sstevel@tonic-gate 	 *
750*0Sstevel@tonic-gate 	 * XXX Jumbogram issues will have to be dealt with here.
751*0Sstevel@tonic-gate 	 * If the plen is 0, we'll have to scan for a HBH header with the
752*0Sstevel@tonic-gate 	 * actual packet length.
753*0Sstevel@tonic-gate 	 */
754*0Sstevel@tonic-gate 	if (data_mp->b_datap->db_ref > 1 ||
755*0Sstevel@tonic-gate 	    (data_mp->b_wptr - data_mp->b_rptr) <
756*0Sstevel@tonic-gate 	    (isv6 ? (ntohs(ip6h->ip6_plen) + sizeof (ip6_t))
757*0Sstevel@tonic-gate 		: ntohs(ipha->ipha_length))) {
758*0Sstevel@tonic-gate 		placeholder = msgpullup(data_mp, -1);
759*0Sstevel@tonic-gate 		if (placeholder == NULL) {
760*0Sstevel@tonic-gate 			IP_ESP_BUMP_STAT(in_discards);
761*0Sstevel@tonic-gate 			/*
762*0Sstevel@tonic-gate 			 * TODO: Extract inbound interface from the IPSEC_IN
763*0Sstevel@tonic-gate 			 * message's ii->ipsec_in_rill_index.
764*0Sstevel@tonic-gate 			 */
765*0Sstevel@tonic-gate 			ip_drop_packet(ipsec_in_mp, B_TRUE, NULL, NULL,
766*0Sstevel@tonic-gate 			    &ipdrops_esp_nomem, &ip_dropper);
767*0Sstevel@tonic-gate 			return (NULL);
768*0Sstevel@tonic-gate 		} else {
769*0Sstevel@tonic-gate 			/* Reset packet with new pulled up mblk. */
770*0Sstevel@tonic-gate 			freemsg(data_mp);
771*0Sstevel@tonic-gate 			data_mp = placeholder;
772*0Sstevel@tonic-gate 			ipsec_in_mp->b_cont = data_mp;
773*0Sstevel@tonic-gate 		}
774*0Sstevel@tonic-gate 	}
775*0Sstevel@tonic-gate 
776*0Sstevel@tonic-gate 	/*
777*0Sstevel@tonic-gate 	 * Find the ESP header, point the address pointers at the appropriate
778*0Sstevel@tonic-gate 	 * IPv4/IPv6 places.
779*0Sstevel@tonic-gate 	 */
780*0Sstevel@tonic-gate 	if (isv6) {
781*0Sstevel@tonic-gate 		ip6h = (ip6_t *)data_mp->b_rptr;
782*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ip6h->ip6_src;
783*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ip6h->ip6_dst;
784*0Sstevel@tonic-gate 		if (ip6h->ip6_nxt != IPPROTO_ESP) {
785*0Sstevel@tonic-gate 			/* There are options that need to be processed. */
786*0Sstevel@tonic-gate 			preamble = ip_hdr_length_v6(data_mp, ip6h);
787*0Sstevel@tonic-gate 		} else {
788*0Sstevel@tonic-gate 			preamble = sizeof (ip6_t);
789*0Sstevel@tonic-gate 		}
790*0Sstevel@tonic-gate 
791*0Sstevel@tonic-gate 		bucket = esp_sadb.s_v6.sdb_if;
792*0Sstevel@tonic-gate 		af = AF_INET6;
793*0Sstevel@tonic-gate 	} else {
794*0Sstevel@tonic-gate 		ipha = (ipha_t *)data_mp->b_rptr;
795*0Sstevel@tonic-gate 		src_ptr = (uint32_t *)&ipha->ipha_src;
796*0Sstevel@tonic-gate 		dst_ptr = (uint32_t *)&ipha->ipha_dst;
797*0Sstevel@tonic-gate 		preamble = IPH_HDR_LENGTH(ipha);
798*0Sstevel@tonic-gate 
799*0Sstevel@tonic-gate 		bucket = esp_sadb.s_v4.sdb_if;
800*0Sstevel@tonic-gate 		af = AF_INET;
801*0Sstevel@tonic-gate 	}
802*0Sstevel@tonic-gate 
803*0Sstevel@tonic-gate 	esph = (esph_t *)(data_mp->b_rptr + preamble);
804*0Sstevel@tonic-gate 
805*0Sstevel@tonic-gate 	/* Since hash is common on inbound (SPI value), hash here. */
806*0Sstevel@tonic-gate 	bucket += INBOUND_HASH(esph->esph_spi);
807*0Sstevel@tonic-gate 	mutex_enter(&bucket->isaf_lock);
808*0Sstevel@tonic-gate 	ipsa = ipsec_getassocbyspi(bucket, esph->esph_spi, src_ptr, dst_ptr,
809*0Sstevel@tonic-gate 	    af);
810*0Sstevel@tonic-gate 	mutex_exit(&bucket->isaf_lock);
811*0Sstevel@tonic-gate 
812*0Sstevel@tonic-gate 	if (ipsa == NULL || ipsa->ipsa_state == IPSA_STATE_DEAD) {
813*0Sstevel@tonic-gate 		/*  This is a loggable error!  AUDIT ME! */
814*0Sstevel@tonic-gate 		IP_ESP_BUMP_STAT(lookup_failure);
815*0Sstevel@tonic-gate 		IP_ESP_BUMP_STAT(in_discards);
816*0Sstevel@tonic-gate 		ipsecesp_in_assocfailure(ipsec_in_mp, 0,
817*0Sstevel@tonic-gate 		    SL_ERROR | SL_CONSOLE | SL_WARN,
818*0Sstevel@tonic-gate 		    "ipsec_inbound_esp_sa: No association found for "
819*0Sstevel@tonic-gate 		    "spi 0x%x, dst addr %s\n",
820*0Sstevel@tonic-gate 		    esph->esph_spi, dst_ptr, af);
821*0Sstevel@tonic-gate 		if (ipsa != NULL) {
822*0Sstevel@tonic-gate 			IPSA_REFRELE(ipsa);
823*0Sstevel@tonic-gate 		}
824*0Sstevel@tonic-gate 		return (NULL);
825*0Sstevel@tonic-gate 	}
826*0Sstevel@tonic-gate 
827*0Sstevel@tonic-gate 	if (ipsa->ipsa_state == IPSA_STATE_LARVAL) {
828*0Sstevel@tonic-gate 		/* Not fully baked; swap the packet under a rock until then */
829*0Sstevel@tonic-gate 		sadb_set_lpkt(ipsa, ipsec_in_mp);
830*0Sstevel@tonic-gate 		IPSA_REFRELE(ipsa);
831*0Sstevel@tonic-gate 		return (NULL);
832*0Sstevel@tonic-gate 	}
833*0Sstevel@tonic-gate 
834*0Sstevel@tonic-gate 	/*
835*0Sstevel@tonic-gate 	 * Save a reference to the association so that it can
836*0Sstevel@tonic-gate 	 * be retrieved after execution. We free any AH SA reference
837*0Sstevel@tonic-gate 	 * already there (innermost SA "wins". The reference to
838*0Sstevel@tonic-gate 	 * the SA will also be used later when doing the policy checks.
839*0Sstevel@tonic-gate 	 */
840*0Sstevel@tonic-gate 	if (ii->ipsec_in_esp_sa != NULL) {
841*0Sstevel@tonic-gate 		IPSA_REFRELE(ii->ipsec_in_esp_sa);
842*0Sstevel@tonic-gate 	}
843*0Sstevel@tonic-gate 	ii->ipsec_in_esp_sa = ipsa;
844*0Sstevel@tonic-gate 
845*0Sstevel@tonic-gate 	return (esph);
846*0Sstevel@tonic-gate }
847