misc: fix issues reported by clang-15
[vpp.git] / src / plugins / ikev2 / ikev2.c
index 7c57f22..f0eaa7a 100644 (file)
@@ -26,6 +26,7 @@
 #include <vnet/ipip/ipip.h>
 #include <plugins/ikev2/ikev2.h>
 #include <plugins/ikev2/ikev2_priv.h>
+#include <plugins/dns/dns.h>
 #include <openssl/sha.h>
 #include <vnet/ipsec/ipsec_punt.h>
 #include <plugins/ikev2/ikev2.api_enum.h>
@@ -109,14 +110,14 @@ typedef enum
 
 typedef u32 ikev2_non_esp_marker;
 
-static_always_inline u16
-ikev2_get_port (ikev2_sa_t * sa)
+static u16
+ikev2_get_port (ikev2_sa_t *sa)
 {
   return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT;
 }
 
-static_always_inline int
-ikev2_insert_non_esp_marker (ike_header_t * ike, int len)
+static int
+ikev2_insert_non_esp_marker (ike_header_t *ike, int len)
 {
   memmove ((u8 *) ike + sizeof (ikev2_non_esp_marker), ike, len);
   clib_memset (ike, 0, sizeof (ikev2_non_esp_marker));
@@ -404,8 +405,8 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa)
       RAND_bytes ((u8 *) & sa->rspi, 8);
 
       /* generate nonce */
-      sa->r_nonce = vec_new (u8, IKEV2_NONCE_SIZE);
-      RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+      sa->r_nonce = vec_new (u8, vec_len (sa->i_nonce));
+      RAND_bytes ((u8 *) sa->r_nonce, vec_len (sa->i_nonce));
     }
 
   /* generate dh keys */
@@ -567,7 +568,7 @@ ikev2_calc_keys (ikev2_sa_t * sa)
 }
 
 static void
-ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
+ikev2_calc_child_keys (ikev2_sa_t *sa, ikev2_child_sa_t *child, u8 kex)
 {
   u8 *s = 0;
   u16 integ_key_len = 0;
@@ -586,6 +587,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
   else
     salt_len = sizeof (u32);
 
+  if (kex)
+    vec_append (s, sa->dh_shared_key);
   vec_append (s, sa->i_nonce);
   vec_append (s, sa->r_nonce);
   /* calculate PRFplus */
@@ -637,8 +640,8 @@ ikev2_calc_child_keys (ikev2_sa_t * sa, ikev2_child_sa_t * child)
   vec_free (keymat);
 }
 
