ipip: Tunnel flags controlling copying data to/from payload/encap
[vpp.git] / src / plugins / ikev2 / ikev2.c
index 929f977..a260bb1 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <vlib/vlib.h>
 #include <vlib/unix/plugin.h>
+#include <vlibmemory/api.h>
 #include <vpp/app/version.h>
 #include <vnet/vnet.h>
 #include <vnet/pg/pg.h>
@@ -22,6 +23,8 @@
 #include <vppinfra/random.h>
 #include <vnet/udp/udp.h>
 #include <vnet/ipsec/ipsec.h>
+#include <vnet/ipsec/ipsec_tun.h>
+#include <vnet/ipip/ipip.h>
 #include <plugins/ikev2/ikev2.h>
 #include <plugins/ikev2/ikev2_priv.h>
 #include <openssl/sha.h>
@@ -243,19 +246,24 @@ ikev2_sa_free_proposal_vector (ikev2_sa_proposal_t ** v)
   vec_free (*v);
 };
 
+static void
+ikev2_sa_free_child_sa (ikev2_child_sa_t * c)
+{
+  ikev2_sa_free_proposal_vector (&c->r_proposals);
+  ikev2_sa_free_proposal_vector (&c->i_proposals);
+  vec_free (c->sk_ai);
+  vec_free (c->sk_ar);
+  vec_free (c->sk_ei);
+  vec_free (c->sk_er);
+  vec_free (c->tsi);
+  vec_free (c->tsr);
+}
+
 static void
 ikev2_sa_free_all_child_sa (ikev2_child_sa_t ** childs)
 {
   ikev2_child_sa_t *c;
-  vec_foreach (c, *childs)
-  {
-    ikev2_sa_free_proposal_vector (&c->r_proposals);
-    ikev2_sa_free_proposal_vector (&c->i_proposals);
-    vec_free (c->sk_ai);
-    vec_free (c->sk_ar);
-    vec_free (c->sk_ei);
-    vec_free (c->sk_er);
-  }
+  vec_foreach (c, *childs) ikev2_sa_free_child_sa (c);
 
   vec_free (*childs);
 }
@@ -263,13 +271,7 @@ ikev2_sa_free_all_child_sa (ikev2_child_sa_t ** childs)
 static void
 ikev2_sa_del_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * child)
 {
-  ikev2_sa_free_proposal_vector (&child->r_proposals);
-  ikev2_sa_free_proposal_vector (&child->i_proposals);
-  vec_free (child->sk_ai);
-  vec_free (child->sk_ar);
-  vec_free (child->sk_ei);
-  vec_free (child->sk_er);
-
+  ikev2_sa_free_child_sa (child);
   vec_del1 (sa->childs, child - sa->childs);
 }
 
@@ -389,8 +391,9 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   sa->iaddr.as_u32 = sai->iaddr.as_u32;
   sa->raddr.as_u32 = sai->raddr.as_u32;
   sa->is_initiator = sai->is_initiator;
-  sa->profile = sai->profile;
   sa->i_id.type = sai->i_id.type;
+  sa->profile_index = sai->profile_index;
+  sa->is_profile_index_set = sai->is_profile_index_set;
   sa->i_id.data = _(sai->i_id.data);
   sa->i_auth.method = sai->i_auth.method;
   sa->i_auth.hex = sai->i_auth.hex;
@@ -437,6 +440,7 @@ ikev2_calc_keys (ikev2_sa_t * sa)
   /* calculate SKEYSEED = prf(Ni | Nr, g^ir) */
   u8 *skeyseed = 0;
   u8 *s = 0;
+  u16 integ_key_len = 0;
   ikev2_sa_transform_t *tr_encr, *tr_prf, *tr_integ;
   tr_encr =
     ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_ENCR);
@@ -445,6 +449,9 @@ ikev2_calc_keys (ikev2_sa_t * sa)
   tr_integ =
     ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG);
 
