ikev2: use new counters data model & add more counters
[vpp.git] / src / plugins / ikev2 / ikev2.c
index ae48b44..c9a8647 100644 (file)
@@ -28,6 +28,7 @@
 #include <plugins/ikev2/ikev2_priv.h>
 #include <openssl/sha.h>
 #include <vnet/ipsec/ipsec_punt.h>
+#include <plugins/ikev2/ikev2.api_enum.h>
 
 #define IKEV2_LIVENESS_RETRIES 3
 #define IKEV2_LIVENESS_PERIOD_CHECK 30
@@ -38,9 +39,9 @@ static int ikev2_delete_tunnel_interface (vnet_main_t * vnm,
                                          ikev2_sa_t * sa,
                                          ikev2_child_sa_t * child);
 
-#define ikev2_set_state(sa, v) do { \
+#define ikev2_set_state(sa, v, ...) do { \
     (sa)->state = v; \
-    ikev2_elog_sa_state("ispi %lx SA state changed to " #v, sa->ispi); \
+    ikev2_elog_sa_state("ispi %lx SA state changed to " #v __VA_ARGS__, sa->ispi); \
   } while(0);
 
 typedef struct
@@ -49,6 +50,14 @@ typedef struct
   u32 sw_if_index;
 } ikev2_trace_t;
 
+typedef struct
+{
+  u16 n_keepalives;
+  u16 n_rekey_req;
+  u16 n_exchange_sa_req;
+  u16 n_ike_auth_req;
+} ikev2_stats_t;
+
 static u8 *
 format_ikev2_trace (u8 * s, va_list * args)
 {
@@ -61,30 +70,36 @@ format_ikev2_trace (u8 * s, va_list * args)
   return s;
 }
 
-#define foreach_ikev2_error \
-_(PROCESSED, "IKEv2 packets processed") \
-_(IKE_SA_INIT_RETRANSMIT, "IKE_SA_INIT retransmit ") \
-_(IKE_SA_INIT_IGNORE, "IKE_SA_INIT ignore (IKE SA already auth)") \
-_(IKE_REQ_RETRANSMIT, "IKE request retransmit") \
-_(IKE_REQ_IGNORE, "IKE request ignore (old msgid)") \
-_(NOT_IKEV2, "Non IKEv2 packets received") \
-_(BAD_LENGTH, "Bad packet length") \
-_(MALFORMED_PACKET, "Malformed packet") \
-_(NO_BUFF_SPACE, "No buffer space")
+#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
 {
-#define _(sym,str) IKEV2_ERROR_##sym,
-  foreach_ikev2_error
-#undef _
-    IKEV2_N_ERROR,
-} ikev2_error_t;
+  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 char *ikev2_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ikev2_error
-#undef _
-};
+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;
+}
 
 typedef enum
 {
@@ -105,7 +120,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 +372,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 +394,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 +419,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 +441,8 @@ 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;
+  if (sai->natt_state == IKEV2_NATT_DISABLED)
+    sa->natt_state = IKEV2_NATT_DISABLED;
   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 +450,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;
@@ -683,9 +700,8 @@ ikev2_check_payload_length (const ike_payload_header_t * ikep, int rlen,
 }
 
 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)
+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;
@@ -700,6 +716,7 @@ ikev2_process_sa_init_req (vlib_main_t * vm,
                       ip_addr_version (&sa->iaddr) == AF_IP4);
 
   sa->ispi = clib_net_to_host_u64 (ike->ispi);
+  sa->sw_if_index = sw_if_index;
 
   /* store whole IKE payload - needed for PSK auth */
   vec_reset_length (sa->last_sa_init_req_packet_data);
@@ -743,7 +760,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 +774,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 +888,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);
                }
