Fixup Path weights of 0 63/3863/2
authorNeale Ranns <nranns@cisco.com>
Wed, 16 Nov 2016 11:47:44 +0000 (11:47 +0000)
committerDamjan Marion <dmarion.lists@gmail.com>
Thu, 17 Nov 2016 18:46:20 +0000 (18:46 +0000)
A path wight of 0 is a meaningless value. We can either reject it and thus expect CLI/API clients to always write a non-zero value, or we can accept it and assume the intent was a weight of 1 - this fix does the latter.

Change-Id: Ide736dbbb8376f85441f5a67388d5e3acad4d34e
Signed-off-by: Neale Ranns <nranns@cisco.com>
vnet/vnet/dpo/load_balance.c
vnet/vnet/fib/fib_path.c
vnet/vnet/fib/fib_test.c

index 8abfc43..a244776 100644 (file)
@@ -375,6 +375,19 @@ ip_multipath_normalize_next_hops (load_balance_path_t * raw_next_hops,
             n_adj_left -= n;
             error += fabs (nf - n);
             nhs[i].path_weight = n;
+
+            if (0 == nhs[i].path_weight)
+            {
+                /*
+                 * when the weight skew is high (norm is small) and n == nf.
+                 * without this correction the path with a low weight would have
+                 * no represenation in the load-balanace - don't want that.
+                 * If the weight skew is high so the load-balance has many buckets
+                 * to allow it. pays ya money takes ya choice.
+                 */
+                error = n_adj;
+                break;
+            }
         }
 
         nhs[0].path_weight += n_adj_left;
@@ -565,15 +578,45 @@ load_balance_multipath_update (const dpo_id_t *dpo,
             }
             else
             {
-                /*
-                 * we are not crossing the threshold. we can write the new on the
-                 * old, whether they be inline or not.
-                 */
-                load_balance_fill_buckets(lb, nhs,
-                                          load_balance_get_buckets(lb),
-                                          n_buckets);
-                CLIB_MEMORY_BARRIER();
-                load_balance_set_n_buckets(lb, n_buckets);
+                if (n_buckets <= LB_NUM_INLINE_BUCKETS)
+                {
+                    /*
+                     * we are not crossing the threshold and it's still inline buckets.
+                     * we can write the new on the old..
+                     */
+                    load_balance_fill_buckets(lb, nhs,
+                                              load_balance_get_buckets(lb),
+                                              n_buckets);
+                    CLIB_MEMORY_BARRIER();
+                    load_balance_set_n_buckets(lb, n_buckets);
+                }
+                else
+                {
+                    /*
+                     * we are not crossing the threshold. We need a new bucket array to
+                     * hold the increased number of choices.
+                     */
+                    dpo_id_t *new_buckets, *old_buckets, *tmp_dpo;
+
+                    new_buckets = NULL;
+                    old_buckets = load_balance_get_buckets(lb);
+
+                    vec_validate_aligned(new_buckets,
+                                         n_buckets - 1,
+                                         CLIB_CACHE_LINE_BYTES);
+
+                    load_balance_fill_buckets(lb, nhs, new_buckets, n_buckets);
+                    CLIB_MEMORY_BARRIER();
+                    lb->lb_buckets = new_buckets;
+                    CLIB_MEMORY_BARRIER();
+                    load_balance_set_n_buckets(lb, n_buckets);
+
+                    vec_foreach(tmp_dpo, old_buckets)
+                    {
+                        dpo_reset(tmp_dpo);
+                    }
+                    vec_free(old_buckets);
+                }
             }
 
             /*
index 9866931..5796539 100644 (file)
@@ -934,6 +934,14 @@ fib_path_create (fib_node_index_t pl_index,
     path->fp_nh_proto = nh_proto;
     path->fp_via_fib = FIB_NODE_INDEX_INVALID;
     path->fp_weight = rpath->frp_weight;
+    if (0 == path->fp_weight)
+    {
+        /*
+         * a weight of 0 is a meaningless value. We could either reject it, and thus force
+         * clients to always use 1, or we can accept it and fixup approrpiately.
+         */
+        path->fp_weight = 1;
+    }
     path->fp_cfg_flags = flags;
     path->fp_cfg_flags |= fib_path_route_flags_to_cfg_flags(rpath);
 