-static_always_inline u8 *
-ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t * ia, u16 port)
+static u8 *
+ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t *ia, u16 port)
 {
   const u32 max_buf_size =
     sizeof (ispi) + sizeof (rspi) + sizeof (ip6_address_t) + sizeof (u16);
@@ -661,7 +664,10 @@ ikev2_parse_ke_payload (const void *p, u32 rlen, ikev2_sa_t * sa,
   u16 plen = clib_net_to_host_u16 (ke->length);
   ASSERT (plen >= sizeof (*ke) && plen <= rlen);
   if (sizeof (*ke) > rlen)
-    return 0;
+    {
+      ikev2_elog_error ("KE: packet too small");
+      return 0;
+    }
 
   sa->dh_group = clib_net_to_host_u16 (ke->dh_group);
   vec_reset_length (ke_data[0]);
@@ -670,13 +676,20 @@ ikev2_parse_ke_payload (const void *p, u32 rlen, ikev2_sa_t * sa,
 }
 
 static int
-ikev2_parse_nonce_payload (const void *p, u32 rlen, u8 * nonce)
+ikev2_parse_nonce_payload (const void *p, u32 rlen, const u8 **nonce)
 {
   const ike_payload_header_t *ikep = p;
   u16 plen = clib_net_to_host_u16 (ikep->length);
   ASSERT (plen >= sizeof (*ikep) && plen <= rlen);
-  clib_memcpy_fast (nonce, ikep->payload, plen - sizeof (*ikep));
-  return 1;
+  int len = plen - sizeof (*ikep);
+  ASSERT (len >= 16 && len <= 256);
+  if (PREDICT_FALSE (len < 16 || len > 256))
+    {
+      ikev2_elog_error ("NONCE: bad size");
+      return 0;
+    }
+  *nonce = ikep->payload;
+  return len;
 }
 
 static int
@@ -684,10 +697,16 @@ ikev2_check_payload_length (const ike_payload_header_t * ikep, int rlen,
                            u16 * plen)
 {
   if (sizeof (*ikep) > rlen)
-    return 0;
+    {
+      ikev2_elog_error ("payload: packet too small");
+      return 0;
+    }
   *plen = clib_net_to_host_u16 (ikep->length);
   if (*plen < sizeof (*ikep) || *plen > rlen)
-    return 0;
+    {
+      ikev2_elog_error ("payload: bad size");
+      return 0;
+    }
   return 1;
 }
 
@@ -695,7 +714,6 @@ static int
 ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike,
                           udp_header_t *udp, u32 len, u32 sw_if_index)
 {
-  u8 nonce[IKEV2_NONCE_SIZE];
   int p = 0;
   u8 payload = ike->nextpayload;
   ike_payload_header_t *ikep;
@@ -715,7 +733,10 @@ ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike,
   vec_add (sa->last_sa_init_req_packet_data, ike, len);
 
   if (len < sizeof (*ike))
-    return 0;
+    {
+      ikev2_elog_error ("IKE_INIT request too small");
+      return 0;
+    }
 
   len -= sizeof (*ike);
   while (p < len && payload != IKEV2_PAYLOAD_NONE)
@@ -738,9 +759,13 @@ ikev2_process_sa_init_req (vlib_main_t *vm, ikev2_sa_t *sa, ike_header_t *ike,
        }
       else if (payload == IKEV2_PAYLOAD_NONCE)
        {
+         const u8 *nonce;
+         int nonce_len;
          vec_reset_length (sa->i_nonce);
-         if (ikev2_parse_nonce_payload (ikep, current_length, nonce))
-           vec_add (sa->i_nonce, nonce, plen - sizeof (*ikep));
+         if ((nonce_len = ikev2_parse_nonce_payload (ikep, current_length,
+                                                     &nonce)) <= 0)
+           return 0;
+         vec_add (sa->i_nonce, nonce, nonce_len);
        }
       else if (payload == IKEV2_PAYLOAD_NOTIFY)
        {
@@ -804,7 +829,6 @@ ikev2_process_sa_init_resp (vlib_main_t * vm,
                            ikev2_sa_t * sa, ike_header_t * ike,
                            udp_header_t * udp, u32 len)
 {
-  u8 nonce[IKEV2_NONCE_SIZE];
   int p = 0;
   u8 payload = ike->nextpayload;
   ike_payload_header_t *ikep;
@@ -823,7 +847,10 @@ ikev2_process_sa_init_resp (vlib_main_t * vm,
   vec_add (sa->last_sa_init_res_packet_data, ike, len);
 
   if (sizeof (*ike) > len)
-    return;
+    {
+      ikev2_elog_error ("IKE_INIT response too small");
+      return;
+    }
 
   len -= sizeof (*ike);
   while (p < len && payload != IKEV2_PAYLOAD_NONE)
@@ -852,9 +879,13 @@ ikev2_process_sa_init_resp (vlib_main_t * vm,
        }
       else if (payload == IKEV2_PAYLOAD_NONCE)
        {
+         const u8 *nonce;
+         int nonce_len;
          vec_reset_length (sa->r_nonce);
-         if (ikev2_parse_nonce_payload (ikep, current_length, nonce))
-           vec_add (sa->r_nonce, nonce, plen - sizeof (*ikep));
+         if ((nonce_len = ikev2_parse_nonce_payload (ikep, current_length,
+                                                     &nonce)) <= 0)
+           return;
+         vec_add (sa->r_nonce, nonce, nonce_len);
        }
       else if (payload == IKEV2_PAYLOAD_NOTIFY)
        {
@@ -1020,8 +1051,8 @@ ikev2_decrypt_sk_payload (ikev2_sa_t * sa, ike_header_t * ike,
   return plaintext;
 }
 
-static_always_inline int
-ikev2_is_id_equal (ikev2_id_t * i1, ikev2_id_t * i2)
+static int
+ikev2_is_id_equal (const ikev2_id_t *i1, const ikev2_id_t *i2)
 {
   if (i1->type != i2->type)
     return 0;
@@ -1330,6 +1361,55 @@ ikev2_process_informational_req (vlib_main_t * vm,
   return 1;
 }
 
+static int
+ikev2_process_create_child_sa_rekey (ikev2_sa_t *sa, ikev2_sa_t *sar,
+                                    ikev2_rekey_t *rekey,
+                                    ikev2_sa_proposal_t *proposal,
+                                    ikev2_ts_t *tsi, ikev2_ts_t *tsr,
+                                    const u8 *nonce, int nonce_len)
+{
+  ikev2_sa_transform_t *tr;
+
+  rekey->i_proposal = proposal;
+  rekey->r_proposal = ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
+
+  if (sar->dh_group)
+    {
+      tr =
+       ikev2_sa_get_td_for_type (rekey->r_proposal, IKEV2_TRANSFORM_TYPE_DH);
+
+      if (!tr || tr->dh_type != sar->dh_group)
+       {
+         rekey->notify_type = IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD;
+         ikev2_sa_free_proposal_vector (&rekey->r_proposal);
+         return 0;
+       }
+
+      vec_free (sa->dh_shared_key);
+      vec_free (sa->dh_private_key);
+      vec_free (sa->i_dh_data);
+      vec_free (sa->r_dh_data);
+
+      sa->dh_group = sar->dh_group;
+      sa->i_dh_data = sar->i_dh_data;
+      sar->i_dh_data = 0;
+
+      ikev2_generate_dh (sa, tr);
+      rekey->kex = 1;
+    }
+
+  vec_reset_length (sa->i_nonce);
+  vec_add (sa->i_nonce, nonce, nonce_len);
+
+  vec_validate (sa->r_nonce, nonce_len - 1);
+  RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
+
+  rekey->tsi = tsi;
+  rekey->tsr = tsr;
+
+  return 1;
+}
+
 static int
 ikev2_process_create_child_sa_req (vlib_main_t * vm,
                                   ikev2_sa_t * sa, ike_header_t * ike,
@@ -1338,8 +1418,6 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
   int p = 0;
   u8 payload = ike->nextpayload;
   u8 *plaintext = 0;
-  u8 rekeying = 0;
-  u8 nonce[IKEV2_NONCE_SIZE];
   ikev2_rekey_t *rekey;
   ike_payload_header_t *ikep;
   ikev2_notify_t *n = 0;
@@ -1349,6 +1427,11 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
   ikev2_child_sa_t *child_sa;
   u32 dlen = 0, src;
   u16 plen;
+  const u8 *nonce = 0;
+  int nonce_len = 0;
+  ikev2_sa_t sar;
+
+  clib_memset (&sar, 0, sizeof (sar));
 
   if (sa->is_initiator)
     src = ip_addr_v4 (&sa->raddr).as_u32;
@@ -1378,13 +1461,23 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
        {
          proposal = ikev2_parse_sa_payload (ikep, current_length);
        }
+      else if (payload == IKEV2_PAYLOAD_KE)
+       {
+         if (!ikev2_parse_ke_payload (ikep, current_length, &sar,
+                                      &sar.i_dh_data))
+           goto cleanup_and_exit;
+       }
       else if (payload == IKEV2_PAYLOAD_NOTIFY)
        {
-         n = ikev2_parse_notify_payload (ikep, current_length);
-         if (n->msg_type == IKEV2_NOTIFY_MSG_REKEY_SA)
+         ikev2_notify_t *n0;
+         n0 = ikev2_parse_notify_payload (ikep, current_length);
+         if (n0->msg_type == IKEV2_NOTIFY_MSG_REKEY_SA)
            {
-             rekeying = 1;
+             vec_free (n);
+             n = n0;
            }
+         else
+           vec_free (n0);
        }
       else if (payload == IKEV2_PAYLOAD_DELETE)
        {
@@ -1396,7 +1489,9 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
        }
       else if (payload == IKEV2_PAYLOAD_NONCE)
        {
-         ikev2_parse_nonce_payload (ikep, current_length, nonce);
+         nonce_len = ikev2_parse_nonce_payload (ikep, current_length, &nonce);
+         if (nonce_len <= 0)
+           goto cleanup_and_exit;
        }
       else if (payload == IKEV2_PAYLOAD_TSI)
        {
@@ -1420,7 +1515,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       p += plen;
     }
 
-  if (!proposal || proposal->protocol_id != IKEV2_PROTOCOL_ESP)
+  if (!proposal || proposal->protocol_id != IKEV2_PROTOCOL_ESP || !nonce)
     goto cleanup_and_exit;
 
   if (sa->is_initiator)
@@ -1428,6 +1523,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       rekey = sa->rekey;
       if (vec_len (rekey) == 0)
        goto cleanup_and_exit;
+      rekey->notify_type = 0;
       rekey->protocol_id = proposal->protocol_id;
       rekey->i_proposal =
        ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
@@ -1437,7 +1533,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       rekey->tsr = tsr;
       /* update Nr */
       vec_reset_length (sa->r_nonce);
-      vec_add (sa->r_nonce, nonce, IKEV2_NONCE_SIZE);
+      vec_add (sa->r_nonce, nonce, nonce_len);
       child_sa = ikev2_sa_get_child (sa, rekey->ispi, IKEV2_PROTOCOL_ESP, 1);
       if (child_sa)
        {
@@ -1446,7 +1542,7 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
     }
   else
     {
-      if (rekeying)
+      if (n)
        {
          child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1);
          if (!child_sa)
@@ -1456,36 +1552,42 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
              goto cleanup_and_exit;
            }
          vec_add2 (sa->rekey, rekey, 1);
+         rekey->notify_type = 0;
+         rekey->kex = 0;
          rekey->protocol_id = n->protocol_id;
          rekey->spi = n->spi;
-         rekey->i_proposal = proposal;
-         rekey->r_proposal =
-           ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
-         /* update Ni */
-         vec_reset_length (sa->i_nonce);
-         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
-         /* generate new Nr */
-         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
-         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+         if (sa->old_remote_id_present)
+           {
+             rekey->notify_type = IKEV2_NOTIFY_MSG_TEMPORARY_FAILURE;
+             vec_free (proposal);
+             vec_free (tsr);
+             vec_free (tsi);
+           }
+         else if (!ikev2_process_create_child_sa_rekey (
+                    sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len))
+           {
+             vec_free (proposal);
+             vec_free (tsr);
+             vec_free (tsi);
+           }
        }
       else
        {
          /* create new child SA */
          vec_add2 (sa->new_child, rekey, 1);
-         rekey->i_proposal = proposal;
-         rekey->r_proposal =
-           ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
-         /* update Ni */
-         vec_reset_length (sa->i_nonce);
-         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
-         /* generate new Nr */
-         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
-         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+         rekey->notify_type = 0;
+         rekey->kex = 0;
+         if (!ikev2_process_create_child_sa_rekey (
+               sa, &sar, rekey, proposal, tsi, tsr, nonce, nonce_len))
+           {
+             vec_free (proposal);
+             vec_free (tsr);
+             vec_free (tsi);
+           }
        }
-      rekey->tsi = tsi;
-      rekey->tsr = tsr;
     }
   vec_free (n);
+  ikev2_sa_free_all_vec (&sar);
   return 1;
 
 cleanup_and_exit:
@@ -1493,6 +1595,7 @@ cleanup_and_exit:
   vec_free (proposal);
   vec_free (tsr);
   vec_free (tsi);
+  ikev2_sa_free_all_vec (&sar);
   return 0;
 }
 
@@ -1539,6 +1642,25 @@ ikev2_sa_generate_authmsg (ikev2_sa_t * sa, int is_responder)
   return authmsg;
 }
 
+static int
+ikev2_match_profile (const ikev2_profile_t *p, const ikev2_id_t *id_loc,
+                    const ikev2_id_t *id_rem, int is_initiator)
+{
+  /* on the initiator, IDi is always present and must match
+   * however on the responder, IDr (which is our local id) is optional */
+  if ((is_initiator || id_loc->type != 0) &&
+      !ikev2_is_id_equal (&p->loc_id, id_loc))
+    return 0;
+
+  /* on the initiator, we might not have configured a specific remote id
+   * however on the responder, the remote id should always be configured */
+  if ((!is_initiator || p->rem_id.type != 0) &&
+      !ikev2_is_id_equal (&p->rem_id, id_rem))
+    return 0;
+
+  return 1;
+}
+
 static int
 ikev2_ts_cmp (ikev2_ts_t * ts1, ikev2_ts_t * ts2)
 {
@@ -1577,9 +1699,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa)
         id_loc = &sa->r_id;
       }
 
-    /* check id */
-    if (!ikev2_is_id_equal (&p->rem_id, id_rem)
-          || !ikev2_is_id_equal (&p->loc_id, id_loc))
+    if (!ikev2_match_profile (p, id_loc, id_rem, sa->is_initiator))
       continue;
 
     sa->profile_index = p - km->profiles;
@@ -1647,9 +1767,7 @@ ikev2_select_profile (ikev2_main_t *km, ikev2_sa_t *sa,
 
   pool_foreach (p, km->profiles)
     {
-      /* check id */
-      if (!ikev2_is_id_equal (&p->rem_id, id_rem) ||
-         !ikev2_is_id_equal (&p->loc_id, id_loc))
+      if (!ikev2_match_profile (p, id_loc, id_rem, sa->is_initiator))
        continue;
 
       if (sa_auth->method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC)
@@ -1799,9 +1917,9 @@ ikev2_sa_auth_init (ikev2_sa_t * sa)
 
   if (sa->i_auth.method == IKEV2_AUTH_METHOD_SHARED_KEY_MIC)
     {
-      vec_free (sa->i_auth.data);
       key_pad = format (0, "%s", IKEV2_KEY_PAD);
       psk = ikev2_calc_prf (tr_prf, sa->i_auth.data, key_pad);
+      vec_free (sa->i_auth.data);
       sa->i_auth.data = ikev2_calc_prf (tr_prf, psk, authmsg);
       sa->i_auth.method = IKEV2_AUTH_METHOD_SHARED_KEY_MIC;
       vec_free (psk);
@@ -1863,8 +1981,8 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
     .t_mode = TUNNEL_MODE_P2P,
     .t_table_id = 0,
     .t_hop_limit = 255,
-    .t_src = a->local_ip,
-    .t_dst = a->remote_ip,
+    .t_src = a->remote_ip,
+    .t_dst = a->local_ip,
   };
   tunnel_t tun_out = {
     .t_flags = TUNNEL_FLAG_NONE,
@@ -1873,8 +1991,8 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
     .t_mode = TUNNEL_MODE_P2P,
     .t_table_id = 0,
     .t_hop_limit = 255,
-    .t_src = a->remote_ip,
-    .t_dst = a->local_ip,
+    .t_src = a->local_ip,
+    .t_dst = a->remote_ip,
   };
 
   if (~0 == a->sw_if_index)
@@ -1950,10 +2068,9 @@ err0:
 }
 
 static int
-ikev2_create_tunnel_interface (vlib_main_t * vm,
-                              ikev2_sa_t * sa,
-                              ikev2_child_sa_t * child, u32 sa_index,
-                              u32 child_index, u8 is_rekey)
+ikev2_create_tunnel_interface (vlib_main_t *vm, ikev2_sa_t *sa,
+                              ikev2_child_sa_t *child, u32 sa_index,
+                              u32 child_index, u8 is_rekey, u8 kex)
 {
   u32 thread_index = vlib_get_thread_index ();
   ikev2_main_t *km = &ikev2_main;
@@ -2096,7 +2213,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm,
     }
 
   a.integ_type = integ_type;
-  ikev2_calc_child_keys (sa, child);
+  ikev2_calc_child_keys (sa, child, kex);
 
   if (sa->is_initiator)
     {
@@ -2201,7 +2318,7 @@ typedef struct
   u32 sw_if_index;
 } ikev2_del_ipsec_tunnel_args_t;
 
-static_always_inline u32
+static u32
 ikev2_flip_alternate_sa_bit (u32 id)
 {
   u32 mask = 0x800;
@@ -2283,6 +2400,40 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
   return 0;
 }
 
+static void
+ikev2_add_invalid_ke_payload (ikev2_sa_t *sa, ikev2_payload_chain_t *chain)
+{
+  u8 *data = vec_new (u8, 2);
+  ikev2_sa_transform_t *tr_dh =
+    ikev2_sa_get_td_for_type (sa->r_proposals, IKEV2_TRANSFORM_TYPE_DH);
+  ASSERT (tr_dh && tr_dh->dh_type);
+  data[0] = (tr_dh->dh_type >> 8) & 0xff;
+  data[1] = (tr_dh->dh_type) & 0xff;
+  ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD, data);
+  vec_free (data);
+}
+
+static void
+ikev2_add_create_child_resp (ikev2_sa_t *sa, ikev2_rekey_t *rekey,
+                            ikev2_payload_chain_t *chain)
+{
+  if (rekey->notify_type)
+    {
+      if (rekey->notify_type == IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD)
+       ikev2_add_invalid_ke_payload (sa, chain);
+      else
+       ikev2_payload_add_notify (chain, rekey->notify_type, 0);
+      return;
+    }
+
+  ikev2_payload_add_sa (chain, rekey->r_proposal);
+  ikev2_payload_add_nonce (chain, sa->r_nonce);
+  if (rekey->kex)
+    ikev2_payload_add_ke (chain, sa->dh_group, sa->r_dh_data);
+  ikev2_payload_add_ts (chain, rekey->tsi, IKEV2_PAYLOAD_TSI);
+  ikev2_payload_add_ts (chain, rekey->tsr, IKEV2_PAYLOAD_TSR);
+}
+
 static u32
 ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
                        void *user, udp_header_t *udp, ikev2_stats_t *stats)
@@ -2313,20 +2464,7 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
        }
       else if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE)
        {
-         u8 *data = vec_new (u8, 2);
-         ikev2_sa_transform_t *tr_dh;
-         tr_dh =
-           ikev2_sa_get_td_for_type (sa->r_proposals,
-                                     IKEV2_TRANSFORM_TYPE_DH);
-         ASSERT (tr_dh && tr_dh->dh_type);
-
-         data[0] = (tr_dh->dh_type >> 8) & 0xff;
-         data[1] = (tr_dh->dh_type) & 0xff;
-
-         ikev2_payload_add_notify (chain,
-                                   IKEV2_NOTIFY_MSG_INVALID_KE_PAYLOAD,
-                                   data);
-         vec_free (data);
+         ikev2_add_invalid_ke_payload (sa, chain);
          ikev2_set_state (sa, IKEV2_STATE_NOTIFY_AND_DELETE);
        }
       else if (sa->state == IKEV2_STATE_NOTIFY_AND_DELETE)
