Print this page
Bayard's initial drop, needs finishing, or at least testing.
        
*** 19,28 ****
--- 19,29 ----
   * CDDL HEADER END
   */
  /*
   * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
   * Use is subject to license terms.
+  * Copyright (c) 2012 Nexenta Systems, Inc. All rights reserved.
   */
  
  #include <sys/types.h>
  #include <sys/stream.h>
  #include <sys/stropts.h>
*** 70,90 ****
  /*
   * This source file contains Security Association Database (SADB) common
   * routines.  They are linked in with the AH module.  Since AH has no chance
   * of falling under export control, it was safe to link it in there.
   */
- 
- static mblk_t *sadb_extended_acquire(ipsec_selector_t *, ipsec_policy_t *,
-     ipsec_action_t *, boolean_t, uint32_t, uint32_t, sadb_sens_t *,
-     netstack_t *);
  static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
  static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
                              netstack_t *);
  static void sadb_destroy(sadb_t *, netstack_t *);
  static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
  static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
  static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
  
  static time_t sadb_add_time(time_t, uint64_t);
  static void lifetime_fuzz(ipsa_t *);
  static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
  static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
--- 71,96 ----
  /*
   * This source file contains Security Association Database (SADB) common
   * routines.  They are linked in with the AH module.  Since AH has no chance
   * of falling under export control, it was safe to link it in there.
   */
  static ipsa_t *sadb_torch_assoc(isaf_t *, ipsa_t *);
  static void sadb_destroy_acqlist(iacqf_t **, uint_t, boolean_t,
                              netstack_t *);
  static void sadb_destroy(sadb_t *, netstack_t *);
  static mblk_t *sadb_sa2msg(ipsa_t *, sadb_msg_t *);
  static ts_label_t *sadb_label_from_sens(sadb_sens_t *, uint64_t *);
  static sadb_sens_t *sadb_make_sens_ext(ts_label_t *tsl, int *len);
+ /* Args named here, as the booleans can be hard to distinguish */
+ static mblk_t *sadb_construct_acqmsg(ipsacq_t *acqrec, ipsec_selector_t *sel,
+     ipsec_action_t *ap, ipsec_policy_t *pp, netstack_t *ns, sadb_sens_t *sens,
+     boolean_t need_esp, boolean_t tunnel_mode, boolean_t extended,
+     boolean_t with_prop);
+ static uint8_t *sadb_construct_eprop(const ipsec_action_t *,
+     const ipsec_policy_t *, netstack_t *, const uint8_t *, const uint8_t *);
+ static void sadb_insert_prop(sadb_prop_t *, const ipsec_action_t *,
+     netstack_t *, uint_t, boolean_t);
  
  static time_t sadb_add_time(time_t, uint64_t);
  static void lifetime_fuzz(ipsa_t *);
  static void age_pair_peer_list(templist_t *, sadb_t *, boolean_t);
  static int get_ipsa_pair(ipsa_query_t *, ipsap_t *, int *);
*** 97,106 ****
--- 103,119 ----
   * ipsacq_maxpackets is defined here to make it tunable
   * from /etc/system.
   */
  extern uint64_t ipsacq_maxpackets;
  
+ /*
+  * Allocation size for sin_t/sin6_t in address extensions. We allocate IPv6
+  * because it's the larger of the two, and we roundup because the type isn't
+  * defined to guarantee 64-bit alignment.
+  */
+ #define SADB_SOCKADDR_SIZE      (roundup(sizeof (sin6_t), sizeof (uint64_t)))
+ 
  #define SET_EXPIRE(sa, delta, exp) {                            \
          if (((sa)->ipsa_ ## delta) != 0) {                              \
                  (sa)->ipsa_ ## exp = sadb_add_time((sa)->ipsa_addtime,  \
                          (sa)->ipsa_ ## delta);                          \
          }                                                               \
*** 116,126 ****
--- 129,166 ----
                          (sa)->ipsa_ ## exp =                            \
                              MIN((sa)->ipsa_ ## exp, tmp);               \
          }                                                               \
  }
  
+ /* Warning: watch for evaluation issues with complex args */
+ #define INITIALIZE_SAMSG(samsg, type)           \
+         (samsg)->sadb_msg_version = PF_KEY_V2,  \
+         (samsg)->sadb_msg_type = (type),        \
+         (samsg)->sadb_msg_errno = 0,            \
+         (samsg)->sadb_msg_reserved = 0
  
+ /* Warning: watch for evaluation issues with complex args */
+ #define ERRNO_SAMSG(samsg, errno)                               \
+         (samsg)->sadb_msg_len = SADB_8TO64(sizeof (*samsg)),    \
+         (samsg)->sadb_msg_errno = (errno),                      \
+         (samsg)->sadb_x_msg_diagnostic = 0
+ 
+ /*
+  * Warning: watch for evaluation issues with complex args. This is a rough,
+  * conservative calculation (e.g. combined mode encr algs can perform both
+  * encr/auth and ipsecconf drops auth algs in combinations). This is
+  * nevertheless reasonable, given that the kernel doesn't make or guarantee
+  * optimizations reducing the combination space.
+  */
+ #define CALC_COMBS(limit, ipss, need_esp)               {       \
+         limit = (need_esp) ?                                    \
+             (ipss)->ipsec_nalgs[IPSEC_ALG_AUTH] *               \
+             (ipss)->ipsec_nalgs[IPSEC_ALG_ENCR]                 \
+             : (ipss)->ipsec_nalgs[IPSEC_ALG_AUTH];              \
+         ASSERT((limit) > 0);                                    \
+ }
+ 
  /* wrap the macro so we can pass it as a function pointer */
  void
  sadb_sa_refrele(void *target)
  {
          IPSA_REFRELE(((ipsa_t *)target));
*** 973,1056 ****
  
          return (newbie);
  }
  
  /*
!  * Initialize a SADB address extension at the address specified by addrext.
!  * Return a pointer to the end of the new address extension.
   */
  static uint8_t *