+  if (tr_integ)
+    integ_key_len = tr_integ->key_len;
+
   vec_append (s, sa->i_nonce);
   vec_append (s, sa->r_nonce);
   skeyseed = ikev2_calc_prf (tr_prf, s, sa->dh_shared_key);
@@ -459,7 +466,7 @@ ikev2_calc_keys (ikev2_sa_t * sa)
   /* calculate PRFplus */
   u8 *keymat;
   int len = tr_prf->key_trunc +        /* SK_d */
-    tr_integ->key_len * 2 +    /* SK_ai, SK_ar */
+    integ_key_len * 2 +                /* SK_ai, SK_ar */
     tr_encr->key_len * 2 +     /* SK_ei, SK_er */
     tr_prf->key_len * 2;       /* SK_pi, SK_pr */
 
@@ -474,15 +481,18 @@ ikev2_calc_keys (ikev2_sa_t * sa)
   clib_memcpy_fast (sa->sk_d, keymat + pos, tr_prf->key_trunc);
   pos += tr_prf->key_trunc;
 
-  /* SK_ai */
-  sa->sk_ai = vec_new (u8, tr_integ->key_len);
-  clib_memcpy_fast (sa->sk_ai, keymat + pos, tr_integ->key_len);
-  pos += tr_integ->key_len;
+  if (integ_key_len)
+    {
+      /* SK_ai */
+      sa->sk_ai = vec_new (u8, integ_key_len);
+      clib_memcpy_fast (sa->sk_ai, keymat + pos, integ_key_len);
+      pos += integ_key_len;
 
-  /* SK_ar */
-  sa->sk_ar = vec_new (u8, tr_integ->key_len);
-  clib_memcpy_fast (sa->sk_ar, keymat + pos, tr_integ->key_len);
-  pos += tr_integ->key_len;
+      /* SK_ar */
+      sa->sk_ar = vec_new (u8, integ_key_len);
+      clib_memcpy_fast (sa->sk_ar, keymat + pos, integ_key_len);
+      pos += integ_key_len;
+    }
 
   /* SK_ei */
   sa->sk_ei = vec_new (u8, tr_encr->key_len);
@@ -511,6 +521,9 @@ static void
 ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
 {
   u8 *s = 0;
+  u16 integ_key_len = 0;
+  u8 salt_len = 0;
+
   ikev2_sa_transform_t *tr_prf, *ctr_encr, *ctr_integ;
   tr_prf =
     ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_PRF);
@@ -519,11 +532,16 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
   ctr_integ =
     ikev2_sa_get_td_for_type (child->r_proposals, IKEV2_TRANSFORM_TYPE_INTEG);
 
+  if (ctr_integ)
+    integ_key_len = ctr_integ->key_len;
+  else
+    salt_len = sizeof (u32);
+
   vec_append (s, sa->i_nonce);
   vec_append (s, sa->r_nonce);
   /* calculate PRFplus */
   u8 *keymat;
-  int len = ctr_encr->key_len * 2 + ctr_integ->key_len * 2;
+  int len = ctr_encr->key_len * 2 + integ_key_len * 2 + salt_len * 2;
 
   keymat = ikev2_calc_prfplus (tr_prf, sa->sk_d, s, len);
 
@@ -534,20 +552,36 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
   clib_memcpy_fast (child->sk_ei, keymat + pos, ctr_encr->key_len);
   pos += ctr_encr->key_len;
 
-  /* SK_ai */
-  child->sk_ai = vec_new (u8, ctr_integ->key_len);
-  clib_memcpy_fast (child->sk_ai, keymat + pos, ctr_integ->key_len);
-  pos += ctr_integ->key_len;
+  if (ctr_integ)
+    {
+      /* SK_ai */
+      child->sk_ai = vec_new (u8, ctr_integ->key_len);
+      clib_memcpy_fast (child->sk_ai, keymat + pos, ctr_integ->key_len);
+      pos += ctr_integ->key_len;
+    }
+  else
+    {
+      clib_memcpy (&child->salt_ei, keymat + pos, salt_len);
+      pos += salt_len;
+    }
 
   /* SK_er */
   child->sk_er = vec_new (u8, ctr_encr->key_len);
   clib_memcpy_fast (child->sk_er, keymat + pos, ctr_encr->key_len);
   pos += ctr_encr->key_len;
 
