From 41afb33efe81a93ddf5879138802bf23602ccc81 Mon Sep 17 00:00:00 2001
From: Neale Ranns <nranns@cisco.com>
Date: Tue, 16 Jul 2019 06:19:35 -0700
Subject: ipsec: handle UDP keepalives

Type: feature

Change-Id: I87cc1168466f267e8c4bbec318401982f4bdf03a
Signed-off-by: Neale Ranns <nranns@cisco.com>
---
 src/vnet/ipsec/esp.h          |  6 +++-
 src/vnet/ipsec/ipsec_cli.c    | 52 +++++++++++++++++++++++++++++
 src/vnet/ipsec/ipsec_format.c | 34 +++++++++++++++----
 src/vnet/ipsec/ipsec_if.c     | 27 +++++++++------
 src/vnet/ipsec/ipsec_if.h     |  5 ++-
 src/vnet/ipsec/ipsec_if_in.c  | 76 ++++++++++++++++++++++++++++++++++++++-----
 src/vnet/ipsec/ipsec_tun.c    | 20 ++++++++----
 src/vnet/ipsec/ipsec_tun_in.c | 45 +++++++++++++++++++++----
 8 files changed, 227 insertions(+), 38 deletions(-)

(limited to 'src')

diff --git a/src/vnet/ipsec/esp.h b/src/vnet/ipsec/esp.h
index f36f52a4752..2ab7299d7c8 100644
--- a/src/vnet/ipsec/esp.h
+++ b/src/vnet/ipsec/esp.h
@@ -21,7 +21,11 @@
 
 typedef struct
 {
-  u32 spi;
+  union
+  {
+    u32 spi;
+    u8 spi_bytes[4];
+  };
   u32 seq;
   u8 data[0];
 } esp_header_t;
diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c
index a5972bbf2c1..a7a6c0c0d5b 100644
--- a/src/vnet/ipsec/ipsec_cli.c
+++ b/src/vnet/ipsec/ipsec_cli.c
@@ -942,6 +942,58 @@ VLIB_CLI_COMMAND (ipsec_tun_protect_show_node, static) =
 };
 /* *INDENT-ON* */
 
+static clib_error_t *
+ipsec_tun_protect_hash_show (vlib_main_t * vm,
+			     unformat_input_t * input,
+			     vlib_cli_command_t * cmd)
+{
+  ipsec_main_t *im = &ipsec_main;
+
+  {
+    ipsec_tun_lkup_result_t value;
+    ipsec4_tunnel_key_t key;
+
+    vlib_cli_output (vm, "IPv4:");
+
+    /* *INDENT-OFF* */
+    hash_foreach(key.as_u64, value.as_u64, im->tun4_protect_by_key,
+    ({
+      vlib_cli_output (vm, " %U", format_ipsec4_tunnel_key, &key);
+      vlib_cli_output (vm, "  tun:%d sa:%d", value.tun_index, value.sa_index);
+    }));
+    /* *INDENT-ON* */
+  }
+
+  {
+    ipsec_tun_lkup_result_t value;
+    ipsec6_tunnel_key_t *key;
+
+    vlib_cli_output (vm, "IPv6:");
+
+    /* *INDENT-OFF* */
+    hash_foreach_mem(key, value.as_u64, im->tun6_protect_by_key,
+    ({
+      vlib_cli_output (vm, " %U", format_ipsec6_tunnel_key, key);
+      vlib_cli_output (vm, "  tun:%d sa:%d", value.tun_index, value.sa_index);
+    }));
+    /* *INDENT-ON* */
+  }
+
+  return NULL;
+}
+
+/**
+ * show IPSEC tunnel protection hash tables
+ */
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (ipsec_tun_protect_hash_show_node, static) =
+{
+  .path = "show ipsec protect-hash",
+  .function = ipsec_tun_protect_hash_show,
+  .short_help =  "show ipsec protect-hash",
+};
+/* *INDENT-ON* */
+
 clib_error_t *
 ipsec_cli_init (vlib_main_t * vm)
 {
diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c
index 0d596c0c973..7a5e2584719 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -259,9 +259,7 @@ format_ipsec_sa_flags (u8 * s, va_list * args)
 {
   ipsec_sa_flags_t flags = va_arg (*args, int);
 
-  if (0)
-    ;
-#define _(v, f, str) else if (flags & IPSEC_SA_FLAG_##f) s = format(s, "%s ", str);
+#define _(v, f, str) if (flags & IPSEC_SA_FLAG_##f) s = format(s, "%s ", str);
   foreach_ipsec_sa_flags
 #undef _
     return (s);
@@ -285,10 +283,8 @@ format_ipsec_sa (u8 * s, va_list * args)
 
   sa = pool_elt_at_index (im->sad, sai);
 
-  s = format (s, "[%d] sa %d (0x%x) spi %u (0x%08x) mode %s%s protocol %s %U",
+  s = format (s, "[%d] sa %u (0x%x) spi %u (0x%08x) protocol:%s flags:[%U]",
 	      sai, sa->id, sa->id, sa->spi, sa->spi,
-	      ipsec_sa_is_set_IS_TUNNEL (sa) ? "tunnel" : "transport",
-	      ipsec_sa_is_set_IS_TUNNEL_V6 (sa) ? "-ip6" : "",
 	      sa->protocol ? "esp" : "ah", format_ipsec_sa_flags, sa->flags);
 
   if (!(flags & IPSEC_FORMAT_DETAIL))
@@ -402,6 +398,32 @@ done:
   return (s);
 }
 
+u8 *
+format_ipsec4_tunnel_key (u8 * s, va_list * args)
+{
+  ipsec4_tunnel_key_t *key = va_arg (*args, ipsec4_tunnel_key_t *);
+
+  s = format (s, "remote:%U spi:%u (0x%08x)",
+	      format_ip4_address, &key->remote_ip,
+	      clib_net_to_host_u32 (key->spi),
+	      clib_net_to_host_u32 (key->spi));
+
+  return (s);
+}
+
+u8 *
+format_ipsec6_tunnel_key (u8 * s, va_list * args)
+{
+  ipsec6_tunnel_key_t *key = va_arg (*args, ipsec6_tunnel_key_t *);
+
+  s = format (s, "remote:%U spi:%u (0x%08x)",
+	      format_ip6_address, &key->remote_ip,
+	      clib_net_to_host_u32 (key->spi),
+	      clib_net_to_host_u32 (key->spi));
+
+  return (s);
+}
+
 /*
  * fd.io coding-style-patch-verification: ON
  *
diff --git a/src/vnet/ipsec/ipsec_if.c b/src/vnet/ipsec/ipsec_if.c
index 562f40ec9ab..43997bc86c1 100644
--- a/src/vnet/ipsec/ipsec_if.c
+++ b/src/vnet/ipsec/ipsec_if.c
@@ -284,7 +284,7 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
 
   if (!is_ip6)
     {
-      key4.remote_ip = args->remote_ip.ip4.as_u32;
+      key4.remote_ip.as_u32 = args->remote_ip.ip4.as_u32;
       key4.spi = clib_host_to_net_u32 (args->remote_spi);
       p = hash_get (im->ipsec4_if_pool_index_by_key, key4.as_u64);
     }
@@ -375,8 +375,14 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
 	hash_set_mem_alloc (&im->ipsec6_if_pool_index_by_key, &key6,
 			    t - im->tunnel_interfaces);
       else
-	hash_set (im->ipsec4_if_pool_index_by_key, key4.as_u64,
-		  t - im->tunnel_interfaces);
+	{
+	  hash_set (im->ipsec4_if_pool_index_by_key, key4.as_u64,
+		    t - im->tunnel_interfaces);
+	  if (1 == hash_elts (im->ipsec4_if_pool_index_by_key))
+	    udp_register_dst_port (vlib_get_main (),
+				   UDP_DST_PORT_ipsec,
+				   ipsec4_if_input_node.index, 1);
+	}
 
       hw_if_index = vnet_register_interface (vnm, ipsec_device_class.index,
 					     t - im->tunnel_interfaces,
@@ -427,9 +433,13 @@ ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
       if (is_ip6)
 	hash_unset_mem_free (&im->ipsec6_if_pool_index_by_key, &key6);
       else
-	hash_unset (im->ipsec4_if_pool_index_by_key, key4.as_u64);
-
+	{
+	  hash_unset (im->ipsec4_if_pool_index_by_key, key4.as_u64);
+	  if (0 == hash_elts (im->ipsec4_if_pool_index_by_key))
+	    udp_unregister_dst_port (vlib_get_main (), UDP_DST_PORT_ipsec, 1);
+	}
       hash_unset (im->ipsec_if_real_dev_by_show_dev, t->show_instance);
+
       im->ipsec_if_by_sw_if_index[t->sw_if_index] = ~0;
 
       /* delete input and output SA */
@@ -506,7 +516,7 @@ ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id,
 	  ipsec4_tunnel_key_t key;
 
 	  /* unset old inbound hash entry. packets should stop arriving */
-	  key.remote_ip = old_sa->tunnel_src_addr.ip4.as_u32;
+	  key.remote_ip.as_u32 = old_sa->tunnel_src_addr.ip4.as_u32;
 	  key.spi = clib_host_to_net_u32 (old_sa->spi);
 
 	  p = hash_get (im->ipsec4_if_pool_index_by_key, key.as_u64);
@@ -515,7 +525,7 @@ ipsec_set_interface_sa (vnet_main_t * vnm, u32 hw_if_index, u32 sa_id,
 
 	  /* set new inbound SA, then set new hash entry */
 	  t->input_sa_index = sa_index;
-	  key.remote_ip = sa->tunnel_src_addr.ip4.as_u32;
+	  key.remote_ip.as_u32 = sa->tunnel_src_addr.ip4.as_u32;
 	  key.spi = clib_host_to_net_u32 (sa->spi);
 
 	  hash_set (im->ipsec4_if_pool_index_by_key, key.as_u64,
@@ -572,9 +582,6 @@ ipsec_tunnel_if_init (vlib_main_t * vm)
 						     sizeof (uword));
   im->ipsec_if_real_dev_by_show_dev = hash_create (0, sizeof (uword));
 
-  udp_register_dst_port (vm, UDP_DST_PORT_ipsec, ipsec4_if_input_node.index,
-			 1);
-
   /* set up feature nodes to drop outbound packets with no crypto alg set */
   ipsec_add_feature ("ip4-output", "esp4-no-crypto",
 		     &im->esp4_no_crypto_tun_feature_index);
diff --git a/src/vnet/ipsec/ipsec_if.h b/src/vnet/ipsec/ipsec_if.h
index 042dddee880..ecaf3c94205 100644
--- a/src/vnet/ipsec/ipsec_if.h
+++ b/src/vnet/ipsec/ipsec_if.h
@@ -64,7 +64,7 @@ typedef CLIB_PACKED
    */
   union {
     struct {
-      u32 remote_ip;
+      ip4_address_t remote_ip;
       u32 spi;
     };
     u64 as_u64;
@@ -84,6 +84,9 @@ typedef CLIB_PACKED
 }) ipsec6_tunnel_key_t;
 /* *INDENT-ON* */
 
+extern u8 *format_ipsec4_tunnel_key (u8 * s, va_list * args);
+extern u8 *format_ipsec6_tunnel_key (u8 * s, va_list * args);
+
 extern int ipsec_add_del_tunnel_if_internal (vnet_main_t * vnm,
 					     ipsec_add_del_tunnel_args_t *
 					     args, u32 * sw_if_index);
diff --git a/src/vnet/ipsec/ipsec_if_in.c b/src/vnet/ipsec/ipsec_if_in.c
index fea25fe8cee..f9341d62a68 100644
--- a/src/vnet/ipsec/ipsec_if_in.c
+++ b/src/vnet/ipsec/ipsec_if_in.c
@@ -29,6 +29,8 @@
 _(RX, "good packets received")					  \
 _(DISABLED, "ipsec packets received on disabled interface")       \
 _(NO_TUNNEL, "no matching tunnel")                                \
+_(NAT_KEEPALIVE, "NAT Keepalive")                                 \
+_(TOO_SHORT, "Too Short")                                         \
 _(SPI_0, "SPI 0")
 
 static char *ipsec_if_input_error_strings[] = {
@@ -48,7 +50,12 @@ typedef enum
 
 typedef struct
 {
-  u32 spi;
+  union
+  {
+    ipsec4_tunnel_key_t key4;
+    ipsec6_tunnel_key_t key6;
+  };
+  u8 is_ip6;
   u32 seq;
 } ipsec_if_input_trace_t;
 
@@ -59,7 +66,12 @@ format_ipsec_if_input_trace (u8 * s, va_list * args)
   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
   ipsec_if_input_trace_t *t = va_arg (*args, ipsec_if_input_trace_t *);
 
-  s = format (s, "IPSec: spi %u (0x%08x) seq %u", t->spi, t->spi, t->seq);
+  if (t->is_ip6)
+    s = format (s, "IPSec: %U seq %u",
+		format_ipsec6_tunnel_key, &t->key6, t->seq);
+  else
+    s = format (s, "IPSec: %U seq %u",
+		format_ipsec4_tunnel_key, &t->key4, t->seq);
   return s;
 }
 
@@ -215,6 +227,19 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       len0 = vlib_buffer_length_in_chain (vm, b[0]);
       len1 = vlib_buffer_length_in_chain (vm, b[1]);
 
+      /* If the packet is too short to contain an SPI, then maybe
+       * it's a keep alive */
+      if (len0 < sizeof (esp_header_t))
+	{
+	  if (esp0->spi_bytes[0] == 0xff)
+	    b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_NAT_KEEPALIVE];
+	  else
+	    b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_TOO_SHORT];
+
+	  next[0] = IPSEC_INPUT_NEXT_DROP;
+	  goto pkt1;
+	}
+
       if (is_ip6)
 	{
 	  key60.remote_ip = ip60->src_address;
@@ -245,7 +270,7 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	}
       else			/* !is_ip6 */
 	{
-	  key40.remote_ip = ip40->src_address.as_u32;
+	  key40.remote_ip = ip40->src_address;
 	  key40.spi = esp0->spi;
 
 	  if (key40.as_u64 == last_key4.as_u64)
@@ -312,6 +337,18 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	}
 
     pkt1:
+
+      if (len1 < sizeof (esp_header_t))
+	{
+	  if (esp0->spi_bytes[0] == 0xff)
+	    b[1]->error = node->errors[IPSEC_IF_INPUT_ERROR_NAT_KEEPALIVE];
+	  else
+	    b[1]->error = node->errors[IPSEC_IF_INPUT_ERROR_TOO_SHORT];
+
+	  next[1] = IPSEC_INPUT_NEXT_DROP;
+	  goto trace1;
+	}
+
       if (is_ip6)
 	{
 	  key61.remote_ip = ip61->src_address;
@@ -342,7 +379,7 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	}
       else			/* !is_ip6 */
 	{
-	  key41.remote_ip = ip41->src_address.as_u32;
+	  key41.remote_ip = ip41->src_address;
 	  key41.spi = esp1->spi;
 
 	  if (key41.as_u64 == last_key4.as_u64)
@@ -415,14 +452,22 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	    {
 	      ipsec_if_input_trace_t *tr =
 		vlib_add_trace (vm, node, b[0], sizeof (*tr));
-	      tr->spi = clib_host_to_net_u32 (esp0->spi);
+	      if (is_ip6)
+		clib_memcpy (&tr->key6, &key60, sizeof (tr->key6));
+	      else
+		clib_memcpy (&tr->key4, &key40, sizeof (tr->key4));
+	      tr->is_ip6 = is_ip6;
 	      tr->seq = clib_host_to_net_u32 (esp0->seq);
 	    }
 	  if (b[1]->flags & VLIB_BUFFER_IS_TRACED)
 	    {
 	      ipsec_if_input_trace_t *tr =
 		vlib_add_trace (vm, node, b[1], sizeof (*tr));
-	      tr->spi = clib_host_to_net_u32 (esp1->spi);
+	      if (is_ip6)
+		clib_memcpy (&tr->key6, &key61, sizeof (tr->key6));
+	      else
+		clib_memcpy (&tr->key4, &key41, sizeof (tr->key4));
+	      tr->is_ip6 = is_ip6;
 	      tr->seq = clib_host_to_net_u32 (esp1->seq);
 	    }
 	}
@@ -477,6 +522,17 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       vlib_buffer_advance (b[0], buf_adv0);
       len0 = vlib_buffer_length_in_chain (vm, b[0]);
 
+      if (len0 < sizeof (esp_header_t))
+	{
+	  if (esp0->spi_bytes[0] == 0xff)
+	    b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_NAT_KEEPALIVE];
+	  else
+	    b[0]->error = node->errors[IPSEC_IF_INPUT_ERROR_TOO_SHORT];
+
+	  next[0] = IPSEC_INPUT_NEXT_DROP;
+	  goto trace00;
+	}
+
       if (is_ip6)
 	{
 	  key60.remote_ip = ip60->src_address;
@@ -507,7 +563,7 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	}
       else			/* !is_ip6 */
 	{
-	  key40.remote_ip = ip40->src_address.as_u32;
+	  key40.remote_ip = ip40->src_address;
 	  key40.spi = esp0->spi;
 
 	  if (key40.as_u64 == last_key4.as_u64)
@@ -580,7 +636,11 @@ ipsec_if_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	    {
 	      ipsec_if_input_trace_t *tr =
 		vlib_add_trace (vm, node, b[0], sizeof (*tr));
-	      tr->spi = clib_host_to_net_u32 (esp0->spi);
+	      if (is_ip6)
+		clib_memcpy (&tr->key6, &key60, sizeof (tr->key6));
+	      else
+		clib_memcpy (&tr->key4, &key40, sizeof (tr->key4));
+	      tr->is_ip6 = is_ip6;
 	      tr->seq = clib_host_to_net_u32 (esp0->seq);
 	    }
 	}
diff --git a/src/vnet/ipsec/ipsec_tun.c b/src/vnet/ipsec/ipsec_tun.c
index 46980df101b..859fab8899e 100644
--- a/src/vnet/ipsec/ipsec_tun.c
+++ b/src/vnet/ipsec/ipsec_tun.c
@@ -98,10 +98,14 @@ ipsec_tun_protect_db_add (ipsec_main_t * im, const ipsec_tun_protect_t * itp)
       if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
         {
           ipsec4_tunnel_key_t key = {
-            .remote_ip = itp->itp_crypto.dst.ip4.as_u32,
+            .remote_ip = itp->itp_crypto.dst.ip4,
             .spi = clib_host_to_net_u32 (sa->spi),
           };
           hash_set (im->tun4_protect_by_key, key.as_u64, res.as_u64);
+          if (1 == hash_elts(im->tun4_protect_by_key))
+            udp_register_dst_port (vlib_get_main(),
+                                   UDP_DST_PORT_ipsec,
+                                   ipsec4_tun_input_node.index, 1);
         }
       else
         {
@@ -127,10 +131,14 @@ ipsec_tun_protect_db_remove (ipsec_main_t * im,
       if (ip46_address_is_ip4 (&itp->itp_crypto.dst))
         {
           ipsec4_tunnel_key_t key = {
-            .remote_ip = itp->itp_crypto.dst.ip4.as_u32,
+            .remote_ip = itp->itp_crypto.dst.ip4,
             .spi = clib_host_to_net_u32 (sa->spi),
           };
           hash_unset (im->tun4_protect_by_key, &key);
+          if (0 == hash_elts(im->tun4_protect_by_key))
+            udp_unregister_dst_port (vlib_get_main(),
+                                     UDP_DST_PORT_ipsec,
+                                     1);
         }
       else
         {
@@ -359,10 +367,10 @@ ipsec_tun_protect_del (u32 sw_if_index)
 
   pool_put (ipsec_protect_pool, itp);
 
-  /* if (0 == hash_elts (im->tun4_protect_by_key)) */
-  /*   ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */
-  /* if (0 == hash_elts (im->tun6_protect_by_key)) */
-  /*   ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP); */
+  if (0 == hash_elts (im->tun4_protect_by_key))
+    ip4_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
+  if (0 == hash_elts (im->tun6_protect_by_key))
+    ip6_unregister_protocol (IP_PROTOCOL_IPSEC_ESP);
 
   return (0);
 }
diff --git a/src/vnet/ipsec/ipsec_tun_in.c b/src/vnet/ipsec/ipsec_tun_in.c
index df6d9278303..04f7a9296ab 100644
--- a/src/vnet/ipsec/ipsec_tun_in.c
+++ b/src/vnet/ipsec/ipsec_tun_in.c
@@ -32,6 +32,8 @@
   _(DISABLED, "ipsec packets received on disabled interface")     \
   _(NO_TUNNEL, "no matching tunnel")                              \
   _(TUNNEL_MISMATCH, "SPI-tunnel mismatch")                       \
+  _(NAT_KEEPALIVE, "NAT Keepalive")                               \
+  _(TOO_SHORT, "Too Short")                                       \
   _(SPI_0, "SPI 0")
 
 static char *ipsec_tun_protect_input_error_strings[] = {
@@ -59,7 +61,12 @@ typedef enum ipsec_tun_next_t_
 
 typedef struct
 {
-  u32 spi;
+  union
+  {
+    ipsec4_tunnel_key_t key4;
+    ipsec6_tunnel_key_t key6;
+  };
+  u8 is_ip6;
   u32 seq;
 } ipsec_tun_protect_input_trace_t;
 
@@ -71,7 +78,12 @@ format_ipsec_tun_protect_input_trace (u8 * s, va_list * args)
   ipsec_tun_protect_input_trace_t *t =
     va_arg (*args, ipsec_tun_protect_input_trace_t *);
 
-  s = format (s, "IPSec: spi %u seq %u", t->spi, t->seq);
+  if (t->is_ip6)
+    s = format (s, "IPSec: %U seq %u",
+		format_ipsec6_tunnel_key, &t->key6, t->seq);
+  else
+    s = format (s, "IPSec: %U seq %u",
+		format_ipsec4_tunnel_key, &t->key4, t->seq);
   return s;
 }
 
@@ -160,8 +172,10 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       ip4_header_t *ip40;
       ip6_header_t *ip60;
       esp_header_t *esp0;
+      u16 buf_rewind0;
 
-      ip40 = vlib_buffer_get_current (b[0]);
+      ip40 =
+	(ip4_header_t *) (b[0]->data + vnet_buffer (b[0])->l3_hdr_offset);
 
       if (is_ip6)
 	{
@@ -178,11 +192,12 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 		(esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40) +
 				  sizeof (udp_header_t));
 	      hdr_sz0 = 0;
+	      buf_rewind0 = ip4_header_bytes (ip40) + sizeof (udp_header_t);
 	    }
 	  else
 	    {
 	      esp0 = (esp_header_t *) ((u8 *) ip40 + ip4_header_bytes (ip40));
-	      hdr_sz0 = ip4_header_bytes (ip40);
+	      buf_rewind0 = hdr_sz0 = ip4_header_bytes (ip40);
 	    }
 	}
 
