]> git.saurik.com Git - apple/xnu.git/blobdiff - bsd/netkey/key.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / netkey / key.c
index f80c0c0adbd3186e3db6bb6651854c3eb4077ecd..91b6ba0401d54d4fda30f42f8483b6e0b7a58436 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008-2013 Apple Inc. All rights reserved.
+ * Copyright (c) 2008-2014 Apple Inc. All rights reserved.
  *
  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
  *
 #include <netinet6/ip6_var.h>
 #endif /* INET6 */
 
-#if INET
-#include <netinet/in_pcb.h>
-#endif
-#if INET6
-#include <netinet6/in6_pcb.h>
-#endif /* INET6 */
-
 #include <net/pfkeyv2.h>
 #include <netkey/keydb.h>
 #include <netkey/key.h>
@@ -422,7 +415,7 @@ if (d_e) bcopy((d_e), &(idx)->dst_range.end, ((struct sockaddr *)(d_e))->sa_len)
  * set parameters into secasindex buffer.
  * Must allocate secasindex buffer before calling this function.
  */
-#define KEY_SETSECASIDX(p, m, r, s, d, idx) \
+#define KEY_SETSECASIDX(p, m, r, s, d, ifi, idx) \
 do { \
 bzero((idx), sizeof(struct secasindex));                             \
 (idx)->proto = (p);                                                  \
@@ -430,6 +423,7 @@ bzero((idx), sizeof(struct secasindex));                             \
 (idx)->reqid = (r);                                                  \
 bcopy((s), &(idx)->src, ((const struct sockaddr *)(s))->sa_len);           \
 bcopy((d), &(idx)->dst, ((const struct sockaddr *)(d))->sa_len);           \
+(idx)->ipsec_ifindex = (ifi);                                                                          \
 } while (0)
 
 /* key statistics */
@@ -473,9 +467,10 @@ static struct mbuf *key_setdumpsp(struct secpolicy *,
                                                                  u_int8_t, u_int32_t, u_int32_t);
 static u_int key_getspreqmsglen(struct secpolicy *);
 static int key_spdexpire(struct secpolicy *);
-static struct secashead *key_newsah(struct secasindex *, u_int8_t);
+static struct secashead *key_newsah(struct secasindex *, ifnet_t, u_int, u_int8_t);
 static struct secasvar *key_newsav(struct mbuf *,
-                                                                  const struct sadb_msghdr *, struct secashead *, int *);
+                                                                  const struct sadb_msghdr *, struct secashead *, int *,
+                                                                  struct socket *);
 static struct secashead *key_getsah(struct secasindex *);
 static struct secasvar *key_checkspidup(struct secasindex *, u_int32_t);
 static void key_setspi __P((struct secasvar *, u_int32_t));
@@ -495,7 +490,7 @@ static struct mbuf *key_setsadbipsecif(ifnet_t, ifnet_t, ifnet_t, int);
 static struct mbuf *key_setsadbident(u_int16_t, u_int16_t, caddr_t,
                                                                         int, u_int64_t);
 #endif
-static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t);
+static struct mbuf *key_setsadbxsa2(u_int8_t, u_int32_t, u_int32_t, u_int16_t);
 static struct mbuf *key_setsadbxpolicy(u_int16_t, u_int8_t,
                                                                           u_int32_t);
 static void *key_newbuf(const void *, u_int);
@@ -590,6 +585,8 @@ static int key_setsaval2(struct secasvar      *sav,
 extern int ipsec_bypass;
 extern int esp_udp_encap_port;
 int ipsec_send_natt_keepalive(struct secasvar *sav);
+bool ipsec_fill_offload_frame(ifnet_t ifp, struct secasvar *sav, struct ipsec_offload_frame *frame, size_t frame_data_offset);
+u_int32_t key_fill_offload_frames_for_savs (ifnet_t ifp, struct ipsec_offload_frame *frames_array, u_int32_t frames_array_count, size_t frame_data_offset);
 
 void key_init(struct protosw *, struct domain *);
 
@@ -673,12 +670,14 @@ key_start_timehandler(void)
 {
        /* must be called while locked */
        lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
-#ifndef IPSEC_DEBUG2
        if (key_timehandler_running == 0) {
                key_timehandler_running = 1;
                (void)timeout((void *)key_timehandler, (void *)0, hz);
        }
-#endif /*IPSEC_DEBUG2*/
+       
+       /* Turn off the ipsec bypass */
+       if (ipsec_bypass != 0)
+               ipsec_bypass = 0;
 }
 
 /* %%% IPsec policy management */
@@ -831,6 +830,59 @@ found:
        return sp;
 }
 
