ikev2: fix rekey against strongSwan
[vpp.git] / src / plugins / ikev2 / ikev2.c
index 7aaf4f3..59a01f3 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
@@ -92,31 +93,6 @@ format_ikev2_gen_sa_error (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")
-
-typedef enum
-{
-#define _(sym,str) IKEV2_ERROR_##sym,
-  foreach_ikev2_error
-#undef _
-    IKEV2_N_ERROR,
-} ikev2_error_t;
-
-static char *ikev2_error_strings[] = {
-#define _(sym,string) string,
-  foreach_ikev2_error
-#undef _
-};
-
 typedef enum
 {
   IKEV2_NEXT_IP4_LOOKUP,
@@ -716,9 +692,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;
@@ -733,6 +708,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);
@@ -1364,20 +1340,24 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
   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;
   ikev2_ts_t *tsi = 0;
   ikev2_ts_t *tsr = 0;
   ikev2_sa_proposal_t *proposal = 0;
   ikev2_child_sa_t *child_sa;
-  u32 dlen = 0;
+  u32 dlen = 0, src;
   u16 plen;
 
-  ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received "
-                      "from ", clib_host_to_net_u64 (ike->ispi),
-                      clib_host_to_net_u64 (ike->rspi),
-                      ip_addr_v4 (&sa->raddr).as_u32,
+  if (sa->is_initiator)
+    src = ip_addr_v4 (&sa->raddr).as_u32;
+  else
+    src = ip_addr_v4 (&sa->iaddr).as_u32;
+
+  ikev2_elog_exchange ("ispi %lx rspi %lx CREATE_CHILD_SA received from",
+                      clib_host_to_net_u64 (ike->ispi),
+                      clib_host_to_net_u64 (ike->rspi), src,
                       ip_addr_version (&sa->raddr) == AF_IP4);
 
   plaintext = ikev2_decrypt_sk_payload (sa, ike, &payload, len, &dlen);
@@ -1440,10 +1420,12 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
       p += plen;
     }
 
-  if (sa->is_initiator && proposal
-      && proposal->protocol_id == IKEV2_PROTOCOL_ESP)
+  if (!proposal || proposal->protocol_id != IKEV2_PROTOCOL_ESP)
+    goto cleanup_and_exit;
+
+  if (sa->is_initiator)
     {
-      ikev2_rekey_t *rekey = sa->rekey;
+      rekey = sa->rekey;
       if (vec_len (rekey) == 0)
        goto cleanup_and_exit;
       rekey->protocol_id = proposal->protocol_id;
@@ -1462,33 +1444,47 @@ ikev2_process_create_child_sa_req (vlib_main_t * vm,
          child_sa->rekey_retries = 0;
        }
     }
-  else if (rekeying)
+  else
     {
-      ikev2_rekey_t *rekey;
-      child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1);
-      if (!child_sa)
+      if (rekeying)
+       {
+         child_sa = ikev2_sa_get_child (sa, n->spi, n->protocol_id, 1);
+         if (!child_sa)
+           {
+             ikev2_elog_uint (IKEV2_LOG_ERROR, "child SA spi %lx not found",
+                              n->spi);
+             goto cleanup_and_exit;
+           }
+         vec_add2 (sa->rekey, rekey, 1);
+         rekey->protocol_id = n->protocol_id;
+         rekey->spi = n->spi;
+         rekey->i_proposal = proposal;
+         rekey->r_proposal =
+           ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
+         /* update Ni */
+         vec_reset_length (sa->i_nonce);
+         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
+         /* generate new Nr */
+         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
+         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
+       }
+      else
        {
-         ikev2_elog_uint (IKEV2_LOG_ERROR, "child SA spi %lx not found",
-                          n->spi);
-         goto cleanup_and_exit;
+         /* create new child SA */
+         vec_add2 (sa->new_child, rekey, 1);
+         rekey->i_proposal = proposal;
+         rekey->r_proposal =
+           ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
+         /* update Ni */
+         vec_reset_length (sa->i_nonce);
+         vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
+         /* generate new Nr */
+         vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
+         RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
        }
-      vec_add2 (sa->rekey, rekey, 1);
-      rekey->protocol_id = n->protocol_id;
-      rekey->spi = n->spi;
-      rekey->i_proposal = proposal;
-      rekey->r_proposal =
-       ikev2_select_proposal (proposal, IKEV2_PROTOCOL_ESP);
       rekey->tsi = tsi;
       rekey->tsr = tsr;
