ip: Use .api declared error counters
[vpp.git] / src / vnet / ip / reass / ip4_full_reass.c
index 2493fae..3183560 100644 (file)
 #include <vppinfra/vec.h>
 #include <vnet/vnet.h>
 #include <vnet/ip/ip.h>
+#include <vnet/ip/ip.api_enum.h>
 #include <vppinfra/fifo.h>
 #include <vppinfra/bihash_16_8.h>
 #include <vnet/ip/reass/ip4_full_reass.h>
 #include <stddef.h>
 
 #define MSEC_PER_SEC 1000
-#define IP4_REASS_TIMEOUT_DEFAULT_MS             100
-#define IP4_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 10000        // 10 seconds default
+#define IP4_REASS_TIMEOUT_DEFAULT_MS 200
+
+/* As there are only 1024 reass context per thread, either the DDOS attacks
+ * or fractions of real timeouts, would consume these contexts quickly and
+ * running out context space and unable to perform reassembly */
+#define IP4_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS 50 // 50 ms default
 #define IP4_REASS_MAX_REASSEMBLIES_DEFAULT 1024
 #define IP4_REASS_MAX_REASSEMBLY_LENGTH_DEFAULT          3
 #define IP4_REASS_HT_LOAD_FACTOR (0.75)
@@ -155,6 +160,8 @@ typedef struct
   ip4_full_reass_t *pool;
   u32 reass_n;
   u32 id_counter;
+  // for pacing the main thread timeouts
+  u32 last_id;
   clib_spinlock_t lock;
 } ip4_full_reass_per_thread_t;
 