! sadb_make_addr_ext(uint8_t *start, uint8_t *end, uint16_t exttype,
      sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
  {
          struct sockaddr_in *sin;
          struct sockaddr_in6 *sin6;
!         uint8_t *cur = start;
          int addrext_len;
-         int sin_len;
          sadb_address_t *addrext = (sadb_address_t *)cur;
  
!         if (cur == NULL)
!                 return (NULL);
  
          cur += sizeof (*addrext);
          if (cur > end)
                  return (NULL);
  
          addrext->sadb_address_proto = proto;
          addrext->sadb_address_prefixlen = prefix;
          addrext->sadb_address_reserved = 0;
          addrext->sadb_address_exttype = exttype;
  
          switch (af) {
          case AF_INET:
-                 sin = (struct sockaddr_in *)cur;
-                 sin_len = sizeof (*sin);
-                 cur += sin_len;
-                 if (cur > end)
-                         return (NULL);
- 
                  sin->sin_family = af;
                  bzero(sin->sin_zero, sizeof (sin->sin_zero));
                  sin->sin_port = port;
                  IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
                  break;
          case AF_INET6:
-                 sin6 = (struct sockaddr_in6 *)cur;
-                 sin_len = sizeof (*sin6);
-                 cur += sin_len;
-                 if (cur > end)
-                         return (NULL);
- 
                  bzero(sin6, sizeof (*sin6));
                  sin6->sin6_family = af;
                  sin6->sin6_port = port;
                  IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
                  break;
          }
  
!         addrext_len = roundup(cur - start, sizeof (uint64_t));
!         addrext->sadb_address_len = SADB_8TO64(addrext_len);
  
!         cur = start + addrext_len;
!         if (cur > end)
!                 cur = NULL;
  
          return (cur);
  }
  
  /*
!  * Construct a key management cookie extension.
   */
- 
  static uint8_t *
! sadb_make_kmc_ext(uint8_t *cur, uint8_t *end, uint32_t kmp, uint32_t kmc)
  {
          sadb_x_kmc_t *kmcext = (sadb_x_kmc_t *)cur;
  
!         if (cur == NULL)
!                 return (NULL);
  
          cur += sizeof (*kmcext);
  
          if (cur > end)
                  return (NULL);
--- 1013,1213 ----
  
          return (newbie);
  }
  
  /*
!  * Takes two uint8_t (bounds on buffer in which to construct extension) and an
!  * addr (address to write into extension) pointer, a uint16_t (type of address
!  * in extension), and af, port, proto, and prefix values (further extension
!  * content). Returns a byte-aligned pointer to the end of the extension, which
!  * is of variable length depending on the address family.
   */
  static uint8_t *
! sadb_make_addr_ext(const uint8_t *start, const uint8_t *end, uint16_t exttype,
      sa_family_t af, uint32_t *addr, uint16_t port, uint8_t proto, int prefix)
  {
          struct sockaddr_in *sin;
          struct sockaddr_in6 *sin6;
!         uint8_t *cur = (uint8_t *)start;
          int addrext_len;
          sadb_address_t *addrext = (sadb_address_t *)cur;
  
!         ASSERT(cur != NULL && end != NULL);
  
          cur += sizeof (*addrext);
+         sin = (struct sockaddr_in *)cur;
+         sin6 = (struct sockaddr_in6 *)cur;
+         cur += (af == AF_INET) ? sizeof (*sin) : sizeof (*sin6);
+ 
+         addrext_len = roundup(cur - start, sizeof (uint64_t));
+         cur = (uint8_t *)start + addrext_len;
+ 
          if (cur > end)
                  return (NULL);
  
          addrext->sadb_address_proto = proto;
          addrext->sadb_address_prefixlen = prefix;
          addrext->sadb_address_reserved = 0;
          addrext->sadb_address_exttype = exttype;
+         addrext->sadb_address_len = SADB_8TO64(addrext_len);
  
          switch (af) {
          case AF_INET:
                  sin->sin_family = af;
                  bzero(sin->sin_zero, sizeof (sin->sin_zero));
                  sin->sin_port = port;
                  IPSA_COPY_ADDR(&sin->sin_addr, addr, af);
                  break;
          case AF_INET6:
                  bzero(sin6, sizeof (*sin6));
                  sin6->sin6_family = af;
                  sin6->sin6_port = port;
                  IPSA_COPY_ADDR(&sin6->sin6_addr, addr, af);
                  break;
          }
  
!         return (cur);
! }
  
! /*
!  * Takes ipsec_selector_t (address information used in forming addr
!  * extensions) and ipsec_policy_t (contains pointer to selector key used in
!  * tunnel mode) pointers, tunnel mode boolean, and creates address extensions
!  * inside message contents bounds checked by byte-aligned start and end
!  * pointers. Returns new value for cur pointer or NULL on failure.
!  * XXX TODO: Original packet contents go here.
!  */
! static uint8_t *
! sadb_sel_to_addrexts(const ipsec_selector_t *sel, const ipsec_policy_t *pp,
!     const ipsec_action_t *ap, const uint8_t *start, const uint8_t *end,
!     boolean_t tunnel_mode)
! {
!         uint8_t         proto, pfxlen, *cur = (uint8_t *)start;
!         ipsec_selkey_t  *ipsl;
!         sa_family_t     af;
!         uint16_t        lport, rport;
!         uint32_t        *saddrptr, *daddrptr;
  
+         if (tunnel_mode) {
+                 /*
+                  * Form inner address extensions based NOT on the inner
+                  * selectors (i.e. the packet data), but on the policy's
+                  * selector key (i.e. the policy's selector information).
+                  *
+                  * NOTE:  The position of IPv4 and IPv6 addresses is the
+                  * same in ipsec_selkey_t (unless the compiler does very
+                  * strange things with unions, consult your local C language
+                  * lawyer for details).
+                  */
+                 ASSERT(pp != NULL);
+ 
+                 ipsl = &(pp->ipsp_sel->ipsl_key);
+                 if (ipsl->ipsl_valid & IPSL_IPV4) {
+                         af = AF_INET;
+                         ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
+                         ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
+                 } else {
+                         af = AF_INET6;
+                         ASSERT(sel->ips_protocol == IPPROTO_IPV6);
+                         ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
+                 }
+ 
+                 if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
+                         saddrptr = (uint32_t *)(&ipsl->ipsl_local);
+                         pfxlen = ipsl->ipsl_local_pfxlen;
+                 } else {
+                         saddrptr = (uint32_t *)(&ipv6_all_zeros);
+                         pfxlen = 0;
+                 }
+                 /* XXX What about ICMP type/code? */
+                 lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
+                     ipsl->ipsl_lport : 0;
+                 proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
+                     ipsl->ipsl_proto : 0;
+ 
+                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
+                     af, saddrptr, lport, proto, pfxlen);
+                 if (cur == NULL)
+                         goto done;
+ 
+                 if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
+                         daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
+                         pfxlen = ipsl->ipsl_remote_pfxlen;
+                 } else {
+                         daddrptr = (uint32_t *)(&ipv6_all_zeros);
+                         pfxlen = 0;
+                 }
+                 /* XXX What about ICMP type/code? */
+                 rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
+                     ipsl->ipsl_rport : 0;
+ 
+                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
+                     af, daddrptr, rport, proto, pfxlen);
+                 if (cur == NULL)
+                         goto done;
+ 
+                 /*
+                  * TODO - if we go to RFC 3408's dream of transport mode
+                  * IP-in-IP _with_ inner-packet address selectors, we'll need
+                  * to further distinguish tunnel mode here.  For now, having
+                  * inner addresses and/or ports is sufficient.
+                  *
+                  * Meanwhile, whack proto/ports to reflect IP-in-IP for the
+                  * outer addresses.
+                  */
+                 proto = sel->ips_protocol;      /* Either _ENCAP or _IPV6 */
+                 lport = rport = 0;
+         } else if ((ap != NULL) && (!ap->ipa_want_unique)) {
+                 /* Not in tunnel mode, action doesn't want pop from pkt */
+                 proto = 0;
+                 lport = 0;
+                 rport = 0;
+                 if (pp != NULL) {
+                         ipsl = &(pp->ipsp_sel->ipsl_key);
+                         if (ipsl->ipsl_valid & IPSL_PROTOCOL)
+                                 proto = ipsl->ipsl_proto;
+                         if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
+                                 rport = ipsl->ipsl_rport;
+                         if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
+                                 lport = ipsl->ipsl_lport;
+                 }
+         } else {
+                 /* Not in tunnel mode, action wants pop from pkt */
+                 proto = sel->ips_protocol;
+                 lport = sel->ips_local_port;
+                 rport = sel->ips_remote_port;
+         }
+ 
+         af = sel->ips_isv4 ? AF_INET : AF_INET6;
+ 
+         /*
+          * NOTE:  The position of IPv4 and IPv6 addresses is the same
+          * in ipsec_selector_t.
+          */
+         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
+             (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
+         if (cur == NULL)
+                 goto done;
+ 
+         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
+             (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
+ done:
          return (cur);
  }
  
  /*
!  * Use byte aligned buffer defined by cur and end pointers to create a key
!  * management extension using kmc and kmp uint32_t parameters.
   */
  static uint8_t *
! sadb_make_kmc_ext(const uint8_t *start, const uint8_t *end,
!     uint32_t kmp, uint32_t kmc)
  {
+         uint8_t         *cur = (uint8_t *)start;
          sadb_x_kmc_t    *kmcext = (sadb_x_kmc_t *)cur;
  
!         ASSERT(cur != NULL && end != NULL);
  
          cur += sizeof (*kmcext);
  
          if (cur > end)
                  return (NULL);
*** 3196,3206 ****
                   */
                  newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
                  newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
                  newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
  
!                 mutex_enter(&ipss->ipsec_alg_lock);
                  alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
                      [newbie->ipsa_auth_alg];
                  if (alg != NULL && ALG_VALID(alg)) {
                          newbie->ipsa_amech.cm_type = alg->alg_mech_type;
                          newbie->ipsa_amech.cm_param =
--- 3353,3363 ----
                   */
                  newbie->ipsa_kcfauthkey.ck_format = CRYPTO_KEY_RAW;
                  newbie->ipsa_kcfauthkey.ck_length = newbie->ipsa_authkeybits;
                  newbie->ipsa_kcfauthkey.ck_data = newbie->ipsa_authkey;
  
!                 rw_enter(&ipss->ipsec_alg_lock, RW_READER);
                  alg = ipss->ipsec_alglists[IPSEC_ALG_AUTH]
                      [newbie->ipsa_auth_alg];
                  if (alg != NULL && ALG_VALID(alg)) {
                          newbie->ipsa_amech.cm_type = alg->alg_mech_type;
                          newbie->ipsa_amech.cm_param =
*** 3209,3219 ****
                          newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
                  } else {
                          newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
                  }
                  error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
!                 mutex_exit(&ipss->ipsec_alg_lock);
                  if (error != 0) {
                          mutex_exit(&newbie->ipsa_lock);
                          /*
                           * An error here indicates that alg is the wrong type
                           * (IE: not authentication) or its not in the alg tables
--- 3366,3376 ----
                          newbie->ipsa_mac_len = (size_t)alg->alg_datalen;
                  } else {
                          newbie->ipsa_amech.cm_type = CRYPTO_MECHANISM_INVALID;
                  }
                  error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_AUTH);
!                 rw_exit(&ipss->ipsec_alg_lock);
                  if (error != 0) {
                          mutex_exit(&newbie->ipsa_lock);
                          /*
                           * An error here indicates that alg is the wrong type
                           * (IE: not authentication) or its not in the alg tables
*** 3226,3236 ****
                          goto error;
                  }
          }
  
          if (ekey != NULL) {
!                 mutex_enter(&ipss->ipsec_alg_lock);
                  async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
                      IPSEC_ALGS_EXEC_ASYNC);
                  alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
                      [newbie->ipsa_encr_alg];
  
--- 3383,3393 ----
                          goto error;
                  }
          }
  
          if (ekey != NULL) {
!                 rw_enter(&ipss->ipsec_alg_lock, RW_READER);
                  async = async || (ipss->ipsec_algs_exec_mode[IPSEC_ALG_ENCR] ==
                      IPSEC_ALGS_EXEC_ASYNC);
                  alg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
                      [newbie->ipsa_encr_alg];
  
*** 3259,3269 ****
                          newbie->ipsa_emech.cm_param = NULL;
                          newbie->ipsa_emech.cm_param_len = 0;
                  } else {
                          newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
                  }
!                 mutex_exit(&ipss->ipsec_alg_lock);
  
                  /*
                   * The byte stream following the sadb_key_t is made up of:
                   * key bytes, [salt bytes], [IV initial value]
                   * All of these have variable length. The IV is typically
--- 3416,3426 ----
                          newbie->ipsa_emech.cm_param = NULL;
                          newbie->ipsa_emech.cm_param_len = 0;
                  } else {
                          newbie->ipsa_emech.cm_type = CRYPTO_MECHANISM_INVALID;
                  }
!                 rw_exit(&ipss->ipsec_alg_lock);
  
                  /*
                   * The byte stream following the sadb_key_t is made up of:
                   * key bytes, [salt bytes], [IV initial value]
                   * All of these have variable length. The IV is typically
*** 3371,3383 ****
                   */
                  newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
                  newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
                  newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
  
!                 mutex_enter(&ipss->ipsec_alg_lock);
                  error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
!                 mutex_exit(&ipss->ipsec_alg_lock);
                  if (error != 0) {
                          mutex_exit(&newbie->ipsa_lock);
                          /* See above for error explanation. */
                          *diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
                          goto error;
--- 3528,3540 ----
                   */
                  newbie->ipsa_kcfencrkey.ck_format = CRYPTO_KEY_RAW;
                  newbie->ipsa_kcfencrkey.ck_length = newbie->ipsa_encrkeybits;
                  newbie->ipsa_kcfencrkey.ck_data = newbie->ipsa_encrkey;
  
!                 rw_enter(&ipss->ipsec_alg_lock, RW_READER);
                  error = ipsec_create_ctx_tmpl(newbie, IPSEC_ALG_ENCR);
!                 rw_exit(&ipss->ipsec_alg_lock);
                  if (error != 0) {
                          mutex_exit(&newbie->ipsa_lock);
                          /* See above for error explanation. */
                          *diagnostic = SADB_X_DIAGNOSTIC_BAD_CTX;
                          goto error;
*** 3797,3812 ****
          mp = mp->b_cont;
          end = mp->b_wptr + alloclen;
  
          samsg = (sadb_msg_t *)mp->b_wptr;
          mp->b_wptr += sizeof (*samsg);
!         samsg->sadb_msg_version = PF_KEY_V2;
!         samsg->sadb_msg_type = SADB_EXPIRE;
!         samsg->sadb_msg_errno = 0;
          samsg->sadb_msg_satype = assoc->ipsa_type;
          samsg->sadb_msg_len = SADB_8TO64(alloclen);
-         samsg->sadb_msg_reserved = 0;
          samsg->sadb_msg_seq = 0;
          samsg->sadb_msg_pid = 0;
  
          saext = (sadb_sa_t *)mp->b_wptr;
          mp->b_wptr += sizeof (*saext);
--- 3954,3966 ----
          mp = mp->b_cont;
          end = mp->b_wptr + alloclen;
  
          samsg = (sadb_msg_t *)mp->b_wptr;
          mp->b_wptr += sizeof (*samsg);
!         INITIALIZE_SAMSG(samsg, SADB_EXPIRE);
          samsg->sadb_msg_satype = assoc->ipsa_type;
          samsg->sadb_msg_len = SADB_8TO64(alloclen);
          samsg->sadb_msg_seq = 0;
          samsg->sadb_msg_pid = 0;
  
          saext = (sadb_sa_t *)mp->b_wptr;
          mp->b_wptr += sizeof (*saext);
*** 4094,4104 ****
          return (retval);
  }
  
  /*
   * Called by a consumer protocol to do ther dirty work of reaping dead
!  * Security Associations.
   *
   * NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
   * SA's that are already marked DEAD, so expired SA's are only reaped
   * the second time sadb_ager() runs.
   */
--- 4248,4258 ----
          return (retval);
  }
  
  /*
   * Called by a consumer protocol to do ther dirty work of reaping dead
!  * Security Associations and outstanding acquire records.
   *
   * NOTE: sadb_age_assoc() marks expired SA's as DEAD but only removed
   * SA's that are already marked DEAD, so expired SA's are only reaped
   * the second time sadb_ager() runs.
   */
*** 4808,4817 ****
--- 4962,4973 ----
  {
          ipsacq_t *walker;
          sa_family_t fam;
          uint32_t blank_address[4] = {0, 0, 0, 0};
  
+         ASSERT(MUTEX_HELD(&bucket->iacqf_lock));
+ 
          if (isrc == NULL) {
                  ASSERT(idst == NULL);
                  isrc = idst = blank_address;
          }
  
*** 4841,4908 ****
  
          return (walker);
  }
  
  /*
!  * For this mblk, insert a new acquire record.  Assume bucket contains addrs
!  * of all of the same length.  Give up (and drop) if memory
!  * cannot be allocated for a new one; otherwise, invoke callback to
!  * send the acquire up..
   *
!  * In cases where we need both AH and ESP, add the SA to the ESP ACQUIRE
!  * list.  The ah_add_sa_finish() routines can look at the packet's attached
!  * attributes and handle this case specially.
   */
  void
  sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
      boolean_t need_esp)
  {
!         mblk_t  *asyncmp;
          sadbp_t *spp;
          sadb_t *sp;
          ipsacq_t *newbie;
          iacqf_t *bucket;
-         mblk_t *extended;
          ipha_t *ipha = (ipha_t *)datamp->b_rptr;
          ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
!         uint32_t *src, *dst, *isrc, *idst;
          ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
          ipsec_action_t *ap = ixa->ixa_ipsec_action;
          sa_family_t af;
!         int hashoffset;
!         uint32_t seq;
          uint64_t unique_id = 0;
          ipsec_selector_t sel;
          boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
          ts_label_t      *tsl = NULL;
          netstack_t      *ns = ixa->ixa_ipst->ips_netstack;
          ipsec_stack_t   *ipss = ns->netstack_ipsec;
          sadb_sens_t     *sens = NULL;
-         int             sens_len;
- 
-         ASSERT((pp != NULL) || (ap != NULL));
- 
-         ASSERT(need_ah != NULL || need_esp != NULL);
- 
-         /* Assign sadb pointers */
-         if (need_esp) { /* ESP for AH+ESP */
                  ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
- 
-                 spp = &espstack->esp_sadb;
-         } else {
                  ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
  
!                 spp = &ahstack->ah_sadb;
!         }
          sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
  
          if (is_system_labeled())
                  tsl = ixa->ixa_tsl;
  
          if (ap == NULL)
                  ap = pp->ipsp_act;
- 
          ASSERT(ap != NULL);
  
          if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
                  unique_id = SA_FORM_UNIQUE_ID(ixa);
  
--- 4997,5068 ----
  
          return (walker);
  }
  
  /*
!  * Take a pointers to mblk_t (packet for which we need to acquire an SA) and
!  * ip_xmit_attr_t (transmit attributes used to generate or retrieve acquire
!  * record) and two booleans need_ah and need_esp, one but not both of which
!  * must be true. Acquire records are stored in hash buckets, and we assume
!  * bucket contains addrs of all of the same length. If this is a new acquire
!  * record, we generate an acquire samsg to send to protocol keysock layer,
!  * which assumes ownership from there. If we run into problems along the way,
!  * we generate errors if possible and drop packets if need be. Before sending
!  * to keysock, we simply unlock the acquire record and let the ager deal with
!  * releasing locks and freeing resources.
   *
!  * This code is called by the IP stack when trying to send a packet for which
!  * all necessary SAs can't be found to include in ip_xmit_attr_t. Be aware of
!  * the following case: you need both ESP and AH and have SAs for neither. In
!  * that case both need_esp and need_ah are true, but we go with need_esp, as
!  * ESP will call us back for an AH acquire if it's successful and the AH SA
!  * still missing. It can also be that the packet needs both, but an SA already
!  * exists for one, in which case only the missing one will be flagged as
!  * needed, although the ipsec_action_t has want flags for both.
   */
  void
  sadb_acquire(mblk_t *datamp, ip_xmit_attr_t *ixa, boolean_t need_ah,
      boolean_t need_esp)
  {
!         mblk_t          *asyncmp, *regular, *extended, *prop_m, *eprop_m;
          sadbp_t *spp;
          sadb_t *sp;
          ipsacq_t *newbie;
          iacqf_t *bucket;
          ipha_t *ipha = (ipha_t *)datamp->b_rptr;
          ip6_t *ip6h = (ip6_t *)datamp->b_rptr;
!         uint32_t        seq, *src, *dst, *isrc, *idst;
          ipsec_policy_t *pp = ixa->ixa_ipsec_policy;
          ipsec_action_t *ap = ixa->ixa_ipsec_action;
          sa_family_t af;
!         int             hashoffset, sens_len;
          uint64_t unique_id = 0;
+         uint_t          propsize, epropsize, combs_limit;
+         uint8_t         *start, *end;
+         sadb_msg_t      *samsg;
+         sadb_prop_t     *prop, *eprop;
          ipsec_selector_t sel;
          boolean_t tunnel_mode = (ixa->ixa_flags & IXAF_IPSEC_TUNNEL) != 0;
          ts_label_t      *tsl = NULL;
          netstack_t      *ns = ixa->ixa_ipst->ips_netstack;
          ipsec_stack_t   *ipss = ns->netstack_ipsec;
          sadb_sens_t     *sens = NULL;
          ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
          ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
+         queue_t         *q;
  
!         ASSERT(need_ah || need_esp);
!         ASSERT((ap != NULL) || (pp != NULL));
! 
!         spp = need_esp ? &espstack->esp_sadb : &ahstack->ah_sadb;
          sp = (ixa->ixa_flags & IXAF_IS_IPV4) ? &spp->s_v4 : &spp->s_v6;
  
          if (is_system_labeled())
                  tsl = ixa->ixa_tsl;
  
          if (ap == NULL)
                  ap = pp->ipsp_act;
          ASSERT(ap != NULL);
  
          if (ap->ipa_act.ipa_apply.ipp_use_unique || tunnel_mode)
                  unique_id = SA_FORM_UNIQUE_ID(ixa);
  
*** 4911,4934 ****
           *
           * Immediately, make sure the ACQUIRE sequence number doesn't slip
           * below the lowest point allowed in the kernel.  (In other words,
           * make sure the high bit on the sequence number is set.)
           */
- 
          seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
  
          if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
                  src = (uint32_t *)&ipha->ipha_src;
                  dst = (uint32_t *)&ipha->ipha_dst;
                  af = AF_INET;
                  hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
                  ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
          } else {
                  ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
                  src = (uint32_t *)&ip6h->ip6_src;
                  dst = (uint32_t *)&ip6h->ip6_dst;
                  af = AF_INET6;
                  hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
                  ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
          }
  
          if (tunnel_mode) {
--- 5071,5095 ----
           *
           * Immediately, make sure the ACQUIRE sequence number doesn't slip
           * below the lowest point allowed in the kernel.  (In other words,
           * make sure the high bit on the sequence number is set.)
           */
          seq = keysock_next_seq(ns) | IACQF_LOWEST_SEQ;
  
          if (IPH_HDR_VERSION(ipha) == IP_VERSION) {
                  src = (uint32_t *)&ipha->ipha_src;
                  dst = (uint32_t *)&ipha->ipha_dst;
                  af = AF_INET;
+                 ip6h = NULL;
                  hashoffset = OUTBOUND_HASH_V4(sp, ipha->ipha_dst);
                  ASSERT(ixa->ixa_flags & IXAF_IS_IPV4);
          } else {
                  ASSERT(IPH_HDR_VERSION(ipha) == IPV6_VERSION);
                  src = (uint32_t *)&ip6h->ip6_src;
                  dst = (uint32_t *)&ip6h->ip6_dst;
                  af = AF_INET6;
+                 ipha = NULL;
                  hashoffset = OUTBOUND_HASH_V6(sp, ip6h->ip6_dst);
                  ASSERT(!(ixa->ixa_flags & IXAF_IS_IPV4));
          }
  
          if (tunnel_mode) {
*** 4950,4971 ****
          } else {
                  isrc = idst = NULL;
          }
  
          /*
!          * Check buckets to see if there is an existing entry.  If so,
!          * grab it.  sadb_checkacquire locks newbie if found.
           */
          bucket = &(sp->sdb_acq[hashoffset]);
          mutex_enter(&bucket->iacqf_lock);
          newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
              unique_id, tsl);
  
          if (newbie == NULL) {
-                 /*
-                  * Otherwise, allocate a new one.
-                  */
                  newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
                  if (newbie == NULL) {
                          mutex_exit(&bucket->iacqf_lock);
                          ip_drop_packet(datamp, B_FALSE, NULL,
                              DROPPER(ipss, ipds_sadb_acquire_nomem),
--- 5111,5130 ----
          } else {
                  isrc = idst = NULL;
          }
  
          /*
!          * Check bucket for existing matching entry. If so, grab it. On match
!          * sadb_checkacquire returns locked newbie.
           */
          bucket = &(sp->sdb_acq[hashoffset]);
          mutex_enter(&bucket->iacqf_lock);
          newbie = sadb_checkacquire(bucket, ap, pp, src, dst, isrc, idst,
              unique_id, tsl);
  
+         /* If not found, initialize a new one and insert into chain. */
          if (newbie == NULL) {
                  newbie = kmem_zalloc(sizeof (*newbie), KM_NOSLEEP);
                  if (newbie == NULL) {
                          mutex_exit(&bucket->iacqf_lock);
                          ip_drop_packet(datamp, B_FALSE, NULL,
                              DROPPER(ipss, ipds_sadb_acquire_nomem),
*** 4998,5046 ****
           * disperse blame for lock contention.
           *
           * we might be able to dispense with acquire record locks entirely..
           * just use the bucket locks..
           */
- 
          mutex_exit(&bucket->iacqf_lock);
  
          /*
           * This assert looks silly for now, but we may need to enter newbie's
!          * mutex during a search.
           */
          ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
  
!         /*
!          * Make the ip_xmit_attr_t into something we can queue.
!          * If no memory it frees datamp.
!          */
          asyncmp = ip_xmit_attr_to_mblk(ixa);
          if (asyncmp != NULL)
                  linkb(asyncmp, datamp);
  
!         /* Queue up packet.  Use b_next. */
! 
          if (asyncmp == NULL) {
-                 /* Statistics for allocation failure */
                  if (ixa->ixa_flags & IXAF_IS_IPV4) {
                          BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
                              ipIfStatsOutDiscards);
                  } else {
                          BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
                              ipIfStatsOutDiscards);
                  }
                  ip_drop_output("No memory for asyncmp", datamp, NULL);
                  freemsg(datamp);
!         } else if (newbie->ipsacq_numpackets == 0) {
!                 /* First one. */
                  newbie->ipsacq_mp = asyncmp;
                  newbie->ipsacq_numpackets = 1;
                  newbie->ipsacq_expire = gethrestime_sec();
!                 /*
!                  * Extended ACQUIRE with both AH+ESP will use ESP's timeout
!                  * value.
!                  */
                  newbie->ipsacq_expire += *spp->s_acquire_timeout;
                  newbie->ipsacq_seq = seq;
                  newbie->ipsacq_addrfam = af;
  
                  newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
--- 5157,5196 ----
           * disperse blame for lock contention.
           *
           * we might be able to dispense with acquire record locks entirely..
           * just use the bucket locks..
           */
          mutex_exit(&bucket->iacqf_lock);
  
          /*
           * This assert looks silly for now, but we may need to enter newbie's
!          * mutex during a search. Confirms we got locked newbie from
!          * sadb_checkacquire.
           */
          ASSERT(MUTEX_HELD(&newbie->ipsacq_lock));
  
!         /* Make ip_xmit_attr_t into message we can queue, link packet data. */
          asyncmp = ip_xmit_attr_to_mblk(ixa);
          if (asyncmp != NULL)
                  linkb(asyncmp, datamp);
  
!         /* Bump appropriate discard stat & free datamp if allocation failed. */
          if (asyncmp == NULL) {
                  if (ixa->ixa_flags & IXAF_IS_IPV4) {
                          BUMP_MIB(&ixa->ixa_ipst->ips_ip_mib,
                              ipIfStatsOutDiscards);
                  } else {
                          BUMP_MIB(&ixa->ixa_ipst->ips_ip6_mib,
                              ipIfStatsOutDiscards);
                  }
                  ip_drop_output("No memory for asyncmp", datamp, NULL);
                  freemsg(datamp);
!         } else if (newbie->ipsacq_numpackets == 0) { /* Pkt queue forms here. */
                  newbie->ipsacq_mp = asyncmp;
                  newbie->ipsacq_numpackets = 1;
                  newbie->ipsacq_expire = gethrestime_sec();
!                 /* Extended ACQUIRE with AH+ESP uses ESP's timeout */
                  newbie->ipsacq_expire += *spp->s_acquire_timeout;
                  newbie->ipsacq_seq = seq;
                  newbie->ipsacq_addrfam = af;
  
                  newbie->ipsacq_srcport = ixa->ixa_ipsec_src_port;
*** 5060,5092 ****
                  } else {
                          newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
                  }
                  newbie->ipsacq_unique_id = unique_id;
  
!                 if (ixa->ixa_tsl != NULL) {
!                         label_hold(ixa->ixa_tsl);
!                         newbie->ipsacq_tsl = ixa->ixa_tsl;
                  }
!         } else {
!                 /* Scan to the end of the list & insert. */
                  mblk_t *lastone = newbie->ipsacq_mp;
  
                  while (lastone->b_next != NULL)
                          lastone = lastone->b_next;
                  lastone->b_next = asyncmp;
                  if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
                          newbie->ipsacq_numpackets = ipsacq_maxpackets;
                          lastone = newbie->ipsacq_mp;
                          newbie->ipsacq_mp = lastone->b_next;
                          lastone->b_next = NULL;
  
-                         /* Freeing the async message */
                          lastone = ip_xmit_attr_free_mblk(lastone);
                          ip_drop_packet(lastone, B_FALSE, NULL,
                              DROPPER(ipss, ipds_sadb_acquire_toofull),
                              &ipss->ipsec_sadb_dropper);
!                 } else {
                          IP_ACQUIRE_STAT(ipss, qhiwater,
                              newbie->ipsacq_numpackets);
                  }
          }
  
--- 5210,5241 ----
                  } else {
                          newbie->ipsacq_proto = ixa->ixa_ipsec_proto;
                  }
                  newbie->ipsacq_unique_id = unique_id;
  
!                 if (tsl != NULL) {
!                         label_hold(tsl);
!                         newbie->ipsacq_tsl = tsl;
                  }
!         } else { /* Attempt to join packet queue as b_next. */
                  mblk_t *lastone = newbie->ipsacq_mp;
  
                  while (lastone->b_next != NULL)
                          lastone = lastone->b_next;
                  lastone->b_next = asyncmp;
+                 /* Queue maxed: set counter to max, unchain, free & drop pkt */
                  if (newbie->ipsacq_numpackets++ == ipsacq_maxpackets) {
                          newbie->ipsacq_numpackets = ipsacq_maxpackets;
                          lastone = newbie->ipsacq_mp;
                          newbie->ipsacq_mp = lastone->b_next;
                          lastone->b_next = NULL;
  
                          lastone = ip_xmit_attr_free_mblk(lastone);
                          ip_drop_packet(lastone, B_FALSE, NULL,
                              DROPPER(ipss, ipds_sadb_acquire_toofull),
                              &ipss->ipsec_sadb_dropper);
!                 } else { /* Successfully queued */
                          IP_ACQUIRE_STAT(ipss, qhiwater,
                              newbie->ipsacq_numpackets);
                  }
          }
  
*** 5098,5122 ****
  
          newbie->ipsacq_srcaddr = src;
          newbie->ipsacq_dstaddr = dst;
  
          /*
!          * If the acquire record has more than one queued packet, we've
!          * already sent an ACQUIRE, and don't need to repeat ourself.
           */
!         if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1) {
!                 /* I have an acquire outstanding already! */
!                 mutex_exit(&newbie->ipsacq_lock);
!                 return;
          }
  
!         if (!keysock_extended_reg(ns))
!                 goto punt_extended;
          /*
!          * Construct an extended ACQUIRE.  There are logging
!          * opportunities here in failure cases.
           */
          bzero(&sel, sizeof (sel));
          sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
          if (tunnel_mode) {
                  sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
                      IPPROTO_ENCAP : IPPROTO_IPV6;
--- 5247,5308 ----
  
          newbie->ipsacq_srcaddr = src;
          newbie->ipsacq_dstaddr = dst;
  
          /*
!          * Sequence number mismatch or previously populated packet queue means
!          * we retrieved an already-pending ACQUIRE record and needn't repeat
!          * ourself. Unlock and return.
           */
!         if (newbie->ipsacq_seq != seq || newbie->ipsacq_numpackets > 1)
!                 goto unlock_acqrec;
! 
!         /*
!          * Even if we fail before sending to keysock, starting with a NULL
!          * queue pointer, if gets this far, it counts as an acquire request.
!          */
!         if (need_esp) {
!                 ESP_BUMP_STAT(espstack, acquire_requests);
!                 q = espstack->esp_pfkey_q;
!         } else {
!                 AH_BUMP_STAT(ahstack, acquire_requests);
!                 q = ahstack->ah_pfkey_q;
          }
  
!         if (q == NULL)
!                 goto unlock_acqrec;
! 
!         /* Initializes keysock M_CTL message for regular acquire. */
!         regular = sadb_keysock_out(0);
!         if (regular == NULL)
!                 goto unlock_acqrec;
! 
          /*
!          * Check keysock stack to make sure we don't have extended register
!          * pending. If not, have keysock initialize M_CTL msg for extended
!          * acquire. If pending, set extended to NULL so we ignore it hereafter.
           */
+         if (keysock_extended_reg(ns)) {
+                 extended = sadb_keysock_out(0);
+                 if (extended == NULL)
+                         goto bail_and_free_regular;
+         } else {
+                 extended = NULL;
+         }
+ 
+         if (tsl != NULL) {
+                 /*
+                  * XXX MLS correct condition here?
+                  * XXX MLS other credential attributes in acquire?
+                  * XXX malloc failure?  don't fall back to original?
+                  */
+                 sens = sadb_make_sens_ext(tsl, &sens_len);
+ 
+                 if (sens == NULL)
+                         goto bail_extended;
+         }
+         /* re-initialize selector using ixa and ipha */
          bzero(&sel, sizeof (sel));
          sel.ips_isv4 = (ixa->ixa_flags & IXAF_IS_IPV4) != 0;
          if (tunnel_mode) {
                  sel.ips_protocol = (ixa->ixa_ipsec_inaf == AF_INET) ?
                      IPPROTO_ENCAP : IPPROTO_IPV6;
*** 5134,5182 ****
          } else {
                  sel.ips_local_addr_v6 = ip6h->ip6_src;
                  sel.ips_remote_addr_v6 = ip6h->ip6_dst;
          }
  
!         extended = sadb_keysock_out(0);
!         if (extended == NULL)
!                 goto punt_extended;
  
!         if (ixa->ixa_tsl != NULL) {
                  /*
!                  * XXX MLS correct condition here?
!                  * XXX MLS other credential attributes in acquire?
!                  * XXX malloc failure?  don't fall back to original?
                   */
!                 sens = sadb_make_sens_ext(ixa->ixa_tsl, &sens_len);
  
!                 if (sens == NULL) {
!                         freeb(extended);
!                         goto punt_extended;
                  }
-         }
  
!         extended->b_cont = sadb_extended_acquire(&sel, pp, ap, tunnel_mode,
!             seq, 0, sens, ns);
  
!         if (sens != NULL)
!                 kmem_free(sens, sens_len);
  
!         if (extended->b_cont == NULL) {
!                 freeb(extended);
!                 goto punt_extended;
          }
  
!         /*
!          * Send an ACQUIRE message (and possible an extended ACQUIRE) based on
!          * this new record.  The send-acquire callback assumes that acqrec is
!          * already locked.
!          */
!         (*spp->s_acqfn)(newbie, extended, ns);
          return;
  
! punt_extended:
!         (*spp->s_acqfn)(newbie, NULL, ns);
  }
  
  /*
   * Unlink and free an acquire record.
   */
--- 5320,5419 ----
          } else {
                  sel.ips_local_addr_v6 = ip6h->ip6_src;
                  sel.ips_remote_addr_v6 = ip6h->ip6_dst;
          }
  
!         /* Tack message containing sadb_msg_t onto keysock regular M_CTL */
!         regular->b_cont = sadb_construct_acqmsg(newbie, &sel, ap, pp, ns, sens,
!             need_esp, tunnel_mode, B_FALSE, B_FALSE); /* regular, no props */
!         /* We have to do this, no matter the result of previous call */
!         if (sens != NULL)
!                 kmem_free(sens, sens_len);
  
!         if (regular->b_cont == NULL)
!                 goto bail_extended;
! 
          /*
!          * If there's no extended pending, duplicate regular samsg, tacking it
!          * on as the b_cont of the keysock-generated extended M_CTL.
           */
!         if (extended != NULL) {
!                 extended->b_cont = dupb(regular->b_cont);
!                 if (extended->b_cont == NULL)
!                         goto bail_extended;
!         }
  
!         rw_enter(&ipss->ipsec_alg_lock, RW_READER);
!         CALC_COMBS(combs_limit, ipss, need_esp);
!         propsize = sizeof (sadb_prop_t) + (combs_limit * sizeof (sadb_comb_t));
! 
!         if ((prop_m = allocb(propsize, BPRI_HI)) == NULL)
!                 goto bail_and_unlock;
! 
!         if (extended != NULL) {
!                 epropsize = sizeof (sadb_prop_t)
!                     + (combs_limit * sizeof (sadb_x_ecomb_t));
!                 if ((eprop_m = allocb(epropsize, BPRI_HI)) == NULL)
!                         goto bail_and_unlock;
          }
  
!         prop = (sadb_prop_t *)prop_m->b_rptr;
!         sadb_insert_prop(prop, ap, ns, combs_limit, need_esp);
!         if (prop == NULL) {
!                 goto bail_and_unlock;
!         /* 0 length prop is error, mark regular samsg a dud, & freeb prop_m */
!         } else {
!                 samsg = (sadb_msg_t *)regular->b_cont->b_rptr;
  
!                 if (prop->sadb_prop_len == 0) {
!                         ERRNO_SAMSG(samsg, ENOENT);
!                         freeb(prop_m);
!                 }
!                 samsg->sadb_msg_len += prop->sadb_prop_len;
!                 prop_m->b_wptr += SADB_64TO8(prop->sadb_prop_len);
!                 regular->b_cont->b_cont = prop_m;
!         }
  
!         if (extended != NULL) {
!                 start = (uint8_t *)eprop_m->b_rptr;
!                 end = start + epropsize;
!                 eprop =
!                     (sadb_prop_t *)sadb_construct_eprop(ap, pp, ns, start, end);
!                 if (eprop == NULL)
!                         goto bail_and_unlock;
!                 /* If 0 ecombs, mark extended samsg a dud, and freeb eprop_m */
!                 else {
!                         samsg = (sadb_msg_t *)extended->b_cont->b_rptr;
! 
!                         if (eprop->sadb_x_prop_numecombs == 0) {
!                                 ERRNO_SAMSG(samsg, ENOENT);
!                                 freeb(eprop_m);
                          }
+                         samsg->sadb_msg_len += eprop->sadb_prop_len;
+                         eprop_m->b_wptr += SADB_64TO8(eprop->sadb_prop_len);
+                         extended->b_cont->b_cont = eprop_m;
+                 }
+         }
  
!         rw_exit(&ipss->ipsec_alg_lock);
!         mutex_exit(&newbie->ipsacq_lock);
! 
!         if (extended != NULL)
!                 putnext(q, extended);
!         putnext(q, regular);
          return;
  
! /* We used a lot of b_cont mblk chaining, so we need to use freemsg. */
! bail_and_unlock:
!         rw_exit(&ipss->ipsec_alg_lock);
! bail_extended:
!         if (extended != NULL)
!                 freemsg(extended);
! bail_and_free_regular:
!         freemsg(regular);
! unlock_acqrec:
!         mutex_exit(&newbie->ipsacq_lock);
  }
  
  /*
   * Unlink and free an acquire record.
   */