-      /* update Ni */
-      vec_reset_length (sa->i_nonce);
-      vec_add (sa->i_nonce, nonce, IKEV2_NONCE_SIZE);
-      /* generate new Nr */
-      vec_validate (sa->r_nonce, IKEV2_NONCE_SIZE - 1);
-      RAND_bytes ((u8 *) sa->r_nonce, IKEV2_NONCE_SIZE);
     }
-  else
-    goto cleanup_and_exit;
   vec_free (n);
   return 1;
 
@@ -1690,7 +1686,11 @@ ikev2_sa_auth (ikev2_sa_t * sa)
             sel_p = p;
             break;
           }
-
+       else
+         {
+           ikev2_elog_uint (IKEV2_LOG_ERROR, "shared key mismatch! ispi %lx",
+                            sa->ispi);
+         }
       }
     else if (sa_auth->method == IKEV2_AUTH_METHOD_RSA_SIG)
       {
@@ -1703,6 +1703,11 @@ ikev2_sa_auth (ikev2_sa_t * sa)
             sel_p = p;
             break;
           }
+       else
+         {
+           ikev2_elog_uint (IKEV2_LOG_ERROR,
+                            "cert verification failed! ispi %lx", sa->ispi);
+         }
       }
 
     vec_free(auth);
@@ -1834,8 +1839,8 @@ typedef struct
   u32 remote_spi;
   ipsec_crypto_alg_t encr_type;
   ipsec_integ_alg_t integ_type;
-  ip46_address_t local_ip;
-  ip46_address_t remote_ip;
+  ip_address_t local_ip;
+  ip_address_t remote_ip;
   ipsec_key_t loc_ckey, rem_ckey, loc_ikey, rem_ikey;
   u8 is_rekey;
   u32 old_remote_sa_id;
@@ -1850,12 +1855,32 @@ 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;
+  tunnel_t tun_in = {
+    .t_flags = TUNNEL_FLAG_NONE,
+    .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
+    .t_dscp = 0,
+    .t_mode = TUNNEL_MODE_P2P,
+    .t_table_id = 0,
+    .t_hop_limit = 255,
+    .t_src = a->local_ip,
+    .t_dst = a->remote_ip,
+  };
+  tunnel_t tun_out = {
+    .t_flags = TUNNEL_FLAG_NONE,
+    .t_encap_decap_flags = TUNNEL_ENCAP_DECAP_FLAG_NONE,
+    .t_dscp = 0,
+    .t_mode = TUNNEL_MODE_P2P,
+    .t_table_id = 0,
+    .t_hop_limit = 255,
+    .t_src = a->remote_ip,
+    .t_dst = a->local_ip,
+  };
 
   if (~0 == a->sw_if_index)
     {
       /* no tunnel associated with the SA/profile - create a new one */
-      rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0,
-                           &a->local_ip, &a->remote_ip, 0,
+      rv = ipip_add_tunnel (IPIP_TRANSPORT_IP4, ~0, &ip_addr_46 (&a->local_ip),
+                           &ip_addr_46 (&a->remote_ip), 0,
                            TUNNEL_ENCAP_DECAP_FLAG_NONE, IP_DSCP_CS0,
                            TUNNEL_MODE_P2P, &sw_if_index);
 
@@ -1894,24 +1919,18 @@ 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, &a->local_ip,
-                             &a->remote_ip, TUNNEL_ENCAP_DECAP_FLAG_NONE,
-                             IP_DSCP_CS0, NULL, a->src_port, a->dst_port);
+  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, a->salt_local,
+                             a->src_port, a->dst_port, &tun_out, NULL);
   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);
+  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), a->salt_remote,
+    a->ipsec_over_udp_port, a->ipsec_over_udp_port, &tun_in, NULL);
   if (rv)
     goto err1;
 