-  /* SK_ar */
-  child->sk_ar = vec_new (u8, ctr_integ->key_len);
-  clib_memcpy_fast (child->sk_ar, keymat + pos, ctr_integ->key_len);
-  pos += ctr_integ->key_len;
+  if (ctr_integ)
+    {
+      /* SK_ar */
+      child->sk_ar = vec_new (u8, integ_key_len);
+      clib_memcpy_fast (child->sk_ar, keymat + pos, integ_key_len);
+      pos += integ_key_len;
+    }
+  else
+    {
+      clib_memcpy (&child->salt_er, keymat + pos, salt_len);
+      pos += salt_len;
+    }
 
   ASSERT (pos == len);
 
@@ -1274,7 +1308,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa)
       {
         if (ikev2_ts_cmp(p_tsi, ts))
           {
-            tsi = vec_dup(ts);
+            vec_add1 (tsi, ts[0]);
             break;
           }
       }
@@ -1283,7 +1317,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa)
       {
         if (ikev2_ts_cmp(p_tsr, ts))
           {
-            tsr = vec_dup(ts);
+            vec_add1 (tsr, ts[0]);
             break;
           }
       }
@@ -1473,16 +1507,79 @@ ikev2_sa_auth_init (ikev2_sa_t * sa)
   vec_free (authmsg);
 }
 
+static u32
+ikev2_mk_local_sa_id (u32 sai, u32 ci, u32 ti)
+{
+  return (0x80000000 | (ti << 24) | (sai << 12) | ci);
+}
+
+static u32
+ikev2_mk_remote_sa_id (u32 sai, u32 ci, u32 ti)
+{
+  return (0xc0000000 | (ti << 24) | (sai << 12) | ci);
+}
+
+typedef struct
+{
+  u32 salt_local;
+  u32 salt_remote;
+  u32 local_sa_id;
+  u32 remote_sa_id;
+  ipsec_sa_flags_t flags;
+  u32 local_spi;
+  u32 remote_spi;
+  ipsec_crypto_alg_t encr_type;
+  ipsec_integ_alg_t integ_type;
+  ip46_address_t local_ip;
+  ip46_address_t remote_ip;
+  ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey;
+} ikev2_add_ipsec_tunnel_args_t;
+
+static void
+ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
+{
+  u32 sw_if_index;
+  int rv;
+
+  rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0,
+                       &a->local_ip, &a->remote_ip, 0,
+                       IPIP_TUNNEL_FLAG_NONE, IP_DSCP_CS0, &sw_if_index);
+
+  rv |= ipsec_sa_add_and_lock (a->local_sa_id,
+                              a->local_spi,
+                              IPSEC_PROTOCOL_ESP, a->encr_type,
+                              &a->loc_ckey, a->integ_type, &a->loc_ikey,
+                              a->flags, 0, a->salt_local, &a->local_ip,
+                              &a->remote_ip, NULL);
+  rv |= ipsec_sa_add_and_lock (a->remote_sa_id, a->remote_spi,
+                              IPSEC_PROTOCOL_ESP, a->encr_type, &a->rem_ckey,
+                              a->integ_type, &a->rem_ikey,
+                              (a->flags | IPSEC_SA_FLAG_IS_INBOUND), 0,
+                              a->salt_remote, &a->remote_ip,
+                              &a->local_ip, NULL);
+
+  u32 *sas_in = NULL;
+  vec_add1 (sas_in, a->remote_sa_id);
+  rv |= ipsec_tun_protect_update (sw_if_index, a->local_sa_id, sas_in);
+}
 
 static int
-ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
-                              ikev2_child_sa_t * child)
+ikev2_create_tunnel_interface (vnet_main_t * vnm,
+                              u32 thread_index,
+                              ikev2_sa_t * sa,
+                              ikev2_child_sa_t * child, u32 sa_index,
+                              u32 child_index)
 {
-  ipsec_add_del_tunnel_args_t a;
+  ikev2_main_t *km = &ikev2_main;
+  ipsec_crypto_alg_t encr_type;
+  ipsec_integ_alg_t integ_type;
+  ikev2_profile_t *p = 0;
   ikev2_sa_transform_t *tr;
   ikev2_sa_proposal_t *proposals;
-  u8 encr_type = 0;
-  u8 integ_type = 0;
+  u8 is_aead = 0;
+  ikev2_add_ipsec_tunnel_args_t a;
+
+  clib_memset (&a, 0, sizeof (a));
 
   if (!child->r_proposals)
     {
@@ -1490,31 +1587,28 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
       return 1;
     }
 
-  clib_memset (&a, 0, sizeof (a));
-  a.is_add = 1;
   if (sa->is_initiator)
     {
-      a.local_ip.ip4.as_u32 = sa->iaddr.as_u32;
-      a.remote_ip.ip4.as_u32 = sa->raddr.as_u32;
+      ip46_address_set_ip4 (&a.local_ip, &sa->iaddr);
+      ip46_address_set_ip4 (&a.remote_ip, &sa->raddr);
       proposals = child->i_proposals;
       a.local_spi = child->r_proposals[0].spi;
       a.remote_spi = child->i_proposals[0].spi;
     }
   else
     {
-      a.local_ip.ip4.as_u32 = sa->raddr.as_u32;
-      a.remote_ip.ip4.as_u32 = sa->iaddr.as_u32;
+      ip46_address_set_ip4 (&a.local_ip, &sa->raddr);
+      ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr);
       proposals = child->r_proposals;
       a.local_spi = child->i_proposals[0].spi;
       a.remote_spi = child->r_proposals[0].spi;
     }
-  a.anti_replay = 1;
+
+  a.flags = IPSEC_SA_FLAG_USE_ANTI_REPLAY;
 
   tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ESN);
-  if (tr)
-    a.esn = tr->esn_type;
-  else
-    a.esn = 0;
+  if (tr && tr->esn_type)
+    a.flags |= IPSEC_SA_FLAG_USE_ESN;
 
   tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_ENCR);
   if (tr)
@@ -1538,6 +1632,27 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
              break;
            }
        }
+      else if (tr->encr_type == IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16
+              && tr->key_len)
+       {
+         switch (tr->key_len)
+           {
+           case 16:
+             encr_type = IPSEC_CRYPTO_ALG_AES_GCM_128;
+             break;
+           case 24:
+             encr_type = IPSEC_CRYPTO_ALG_AES_GCM_192;
+             break;
+           case 32:
+             encr_type = IPSEC_CRYPTO_ALG_AES_GCM_256;
+             break;
+           default:
+             ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
+             return 1;
+             break;
+           }
+         is_aead = 1;
+       }
       else
        {
          ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
@@ -1549,70 +1664,79 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
       ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
       return 1;
     }
+  a.encr_type = encr_type;
 