*** 5186,5195 ****
--- 5423,5433 ----
          mblk_t          *mp;
          ipsec_stack_t   *ipss = ns->netstack_ipsec;
  
          ASSERT(MUTEX_HELD(acqrec->ipsacq_linklock));
  
+         /* XXX Should references be released before mutex is acquired? */
          if (acqrec->ipsacq_policy != NULL) {
                  IPPOL_REFRELE(acqrec->ipsacq_policy);
          }
          if (acqrec->ipsacq_act != NULL) {
                  IPACT_REFRELE(acqrec->ipsacq_act);
*** 5260,5300 ****
  /*
   * Create an algorithm descriptor for an extended ACQUIRE.  Filter crypto
   * framework's view of reality vs. IPsec's.  EF's wins, BTW.
   */
  static uint8_t *
! sadb_new_algdesc(uint8_t *start, uint8_t *limit,
      sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
      uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
  {
!         uint8_t *cur = start;
          ipsec_alginfo_t *algp;
          sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
  
          cur += sizeof (*algdesc);
!         if (cur >= limit)
                  return (NULL);
  
          ecomb->sadb_x_ecomb_numalgs++;
  
          /*
           * Normalize vs. crypto framework's limits.  This way, you can specify
           * a stronger policy, and when the framework loads a stronger version,
           * you can just keep plowing w/o rewhacking your SPD.
           */
-         mutex_enter(&ipss->ipsec_alg_lock);
          algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
              IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
!         if (algp == NULL) {
!                 mutex_exit(&ipss->ipsec_alg_lock);
                  return (NULL);  /* Algorithm doesn't exist.  Fail gracefully. */
-         }
          if (minbits < algp->alg_ef_minbits)
                  minbits = algp->alg_ef_minbits;
          if (maxbits > algp->alg_ef_maxbits)
                  maxbits = algp->alg_ef_maxbits;
-         mutex_exit(&ipss->ipsec_alg_lock);
  
          algdesc->sadb_x_algdesc_reserved = SADB_8TO1(algp->alg_saltlen);
          algdesc->sadb_x_algdesc_satype = satype;
          algdesc->sadb_x_algdesc_algtype = algtype;
          algdesc->sadb_x_algdesc_alg = alg;
--- 5498,5536 ----
  /*
   * Create an algorithm descriptor for an extended ACQUIRE.  Filter crypto
   * framework's view of reality vs. IPsec's.  EF's wins, BTW.
   */
  static uint8_t *
! sadb_new_algdesc(const uint8_t *start, const uint8_t *end,
      sadb_x_ecomb_t *ecomb, uint8_t satype, uint8_t algtype,
      uint8_t alg, uint16_t minbits, uint16_t maxbits, ipsec_stack_t *ipss)
  {
!         uint8_t *cur = (uint8_t *)start;
          ipsec_alginfo_t *algp;
          sadb_x_algdesc_t *algdesc = (sadb_x_algdesc_t *)cur;
  
+         ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
+ 
          cur += sizeof (*algdesc);
!         if (cur >= end)
                  return (NULL);
  
          ecomb->sadb_x_ecomb_numalgs++;
  
          /*
           * Normalize vs. crypto framework's limits.  This way, you can specify
           * a stronger policy, and when the framework loads a stronger version,
           * you can just keep plowing w/o rewhacking your SPD.
           */
          algp = ipss->ipsec_alglists[(algtype == SADB_X_ALGTYPE_AUTH) ?
              IPSEC_ALG_AUTH : IPSEC_ALG_ENCR][alg];
!         if (algp == NULL)
                  return (NULL);  /* Algorithm doesn't exist.  Fail gracefully. */
          if (minbits < algp->alg_ef_minbits)
                  minbits = algp->alg_ef_minbits;
          if (maxbits > algp->alg_ef_maxbits)
                  maxbits = algp->alg_ef_maxbits;
  
          algdesc->sadb_x_algdesc_reserved = SADB_8TO1(algp->alg_saltlen);
          algdesc->sadb_x_algdesc_satype = satype;
          algdesc->sadb_x_algdesc_algtype = algtype;
          algdesc->sadb_x_algdesc_alg = alg;
*** 5303,5334 ****
  
          return (cur);
  }
  
  /*
!  * Convert the given ipsec_action_t into an ecomb starting at *ecomb
!  * which must fit before *limit
!  *
!  * return NULL if we ran out of room or a pointer to the end of the ecomb.
   */
  static uint8_t *
! sadb_action_to_ecomb(uint8_t *start, uint8_t *limit, ipsec_action_t *act,
!     netstack_t *ns)
  {
!         uint8_t *cur = start;
          sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
          ipsec_prot_t *ipp;
          ipsec_stack_t *ipss = ns->netstack_ipsec;
  
          cur += sizeof (*ecomb);
!         if (cur >= limit)
                  return (NULL);
  
!         ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
  
-         ipp = &act->ipa_act.ipa_apply;
- 
          ecomb->sadb_x_ecomb_numalgs = 0;
          ecomb->sadb_x_ecomb_reserved = 0;
          ecomb->sadb_x_ecomb_reserved2 = 0;
          /*
           * No limits on allocations, since we really don't support that
--- 5539,5571 ----
  
          return (cur);
  }
  
  /*
!  * Use buffer defined by byte-aligned pointers start and end to convert
!  * ipsec_action_t pointer act into an ecomb, using alg data hanging off of
!  * netstack_t pointer ns. Return NULL rather than overrun buffer, otherwise
!  * pointer to end of ecomb (which should be exact size of buffer).
   */
  static uint8_t *
! sadb_action_to_ecomb(const uint8_t *start, const uint8_t *end,
!     const ipsec_action_t *act, netstack_t *ns)
  {
!         uint8_t *cur = (uint8_t *)start;
          sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)cur;
          ipsec_prot_t *ipp;
          ipsec_stack_t *ipss = ns->netstack_ipsec;
  
+         ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
+         ASSERT(act->ipa_act.ipa_type == IPSEC_ACT_APPLY);
+ 
          cur += sizeof (*ecomb);
!         if (cur >= end)
                  return (NULL);
  
!         ipp = &((ipsec_action_t *)act)->ipa_act.ipa_apply;
  
          ecomb->sadb_x_ecomb_numalgs = 0;
          ecomb->sadb_x_ecomb_reserved = 0;
          ecomb->sadb_x_ecomb_reserved2 = 0;
          /*
           * No limits on allocations, since we really don't support that
*** 5348,5377 ****
          ecomb->sadb_x_ecomb_hard_addtime = 0;
          ecomb->sadb_x_ecomb_soft_usetime = 0;
          ecomb->sadb_x_ecomb_hard_usetime = 0;
  
          if (ipp->ipp_use_ah) {
!                 cur = sadb_new_algdesc(cur, limit, ecomb,
                      SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
                      ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
                  if (cur == NULL)
                          return (NULL);
                  ipsecah_fill_defs(ecomb, ns);
          }
  
          if (ipp->ipp_use_esp) {
                  if (ipp->ipp_use_espa) {
!                         cur = sadb_new_algdesc(cur, limit, ecomb,
                              SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
                              ipp->ipp_esp_auth_alg,
                              ipp->ipp_espa_minbits,
                              ipp->ipp_espa_maxbits, ipss);
                          if (cur == NULL)
                                  return (NULL);
                  }
  
!                 cur = sadb_new_algdesc(cur, limit, ecomb,
                      SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
                      ipp->ipp_encr_alg,
                      ipp->ipp_espe_minbits,
                      ipp->ipp_espe_maxbits, ipss);
                  if (cur == NULL)
--- 5585,5614 ----
          ecomb->sadb_x_ecomb_hard_addtime = 0;
          ecomb->sadb_x_ecomb_soft_usetime = 0;
          ecomb->sadb_x_ecomb_hard_usetime = 0;
  
          if (ipp->ipp_use_ah) {
!                 cur = sadb_new_algdesc(cur, end, ecomb,
                      SADB_SATYPE_AH, SADB_X_ALGTYPE_AUTH, ipp->ipp_auth_alg,
                      ipp->ipp_ah_minbits, ipp->ipp_ah_maxbits, ipss);
                  if (cur == NULL)
                          return (NULL);
                  ipsecah_fill_defs(ecomb, ns);
          }
  
          if (ipp->ipp_use_esp) {
                  if (ipp->ipp_use_espa) {
!                         cur = sadb_new_algdesc(cur, end, ecomb,
                              SADB_SATYPE_ESP, SADB_X_ALGTYPE_AUTH,
                              ipp->ipp_esp_auth_alg,
                              ipp->ipp_espa_minbits,
                              ipp->ipp_espa_maxbits, ipss);
                          if (cur == NULL)
                                  return (NULL);
                  }
  
!                 cur = sadb_new_algdesc(cur, end, ecomb,
                      SADB_SATYPE_ESP, SADB_X_ALGTYPE_CRYPT,
                      ipp->ipp_encr_alg,
                      ipp->ipp_espe_minbits,
                      ipp->ipp_espe_maxbits, ipss);
                  if (cur == NULL)
*** 5477,5869 ****
  }
  
  /* End XXX label-library-leakage */
  
  /*
!  * Construct an extended ACQUIRE message based on a selector and the resulting
!  * IPsec action.
!  *
!  * NOTE: This is used by both inverse ACQUIRE and actual ACQUIRE
!  * generation. As a consequence, expect this function to evolve
!  * rapidly.
   */
! static mblk_t *
! sadb_extended_acquire(ipsec_selector_t *sel, ipsec_policy_t *pol,
!     ipsec_action_t *act, boolean_t tunnel_mode, uint32_t seq, uint32_t pid,
!     sadb_sens_t *sens, netstack_t *ns)
  {
!         mblk_t *mp;
!         sadb_msg_t *samsg;
!         uint8_t *start, *cur, *end;
!         uint32_t *saddrptr, *daddrptr;
!         sa_family_t af;
!         sadb_prop_t *eprop;
!         ipsec_action_t *ap, *an;
!         ipsec_selkey_t *ipsl;
!         uint8_t proto, pfxlen;
!         uint16_t lport, rport;
!         uint32_t kmp, kmc;
  
!         /*
!          * Find the action we want sooner rather than later..
!          */
!         an = NULL;
!         if (pol == NULL) {
!                 ap = act;
!         } else {
!                 ap = pol->ipsp_act;
  
!                 if (ap != NULL)
!                         an = ap->ipa_next;
!         }
  
!         /*
!          * Just take a swag for the allocation for now.  We can always
!          * alter it later.
!          */
! #define SADB_EXTENDED_ACQUIRE_SIZE      4096
!         mp = allocb(SADB_EXTENDED_ACQUIRE_SIZE, BPRI_HI);
!         if (mp == NULL)
!                 return (NULL);
  
!         start = mp->b_rptr;
!         end = start + SADB_EXTENDED_ACQUIRE_SIZE;
  
!         cur = start;
  
!         samsg = (sadb_msg_t *)cur;
!         cur += sizeof (*samsg);
  
-         samsg->sadb_msg_version = PF_KEY_V2;
-         samsg->sadb_msg_type = SADB_ACQUIRE;
-         samsg->sadb_msg_errno = 0;
-         samsg->sadb_msg_reserved = 0;
-         samsg->sadb_msg_satype = 0;
-         samsg->sadb_msg_seq = seq;
-         samsg->sadb_msg_pid = pid;
- 
-         if (tunnel_mode) {
                  /*
!                  * Form inner address extensions based NOT on the inner
!                  * selectors (i.e. the packet data), but on the policy's
!                  * selector key (i.e. the policy's selector information).
!                  *
!                  * NOTE:  The position of IPv4 and IPv6 addresses is the
!                  * same in ipsec_selkey_t (unless the compiler does very
!                  * strange things with unions, consult your local C language
!                  * lawyer for details).
                   */
!                 ASSERT(pol != NULL);
! 
!                 ipsl = &(pol->ipsp_sel->ipsl_key);
!                 if (ipsl->ipsl_valid & IPSL_IPV4) {
!                         af = AF_INET;
!                         ASSERT(sel->ips_protocol == IPPROTO_ENCAP);
!                         ASSERT(!(ipsl->ipsl_valid & IPSL_IPV6));
!                 } else {
!                         af = AF_INET6;
!                         ASSERT(sel->ips_protocol == IPPROTO_IPV6);
!                         ASSERT(ipsl->ipsl_valid & IPSL_IPV6);
                  }
  
!                 if (ipsl->ipsl_valid & IPSL_LOCAL_ADDR) {
!                         saddrptr = (uint32_t *)(&ipsl->ipsl_local);
!                         pfxlen = ipsl->ipsl_local_pfxlen;
!                 } else {
!                         saddrptr = (uint32_t *)(&ipv6_all_zeros);
!                         pfxlen = 0;
                  }
-                 /* XXX What about ICMP type/code? */
-                 lport = (ipsl->ipsl_valid & IPSL_LOCAL_PORT) ?
-                     ipsl->ipsl_lport : 0;
-                 proto = (ipsl->ipsl_valid & IPSL_PROTOCOL) ?
-                     ipsl->ipsl_proto : 0;
  
!                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
!                     af, saddrptr, lport, proto, pfxlen);
!                 if (cur == NULL) {
!                         freeb(mp);
!                         return (NULL);
!                 }
  
!                 if (ipsl->ipsl_valid & IPSL_REMOTE_ADDR) {
!                         daddrptr = (uint32_t *)(&ipsl->ipsl_remote);
!                         pfxlen = ipsl->ipsl_remote_pfxlen;
                  } else {
!                         daddrptr = (uint32_t *)(&ipv6_all_zeros);
!                         pfxlen = 0;
                  }
-                 /* XXX What about ICMP type/code? */
-                 rport = (ipsl->ipsl_valid & IPSL_REMOTE_PORT) ?
-                     ipsl->ipsl_rport : 0;
  
!                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
!                     af, daddrptr, rport, proto, pfxlen);
!                 if (cur == NULL) {
!                         freeb(mp);
!                         return (NULL);
!                 }
!                 /*
!                  * TODO  - if we go to 3408's dream of transport mode IP-in-IP
!                  * _with_ inner-packet address selectors, we'll need to further
!                  * distinguish tunnel mode here.  For now, having inner
!                  * addresses and/or ports is sufficient.
!                  *
!                  * Meanwhile, whack proto/ports to reflect IP-in-IP for the
!                  * outer addresses.
!                  */
!                 proto = sel->ips_protocol;      /* Either _ENCAP or _IPV6 */
!                 lport = rport = 0;
!         } else if ((ap != NULL) && (!ap->ipa_want_unique)) {
!                 proto = 0;
!                 lport = 0;
!                 rport = 0;
!                 if (pol != NULL) {
!                         ipsl = &(pol->ipsp_sel->ipsl_key);
!                         if (ipsl->ipsl_valid & IPSL_PROTOCOL)
!                                 proto = ipsl->ipsl_proto;
!                         if (ipsl->ipsl_valid & IPSL_REMOTE_PORT)
!                                 rport = ipsl->ipsl_rport;
!                         if (ipsl->ipsl_valid & IPSL_LOCAL_PORT)
!                                 lport = ipsl->ipsl_lport;
!                 }
          } else {
!                 proto = sel->ips_protocol;
!                 lport = sel->ips_local_port;
!                 rport = sel->ips_remote_port;
          }
  
-         af = sel->ips_isv4 ? AF_INET : AF_INET6;
- 
          /*
!          * NOTE:  The position of IPv4 and IPv6 addresses is the same in
!          * ipsec_selector_t.
           */
-         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
-             (uint32_t *)(&sel->ips_local_addr_v6), lport, proto, 0);
  
!         if (cur == NULL) {
!                 freeb(mp);
!                 return (NULL);
          }
  
!         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
!             (uint32_t *)(&sel->ips_remote_addr_v6), rport, proto, 0);
! 
!         if (cur == NULL) {
!                 freeb(mp);
!                 return (NULL);
          }
  
!         if (sens != NULL) {
!                 uint8_t *sensext = cur;
!                 int senslen = SADB_64TO8(sens->sadb_sens_len);
  
!                 cur += senslen;
!                 if (cur > end) {
!                         freeb(mp);
!                         return (NULL);
!                 }
!                 bcopy(sens, sensext, senslen);
!         }
  
!         /*
!          * This section will change a lot as policy evolves.
!          * For now, it'll be relatively simple.
           */
!         eprop = (sadb_prop_t *)cur;
          cur += sizeof (*eprop);
!         if (cur > end) {
!                 /* no space left */
!                 freeb(mp);
                  return (NULL);
-         }
  
          eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
          eprop->sadb_x_prop_ereserved = 0;
          eprop->sadb_x_prop_numecombs = 0;
          eprop->sadb_prop_replay = 32;   /* default */
  
-         kmc = kmp = 0;
- 
          for (; ap != NULL; ap = an) {
-                 an = (pol != NULL) ? ap->ipa_next : NULL;
- 
                  /*
!                  * Skip non-IPsec policies
                   */
                  if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
                          continue;
  
-                 if (ap->ipa_act.ipa_apply.ipp_km_proto)
-                         kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
-                 if (ap->ipa_act.ipa_apply.ipp_km_cookie)
-                         kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
                  if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
                          eprop->sadb_prop_replay =
                              ap->ipa_act.ipa_apply.ipp_replay_depth;
                  }
  
                  cur = sadb_action_to_ecomb(cur, end, ap, ns);
!                 if (cur == NULL) { /* no space */
!                         freeb(mp);
                          return (NULL);
-                 }
                  eprop->sadb_x_prop_numecombs++;
          }
  
