ikev2: better handling when no IKE DH configured
[vpp.git] / src / plugins / ikev2 / ikev2.c
index 4b31860..336f896 100644 (file)
@@ -61,6 +61,37 @@ format_ikev2_trace (u8 * s, va_list * args)
   return s;
 }
 
+#define IKEV2_GENERATE_SA_INIT_OK_str ""
+#define IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR \
+  "no DH group configured for IKE proposals!"
+#define IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR \
+  "DH group not supported!"
+
+typedef enum
+{
+  IKEV2_GENERATE_SA_INIT_OK,
+  IKEV2_GENERATE_SA_INIT_ERR_NO_DH,
+  IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH,
+} ikev2_generate_sa_error_t;
+
+static u8 *
+format_ikev2_gen_sa_error (u8 * s, va_list * args)
+{
+  ikev2_generate_sa_error_t e = va_arg (*args, ikev2_generate_sa_error_t);
+  switch (e)
+    {
+    case IKEV2_GENERATE_SA_INIT_OK:
+      break;
+    case IKEV2_GENERATE_SA_INIT_ERR_NO_DH:
+      s = format (s, IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR);
+      break;
+    case IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH:
+      s = format (s, IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR);
+      break;
+    }
+  return s;
+}
+
 #define foreach_ikev2_error \
 _(PROCESSED, "IKEv2 packets processed") \
 _(IKE_SA_INIT_RETRANSMIT, "IKE_SA_INIT retransmit ") \
@@ -105,7 +136,7 @@ typedef u32 ikev2_non_esp_marker;
 static_always_inline u16
 ikev2_get_port (ikev2_sa_t * sa)
 {
-  return sa->natt ? IKEV2_PORT_NATT : IKEV2_PORT;
+  return ikev2_natt_active (sa) ? IKEV2_PORT_NATT : IKEV2_PORT;
 }
 
 static_always_inline int
@@ -357,16 +388,14 @@ ikev2_delete_sa (ikev2_main_per_thread_data_t * ptd, ikev2_sa_t * sa)
     }
 }
 
-static void
+static ikev2_generate_sa_error_t
 ikev2_generate_sa_init_data (ikev2_sa_t * sa)
 {
   ikev2_sa_transform_t *t = 0, *t2;
   ikev2_main_t *km = &ikev2_main;
 
   if (sa->dh_group == IKEV2_TRANSFORM_DH_TYPE_NONE)
-    {
-      return;
-    }
+    return IKEV2_GENERATE_SA_INIT_ERR_NO_DH;
 
   /* check if received DH group is on our list of supported groups */
   vec_foreach (t2, km->supported_transforms)
@@ -381,7 +410,7 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa)
   if (!t)
     {
       sa->dh_group = IKEV2_TRANSFORM_DH_TYPE_NONE;
-      return;
+      return IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH;
     }
 
   if (sa->is_initiator)
@@ -406,6 +435,7 @@ ikev2_generate_sa_init_data (ikev2_sa_t * sa)
   /* generate dh keys */
   ikev2_generate_dh (sa, t);
 
+  return IKEV2_GENERATE_SA_INIT_OK;
 }
 
 static void
@@ -427,6 +457,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   sa->profile_index = sai->profile_index;
   sa->tun_itf = sai->tun_itf;
   sa->is_tun_itf_set = sai->is_tun_itf_set;
+  sa->natt_state = sai->natt_state;
   sa->i_id.data = _(sai->i_id.data);
   sa->r_id.data = _(sai->r_id.data);
   sa->i_auth.method = sai->i_auth.method;
@@ -434,6 +465,7 @@ ikev2_complete_sa_data (ikev2_sa_t * sa, ikev2_sa_t * sai)
   sa->i_auth.data = _(sai->i_auth.data);
   sa->i_auth.key = _(sai->i_auth.key);
   sa->last_sa_init_req_packet_data = _(sai->last_sa_init_req_packet_data);
+  sa->last_init_msg_id = sai->last_init_msg_id;
   sa->childs = _(sai->childs);
   sa->udp_encap = sai->udp_encap;
   sa->ipsec_over_udp_port = sai->ipsec_over_udp_port;