-  tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_INTEG);
-  if (tr)
+  if (!is_aead)
     {
-      switch (tr->integ_type)
-       {
-       case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_256_128:
-         integ_type = IPSEC_INTEG_ALG_SHA_256_128;
-         break;
-       case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_384_192:
-         integ_type = IPSEC_INTEG_ALG_SHA_384_192;
-         break;
-       case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_512_256:
-         integ_type = IPSEC_INTEG_ALG_SHA_512_256;
-         break;
-       case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA1_96:
-         integ_type = IPSEC_INTEG_ALG_SHA1_96;
-         break;
-       default:
+      tr = ikev2_sa_get_td_for_type (proposals, IKEV2_TRANSFORM_TYPE_INTEG);
+      if (tr)
+       {
+         switch (tr->integ_type)
+           {
+           case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_256_128:
+             integ_type = IPSEC_INTEG_ALG_SHA_256_128;
+             break;
+           case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_384_192:
+             integ_type = IPSEC_INTEG_ALG_SHA_384_192;
+             break;
+           case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA2_512_256:
+             integ_type = IPSEC_INTEG_ALG_SHA_512_256;
+             break;
+           case IKEV2_TRANSFORM_INTEG_TYPE_AUTH_HMAC_SHA1_96:
+             integ_type = IPSEC_INTEG_ALG_SHA1_96;
+             break;
+           default:
+             ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
+             return 1;
+           }
+       }
+      else
+       {
          ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
          return 1;
        }
     }
   else
     {
-      ikev2_set_state (sa, IKEV2_STATE_NO_PROPOSAL_CHOSEN);
-      return 1;
+      integ_type = IPSEC_INTEG_ALG_NONE;
     }
 
+  a.integ_type = integ_type;
   ikev2_calc_child_keys (sa, child);
 
-  u8 *loc_ckey, *rem_ckey, *loc_ikey, *rem_ikey;
   if (sa->is_initiator)
     {
-      loc_ikey = child->sk_ai;
-      rem_ikey = child->sk_ar;
-      loc_ckey = child->sk_ei;
-      rem_ckey = child->sk_er;
+      ipsec_mk_key (&a.loc_ikey, child->sk_ai, vec_len (child->sk_ai));
+      ipsec_mk_key (&a.rem_ikey, child->sk_ar, vec_len (child->sk_ar));
+      ipsec_mk_key (&a.loc_ckey, child->sk_ei, vec_len (child->sk_ei));
+      ipsec_mk_key (&a.rem_ckey, child->sk_er, vec_len (child->sk_er));
+      if (is_aead)
+       {
+         a.salt_remote = child->salt_er;
+         a.salt_local = child->salt_ei;
+       }
     }
   else
     {
-      loc_ikey = child->sk_ar;
-      rem_ikey = child->sk_ai;
-      loc_ckey = child->sk_er;
-      rem_ckey = child->sk_ei;
+      ipsec_mk_key (&a.loc_ikey, child->sk_ar, vec_len (child->sk_ar));
+      ipsec_mk_key (&a.rem_ikey, child->sk_ai, vec_len (child->sk_ai));
+      ipsec_mk_key (&a.loc_ckey, child->sk_er, vec_len (child->sk_er));
+      ipsec_mk_key (&a.rem_ckey, child->sk_ei, vec_len (child->sk_ei));
+      if (is_aead)
+       {
+         a.salt_remote = child->salt_ei;
+         a.salt_local = child->salt_er;
+       }
     }
 
-  a.integ_alg = integ_type;
-  a.local_integ_key_len = vec_len (loc_ikey);
-  clib_memcpy_fast (a.local_integ_key, loc_ikey, a.local_integ_key_len);
-  a.remote_integ_key_len = vec_len (rem_ikey);
-  clib_memcpy_fast (a.remote_integ_key, rem_ikey, a.remote_integ_key_len);
-
-  a.crypto_alg = encr_type;
-  a.local_crypto_key_len = vec_len (loc_ckey);
-  clib_memcpy_fast (a.local_crypto_key, loc_ckey, a.local_crypto_key_len);
-  a.remote_crypto_key_len = vec_len (rem_ckey);
-  clib_memcpy_fast (a.remote_crypto_key, rem_ckey, a.remote_crypto_key_len);
+  if (sa->is_profile_index_set)
+    p = pool_elt_at_index (km->profiles, sa->profile_index);
 
-  if (sa->profile && sa->profile->lifetime)
+  if (p && p->lifetime)
     {
-      child->time_to_expiration = vlib_time_now (vnm->vlib_main)
-       + sa->profile->lifetime;
-      if (sa->profile->lifetime_jitter)
+      child->time_to_expiration =
+       vlib_time_now (vnm->vlib_main) + p->lifetime;
+      if (p->lifetime_jitter)
        {
          // This is not much better than rand(3), which Coverity warns
          // is unsuitable for security applications; random_u32 is
@@ -1622,46 +1746,84 @@ ikev2_create_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
          u32 rnd = (u32) (vlib_time_now (vnm->vlib_main) * 1e6);
          rnd = random_u32 (&rnd);
 
-         child->time_to_expiration +=
-           1 + (rnd % sa->profile->lifetime_jitter);
+         child->time_to_expiration += 1 + (rnd % p->lifetime_jitter);
        }
     }
 
-  ipsec_add_del_tunnel_if (&a);
+  if (thread_index & 0xffffffc0)
+    clib_warning ("error: thread index exceeds max range 0x3f!");
+
+  if (child_index & 0xfffff000 || sa_index & 0xfffff000)
+    clib_warning ("error: sa/child index exceeds max range 0xfff!");
 
+  child->local_sa_id =
+    a.local_sa_id =
+    ikev2_mk_local_sa_id (sa_index, child_index, thread_index);
+  child->remote_sa_id =
+    a.remote_sa_id =
+    ikev2_mk_remote_sa_id (sa_index, child_index, thread_index);
+
+  vl_api_rpc_call_main_thread (ikev2_add_tunnel_from_main,
+                              (u8 *) & a, sizeof (a));
   return 0;
 }
 