-         if (eprop->sadb_x_prop_numecombs == 0) {
                  /*
!                  * This will happen if we fail to find a policy
!                  * allowing for IPsec processing.
!                  * Construct an error message.
                   */
!                 samsg->sadb_msg_len = SADB_8TO64(sizeof (*samsg));
!                 samsg->sadb_msg_errno = ENOENT;
!                 samsg->sadb_x_msg_diagnostic = 0;
!                 return (mp);
!         }
  
!         if ((kmp != 0) || (kmc != 0)) {
!                 cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
!                 if (cur == NULL) {
!                         freeb(mp);
                          return (NULL);
                  }
-         }
  
!         eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)eprop);
!         samsg->sadb_msg_len = SADB_8TO64(cur - start);
!         mp->b_wptr = cur;
  
!         return (mp);
  }
  
  /*
!  * Generic setup of an RFC 2367 ACQUIRE message.  Caller sets satype.
!  *
!  * NOTE: This function acquires alg_lock as a side-effect if-and-only-if we
!  * succeed (i.e. return non-NULL).  Caller MUST release it.  This is to
!  * maximize code consolidation while preventing algorithm changes from messing
!  * with the callers finishing touches on the ACQUIRE itself.
   */
! mblk_t *
! sadb_setup_acquire(ipsacq_t *acqrec, uint8_t satype, ipsec_stack_t *ipss)
  {
!         uint_t allocsize;
!         mblk_t *pfkeymp, *msgmp;
!         sa_family_t af;
          uint8_t *cur, *end;
          sadb_msg_t *samsg;
!         uint16_t sport_typecode;
!         uint16_t dport_typecode;
!         uint8_t check_proto;
!         boolean_t tunnel_mode = (acqrec->ipsacq_inneraddrfam != 0);
  
!         ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
  
!         pfkeymp = sadb_keysock_out(0);
!         if (pfkeymp == NULL)
!                 return (NULL);
  
          /*
!          * First, allocate a basic ACQUIRE message
           */
!         allocsize = sizeof (sadb_msg_t) + sizeof (sadb_address_t) +
!             sizeof (sadb_address_t) + sizeof (sadb_prop_t);
  
!         /* Make sure there's enough to cover both AF_INET and AF_INET6. */
!         allocsize += 2 * sizeof (struct sockaddr_in6);
  
!         mutex_enter(&ipss->ipsec_alg_lock);
!         /* NOTE:  The lock is now held through to this function's return. */
!         allocsize += ipss->ipsec_nalgs[IPSEC_ALG_AUTH] *
!             ipss->ipsec_nalgs[IPSEC_ALG_ENCR] * sizeof (sadb_comb_t);
  
!         if (tunnel_mode) {
!                 /* Tunnel mode! */
!                 allocsize += 2 * sizeof (sadb_address_t);
!                 /* Enough to cover both AF_INET and AF_INET6. */
!                 allocsize += 2 * sizeof (struct sockaddr_in6);
!         }
! 
!         msgmp = allocb(allocsize, BPRI_HI);
!         if (msgmp == NULL) {
!                 freeb(pfkeymp);
!                 mutex_exit(&ipss->ipsec_alg_lock);
!                 return (NULL);
!         }
! 
!         pfkeymp->b_cont = msgmp;
!         cur = msgmp->b_rptr;
          end = cur + allocsize;
          samsg = (sadb_msg_t *)cur;
!         cur += sizeof (sadb_msg_t);
  
!         af = acqrec->ipsacq_addrfam;
!         switch (af) {
!         case AF_INET:
!                 check_proto = IPPROTO_ICMP;
!                 break;
!         case AF_INET6:
!                 check_proto = IPPROTO_ICMPV6;
!                 break;
!         default:
!                 /* This should never happen unless we have kernel bugs. */
!                 cmn_err(CE_WARN,
!                     "sadb_setup_acquire:  corrupt ACQUIRE record.\n");
!                 ASSERT(0);
!                 mutex_exit(&ipss->ipsec_alg_lock);
!                 return (NULL);
          }
  
!         samsg->sadb_msg_version = PF_KEY_V2;
!         samsg->sadb_msg_type = SADB_ACQUIRE;
!         samsg->sadb_msg_satype = satype;
!         samsg->sadb_msg_errno = 0;
!         samsg->sadb_msg_pid = 0;
!         samsg->sadb_msg_reserved = 0;
!         samsg->sadb_msg_seq = acqrec->ipsacq_seq;
  
!         ASSERT(MUTEX_HELD(&acqrec->ipsacq_lock));
  
!         if ((acqrec->ipsacq_proto == check_proto) || tunnel_mode) {
!                 sport_typecode = dport_typecode = 0;
          } else {
!                 sport_typecode = acqrec->ipsacq_srcport;
!                 dport_typecode = acqrec->ipsacq_dstport;
          }
  
!         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_SRC, af,
!             acqrec->ipsacq_srcaddr, sport_typecode, acqrec->ipsacq_proto, 0);
  