@@ -2371,7 +2509,6 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
       if (sa->state == IKEV2_STATE_AUTHENTICATED)
        {
          ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR);
-         ikev2_payload_add_id (chain, &sa->i_id, IKEV2_PAYLOAD_IDI);
          ikev2_payload_add_auth (chain, &sa->r_auth);
          ikev2_payload_add_sa (chain, sa->childs[0].r_proposals);
          ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI);
@@ -2413,7 +2550,10 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
       else if (sa->state == IKEV2_STATE_SA_INIT)
        {
          ikev2_payload_add_id (chain, &sa->i_id, IKEV2_PAYLOAD_IDI);
-         ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR);
+         /* IDr is optional when sending INIT from the initiator */
+         ASSERT (sa->r_id.type != 0 || sa->is_initiator);
+         if (sa->r_id.type != 0)
+           ikev2_payload_add_id (chain, &sa->r_id, IKEV2_PAYLOAD_IDR);
          ikev2_payload_add_auth (chain, &sa->i_auth);
          ikev2_payload_add_sa (chain, sa->childs[0].i_proposals);
          ikev2_payload_add_ts (chain, sa->childs[0].tsi, IKEV2_PAYLOAD_TSI);
@@ -2503,20 +2643,12 @@ ikev2_generate_message (vlib_buffer_t *b, ikev2_sa_t *sa, ike_header_t *ike,
        }
       else if (vec_len (sa->rekey) > 0)
        {
-         ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
-         ikev2_payload_add_nonce (chain, sa->r_nonce);
-         ikev2_payload_add_ts (chain, sa->rekey[0].tsi, IKEV2_PAYLOAD_TSI);
-         ikev2_payload_add_ts (chain, sa->rekey[0].tsr, IKEV2_PAYLOAD_TSR);
+         ikev2_add_create_child_resp (sa, &sa->rekey[0], chain);
          vec_del1 (sa->rekey, 0);
        }
       else if (vec_len (sa->new_child) > 0)
        {
-         ikev2_payload_add_sa (chain, sa->new_child[0].r_proposal);
-         ikev2_payload_add_nonce (chain, sa->r_nonce);
-         ikev2_payload_add_ts (chain, sa->new_child[0].tsi,
-                               IKEV2_PAYLOAD_TSI);
-         ikev2_payload_add_ts (chain, sa->new_child[0].tsr,
-                               IKEV2_PAYLOAD_TSR);
+         ikev2_add_create_child_resp (sa, &sa->new_child[0], chain);
          vec_del1 (sa->new_child, 0);
        }
       else if (sa->unsupported_cp)