+typedef struct
+{
+  ip46_address_t local_ip;
+  ip46_address_t remote_ip;
+  u32 remote_sa_id;
+  u32 local_sa_id;
+} ikev2_del_ipsec_tunnel_args_t;
+
+static void
+ikev2_del_tunnel_from_main (ikev2_del_ipsec_tunnel_args_t * a)
+{
+  /* *INDENT-OFF* */
+  ipip_tunnel_key_t key = {
+    .src = a->local_ip,
+    .dst = a->remote_ip,
+    .transport = IPIP_TRANSPORT_IP4,
+    .fib_index = 0,
+  };
+  ipip_tunnel_t *ipip;
+  /* *INDENT-ON* */
+
+  ipip = ipip_tunnel_db_find (&key);
+
+  if (ipip)
+    {
+      ipsec_tun_protect_del (ipip->sw_if_index);
+      ipsec_sa_unlock_id (a->remote_sa_id);
+      ipsec_sa_unlock_id (a->local_sa_id);
+      ipip_del_tunnel (ipip->sw_if_index);
+    }
+}
+
 static int
 ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
                               ikev2_child_sa_t * child)
 {
-  ipsec_add_del_tunnel_args_t a;
+  ikev2_del_ipsec_tunnel_args_t a;
+
+  clib_memset (&a, 0, sizeof (a));
 
   if (sa->is_initiator)
     {
-      if (!vec_len (child->i_proposals))
-       return 0;
-
-      a.is_add = 0;
-      a.local_ip.ip4.as_u32 = sa->iaddr.as_u32;
-      a.remote_ip.ip4.as_u32 = sa->raddr.as_u32;
-      a.local_spi = child->r_proposals[0].spi;
-      a.remote_spi = child->i_proposals[0].spi;
+      ip46_address_set_ip4 (&a.local_ip, &sa->iaddr);
+      ip46_address_set_ip4 (&a.remote_ip, &sa->raddr);
     }
   else
     {
-      if (!vec_len (child->r_proposals))
-       return 0;
-
-      a.is_add = 0;
-      a.local_ip.ip4.as_u32 = sa->raddr.as_u32;
-      a.remote_ip.ip4.as_u32 = sa->iaddr.as_u32;
-      a.local_spi = child->i_proposals[0].spi;
-      a.remote_spi = child->r_proposals[0].spi;
+      ip46_address_set_ip4 (&a.local_ip, &sa->raddr);
+      ip46_address_set_ip4 (&a.remote_ip, &sa->iaddr);
     }
 
-  ipsec_add_del_tunnel_if (&a);
+  a.remote_sa_id = child->remote_sa_id;
+  a.local_sa_id = child->local_sa_id;
+
+  vl_api_rpc_call_main_thread (ikev2_del_tunnel_from_main, (u8 *) & a,
+                              sizeof (a));
   return 0;
 }
 
