NAT: fix maximum out of order fragments (VPP-1399)
[vpp.git] / src / plugins / nat / nat_reass.c
old mode 100644 (file)
new mode 100755 (executable)
index 239bc70..8fd370d
@@ -19,6 +19,7 @@
 
 #include <vnet/vnet.h>
 #include <nat/nat_reass.h>
+#include <nat/nat_ipfix_logging.h>
 
 nat_reass_main_t nat_reass_main;
 
@@ -194,6 +195,27 @@ nat_ip4_reass_lookup (nat_reass_ip4_key_t * k, f64 now)
   return 0;
 }
 
+nat_reass_ip4_t *
+nat_ip4_reass_find (ip4_address_t src, ip4_address_t dst, u16 frag_id,
+                   u8 proto)
+{
+  nat_reass_main_t *srm = &nat_reass_main;
+  nat_reass_ip4_t *reass = 0;
+  nat_reass_ip4_key_t k;
+  f64 now = vlib_time_now (srm->vlib_main);
+
+  k.src.as_u32 = src.as_u32;
+  k.dst.as_u32 = dst.as_u32;
+  k.frag_id = frag_id;
+  k.proto = proto;
+
+  clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
+  reass = nat_ip4_reass_lookup (&k, now);
+  clib_spinlock_unlock_if_init (&srm->ip4_reass_lock);
+
+  return reass;
+}
+
 nat_reass_ip4_t *
 nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
                              u16 frag_id, u8 proto, u8 reset_timeout,
@@ -206,7 +228,7 @@ nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
   dlist_elt_t *oldest_elt, *elt;
   dlist_elt_t *per_reass_list_head_elt;
   u32 oldest_index, elt_index;
-  clib_bihash_kv_16_8_t kv;
+  clib_bihash_kv_16_8_t kv, value;
 
   k.src.as_u32 = src.as_u32;
   k.dst.as_u32 = dst.as_u32;
@@ -227,6 +249,13 @@ nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
                              srm->ip4_reass_head_index,
                              reass->lru_list_index);
        }
+
+      if (reass->flags && NAT_REASS_FLAG_MAX_FRAG_DROP)
+       {
+         reass = 0;
+         goto unlock;
+       }
+
       goto unlock;
     }
 
@@ -251,12 +280,18 @@ nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
       clib_dlist_addtail (srm->ip4_reass_lru_list_pool,
                          srm->ip4_reass_head_index, oldest_index);
 
-      kv.key[0] = k.as_u64[0];
-      kv.key[1] = k.as_u64[1];
-      if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 0))
+      kv.key[0] = reass->key.as_u64[0];
+      kv.key[1] = reass->key.as_u64[1];
+      if (!clib_bihash_search_16_8 (&srm->ip4_reass_hash, &kv, &value))
        {
-         reass = 0;
-         goto unlock;
+         if (value.value == (reass - srm->ip4_reass_pool))
+           {
+             if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 0))
+               {
+                 reass = 0;
+                 goto unlock;
+               }
+           }
        }
 
       nat_ip4_reass_get_frags_inline (reass, bi_to_drop);
@@ -282,7 +317,9 @@ nat_ip4_reass_find_or_create (ip4_address_t src, ip4_address_t dst,
   reass->key.as_u64[1] = kv.key[1] = k.as_u64[1];
   kv.value = reass - srm->ip4_reass_pool;
   reass->sess_index = (u32) ~ 0;
+  reass->thread_index = (u32) ~ 0;
   reass->last_heard = now;
+  reass->frag_n = 0;
 
   if (clib_bihash_add_del_16_8 (&srm->ip4_reass_hash, &kv, 1))
     {
@@ -296,14 +333,21 @@ unlock:
 }
 
 int
-nat_ip4_reass_add_fragment (nat_reass_ip4_t * reass, u32 bi)
+nat_ip4_reass_add_fragment (nat_reass_ip4_t * reass, u32 bi,
+                           u32 ** bi_to_drop)
 {
   nat_reass_main_t *srm = &nat_reass_main;
   dlist_elt_t *elt;
   u32 elt_index;
 
   if (reass->frag_n >= srm->ip4_max_frag)
-    return -1;
+    {
+      nat_ipfix_logging_max_fragments_ip4 (srm->ip4_max_frag,
+                                          &reass->key.src);
+      reass->flags |= NAT_REASS_FLAG_MAX_FRAG_DROP;
+      nat_ip4_reass_get_frags_inline (reass, bi_to_drop);
+      return -1;
+    }
 
   clib_spinlock_lock_if_init (&srm->ip4_reass_lock);
 
@@ -412,6 +456,13 @@ nat_ip6_reass_find_or_create (ip6_address_t src, ip6_address_t dst,
                              srm->ip6_reass_head_index,
                              reass->lru_list_index);
        }
+
+      if (reass->flags && NAT_REASS_FLAG_MAX_FRAG_DROP)
+       {
+         reass = 0;
+         goto unlock;
+       }
+
       goto unlock;
     }
 
@@ -488,14 +539,21 @@ unlock:
 }
 
 int
-nat_ip6_reass_add_fragment (nat_reass_ip6_t * reass, u32 bi)
+nat_ip6_reass_add_fragment (nat_reass_ip6_t * reass, u32 bi,
+                           u32 ** bi_to_drop)
 {
   nat_reass_main_t *srm = &nat_reass_main;
   dlist_elt_t *elt;
   u32 elt_index;
 
   if (reass->frag_n >= srm->ip6_max_frag)
-    return -1;
+    {
+      nat_ipfix_logging_max_fragments_ip6 (srm->ip6_max_frag,
+                                          &reass->key.src);
+      reass->flags |= NAT_REASS_FLAG_MAX_FRAG_DROP;
+      nat_ip6_reass_get_frags_inline (reass, bi_to_drop);
+      return -1;
+    }
 
   clib_spinlock_lock_if_init (&srm->ip6_reass_lock);
 
@@ -696,7 +754,7 @@ show_nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
 {
   vlib_cli_output (vm, "NAT IPv4 virtual fragmentation reassembly is %s",
                   nat_reass_is_drop_frag (0) ? "DISABLED" : "ENABLED");
-  vlib_cli_output (vm, " max-reasssemblies %u", nat_reass_get_max_reass (0));
+  vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (0));
   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (0));
   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (0));
   vlib_cli_output (vm, " reassemblies:");
@@ -704,7 +762,7 @@ show_nat_reass_command_fn (vlib_main_t * vm, unformat_input_t * input,
 
   vlib_cli_output (vm, "NAT IPv6 virtual fragmentation reassembly is %s",
                   nat_reass_is_drop_frag (1) ? "DISABLED" : "ENABLED");
-  vlib_cli_output (vm, " max-reasssemblies %u", nat_reass_get_max_reass (1));
+  vlib_cli_output (vm, " max-reassemblies %u", nat_reass_get_max_reass (1));
   vlib_cli_output (vm, " max-fragments %u", nat_reass_get_max_frag (1));
   vlib_cli_output (vm, " timeout %usec", nat_reass_get_timeout (1));
   vlib_cli_output (vm, " reassemblies:");