!         cur = sadb_make_addr_ext(cur, end, SADB_EXT_ADDRESS_DST, af,
!             acqrec->ipsacq_dstaddr, dport_typecode, acqrec->ipsacq_proto, 0);
  
!         if (tunnel_mode) {
!                 sport_typecode = acqrec->ipsacq_srcport;
!                 dport_typecode = acqrec->ipsacq_dstport;
!                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_SRC,
!                     acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innersrc,
!                     sport_typecode, acqrec->ipsacq_inner_proto,
!                     acqrec->ipsacq_innersrcpfx);
!                 cur = sadb_make_addr_ext(cur, end, SADB_X_EXT_ADDRESS_INNER_DST,
!                     acqrec->ipsacq_inneraddrfam, acqrec->ipsacq_innerdst,
!                     dport_typecode, acqrec->ipsacq_inner_proto,
!                     acqrec->ipsacq_innerdstpfx);
          }
  
!         /* XXX Insert identity information here. */
  
!         /* XXXMLS Insert sensitivity information here. */
  
!         if (cur != NULL)
!                 samsg->sadb_msg_len = SADB_8TO64(cur - msgmp->b_rptr);
!         else
!                 mutex_exit(&ipss->ipsec_alg_lock);
  
!         return (pfkeymp);
  }
  
  /*
   * Given an SADB_GETSPI message, find an appropriately ranged SA and
   * allocate an SA.  If there are message improprieties, return (ipsa_t *)-1.
--- 5714,6123 ----
  }
  
  /* End XXX label-library-leakage */
  
  /*
!  * Takes a pointer to sadb_prop_t (what we're initializing), ipsec_action_t
!  * (first action in chain we need to walk of actions for each alg
!  * combination), netstack_ns (contains pointers to alg properties and
!  * per-protocol settings), a combs_limit integer (maximum applicable
!  * combinations derived from per-protcol netstack_t alg array), and need_esp
!  * boolean_t. We distinguish between two error cases: we exceed combs_limit,
!  * which should only be a kernel bug (ipsec_alg_lock is our shepherd), or we
!  * have an alg ID with a NULL netstack member or member with the valid bit
!  * flipped, both of which indicate the needs to reset state, which we flag by
!  * returning no combs. We return NULL if we exceed combs_limit and zero-length
!  * prop if we run into an alg that can't be transferred into the prop.
   */