@@ -2250,8 +2412,10 @@ ikev2_node_fn (vlib_main_t * vm,
                      ikev2_initial_contact_cleanup (sa0);
                      ikev2_sa_match_ts (sa0);
                      if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE)
-                       ikev2_create_tunnel_interface (km->vnet_main, sa0,
-                                                      &sa0->childs[0]);
+                       ikev2_create_tunnel_interface (km->vnet_main,
+                                                      thread_index, sa0,
+                                                      &sa0->childs[0],
+                                                      p[0], 0);
                    }
 
                  if (sa0->is_initiator)
@@ -2377,8 +2541,10 @@ ikev2_node_fn (vlib_main_t * vm,
                          child->i_proposals = sa0->rekey[0].i_proposal;
                          child->tsi = sa0->rekey[0].tsi;
                          child->tsr = sa0->rekey[0].tsr;
-                         ikev2_create_tunnel_interface (km->vnet_main, sa0,
-                                                        child);
+                         ikev2_create_tunnel_interface (km->vnet_main,
+                                                        thread_index, sa0,
+                                                        child, p[0],
+                                                        child - sa0->childs);
                        }
                      if (sa0->is_initiator)
                        {
@@ -2555,24 +2721,27 @@ ikev2_set_initiator_proposals (vlib_main_t * vm, ikev2_sa_t * sa,
     }
 
   /* DH */
-  error = 1;
-  vec_foreach (td, km->supported_transforms)
-  {
-    if (td->type == IKEV2_TRANSFORM_TYPE_DH && td->dh_type == ts->dh_type)
+  if (is_ike || ts->dh_type != IKEV2_TRANSFORM_DH_TYPE_NONE)
+    {
+      error = 1;
+      vec_foreach (td, km->supported_transforms)
       {
-       vec_add1 (proposal->transforms, *td);
-       if (is_ike)
+       if (td->type == IKEV2_TRANSFORM_TYPE_DH && td->dh_type == ts->dh_type)
          {
-           sa->dh_group = td->dh_type;
+           vec_add1 (proposal->transforms, *td);
+           if (is_ike)
+             {
+               sa->dh_group = td->dh_type;
+             }
+           error = 0;
+           break;
          }
-       error = 0;
-       break;
       }
-  }
-  if (error)
-    {
-      r = clib_error_return (0, "Unsupported algorithm");
-      return r;
+      if (error)
+       {
+         r = clib_error_return (0, "Unsupported algorithm");
+         return r;
+       }
     }
 
   if (!is_ike)
@@ -2973,7 +3142,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
     ikev2_sa_free_proposal_vector (&proposals);
 
     sa.is_initiator = 1;
-    sa.profile = p;
+    sa.profile_index = km->profiles - p;
+    sa.is_profile_index_set = 1;
     sa.state = IKEV2_STATE_SA_INIT;
     ikev2_generate_sa_init_data (&sa);
     ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data);
@@ -3036,6 +3206,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
     ike0->flags = IKEV2_HDR_FLAG_INITIATOR;
     ike0->exchange = IKEV2_EXCHANGE_SA_INIT;
     ike0->ispi = sa.ispi;
+    ike0->rspi = 0;
+    ike0->msgid = 0;
 
     /* store whole IKE payload - needed for PSK auth */
     vec_free (sa.last_sa_init_req_packet_data);
@@ -3049,12 +3221,6 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
     sa.i_auth.method = p->auth.method;
     sa.i_auth.hex = p->auth.hex;
     sa.i_auth.data = vec_dup (p->auth.data);
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-    clib_memcpy_fast (sa.i_auth.key, p->auth.key,
-                     EVP_PKEY_size (p->auth.key));
-#else
-    sa.i_auth.key = vec_dup (p->auth.key);
-#endif
     vec_add (sa.childs[0].tsi, &p->loc_ts, 1);
     vec_add (sa.childs[0].tsr, &p->rem_ts, 1);
 
@@ -3295,13 +3461,9 @@ clib_error_t *
 ikev2_init (vlib_main_t * vm)
 {
   ikev2_main_t *km = &ikev2_main;
-  clib_error_t *error;
   vlib_thread_main_t *tm = vlib_get_thread_main ();
   int thread_id;
 
-  if ((error = vlib_call_init_function (vm, ipsec_init)))
-    return error;
-
   clib_memset (km, 0, sizeof (ikev2_main_t));
   km->vnet_main = vnet_get_main ();
   km->vlib_main = vm;
@@ -3319,32 +3481,40 @@ ikev2_init (vlib_main_t * vm)
 
   km->sa_by_ispi = hash_create (0, sizeof (uword));
 
-
-  if ((error = vlib_call_init_function (vm, ikev2_cli_init)))
-    return error;
-
   udp_register_dst_port (vm, 500, ikev2_node.index, 1);
 
+  ikev2_cli_reference ();
+
   return 0;
 }
 
-VLIB_INIT_FUNCTION (ikev2_init);
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (ikev2_init) =
+{
+  .runs_after = VLIB_INITS("ipsec_init"),
+};
+/* *INDENT-ON* */
+
 
 static u8
 ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa)
 {
   ikev2_main_t *km = &ikev2_main;
+  ikev2_profile_t *p = 0;
   vlib_main_t *vm = km->vlib_main;
   f64 now = vlib_time_now (vm);
   u8 res = 0;
 
-  if (sa->is_initiator && sa->profile && csa->time_to_expiration
+  if (sa->is_profile_index_set)
+    p = pool_elt_at_index (km->profiles, sa->profile_index);
+
+  if (sa->is_initiator && p && csa->time_to_expiration
       && now > csa->time_to_expiration)
     {
       if (!csa->is_expired || csa->rekey_retries > 0)
        {
          ikev2_rekey_child_sa_internal (vm, sa, csa);
-         csa->time_to_expiration = now + sa->profile->handover;
+         csa->time_to_expiration = now + p->handover;
          csa->is_expired = 1;
          if (csa->rekey_retries == 0)
            {
@@ -3380,6 +3550,7 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
   vlib_main_t *vm = km->vlib_main;
   ikev2_main_per_thread_data_t *tkm;
   ikev2_sa_t *fsa = 0;
+  ikev2_profile_t *p = 0;
   ikev2_child_sa_t *fchild = 0;
   f64 now = vlib_time_now (vm);
   vlib_counter_t counts;
@@ -3404,10 +3575,12 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
   vlib_get_combined_counter (&ipsec_sa_counters,
                             ipsec_sa->stat_index, &counts);
 
-  if (fchild && fsa && fsa->profile && fsa->profile->lifetime_maxdata)
+  if (fsa && fsa->is_profile_index_set)
+    p = pool_elt_at_index (km->profiles, fsa->profile_index);
+
+  if (fchild && p && p->lifetime_maxdata)
     {
-      if (!fchild->is_expired
-         && counts.bytes > fsa->profile->lifetime_maxdata)
+      if (!fchild->is_expired && counts.bytes > p->lifetime_maxdata)
        {
          fchild->time_to_expiration = now;
        }
@@ -3474,7 +3647,7 @@ VLIB_REGISTER_NODE (ikev2_mngr_process_node, static) = {
 
 VLIB_PLUGIN_REGISTER () = {
     .version = VPP_BUILD_VER,
-    .description = "IKEv2",
+    .description = "Internet Key Exchange (IKEv2) Protocol",
 };
 /* *INDENT-ON* */