index 358e912..d2216df 100644 (file)
@@ -236,6 +236,250 @@ fib_test_build_rewrite (u8 *eth_addr)
     return (rewrite);
 }
 
+typedef enum fib_test_lb_bucket_type_t_ {
+    FT_LB_LABEL_O_ADJ,
+    FT_LB_LABEL_O_LB,
+    FT_LB_O_LB,
+    FT_LB_SPECIAL,
+    FT_LB_ADJ,
+} fib_test_lb_bucket_type_t;
+
+typedef struct fib_test_lb_bucket_t_ {
+    fib_test_lb_bucket_type_t type;
+
+    union
+    {
+       struct
+       {
+           mpls_eos_bit_t eos;
+           mpls_label_t label;
+           u8 ttl;
+           adj_index_t adj;
+       } label_o_adj;
+       struct
+       {
+           mpls_eos_bit_t eos;
+           mpls_label_t label;
+           u8 ttl;
+           index_t lb;
+       } label_o_lb;
+       struct
+       {
+           index_t adj;
+       } adj;
+       struct
+       {
+           index_t lb;
+       } lb;
+       struct
+       {
+           index_t adj;
+       } special;
+    };
+} fib_test_lb_bucket_t;
+
+#define FIB_TEST_LB(_cond, _comment, _args...)                 \
+{                                                              \
+    if (!FIB_TEST_I(_cond, _comment, ##_args)) {               \
+       return (0);                                             \
+    }                                                          \
+}
+
+static int
+fib_test_validate_lb_v (const load_balance_t *lb,
+                       u16 n_buckets,
+                       va_list ap)
+{
+    const dpo_id_t *dpo;
+    int bucket;
+
+    FIB_TEST_LB((n_buckets == lb->lb_n_buckets), "n_buckets = %d", lb->lb_n_buckets);
+
+    for (bucket = 0; bucket < n_buckets; bucket++)
+    {
+       const fib_test_lb_bucket_t *exp;
+
+       exp = va_arg(ap, fib_test_lb_bucket_t*);
+       dpo = load_balance_get_bucket_i(lb, bucket);
+
+       switch (exp->type)
+       {
+       case FT_LB_LABEL_O_ADJ:
+           {
+               const mpls_label_dpo_t *mld;
+                mpls_label_t hdr;
+               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                          "bucket %d stacks on %U",
+                          bucket,
+                          format_dpo_type, dpo->dpoi_type);
+           
+               mld = mpls_label_dpo_get(dpo->dpoi_index);
+                hdr = clib_net_to_host_u32(mld->mld_hdr.label_exp_s_ttl);
+
+               FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
+                            exp->label_o_adj.label),
+                           "bucket %d stacks on label %d",
+                           bucket,
+                           exp->label_o_adj.label);
+
+               FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
+                            exp->label_o_adj.eos),
+                           "bucket %d stacks on label %d %U",
+                           bucket,
+                           exp->label_o_adj.label,
+                           format_mpls_eos_bit, exp->label_o_adj.eos);
+
+               FIB_TEST_LB((DPO_ADJACENCY_INCOMPLETE == mld->mld_dpo.dpoi_type),
+                           "bucket %d label stacks on %U",
+                           bucket,
+                           format_dpo_type, mld->mld_dpo.dpoi_type);
+
+               FIB_TEST_LB((exp->label_o_adj.adj == mld->mld_dpo.dpoi_index),
+                           "bucket %d label stacks on adj %d",
+                           bucket,
+                           exp->label_o_adj.adj);
+           }
+           break;
+       case FT_LB_LABEL_O_LB:
+           {
+               const mpls_label_dpo_t *mld;
+                mpls_label_t hdr;
+
+               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                          "bucket %d stacks on %U",
+                          bucket,
+                          format_dpo_type, dpo->dpoi_type);
+
+               mld = mpls_label_dpo_get(dpo->dpoi_index);
+                hdr = clib_net_to_host_u32(mld->mld_hdr.label_exp_s_ttl);
+
+               FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
+                            exp->label_o_lb.label),
+                           "bucket %d stacks on label %d",
+                           bucket,
+                           exp->label_o_lb.label);
+
+               FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
+                            exp->label_o_lb.eos),
+                           "bucket %d stacks on label %d %U",
+                           bucket,
+                           exp->label_o_lb.label,
+                           format_mpls_eos_bit, exp->label_o_lb.eos);
+
+               FIB_TEST_LB((DPO_LOAD_BALANCE == mld->mld_dpo.dpoi_type),
+                           "bucket %d label stacks on %U",
+                           bucket,
+                           format_dpo_type, mld->mld_dpo.dpoi_type);
+
+               FIB_TEST_LB((exp->label_o_lb.lb == mld->mld_dpo.dpoi_index),
+                           "bucket %d label stacks on LB %d",
+                           bucket,
+                           exp->label_o_lb.lb);
+           }
+           break;
+       case FT_LB_ADJ:
+           FIB_TEST_I(((DPO_ADJACENCY == dpo->dpoi_type) ||
+                       (DPO_ADJACENCY_INCOMPLETE == dpo->dpoi_type)),
+                      "bucket %d stacks on %U",
+                      bucket,
+                      format_dpo_type, dpo->dpoi_type);
+           FIB_TEST_LB((exp->adj.adj == dpo->dpoi_index),
+                       "bucket %d stacks on adj %d",
+                       bucket,
+                       exp->adj.adj);
+           break;
+       case FT_LB_O_LB:
+           FIB_TEST_I((DPO_LOAD_BALANCE == dpo->dpoi_type),
+                       "bucket %d stacks on %U",
+                       bucket,
+                       format_dpo_type, dpo->dpoi_type);
+           FIB_TEST_LB((exp->lb.lb == dpo->dpoi_index),
+                       "bucket %d stacks on lb %d",
+                       bucket,
+                       exp->lb.lb);
+           break;
+       case FT_LB_SPECIAL:
+           FIB_TEST_I((DPO_DROP == dpo->dpoi_type),
+                      "bucket %d stacks on %U",
+                      bucket,
+                      format_dpo_type, dpo->dpoi_type);
+           FIB_TEST_LB((exp->special.adj == dpo->dpoi_index),
+                       "bucket %d stacks on drop %d",
+                       bucket,
+                       exp->special.adj);
+           break;
+       }
+    }
+    return (!0);
+}
+
+static int
+fib_test_validate_entry (fib_node_index_t fei,
+                        fib_forward_chain_type_t fct,
+                        u16 n_buckets,
+                        ...)
+{
+    const load_balance_t *lb;
+    dpo_id_t dpo = DPO_INVALID;
+    fib_prefix_t pfx;
+    index_t fw_lbi;
+    u32 fib_index;
+    va_list ap;
+    int res;
+
+    va_start(ap, n_buckets);
+
+    fib_entry_get_prefix(fei, &pfx);
+    fib_index = fib_entry_get_fib_index(fei);
+    fib_entry_contribute_forwarding(fei, fct, &dpo);
+
+    FIB_TEST_LB((DPO_LOAD_BALANCE == dpo.dpoi_type),
+               "Entry links to %U",
+               format_dpo_type, dpo.dpoi_type);
+    lb = load_balance_get(dpo.dpoi_index);
+
+    res = fib_test_validate_lb_v(lb, n_buckets, ap);
+
+    /*
+     * ensure that the LB contributed by the entry is the
+     * same as the LB in the forwarding tables
+     */
+    switch (pfx.fp_proto)
+    {
+    case FIB_PROTOCOL_IP4:
+       fw_lbi = ip4_fib_forwarding_lookup(fib_index, &pfx.fp_addr.ip4);
+       break;
+    case FIB_PROTOCOL_IP6:
+       fw_lbi = ip6_fib_table_fwding_lookup(&ip6_main, fib_index, &pfx.fp_addr.ip6);
+       break;
+    case FIB_PROTOCOL_MPLS:
+       {
+           mpls_unicast_header_t hdr = {
+               .label_exp_s_ttl = 0,
+           };
+
+           vnet_mpls_uc_set_label(&hdr.label_exp_s_ttl, pfx.fp_label);
+           vnet_mpls_uc_set_s(&hdr.label_exp_s_ttl, pfx.fp_eos);
+           hdr.label_exp_s_ttl = clib_host_to_net_u32(hdr.label_exp_s_ttl);
+
+           fw_lbi = mpls_fib_table_forwarding_lookup(fib_index, &hdr);
+           break;
+       }
+    default:
+       fw_lbi = 0;
+    }
+    FIB_TEST_LB((fw_lbi == dpo.dpoi_index),
+               "Contributed LB = FW LB: %U\n %U",
+               format_load_balance, fw_lbi, 0,
+               format_load_balance, dpo.dpoi_index, 0);
+
+    dpo_reset(&dpo);
+
+    va_end(ap);
+
+    return (res);
+}
+
 static void
 fib_test_v4 (void)
 {
@@ -1148,10 +1392,387 @@ fib_test_v4 (void)
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 14, ai_12_12_12_12);
     FIB_TEST_LB_BUCKET_VIA_ADJ(&pfx_1_2_3_5_s_32, 15, ai_12_12_12_12);
 
