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