@@ -2795,8 +2927,8 @@ ikev2_del_sa_init (u64 ispi)
                               sizeof (ispi));
 }
 
-static_always_inline void
-ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih)
+static void
+ikev2_rewrite_v6_addrs (ikev2_sa_t *sa, ip6_header_t *ih)
 {
   if (sa->is_initiator)
     {
@@ -2810,8 +2942,8 @@ ikev2_rewrite_v6_addrs (ikev2_sa_t * sa, ip6_header_t * ih)
     }
 }
 
-static_always_inline void
-ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih)
+static void
+ikev2_rewrite_v4_addrs (ikev2_sa_t *sa, ip4_header_t *ih)
 {
   if (sa->is_initiator)
     {
@@ -2825,7 +2957,7 @@ ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih)
     }
 }
 
-static_always_inline void
+static void
 ikev2_set_ip_address (ikev2_sa_t *sa, const void *iaddr, const void *raddr,
                      const ip_address_family_t af)
 {
@@ -2880,7 +3012,7 @@ ikev2_update_stats (vlib_main_t *vm, u32 node_index, ikev2_stats_t *s)
                               s->n_sa_auth_req);
 }
 
-static_always_inline uword
+static uword
 ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                     vlib_frame_t *frame, u8 is_ip4, u8 natt)
 {
@@ -3139,9 +3271,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                  ikev2_initial_contact_cleanup (ptd, sa0);
                  ikev2_sa_match_ts (sa0);
                  if (sa0->state != IKEV2_STATE_TS_UNACCEPTABLE)
-                   ikev2_create_tunnel_interface (vm, sa0,
-                                                  &sa0->childs[0],
-                                                  p[0], 0, 0);
+                   ikev2_create_tunnel_interface (vm, sa0, &sa0->childs[0],
+                                                  p[0], 0, 0, 0);
                }
 
              if (sa0->is_initiator)
