ikev2: do not require optional IDr on IKE AUTH
[vpp.git] / src / plugins / ikev2 / ikev2.c
index d5dd013..c184a46 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 */
@@ -637,8 +638,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 +662,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 +674,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 +695,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 +712,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 +731,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 +757,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 +827,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 +845,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 +877,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 +1049,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;
@@ -1339,7 +1368,6 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
   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 +1377,8 @@ 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;
 
   if (sa->is_initiator)
     src = ip_addr_v4 (&sa->raddr).as_u32;
@@ -1396,7 +1426,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 +1452,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)
@@ -1437,7 +1469,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)
        {
@@ -1463,10 +1495,10 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
            ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
          /* update Ni */
          vec_reset_length (sa->i_nonce);
-         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
+         vec_add (sa->i_nonce, nonce, nonce_len);
          /* generate new Nr */
-         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
-         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+         vec_validate (sa->r_nonce, nonce_len - 1);
+         RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
        }
       else
        {
@@ -1477,10 +1509,10 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
            ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
          /* update Ni */
          vec_reset_length (sa->i_nonce);
-         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
+         vec_add (sa->i_nonce, nonce, nonce_len);
          /* generate new Nr */
-         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
-         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+         vec_validate (sa->r_nonce, nonce_len - 1);
+         RAND_bytes ((u8 *) sa->r_nonce, nonce_len);
        }
       rekey->tsi = tsi;
       rekey->tsr = tsr;
@@ -1539,6 +1571,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 +1628,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 +1696,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 +1846,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);
@@ -2201,7 +2248,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;
@@ -2371,7 +2418,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 +2459,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);
@@ -2795,8 +2844,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 +2859,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 +2874,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 +2929,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)
 {
@@ -3720,55 +3769,21 @@ 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)
 {
-  ikev2_main_t *km = &ikev2_main;
-  udp_dst_port_info_t *pi;
-
-  uword *v = hash_get (km->udp_ports, port);
-  pi = udp_get_dst_port_info (&udp_main, port, UDP_IP4);
-
-  if (v)
-    {
-      /* IKE already uses this port, only increment reference counter */
-      ASSERT (pi);
-      v[0]++;
-    }
-  else
-    {
-      if (pi)
-       return VNET_API_ERROR_UDP_PORT_TAKEN;
-
-      udp_register_dst_port (km->vlib_main, port,
-                            ipsec4_tun_input_node.index, 1);
-      hash_set (km->udp_ports, port, 1);
-    }
+  ipsec_register_udp_port (port);
   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)
 {
-  ikev2_main_t *km = &ikev2_main;
-  uword *v;
-
   if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE)
     return;
 
-  v = hash_get (km->udp_ports, p->ipsec_over_udp_port);
-  if (!v)
-    return;
-
-  v[0]--;
-
-  if (v[0] == 0)
-    {
-      udp_unregister_dst_port (km->vlib_main, p->ipsec_over_udp_port, 1);
-      hash_unset (km->udp_ports, p->ipsec_over_udp_port);
-    }
-
+  ipsec_unregister_udp_port (p->ipsec_over_udp_port);
   p->ipsec_over_udp_port = IPSEC_UDP_PORT_NONE;
 }
 
@@ -3890,6 +3905,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)
 {
@@ -3899,6 +3920,8 @@ 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);
 }
@@ -4020,8 +4043,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;
@@ -4029,9 +4052,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);
@@ -4076,6 +4099,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,
@@ -4092,6 +4136,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);
 
@@ -4106,15 +4151,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;
@@ -4171,9 +4216,7 @@ ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, u8 * name, u16 port,
                                  u8 is_set)
 {
   ikev2_profile_t *p = ikev2_profile_index_by_name (name);
-  ikev2_main_t *km = &ikev2_main;
   vnet_api_error_t rv = 0;
-  uword *v;
 
   if (!p)
     return VNET_API_ERROR_INVALID_VALUE;
@@ -4187,10 +4230,6 @@ ikev2_set_profile_ipsec_udp_port (vlib_main_t * vm, u8 * name, u16 port,
     }
   else
     {
-      v = hash_get (km->udp_ports, port);
-      if (!v)
-       return VNET_API_ERROR_IKE_NO_PORT;
-
       if (p->ipsec_over_udp_port == IPSEC_UDP_PORT_NONE)
        return VNET_API_ERROR_INVALID_VALUE;
 
@@ -4266,6 +4305,37 @@ 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)
+    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 = km->dns_resolve_name (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)
 {
@@ -4276,7 +4346,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);
 
@@ -4286,15 +4356,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;
     }
@@ -4348,10 +4429,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);
@@ -4405,7 +4485,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);
@@ -4428,8 +4508,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 ",
@@ -4437,14 +4517,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;
 }
@@ -4761,7 +4833,6 @@ ikev2_init (vlib_main_t * vm)
 
   km->sa_by_ispi = hash_create (0, sizeof (uword));
   km->sw_if_indices = hash_create (0, 0);
-  km->udp_ports = hash_create (0, sizeof (uword));
 
   udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip4.index, 1);
   udp_register_dst_port (vm, IKEV2_PORT, ikev2_node_ip6.index, 0);
@@ -4773,15 +4844,19 @@ ikev2_init (vlib_main_t * vm)
                      "ikev2-ip4-natt");
   ikev2_cli_reference ();
 
+  km->dns_resolve_name =
+    vlib_get_plugin_symbol ("dns_plugin.so", "dns_resolve_name");
+  if (!km->dns_resolve_name)
+    ikev2_log_error ("cannot load symbols from dns plugin");
+
   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", "dns_init"),
 };
 /* *INDENT-ON* */
 
@@ -4969,15 +5044,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))
@@ -5014,7 +5118,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;
@@ -5027,7 +5131,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* */
 }
@@ -5087,8 +5191,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;
@@ -5115,7 +5219,6 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
                       vlib_frame_t * f)
 {
   ikev2_main_t *km = &ikev2_main;
-  ipsec_main_t *im = &ipsec_main;
   ikev2_profile_t *p;
   ikev2_child_sa_t *c;
   u32 *sai;
@@ -5159,7 +5262,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);
@@ -5171,7 +5275,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);
@@ -5189,12 +5293,13 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
       /* process ipsec sas */
       ipsec_sa_t *sa;
       /* *INDENT-OFF* */
-      pool_foreach (sa, im->sad)  {
-        ikev2_mngr_process_ipsec_sa(sa);
-      }
+      pool_foreach (sa, ipsec_sa_pool)
+       {
+         ikev2_mngr_process_ipsec_sa (sa);
+       }
       /* *INDENT-ON* */
 
-      ikev2_process_pending_sa_init (km);
+      ikev2_process_pending_sa_init (vm, km);
     }
   return 0;
 }