! static void
! sadb_insert_prop(sadb_prop_t *prop, const ipsec_action_t *ap, netstack_t *ns,
!     uint_t combs_limit, boolean_t need_esp)
  {
!         sadb_comb_t     *comb = (sadb_comb_t *)(prop + 1);
!         ipsec_action_t  *act = (ipsec_action_t *)ap;
!         ipsec_prot_t    *prot;
!         ipsecah_stack_t *ahstack = ns->netstack_ipsecah;
!         ipsecesp_stack_t *espstack = ns->netstack_ipsecesp;
!         ipsec_stack_t   *ipss = ns->netstack_ipsec;
!         boolean_t       need_ah = !need_esp;
  
!         ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
!         ASSERT((need_esp && ap->ipa_want_esp) || (need_ah && ap->ipa_want_ah));
  
!         prop->sadb_prop_exttype = SADB_EXT_PROPOSAL;
!         prop->sadb_prop_len = SADB_8TO64(sizeof (sadb_prop_t));
!         *(uint32_t *)(&prop->sadb_prop_replay) = 0; /* Quick zero-out! */
!         prop->sadb_prop_replay = need_esp ?
!             espstack->ipsecesp_replay_size : ahstack->ipsecah_replay_size;
  
!         /* Prioritize a proposal, preserving policy order. */
!         for (; act != NULL; act = act->ipa_next) {
!                 ipsec_alginfo_t *aalg = NULL;
!                 ipsec_alginfo_t *ealg = NULL;
  
!                 if ((act->ipa_act.ipa_type != IPSEC_POLICY_APPLY) ||
!                     (need_esp && !act->ipa_act.ipa_apply.ipp_use_esp) ||
!                     (need_ah && !act->ipa_act.ipa_apply.ipp_use_ah))
!                         continue;
  
!                 if (--combs_limit == 0) {
!                         prop = NULL;
!                         return;
!                 }
  
!                 prot = &act->ipa_act.ipa_apply;
  
                  /*
!                  * Alg ID 0 is none/any, which is valid only for ESP without
!                  * message integrity (ipp_esp_auth_alg). NULL encryption ESP
!                  * uses a distinct alg, non-zero ID.
                   */
!                 if ((need_esp && prot->ipp_esp_auth_alg != 0) || need_ah) {
!                         ASSERT(need_esp || (prot->ipp_auth_alg > 0));
!                         aalg = ipss->ipsec_alglists[IPSEC_ALG_AUTH][need_esp ?
!                             prot->ipp_esp_auth_alg : prot->ipp_auth_alg];
!                         if (aalg == NULL || !ALG_VALID(aalg))
!                                 goto failure;
                  }
  
!                 if (need_esp) {
!                         ASSERT(prot->ipp_encr_alg > 0);
!                         ealg = ipss->ipsec_alglists[IPSEC_ALG_ENCR]
!                             [prot->ipp_encr_alg];
!                         if (ealg == NULL || !ALG_VALID(ealg))
!                                 goto failure;
                  }
  
!                 comb->sadb_comb_flags = 0;
!                 comb->sadb_comb_reserved = 0;
  
!                 if (ealg != NULL) {
!                         comb->sadb_comb_encrypt = ealg->alg_id;
!                         comb->sadb_comb_encrypt_minbits =
!                             MAX(prot->ipp_espe_minbits, ealg->alg_ef_minbits);
!                         comb->sadb_comb_encrypt_maxbits =
!                             MIN(prot->ipp_espe_maxbits, ealg->alg_ef_maxbits);
                  } else {
!                         comb->sadb_comb_encrypt = 0;
!                         comb->sadb_comb_encrypt_minbits = 0;
!                         comb->sadb_comb_encrypt_maxbits = 0;
                  }
  
!                 if (aalg != NULL) {
!                         uint16_t minbits, maxbits;
!                         minbits = need_esp ?
!                             prot->ipp_espa_minbits : prot->ipp_ah_minbits;
!                         maxbits = need_esp ?
!                             prot->ipp_espa_maxbits : prot->ipp_ah_maxbits;
!                         comb->sadb_comb_auth = aalg->alg_id;
!                         comb->sadb_comb_auth_minbits =
!                             MAX(minbits, aalg->alg_ef_minbits);
!                         comb->sadb_comb_auth_maxbits =
!                             MIN(maxbits, aalg->alg_ef_maxbits);
                  } else {
!                         comb->sadb_comb_auth = 0;
!                         comb->sadb_comb_auth_minbits = 0;
!                         comb->sadb_comb_auth_maxbits = 0;
                  }
  
                  /*
!                  * The following may be based on algorithm properties, but in
!                  * the meantime, we just pick some good, sensible numbers.
!                  * Key mgmt. can (and perhaps should) be the place to finalize
!                  * such decisions.
                   */
  
!                 /* 0 == unlimited == unsupported */
!                 comb->sadb_comb_soft_allocations = 0;
!                 comb->sadb_comb_hard_allocations = 0;
! 
!                 /* These may want to come from policy rule. */
!                 if (need_esp) {
!                         comb->sadb_comb_soft_bytes =
!                             espstack->ipsecesp_default_soft_bytes;
!                         comb->sadb_comb_hard_bytes =
!                             espstack->ipsecesp_default_hard_bytes;
!                         comb->sadb_comb_soft_addtime =
!                             espstack->ipsecesp_default_soft_addtime;
!                         comb->sadb_comb_hard_addtime =
!                             espstack->ipsecesp_default_hard_addtime;
!                         comb->sadb_comb_soft_usetime =
!                             espstack->ipsecesp_default_soft_usetime;
!                         comb->sadb_comb_hard_usetime =
!                             espstack->ipsecesp_default_hard_usetime;
!                 } else {
!                         comb->sadb_comb_soft_bytes =
!                             ahstack->ipsecah_default_soft_bytes;
!                         comb->sadb_comb_hard_bytes =
!                             ahstack->ipsecah_default_hard_bytes;
!                         comb->sadb_comb_soft_addtime =
!                             ahstack->ipsecah_default_soft_addtime;
!                         comb->sadb_comb_hard_addtime =
!                             ahstack->ipsecah_default_hard_addtime;
!                         comb->sadb_comb_soft_usetime =
!                             ahstack->ipsecah_default_soft_usetime;
!                         comb->sadb_comb_hard_usetime =
!                             ahstack->ipsecah_default_hard_usetime;
                  }
  
!                 prop->sadb_prop_len += SADB_8TO64(sizeof (*comb));
!                 comb++;
          }
  
!         return;
  
! failure:
!         prop->sadb_prop_len = 0;
! }
  
! /*
!  * Construct extended properties using ipsec_action_t, ipsec_policy_t, and
!  * netstack_t pointers. Byte-aligned pointers cur and end are used for bounds
!  * checking here and in called code. We don't set length if numecombs is 0, so
!  * callers must check this for error handling.
   */
! static uint8_t *
! sadb_construct_eprop(const ipsec_action_t *act, const ipsec_policy_t *pp,
!     netstack_t *ns, const uint8_t *start, const uint8_t *end)
! {
!         uint8_t         *cur = (uint8_t *)start;
!         sadb_prop_t     *eprop = (sadb_prop_t *)cur;
!         ipsec_action_t  *an, *ap = (ipsec_action_t *)act;
!         ipsec_stack_t   *ipss = ns->netstack_ipsec;
! 
!         ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
! 
          cur += sizeof (*eprop);
!         if (cur > end)
                  return (NULL);
  
          eprop->sadb_prop_exttype = SADB_X_EXT_EPROP;
          eprop->sadb_x_prop_ereserved = 0;
          eprop->sadb_x_prop_numecombs = 0;
          eprop->sadb_prop_replay = 32;   /* default */
  
          for (; ap != NULL; ap = an) {
                  /*
!                  * XXX Don't walk past first ap if there's no pp. Not clear on
!                  * the rationale for this, but it's what extended path did.
                   */
+                 an = (pp != NULL) ? ap->ipa_next : NULL;
+ 
                  if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
                          continue;
  
                  if (ap->ipa_act.ipa_apply.ipp_replay_depth) {
                          eprop->sadb_prop_replay =
                              ap->ipa_act.ipa_apply.ipp_replay_depth;
                  }
  
                  cur = sadb_action_to_ecomb(cur, end, ap, ns);
!                 if (cur == NULL)
                          return (NULL);
                  eprop->sadb_x_prop_numecombs++;
          }
  
          /*
!          * This is an error. We return what we've got of eprops, caller needs
!          * to check for condition and pass it further up (e.g. by error samsg).
           */
!         if (eprop->sadb_x_prop_numecombs == 0)
!                 return (cur);
  
!         eprop->sadb_prop_len = SADB_8TO64(cur - (uint8_t *)start);
! 
!         return (cur);
! bail:
          return (NULL);
+ }
+ 
+ /*
+  * Convert ipsec_policy_t and ipsec_action_t pointers to kmc extension. Byte-
+  * aligned cur and end pointers used for bounds checking. sadb_x_kmcext_t
+  * handling encapsulated in sadb_make_kmc_ext. Returns new value for cur,
+  * NULL on failure.
+  * We encapsulate for recursion since we have to walk ipsec_action_t.
+  */
+ static uint8_t *
+ sadb_policy_to_kmcext(const ipsec_policy_t *pp, const ipsec_action_t *act,
+     const uint8_t *start, const uint8_t *end)
+ {
+         uint8_t         *cur = (uint8_t *)start;
+         ipsec_action_t  *an, *ap = (ipsec_action_t *)act;
+         uint32_t        kmp = 0, kmc = 0;
+ 
+         for (; ap != NULL; ap = an) {
+                 an = (pp != NULL) ? ap->ipa_next : NULL;
+ 
+                 /*
+                  * Skip non-IPsec policies
+                  */
+                 if (ap->ipa_act.ipa_type != IPSEC_ACT_APPLY)
+                         continue;
+ 
+                 if (ap->ipa_act.ipa_apply.ipp_km_proto)
+                         kmp = ap->ipa_act.ipa_apply.ipp_km_proto;
+                 if (ap->ipa_act.ipa_apply.ipp_km_cookie)
+                         kmc = ap->ipa_act.ipa_apply.ipp_km_cookie;
          }
  
!         if ((kmp != 0) || (kmc != 0))
!                 cur = sadb_make_kmc_ext(cur, end, kmp, kmc);
  
!         return (cur);
  }
  
  /*
!  * Prepare the SADB_ACQUIRE message proper, which should be a b_cont to a
!  * keysock registered M_CTL message. Takes a pointer to ipsacq_t (optional
!  * acquire record for which we're sending message), ipsec_selector_t,
!  * ipsec_action_t, ipsec_policy_t, netstack_t, and sense (required for called
!  * to generate the message), and booleans for need_esp, tunnel_mode,
!  * extended, and with_prop (all of these should be self-explanatory). Because
!  * extended messages set satype to SADB_SATYPE_UNSPEC, extended-only callers
!  * can fudge need_esp.
   */