@@ -3266,11 +3397,12 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                  goto dispatch0;
                }
 
-             if (sa0->rekey)
+             if (vec_len (sa0->rekey) > 0)
                {
-                 if (sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE)
+                 if (!sa0->rekey[0].notify_type &&
+                     sa0->rekey[0].protocol_id != IKEV2_PROTOCOL_IKE)
                    {
-                     if (sa0->childs)
+                     if (vec_len (sa0->childs) > 0)
                        ikev2_sa_free_all_child_sa (&sa0->childs);
                      ikev2_child_sa_t *child;
                      vec_add2 (sa0->childs, child, 1);
@@ -3280,7 +3412,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                      child->tsi = sa0->rekey[0].tsi;
                      child->tsr = sa0->rekey[0].tsr;
                      ikev2_create_tunnel_interface (vm, sa0, child, p[0],
-                                                    child - sa0->childs, 1);
+                                                    child - sa0->childs, 1,
+                                                    sa0->rekey[0].kex);
                    }
                  if (ike_hdr_is_response (ike0))
                    {
@@ -3299,7 +3432,7 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                                                     1);
                    }
                }
-             else if (sa0->new_child)
+             else if (vec_len (sa0->new_child) > 0)
                {
                  ikev2_child_sa_t *c;
                  vec_add2 (sa0->childs, c, 1);
@@ -3309,7 +3442,8 @@ ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
                  c->tsi = sa0->new_child[0].tsi;
                  c->tsr = sa0->new_child[0].tsr;
                  ikev2_create_tunnel_interface (vm, sa0, c, p[0],
-                                                c - sa0->childs, 0);
+                                                c - sa0->childs, 0,
+                                                sa0->new_child[0].kex);
                  if (ike_hdr_is_request (ike0))
                    {
                      ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
@@ -3720,21 +3854,23 @@ ikev2_set_local_key (vlib_main_t * vm, u8 * file)
   return 0;
 }
 
-static_always_inline vnet_api_error_t
-ikev2_register_udp_port (ikev2_profile_t * p, u16 port)
+static vnet_api_error_t
+ikev2_register_udp_port (ikev2_profile_t *p, u16 port)
 {
-  ipsec_register_udp_port (port);
+  ipsec_register_udp_port (port, 0 /* is_ip4 */);
+  ipsec_register_udp_port (port, 1 /* is_ip4 */);
   p->ipsec_over_udp_port = port;
   return 0;
 }
 
-static_always_inline void
-ikev2_unregister_udp_port (ikev2_profile_t * p)
+static void
+ikev2_unregister_udp_port (ikev2_profile_t *p)
 {
   if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE)
     return;
 
-  ipsec_unregister_udp_port (p->ipsec_over_udp_port);
+  ipsec_unregister_udp_port (p->ipsec_over_udp_port, 0 /* is_ip4 */);
+  ipsec_unregister_udp_port (p->ipsec_over_udp_port, 1 /* is_ip4 */);
   p->ipsec_over_udp_port = IPSEC_UDP_PORT_NONE;
 }
 
@@ -3856,6 +3992,12 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p)
   vec_free (del_sai);
 }
 
+static void
+ikev2_profile_responder_free (ikev2_responder_t *r)
+{
+  vec_free (r->hostname);
+}
+
 static void
 ikev2_profile_free (ikev2_profile_t * p)
 {
@@ -3865,16 +4007,57 @@ ikev2_profile_free (ikev2_profile_t * p)
   if (p->auth.key)
     EVP_PKEY_free (p->auth.key);
 
+  ikev2_profile_responder_free (&p->responder);
+
   vec_free (p->loc_id.data);
   vec_free (p->rem_id.data);
 }
 