@@ -640,7 +672,7 @@ ikev2_compute_nat_sha1 (u64 ispi, u64 rspi, ip_address_t * ia, u16 port)
   clib_memcpy_fast (&buf[8], &rspi, sizeof (rspi));
   clib_memcpy_fast (&buf[8 + 8], ip_addr_bytes (ia), ip_address_size (ia));
   clib_memcpy_fast (&buf[8 + 8 + ip_address_size (ia)], &port, sizeof (port));
-  SHA1 (buf, sizeof (buf), res);
+  SHA1 (buf, 2 * sizeof (ispi) + sizeof (port) + ip_address_size (ia), res);
   return res;
 }
 
@@ -743,7 +775,8 @@ ikev2_process_sa_init_req (vlib_main_t * vm,
                                                    udp->src_port);
              if (clib_memcmp (src_sha, n->data, vec_len (src_sha)))
                {
-                 sa->natt = 1;
+                 if (sa->natt_state == IKEV2_NATT_ENABLED)
+                   sa->natt_state = IKEV2_NATT_ACTIVE;
                  ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator"
                                   " behind NAT", sa->ispi);
                }
@@ -756,7 +789,8 @@ ikev2_process_sa_init_req (vlib_main_t * vm,
                                                    udp->dst_port);
              if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha)))
                {
-                 sa->natt = 1;
+                 if (sa->natt_state == IKEV2_NATT_ENABLED)
+                   sa->natt_state = IKEV2_NATT_ACTIVE;
                  ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx responder"
                                   " (self) behind NAT", sa->ispi);
                }
@@ -869,7 +903,8 @@ ikev2_process_sa_init_resp (vlib_main_t * vm,
                                                    udp->dst_port);
              if (clib_memcmp (dst_sha, n->data, vec_len (dst_sha)))
                {
-                 sa->natt = 1;
+                 if (sa->natt_state == IKEV2_NATT_ENABLED)
+                   sa->natt_state = IKEV2_NATT_ACTIVE;
                  ikev2_elog_uint (IKEV2_LOG_DEBUG, "ispi %lx initiator"
                                   " (self) behind NAT", sa->ispi);
                }
@@ -1404,9 +1439,12 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       p += plen;
     }
 
-  if (sa->is_initiator && proposal->protocol_id == IKEV2_PROTOCOL_ESP)
+  if (sa->is_initiator && proposal
+      && proposal->protocol_id == IKEV2_PROTOCOL_ESP)
     {
-      ikev2_rekey_t *rekey = &sa->rekey[0];
+      ikev2_rekey_t *rekey = sa->rekey;
+      if (vec_len (rekey) == 0)
+       goto cleanup_and_exit;
       rekey->protocol_id = proposal->protocol_id;
       rekey->i_proposal =
        ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
@@ -1442,17 +1480,22 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       rekey->tsi = tsi;
       rekey->tsr = tsr;
       /* update Ni */
-      vec_free (sa->i_nonce);
+      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);
-      vec_free (n);
     }
+  else
+    goto cleanup_and_exit;
+  vec_free (n);
   return 1;
 
 cleanup_and_exit:
   vec_free (n);
+  vec_free (proposal);
+  vec_free (tsr);
+  vec_free (tsi);
   return 0;
 }
 
@@ -1806,7 +1849,6 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
   ikev2_main_t *km = &ikev2_main;
   u32 sw_if_index;
   int rv = 0;
-  ip46_address_t zero_addr = ip46_address_initializer;
 
   if (~0 == a->sw_if_index)
     {
@@ -1851,21 +1893,39 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
       vec_add1 (sas_in, a->old_remote_sa_id);
     }
 
-  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, &zero_addr,
-                              &zero_addr, NULL, a->src_port, a->dst_port);
-  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, &zero_addr,
-                              &zero_addr, NULL, a->ipsec_over_udp_port,
-                              a->ipsec_over_udp_port);
-
-  rv |= ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in);
+  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, TUNNEL_ENCAP_DECAP_FLAG_NONE,
+                             IP_DSCP_CS0, NULL, a->src_port, a->dst_port);
+  if (rv)
+    goto err0;
+
+  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, TUNNEL_ENCAP_DECAP_FLAG_NONE,
+                             IP_DSCP_CS0, NULL,
+                             a->ipsec_over_udp_port, a->ipsec_over_udp_port);
+  if (rv)
+    goto err1;
+
+  rv = ipsec_tun_protect_update (sw_if_index, NULL, a->local_sa_id, sas_in);
+  if (rv)
+    goto err2;
+
+  return;
+
+err2:
+  ipsec_sa_unlock_id (a->remote_sa_id);
+err1:
+  ipsec_sa_unlock_id (a->local_sa_id);
+err0:
+  vec_free (sas_in);
 }
 
 static int