@@ -1034,14 +1054,14 @@ ikev2_initial_contact_cleanup_internal (ikev2_main_per_thread_data_t * ptd,
 
   /* find old IKE SAs with the same authenticated identity */
   /* *INDENT-OFF* */
-  pool_foreach (tmp, ptd->sas, ({
+  pool_foreach (tmp, ptd->sas)  {
     if (!ikev2_is_id_equal (&tmp->i_id, &sa->i_id)
         || !ikev2_is_id_equal(&tmp->r_id, &sa->r_id))
       continue;
 
     if (sa->rspi != tmp->rspi)
       vec_add1(delete, tmp - ptd->sas);
-  }));
+  }
   /* *INDENT-ON* */
 
   for (i = 0; i < vec_len (delete); i++)
@@ -1244,7 +1264,7 @@ ikev2_process_auth_req (vlib_main_t * vm, ikev2_sa_t * sa,
   return 1;
 
 malformed:
-  ikev2_set_state (sa, IKEV2_STATE_DELETED);
+  ikev2_set_state (sa, IKEV2_STATE_DELETED, ": malformed IKE_AUTH");
   return 0;
 }
 
@@ -1404,9 +1424,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);
@@ -1525,7 +1548,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa)
   ikev2_id_t *id_rem, *id_loc;
 
   /* *INDENT-OFF* */
-  pool_foreach (p, km->profiles, ({
+  pool_foreach (p, km->profiles)  {
 
     if (sa->is_initiator)
       {
@@ -1568,7 +1591,7 @@ ikev2_sa_match_ts (ikev2_sa_t * sa)
       }
 
     break;
-  }));
+  }
   /* *INDENT-ON* */
 
   if (tsi && tsr)
@@ -1628,7 +1651,7 @@ ikev2_sa_auth (ikev2_sa_t * sa)
     }
 
   /* *INDENT-OFF* */
-  pool_foreach (p, km->profiles, ({
+  pool_foreach (p, km->profiles)  {
 
     /* check id */
     if (!ikev2_is_id_equal (&p->rem_id, id_rem)
@@ -1668,7 +1691,7 @@ ikev2_sa_auth (ikev2_sa_t * sa)
 
     vec_free(auth);
     vec_free(psk);
-  }));
+  }
   /* *INDENT-ON* */
 
   if (sel_p)
@@ -1811,7 +1834,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)
     {
@@ -1856,25 +1878,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, TUNNEL_ENCAP_DECAP_FLAG_NONE,
-                              IP_DSCP_CS0, 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, TUNNEL_ENCAP_DECAP_FLAG_NONE,
-                              IP_DSCP_CS0, 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
@@ -1924,7 +1960,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;
 
@@ -2050,7 +2086,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;
     }
 
@@ -2211,8 +2248,8 @@ ikev2_delete_tunnel_interface (vnet_main_t * vnm, ikev2_sa_t * sa,
 }
 
 static u32
-ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
-                       ike_header_t * ike, void *user, udp_header_t * udp)
+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)
 {
   ikev2_main_t *km = &ikev2_main;
   u16 buffer_data_size = vlib_buffer_get_default_data_size (km->vlib_main);
@@ -2350,7 +2387,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
        }
       else
        {
-         ikev2_set_state (sa, IKEV2_STATE_DELETED);
+         ikev2_set_state (sa, IKEV2_STATE_DELETED, ": unexpected IKE_AUTH");
          goto done;
        }
     }
@@ -2380,7 +2417,7 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
       /* received N(AUTHENTICATION_FAILED) */
       else if (sa->state == IKEV2_STATE_AUTH_FAILED)
        {
-         ikev2_set_state (sa, IKEV2_STATE_DELETED);
+         ikev2_set_state (sa, IKEV2_STATE_DELETED, ": auth failed");
          goto done;
        }
       /* received unsupported critical payload */
@@ -2395,7 +2432,15 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
          vec_free (data);
          sa->unsupported_cp = 0;
        }