@@ -1955,16 +1974,16 @@ ikev2_create_tunnel_interface (vlib_main_t * vm,
 
   if (sa->is_initiator)
     {
-      ip_address_to_46 (&sa->iaddr, &a.local_ip);
-      ip_address_to_46 (&sa->raddr, &a.remote_ip);
+      ip_address_copy (&a.local_ip, &sa->iaddr);
+      ip_address_copy (&a.remote_ip, &sa->raddr);
       proposals = child->r_proposals;
       a.local_spi = child->r_proposals[0].spi;
       a.remote_spi = child->i_proposals[0].spi;
     }
   else
     {
-      ip_address_to_46 (&sa->raddr, &a.local_ip);
-      ip_address_to_46 (&sa->iaddr, &a.remote_ip);
+      ip_address_copy (&a.local_ip, &sa->raddr);
+      ip_address_copy (&a.remote_ip, &sa->iaddr);
       proposals = child->i_proposals;
       a.local_spi = child->i_proposals[0].spi;
       a.remote_spi = child->r_proposals[0].spi;
@@ -2264,8 +2283,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);
@@ -2448,7 +2467,16 @@ 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++;
+             sa->stats.n_keepalives++;
+           }
+       }
     }
   else if (ike->exchange == IKEV2_EXCHANGE_CREATE_CHILD_SA)
     {
@@ -2472,35 +2500,38 @@ ikev2_generate_message (vlib_buffer_t * b, ikev2_sa_t * sa,
 
          vec_free (data);
        }
+      else if (vec_len (sa->rekey) > 0)
+       {
+         ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
+         ikev2_payload_add_nonce (chain, sa->r_nonce);
+         ikev2_payload_add_ts (chain, sa->rekey[0].tsi, IKEV2_PAYLOAD_TSI);
+         ikev2_payload_add_ts (chain, sa->rekey[0].tsr, IKEV2_PAYLOAD_TSR);
+         vec_del1 (sa->rekey, 0);
+       }
+      else if (vec_len (sa->new_child) > 0)
+       {
+         ikev2_payload_add_sa (chain, sa->new_child[0].r_proposal);
+         ikev2_payload_add_nonce (chain, sa->r_nonce);
+         ikev2_payload_add_ts (chain, sa->new_child[0].tsi,
+                               IKEV2_PAYLOAD_TSI);
+         ikev2_payload_add_ts (chain, sa->new_child[0].tsr,
+                               IKEV2_PAYLOAD_TSR);
+         vec_del1 (sa->new_child, 0);
+       }
+      else if (sa->unsupported_cp)
+       {
+         u8 *data = vec_new (u8, 1);
+
+         data[0] = sa->unsupported_cp;
+         ikev2_payload_add_notify (
+           chain, IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD, data);
+         vec_free (data);
+         sa->unsupported_cp = 0;
+       }
       else
        {
-         if (vec_len (sa->rekey) > 0)
-           {
-             ikev2_payload_add_sa (chain, sa->rekey[0].r_proposal);
-             ikev2_payload_add_nonce (chain, sa->r_nonce);
-             ikev2_payload_add_ts (chain, sa->rekey[0].tsi,
-                                   IKEV2_PAYLOAD_TSI);
-             ikev2_payload_add_ts (chain, sa->rekey[0].tsr,
-                                   IKEV2_PAYLOAD_TSR);
-             vec_del1 (sa->rekey, 0);
-           }
-         else if (sa->unsupported_cp)
-           {
-             u8 *data = vec_new (u8, 1);
-
-             data[0] = sa->unsupported_cp;
-             ikev2_payload_add_notify (chain,
-                                       IKEV2_NOTIFY_MSG_UNSUPPORTED_CRITICAL_PAYLOAD,
-                                       data);
-             vec_free (data);
-             sa->unsupported_cp = 0;
-           }
-         else
-           {
-             ikev2_payload_add_notify (chain,
-                                       IKEV2_NOTIFY_MSG_NO_ADDITIONAL_SAS,
-                                       0);
-           }
+         ikev2_payload_add_notify (chain, IKEV2_NOTIFY_MSG_NO_ADDITIONAL_SAS,
+                                   0);
        }
     }
 