+static void
+ikev2_bind (vlib_main_t *vm, ikev2_main_t *km)
+{
+  if (0 == km->bind_refcount)
+    {
+      udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip4.index, 1);
+      udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0);
+      udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1);
+      udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0);
+
+      vlib_punt_register (km->punt_hdl,
+                         ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0],
+                         "ikev2-ip4-natt");
+    }
+
+  km->bind_refcount++;
+}
+
+static void
+ikev2_unbind (vlib_main_t *vm, ikev2_main_t *km)
+{
+  km->bind_refcount--;
+  if (0 == km->bind_refcount)
+    {
+      vlib_punt_unregister (km->punt_hdl,
+                           ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0],
+                           "ikev2-ip4-natt");
+
+      udp_unregister_dst_port (vm, IKEV2_PORT_NATT, 0);
+      udp_unregister_dst_port (vm, IKEV2_PORT_NATT, 1);
+      udp_unregister_dst_port (vm, IKEV2_PORT, 0);
+      udp_unregister_dst_port (vm, IKEV2_PORT, 1);
+    }
+}
+
+static void ikev2_lazy_init (ikev2_main_t *km);
+
 clib_error_t *
 ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
 {
   ikev2_main_t *km = &ikev2_main;
   ikev2_profile_t *p;
 
+  ikev2_lazy_init (km);
+
   if (is_add)
     {
       if (ikev2_profile_index_by_name (name))
@@ -3888,6 +4071,8 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
       p->tun_itf = ~0;
       uword index = p - km->profiles;
       mhash_set_mem (&km->profile_index_by_name, name, &index, 0);
+
+      ikev2_bind (vm, km);
     }
   else
     {
@@ -3895,6 +4080,8 @@ ikev2_add_del_profile (vlib_main_t * vm, u8 * name, int is_add)
       if (!p)
        return clib_error_return (0, "policy %v does not exists", name);
 
+      ikev2_unbind (vm, km);
+
       ikev2_unregister_udp_port (p);
       ikev2_cleanup_profile_sessions (km, p);
 
@@ -3986,8 +4173,8 @@ ikev2_set_profile_id (vlib_main_t * vm, u8 * name, u8 id_type, u8 * data,
   return 0;
 }
 
-static_always_inline void
-ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr)
+static void
+ikev2_set_ts_type (ikev2_ts_t *ts, const ip_address_t *addr)
 {
   if (ip_addr_version (addr) == AF_IP4)
     ts->ts_type = TS_IPV4_ADDR_RANGE;
@@ -3995,9 +4182,9 @@ ikev2_set_ts_type (ikev2_ts_t * ts, const ip_address_t * addr)
     ts->ts_type = TS_IPV6_ADDR_RANGE;
 }
 
-static_always_inline void
-ikev2_set_ts_addrs (ikev2_ts_t * ts, const ip_address_t * start,
-                   const ip_address_t * end)
+static void
+ikev2_set_ts_addrs (ikev2_ts_t *ts, const ip_address_t *start,
+                   const ip_address_t *end)
 {
   ip_address_copy (&ts->start_addr, start);
   ip_address_copy (&ts->end_addr, end);
@@ -4042,6 +4229,27 @@ ikev2_set_profile_ts (vlib_main_t * vm, u8 * name, u8 protocol_id,
   return 0;
 }
 
+clib_error_t *
+ikev2_set_profile_responder_hostname (vlib_main_t *vm, u8 *name, u8 *hostname,
+                                     u32 sw_if_index)
+{
+  ikev2_profile_t *p;
+  clib_error_t *r;
+
+  p = ikev2_profile_index_by_name (name);
+
+  if (!p)
+    {
+      r = clib_error_return (0, "unknown profile %v", name);
+      return r;
+    }
+
+  p->responder.is_resolved = 0;
+  p->responder.sw_if_index = sw_if_index;
+  p->responder.hostname = vec_dup (hostname);
+
+  return 0;
+}
 
 clib_error_t *
 ikev2_set_profile_responder (vlib_main_t * vm, u8 * name,
@@ -4058,6 +4266,7 @@ ikev2_set_profile_responder (vlib_main_t * vm, u8 * name,
       return r;
     }
 
+  p->responder.is_resolved = 1;
   p->responder.sw_if_index = sw_if_index;
   ip_address_copy (&p->responder.addr, &addr);
 
@@ -4072,15 +4281,15 @@ ikev2_set_profile_ike_transforms (vlib_main_t * vm, u8 * name,
                                  u32 crypto_key_size)
 {
   ikev2_profile_t *p;
-  clib_error_t *r;
 
   p = ikev2_profile_index_by_name (name);
-
   if (!p)
-    {
-      r = clib_error_return (0, "unknown profile %v", name);
-      return r;
-    }
+    return clib_error_return (0, "unknown profile %v", name);
+
+  if ((IKEV2_TRANSFORM_INTEG_TYPE_NONE != integ_alg) +
+       (IKEV2_TRANSFORM_ENCR_TYPE_AES_GCM_16 == crypto_alg) !=
+      1)
+    return clib_error_return (0, "invalid cipher + integrity algorithm");
 
   p->ike_ts.crypto_alg = crypto_alg;
   p->ike_ts.integ_alg = integ_alg;
@@ -4226,6 +4435,38 @@ ikev2_get_if_address (u32 sw_if_index, ip_address_family_t af,
   return 0;
 }
 
+static clib_error_t *
+ikev2_resolve_responder_hostname (vlib_main_t *vm, ikev2_responder_t *r)
+{
+  ikev2_main_t *km = &ikev2_main;
+  dns_cache_entry_t *ep = 0;
+  dns_pending_request_t _t0, *t0 = &_t0;
+  dns_resolve_name_t _rn, *rn = &_rn;
+  u8 *name;
+  int rv;
+
+  if (!km->dns_resolve_name_ptr)
+    return clib_error_return (0, "cannot load symbols from dns plugin");
+
+  t0->request_type = DNS_API_PENDING_NAME_TO_IP;
+  /* VPP main curse: IKEv2 uses only non-NULL terminated vectors internally
+   * whereas DNS resolver expects a NULL-terminated C-string */
+  name = vec_dup (r->hostname);
+  vec_terminate_c_string (name);
+  rv = ((__typeof__ (dns_resolve_name) *) km->dns_resolve_name_ptr) (name, &ep,
+                                                                    t0, rn);
+  vec_free (name);
+  if (rv < 0)
+    return clib_error_return (0, "dns lookup failure");
+
+  if (ep == 0)
+    return 0;
+
+  ip_address_copy (&r->addr, &rn->address);
+  r->is_resolved = 1;
+  return 0;
+}
+
 clib_error_t *
 ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
 {
@@ -4236,7 +4477,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   ike_header_t *ike0;
   u32 bi0 = 0;
   int len = sizeof (ike_header_t), valid_ip = 0;
-  ip_address_t if_ip = ip_address_initializer;
+  ip_address_t src_if_ip = ip_address_initializer;
 
   p = ikev2_profile_index_by_name (name);
 
@@ -4246,15 +4487,26 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
       return r;
     }
 
-  if (p->responder.sw_if_index == ~0
-      || ip_address_is_zero (&p->responder.addr))
+  if (p->responder.sw_if_index == ~0 ||
+      (ip_address_is_zero (&p->responder.addr) &&
+       vec_len (p->responder.hostname) == 0))
     {
       r = clib_error_return (0, "responder not set for profile %v", name);
       return r;
     }
 
-  if (ikev2_get_if_address (p->responder.sw_if_index,
-                           ip_addr_version (&p->responder.addr), &if_ip))
+  if (!p->responder.is_resolved)
+    {
+      /* try to resolve using dns plugin
+       * success does not mean we have resolved the name */
+      r = ikev2_resolve_responder_hostname (vm, &p->responder);
+      if (r)
+       return r;
+    }
+
+  if (p->responder.is_resolved &&
+      ikev2_get_if_address (p->responder.sw_if_index,
+                           ip_addr_version (&p->responder.addr), &src_if_ip))
     {
       valid_ip = 1;
     }
@@ -4308,10 +4560,9 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
              sizeof (sa.childs[0].i_proposals[0].spi));
 
   /* Add NAT detection notification messages (mandatory) */
-  u8 *nat_detection_sha1 =
-    ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa.ispi),
-                           clib_host_to_net_u64 (sa.rspi),
-                           &if_ip, clib_host_to_net_u16 (IKEV2_PORT));
+  u8 *nat_detection_sha1 = ikev2_compute_nat_sha1 (
+    clib_host_to_net_u64 (sa.ispi), clib_host_to_net_u64 (sa.rspi), &src_if_ip,
+    clib_host_to_net_u16 (IKEV2_PORT));
 
   ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP,
                            nat_detection_sha1);