@@ -453,6 +460,11 @@ ip4_full_reass_drop_all (vlib_main_t *vm, vlib_node_runtime_t *node,
       next_index = reass->error_next_index;
       u32 bi = ~0;
 
+      /* record number of packets sent to custom app */
+      vlib_node_increment_counter (vm, node->node_index,
+                                  IP4_ERROR_REASS_TO_CUSTOM_APP,
+                                  vec_len (to_free));
+
       while (vec_len (to_free) > 0)
        {
          vlib_get_next_frame (vm, node, next_index, *to_next,
@@ -465,7 +477,7 @@ ip4_full_reass_drop_all (vlib_main_t *vm, vlib_node_runtime_t *node,
              if (~0 != bi)
                {
                  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
-                 if ((b->flags & VLIB_BUFFER_IS_TRACED))
+                 if (PREDICT_FALSE (b->flags & VLIB_BUFFER_IS_TRACED))
                    {
                      ip4_full_reass_add_trace (vm, node, reass, bi,
                                                RANGE_DISCARD, 0, ~0);
@@ -579,6 +591,8 @@ again:
 
       if (now > reass->last_heard + rm->timeout)
        {
+         vlib_node_increment_counter (vm, node->node_index,
+                                      IP4_ERROR_REASS_TIMEOUT, 1);
          ip4_full_reass_drop_all (vm, node, reass, n_left_to_next, to_next);
          ip4_full_reass_free (rm, rt, reass);
          reass = NULL;
@@ -825,6 +839,15 @@ ip4_full_reass_finalize (vlib_main_t * vm, vlib_node_runtime_t * node,
     }
   vnet_buffer (first_b)->ip.reass.estimated_mtu = reass->min_fragment_length;
 
+  /* Keep track of number of successfully reassembled packets and number of
+   * fragments reassembled */
+  vlib_node_increment_counter (vm, node->node_index, IP4_ERROR_REASS_SUCCESS,
+                              1);
+
+  vlib_node_increment_counter (vm, node->node_index,
+                              IP4_ERROR_REASS_FRAGMENTS_REASSEMBLED,
+                              reass->fragments_n);
+
   *error0 = IP4_ERROR_NONE;
   ip4_full_reass_free (rm, rt, reass);
   reass = NULL;
@@ -1214,6 +1237,10 @@ ip4_full_reass_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
            clib_net_to_host_u16 (ip0->length) - ip4_header_bytes (ip0);
          const u32 fragment_last = fragment_first + fragment_length - 1;
 
+         /* Keep track of received fragments */
+         vlib_node_increment_counter (vm, node->node_index,
+                                      IP4_ERROR_REASS_FRAGMENTS_RCVD, 1);
+
          if (fragment_first > fragment_last ||
              fragment_first + fragment_length > UINT16_MAX - 20 ||
              (fragment_length < 8 && // 8 is minimum frag length per RFC 791
@@ -1335,6 +1362,14 @@ ip4_full_reass_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
                  vnet_feature_next (&next0, b0);
                }
 
+             /* Increment the counter to-custom-app also as this fragment is
+              * also going to application */
+             if (CUSTOM == type)
+               {
+                 vlib_node_increment_counter (
+                   vm, node->node_index, IP4_ERROR_REASS_TO_CUSTOM_APP, 1);
+               }
+
              vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                               to_next, n_left_to_next,
                                               bi0, next0);
@@ -1353,12 +1388,6 @@ ip4_full_reass_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
   return frame->n_vectors;
 }
 
-static char *ip4_full_reass_error_strings[] = {
-#define _(sym, string) string,
-  foreach_ip4_error
-#undef _
-};
-
 VLIB_NODE_FN (ip4_full_reass_node) (vlib_main_t * vm,
                                    vlib_node_runtime_t * node,
                                    vlib_frame_t * frame)
@@ -1370,8 +1399,8 @@ VLIB_REGISTER_NODE (ip4_full_reass_node) = {
     .name = "ip4-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1392,8 +1421,8 @@ VLIB_REGISTER_NODE (ip4_local_full_reass_node) = {
     .name = "ip4-local-full-reassembly",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1416,8 +1445,8 @@ VLIB_REGISTER_NODE (ip4_full_reass_node_feature) = {
     .name = "ip4-full-reassembly-feature",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1446,8 +1475,8 @@ VLIB_REGISTER_NODE (ip4_full_reass_node_custom) = {
     .name = "ip4-full-reassembly-custom",
     .vector_size = sizeof (u32),
     .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
+    .n_errors = IP4_N_ERROR,
+    .error_counters = ip4_error_counters,
     .n_next_nodes = IP4_FULL_REASS_N_NEXT,
     .next_nodes =
         {
@@ -1671,15 +1700,38 @@ ip4_full_reass_walk_expired (vlib_main_t *vm, vlib_node_runtime_t *node,
 
          vec_reset_length (pool_indexes_to_free);
 
-         pool_foreach_index (index, rt->pool)
+         /* Pace the number of timeouts handled per thread,to avoid barrier
+          * sync issues in real world scenarios */
+
+         u32 beg = rt->last_id;
+         /* to ensure we walk at least once per sec per context */
+         u32 end =
+           beg + (IP4_REASS_MAX_REASSEMBLIES_DEFAULT *
+                    IP4_REASS_EXPIRE_WALK_INTERVAL_DEFAULT_MS / MSEC_PER_SEC +
+                  1);
+         if (end > vec_len (rt->pool))
            {
-             reass = pool_elt_at_index (rt->pool, index);
-             if (now > reass->last_heard + rm->timeout)
-               {
-                 vec_add1 (pool_indexes_to_free, index);
-               }
+             end = vec_len (rt->pool);
+             rt->last_id = 0;
+           }
+         else
+           {
+             rt->last_id = end;
            }
 
+         pool_foreach_stepping_index (index, beg, end, rt->pool)
+         {
+           reass = pool_elt_at_index (rt->pool, index);
+           if (now > reass->last_heard + rm->timeout)
+             {
+               vec_add1 (pool_indexes_to_free, index);
+             }
+         }
+
+         if (vec_len (pool_indexes_to_free))
+           vlib_node_increment_counter (vm, node->node_index,
+                                        IP4_ERROR_REASS_TIMEOUT,
+                                        vec_len (pool_indexes_to_free));
          int *i;
           vec_foreach (i, pool_indexes_to_free)
           {
@@ -1703,13 +1755,12 @@ ip4_full_reass_walk_expired (vlib_main_t *vm, vlib_node_runtime_t *node,
 }
 
 VLIB_REGISTER_NODE (ip4_full_reass_expire_node) = {
-    .function = ip4_full_reass_walk_expired,
-    .type = VLIB_NODE_TYPE_PROCESS,
-    .name = "ip4-full-reassembly-expire-walk",
-    .format_trace = format_ip4_full_reass_trace,
-    .n_errors = ARRAY_LEN (ip4_full_reass_error_strings),
-    .error_strings = ip4_full_reass_error_strings,
-
+  .function = ip4_full_reass_walk_expired,
+  .type = VLIB_NODE_TYPE_PROCESS,
+  .name = "ip4-full-reassembly-expire-walk",
+  .format_trace = format_ip4_full_reass_trace,
+  .n_errors = IP4_N_ERROR,
+  .error_counters = ip4_error_counters,
 };
 
 static u8 *