@@ -2631,6 +2662,7 @@ ikev2_retransmit_sa_init_one (ikev2_sa_t * sa, ike_header_t * ike,
          /* req is retransmit */
          if (sa->state == IKEV2_STATE_SA_INIT)
            {
+             sa->stats.n_init_retransmit++;
              tmp = (ike_header_t *) sa->last_sa_init_res_packet_data;
              u32 slen = clib_net_to_host_u32 (tmp->length);
              ike->ispi = tmp->ispi;
@@ -2708,6 +2740,7 @@ ikev2_retransmit_resp (ikev2_sa_t * sa, ike_header_t * ike)
   /* retransmitted req */
   if (msg_id == sa->last_msg_id)
     {
+      sa->stats.n_retransmit++;
       ike_header_t *tmp = (ike_header_t *) sa->last_res_packet_data;
       u32 slen = clib_net_to_host_u32 (tmp->length);
       ike->ispi = tmp->ispi;
@@ -2792,8 +2825,8 @@ 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 *iaddr,
-                     const void *raddr, const int af)
+ikev2_set_ip_address (ikev2_sa_t *sa, const void *iaddr, const void *raddr,
+                     const ip_address_family_t af)
 {
   ip_address_set (&sa->raddr, raddr, af);
   ip_address_set (&sa->iaddr, iaddr, af);
@@ -2833,18 +2866,32 @@ ikev2_generate_sa_init_data_and_log (ikev2_sa_t * sa)
     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_INIT_SA_REQ,
+                              s->n_sa_init_req);
+  vlib_node_increment_counter (vm, node_index, IKEV2_ERROR_IKE_AUTH_REQ,
+                              s->n_sa_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;
@@ -2862,15 +2909,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;
 
-      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])
+      if (natt)
        {
          u8 *ptr = vlib_buffer_get_current (b0);
          ip40 = (ip4_header_t *) ptr;
@@ -2903,12 +2944,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)
@@ -2932,6 +2973,8 @@ ikev2_node_internal (vlib_main_t * vm,
 
          if (ike_hdr_is_initiator (ike0))
            {
+             sa0->stats.n_sa_init_req++;
+             stats->n_sa_init_req++;
              if (ike0->rspi == 0)
                {
                  if (is_ip4)
@@ -2958,7 +3001,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,
@@ -2977,7 +3022,8 @@ ikev2_node_internal (vlib_main_t * vm,
                      || 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,
@@ -3028,8 +3074,8 @@ ikev2_node_internal (vlib_main_t * vm,
                          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);
+                         slen = ikev2_generate_message (b0, sa0, ike0, 0,
+                                                        udp0, stats);
                          if (~0 == slen)
                            vlib_node_increment_counter (vm,
                                                         node->node_index,
@@ -3100,8 +3146,12 @@ ikev2_node_internal (vlib_main_t * vm,
                }
              else
                {
+                 sa0->stats.n_sa_auth_req++;
+                 stats->n_sa_auth_req++;
                  ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
-                 slen = ikev2_generate_message (b0, sa0, ike0, 0, udp0);
+                 sa0->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,
                                                 IKEV2_ERROR_NO_BUFF_SPACE,
@@ -3173,7 +3223,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,
@@ -3233,14 +3284,38 @@ ikev2_node_internal (vlib_main_t * vm,
                    }
                  else
                    {
+                     stats->n_rekey_req++;
+                     sa0->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,
                                                     1);
                    }
                }
+             else if (sa0->new_child)
+               {
+                 ikev2_child_sa_t *c;
+                 vec_add2 (sa0->childs, c, 1);
+                 memset (c, 0, sizeof (*c));
+                 c->r_proposals = sa0->new_child[0].r_proposal;
+                 c->i_proposals = sa0->new_child[0].i_proposal;
+                 c->tsi = sa0->new_child[0].tsi;
+                 c->tsr = sa0->new_child[0].tsr;
+                 ikev2_create_tunnel_interface (vm, sa0, c, p[0],
+                                                c - sa0->childs, 0);
+                 if (ike_hdr_is_request (ike0))
+                   {
+                     ike0->flags = IKEV2_HDR_FLAG_RESPONSE;
+                     slen =
+                       ikev2_generate_message (b0, sa0, ike0, 0, udp0, 0);
+                     if (~0 == slen)
+                       vlib_node_increment_counter (
+                         vm, node->node_index, IKEV2_ERROR_NO_BUFF_SPACE, 1);
+                   }
+               }
            }
        }
       else
@@ -3271,13 +3346,13 @@ ikev2_node_internal (vlib_main_t * vm,
              if (udp0->dst_port == clib_net_to_host_u16 (IKEV2_PORT_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;
@@ -3323,6 +3398,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);
@@ -3332,13 +3408,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* */
@@ -3349,8 +3432,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 = {
@@ -3366,8 +3466,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 = {
@@ -3703,7 +3803,7 @@ ikev2_initiate_delete_ike_sa_internal (vlib_main_t * vm,
       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);
+      len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0);
       if (~0 == len)
        return;
 
@@ -4373,7 +4473,7 @@ ikev2_delete_child_sa_internal (vlib_main_t * vm, ikev2_sa_t * sa,
   sa->del->spi = csa->i_proposals->spi;
   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);
+  len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0);
   if (~0 == len)
     return;
 
@@ -4499,7 +4599,7 @@ 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;
 
@@ -4664,9 +4764,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;
@@ -4953,7 +5053,7 @@ ikev2_send_informational_request (ikev2_sa_t * sa)
   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);
+  len = ikev2_generate_message (b0, sa, ike0, 0, 0, 0);
   if (~0 == len)
     return;