@@ -4365,7 +4616,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   vec_add (sa.last_sa_init_req_packet_data, ike0, len);
 
   /* add data to the SA then add it to the pool */
-  ip_address_copy (&sa.iaddr, &if_ip);
+  ip_address_copy (&sa.iaddr, &src_if_ip);
   ip_address_copy (&sa.raddr, &p->responder.addr);
   sa.i_id.type = p->loc_id.type;
   sa.i_id.data = vec_dup (p->loc_id.data);
@@ -4388,8 +4639,8 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
 
   if (valid_ip)
     {
-      ikev2_send_ike (vm, &if_ip, &p->responder.addr, bi0, len,
-                     IKEV2_PORT, sa.dst_port, sa.sw_if_index);
+      ikev2_send_ike (vm, &src_if_ip, &p->responder.addr, bi0, len, IKEV2_PORT,
+                     sa.dst_port, sa.sw_if_index);
 
       ikev2_elog_exchange
        ("ispi %lx rspi %lx IKEV2_EXCHANGE_SA_INIT sent to ",
@@ -4397,14 +4648,6 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
         ip_addr_v4 (&p->responder.addr).as_u32,
         ip_addr_version (&p->responder.addr) == AF_IP4);
     }
-  else
-    {
-      r =
-       clib_error_return (0, "interface  %U does not have any IP address!",
-                          format_vnet_sw_if_index_name, vnet_get_main (),
-                          p->responder.sw_if_index);
-      return r;
-    }
 
   return 0;
 }
@@ -4557,6 +4800,7 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   ikev2_rekey_t *rekey;
   vec_reset_length (sa->rekey);
   vec_add2 (sa->rekey, rekey, 1);
+  rekey->kex = 0;
   ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals);
 
   /*need new ispi */
@@ -4686,61 +4930,24 @@ clib_error_t *
 ikev2_init (vlib_main_t * vm)
 {
   ikev2_main_t *km = &ikev2_main;
-  vlib_thread_main_t *tm = vlib_get_thread_main ();
-  int thread_id;
 
   clib_memset (km, 0, sizeof (ikev2_main_t));
+
+  km->log_level = IKEV2_LOG_ERROR;
+  km->log_class = vlib_log_register_class ("ikev2", 0);
+
   km->vnet_main = vnet_get_main ();
   km->vlib_main = vm;
 
   km->liveness_period = IKEV2_LIVENESS_PERIOD_CHECK;
   km->liveness_max_retries = IKEV2_LIVENESS_RETRIES;
-  ikev2_crypto_init (km);
 
-  mhash_init_vec_string (&km->profile_index_by_name, sizeof (uword));
-
-  vec_validate_aligned (km->per_thread_data, tm->n_vlib_mains - 1,
-                       CLIB_CACHE_LINE_BYTES);
-  for (thread_id = 0; thread_id < tm->n_vlib_mains; thread_id++)
-    {
-      ikev2_main_per_thread_data_t *ptd =
-       vec_elt_at_index (km->per_thread_data, thread_id);
-
-      ptd->sa_by_rspi = hash_create (0, sizeof (uword));
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-      ptd->evp_ctx = EVP_CIPHER_CTX_new ();
-      ptd->hmac_ctx = HMAC_CTX_new ();
-#else
-      EVP_CIPHER_CTX_init (&ptd->_evp_ctx);
-      ptd->evp_ctx = &ptd->_evp_ctx;
-      HMAC_CTX_init (&(ptd->_hmac_ctx));
-      ptd->hmac_ctx = &ptd->_hmac_ctx;
-#endif
-    }
-
-  km->sa_by_ispi = hash_create (0, sizeof (uword));
-  km->sw_if_indices = hash_create (0, 0);
-
-  udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip4.index, 1);
-  udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0);
-  udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip4.index, 1);
-  udp_register_dst_port (vm, IKEV2_PORT_NATT, ikev2_node_ip6.index, 0);
-
-  vlib_punt_hdl_t punt_hdl = vlib_punt_client_register ("ikev2-ip4-natt");
-  vlib_punt_register (punt_hdl, ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0],
-                     "ikev2-ip4-natt");
-  ikev2_cli_reference ();
-
-  km->log_level = IKEV2_LOG_ERROR;
-  km->log_class = vlib_log_register_class ("ikev2", 0);
   return 0;
 }
 
 /* *INDENT-OFF* */
-VLIB_INIT_FUNCTION (ikev2_init) =
-{
-  .runs_after = VLIB_INITS("ipsec_init", "ipsec_punt_init"),
+VLIB_INIT_FUNCTION (ikev2_init) = {
+  .runs_after = VLIB_INITS ("ipsec_init", "ipsec_punt_init"),
 };
 /* *INDENT-ON* */
 
@@ -4928,15 +5135,44 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
 }
 
 static void
-ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
+ikev2_process_pending_sa_init_one (vlib_main_t *vm, ikev2_main_t *km,
+                                  ikev2_sa_t *sa)
 {
   ikev2_profile_t *p;
   u32 bi0;
   u8 *nat_sha, *np;
+  p = pool_elt_at_index (km->profiles, sa->profile_index);
+
+  if (!p->responder.is_resolved)
+    {
+      clib_error_t *r = ikev2_resolve_responder_hostname (vm, &p->responder);
+      if (r)
+       {
+         clib_error_free (r);
+         return;
+       }
+
+      if (!p->responder.is_resolved)
+       return;
+
+      ip_address_copy (&sa->raddr, &p->responder.addr);
+
+      /* update nat detection destination hash */
+      np = ikev2_find_ike_notify_payload (
+       (ike_header_t *) sa->last_sa_init_req_packet_data,
+       IKEV2_NOTIFY_MSG_NAT_DETECTION_DESTINATION_IP);
+      if (np)
+       {
+         nat_sha = ikev2_compute_nat_sha1 (
+           clib_host_to_net_u64 (sa->ispi), clib_host_to_net_u64 (sa->rspi),
+           &sa->raddr, clib_host_to_net_u16 (sa->dst_port));
+         clib_memcpy_fast (np, nat_sha, vec_len (nat_sha));
+         vec_free (nat_sha);
+       }
+    }
 
   if (ip_address_is_zero (&sa->iaddr))
     {
-      p = pool_elt_at_index (km->profiles, sa->profile_index);
       if (!ikev2_get_if_address (p->responder.sw_if_index,
                                 ip_addr_version (&p->responder.addr),
                                 &sa->iaddr))
@@ -4973,7 +5209,7 @@ ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
 }
 
 static void
-ikev2_process_pending_sa_init (ikev2_main_t * km)
+ikev2_process_pending_sa_init (vlib_main_t *vm, ikev2_main_t *km)
 {
   u32 sai;
   u64 ispi;
@@ -4986,7 +5222,7 @@ ikev2_process_pending_sa_init (ikev2_main_t * km)
     if (sa->init_response_received)
       continue;
 
-    ikev2_process_pending_sa_init_one (km, sa);
+    ikev2_process_pending_sa_init_one (vm, km, sa);
   }));
   /* *INDENT-ON* */
 }
@@ -5046,8 +5282,8 @@ ikev2_disable_dpd (void)
   km->dpd_disabled = 1;
 }
 
-static_always_inline int
-ikev2_mngr_process_responder_sas (ikev2_sa_t * sa)
+static int
+ikev2_mngr_process_responder_sas (ikev2_sa_t *sa)
 {
   ikev2_main_t *km = &ikev2_main;
   vlib_main_t *vm = km->vlib_main;
@@ -5078,6 +5314,9 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
   ikev2_child_sa_t *c;
   u32 *sai;
 
+  /* lazy init will wake it up */
+  vlib_process_wait_for_event (vm);
+
   while (1)
     {
       vlib_process_wait_for_event_or_clock (vm, 2);
@@ -5117,7 +5356,8 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
        vec_foreach (sai, to_be_deleted)
        {
          sa = pool_elt_at_index (tkm->sas, sai[0]);
-         u8 reinitiate = (sa->is_initiator && sa->profile_index != ~0);
+         const u32 profile_index = sa->profile_index;
+         const int reinitiate = (sa->is_initiator && profile_index != ~0);
          vec_foreach (c, sa->childs)
          {
            ikev2_delete_tunnel_interface (km->vnet_main, sa, c);
@@ -5129,7 +5369,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
 
          if (reinitiate)
            {
-             p = pool_elt_at_index (km->profiles, sa->profile_index);
+             p = pool_elt_at_index (km->profiles, profile_index);
              if (p)
                {
                  clib_error_t *e = ikev2_initiate_sa_init (vm, p->name);
@@ -5153,7 +5393,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
        }
       /* *INDENT-ON* */
 
-      ikev2_process_pending_sa_init (km);
+      ikev2_process_pending_sa_init (vm, km);
     }
   return 0;
 }
@@ -5166,6 +5406,56 @@ VLIB_REGISTER_NODE (ikev2_mngr_process_node, static) = {
     "ikev2-manager-process",
 };
 
+static void
+ikev2_lazy_init (ikev2_main_t *km)
+{
+  vlib_thread_main_t *tm = vlib_get_thread_main ();
+  int thread_id;
+
+  if (km->lazy_init_done)
+    return;
+
+  ikev2_crypto_init (km);
+
+  mhash_init_vec_string (&km->profile_index_by_name, sizeof (uword));
+
+  vec_validate_aligned (km->per_thread_data, tm->n_vlib_mains - 1,
+                       CLIB_CACHE_LINE_BYTES);
+  for (thread_id = 0; thread_id < tm->n_vlib_mains; thread_id++)
+    {
+      ikev2_main_per_thread_data_t *ptd =
+       vec_elt_at_index (km->per_thread_data, thread_id);
+
+      ptd->sa_by_rspi = hash_create (0, sizeof (uword));
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+      ptd->evp_ctx = EVP_CIPHER_CTX_new ();
+      ptd->hmac_ctx = HMAC_CTX_new ();
+#else
+      EVP_CIPHER_CTX_init (&ptd->_evp_ctx);
+      ptd->evp_ctx = &ptd->_evp_ctx;
+      HMAC_CTX_init (&(ptd->_hmac_ctx));
+      ptd->hmac_ctx = &ptd->_hmac_ctx;
+#endif
+    }
+
+  km->sa_by_ispi = hash_create (0, sizeof (uword));
+  km->sw_if_indices = hash_create (0, 0);
+
+  km->punt_hdl = vlib_punt_client_register ("ikev2");
+
+  km->dns_resolve_name_ptr =
+    vlib_get_plugin_symbol ("dns_plugin.so", "dns_resolve_name");
+  if (!km->dns_resolve_name_ptr)
+    ikev2_log_error ("cannot load symbols from dns plugin");
+
+  /* wake up ikev2 process */
+  vlib_process_signal_event (vlib_get_first_main (),
+                            ikev2_mngr_process_node.index, 0, 0);
+
+  km->lazy_init_done = 1;
+}
+
 VLIB_PLUGIN_REGISTER () = {
     .version = VPP_BUILD_VER,
     .description = "Internet Key Exchange (IKEv2) Protocol",