-      /* else send empty response */
+      else
+       /* else send empty response */
+       {
+         if (ike_hdr_is_response (ike))
+           {
+             ASSERT (stats != 0);
+             stats->n_keepalives++;
+           }
+       }
     }
   else if (ike->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA)
     {
@@ -2459,11 +2504,6 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
   if (sa->is_initiator)
     ike->flags |= IKEV2_HDR_FLAG_INITIATOR;
 
-  if (ike_hdr_is_request (ike))
-    {
-      sa->last_init_msg_id = clib_net_to_host_u32 (ike->msgid);
-    }
-
   if (ike->exchange == IKEV2_EXCHANGE_SA_INIT)
     {
       tlen += vec_len (chain->data);
@@ -2631,11 +2671,11 @@ ikev2_retransmit_sa_init (ike_header_t * ike, ip_address_t iaddr,
   ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
 
   /* *INDENT-OFF* */
-  pool_foreach (sa, ptd->sas, ({
+  pool_foreach (sa, ptd->sas)  {
     res = ikev2_retransmit_sa_init_one (sa, ike, iaddr, raddr, rlen);
     if (res)
       return res;
-  }));
+  }
   /* *INDENT-ON* */
 
   /* req is not retransmit */
@@ -2771,18 +2811,46 @@ 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 void
+ikev2_update_stats (vlib_main_t *vm, u32 node_index, ikev2_stats_t *s)
+{
+  vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_KEEPALIVE,
+                              s->n_keepalives);
+  vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_REKEY_REQ,
+                              s->n_rekey_req);
+  vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_EXCHANGE_SA_REQ,
+                              s->n_exchange_sa_req);
+  vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_IKE_AUTH_REQ,
+                              s->n_ike_auth_req);
+}
+
 static_always_inline uword