-    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
-                                   tm->hw[0]->sw_if_index,
-                                   tm->hw[1]->sw_if_index),
-            "RPF list for 1.2.3.4/32 contains both adjs");
+    FIB_TEST(fib_test_urpf_is_equal(fei, FIB_FORW_CHAIN_TYPE_UNICAST_IP4, 2,
+                                   tm->hw[0]->sw_if_index,
+                                   tm->hw[1]->sw_if_index),
+            "RPF list for 1.2.3.4/32 contains both adjs");
+
+    /*
+     * Test UCMP with a large weight skew - this produces load-balance objects with large
+     * numbers of buckets to accommodate the skew. By updating said load-balances we are
+     * laso testing the LB in placce modify code when number of buckets is large.
+     */
+    fib_prefix_t pfx_6_6_6_6_s_32 = {
+       .fp_len = 32,
+       .fp_proto = FIB_PROTOCOL_IP4,
+       .fp_addr = {
+           /* 1.1.1.1/32 */
+           .ip4.as_u32 = clib_host_to_net_u32(0x06060606),
+       },
+    };
+    fib_test_lb_bucket_t ip_6_6_6_6_o_10_10_10_1 = {
+       .type = FT_LB_ADJ,
+       .adj = {
+           .adj = ai_01,
+       },
+    };
+    fib_test_lb_bucket_t ip_6_6_6_6_o_10_10_10_2 = {
+        .type = FT_LB_ADJ,
+        .adj = {
+            .adj = ai_02,
+        },
+    };
+    fib_test_lb_bucket_t ip_6_6_6_6_o_12_12_12_12 = {
+        .type = FT_LB_ADJ,
+        .adj = {
+            .adj = ai_12_12_12_12,
+        },
+    };
+    fib_table_entry_update_one_path(fib_index,
+                                   &pfx_6_6_6_6_s_32,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   FIB_PROTOCOL_IP4,
+                                   &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0, // invalid fib index
+                                   0,  // zero weigth
+                                   MPLS_LABEL_INVALID,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &ip_6_6_6_6_o_10_10_10_1),
+            "6.6.6.6/32 via 10.10.10.1");
+
+    fib_table_entry_path_add(fib_index,
+                             &pfx_6_6_6_6_s_32,
+                             FIB_SOURCE_API,
+                             FIB_ENTRY_FLAG_NONE,
+                             FIB_PROTOCOL_IP4,
+                             &nh_10_10_10_2,
+                             tm->hw[0]->sw_if_index,
+                             ~0, // invalid fib index
+                             100,
+                             MPLS_LABEL_INVALID,
+                             FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    64,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_1),
+            "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
+
+    fib_table_entry_path_add(fib_index,
+                             &pfx_6_6_6_6_s_32,
+                             FIB_SOURCE_API,
+                             FIB_ENTRY_FLAG_NONE,
+                             FIB_PROTOCOL_IP4,
+                             &nh_12_12_12_12,
+                             tm->hw[1]->sw_if_index,
+                             ~0, // invalid fib index
+                             100,
+                             MPLS_LABEL_INVALID,
+                             FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    128,
+                                    &ip_6_6_6_6_o_10_10_10_1,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12,
+                                    &ip_6_6_6_6_o_12_12_12_12),
+            "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
+
+    fib_table_entry_path_remove(fib_index,
+                                &pfx_6_6_6_6_s_32,
+                                FIB_SOURCE_API,
+                                FIB_PROTOCOL_IP4,
+                                &nh_12_12_12_12,
+                                tm->hw[1]->sw_if_index,
+                                ~0, // invalid fib index
+                                100,
+                                FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    64,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_2,
+                                    &ip_6_6_6_6_o_10_10_10_1),
+            "6.6.6.6/32 via 10.10.10.1 and 10.10.10.2 in 63:1 ratio");
+
+    fib_table_entry_path_remove(fib_index,
+                                &pfx_6_6_6_6_s_32,
+                                FIB_SOURCE_API,
+                                FIB_PROTOCOL_IP4,
+                                &nh_10_10_10_2,
+                                tm->hw[0]->sw_if_index,
+                                ~0, // invalid fib index
+                                100,
+                                FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup(fib_index, &pfx_6_6_6_6_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &ip_6_6_6_6_o_10_10_10_1),
+            "6.6.6.6/32 via 10.10.10.1");
+
+    fib_table_entry_delete(fib_index, &pfx_6_6_6_6_s_32, FIB_SOURCE_API);
 
     /*
      * A recursive via the two unequal cost entries
@@ -4733,250 +5354,6 @@ fib_test_ae (void)
             adj_nbr_db_size());
 }
 
-typedef enum fib_test_lb_bucket_type_t_ {
-    FT_LB_LABEL_O_ADJ,
-    FT_LB_LABEL_O_LB,
-    FT_LB_O_LB,
-    FT_LB_SPECIAL,
-    FT_LB_ADJ,
-} fib_test_lb_bucket_type_t;
-
-typedef struct fib_test_lb_bucket_t_ {
-    fib_test_lb_bucket_type_t type;
-
-    union
-    {
-       struct
-       {
-           mpls_eos_bit_t eos;
-           mpls_label_t label;
-           u8 ttl;
-           adj_index_t adj;
-       } label_o_adj;
-       struct
-       {
-           mpls_eos_bit_t eos;
-           mpls_label_t label;
-           u8 ttl;
-           index_t lb;
-       } label_o_lb;
-       struct
-       {
-           index_t adj;
-       } adj;
-       struct
-       {
-           index_t lb;
-       } lb;
-       struct
-       {
-           index_t adj;
-       } special;
-    };
-} fib_test_lb_bucket_t;
-
-#define FIB_TEST_LB(_cond, _comment, _args...)                 \
-{                                                              \
-    if (!FIB_TEST_I(_cond, _comment, ##_args)) {               \
-       return (0);                                             \
-    }                                                          \
-}
-
-static int
-fib_test_validate_lb_v (const load_balance_t *lb,
-                       u16 n_buckets,
-                       va_list ap)
-{
-    const dpo_id_t *dpo;
-    int bucket;
-
-    FIB_TEST_LB((n_buckets == lb->lb_n_buckets), "n_buckets = %d", lb->lb_n_buckets);
-
-    for (bucket = 0; bucket < n_buckets; bucket++)
-    {
-       const fib_test_lb_bucket_t *exp;
-
-       exp = va_arg(ap, fib_test_lb_bucket_t*);
-       dpo = load_balance_get_bucket_i(lb, bucket);
-
-       switch (exp->type)
-       {
-       case FT_LB_LABEL_O_ADJ:
-           {
-               const mpls_label_dpo_t *mld;
-                mpls_label_t hdr;
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
-                          "bucket %d stacks on %U",
-                          bucket,
-                          format_dpo_type, dpo->dpoi_type);
-           
-               mld = mpls_label_dpo_get(dpo->dpoi_index);
-                hdr = clib_net_to_host_u32(mld->mld_hdr.label_exp_s_ttl);
-
-               FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
-                            exp->label_o_adj.label),
-                           "bucket %d stacks on label %d",
-                           bucket,
-                           exp->label_o_adj.label);
-
-               FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
-                            exp->label_o_adj.eos),
-                           "bucket %d stacks on label %d %U",
-                           bucket,
-                           exp->label_o_adj.label,
-                           format_mpls_eos_bit, exp->label_o_adj.eos);
-
-               FIB_TEST_LB((DPO_ADJACENCY_INCOMPLETE == mld->mld_dpo.dpoi_type),
-                           "bucket %d label stacks on %U",
-                           bucket,
-                           format_dpo_type, mld->mld_dpo.dpoi_type);
-
-               FIB_TEST_LB((exp->label_o_adj.adj == mld->mld_dpo.dpoi_index),
-                           "bucket %d label stacks on adj %d",
-                           bucket,
-                           exp->label_o_adj.adj);
-           }
-           break;
-       case FT_LB_LABEL_O_LB:
-           {
-               const mpls_label_dpo_t *mld;
-                mpls_label_t hdr;
-
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
-                          "bucket %d stacks on %U",
-                          bucket,
-                          format_dpo_type, dpo->dpoi_type);
-           
-               mld = mpls_label_dpo_get(dpo->dpoi_index);
-                hdr = clib_net_to_host_u32(mld->mld_hdr.label_exp_s_ttl);
-
-               FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
-                            exp->label_o_lb.label),
-                           "bucket %d stacks on label %d",
-                           bucket,
-                           exp->label_o_lb.label);
-
-               FIB_TEST_LB((vnet_mpls_uc_get_s(hdr) ==
-                            exp->label_o_lb.eos),
-                           "bucket %d stacks on label %d %U",
-                           bucket,
-                           exp->label_o_lb.label,
-                           format_mpls_eos_bit, exp->label_o_lb.eos);
-
-               FIB_TEST_LB((DPO_LOAD_BALANCE == mld->mld_dpo.dpoi_type),
-                           "bucket %d label stacks on %U",
-                           bucket,
-                           format_dpo_type, mld->mld_dpo.dpoi_type);
-
-               FIB_TEST_LB((exp->label_o_lb.lb == mld->mld_dpo.dpoi_index),
-                           "bucket %d label stacks on LB %d",
-                           bucket,
-                           exp->label_o_lb.lb);
-           }
-           break;
-       case FT_LB_ADJ:
-           FIB_TEST_I(((DPO_ADJACENCY == dpo->dpoi_type) ||
-                       (DPO_ADJACENCY_INCOMPLETE == dpo->dpoi_type)),
-                      "bucket %d stacks on %U",
-                      bucket,
-                      format_dpo_type, dpo->dpoi_type);
-           FIB_TEST_LB((exp->adj.adj == dpo->dpoi_index),
-                       "bucket %d stacks on adj %d",
-                       bucket,
-                       exp->adj.adj);
-           break;
-       case FT_LB_O_LB:
-           FIB_TEST_I((DPO_LOAD_BALANCE == dpo->dpoi_type),
-                       "bucket %d stacks on %U",
-                       bucket,
-                       format_dpo_type, dpo->dpoi_type);
-           FIB_TEST_LB((exp->lb.lb == dpo->dpoi_index),
-                       "bucket %d stacks on lb %d",
-                       bucket,
-                       exp->lb.lb);
-           break;
-       case FT_LB_SPECIAL:
-           FIB_TEST_I((DPO_DROP == dpo->dpoi_type),
-                      "bucket %d stacks on %U",
-                      bucket,
-                      format_dpo_type, dpo->dpoi_type);
-           FIB_TEST_LB((exp->special.adj == dpo->dpoi_index),
-                       "bucket %d stacks on drop %d",
-                       bucket,
-                       exp->special.adj);
-           break;
-       }
-    }
-    return (!0);
-}
-
-static int
-fib_test_validate_entry (fib_node_index_t fei,
-                        fib_forward_chain_type_t fct,
-                        u16 n_buckets,
-                        ...)
-{
-    const load_balance_t *lb;
-    dpo_id_t dpo = DPO_INVALID;
-    fib_prefix_t pfx;
-    index_t fw_lbi;
-    u32 fib_index;
-    va_list ap;
-    int res;
-
-    va_start(ap, n_buckets);
-
-    fib_entry_get_prefix(fei, &pfx);
-    fib_index = fib_entry_get_fib_index(fei);
-    fib_entry_contribute_forwarding(fei, fct, &dpo);
-
-    FIB_TEST_LB((DPO_LOAD_BALANCE == dpo.dpoi_type),
-               "Entry links to %U",
-               format_dpo_type, dpo.dpoi_type);
-    lb = load_balance_get(dpo.dpoi_index);
-
-    res = fib_test_validate_lb_v(lb, n_buckets, ap);
-
-    /*
-     * ensure that the LB contributed by the entry is the
-     * same as the LB in the forwarding tables
-     */
-    switch (pfx.fp_proto)
-    {
-    case FIB_PROTOCOL_IP4:
-       fw_lbi = ip4_fib_forwarding_lookup(fib_index, &pfx.fp_addr.ip4);
-       break;
-    case FIB_PROTOCOL_IP6:
-       fw_lbi = ip6_fib_table_fwding_lookup(&ip6_main, fib_index, &pfx.fp_addr.ip6);
-       break;
-    case FIB_PROTOCOL_MPLS:
-       {
-           mpls_unicast_header_t hdr = {
-               .label_exp_s_ttl = 0,
-           };
-
-           vnet_mpls_uc_set_label(&hdr.label_exp_s_ttl, pfx.fp_label);
-           vnet_mpls_uc_set_s(&hdr.label_exp_s_ttl, pfx.fp_eos);
-           hdr.label_exp_s_ttl = clib_host_to_net_u32(hdr.label_exp_s_ttl);
-
-           fw_lbi = mpls_fib_table_forwarding_lookup(fib_index, &hdr);
-           break;
-       }
-    default:
-       fw_lbi = 0;
-    }
-    FIB_TEST_LB((fw_lbi == dpo.dpoi_index),
-               "Contributed LB = FW LB: %U\n %U",
-               format_load_balance, fw_lbi, 0,
-               format_load_balance, dpo.dpoi_index, 0);
-
-    dpo_reset(&dpo);
-
-    va_end(ap);
-
-    return (res);
-}
-
 /*
  * Test the recursive route route handling for GRE tunnels
  */