@@ -1915,7 +1975,7 @@ ikev2_create_tunnel_interface (vlib_main_t * vm,
       a.flags |= IPSEC_SA_FLAG_IS_TUNNEL;
       a.flags |= IPSEC_SA_FLAG_UDP_ENCAP;
     }
-  if (sa->natt)
+  if (ikev2_natt_active (sa))
     a.flags |= IPSEC_SA_FLAG_UDP_ENCAP;
   a.is_rekey = is_rekey;
 
@@ -2041,7 +2101,8 @@ ikev2_create_tunnel_interface (vlib_main_t * vm,
          a.salt_remote = child->salt_ei;
          a.salt_local = child->salt_er;
        }
-      a.dst_port = sa->natt ? sa->dst_port : sa->ipsec_over_udp_port;
+      a.dst_port =
+       ikev2_natt_active (sa) ? sa->dst_port : sa->ipsec_over_udp_port;
       a.src_port = sa->ipsec_over_udp_port;
     }
 
@@ -2352,7 +2413,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
        {
          if (sa->del[0].protocol_id == IKEV2_PROTOCOL_IKE)
            {
-             if (sa->is_initiator)
+             if (ike_hdr_is_request (ike))
                ikev2_payload_add_delete (chain, sa->del);
 
              /* The response to a request that deletes the IKE SA is an empty
@@ -2446,16 +2507,9 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
   ike->version = IKE_VERSION_2;
   ike->nextpayload = IKEV2_PAYLOAD_SK;
   tlen = sizeof (*ike);
-  if (sa->is_initiator)
-    {
-      ike->flags = IKEV2_HDR_FLAG_INITIATOR;
-      sa->last_init_msg_id = clib_net_to_host_u32 (ike->msgid);
-    }
-  else
-    {
-      ike->flags = IKEV2_HDR_FLAG_RESPONSE;
-    }
 
+  if (sa->is_initiator)
+    ike->flags |= IKEV2_HDR_FLAG_INITIATOR;
 
   if (ike->exchange == IKEV2_EXCHANGE_SA_INIT)
     {
@@ -2638,6 +2692,9 @@ ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr,
 static u32
 ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike)
 {
+  if (ike_hdr_is_response (ike))
+    return 0;
+
   u32 msg_id = clib_net_to_host_u32 (ike->msgid);
 
   /* new req */
@@ -2734,11 +2791,9 @@ ikev2_rewrite_v4_addrs (ikev2_sa_t * sa, ip4_header_t * ih)
 }
 
 static_always_inline void
-ikev2_set_ip_address (ikev2_sa_t * sa, const void *src,
-                     const void *dst, const int af, const int is_initiator)
+ikev2_set_ip_address (ikev2_sa_t * sa, const void *iaddr,
+                     const void *raddr, const int af)
 {
-  const void *raddr = is_initiator ? src : dst;
-  const void *iaddr = is_initiator ? dst : src;
   ip_address_set (&sa->raddr, raddr, af);
   ip_address_set (&sa->iaddr, iaddr, af);
 }
@@ -2763,6 +2818,20 @@ ikev2_elog_uint_peers_addr (u32 exchange, ip4_header_t * ip4,
                         exchange, src, dst);
 }
 
+static void
+ikev2_generate_sa_init_data_and_log (ikev2_sa_t * sa)
+{
+  ikev2_generate_sa_error_t rc = ikev2_generate_sa_init_data (sa);
+
+  if (PREDICT_TRUE (rc == IKEV2_GENERATE_SA_INIT_OK))
+    return;
+
+  if (rc == IKEV2_GENERATE_SA_INIT_ERR_NO_DH)
+    ikev2_elog_error (IKEV2_GENERATE_SA_INIT_OK_ERR_NO_DH_STR);
+  else if (rc == IKEV2_GENERATE_SA_INIT_ERR_UNSUPPORTED_DH)
+    ikev2_elog_error (IKEV2_GENERATE_SA_INIT_OK_ERR_UNSUPP_STR);
+}
+
 static_always_inline uword
 ikev2_node_internal (vlib_main_t * vm,
                     vlib_node_runtime_t * node, vlib_frame_t * frame,
@@ -2794,7 +2863,13 @@ ikev2_node_internal (vlib_main_t * vm,
       int ip_hdr_sz = 0;
       int is_req = 0, has_non_esp_marker = 0;
 
-      if (b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0])
+      ASSERT (0 == b0->punt_reason
+             || (is_ip4
+                 && b0->punt_reason ==
+                 ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0]));
+
+      if (is_ip4
+         && b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0])
        {
          u8 *ptr = vlib_buffer_get_current (b0);
          ip40 = (ip4_header_t *) ptr;
@@ -2854,19 +2929,16 @@ ikev2_node_internal (vlib_main_t * vm,
          sa0 = &sa;
          clib_memset (sa0, 0, sizeof (*sa0));
 
-         u8 is_initiator = ike0->flags & IKEV2_HDR_FLAG_INITIATOR;
-         if (is_initiator)
+         if (ike_hdr_is_initiator (ike0))
            {
              if (ike0->rspi == 0)
                {
                  if (is_ip4)
-                   ikev2_set_ip_address (sa0, &ip40->dst_address,
-                                         &ip40->src_address, AF_IP4,
-                                         is_initiator);
+                   ikev2_set_ip_address (sa0, &ip40->src_address,
+                                         &ip40->dst_address, AF_IP4);
                  else
-                   ikev2_set_ip_address (sa0, &ip60->dst_address,
-                                         &ip60->src_address, AF_IP6,
-                                         is_initiator);
+                   ikev2_set_ip_address (sa0, &ip60->src_address,
+                                         &ip60->dst_address, AF_IP6);
 
                  sa0->dst_port = clib_net_to_host_u16 (udp0->src_port);
 
@@ -2897,12 +2969,13 @@ ikev2_node_internal (vlib_main_t * vm,
                      sa0->r_proposals =
                        ikev2_select_proposal (sa0->i_proposals,
                                               IKEV2_PROTOCOL_IKE);
-                     ikev2_generate_sa_init_data (sa0);
+                     ikev2_generate_sa_init_data_and_log (sa0);
                    }
 
                  if (sa0->state == IKEV2_STATE_SA_INIT
                      || sa0->state == IKEV2_STATE_NOTIFY_AND_DELETE)
                    {
+                     ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                      slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
@@ -2927,13 +3000,11 @@ ikev2_node_internal (vlib_main_t * vm,
          else                  //received sa_init without initiator flag
            {
              if (is_ip4)
-               ikev2_set_ip_address (sa0, &ip40->src_address,
-                                     &ip40->dst_address, AF_IP4,
-                                     is_initiator);
+               ikev2_set_ip_address (sa0, &ip40->dst_address,
+                                     &ip40->src_address, AF_IP4);
              else
-               ikev2_set_ip_address (sa0, &ip60->src_address,
-                                     &ip60->dst_address, AF_IP6,
-                                     is_initiator);
+               ikev2_set_ip_address (sa0, &ip60->dst_address,
+                                     &ip60->src_address, AF_IP6);
 
              ikev2_process_sa_init_resp (vm, sa0, ike0, udp0, rlen);
 
@@ -2952,6 +3023,10 @@ ikev2_node_internal (vlib_main_t * vm,
                          ikev2_complete_sa_data (sa0, sai);
                          ikev2_calc_keys (sa0);
                          ikev2_sa_auth_init (sa0);
+                         ike0->flags = IKEV2_HDR_FLAG_INITIATOR;
+                         ike0->msgid =
+                           clib_net_to_host_u32 (sai->last_init_msg_id);
+                         sa0->last_init_msg_id = sai->last_init_msg_id + 1;
                          slen =
                            ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                          if (~0 == slen)
@@ -3024,6 +3099,7 @@ ikev2_node_internal (vlib_main_t * vm,
                }
              else
                {
+                 ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                  slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                  if (~0 == slen)
                    vlib_node_increment_counter (vm, node->node_index,
@@ -3093,9 +3169,9 @@ ikev2_node_internal (vlib_main_t * vm,
                        }
                    }
                }
-             if (!(ike0->flags & IKEV2_HDR_FLAG_RESPONSE))
+             if (ike_hdr_is_request (ike0))
                {
-                 ike0->flags |= IKEV2_HDR_FLAG_RESPONSE;
+                 ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                  slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                  if (~0 == slen)
                    vlib_node_increment_counter (vm, node->node_index,
@@ -3150,12 +3226,13 @@ ikev2_node_internal (vlib_main_t * vm,
                      ikev2_create_tunnel_interface (vm, sa0, child, p[0],
                                                     child - sa0->childs, 1);
                    }
-                 if (sa0->is_initiator)
+                 if (ike_hdr_is_response (ike0))
                    {
                      vec_free (sa0->rekey);
                    }
                  else
                    {
+                     ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
                      slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
@@ -3191,7 +3268,7 @@ ikev2_node_internal (vlib_main_t * vm,
                clib_net_to_host_u16 (ikev2_get_port (sa0));
 
              if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_NATT)
-                 && sa0->natt)
+                 && ikev2_natt_active (sa0))
                {
                  if (!has_non_esp_marker)
                    slen = ikev2_insert_non_esp_marker (ike0, slen);
@@ -3622,14 +3699,14 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
       ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
       ike0->ispi = clib_host_to_net_u64 (sa->ispi);
       ike0->rspi = clib_host_to_net_u64 (sa->rspi);
-
-      ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
-      sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
+      ike0->flags = 0;
+      ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id);
+      sa->last_init_msg_id += 1;
       len = ikev2_generate_message (b0, sa, ike0, 0, 0);
       if (~0 == len)
        return;
 
-      if (sa->natt)
+      if (ikev2_natt_active (sa))
        len = ikev2_insert_non_esp_marker (ike0, len);
 
       if (sa->is_initiator)
@@ -4117,15 +4194,6 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
       valid_ip = 1;
     }
 
-  bi0 = ikev2_get_new_ike_header_buff (vm, &b0);
-  if (!bi0)
-    {
-      char *errmsg = "buffer alloc failure";
-      ikev2_log_error (errmsg);
-      return clib_error_return (0, errmsg);
-    }
-  ike0 = vlib_buffer_get_current (b0);
-
   /* Prepare the SA and the IKE payload */
   ikev2_sa_t sa;
   clib_memset (&sa, 0, sizeof (ikev2_sa_t));
@@ -4147,11 +4215,21 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   sa.state = IKEV2_STATE_SA_INIT;
   sa.tun_itf = p->tun_itf;
   sa.udp_encap = p->udp_encap;
+  if (p->natt_disabled)
+    sa.natt_state = IKEV2_NATT_DISABLED;
   sa.ipsec_over_udp_port = p->ipsec_over_udp_port;
   sa.is_tun_itf_set = 1;
   sa.initial_contact = 1;
   sa.dst_port = IKEV2_PORT;
-  ikev2_generate_sa_init_data (&sa);
+
+  ikev2_generate_sa_error_t rc = ikev2_generate_sa_init_data (&sa);
+  if (rc != IKEV2_GENERATE_SA_INIT_OK)
+    {
+      ikev2_sa_free_all_vec (&sa);
+      ikev2_payload_destroy_chain (chain);
+      return clib_error_return (0, "%U", format_ikev2_gen_sa_error, rc);
+    }
+
   ikev2_payload_add_ke (chain, sa.dh_group, sa.i_dh_data);
   ikev2_payload_add_nonce (chain, sa.i_nonce);
 
@@ -4191,6 +4269,17 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
                            sig_hash_algo);
   vec_free (sig_hash_algo);
 
+  bi0 = ikev2_get_new_ike_header_buff (vm, &b0);
+  if (!bi0)
+    {
+      ikev2_sa_free_all_vec (&sa);
+      ikev2_payload_destroy_chain (chain);
+      char *errmsg = "buffer alloc failure";
+      ikev2_log_error (errmsg);
+      return clib_error_return (0, errmsg);
+    }
+  ike0 = vlib_buffer_get_current (b0);
+
   /* Buffer update and boilerplate */
   len += vec_len (chain->data);
   ike0->nextpayload = chain->first_payload_type;
@@ -4204,6 +4293,7 @@ ikev2_initiate_sa_init (vlib_main_t * vm, u8 * name)
   ike0->ispi = clib_host_to_net_u64 (sa.ispi);
   ike0->rspi = 0;
   ike0->msgid = 0;
+  sa.last_init_msg_id += 1;
 
   /* store whole IKE payload - needed for PSK auth */
   vec_reset_length (sa.last_sa_init_req_packet_data);
@@ -4276,16 +4366,17 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
   ike0->ispi = clib_host_to_net_u64 (sa->ispi);
   ike0->rspi = clib_host_to_net_u64 (sa->rspi);
+  ike0->flags = 0;
   vec_resize (sa->del, 1);
   sa->del->protocol_id = IKEV2_PROTOCOL_ESP;
   sa->del->spi = csa->i_proposals->spi;
-  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
-  sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
+  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id);
+  sa->last_init_msg_id += 1;
   len = ikev2_generate_message (b0, sa, ike0, 0, 0);
   if (~0 == len)
     return;
 
-  if (sa->natt)
+  if (ikev2_natt_active (sa))
     len = ikev2_insert_non_esp_marker (ike0, len);
   ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len,
                  ikev2_get_port (sa), sa->dst_port, sa->sw_if_index);
@@ -4395,10 +4486,11 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   ike0->exchange = IKEV2_EXCHANGE_CREATE_CHILD_SA;
   ike0->ispi = clib_host_to_net_u64 (sa->ispi);
   ike0->rspi = clib_host_to_net_u64 (sa->rspi);
-  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
-  sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
+  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id);
+  sa->last_init_msg_id += 1;
 
   ikev2_rekey_t *rekey;
+  vec_reset_length (sa->rekey);
   vec_add2 (sa->rekey, rekey, 1);
   ikev2_sa_proposal_t *proposals = vec_dup (csa->i_proposals);
 
@@ -4410,7 +4502,7 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   if (~0 == len)
     return;
 
-  if (sa->natt)
+  if (ikev2_natt_active (sa))
     len = ikev2_insert_non_esp_marker (ike0, len);
   ikev2_send_ike (vm, &sa->iaddr, &sa->raddr, bi0, len,
                  ikev2_get_port (sa), ikev2_get_port (sa), sa->sw_if_index);
@@ -4677,7 +4769,10 @@ ikev2_mngr_process_child_sa (ikev2_sa_t * sa, ikev2_child_sa_t * csa,
       u32 *sas_in = NULL;
       vec_add1 (sas_in, csa->remote_sa_id);
       vlib_worker_thread_barrier_sync (vm);
-      ipsec_tun_protect_update (sw_if_index, NULL, csa->local_sa_id, sas_in);
+      int rv = ipsec_tun_protect_update (sw_if_index, NULL,
+                                        csa->local_sa_id, sas_in);
+      if (rv)
+       vec_free (sas_in);
       ipsec_sa_unlock_id (ikev2_flip_alternate_sa_bit (csa->remote_sa_id));
       vlib_worker_thread_barrier_release (vm);
     }
@@ -4713,6 +4808,17 @@ ikev2_set_liveness_params (u32 period, u32 max_retries)
   return 0;
 }
 
+clib_error_t *
+ikev2_profile_natt_disable (u8 * name)
+{
+  ikev2_profile_t *p = ikev2_profile_index_by_name (name);
+  if (!p)
+    return clib_error_return (0, "unknown profile %v", name);
+
+  p->natt_disabled = 1;
+  return 0;
+}
+
 static void
 ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
 {
@@ -4757,33 +4863,12 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
     }
 }
 
-static ike_payload_header_t *
-ikev2_find_ike_payload (ike_header_t * ike, u32 payload_type)
-{
-  int p = 0;
-  ike_payload_header_t *ikep;
-  u32 payload = ike->nextpayload;
-
-  while (payload != IKEV2_PAYLOAD_NONE)
-    {
-      ikep = (ike_payload_header_t *) & ike->payload[p];
-      if (payload == payload_type)
-       return ikep;
-
-      u16 plen = clib_net_to_host_u16 (ikep->length);
-      payload = ikep->nextpayload;
-      p += plen;
-    }
-  return 0;
-}
-
 static void
 ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
 {
   ikev2_profile_t *p;
   u32 bi0;
-  u8 *nat_sha;
-  ike_payload_header_t *ph;
+  u8 *nat_sha, *np;
 
   if (ip_address_is_zero (&sa->iaddr))
     {
@@ -4794,20 +4879,20 @@ ikev2_process_pending_sa_init_one (ikev2_main_t * km, ikev2_sa_t * sa)
        return;
 
       /* update NAT detection payload */
-      ph =
-       ikev2_find_ike_payload ((ike_header_t *)
-                               sa->last_sa_init_req_packet_data,
-                               IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP);
-      if (!ph)
-       return;
-
-      nat_sha =
-       ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa->ispi),
-                               clib_host_to_net_u64 (sa->rspi),
-                               &sa->iaddr,
-                               clib_host_to_net_u16 (IKEV2_PORT));
-      clib_memcpy_fast (ph->payload, nat_sha, vec_len (nat_sha));
-      vec_free (nat_sha);
+      np =
+       ikev2_find_ike_notify_payload
+       ((ike_header_t *) sa->last_sa_init_req_packet_data,
+        IKEV2_NOTIFY_MSG_NAT_DETECTION_SOURCE_IP);
+      if (np)
+       {
+         nat_sha =
+           ikev2_compute_nat_sha1 (clib_host_to_net_u64 (sa->ispi),
+                                   clib_host_to_net_u64 (sa->rspi),
+                                   &sa->iaddr,
+                                   clib_host_to_net_u16 (IKEV2_PORT));
+         clib_memcpy_fast (np, nat_sha, vec_len (nat_sha));
+         vec_free (nat_sha);
+       }
     }
 
   if (vlib_buffer_alloc (km->vlib_main, &bi0, 1) != 1)
@@ -4864,13 +4949,14 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
   ike0->exchange = IKEV2_EXCHANGE_INFORMATIONAL;
   ike0->ispi = clib_host_to_net_u64 (sa->ispi);
   ike0->rspi = clib_host_to_net_u64 (sa->rspi);
-  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id + 1);
-  sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
+  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id);
+  ike0->flags = 0;
+  sa->last_init_msg_id += 1;
   len = ikev2_generate_message (b0, sa, ike0, 0, 0);
   if (~0 == len)
     return;
 
-  if (sa->natt)
+  if (ikev2_natt_active (sa))
     len = ikev2_insert_non_esp_marker (ike0, len);
 
   if (sa->is_initiator)
@@ -4889,6 +4975,13 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
                  sa->sw_if_index);
 }
 
+void
+ikev2_disable_dpd (void)
+{
+  ikev2_main_t *km = &ikev2_main;
+  km->dpd_disabled = 1;
+}
+
 static_always_inline int
 ikev2_mngr_process_responder_sas (ikev2_sa_t * sa)
 {
@@ -4924,8 +5017,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
 
   while (1)
     {
-      u8 req_sent = 0;
-      vlib_process_wait_for_event_or_clock (vm, 1);
+      vlib_process_wait_for_event_or_clock (vm, 2);
       vlib_process_get_events (vm, NULL);
 
       /* process ike child sas */
@@ -4952,11 +5044,9 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
             sa->old_id_expiration -= 1;
 
           vec_foreach (c, sa->childs)
-            {
-            req_sent |= ikev2_mngr_process_child_sa(sa, c, del_old_ids);
-            }
+            ikev2_mngr_process_child_sa(sa, c, del_old_ids);
 
-          if (ikev2_mngr_process_responder_sas (sa))
+          if (!km->dpd_disabled && ikev2_mngr_process_responder_sas (sa))
             vec_add1 (to_be_deleted, sa - tkm->sas);
         }));
         /* *INDENT-ON* */
@@ -4979,7 +5069,12 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
              p = pool_elt_at_index (km->profiles, sa->profile_index);
              if (p)
                {
-                 ikev2_initiate_sa_init (vm, p->name);
+                 clib_error_t *e = ikev2_initiate_sa_init (vm, p->name);
+                 if (e)
+                   {
+                     ikev2_log_error ("%U", format_clib_error, e);
+                     clib_error_free (e);
+                   }
                }
            }
        }
@@ -4995,14 +5090,6 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
       /* *INDENT-ON* */
 
       ikev2_process_pending_sa_init (km);
-
-      if (req_sent)
-       {
-         vlib_process_wait_for_event_or_clock (vm, 5);
-         vlib_process_get_events (vm, NULL);
-         req_sent = 0;
-       }
-
     }
   return 0;
 }