! static mblk_t *
! sadb_construct_acqmsg(ipsacq_t *acqrec, ipsec_selector_t *sel,
!     ipsec_action_t *ap, ipsec_policy_t *pp, netstack_t *ns, sadb_sens_t *sens,
!     boolean_t need_esp, boolean_t tunnel_mode, boolean_t extended,
!     boolean_t with_prop)
  {
!         uint_t          combs_limit, allocsize;
          uint8_t         *cur, *end;
          sadb_msg_t      *samsg;
!         sadb_prop_t     *prop, *eprop;
!         mblk_t          *mp;
!         int             satype = extended ? SADB_SATYPE_UNSPEC
!             : (need_esp ? SADB_SATYPE_ESP : SADB_SATYPE_AH);
!         ipsec_stack_t   *ipss = ns->netstack_ipsec;
  
!         ASSERT((acqrec == NULL) || (MUTEX_HELD(&acqrec->ipsacq_lock)));
!         ASSERT(ap != NULL);
!         ASSERT((pp == NULL) || (pp->ipsp_refs != 0));
!         ASSERT((ap == NULL) || (ap->ipa_refs != 0));
  
!         /*
!          * Set the limit used to size [e]prop [e]combs array to as many
!          * algorithms as defined on the netstack (must hold ipsec_alg_lock
!          * from here to when done reading off netstack for [e]prop
!          * formation). need_esp may be fudged, so be generous to extended.
!          */
!         if (with_prop) {
!                 if (extended)
!                         need_esp = B_TRUE;
!                 rw_enter(&ipss->ipsec_alg_lock, RW_READER);
!                 CALC_COMBS(combs_limit, ipss, need_esp);
!         }
  
          /*
!          * If this code is right, we may not need cur & end for bounds
!          * checking, but we'll keep normal runtime checks until that statement
!          * looks credible rather than merely plausible, at which point checks
!          * can be moved to ASSERTs. sens is variably sized but already
!          * set. kmc is fixed size. Pointers into message are byte-aligned, so
!          * we're generally depending on all structures used in this
!          * calculation to be so, too (in fact, all sadb_*_t types used here
!          * are 64-bit aligned per PF_KEY requirements).
           */
!         allocsize = sizeof (sadb_msg_t) + sizeof (sadb_prop_t);
!         allocsize += ((tunnel_mode) ? 4 : 2) * (sizeof (sadb_address_t)
!             + SADB_SOCKADDR_SIZE);
!         if (sens != NULL)
!                 allocsize += SADB_64TO8(sens->sadb_sens_len);
!         allocsize += sizeof (sadb_x_kmc_t);
!         /* If we need props, size combs/combs array using combs_limit */
!         if (with_prop)
!                 allocsize += combs_limit * (extended ?
!                     sizeof (sadb_x_ecomb_t) : sizeof (sadb_comb_t));
  
!         ASSERT((allocsize & 0x7) == 0);
  
!         mp = allocb(allocsize, BPRI_HI);
!         if (mp == NULL)
!                 goto unlock_and_fail;
  
!         cur = mp->b_rptr;
          end = cur + allocsize;
+ 
          samsg = (sadb_msg_t *)cur;
!         INITIALIZE_SAMSG(samsg, SADB_ACQUIRE);
!         samsg->sadb_msg_satype = satype;
!         samsg->sadb_msg_pid = 0;
!         samsg->sadb_msg_seq = (acqrec != NULL) ? acqrec->ipsacq_seq : 0;
  
!         /* CALC_COMBS asserts on zero limit; broken config still possible */
!         if (with_prop && (combs_limit == 0)) {
!                 ERRNO_SAMSG(samsg, ENOENT);
!                 goto unlock_and_bail;
          }
  
!         cur += sizeof (sadb_msg_t);
  
!         cur = sadb_sel_to_addrexts(sel, pp, ap, cur, end, tunnel_mode);
!         if (cur == NULL)
!                 goto unlock_and_fail;
  
!         if (with_prop) {
!                 if (extended) {
!                         cur = sadb_construct_eprop(ap, pp, ns, cur, end);
!                         if (cur == NULL)
!                                 goto unlock_and_fail;
! 
!                         eprop = (sadb_prop_t *)cur;
!                         if (eprop->sadb_x_prop_numecombs == 0) {
!                                 ERRNO_SAMSG(samsg, ENOENT);
!                                 goto unlock_and_bail;
!                         }
                  } else {
!                         prop = (sadb_prop_t *)cur;
! 
!                         sadb_insert_prop(prop, ap, ns, combs_limit, need_esp);
!                         if (prop == NULL) {
!                                 goto unlock_and_fail;
!                         } else if (prop->sadb_prop_len == 0) {
!                                 ERRNO_SAMSG(samsg, ENOENT);
!                                 goto unlock_and_bail;
                          }
  
!                         cur += SADB_64TO8(prop->sadb_prop_len);
!                 }
  
!                 rw_exit(&ipss->ipsec_alg_lock);
!         }
  
!         if (sens != NULL) {
!                 uint8_t *sensext = cur;
!                 int senslen = SADB_64TO8(sens->sadb_sens_len);
! 
!                 cur += senslen;
!                 if (cur > end)
!                         goto freeb_bail;
!                 bcopy(sens, sensext, senslen);
          }
  
!         cur = sadb_policy_to_kmcext(pp, ap, cur, end);
!         if (cur == NULL)
!                 goto freeb_bail;
  
!         samsg->sadb_msg_len = SADB_8TO64(cur - mp->b_rptr);
!         mp->b_wptr = cur;
  
!         return (mp);
  
! freeb_bail:
!         /* This message isn't chained, so we can freeb. */
!         freeb(mp);
!         return (NULL);
! unlock_and_bail:
!         if (with_prop)
!                 rw_exit(&ipss->ipsec_alg_lock);
!         return (mp);
! unlock_and_fail:
!         if (with_prop)
!                 rw_exit(&ipss->ipsec_alg_lock);
!         return (NULL);
  }
  
  /*
   * Given an SADB_GETSPI message, find an appropriately ranged SA and
   * allocate an SA.  If there are message improprieties, return (ipsa_t *)-1.
*** 6244,6282 ****
              inet_ntop(af, addr, buf, sizeof (buf)));
  }
  
  /*
   * Fills in a reference to the policy, if any, from the conn, in *ppp
   */
  static void
  ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
  {
          ipsec_policy_t  *pp;
          ipsec_latch_t   *ipl = connp->conn_latch;
  
          if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
                  pp = connp->conn_ixa->ixa_ipsec_policy;
                  IPPOL_REFHOLD(pp);
!         } else {
                  pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
                      connp->conn_netstack);
          }
          *ppp = pp;
  }
  
  /*
!  * The following functions scan through active conn_t structures
!  * and return a reference to the best-matching policy it can find.
!  * Caller must release the reference.
   */
  static void
  ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
  {
          connf_t *connfp;
          conn_t *connp = NULL;
          ipsec_selector_t portonly;
  
          bzero((void *)&portonly, sizeof (portonly));
  
          if (sel->ips_local_port == 0)
                  return;
  
--- 6498,6541 ----
              inet_ntop(af, addr, buf, sizeof (buf)));
  }
  
  /*
   * Fills in a reference to the policy, if any, from the conn, in *ppp
+  * If found, we hold a reference to the policy, caller must release.
   */
  static void
  ipsec_conn_pol(ipsec_selector_t *sel, conn_t *connp, ipsec_policy_t **ppp)
  {
          ipsec_policy_t  *pp;
          ipsec_latch_t   *ipl = connp->conn_latch;
  
+         /* Use policy pointer already on conn_t if it's there. */
          if ((ipl != NULL) && (connp->conn_ixa->ixa_ipsec_policy != NULL)) {
                  pp = connp->conn_ixa->ixa_ipsec_policy;
                  IPPOL_REFHOLD(pp);
!         } else { /* otherwise query SPD */
!                 /* This holds a reference for us if successful) */
                  pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, connp, sel,
                      connp->conn_netstack);
          }
          *ppp = pp;
  }
  
  /*
!  * Takes ipsec_selector_t (for attributes to query), ipsec_policy_t (what we're
!  * trying to find), and ip_stack_t (contains udp fanout we need to query). If we
!  * find a matching connection, we return its policy settings.
   */
  static void
  ipsec_udp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
  {
          connf_t *connfp;
          conn_t *connp = NULL;
          ipsec_selector_t portonly;
  
+         ASSERT(*ppp == NULL);
+ 
          bzero((void *)&portonly, sizeof (portonly));
  
          if (sel->ips_local_port == 0)
                  return;
  
*** 6322,6350 ****
  
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  static conn_t *
! ipsec_find_listen_conn(uint16_t *pptr, ipsec_selector_t *sel, ip_stack_t *ipst)
  {
          connf_t *connfp;
          conn_t *connp = NULL;
          const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
  
!         if (sel->ips_local_port == 0)
!                 return (NULL);
  
          connfp = &ipst->ips_ipcl_bind_fanout[
              IPCL_BIND_HASH(sel->ips_local_port, ipst)];
          mutex_enter(&connfp->connf_lock);
  
          if (sel->ips_isv4) {
                  connp = connfp->connf_head;
                  while (connp != NULL) {
                          if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
!                             sel->ips_local_addr_v4, pptr[1]))
                                  break;
                          connp = connp->conn_next;
                  }
  
                  if (connp == NULL) {
--- 6581,6615 ----
  
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
+ /*
+  * Takes ipsec_selector_t (connection attributes to form query) and ip_stack_t
+  * (contains bind fanout we need to query) pointers to look up existing TCP
+  * listener, returned via conn_t pointer. We return NULL on failure.
+  * We increment reference count on match, caller must decrement.
+  */
  static conn_t *
! ipsec_find_listen_conn(ipsec_selector_t *sel, ip_stack_t *ipst)
  {
          connf_t *connfp;
          conn_t *connp = NULL;
          const in6_addr_t *v6addrmatch = &sel->ips_local_addr_v6;
  
!         /* XXX Sure about the second part? */
!         ASSERT(sel->ips_local_port != 0 && ipst != NULL);
  
          connfp = &ipst->ips_ipcl_bind_fanout[
              IPCL_BIND_HASH(sel->ips_local_port, ipst)];
          mutex_enter(&connfp->connf_lock);
  
          if (sel->ips_isv4) {
                  connp = connfp->connf_head;
                  while (connp != NULL) {
                          if (IPCL_BIND_MATCH(connp, IPPROTO_TCP,
!                             sel->ips_local_addr_v4, sel->ips_local_port))
                                  break;
                          connp = connp->conn_next;
                  }
  
                  if (connp == NULL) {
*** 6355,6365 ****
  
          if (connp == NULL) {
                  connp = connfp->connf_head;
                  while (connp != NULL) {
                          if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
!                             *v6addrmatch, pptr[1]))
                                  break;
                          connp = connp->conn_next;
                  }
  
                  if (connp == NULL) {
--- 6620,6630 ----
  
          if (connp == NULL) {
                  connp = connfp->connf_head;
                  while (connp != NULL) {
                          if (IPCL_BIND_MATCH_V6(connp, IPPROTO_TCP,
!                             *v6addrmatch, sel->ips_local_port))
                                  break;
                          connp = connp->conn_next;
                  }
  
                  if (connp == NULL) {
*** 6371,6403 ****
          CONN_INC_REF(connp);
          mutex_exit(&connfp->connf_lock);
          return (connp);
  }
  
  static void
  ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
  {
          connf_t         *connfp;
          conn_t          *connp;
          uint32_t        ports;
          uint16_t        *pptr = (uint16_t *)&ports;
  
          /*
           * Find TCP state in the following order:
!          * 1.) Connected conns.
           * 2.) Listeners.
           *
           * Even though #2 will be the common case for inbound traffic, only
           * following this order insures correctness.
           */
  
-         if (sel->ips_local_port == 0)
-                 return;
  
          /*
!          * 0 should be fport, 1 should be lport.  SRC is the local one here.
!          * See ipsec_construct_inverse_acquire() for details.
           */
          pptr[0] = sel->ips_remote_port;
          pptr[1] = sel->ips_local_port;
  
          connfp = &ipst->ips_ipcl_conn_fanout[
--- 6636,6674 ----
          CONN_INC_REF(connp);
          mutex_exit(&connfp->connf_lock);
          return (connp);
  }
  
+ /*
+  * Given ipsec_selector_t (contains attributes to query, ipsec_policy_t (what we
+  * need to find), and ip_stack_t pointer (contains connection state to query),
+  * find a matching TCP connection or listener and return its policy pointer.
+  */
  static void
  ipsec_tcp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
  {
          connf_t         *connfp;
          conn_t          *connp;
          uint32_t        ports;
          uint16_t        *pptr = (uint16_t *)&ports;
  
+         ASSERT(sel->ips_local_port != 0 && *ppp == NULL);
+ 
          /*
           * Find TCP state in the following order:
!          * 1.) Connected conns. (walk ipst connection fanout)
           * 2.) Listeners.
           *
           * Even though #2 will be the common case for inbound traffic, only
           * following this order insures correctness.
           */
  
  
          /*
!          * pptr makes an array of port values, 0 for fport, 1 for lport.  SRC is
!          * the local one here. Connection lookup macros want this instead of
!          * selector port members.
           */
          pptr[0] = sel->ips_remote_port;
          pptr[1] = sel->ips_local_port;
  
          connfp = &ipst->ips_ipcl_conn_fanout[
*** 6427,6468 ****
                  CONN_INC_REF(connp);
                  mutex_exit(&connfp->connf_lock);
          } else {
                  mutex_exit(&connfp->connf_lock);
  
!                 /* Try the listen hash. */
!                 if ((connp = ipsec_find_listen_conn(pptr, sel, ipst)) == NULL)
                          return;
          }
  
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  static void
! ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
!     ip_stack_t *ipst)
  {
          conn_t          *connp;
          uint32_t        ports;
          uint16_t        *pptr = (uint16_t *)&ports;
  
          /*
           * Find SCP state in the following order:
           * 1.) Connected conns.
           * 2.) Listeners.
           *
           * Even though #2 will be the common case for inbound traffic, only
           * following this order insures correctness.
           */
  
-         if (sel->ips_local_port == 0)
-                 return;
- 
          /*
!          * 0 should be fport, 1 should be lport.  SRC is the local one here.
!          * See ipsec_construct_inverse_acquire() for details.
           */
          pptr[0] = sel->ips_remote_port;
          pptr[1] = sel->ips_local_port;
  
          /*
--- 6698,6743 ----
                  CONN_INC_REF(connp);
                  mutex_exit(&connfp->connf_lock);
          } else {
                  mutex_exit(&connfp->connf_lock);
  
!                 /* Try the listen hash. If found, comes with incremented ref. */
!                 if ((connp = ipsec_find_listen_conn(sel, ipst)) == NULL)
                          return;
          }
  
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
+ /*
+  * Given ipsec_selector_t (connection attributes to form query), ipsec_policy_t
+  * (populate with match), and ip_stack_t (connection state to query) pointers,
+  * call into sctp to find an existing connection and return its policy.
+  */
  static void
! ipsec_sctp_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp, ip_stack_t *ipst)
  {
          conn_t          *connp;
          uint32_t        ports;
          uint16_t        *pptr = (uint16_t *)&ports;
  
+         ASSERT(sel->ips_local_port != 0 && *ppp == NULL);
+ 
          /*
           * Find SCP state in the following order:
           * 1.) Connected conns.
           * 2.) Listeners.
           *
           * Even though #2 will be the common case for inbound traffic, only
           * following this order insures correctness.
           */
  
          /*
!          * pptr makes an array of port values, 0 for fport, 1 for lport.  SRC is
!          * the local one here. Connection lookup macros want this instead of
!          * selector port members.
           */
          pptr[0] = sel->ips_remote_port;
          pptr[1] = sel->ips_local_port;
  
          /*
*** 6487,6500 ****
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  /*
!  * Fill in a query for the SPD (in "sel") using two PF_KEY address extensions.
!  * Returns 0 or errno, and always sets *diagnostic to something appropriate
!  * to PF_KEY.
!  *
   * NOTE:  For right now, this function (and ipsec_selector_t for that matter),
   * ignore prefix lengths in the address extension.  Since we match on first-
   * entered policies, this shouldn't matter.  Also, since we normalize prefix-
   * set addresses to mask out the lower bits, we should get a suitable search
   * key for the SPD anyway.  This is the function to change if the assumption
--- 6762,6774 ----
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  /*
!  * Takes ipsec_selector_t (what we're forming), two sadb_address_t (address
!  * extentions needed to create selector), and diagnostic (what, if anything,
!  * went wrong in PF_KEY terms) pointers, returns int (0 or errno).
   * NOTE:  For right now, this function (and ipsec_selector_t for that matter),
   * ignore prefix lengths in the address extension.  Since we match on first-
   * entered policies, this shouldn't matter.  Also, since we normalize prefix-
   * set addresses to mask out the lower bits, we should get a suitable search
   * key for the SPD anyway.  This is the function to change if the assumption
*** 6546,6611 ****
          }
          return (0);
  }
  
  /*
!  * We have encapsulation.
!  * - Lookup tun_t by address and look for an associated
!  *   tunnel policy
!  * - If there are inner selectors
!  *   - check ITPF_P_TUNNEL and ITPF_P_ACTIVE
!  *   - Look up tunnel policy based on selectors
!  * - Else
!  *   - Sanity check the negotation
!  *   - If appropriate, fall through to global policy
   */
  static int
  ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
      sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
      int *diagnostic)
  {
          int err;
          ipsec_policy_head_t *polhead;
  
          *diagnostic = 0;
  
          /* Check for inner selectors and act appropriately */
- 
          if (innsrcext != NULL) {
!                 /* Inner selectors present */
!                 ASSERT(inndstext != NULL);
                  if ((itp == NULL) ||
                      (itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
                      (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
-                         /*
-                          * If inner packet selectors, we must have negotiate
-                          * tunnel and active policy.  If the tunnel has
-                          * transport-mode policy set on it, or has no policy,
-                          * fail.
-                          */
                          return (ENOENT);
                  } else {
                          /*
!                          * Reset "sel" to indicate inner selectors.  Pass
!                          * inner PF_KEY address extensions for this to happen.
                           */
                          if ((err = ipsec_get_inverse_acquire_sel(sel,
                              innsrcext, inndstext, diagnostic)) != 0)
                                  return (err);
-                         /*
-                          * Now look for a tunnel policy based on those inner
-                          * selectors.  (Common code is below.)
-                          */
                  }
!         } else {
!                 /* No inner selectors present */
!                 if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
                          /*
!                          * Transport mode negotiation with no tunnel policy
!                          * configured - return to indicate a global policy
!                          * check is needed.
                           */
                          return (0);
                  } else if (itp->itp_flags & ITPF_P_TUNNEL) {
                          /* Tunnel mode set with no inner selectors. */
                          return (ENOENT);
                  }
--- 6820,6877 ----
          }
          return (0);
  }
  
  /*
!  * We're passed pointers to ipsec_selector (inner info needed to form query),
!  * ipsec_policy_t (what we're trying to populate), a pair of sadb_address_t
!  * (extentions needed to reset selector), ipsec_tun_pol_t (tunnel policy that
!  * may already be populated from previous SPD query), and integer (error detail
!  * in PF_KEY2 terms, always 0). Return 0 or errno.
!  * Caller may have fudged inner selector, so we need to reset it via if we have
!  * to reuse it.
   */
  static int
  ipsec_tun_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
      sadb_address_t *innsrcext, sadb_address_t *inndstext, ipsec_tun_pol_t *itp,
      int *diagnostic)
  {
          int err;
          ipsec_policy_head_t *polhead;
  
+         ASSERT(*ppp == NULL);
+ 
          *diagnostic = 0;
  
          /* Check for inner selectors and act appropriately */
          if (innsrcext != NULL) {
!                 ASSERT(inndstext != NULL); /* Need a pair */
!                 /*
!                  * If inner packet selectors, we must have negotiated tunnel and
!                  * active policy already. If the tunnel has transport-mode
!                  * policy set on it or no policy at all, fail.
!                  */
                  if ((itp == NULL) ||
                      (itp->itp_flags & (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) !=
                      (ITPF_P_ACTIVE | ITPF_P_TUNNEL)) {
                          return (ENOENT);
                  } else {
                          /*
!                          * If we got a sane policy back from the SPD, reset the
!                          * possibly fudged selector for subsequent operations.
                           */
                          if ((err = ipsec_get_inverse_acquire_sel(sel,
                                      innsrcext, inndstext, diagnostic)) != 0)
                                  return (err);
                  }
!         } else { /* No inner selectors present */
! 
                  /*
!                  * Transport mode negotiation with no tunnel policy configured
!                  * - return to indicate a global policy check is needed.
                   */
+                 if ((itp == NULL) || !(itp->itp_flags & ITPF_P_ACTIVE)) {
                          return (0);
                  } else if (itp->itp_flags & ITPF_P_TUNNEL) {
                          /* Tunnel mode set with no inner selectors. */
                          return (ENOENT);
                  }
*** 6633,6653 ****
  
          return (0);
  }
  
  /*
!  * For sctp conn_faddr is the primary address, hence this is of limited
!  * use for sctp.
   */
  static void
  ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
      ip_stack_t *ipst)
  {
          boolean_t       isv4 = sel->ips_isv4;
          connf_t         *connfp;
          conn_t          *connp;
  
          if (isv4) {
                  connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
          } else {
                  connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
          }
--- 6899,6925 ----
  
          return (0);
  }
  
  /*
!  * Takes ipsec_selector_t (data to form query), ipsec_policy_t (what we need
!  * to populate), and ip_stack_t (contains state data to query) pointers. This is
!  * a generic protocol look-up function to find a relevant connection that can be
!  * converted in a policy.
!  * XXX For sctp conn_faddr is the primary address, hence this is of limited
!  * use for sctp. Do we care, given sctp has its own lookup?
   */
  static void
  ipsec_oth_pol(ipsec_selector_t *sel, ipsec_policy_t **ppp,
      ip_stack_t *ipst)
  {
          boolean_t       isv4 = sel->ips_isv4;
          connf_t         *connfp;
          conn_t          *connp;
  
+         ASSERT(*ppp == NULL);
+ 
          if (isv4) {
                  connfp = &ipst->ips_ipcl_proto_fanout_v4[sel->ips_protocol];
          } else {
                  connfp = &ipst->ips_ipcl_proto_fanout_v6[sel->ips_protocol];
          }
*** 6682,6704 ****
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  /*
!  * Construct an inverse ACQUIRE reply based on:
   *
-  * 1.) Current global policy.
-  * 2.) An conn_t match depending on what all was passed in the extv[].
-  * 3.) A tunnel's policy head.
-  * ...
-  * N.) Other stuff TBD (e.g. identities)
-  *
-  * If there is an error, set sadb_msg_errno and sadb_x_msg_diagnostic
-  * in this function so the caller can extract them where appropriately.
-  *
-  * The SRC address is the local one - just like an outbound ACQUIRE message.
-  *
   * XXX MLS: key management supplies a label which we just reflect back up
   * again.  clearly we need to involve the label in the rest of the checks.
   */
  mblk_t *
  ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
--- 6954,6973 ----
          ipsec_conn_pol(sel, connp, ppp);
          CONN_DEC_REF(connp);
  }
  
  /*
!  * This code is called from keysock to handle inverse acquire messages.  We
!  * are passed a pointer to sadb_msg_t, a fixed-size array of sadb_ext_t, and a
!  * netstack_t pointer and return a mblk_t pointer, in which we attempt to
!  * construct a return acquire message. In case of errors, we return a NULL
!  * pointer and populate samsg->sadb_msg_errno and samsg->sadb_msg_diagnostic,
!  * which is handled as an error at the keysock layer. Otherwise keysock does a
!  * passup with our message.
!  * Caller performs basic sanity checks such as NULL external addresses and
!  * only one of two inner addrs being NULL. Remaining checks happen here.
   *
   * XXX MLS: key management supplies a label which we just reflect back up
   * again.  clearly we need to involve the label in the rest of the checks.
   */
  mblk_t *
  ipsec_construct_inverse_acquire(sadb_msg_t *samsg, sadb_ext_t *extv[],
*** 6714,6727 ****
          struct sockaddr_in6 *src, *dst;
          struct sockaddr_in6 *isrc, *idst;
          ipsec_tun_pol_t *itp = NULL;
          ipsec_policy_t *pp = NULL;
          ipsec_selector_t sel, isel;
!         mblk_t *retmp = NULL;
          ip_stack_t      *ipst = ns->netstack_ip;
  
- 
          /* Normalize addresses */
          if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
              == KS_IN_ADDR_UNKNOWN) {
                  err = EINVAL;
                  diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
--- 6983,6998 ----
          struct sockaddr_in6 *src, *dst;
          struct sockaddr_in6 *isrc, *idst;
          ipsec_tun_pol_t *itp = NULL;
          ipsec_policy_t *pp = NULL;
          ipsec_selector_t sel, isel;
!         mblk_t *retmp;
          ip_stack_t      *ipst = ns->netstack_ip;
+         sadb_msg_t *retmsg;
+         ipsec_action_t *ap;
+         boolean_t tunnel_mode = B_FALSE;
  
          /* Normalize addresses */
          if (sadb_addrcheck(NULL, (mblk_t *)samsg, (sadb_ext_t *)srcext, 0, ns)
              == KS_IN_ADDR_UNKNOWN) {
                  err = EINVAL;
                  diagnostic = SADB_X_DIAGNOSTIC_BAD_SRC;
*** 6740,6755 ****
                  diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
                  goto bail;
          }
  
          /* Check for tunnel mode and act appropriately */
          if (innsrcext != NULL) {
!                 if (inndstext == NULL) {
!                         err = EINVAL;
!                         diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_DST;
!                         goto bail;
!                 }
                  if (sadb_addrcheck(NULL, (mblk_t *)samsg,
                      (sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
                          err = EINVAL;
                          diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
                          goto bail;
--- 7011,7026 ----
                  diagnostic = SADB_X_DIAGNOSTIC_AF_MISMATCH;
                  goto bail;
          }
  
          /* Check for tunnel mode and act appropriately */
+         /*
+          * Note: keysock_inverse_acquire catches unbalanced extensions and
+          * makes them into keysock_error calls, so ASSERTs here to confirm.
+          */
          if (innsrcext != NULL) {
!                 ASSERT(inndstext != NULL);
                  if (sadb_addrcheck(NULL, (mblk_t *)samsg,
                      (sadb_ext_t *)innsrcext, 0, ns) == KS_IN_ADDR_UNKNOWN) {
                          err = EINVAL;
                          diagnostic = SADB_X_DIAGNOSTIC_MALFORMED_INNER_SRC;
                          goto bail;
*** 6771,6793 ****
                      isrc->sin6_family != AF_INET6) {
                          err = EINVAL;
                          diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
                          goto bail;
                  }
!         } else if (inndstext != NULL) {
!                 err = EINVAL;
!                 diagnostic = SADB_X_DIAGNOSTIC_MISSING_INNER_SRC;
!                 goto bail;
!         }
  
!         /* Get selectors first, based on outer addresses */
          err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
          if (err != 0)
                  goto bail;
  
!         /* Check for tunnel mode mismatches. */
!         if (innsrcext != NULL &&
              ((isrc->sin6_family == AF_INET &&
              sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
              (isrc->sin6_family == AF_INET6 &&
              sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
                  err = EPROTOTYPE;
--- 7042,7062 ----
                      isrc->sin6_family != AF_INET6) {
                          err = EINVAL;
                          diagnostic = SADB_X_DIAGNOSTIC_BAD_INNER_SRC_AF;
                          goto bail;
                  }
!                 tunnel_mode = B_TRUE;
!         } else
!                 ASSERT(inndstext == NULL);
  
!         /* Convert address extensions into outer selector */
          err = ipsec_get_inverse_acquire_sel(&sel, srcext, dstext, &diagnostic);
          if (err != 0)
                  goto bail;
  
!         /* Sanity-check newfound outer selector for tunnel mode mismatches */
!         if (tunnel_mode &&
              ((isrc->sin6_family == AF_INET &&
              sel.ips_protocol != IPPROTO_ENCAP && sel.ips_protocol != 0) ||
              (isrc->sin6_family == AF_INET6 &&
              sel.ips_protocol != IPPROTO_IPV6 && sel.ips_protocol != 0))) {
                  err = EPROTOTYPE;
*** 6794,6806 ****
                  goto bail;
          }
  
          /*
           * Okay, we have the addresses and other selector information.
!          * Let's first find a conn...
           */
-         pp = NULL;
          switch (sel.ips_protocol) {
          case IPPROTO_TCP:
                  ipsec_tcp_pol(&sel, &pp, ipst);
                  break;
          case IPPROTO_UDP:
--- 7063,7077 ----
                  goto bail;
          }
  
          /*
           * Okay, we have the addresses and other selector information.
!          * If our selector is for a protocol on top of IP, we make protocol-
!          * specific queries that work through useful state (e.g. connections or
!          * listeners). If we get something back, a reference to it will already
!          * be held, and we need to release that reference.
           */
          switch (sel.ips_protocol) {
          case IPPROTO_TCP:
                  ipsec_tcp_pol(&sel, &pp, ipst);
                  break;
          case IPPROTO_UDP:
*** 6810,6821 ****
                  ipsec_sctp_pol(&sel, &pp, ipst);
                  break;
          case IPPROTO_ENCAP:
          case IPPROTO_IPV6:
                  /*
!                  * Assume sel.ips_remote_addr_* has the right address at
!                  * that exact position.
                   */
                  itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
                      (uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
                      ipst);
  
--- 7081,7094 ----
                  ipsec_sctp_pol(&sel, &pp, ipst);
                  break;
          case IPPROTO_ENCAP:
          case IPPROTO_IPV6:
                  /*
!                  * These cases are IPv6 in IP or IP in IP. Revert to querying
!                  * SPD for tunnel policy, since there's no higher-level protocol
!                  * or stack state to assist. Assume sel.ips_remote_addr_* has
!                  * right address at exact position.
                   */
                  itp = itp_get_byaddr((uint32_t *)(&sel.ips_local_addr_v6),
                      (uint32_t *)(&sel.ips_remote_addr_v6), src->sin6_family,
                      ipst);
  
*** 6824,6833 ****
--- 7097,7107 ----
                           * Transport-mode tunnel, make sure we fake out isel
                           * to contain something based on the outer protocol.
                           */
                          bzero(&isel, sizeof (isel));
                          isel.ips_isv4 = (sel.ips_protocol == IPPROTO_ENCAP);
+                         /* XXX does this make tunnel_mode true? */
                  } /* Else isel is initialized by ipsec_tun_pol(). */
                  err = ipsec_tun_pol(&isel, &pp, innsrcext, inndstext, itp,
                      &diagnostic);
                  /*
                   * NOTE:  isel isn't used for now, but in RFC 430x IPsec, it
*** 6834,6851 ****
                   * may be.
                   */
                  if (err != 0)
                          goto bail;
                  break;
!         default:
                  ipsec_oth_pol(&sel, &pp, ipst);
                  break;
          }
  
          /*
!          * If we didn't find a matching conn_t or other policy head, take a
!          * look in the global policy.
           */
          if (pp == NULL) {
                  pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
                  if (pp == NULL) {
                          /* There's no global policy. */
--- 7108,7125 ----
                   * may be.
                   */
                  if (err != 0)
                          goto bail;
                  break;
!         default: /* Fall through to generic lookup */
                  ipsec_oth_pol(&sel, &pp, ipst);
                  break;
          }
  
          /*
!          * If we didn't find a matching conn_t or other policy head (pp retains
!          * initial NULL value), attempt to revert to the global policy.
           */
          if (pp == NULL) {
                  pp = ipsec_find_policy(IPSEC_TYPE_OUTBOUND, NULL, &sel, ns);
                  if (pp == NULL) {
                          /* There's no global policy. */
*** 6858,6880 ****
          /*
           * Now that we have a policy entry/widget, construct an ACQUIRE
           * message based on that, fix fields where appropriate,
           * and return the message.
           */
!         retmp = sadb_extended_acquire(&sel, pp, NULL,
!             (itp != NULL && (itp->itp_flags & ITPF_P_TUNNEL)),
!             samsg->sadb_msg_seq, samsg->sadb_msg_pid, sens, ns);
!         if (pp != NULL) {
                  IPPOL_REFRELE(pp);
!         }
          ASSERT(err == 0 && diagnostic == 0);
-         if (retmp == NULL)
                  err = ENOMEM;
  bail:
          if (itp != NULL) {
                  ITP_REFRELE(itp, ns);
          }
          samsg->sadb_msg_errno = (uint8_t)err;
          samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
          return (retmp);
  }
  
--- 7132,7178 ----
          /*
           * Now that we have a policy entry/widget, construct an ACQUIRE
           * message based on that, fix fields where appropriate,
           * and return the message.
           */
!         ap = pp->ipsp_act;
!         ASSERT(ap != NULL);
! 
!         if (ap != NULL)
!                 IPACT_REFHOLD(ap);
! 
!         retmp = sadb_construct_acqmsg(NULL, &sel, ap, pp, ns, sens, 0,
!             tunnel_mode, B_TRUE, B_TRUE);
!         if (retmp == NULL)
!                 goto nomem_bail;
! 
!         retmsg = (sadb_msg_t *)retmp->b_rptr;
!         retmsg->sadb_msg_seq = samsg->sadb_msg_seq;
!         retmsg->sadb_msg_pid = samsg->sadb_msg_pid;
! 
!         if (pp != NULL)
                  IPPOL_REFRELE(pp);
!         if (ap != NULL)
!                 IPACT_REFRELE(ap);
! 
!         return (retmp);
! 
! nomem_bail:
!         if (pp != NULL)
!                 IPPOL_REFRELE(pp);
!         if (ap != NULL)
!                 IPACT_REFRELE(ap);
          ASSERT(err == 0 && diagnostic == 0);
          err = ENOMEM;
  bail:
          if (itp != NULL) {
                  ITP_REFRELE(itp, ns);
          }
+         /*
+          * Write error info into original message, as we may not have resources
+          * for a proper reply.
+          */
          samsg->sadb_msg_errno = (uint8_t)err;
          samsg->sadb_x_msg_diagnostic = (uint16_t)diagnostic;
          return (retmp);
  }
  
*** 7188,7198 ****
          crypto_key_t *key;
          crypto_ctx_template_t *sa_tmpl;
          int rv;
          ipsec_stack_t   *ipss = sa->ipsa_netstack->netstack_ipsec;
  
!         ASSERT(MUTEX_HELD(&ipss->ipsec_alg_lock));
          ASSERT(MUTEX_HELD(&sa->ipsa_lock));
  
          /* get pointers to the algorithm info, context template, and key */
          switch (alg_type) {
          case IPSEC_ALG_AUTH:
--- 7486,7496 ----
          crypto_key_t *key;
          crypto_ctx_template_t *sa_tmpl;
          int rv;
          ipsec_stack_t   *ipss = sa->ipsa_netstack->netstack_ipsec;
  
!         ASSERT(RW_READ_HELD(&ipss->ipsec_alg_lock));
          ASSERT(MUTEX_HELD(&sa->ipsa_lock));
  
          /* get pointers to the algorithm info, context template, and key */
          switch (alg_type) {
          case IPSEC_ALG_AUTH: