ipsec: handle UDP keepalives
[vpp.git] / src / vnet / ipsec / ipsec_if_in.c
index fea25fe..f9341d6 100644 (file)
@@ -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);
            }
        }