-ikev2_node_internal (vlib_main_t * vm,
-                    vlib_node_runtime_t * node, vlib_frame_t * frame,
-                    u8 is_ip4)
+ikev2_node_internal (vlib_main_t *vm, vlib_node_runtime_t *node,
+                    vlib_frame_t *frame, u8 is_ip4, u8 natt)
 {
   u32 n_left = frame->n_vectors, *from;
   ikev2_main_t *km = &ikev2_main;
   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
   ikev2_main_per_thread_data_t *ptd = ikev2_get_per_thread_data ();
+  ikev2_stats_t _stats, *stats = &_stats;
   int res;
 
+  clib_memset_u16 (stats, 0, sizeof (stats[0]) / sizeof (u16));
   from = vlib_frame_vector_args (frame);
   vlib_get_buffers (vm, from, bufs, n_left);
   b = bufs;
@@ -2800,9 +2868,9 @@ ikev2_node_internal (vlib_main_t * vm,
       ikev2_sa_t sa;           /* temporary store for SA */
       u32 rlen, slen = 0;
       int ip_hdr_sz = 0;
-      int is_req = 0, has_non_esp_marker = 0;
+      int is_req = 0;
 
-      if (b0->punt_reason == ipsec_punt_reason[IPSEC_PUNT_IP4_SPI_UDP_0])
+      if (natt)
        {
          u8 *ptr = vlib_buffer_get_current (b0);
          ip40 = (ip4_header_t *) ptr;
@@ -2835,12 +2903,12 @@ ikev2_node_internal (vlib_main_t * vm,
       rlen = b0->current_length - ip_hdr_sz - sizeof (*udp0);
 
       /* check for non-esp marker */
-      if (*((u32 *) ike0) == 0)
+      if (natt)
        {
+         ASSERT (*((u32 *) ike0) == 0);
          ike0 =
            (ike_header_t *) ((u8 *) ike0 + sizeof (ikev2_non_esp_marker));
          rlen -= sizeof (ikev2_non_esp_marker);
-         has_non_esp_marker = 1;
        }
 
       if (clib_net_to_host_u32 (ike0->length) != rlen)
@@ -2864,6 +2932,7 @@ ikev2_node_internal (vlib_main_t * vm,
 
          if (ike_hdr_is_initiator (ike0))
            {
+             stats->n_exchange_sa_req++;
              if (ike0->rspi == 0)
                {
                  if (is_ip4)
@@ -2890,7 +2959,9 @@ ikev2_node_internal (vlib_main_t * vm,
                      goto dispatch0;
                    }
 
-                 res = ikev2_process_sa_init_req (vm, sa0, ike0, udp0, rlen);
+                 res = ikev2_process_sa_init_req (
+                   vm, sa0, ike0, udp0, rlen,
+                   vnet_buffer (b0)->sw_if_index[VLIB_RX]);
                  if (!res)
                    vlib_node_increment_counter (vm, node->node_index,
                                                 IKEV2_ERROR_MALFORMED_PACKET,
@@ -2902,14 +2973,15 @@ 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);
+                     slen =
+                       ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
                                                     IKEV2_ERROR_NO_BUFF_SPACE,
@@ -2957,8 +3029,11 @@ ikev2_node_internal (vlib_main_t * vm,
                          ikev2_calc_keys (sa0);
                          ikev2_sa_auth_init (sa0);
                          ike0->flags = IKEV2_HDR_FLAG_INITIATOR;
-                         slen =
-                           ikev2_generate_message (b0, sa0, ike0, 0, udp0);
+                         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, stats);
                          if (~0 == slen)
                            vlib_node_increment_counter (vm,
                                                         node->node_index,
@@ -3029,7 +3104,10 @@ ikev2_node_internal (vlib_main_t * vm,
                }
              else
                {
-                 slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
+                 stats->n_ike_auth_req++;
+                 ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
+                 slen =
+                   ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats);
                  if (~0 == slen)
                    vlib_node_increment_counter (vm, node->node_index,
                                                 IKEV2_ERROR_NO_BUFF_SPACE,
@@ -3101,7 +3179,8 @@ ikev2_node_internal (vlib_main_t * vm,
              if (ike_hdr_is_request (ike0))
                {
                  ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
-                 slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
+                 slen =
+                   ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats);
                  if (~0 == slen)
                    vlib_node_increment_counter (vm, node->node_index,
                                                 IKEV2_ERROR_NO_BUFF_SPACE,
@@ -3161,8 +3240,10 @@ ikev2_node_internal (vlib_main_t * vm,
                    }
                  else
                    {
+                     stats->n_rekey_req++;
                      ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
-                     slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
+                     slen =
+                       ikev2_generate_message (b0, sa0, ike0, 0, udp0, stats);
                      if (~0 == slen)
                        vlib_node_increment_counter (vm, node->node_index,
                                                     IKEV2_ERROR_NO_BUFF_SPACE,
@@ -3197,15 +3278,15 @@ 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)
+                 if (!natt)
                    slen = ikev2_insert_non_esp_marker (ike0, slen);
                }
            }
          else
            {
-             if (has_non_esp_marker)
+             if (natt)
                slen += sizeof (ikev2_non_esp_marker);
 
              u16 tp = udp0->dst_port;
@@ -3251,6 +3332,7 @@ ikev2_node_internal (vlib_main_t * vm,
       b += 1;
     }
 
+  ikev2_update_stats (vm, node->node_index, stats);
   vlib_node_increment_counter (vm, node->node_index,
                               IKEV2_ERROR_PROCESSED, frame->n_vectors);
   vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
@@ -3260,13 +3342,20 @@ ikev2_node_internal (vlib_main_t * vm,
 static uword
 ikev2_ip4 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */ );
+  return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */, 0);
+}
+
+static uword
+ikev2_ip4_natt (vlib_main_t *vm, vlib_node_runtime_t *node,
+               vlib_frame_t *frame)
+{
+  return ikev2_node_internal (vm, node, frame, 1 /* is_ip4 */, 1 /* natt */);
 }
 
 static uword
 ikev2_ip6 (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame)
 {
-  return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */ );
+  return ikev2_node_internal (vm, node, frame, 0 /* is_ip4 */, 0);
 }
 
 /* *INDENT-OFF* */
@@ -3277,8 +3366,25 @@ VLIB_REGISTER_NODE (ikev2_node_ip4,static) = {
   .format_trace = format_ikev2_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
-  .n_errors = ARRAY_LEN(ikev2_error_strings),
-  .error_strings = ikev2_error_strings,
+  .n_errors = IKEV2_N_ERROR,
+  .error_counters = ikev2_error_counters,
+
+  .n_next_nodes = IKEV2_IP4_N_NEXT,
+  .next_nodes = {
+    [IKEV2_NEXT_IP4_LOOKUP] = "ip4-lookup",
+    [IKEV2_NEXT_IP4_ERROR_DROP] = "error-drop",
+  },
+};
+
+VLIB_REGISTER_NODE (ikev2_node_ip4_natt,static) = {
+  .function = ikev2_ip4_natt,
+  .name = "ikev2-ip4-natt",
+  .vector_size = sizeof (u32),
+  .format_trace = format_ikev2_trace,
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = IKEV2_N_ERROR,
+  .error_counters = ikev2_error_counters,
 
   .n_next_nodes = IKEV2_IP4_N_NEXT,
   .next_nodes = {
@@ -3294,8 +3400,8 @@ VLIB_REGISTER_NODE (ikev2_node_ip6,static) = {
   .format_trace = format_ikev2_trace,
   .type = VLIB_NODE_TYPE_INTERNAL,
 
-  .n_errors = ARRAY_LEN(ikev2_error_strings),
-  .error_strings = ikev2_error_strings,
+  .n_errors = IKEV2_N_ERROR,
+  .error_counters = ikev2_error_counters,
 
   .n_next_nodes = IKEV2_IP6_N_NEXT,
   .next_nodes = {
@@ -3629,13 +3735,13 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
       ike0->ispi = clib_host_to_net_u64 (sa->ispi);
       ike0->rspi = clib_host_to_net_u64 (sa->rspi);
       ike0->flags = 0;
-      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);
-      len = ikev2_generate_message (b0, sa, ike0, 0, 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, 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)
@@ -3678,10 +3784,10 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p)
   u32 *del_sai = 0;
 
   /* *INDENT-OFF* */
-  pool_foreach(sa, km->sais, ({
+  pool_foreach (sa, km->sais)  {
     if (pi == sa->profile_index)
       vec_add1 (del_sai, sa - km->sais);
-  }));
+  }
   /* *INDENT-ON* */
 
   vec_foreach (sai, del_sai)
@@ -3696,10 +3802,10 @@ ikev2_cleanup_profile_sessions (ikev2_main_t * km, ikev2_profile_t * p)
   vec_foreach (tkm, km->per_thread_data)
   {
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       if (sa->profile_index != ~0 && pi == sa->profile_index)
         vec_add1 (del_sai, sa - tkm->sas);
-    }));
+    }
     /* *INDENT-ON* */
 
     vec_foreach (sai, del_sai)
@@ -4123,15 +4229,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));
@@ -4153,11 +4250,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);
 
@@ -4197,6 +4304,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;
@@ -4210,6 +4328,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);
@@ -4286,13 +4405,13 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   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);
-  len = ikev2_generate_message (b0, sa, ike0, 0, 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, 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);
@@ -4318,14 +4437,14 @@ ikev2_initiate_delete_child_sa (vlib_main_t * vm, u32 ispi)
     if (fchild)
       break;
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1);
       if (fchild)
         {
           fsa = sa;
           break;
         }
-    }));
+    }
     /* *INDENT-ON* */
   }
 