@@ -191,6 +206,19 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
       vlib_buffer_advance (b[0], hdr_sz0);
       len0 = vlib_buffer_length_in_chain (vm, b[0]);
 
+      if (len0 < sizeof (esp_header_t))
+	{
+	  if (esp0->spi_bytes[0] == 0xff)
+	    b[0]->error =
+	      node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_NAT_KEEPALIVE];
+	  else
+	    b[0]->error =
+	      node->errors[IPSEC_TUN_PROTECT_INPUT_ERROR_TOO_SHORT];
+
+	  next[0] = IPSEC_INPUT_NEXT_DROP;
+	  goto trace00;
+	}
+
       if (is_ip6)
 	{
 	  key60.remote_ip = ip60->src_address;
@@ -219,7 +247,7 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	}
       else
 	{
-	  key40.remote_ip = ip40->src_address.as_u32;
+	  key40.remote_ip = ip40->src_address;
 	  key40.spi = esp0->spi;
 
 	  if (key40.as_u64 == last_key4.as_u64)
@@ -238,6 +266,7 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	      else
 		{
 		  next[0] = ipsec_ip4_if_no_tunnel (node, b[0], esp0, ip40);
+		  vlib_buffer_advance (b[0], -buf_rewind0);
 		  n_no_tunnel++;
 		  goto trace00;
 		}
@@ -342,7 +371,11 @@ ipsec_tun_protect_input_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
 	    {
 	      ipsec_tun_protect_input_trace_t *tr =
 		vlib_add_trace (vm, node, b[0], sizeof (*tr));
-	      tr->spi = clib_host_to_net_u32 (esp0->spi);
+	      if (is_ip6)
+		clib_memcpy (&tr->key6, &key60, sizeof (tr->key6));
+	      else
+		clib_memcpy (&tr->key4, &key40, sizeof (tr->key4));
+	      tr->is_ip6 = is_ip6;
 	      tr->seq = clib_host_to_net_u32 (esp0->seq);
 	    }
 	}
-- 
cgit