fib: fix accessing empty dpo pool elements
[vpp.git] / src / vnet / dpo / replicate_dpo.c
index c779516..cd753dc 100644 (file)
 #include <vnet/ip/lookup.h>
 #include <vnet/dpo/replicate_dpo.h>
 #include <vnet/dpo/drop_dpo.h>
+#include <vnet/dpo/receive_dpo.h>
 #include <vnet/adj/adj.h>
+#include <vnet/mpls/mpls_types.h>
 
-#undef REP_DEBUG
+/**
+ * the logger
+ */
+vlib_log_class_t replicate_logger;
 
-#ifdef REP_DEBUG
 #define REP_DBG(_rep, _fmt, _args...)                                   \
 {                                                                       \
-    u8* _tmp =NULL;                                                     \
-    clib_warning("rep:[%s]:" _fmt,                                      \
-                 replicate_format(replicate_get_index((_rep)),          \
-                                  0, _tmp),                             \
-                 ##_args);                                              \
-    vec_free(_tmp);                                                     \
+    vlib_log_debug(replicate_logger,                                    \
+                   "rep:[%U]:" _fmt,                                    \
+                   format_replicate,                                    \
+                   replicate_get_index(_rep),                           \
+                   REPLICATE_FORMAT_NONE,                               \
+                   ##_args);                                            \
 }
-#else
-#define REP_DBG(_p, _fmt, _args...)
-#endif
 
+#define foreach_replicate_dpo_error                       \
+_(BUFFER_ALLOCATION_FAILURE, "Buffer Allocation Failure")
+
+typedef enum {
+#define _(sym,str) REPLICATE_DPO_ERROR_##sym,
+  foreach_replicate_dpo_error
+#undef _
+  REPLICATE_DPO_N_ERROR,
+} replicate_dpo_error_t;
+
+static char * replicate_dpo_error_strings[] = {
+#define _(sym,string) string,
+  foreach_replicate_dpo_error
+#undef _
+};
 
 /**
  * Pool of all DPOs. It's not static so the DP can have fast access
@@ -43,7 +59,12 @@ replicate_t *replicate_pool;
 /**
  * The one instance of replicate main
  */
-replicate_main_t replicate_main;
+replicate_main_t replicate_main = {
+    .repm_counters = {
+        .name = "mroutes",
+        .stat_segment_name = "/net/mroute",
+    },
+};
 
 static inline index_t
 replicate_get_index (const replicate_t *rep)
@@ -70,7 +91,7 @@ replicate_alloc_i (void)
     replicate_t *rep;
 
     pool_get_aligned(replicate_pool, rep, CLIB_CACHE_LINE_BYTES);
-    memset(rep, 0, sizeof(*rep));
+    clib_memset(rep, 0, sizeof(*rep));
 
     vlib_validate_combined_counter(&(replicate_main.repm_counters),
                                    replicate_get_index(rep));
@@ -80,6 +101,23 @@ replicate_alloc_i (void)
     return (rep);
 }
 
+static u8*
+format_replicate_flags (u8 *s, va_list *args)
+{
+    int flags = va_arg (*args, int);
+
+    if (flags == REPLICATE_FLAGS_NONE)
+    {
+        s = format (s, "none");
+    }
+    else if (flags & REPLICATE_FLAGS_HAS_LOCAL)
+    {
+        s = format (s, "has-local ");
+    }
+
+    return (s);
+}
+
 static u8*
 replicate_format (index_t repi,
                   replicate_format_flags_t flags,
@@ -91,12 +129,14 @@ replicate_format (index_t repi,
     dpo_id_t *buckets;
     u32 i;
 
+    repi &= ~MPLS_IS_REPLICATE;
     rep = replicate_get(repi);
     vlib_get_combined_counter(&(replicate_main.repm_counters), repi, &to);
     buckets = replicate_get_buckets(rep);
 
     s = format(s, "%U: ", format_dpo_type, DPO_REPLICATE);
     s = format(s, "[index:%d buckets:%d ", repi, rep->rep_n_buckets);
+    s = format(s, "flags:[%U] ", format_replicate_flags, rep->rep_flags);
     s = format(s, "to:[%Ld:%Ld]]", to.packets, to.bytes);
 
     for (i = 0; i < rep->rep_n_buckets; i++)
@@ -161,6 +201,14 @@ replicate_set_bucket_i (replicate_t *rep,
                         dpo_id_t *buckets,
                         const dpo_id_t *next)
 {
+    if (dpo_is_receive(&buckets[bucket]))
+    {
+        rep->rep_flags &= ~REPLICATE_FLAGS_HAS_LOCAL;
+    }
+    if (dpo_is_receive(next))
+    {
+        rep->rep_flags |= REPLICATE_FLAGS_HAS_LOCAL;
+    }
     dpo_stack(DPO_REPLICATE, rep->rep_proto, &buckets[bucket], next);
 }
 
@@ -172,6 +220,7 @@ replicate_set_bucket (index_t repi,
     replicate_t *rep;
     dpo_id_t *buckets;
 
+    repi &= ~MPLS_IS_REPLICATE;
     rep = replicate_get(repi);
     buckets = replicate_get_buckets(rep);
 
@@ -184,11 +233,13 @@ int
 replicate_is_drop (const dpo_id_t *dpo)
 {
     replicate_t *rep;
+    index_t repi;
 
     if (DPO_REPLICATE != dpo->dpoi_type)
         return (0);
 
-    rep = replicate_get(dpo->dpoi_index);
+    repi = dpo->dpoi_index & ~MPLS_IS_REPLICATE;
+    rep = replicate_get(repi);
 
     if (1 == rep->rep_n_buckets)
     {
@@ -203,6 +254,7 @@ replicate_get_bucket (index_t repi,
 {
     replicate_t *rep;
 
+    repi &= ~MPLS_IS_REPLICATE;
     rep = replicate_get(repi);
 
     return (replicate_get_bucket_i(rep, bucket));
@@ -240,7 +292,7 @@ replicate_fill_buckets (replicate_t *rep,
                         u32 n_buckets)
 {
     load_balance_path_t * nh;
-    u16 ii, bucket;
+    u16 bucket;
 
     bucket = 0;
 
@@ -250,11 +302,8 @@ replicate_fill_buckets (replicate_t *rep,
      */
     vec_foreach (nh, nhs)
     {
-        for (ii = 0; ii < nh->path_weight; ii++)
-        {
-            ASSERT(bucket < n_buckets);
-            replicate_set_bucket_i(rep, bucket++, buckets, &nh->path_dpo);
-        }
+        ASSERT(bucket < n_buckets);
+        replicate_set_bucket_i(rep, bucket++, buckets, &nh->path_dpo);
     }
 }
 
@@ -273,9 +322,11 @@ replicate_multipath_update (const dpo_id_t *dpo,
     dpo_id_t *tmp_dpo;
     u32 ii, n_buckets;
     replicate_t *rep;
+    index_t repi;
 
     ASSERT(DPO_REPLICATE == dpo->dpoi_type);
-    rep = replicate_get(dpo->dpoi_index);
+    repi = dpo->dpoi_index & ~MPLS_IS_REPLICATE;
+    rep = replicate_get(repi);
     nhs = replicate_multipath_next_hop_fixup(next_hops,
                                              rep->rep_proto);
     n_buckets = vec_len(nhs);
@@ -468,6 +519,67 @@ replicate_lock (dpo_id_t *dpo)
     rep->rep_locks++;
 }
 
+index_t
+replicate_dup (replicate_flags_t flags,
+               index_t repi)
+{
+    replicate_t *rep, *copy;
+
+    rep = replicate_get(repi);
+
+    if (rep->rep_flags == flags ||
+        flags & REPLICATE_FLAGS_HAS_LOCAL)
+    {
+        /*
+         * we can include all the buckets from the original in the copy
+         */
+        return (repi);
+    }
+    else
+    {
+        /*
+         * caller doesn't want the local paths that the original has
+         */
+        if (rep->rep_n_buckets == 1)
+        {
+            /*
+             * original has only one bucket that is the local, so create
+             * a new one with only the drop
+             */
+            copy = replicate_create_i (1, rep->rep_proto);
+
+            replicate_set_bucket_i(copy, 0,
+                                   replicate_get_buckets(copy),
+                                   drop_dpo_get(rep->rep_proto));
+        }
+        else
+        {
+            dpo_id_t *old_buckets, *copy_buckets;
+            u16 bucket, pos;
+
+            copy = replicate_create_i(rep->rep_n_buckets - 1,
+                                      rep->rep_proto);
+
+            rep = replicate_get(repi);
+            old_buckets = replicate_get_buckets(rep);
+            copy_buckets = replicate_get_buckets(copy);
+            pos = 0;
+
+            for (bucket = 0; bucket < rep->rep_n_buckets; bucket++)
+            {
+                if (!dpo_is_receive(&old_buckets[bucket]))
+                {
+                    replicate_set_bucket_i(copy, pos, copy_buckets,
+                                           (&old_buckets[bucket]));
+                    pos++;
+                }
+            }
+        }
+    }
+
+    return (replicate_get_index(copy));
+}
+
 static void
 replicate_destroy (replicate_t *rep)
 {
@@ -555,6 +667,7 @@ void
 replicate_module_init (void)
 {
     dpo_register(DPO_REPLICATE, &rep_vft, replicate_nodes);
+    replicate_logger = vlib_log_register_class("dpo", "replicate");
 }
 
 static clib_error_t *
@@ -574,7 +687,10 @@ replicate_show (vlib_main_t * vm,
 
     if (INDEX_INVALID != repi)
     {
-        vlib_cli_output (vm, "%U", format_replicate, repi,
+           if (pool_is_free_index (replicate_pool, repi))
+               vlib_cli_output (vm, "no such index %d", repi);
+           else
+               vlib_cli_output (vm, "%U", format_replicate, repi,
                          REPLICATE_FORMAT_DETAIL);
     }
     else
@@ -610,8 +726,9 @@ replicate_inline (vlib_main_t * vm,
                   vlib_frame_t * frame)
 {
     vlib_combined_counter_main_t * cm = &replicate_main.repm_counters;
+    replicate_main_t * rm = &replicate_main;
     u32 n_left_from, * from, * to_next, next_index;
-    u32 cpu_index = os_get_cpu_number();
+    u32 thread_index = vlib_get_thread_index();
 
     from = vlib_frame_vector_args (frame);
     n_left_from = frame->n_vectors;
@@ -630,57 +747,37 @@ replicate_inline (vlib_main_t * vm,
             const replicate_t *rep0;
             vlib_buffer_t * b0, *c0;
             const dpo_id_t *dpo0;
+           u8 num_cloned;
 
             bi0 = from[0];
-            to_next[0] = bi0;
             from += 1;
-            to_next += 1;
             n_left_from -= 1;
-            n_left_to_next -= 1;
 
             b0 = vlib_get_buffer (vm, bi0);
             repi0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX];
             rep0 = replicate_get(repi0);
 
             vlib_increment_combined_counter(
-                cm, cpu_index, repi0, 1,
+                cm, thread_index, repi0, 1,
                 vlib_buffer_length_in_chain(vm, b0));
 
-            /* ship the original to the first bucket */
-            dpo0 = replicate_get_bucket_i(rep0, 0);
-            next0 = dpo0->dpoi_next_node;
-            vnet_buffer (b0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
+           vec_validate (rm->clones[thread_index], rep0->rep_n_buckets - 1);
 
-            if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
-            {
-                replicate_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
-                t->rep_index = repi0;
-                t->dpo = *dpo0;
-            }
-            vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
-                                             to_next, n_left_to_next,
-                                             bi0, next0);
+           num_cloned = vlib_buffer_clone (vm, bi0, rm->clones[thread_index],
+                                            rep0->rep_n_buckets,
+                                           VLIB_BUFFER_CLONE_HEAD_SIZE);
 
-            /* ship copies to the rest of the buckets */
-            for (bucket = 1; bucket < rep0->rep_n_buckets; bucket++)
-            {
-                /*
-                 * After the enqueue of the first buffer, and of all subsequent
-                 * buffers in this loop, it is possible that we over-flow the
-                 * frame of the to-next node. When this happens we need to 'put'
-                 * that full frame to the node and get a fresh empty one.
-                 * Note that these are macros with side effects that change
-                 * to_next & n_left_to_next
-                 */
-                if (PREDICT_FALSE(0 == n_left_to_next))
-                {
-                    vlib_put_next_frame (vm, node, next_index, n_left_to_next);
-                    vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
-                }
+           if (num_cloned != rep0->rep_n_buckets)
+             {
+               vlib_node_increment_counter
+                 (vm, node->node_index,
+                  REPLICATE_DPO_ERROR_BUFFER_ALLOCATION_FAILURE, 1);
+             }
 
-                /* Make a copy */
-                c0 = vlib_buffer_copy(vm, b0);
-                ci0 = vlib_get_buffer_index(vm, c0);
+            for (bucket = 0; bucket < num_cloned; bucket++)
+            {
+                ci0 = rm->clones[thread_index][bucket];
+                c0 = vlib_get_buffer(vm, ci0);
 
                 to_next[0] = ci0;
                 to_next += 1;
@@ -690,9 +787,13 @@ replicate_inline (vlib_main_t * vm,
                 next0 = dpo0->dpoi_next_node;
                 vnet_buffer (c0)->ip.adj_index[VLIB_TX] = dpo0->dpoi_index;
 
-                if (PREDICT_FALSE(c0->flags & VLIB_BUFFER_IS_TRACED))
+                if (PREDICT_FALSE(b0->flags & VLIB_BUFFER_IS_TRACED))
                 {
-                    replicate_trace_t *t = vlib_add_trace (vm, node, c0, sizeof (*t));
+                    replicate_trace_t *t;
+
+                    if (c0 != b0)
+                     VLIB_BUFFER_TRACE_TRAJECTORY_INIT (c0);
+                    t = vlib_add_trace (vm, node, c0, sizeof (*t));
                     t->rep_index = repi0;
                     t->dpo = *dpo0;
                 }
@@ -700,7 +801,13 @@ replicate_inline (vlib_main_t * vm,
                 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
                                                  to_next, n_left_to_next,
                                                  ci0, next0);
+               if (PREDICT_FALSE (n_left_to_next == 0))
+                 {
+                   vlib_put_next_frame (vm, node, next_index, n_left_to_next);
+                   vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
+                 }
             }
+           vec_reset_length (rm->clones[thread_index]);
         }
 
         vlib_put_next_frame (vm, node, next_index, n_left_to_next);
@@ -718,7 +825,7 @@ format_replicate_trace (u8 * s, va_list * args)
 
   s = format (s, "replicate: %d via %U",
               t->rep_index,
-              format_dpo_id, &t->dpo);
+              format_dpo_id, &t->dpo, 0);
   return s;
 }
 
@@ -731,17 +838,20 @@ ip4_replicate (vlib_main_t * vm,
 }
 
 /**
- * @brief
+ * @brief IP4 replication node
  */
 VLIB_REGISTER_NODE (ip4_replicate_node) = {
   .function = ip4_replicate,
   .name = "ip4-replicate",
   .vector_size = sizeof (u32),
 
+  .n_errors = ARRAY_LEN(replicate_dpo_error_strings),
+  .error_strings = replicate_dpo_error_strings,
+
   .format_trace = format_replicate_trace,
   .n_next_nodes = 1,
   .next_nodes = {
-      [0] = "error-drop",
+      [0] = "ip4-drop",
   },
 };
 
@@ -754,16 +864,57 @@ ip6_replicate (vlib_main_t * vm,
 }
 
 /**
- * @brief
+ * @brief IPv6 replication node
  */
 VLIB_REGISTER_NODE (ip6_replicate_node) = {
   .function = ip6_replicate,
   .name = "ip6-replicate",
   .vector_size = sizeof (u32),
 
+  .n_errors = ARRAY_LEN(replicate_dpo_error_strings),
+  .error_strings = replicate_dpo_error_strings,
+
   .format_trace = format_replicate_trace,
   .n_next_nodes = 1,
   .next_nodes = {
-      [0] = "error-drop",
+      [0] = "ip6-drop",
   },
 };
+
+static uword
+mpls_replicate (vlib_main_t * vm,
+                vlib_node_runtime_t * node,
+                vlib_frame_t * frame)
+{
+    return (replicate_inline (vm, node, frame));
+}
+
+/**
+ * @brief MPLS replication node
+ */
+VLIB_REGISTER_NODE (mpls_replicate_node) = {
+  .function = mpls_replicate,
+  .name = "mpls-replicate",
+  .vector_size = sizeof (u32),
+
+  .n_errors = ARRAY_LEN(replicate_dpo_error_strings),
+  .error_strings = replicate_dpo_error_strings,
+
+  .format_trace = format_replicate_trace,
+  .n_next_nodes = 1,
+  .next_nodes = {
+      [0] = "mpls-drop",
+  },
+};
+
+clib_error_t *
+replicate_dpo_init (vlib_main_t * vm)
+{
+  replicate_main_t * rm = &replicate_main;
+
+  vec_validate (rm->clones, vlib_num_workers());
+
+  return 0;
+}
+
+VLIB_INIT_FUNCTION (replicate_dpo_init);