@@ -4358,14 +4477,14 @@ ikev2_initiate_delete_ike_sa (vlib_main_t * vm, u64 ispi)
     if (fsa)
       break;
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       if (sa->ispi == ispi)
         {
           fsa = sa;
           ftkm = tkm;
           break;
         }
-    }));
+    }
     /* *INDENT-ON* */
   }
 
@@ -4402,10 +4521,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);
 
@@ -4413,11 +4533,11 @@ ikev2_rekey_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   RAND_bytes ((u8 *) & proposals[0].spi, sizeof (proposals[0].spi));
   rekey->spi = proposals[0].spi;
   rekey->ispi = csa->i_proposals->spi;
-  len = ikev2_generate_message (b0, sa, ike0, proposals, 0);
+  len = ikev2_generate_message (b0, sa, ike0, proposals, 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), ikev2_get_port (sa), sa->sw_if_index);
@@ -4440,14 +4560,14 @@ ikev2_initiate_rekey_child_sa (vlib_main_t * vm, u32 ispi)
     if (fchild)
       break;
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       fchild = ikev2_sa_get_child(sa, ispi, IKEV2_PROTOCOL_ESP, 1);
       if (fchild)
         {
           fsa = sa;
           break;
         }
-    }));
+    }
     /* *INDENT-ON* */
   }
 