+struct secasvar *key_alloc_outbound_sav_for_interface(ifnet_t interface, int family)
+{
+       struct secashead *sah;
+       struct secasvar *sav;
+       u_int stateidx;
+       u_int state;
+       const u_int *saorder_state_valid;
+       int arraysize;
+       struct sockaddr_in *sin;
+       u_int16_t dstport;
+    
+       if (interface == NULL)
+        return NULL;
+       
+       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED);
+       
+       lck_mtx_lock(sadb_mutex);
+       
+       LIST_FOREACH(sah, &sahtree, chain) {
+               if (sah->ipsec_if == interface &&
+                       (family == AF_INET6 || sah->saidx.dst.ss_family == family) && /* IPv6 can go over IPv4 */
+                       sah->dir == IPSEC_DIR_OUTBOUND) {
+                       /* This SAH is linked to the IPSec interface, and the right family. We found it! */
+                       if (key_preferred_oldsa) {
+                               saorder_state_valid = saorder_state_valid_prefer_old;
+                               arraysize = _ARRAYLEN(saorder_state_valid_prefer_old);
+                       } else {
+                               saorder_state_valid = saorder_state_valid_prefer_new;
+                               arraysize = _ARRAYLEN(saorder_state_valid_prefer_new);
+                       }
+                       
+                       sin = (struct sockaddr_in *)&sah->saidx.dst;
+                       dstport = sin->sin_port;
+                       if (sah->saidx.mode == IPSEC_MODE_TRANSPORT)
+                               sin->sin_port = IPSEC_PORT_ANY;
+                       
+                       for (stateidx = 0; stateidx < arraysize; stateidx++) {
+                               state = saorder_state_valid[stateidx];
+                               sav = key_do_allocsa_policy(sah, state, dstport);
+                               if (sav != NULL) {
+                                       lck_mtx_unlock(sadb_mutex);
+                                       return sav;
+                               }
+                       }
+                       
+                       break;
+               }
+       }
+       
+       lck_mtx_unlock(sadb_mutex);
+       return NULL;
+}
+
 /*
  * allocating an SA entry for an *OUTBOUND* packet.
  * checking each request entries in SP, and acquire an SA if need.
@@ -1441,7 +1493,6 @@ key_do_get_translated_port(
 
 /*
  * Must be called after calling key_allocsp().
- * For both the packet without socket and key_freeso().
  */
 void
 key_freesp(
@@ -1469,102 +1520,6 @@ key_freesp(
        return;
 }
 
-#if 0
-static void key_freesp_so(struct secpolicy **);
-
-/*
- * Must be called after calling key_allocsp().
- * For the packet with socket.
- */
-void
-key_freeso(
-                  struct socket *so)
-{
-       
-       /* sanity check */
-       if (so == NULL)
-               panic("key_freeso: NULL pointer is passed.\n");
-       
-       lck_mtx_lock(sadb_mutex);
-       switch (SOCK_DOM(so)) {
-#if INET
-               case PF_INET:
-           {
-                       struct inpcb *pcb = sotoinpcb(so);
-                       
-                       /* Does it have a PCB ? */
-                       if (pcb == NULL || pcb->inp_sp == NULL)
-                               goto done;
-                       key_freesp_so(&pcb->inp_sp->sp_in);
-                       key_freesp_so(&pcb->inp_sp->sp_out);
-           }
-                       break;
-#endif
-#if INET6
-               case PF_INET6:
-           {
-#if HAVE_NRL_INPCB
-                       struct inpcb *pcb  = sotoinpcb(so);
-                       
-                       /* Does it have a PCB ? */
-                       if (pcb == NULL || pcb->inp_sp == NULL)
-                               goto done;
-                       key_freesp_so(&pcb->inp_sp->sp_in);
-                       key_freesp_so(&pcb->inp_sp->sp_out);
-#else
-                       struct in6pcb *pcb  = sotoin6pcb(so);
-                       
-                       /* Does it have a PCB ? */
-                       if (pcb == NULL || pcb->in6p_sp == NULL)
-                               goto done;
-                       key_freesp_so(&pcb->in6p_sp->sp_in);
-                       key_freesp_so(&pcb->in6p_sp->sp_out);
-#endif
-           }
-                       break;
-#endif /* INET6 */
-               default:
-                       ipseclog((LOG_DEBUG, "key_freeso: unknown address family=%d.\n",
-                           SOCK_DOM(so)));
-                       break;
-       }
-done:
-       lck_mtx_unlock(sadb_mutex);
-       
-       return;
-}
-
-static void
-key_freesp_so(
-                         struct secpolicy **sp)
-{
-       
-       /* sanity check */
-       if (sp == NULL || *sp == NULL)
-               panic("key_freesp_so: sp == NULL\n");
-       
-       lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_OWNED);
-       
-       switch ((*sp)->policy) {
-               case IPSEC_POLICY_IPSEC:
-                       KEYDEBUG(KEYDEBUG_IPSEC_STAMP,
-                           printf("DP freeso calls free SP:0x%llx\n",
-                           (uint64_t)VM_KERNEL_ADDRPERM(*sp)));
-                       key_freesp(*sp, KEY_SADB_LOCKED);
-                       *sp = NULL;
-                       break;
-               case IPSEC_POLICY_ENTRUST:
-               case IPSEC_POLICY_BYPASS:
-                       return;
-               default:
-                       panic("key_freesp_so: Invalid policy found %d", (*sp)->policy);
-       }
-       
-       return;
-}
-
-#endif
-
 /*
  * Must be called after calling key_allocsa().
  * This function is called by key_freesp() to free some SA allocated
@@ -2267,7 +2222,7 @@ key_spdadd(
         }
     }
     
-    /* checking the direciton. */
+    /* checking the direction. */
        switch (xpl0->sadb_x_policy_dir) {
         case IPSEC_DIR_INBOUND:
         case IPSEC_DIR_OUTBOUND:
@@ -2629,7 +2584,7 @@ key_spddelete(
        xpl0 = (struct sadb_x_policy *)(void *)mhp->ext[SADB_X_EXT_POLICY];
     ipsecifopts = (struct sadb_x_ipsecif *)(void *)mhp->ext[SADB_X_EXT_IPSECIF];
     
-    /* checking the direciton. */
+    /* checking the direction. */
        switch (xpl0->sadb_x_policy_dir) {
         case IPSEC_DIR_INBOUND:
         case IPSEC_DIR_OUTBOUND:
@@ -3487,9 +3442,10 @@ fail:
  *     others  : pointer to new SA head.
  */
 static struct secashead *
-key_newsah(
-                  struct secasindex *saidx,
-                  u_int8_t           dir)
+key_newsah(struct secasindex *saidx,
+                  ifnet_t ipsec_if,
+                  u_int outgoing_if,
+                  u_int8_t dir)
 {
        struct secashead *newsah;
        
@@ -3525,11 +3481,17 @@ key_newsah(
                        break;
        }
        
+       newsah->outgoing_if = outgoing_if;
+       if (ipsec_if) {
+               ifnet_reference(ipsec_if);
+               newsah->ipsec_if = ipsec_if;
+       }
        newsah->dir = dir;
        /* add to saidxtree */
        newsah->state = SADB_SASTATE_MATURE;
        LIST_INSERT_HEAD(&sahtree, newsah, chain);
        key_start_timehandler();
+
        return(newsah);
 }
 
@@ -3585,6 +3547,11 @@ key_delsah(
        
        ROUTE_RELEASE(&sah->sa_route);
     
+       if (sah->ipsec_if) {
+               ifnet_release(sah->ipsec_if);
+               sah->ipsec_if = NULL;
+       }
+       
     if (sah->idents) {
         KFREE(sah->idents);
     }
@@ -3619,7 +3586,8 @@ key_newsav(
                   struct mbuf *m,
                   const struct sadb_msghdr *mhp,
                   struct secashead *sah,
-                  int *errp)
+                  int *errp,
+                  struct socket *so)
 {
        struct secasvar *newsav;
        const struct sadb_sa *xsa;
@@ -3660,7 +3628,7 @@ key_newsav(
                case SADB_ADD:
                        /* sanity check */
                        if (mhp->ext[SADB_EXT_SA] == NULL) {
-                               KFREE(newsav);
+                               key_delsav(newsav);
                                ipseclog((LOG_DEBUG, "key_newsa: invalid message is passed.\n"));
                                *errp = EINVAL;
                                return NULL;
@@ -3670,7 +3638,7 @@ key_newsav(
                        newsav->seq = mhp->msg->sadb_msg_seq;
                        break;
                default:
-                       KFREE(newsav);
+                       key_delsav(newsav);
                        *errp = EINVAL;
                        return NULL;
        }
@@ -3678,15 +3646,17 @@ key_newsav(
        if (mhp->ext[SADB_X_EXT_SA2] != NULL) {
                if (((struct sadb_x_sa2 *)(void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_alwaysexpire)
                        newsav->always_expire = 1;
+               newsav->flags2 = ((struct sadb_x_sa2 *)(void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_flags;
+               if (newsav->flags2 & SADB_X_EXT_SA2_DELETE_ON_DETACH) {
+                       newsav->so = so;
+               }
        }
        
        /* copy sav values */
        if (mhp->msg->sadb_msg_type != SADB_GETSPI) {
                *errp = key_setsaval(newsav, m, mhp);
                if (*errp) {
-                       if (newsav->spihash.le_prev || newsav->spihash.le_next)
-                               LIST_REMOVE(newsav, spihash);
-                       KFREE(newsav);
+                       key_delsav(newsav);
                        return NULL;
                }
        } else {
@@ -3706,7 +3676,7 @@ key_newsav(
                                lck_mtx_lock(sadb_mutex);
                                if (newsav->lft_c == NULL) {
                                        ipseclog((LOG_DEBUG, "key_newsa: No more memory.\n"));
-                                       KFREE(newsav);
+                                       key_delsav(newsav);
                                        *errp = ENOBUFS;
                                        return NULL;
                                }
@@ -3723,14 +3693,14 @@ key_newsav(
                        
                        if (mhp->extlen[SADB_EXT_LIFETIME_HARD] < sizeof(*lft0)) {
                                ipseclog((LOG_DEBUG, "key_newsa: invalid hard lifetime ext len.\n"));
-                               KFREE(newsav);
+                               key_delsav(newsav);
                                *errp = EINVAL;
                                return NULL;
                        }
                        newsav->lft_h = (struct sadb_lifetime *)key_newbuf(lft0, sizeof(*lft0));
                        if (newsav->lft_h == NULL) {
                                ipseclog((LOG_DEBUG, "key_newsa: No more memory.\n"));
-                               KFREE(newsav);
+                               key_delsav(newsav);
                                *errp = ENOBUFS;
                                return NULL;
                        }
@@ -3830,9 +3800,7 @@ key_newsav2(struct secashead     *sah,
                                          pid,
                                          lifetime_hard,
                                          lifetime_soft)) {
-               if (newsav->spihash.le_prev || newsav->spihash.le_next)
-                       LIST_REMOVE(newsav, spihash);
-               KFREE(newsav);
+               key_delsav(newsav);
                return NULL;
        }
        
@@ -3935,8 +3903,7 @@ key_delsav(
  *     others  : found, pointer to a SA.
  */
 static struct secashead *
-key_getsah(
-                  struct secasindex *saidx)
+key_getsah(struct secasindex *saidx)
 {
        struct secashead *sah;
        
@@ -3962,7 +3929,7 @@ key_newsah2 (struct secasindex *saidx,
        
        sah = key_getsah(saidx);
        if (!sah) {
-               return(key_newsah(saidx, dir));
+               return(key_newsah(saidx, NULL, 0, dir));
        }
        return sah;
 }
@@ -4122,6 +4089,7 @@ key_setsaval(
                                goto fail;
                        }
                        sav->remote_ike_port = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_port;
+                       sav->natt_interval = ((const struct sadb_sa_2*)(sa0))->sadb_sa_natt_interval;
                }
                
                /*
@@ -4819,7 +4787,8 @@ key_setdumpsa(
                        case SADB_X_EXT_SA2:
                                m = key_setsadbxsa2(sav->sah->saidx.mode,
                                                                        sav->replay ? sav->replay->count : 0,
-                                                                       sav->sah->saidx.reqid);
+                                                                       sav->sah->saidx.reqid,
+                                                                       sav->flags2);
                                if (!m)
                                        goto fail;
                                break;
@@ -4907,6 +4876,13 @@ key_setdumpsa(
        
        m_cat(result, tres);
        
+       if (sav->sah && (sav->sah->outgoing_if || sav->sah->ipsec_if)) {
+        m = key_setsadbipsecif(NULL, ifindex2ifnet[sav->sah->outgoing_if], sav->sah->ipsec_if, 0);
+        if (!m)
+            goto fail;
+        m_cat(result, m);
+    }
+       
        if (result->m_len < sizeof(struct sadb_msg)) {
                result = m_pullup(result, sizeof(struct sadb_msg));
                if (result == NULL)
@@ -5085,11 +5061,11 @@ key_setsadbipsecif(ifnet_t internal_if,
        p->sadb_x_ipsecif_exttype = SADB_X_EXT_IPSECIF;
     
     if (internal_if && internal_if->if_xname)
-        strncpy(p->sadb_x_ipsecif_internal_if, internal_if->if_xname, IFXNAMSIZ);
+        strlcpy(p->sadb_x_ipsecif_internal_if, internal_if->if_xname, IFXNAMSIZ);
     if (outgoing_if && outgoing_if->if_xname)
-        strncpy(p->sadb_x_ipsecif_outgoing_if, outgoing_if->if_xname, IFXNAMSIZ);
+        strlcpy(p->sadb_x_ipsecif_outgoing_if, outgoing_if->if_xname, IFXNAMSIZ);
     if (ipsec_if && ipsec_if->if_xname)
-        strncpy(p->sadb_x_ipsecif_ipsec_if, ipsec_if->if_xname, IFXNAMSIZ);
+        strlcpy(p->sadb_x_ipsecif_ipsec_if, ipsec_if->if_xname, IFXNAMSIZ);
     
        p->sadb_x_ipsecif_init_disabled = init_disabled;
     
@@ -5214,7 +5190,8 @@ static struct mbuf *
 key_setsadbxsa2(
                                u_int8_t mode,
                                u_int32_t seq,
-                               u_int32_t reqid)
+                               u_int32_t reqid,
+                               u_int16_t flags)
 {
        struct mbuf *m;
        struct sadb_x_sa2 *p;
@@ -5238,6 +5215,7 @@ key_setsadbxsa2(
        p->sadb_x_sa2_reserved2 = 0;
        p->sadb_x_sa2_sequence = seq;
        p->sadb_x_sa2_reqid = reqid;
+       p->sadb_x_sa2_flags = flags;
        
        return m;
 }
@@ -5427,6 +5405,9 @@ key_cmpsaidx(
        if (saidx0 == NULL || saidx1 == NULL)
                return 0;
        
+       if (saidx0->ipsec_ifindex != 0 && saidx0->ipsec_ifindex != saidx1->ipsec_ifindex)
+               return 0;
+       
        if (saidx0->proto != saidx1->proto)
                return 0;
        
@@ -5976,7 +5957,7 @@ key_timehandler(void)
                         */
                        if (savkabuf && savkacount < savbufcount) {
                                sav = LIST_FIRST(&sah->savtree[SADB_SASTATE_MATURE]);   //%%% should we check dying list if this is empty???
-                               if (natt_keepalive_interval && sav &&
+                               if (sav && (natt_keepalive_interval || sav->natt_interval) &&
                                        (sav->flags & (SADB_X_EXT_NATT_KEEPALIVE | SADB_X_EXT_ESP_KEEPALIVE)) != 0) {
                                        sav->refcnt++;
                                        *savkaptr++ = sav;
@@ -6260,14 +6241,14 @@ key_timehandler(void)
                KFREE(savexbuf);
        }
        
-#ifndef IPSEC_DEBUG2
-       if (stop_handler)
+       if (stop_handler) {
                key_timehandler_running = 0;
-       else {
+               /* Turn on the ipsec bypass */
+               ipsec_bypass = 1;
+       } else {
                /* do exchange to tick time !! */
                (void)timeout((void *)key_timehandler, (void *)0, hz);
        }
-#endif /* IPSEC_DEBUG2 */
 
        lck_mtx_unlock(sadb_mutex);
        return;
@@ -6382,6 +6363,38 @@ key_proto2satype(
        /* NOTREACHED */
 }
 
+static ifnet_t
+key_get_ipsec_if_from_message (const struct sadb_msghdr *mhp)
+{
+       struct sadb_x_ipsecif *ipsecifopts = NULL;
+       ifnet_t ipsec_if = NULL;
+       
+       ipsecifopts = (struct sadb_x_ipsecif *)(void *)mhp->ext[SADB_X_EXT_IPSECIF];
+       if (ipsecifopts != NULL) {
+               if (ipsecifopts->sadb_x_ipsecif_internal_if) {
+                       ifnet_find_by_name(ipsecifopts->sadb_x_ipsecif_ipsec_if, &ipsec_if);
+               }
+       }
+       
+       return ipsec_if;
+}
+
+static u_int
+key_get_outgoing_ifindex_from_message (const struct sadb_msghdr *mhp)
+{
+       struct sadb_x_ipsecif *ipsecifopts = NULL;
+       ifnet_t outgoing_if = NULL;
+       
+       ipsecifopts = (struct sadb_x_ipsecif *)(void *)mhp->ext[SADB_X_EXT_IPSECIF];
+       if (ipsecifopts != NULL) {
+               if (ipsecifopts->sadb_x_ipsecif_outgoing_if) {
+                       ifnet_find_by_name(ipsecifopts->sadb_x_ipsecif_outgoing_if, &outgoing_if);
+        }
+    }
+       
+       return outgoing_if ? outgoing_if->if_index : 0;
+}
+
 /* %%% PF_KEY */
 /*
  * SADB_GETSPI processing is to receive
@@ -6405,6 +6418,7 @@ key_getspi(
        struct secasindex saidx;
        struct secashead *newsah;
        struct secasvar *newsav;
+       ifnet_t ipsec_if = NULL;
        u_int8_t proto;
        u_int32_t spi;
        u_int8_t mode;
@@ -6440,6 +6454,8 @@ key_getspi(
        src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
        dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
        
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
+       
        /* map satype to proto */
        if ((proto = key_satype2proto(mhp->msg->sadb_msg_satype)) == 0) {
                ipseclog((LOG_DEBUG, "key_getspi: invalid satype is passed.\n"));
@@ -6481,7 +6497,7 @@ key_getspi(
        }
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        lck_mtx_lock(sadb_mutex);
        
@@ -6496,7 +6512,7 @@ key_getspi(
        /* get a SA index */
        if ((newsah = key_getsah(&saidx)) == NULL) {
                /* create a new SA index: key_addspi is always used for inbound spi */
-               if ((newsah = key_newsah(&saidx, IPSEC_DIR_INBOUND)) == NULL) {
+               if ((newsah = key_newsah(&saidx, ipsec_if, key_get_outgoing_ifindex_from_message(mhp), IPSEC_DIR_INBOUND)) == NULL) {
                        lck_mtx_unlock(sadb_mutex);
                        ipseclog((LOG_DEBUG, "key_getspi: No more memory.\n"));
                        return key_senderror(so, m, ENOBUFS);
@@ -6505,7 +6521,7 @@ key_getspi(
        
        /* get a new SA */
        /* XXX rewrite */
-       newsav = key_newsav(m, mhp, newsah, &error);
+       newsav = key_newsav(m, mhp, newsah, &error, so);
        if (newsav == NULL) {
                /* XXX don't free new SA index allocated in above. */
                lck_mtx_unlock(sadb_mutex);
@@ -6614,7 +6630,7 @@ key_getspi2(struct sockaddr      *src,
        lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, mode, reqid, src, dst, &saidx);
+       KEY_SETSECASIDX(proto, mode, reqid, src, dst, 0, &saidx);
        
        /* make sure if port number is zero. */
        switch (((struct sockaddr *)&saidx.src)->sa_family) {
@@ -6755,12 +6771,14 @@ key_update(
 {
        struct sadb_sa *sa0;
        struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *sah;
        struct secasvar *sav;
        u_int16_t proto;
        u_int8_t mode;
        u_int32_t reqid;
+       u_int16_t flags2;
        int error;
        
        lck_mtx_assert(sadb_mutex, LCK_MTX_ASSERT_NOTOWNED);
@@ -6800,18 +6818,21 @@ key_update(
                                (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
                reqid = ((struct sadb_x_sa2 *)
                                 (void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_reqid;
+               flags2 = ((struct sadb_x_sa2 *)(void *)mhp->ext[SADB_X_EXT_SA2])->sadb_x_sa2_flags;
        } else {
                mode = IPSEC_MODE_ANY;
                reqid = 0;
+               flags2 = 0;
        }
        /* XXX boundary checking for other extensions */
        
        sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA];
        src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
        dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        lck_mtx_lock(sadb_mutex);
        
@@ -6883,7 +6904,12 @@ key_update(
                lck_mtx_unlock(sadb_mutex);
                return key_senderror(so, m, error);
        }
-       
+
+       sav->flags2 = flags2;
+       if (flags2 & SADB_X_EXT_SA2_DELETE_ON_DETACH) {
+               sav->so = so;
+       }
+
        /*
         * Verify if SADB_X_EXT_NATT_MULTIPLEUSERS flag is set that
         * this SA is for transport mode - otherwise clear it.
@@ -6979,6 +7005,7 @@ key_add(
 {
        struct sadb_sa *sa0;
        struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *newsah;
        struct secasvar *newsav;
@@ -7033,16 +7060,17 @@ key_add(
        sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA];
        src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
        dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, mode, reqid, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        lck_mtx_lock(sadb_mutex);
        
        /* get a SA header */
        if ((newsah = key_getsah(&saidx)) == NULL) {
                /* create a new SA header: key_addspi is always used for outbound spi */
-               if ((newsah = key_newsah(&saidx, IPSEC_DIR_OUTBOUND)) == NULL) {
+               if ((newsah = key_newsah(&saidx, ipsec_if, key_get_outgoing_ifindex_from_message(mhp), IPSEC_DIR_OUTBOUND)) == NULL) {
                        lck_mtx_unlock(sadb_mutex);
                        ipseclog((LOG_DEBUG, "key_add: No more memory.\n"));
                        return key_senderror(so, m, ENOBUFS);
@@ -7064,7 +7092,7 @@ key_add(
                ipseclog((LOG_DEBUG, "key_add: SA already exists.\n"));
                return key_senderror(so, m, EEXIST);
        }
-       newsav = key_newsav(m, mhp, newsah, &error);
+       newsav = key_newsav(m, mhp, newsah, &error, so);
        if (newsav == NULL) {
                lck_mtx_unlock(sadb_mutex);
                return key_senderror(so, m, error);
@@ -7250,6 +7278,7 @@ key_delete(
 {
        struct sadb_sa *sa0;
        struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *sah;
        struct secasvar *sav = NULL;
@@ -7299,9 +7328,10 @@ key_delete(
        sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA];
        src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
        dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        /* get a SA header */
        LIST_FOREACH(sah, &sahtree, chain) {
@@ -7363,6 +7393,7 @@ key_delete_all(
                           u_int16_t proto)
 {
        struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *sah;
        struct secasvar *sav, *nextsav;
@@ -7372,9 +7403,10 @@ key_delete_all(
        
        src0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_SRC]);
        dst0 = (struct sadb_address *)(mhp->ext[SADB_EXT_ADDRESS_DST]);
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        LIST_FOREACH(sah, &sahtree, chain) {
                if (sah->state == SADB_SASTATE_DEAD)
@@ -7453,6 +7485,7 @@ key_get(
 {
        struct sadb_sa *sa0;
        struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *sah;
        struct secasvar *sav = NULL;
@@ -7486,9 +7519,10 @@ key_get(
        sa0 = (struct sadb_sa *)(void *)mhp->ext[SADB_EXT_SA];
        src0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
        dst0 = (struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
-       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        lck_mtx_lock(sadb_mutex);
        
@@ -8200,6 +8234,7 @@ key_acquire2(
                         const struct sadb_msghdr *mhp)
 {
        const struct sadb_address *src0, *dst0;
+       ifnet_t ipsec_if = NULL;
        struct secasindex saidx;
        struct secashead *sah;
        u_int16_t proto;
@@ -8281,10 +8316,11 @@ key_acquire2(
        
        src0 = (const struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_SRC];
        dst0 = (const struct sadb_address *)mhp->ext[SADB_EXT_ADDRESS_DST];
+       ipsec_if = key_get_ipsec_if_from_message(mhp);
        
        /* XXX boundary check against sa_len */
        /* cast warnings */
-       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, &saidx);
+       KEY_SETSECASIDX(proto, IPSEC_MODE_ANY, 0, src0 + 1, dst0 + 1, ipsec_if ? ipsec_if->if_index : 0, &saidx);
        
        /* get a SA index */
        LIST_FOREACH(sah, &sahtree, chain) {
@@ -8489,6 +8525,32 @@ setmsg:
     }
 }
 
+static void
+key_delete_all_for_socket (struct socket *so)
+{
+       struct secashead *sah, *nextsah;
+       struct secasvar *sav, *nextsav;
+       u_int stateidx;
+       u_int state;
+
+       for (sah = LIST_FIRST(&sahtree);
+                sah != NULL;
+                sah = nextsah) {
+               nextsah = LIST_NEXT(sah, chain);
+               for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_alive); stateidx++) {
+                       state = saorder_state_any[stateidx];
+                       for (sav = LIST_FIRST(&sah->savtree[state]); sav != NULL; sav = nextsav) {
+                               nextsav = LIST_NEXT(sav, chain);
+                               if (sav->flags2 & SADB_X_EXT_SA2_DELETE_ON_DETACH &&
+                                       sav->so == so) {
+                                       key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+                                       key_freesav(sav, KEY_SADB_LOCKED);
+                               }
+                       }
+               }
+       }
+}
+
 /*
  * free secreg entry registered.
  * XXX: I want to do free a socket marked done SADB_RESIGER to socket.
@@ -8510,6 +8572,7 @@ key_freereg(
         * one socket is registered to multiple type of SA.
         */
        lck_mtx_lock(sadb_mutex);
+       key_delete_all_for_socket(so);
        for (i = 0; i <= SADB_SATYPE_MAX; i++) {
                LIST_FOREACH(reg, &regtree[i], chain) {
                        if (reg->so == so
@@ -8573,7 +8636,8 @@ key_expire(
        /* create SA extension */
        m = key_setsadbxsa2(sav->sah->saidx.mode,
                                                sav->replay ? sav->replay->count : 0,
-                                               sav->sah->saidx.reqid);
+                                               sav->sah->saidx.reqid,
+                                               sav->flags2);
        if (!m) {
                error = ENOBUFS;
                goto fail;
@@ -9885,6 +9949,10 @@ fail:
 void
 key_delsp_for_ipsec_if (ifnet_t ipsec_if)
 {
+       struct secashead *sah;
+       struct secasvar *sav, *nextsav;
+       u_int stateidx;
+       u_int state;
        struct secpolicy *sp, *nextsp;
        int dir;
     
@@ -9914,6 +9982,58 @@ key_delsp_for_ipsec_if (ifnet_t ipsec_if)
                }
        }
        
+       LIST_FOREACH(sah, &sahtree, chain) {
+               if (sah->ipsec_if == ipsec_if) {
+                       /* This SAH is linked to the IPSec interface. It now needs to close. */
+                       ifnet_release(sah->ipsec_if);
+                       sah->ipsec_if = NULL;
+                       
+                       for (stateidx = 0; stateidx < _ARRAYLEN(saorder_state_alive); stateidx++) {
+                               state = saorder_state_any[stateidx];
+                               for (sav = LIST_FIRST(&sah->savtree[state]); sav != NULL; sav = nextsav) {
+                                       nextsav = LIST_NEXT(sav, chain);
+                                       
+                                       key_sa_chgstate(sav, SADB_SASTATE_DEAD);
+                                       key_freesav(sav, KEY_SADB_LOCKED);
+                               }
+                       }
+                       
+                       sah->state = SADB_SASTATE_DEAD;
+               }
+       }
+       
        lck_mtx_unlock(sadb_mutex);
-    
+}
+
+__private_extern__ u_int32_t
+key_fill_offload_frames_for_savs (ifnet_t ifp,
+                                                                 struct ipsec_offload_frame *frames_array,
+                                                                 u_int32_t frames_array_count,
+                                                                 size_t frame_data_offset)
+{
+       struct secashead *sah = NULL;
+       struct secasvar *sav = NULL;
+       struct ipsec_offload_frame *frame = frames_array;
+       u_int32_t frame_index = 0;
+
+       if (frame == NULL || frames_array_count == 0) {
+               return (frame_index);
+       }
+
+       lck_mtx_lock(sadb_mutex);
+       LIST_FOREACH(sah, &sahtree, chain) {
+               LIST_FOREACH(sav, &sah->savtree[SADB_SASTATE_MATURE], chain) {
+                       if (ipsec_fill_offload_frame(ifp, sav, frame, frame_data_offset)) {
+                               frame_index++;
+                               if (frame_index >= frames_array_count) {
+                                       lck_mtx_unlock(sadb_mutex);
+                                       return (frame_index);
+                               }
+                               frame = &(frames_array[frame_index]);
+                       }
+               }
+       }
+       lck_mtx_unlock(sadb_mutex);
+
+       return (frame_index);
 }