@@ -4482,10 +4602,10 @@ ikev2_sa_del (ikev2_profile_t * p, u32 sw_if_index)
   vec_foreach (tkm, km->per_thread_data)
   {
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       if (ikev2_sa_sw_if_match (sa, sw_if_index))
         vec_add1 (sa_vec, sa);
-    }));
+    }
     /* *INDENT-ON* */
 
     vec_foreach (sap, sa_vec)
@@ -4497,10 +4617,10 @@ ikev2_sa_del (ikev2_profile_t * p, u32 sw_if_index)
   vec_free (sa_vec);
 
   /* *INDENT-OFF* */
-  pool_foreach (sa, km->sais, ({
+  pool_foreach (sa, km->sais)  {
     if (ikev2_sa_sw_if_match (sa, sw_if_index))
       vec_add1 (ispi_vec, sa->ispi);
-  }));
+  }
   /* *INDENT-ON* */
 
   vec_foreach (ispi, ispi_vec)
@@ -4521,10 +4641,10 @@ ikev2_sw_interface_add_del (vnet_main_t * vnm, u32 sw_if_index, u32 is_add)
     return 0;
 
   /* *INDENT-OFF* */
-  pool_foreach (p, km->profiles, ({
+  pool_foreach (p, km->profiles)  {
     if (p->responder.sw_if_index == sw_if_index)
       ikev2_sa_del (p, sw_if_index);
-  }));
+  }
   /* *INDENT-ON* */
 
   return 0;
@@ -4578,9 +4698,9 @@ ikev2_init (vlib_main_t * vm)
   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");
+  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");
+                     "ikev2-ip4-natt");
   ikev2_cli_reference ();
 
   km->log_level = IKEV2_LOG_ERROR;
@@ -4684,7 +4804,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);
     }
@@ -4720,6 +4843,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)
 {
@@ -4739,14 +4873,14 @@ ikev2_mngr_process_ipsec_sa (ipsec_sa_t * ipsec_sa)
     if (fchild)
       break;
     /* *INDENT-OFF* */
-    pool_foreach (sa, tkm->sas, ({
+    pool_foreach (sa, tkm->sas)  {
       fchild = ikev2_sa_get_child(sa, ipsec_sa->spi, IKEV2_PROTOCOL_ESP, 1);
       if (fchild)
         {
           fsa = sa;
           break;
         }
-    }));
+    }
     /* *INDENT-ON* */
   }
   vlib_get_combined_counter (&ipsec_sa_counters,
@@ -4850,14 +4984,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);
+  ike0->msgid = clib_host_to_net_u32 (sa->last_init_msg_id);
   ike0->flags = 0;
-  sa->last_init_msg_id = clib_net_to_host_u32 (ike0->msgid);
-  len = ikev2_generate_message (b0, sa, ike0, 0, 0);
+  sa->last_init_msg_id += 1;
+  len = ikev2_generate_message (b0, sa, ike0, 0, 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)
@@ -4929,7 +5063,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
        u32 *to_be_deleted = 0;
 
         /* *INDENT-OFF* */
-        pool_foreach (sa, tkm->sas, ({
+        pool_foreach (sa, tkm->sas)  {
           ikev2_child_sa_t *c;
           u8 del_old_ids = 0;
 
@@ -4949,7 +5083,7 @@ ikev2_mngr_process_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
 
           if (!km->dpd_disabled && ikev2_mngr_process_responder_sas (sa))
             vec_add1 (to_be_deleted, sa - tkm->sas);
-        }));
+        }
         /* *INDENT-ON* */
 
        vec_foreach (sai, to_be_deleted)
@@ -4970,7 +5104,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);
+                   }
                }
            }
        }
@@ -4980,9 +5119,9 @@ 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, ({
+      pool_foreach (sa, im->sad)  {
         ikev2_mngr_process_ipsec_sa(sa);
-      }));
+      }
       /* *INDENT-ON* */
 
       ikev2_process_pending_sa_init (km);