MPLS Unifom mode
[vpp.git] / src / vnet / fib / fib_test.c
index 03c9ee7..8f7bba0 100644 (file)
@@ -13,6 +13,7 @@
  * limitations under the License.
  */
 
+#include <vnet/fib/fib_test.h>
 #include <vnet/fib/ip6_fib.h>
 #include <vnet/fib/ip4_fib.h>
 #include <vnet/fib/mpls_fib.h>
@@ -27,7 +28,8 @@
 #include <vnet/bfd/bfd_main.h>
 #include <vnet/dpo/interface_rx_dpo.h>
 #include <vnet/dpo/replicate_dpo.h>
-#include <vnet/dpo/l2_bridge_dpo.h>
+#include <vnet/dpo/dvr_dpo.h>
+#include <vnet/dpo/mpls_disposition.h>
 
 #include <vnet/mpls/mpls.h>
 
@@ -299,7 +301,8 @@ fib_test_validate_rep_v (const replicate_t *rep,
            {
                const mpls_label_dpo_t *mld;
                 mpls_label_t hdr;
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+               FIB_TEST_LB((mpls_label_dpo_get_type(MPLS_LABEL_DPO_FLAG_NONE)
+                             == dpo->dpoi_type),
                             "bucket %d stacks on %U",
                             bucket,
                             format_dpo_type, dpo->dpoi_type);
@@ -353,7 +356,7 @@ fib_test_validate_rep_v (const replicate_t *rep,
 
 int
 fib_test_validate_lb_v (const load_balance_t *lb,
-                       u16 n_buckets,
+                       int n_buckets,
                        va_list *ap)
 {
     const dpo_id_t *dpo;
@@ -373,14 +376,19 @@ fib_test_validate_lb_v (const load_balance_t *lb,
        case FT_LB_LABEL_STACK_O_ADJ:
            {
                const mpls_label_dpo_t *mld;
+                mpls_label_dpo_flags_t mf;
                 mpls_label_t hdr;
                u32 ii;
 
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                mf = ((exp->label_stack_o_adj.mode ==
+                       FIB_MPLS_LSP_MODE_UNIFORM) ?
+                      MPLS_LABEL_DPO_FLAG_UNIFORM_MODE :
+                      MPLS_LABEL_DPO_FLAG_NONE);
+               FIB_TEST_LB((mpls_label_dpo_get_type(mf) == dpo->dpoi_type),
                           "bucket %d stacks on %U",
                           bucket,
                           format_dpo_type, dpo->dpoi_type);
-           
+
                mld = mpls_label_dpo_get(dpo->dpoi_index);
 
                FIB_TEST_LB(exp->label_stack_o_adj.label_stack_size == mld->mld_n_labels,
@@ -431,7 +439,8 @@ fib_test_validate_lb_v (const load_balance_t *lb,
            {
                const mpls_label_dpo_t *mld;
                 mpls_label_t hdr;
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+               FIB_TEST_LB((mpls_label_dpo_get_type(MPLS_LABEL_DPO_FLAG_NONE)
+                             == dpo->dpoi_type),
                           "bucket %d stacks on %U",
                           bucket,
                           format_dpo_type, dpo->dpoi_type);
@@ -466,13 +475,18 @@ fib_test_validate_lb_v (const load_balance_t *lb,
        case FT_LB_LABEL_O_LB:
            {
                const mpls_label_dpo_t *mld;
+                mpls_label_dpo_flags_t mf;
                 mpls_label_t hdr;
 
-               FIB_TEST_LB((DPO_MPLS_LABEL == dpo->dpoi_type),
+                mf = ((exp->label_o_lb.mode ==
+                       FIB_MPLS_LSP_MODE_UNIFORM) ?
+                      MPLS_LABEL_DPO_FLAG_UNIFORM_MODE :
+                      MPLS_LABEL_DPO_FLAG_NONE);
+               FIB_TEST_LB((mpls_label_dpo_get_type(mf) == 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[0].label_exp_s_ttl);
 
@@ -513,6 +527,30 @@ fib_test_validate_lb_v (const load_balance_t *lb,
                        bucket,
                        exp->adj.adj);
            break;
+       case FT_LB_MPLS_DISP_PIPE_O_ADJ:
+        {
+            const mpls_disp_dpo_t *mdd;
+
+            FIB_TEST_I((DPO_MPLS_DISPOSITION_PIPE == dpo->dpoi_type),
+                      "bucket %d stacks on %U",
+                      bucket,
+                      format_dpo_type, dpo->dpoi_type);
+
+            mdd = mpls_disp_dpo_get(dpo->dpoi_index);
+
+            dpo = &mdd->mdd_dpo;
+
+           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_INTF:
            FIB_TEST_I((DPO_INTERFACE_RX == dpo->dpoi_type),
                       "bucket %d stacks on %U",
@@ -524,7 +562,7 @@ fib_test_validate_lb_v (const load_balance_t *lb,
                        exp->adj.adj);
            break;
        case FT_LB_L2:
-           FIB_TEST_I((DPO_L2_BRIDGE == dpo->dpoi_type),
+           FIB_TEST_I((DPO_DVR == dpo->dpoi_type),
                       "bucket %d stacks on %U",
                       bucket,
                       format_dpo_type, dpo->dpoi_type);
@@ -544,25 +582,70 @@ fib_test_validate_lb_v (const load_balance_t *lb,
                        dpo->dpoi_index,
                         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",
+       case FT_LB_BIER_TABLE:
+           FIB_TEST_LB((DPO_BIER_TABLE == dpo->dpoi_type),
+                        "bucket %d stacks on %U",
+                        bucket,
+                        format_dpo_type, dpo->dpoi_type);
+           FIB_TEST_LB((exp->bier.table == dpo->dpoi_index),
+                       "bucket %d stacks on lb %d",
+                       bucket,
+                       exp->bier.table);
+            break;
+       case FT_LB_BIER_FMASK:
+           FIB_TEST_LB((DPO_BIER_FMASK == dpo->dpoi_type),
+                        "bucket %d stacks on %U",
+                        bucket,
+                        format_dpo_type, dpo->dpoi_type);
+           FIB_TEST_LB((exp->bier.fmask == dpo->dpoi_index),
+                       "bucket %d stacks on lb %d",
                        bucket,
-                       exp->special.adj);
+                       exp->bier.fmask);
+            break;
+       case FT_LB_DROP:
+           FIB_TEST_LB((DPO_DROP == dpo->dpoi_type),
+                        "bucket %d stacks on %U",
+                        bucket,
+                        format_dpo_type, dpo->dpoi_type);
            break;
        }
     }
     return (!0);
 }
 
+int
+fib_test_validate_lb (const dpo_id_t *dpo,
+                     int n_buckets,
+                     ...)
+{
+    const load_balance_t *lb;
+    va_list ap;
+    int res;
+
+    va_start(ap, n_buckets);
+
+    if (FIB_TEST_I((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);
+    }
+    else
+    {
+        res = !0;
+    }
+
+    va_end(ap);
+
+    return (res);
+}
+
 int
 fib_test_validate_entry (fib_node_index_t fei,
                         fib_forward_chain_type_t fct,
-                        u16 n_buckets,
+                        int n_buckets,
                         ...)
 {
     dpo_id_t dpo = DPO_INVALID;
@@ -590,7 +673,8 @@ fib_test_validate_entry (fib_node_index_t fei,
         const load_balance_t *lb;
 
         FIB_TEST_LB((DPO_LOAD_BALANCE == dpo.dpoi_type),
-                    "Entry links to %U",
+                    "%U Entry links to %U",
+                    format_fib_prefix, &pfx,
                     format_dpo_type, dpo.dpoi_type);
 
         lb = load_balance_get(dpo.dpoi_index);
@@ -627,7 +711,7 @@ fib_test_validate_entry (fib_node_index_t fei,
                 fw_lbi = 0;
             }
             FIB_TEST_LB((fw_lbi == dpo.dpoi_index),
-                        "Contributed LB = FW LB: %U\n %U",
+                        "Contributed LB = FW LB:\n fwd:%U\n cont:%U",
                         format_load_balance, fw_lbi, 0,
                         format_load_balance, dpo.dpoi_index, 0);
         }
@@ -3543,6 +3627,49 @@ fib_test_v4 (void)
             "4.4.4.4/32 is deag in %d %U",
              lkd->lkd_fib_index,
              format_dpo_id, dpo, 0);
+    FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
+            "4.4.4.4/32 is source deag in %d %U",
+             lkd->lkd_input,
+             format_dpo_id, dpo, 0);
+
+    fib_table_entry_delete(fib_index,
+                          &pfx_4_4_4_4_s_32,
+                          FIB_SOURCE_API);
+    FIB_TEST(FIB_NODE_INDEX_INVALID ==
+            fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32),
+            "4.4.4.4/32 removed");
+    vec_free(r_paths);
+
+    /*
+     * A route deag route in a source lookup table
+     */
+    fib_table_entry_path_add(fib_index,
+                            &pfx_4_4_4_4_s_32,
+                            FIB_SOURCE_API,
+                            FIB_ENTRY_FLAG_NONE,
+                            DPO_PROTO_IP4,
+                            &zero_addr,
+                            ~0,
+                            fib_index,
+                            1,
+                            NULL,
+                            FIB_ROUTE_PATH_SOURCE_LOOKUP);
+
+    fei = fib_table_lookup_exact_match(fib_index, &pfx_4_4_4_4_s_32);
+    FIB_TEST((FIB_NODE_INDEX_INVALID != fei), "4.4.4.4/32 present");
+
+    dpo = fib_entry_contribute_ip_forwarding(fei);
+    dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
+    lkd = lookup_dpo_get(dpo->dpoi_index);
+
+    FIB_TEST((fib_index == lkd->lkd_fib_index),
+            "4.4.4.4/32 is deag in %d %U",
+             lkd->lkd_fib_index,
+             format_dpo_id, dpo, 0);
+    FIB_TEST((LOOKUP_INPUT_SRC_ADDR == lkd->lkd_input),
+            "4.4.4.4/32 is source deag in %d %U",
+             lkd->lkd_input,
+             format_dpo_id, dpo, 0);
 
     fib_table_entry_delete(fib_index,
                           &pfx_4_4_4_4_s_32,
@@ -4002,25 +4129,25 @@ fib_test_v4 (void)
              format_ip_flow_hash_config, lb->lb_hash_config);
 
     /*
-     * A route via an L2 Bridge
+     * A route via DVR DPO
      */
     fei = fib_table_entry_path_add(fib_index,
                                    &pfx_10_10_10_3_s_32,
                                    FIB_SOURCE_API,
                                    FIB_ENTRY_FLAG_NONE,
-                                   DPO_PROTO_ETHERNET,
+                                   DPO_PROTO_IP4,
                                    &zero_addr,
                                    tm->hw[0]->sw_if_index,
                                    ~0,
                                    1,
                                    NULL,
-                                   FIB_ROUTE_PATH_FLAG_NONE);
-    dpo_id_t l2_dpo = DPO_INVALID;
-    l2_bridge_dpo_add_or_lock(tm->hw[0]->sw_if_index, &l2_dpo);
+                                   FIB_ROUTE_PATH_DVR);
+    dpo_id_t dvr_dpo = DPO_INVALID;
+    dvr_dpo_add_or_lock(tm->hw[0]->sw_if_index, DPO_PROTO_IP4, &dvr_dpo);
     fib_test_lb_bucket_t ip_o_l2 = {
         .type = FT_LB_L2,
         .adj = {
-            .adj = l2_dpo.dpoi_index,
+            .adj = dvr_dpo.dpoi_index,
         },
     };
 
@@ -4032,13 +4159,13 @@ fib_test_v4 (void)
     fib_table_entry_path_remove(fib_index,
                                 &pfx_10_10_10_3_s_32,
                                 FIB_SOURCE_API,
-                                DPO_PROTO_ETHERNET,
+                                DPO_PROTO_IP4,
                                 &zero_addr,
                                 tm->hw[0]->sw_if_index,
                                 fib_index,
                                 1,
-                                FIB_ROUTE_PATH_FLAG_NONE);
-    dpo_reset(&l2_dpo);
+                                FIB_ROUTE_PATH_DVR);
+    dpo_reset(&dvr_dpo);
 
     /*
      * CLEANUP
@@ -4139,8 +4266,8 @@ fib_test_v4 (void)
             pool_elts(load_balance_map_pool));
     FIB_TEST((lb_count == pool_elts(load_balance_pool)), "LB pool size is %d",
              pool_elts(load_balance_pool));
-    FIB_TEST((0 == pool_elts(l2_bridge_dpo_pool)), "L2 DPO pool size is %d",
-             pool_elts(l2_bridge_dpo_pool));
+    FIB_TEST((0 == pool_elts(dvr_dpo_pool)), "L2 DPO pool size is %d",
+             pool_elts(dvr_dpo_pool));
 
     return 0;
 }
@@ -6217,8 +6344,10 @@ fib_test_label (void)
            .eos = MPLS_NON_EOS,
        },
     };
-    mpls_label_t *l99 = NULL;
-    vec_add1(l99, 99);
+    fib_mpls_label_t *l99 = NULL, fml99 = {
+        .fml_value = 99,
+    };
+    vec_add1(l99, fml99);
 
     fib_table_entry_update_one_path(fib_index,
                                    &pfx_1_1_1_1_s_32,
@@ -6256,8 +6385,10 @@ fib_test_label (void)
            .adj = ai_mpls_10_10_11_1,
        },
     };
-    mpls_label_t *l_imp_null = NULL;
-    vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
+    fib_mpls_label_t *l_imp_null = NULL, fml_imp_null = {
+        .fml_value =  MPLS_IETF_IMPLICIT_NULL_LABEL,
+    };
+    vec_add1(l_imp_null, fml_imp_null);
 
     fei = fib_table_entry_path_add(fib_index,
                                   &pfx_1_1_1_1_s_32,
@@ -6297,6 +6428,12 @@ fib_test_label (void)
        .fp_label = 24001,
        .fp_eos = MPLS_NON_EOS,
     };
+    fib_test_lb_bucket_t disp_o_10_10_11_1 = {
+       .type = FT_LB_MPLS_DISP_PIPE_O_ADJ,
+       .adj = {
+           .adj = ai_v4_10_10_11_1,
+       },
+    };
 
     /*
      * The EOS entry should link to both the paths,
@@ -6310,10 +6447,10 @@ fib_test_label (void)
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     2,
                                     &l99_eos_o_10_10_10_1,
-                                    &a_o_10_10_11_1),
+                                    &disp_o_10_10_11_1),
             "24001/eos LB 2 buckets via: "
             "label 99 over 10.10.10.1, "
-            "adj over 10.10.11.1");
+            "mpls disp adj over 10.10.11.1");
 
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
@@ -6336,6 +6473,13 @@ fib_test_label (void)
            .adj = ai_v4_10_10_11_2,
        },
     };
+    fib_test_lb_bucket_t disp_o_10_10_11_2 = {
+       .type = FT_LB_MPLS_DISP_PIPE_O_ADJ,
+       .adj = {
+           .adj = ai_v4_10_10_11_2,
+       },
+    };
+
 
     fei = fib_table_entry_path_add(fib_index,
                                   &pfx_1_1_1_1_s_32,
@@ -6412,24 +6556,27 @@ fib_test_label (void)
            .lb = non_eos_1_1_1_1.dpoi_index,
            .label = 1600,
            .eos = MPLS_EOS,
+            .mode = FIB_MPLS_LSP_MODE_UNIFORM,
        },
     };
-    mpls_label_t *l1600 = NULL;
-    vec_add1(l1600, 1600);
+    fib_mpls_label_t *l1600 = NULL, fml1600 = {
+        .fml_value = 1600,
+        .fml_mode = FIB_MPLS_LSP_MODE_UNIFORM,
+    };
+    vec_add1(l1600, fml1600);
 
-    fib_table_entry_update_one_path(fib_index,
-                                   &pfx_2_2_2_2_s_32,
-                                   FIB_SOURCE_API,
-                                   FIB_ENTRY_FLAG_NONE,
-                                   DPO_PROTO_IP4,
-                                   &pfx_1_1_1_1_s_32.fp_addr,
-                                   ~0,
-                                   fib_index,
-                                   1,
-                                   l1600,
-                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_entry_update_one_path(fib_index,
+                                          &pfx_2_2_2_2_s_32,
+                                          FIB_SOURCE_API,
+                                          FIB_ENTRY_FLAG_NONE,
+                                          DPO_PROTO_IP4,
+                                          &pfx_1_1_1_1_s_32.fp_addr,
+                                          ~0,
+                                          fib_index,
+                                          1,
+                                          l1600,
+                                          FIB_ROUTE_PATH_FLAG_NONE);
 
-    fei = fib_table_lookup(fib_index, &pfx_2_2_2_2_s_32);
     FIB_TEST(fib_test_validate_entry(fei, 
                                     FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
                                     1,
@@ -6484,11 +6631,11 @@ fib_test_label (void)
     FIB_TEST(fib_test_validate_entry(fei, 
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     2,
-                                    &a_o_10_10_11_1,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_2),
             "24001/eos LB 2 buckets via: "
-            "adj over 10.10.11.1, ",
-            "adj-v4 over 10.10.11.2");
+            "mpls-disp adj over 10.10.11.1, ",
+            "mpls-disp adj-v4 over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_24001_neos);
@@ -6561,20 +6708,20 @@ fib_test_label (void)
                                     &l99_eos_o_10_10_10_1,
                                     &l99_eos_o_10_10_10_1,
                                     &l99_eos_o_10_10_10_1,
-                                    &a_o_10_10_11_1,
-                                    &a_o_10_10_11_1,
-                                    &a_o_10_10_11_1,
-                                    &a_o_10_10_11_1,
-                                    &a_o_10_10_11_1,
-                                    &adj_o_10_10_11_2,
-                                    &adj_o_10_10_11_2,
-                                    &adj_o_10_10_11_2,
-                                    &adj_o_10_10_11_2,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_2,
+                                    &disp_o_10_10_11_2,
+                                    &disp_o_10_10_11_2,
+                                    &disp_o_10_10_11_2,
+                                    &disp_o_10_10_11_2),
             "24001/eos LB 16 buckets via: "
             "label 99 over 10.10.10.1, "
-            "adj over 10.10.11.1",
-            "adj-v4 over 10.10.11.2");
+            "MPLS disp adj over 10.10.11.1",
+            "MPLS disp adj-v4 over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_24001_neos);
@@ -6615,11 +6762,11 @@ fib_test_label (void)
     FIB_TEST(fib_test_validate_entry(fei, 
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     2,
-                                    &a_o_10_10_11_1,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_1,
+                                    &disp_o_10_10_11_2),
             "24001/eos LB 2 buckets via: "
-            "adj over 10.10.11.1, "
-            "adj-v4 over 10.10.11.2");
+            "MPLS disp adj over 10.10.11.1, "
+            "MPLS disp adj-v4 over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_24001_neos);
@@ -6635,13 +6782,10 @@ fib_test_label (void)
      * remove the other path with a valid label
      */
     fib_test_lb_bucket_t bucket_drop = {
-       .type = FT_LB_SPECIAL,
-       .special = {
-           .adj = DPO_PROTO_IP4,
-       },
+       .type = FT_LB_DROP,
     };
     fib_test_lb_bucket_t mpls_bucket_drop = {
-       .type = FT_LB_SPECIAL,
+       .type = FT_LB_DROP,
        .special = {
            .adj = DPO_PROTO_MPLS,
        },
@@ -6670,9 +6814,9 @@ fib_test_label (void)
     FIB_TEST(fib_test_validate_entry(fei, 
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     1,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_2),
             "24001/eos LB 1 buckets via: "
-            "adj over 10.10.11.2");
+            "MPLS disp adj over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_24001_neos);
@@ -6686,7 +6830,7 @@ fib_test_label (void)
      * add back the path with the valid label
      */
     l99 = NULL;
-    vec_add1(l99, 99);
+    vec_add1(l99, fml99);
 
     fib_table_entry_path_add(fib_index,
                             &pfx_1_1_1_1_s_32,
@@ -6716,10 +6860,10 @@ fib_test_label (void)
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     2,
                                     &l99_eos_o_10_10_10_1,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_2),
             "24001/eos LB 2 buckets via: "
             "label 99 over 10.10.10.1, "
-            "adj over 10.10.11.2");
+            "MPLS disp adj over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_24001_neos);
@@ -6761,10 +6905,10 @@ fib_test_label (void)
                                     FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                     2,
                                     &l99_eos_o_10_10_10_1,
-                                    &adj_o_10_10_11_2),
+                                    &disp_o_10_10_11_2),
             "25005/eos LB 2 buckets via: "
             "label 99 over 10.10.10.1, "
-            "adj over 10.10.11.2");
+            "MPLS disp adj over 10.10.11.2");
 
     fei = fib_table_lookup(MPLS_FIB_DEFAULT_TABLE_ID,
                           &pfx_25005_neos);
@@ -6816,8 +6960,10 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *l101 = NULL;
-    vec_add1(l101, 101);
+    fib_mpls_label_t *l101 = NULL, fml101 = {
+        .fml_value = 101,
+    };
+    vec_add1(l101, fml101);
 
     fei = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1_1_1_2_s_32,
@@ -6856,8 +7002,10 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *l1601 = NULL;
-    vec_add1(l1601, 1601);
+    fib_mpls_label_t *l1601 = NULL, fml1601 = {
+        .fml_value = 1601,
+    };
+    vec_add1(l1601, fml1601);
 
     l1600_eos_o_1_1_1_1.label_o_lb.lb = non_eos_1_1_1_1.dpoi_index;
 
@@ -6887,7 +7035,7 @@ fib_test_label (void)
      * the LB for the recursive can use an imp-null
      */
     l_imp_null = NULL;
-    vec_add1(l_imp_null, MPLS_IETF_IMPLICIT_NULL_LABEL);
+    vec_add1(l_imp_null, fml_imp_null);
 
     fei = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1_1_1_2_s_32,
@@ -7051,11 +7199,11 @@ fib_test_label (void)
            .eos = MPLS_EOS,
        },
     };
-    mpls_label_t *label_stack = NULL;
+    fib_mpls_label_t *label_stack = NULL;
     vec_validate(label_stack, 7);
     for (ii = 0; ii < 8; ii++)
     {
-       label_stack[ii] = ii + 200;
+       label_stack[ii].fml_value = ii + 200;
     }
 
     fei = fib_table_entry_update_one_path(fib_index,
@@ -8130,12 +8278,13 @@ static int
 lfib_test (void)
 {
     const mpls_label_t deag_label = 50;
+    dpo_id_t dpo = DPO_INVALID;
+    const mpls_disp_dpo_t *mdd;
     const u32 lfib_index = 0;
     const u32 fib_index = 0;
-    dpo_id_t dpo = DPO_INVALID;
+    const lookup_dpo_t *lkd;
     const dpo_id_t *dpo1;
     fib_node_index_t lfe;
-    lookup_dpo_t *lkd;
     test_main_t *tm;
     int lb_count;
     adj_index_t ai_mpls_10_10_10_1;
@@ -8202,7 +8351,6 @@ lfib_test (void)
              format_mpls_eos_bit, MPLS_EOS,
              format_dpo_proto, lkd->lkd_proto);
 
-
     /*
      * A route deag route for EOS
      */
@@ -8233,7 +8381,14 @@ lfib_test (void)
                                    FIB_FORW_CHAIN_TYPE_MPLS_EOS,
                                    &dpo);
     dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
-    lkd = lookup_dpo_get(dpo1->dpoi_index);
+    mdd = mpls_disp_dpo_get(dpo1->dpoi_index);
+
+    FIB_TEST((FIB_MPLS_LSP_MODE_PIPE == mdd->mdd_mode),
+             "%U/%U disp is pipe mode",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+
+    lkd = lookup_dpo_get(mdd->mdd_dpo.dpoi_index);
 
     FIB_TEST((fib_index == lkd->lkd_fib_index),
               "%U/%U is deag in %d %U",
@@ -8258,6 +8413,70 @@ lfib_test (void)
               "%U/%U not present",
               format_mpls_unicast_label, deag_label,
               format_mpls_eos_bit, MPLS_EOS);
+    dpo_reset(&dpo);
+
+    /*
+     * A route deag route for EOS with LSP mode uniform
+     */
+    fib_mpls_label_t *l_pops = NULL, l_pop = {
+        .fml_value = MPLS_LABEL_POP,
+        .fml_mode = FIB_MPLS_LSP_MODE_UNIFORM,
+    };
+    vec_add1(l_pops, l_pop);
+    lfe = fib_table_entry_path_add(lfib_index,
+                                  &pfx,
+                                  FIB_SOURCE_CLI,
+                                  FIB_ENTRY_FLAG_NONE,
+                                  DPO_PROTO_IP4,
+                                  &zero_addr,
+                                  ~0,
+                                  fib_index,
+                                  1,
+                                  l_pops,
+                                  FIB_ROUTE_PATH_FLAG_NONE);
+
+    FIB_TEST((lfe == fib_table_lookup(lfib_index, &pfx)),
+              "%U/%U present",
+              format_mpls_unicast_label, deag_label,
+              format_mpls_eos_bit, MPLS_EOS);
+
+    fib_entry_contribute_forwarding(lfe,
+                                   FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+                                   &dpo);
+    dpo1 = load_balance_get_bucket(dpo.dpoi_index, 0);
+    mdd = mpls_disp_dpo_get(dpo1->dpoi_index);
+
+    FIB_TEST((FIB_MPLS_LSP_MODE_UNIFORM == mdd->mdd_mode),
+             "%U/%U disp is uniform mode",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+
+    lkd = lookup_dpo_get(mdd->mdd_dpo.dpoi_index);
+
+    FIB_TEST((fib_index == lkd->lkd_fib_index),
+              "%U/%U is deag in %d %U",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS,
+             lkd->lkd_fib_index,
+             format_dpo_id, &dpo, 0);
+    FIB_TEST((LOOKUP_INPUT_DST_ADDR == lkd->lkd_input),
+             "%U/%U is dst deag",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS);
+    FIB_TEST((DPO_PROTO_IP4 == lkd->lkd_proto),
+             "%U/%U is %U dst deag",
+             format_mpls_unicast_label, deag_label,
+             format_mpls_eos_bit, MPLS_EOS,
+             format_dpo_proto, lkd->lkd_proto);
+
+    fib_table_entry_delete_index(lfe, FIB_SOURCE_CLI);
+
+    FIB_TEST((FIB_NODE_INDEX_INVALID == fib_table_lookup(lfib_index,
+                                                        &pfx)),
+              "%U/%U not present",
+              format_mpls_unicast_label, deag_label,
+              format_mpls_eos_bit, MPLS_EOS);
+    dpo_reset(&dpo);
 
     /*
      * A route deag route for non-EOS
@@ -8335,11 +8554,15 @@ lfib_test (void)
     };
     dpo_id_t neos_1200 = DPO_INVALID;
     dpo_id_t ip_1200 = DPO_INVALID;
-    mpls_label_t *l200 = NULL;
-    vec_add1(l200, 200);
-    vec_add1(l200, 300);
-    vec_add1(l200, 400);
-    vec_add1(l200, 500);
+    fib_mpls_label_t *l200 = NULL;
+    u32 ii;
+    for (ii = 0; ii < 4; ii++)
+    {
+        fib_mpls_label_t fml = {
+            .fml_value = 200 + (ii * 100),
+        };
+        vec_add1(l200, fml);
+    };
 
     lfe = fib_table_entry_update_one_path(fib_index,
                                          &pfx_1200,
@@ -8398,8 +8621,10 @@ lfib_test (void)
            .ip4.as_u32 = clib_host_to_net_u32(0x02020204),
        },
     };
-    mpls_label_t *l999 = NULL;
-    vec_add1(l999, 999);
+    fib_mpls_label_t *l999 = NULL, fml_999 = {
+        .fml_value = 999,
+    };
+    vec_add1(l999, fml_999);
     rpaths[0].frp_label_stack = l999,
 
     fib_table_entry_path_add2(fib_index,
@@ -8454,13 +8679,13 @@ lfib_test (void)
      * A recursive via a label that does not exist
      */
     fib_test_lb_bucket_t bucket_drop = {
-       .type = FT_LB_SPECIAL,
+       .type = FT_LB_DROP,
        .special = {
            .adj = DPO_PROTO_IP4,
        },
     };
     fib_test_lb_bucket_t mpls_bucket_drop = {
-       .type = FT_LB_SPECIAL,
+       .type = FT_LB_DROP,
        .special = {
            .adj = DPO_PROTO_MPLS,
        },
@@ -8565,8 +8790,10 @@ lfib_test (void)
             .adj = idpo.dpoi_index,
         },
     };
-    mpls_label_t *l3300 = NULL;
-    vec_add1(l3300, 3300);
+    fib_mpls_label_t *l3300 = NULL, fml_3300 = {
+        .fml_value = 3300,
+    };
+    vec_add1(l3300, fml_3300);
 
     lfe = fib_table_entry_update_one_path(lfib_index,
                                          &pfx_3500,
@@ -8647,6 +8874,9 @@ lfib_test (void)
                                      0, 1);
     mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API);
 
+    FIB_TEST(0 == pool_elts(mpls_disp_dpo_pool),
+            "mpls_disp_dpo resources freed %d of %d",
+             0, pool_elts(mpls_disp_dpo_pool));
     FIB_TEST(lb_count == pool_elts(load_balance_pool),
             "Load-balance resources freed %d of %d",
              lb_count, pool_elts(load_balance_pool));
@@ -8657,34 +8887,824 @@ lfib_test (void)
     return (0);
 }
 
-static clib_error_t *
-fib_test (vlib_main_t * vm, 
-         unformat_input_t * input,
-         vlib_cli_command_t * cmd_arg)
+static int
+fib_test_inherit (void)
 {
-    int res;
+    fib_node_index_t fei;
+    test_main_t *tm;
+    int n_feis;
 
-    res = 0;
-    fib_test_mk_intf(4);
+    n_feis = fib_entry_pool_size();
+    tm = &test_main;
 
-    if (unformat (input, "debug"))
-    {
-        fib_test_do_debug = 1;
-    }
+    const ip46_address_t nh_10_10_10_1 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
+    };
+    const ip46_address_t nh_10_10_10_2 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
+    };
+    const ip46_address_t nh_10_10_10_16 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a10),
+    };
+    const ip46_address_t nh_10_10_10_20 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a14),
+    };
+    const ip46_address_t nh_10_10_10_21 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a15),
+    };
+    const ip46_address_t nh_10_10_10_22 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a16),
+    };
+    const ip46_address_t nh_10_10_10_255 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0aff),
+    };
+    const ip46_address_t nh_10_10_10_0 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a00),
+    };
+    const ip46_address_t nh_10_10_0_0 = {
+       .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0000),
+    };
 
-    if (unformat (input, "ip"))
-    {
-       res += fib_test_v4();
-       res += fib_test_v6();
-    }
-    else if (unformat (input, "label"))
-    {
-       res += fib_test_label();
-    }
-    else if (unformat (input, "ae"))
-    {
-       res += fib_test_ae();
-    }
+    /*
+     * prefixes at the base of a sub-tree
+     */
+    const fib_prefix_t pfx_10_10_10_21_s_32 = {
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_21,
+    };
+    const fib_prefix_t pfx_10_10_10_22_s_32 = {
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_22,
+    };
+    const fib_prefix_t pfx_10_10_10_255_s_32 = {
+        .fp_len = 32,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_255,
+    };
+
+    fib_table_entry_special_add(0,
+                               &pfx_10_10_10_21_s_32,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+    fib_table_entry_special_add(0,
+                               &pfx_10_10_10_22_s_32,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+    fib_table_entry_special_add(0,
+                               &pfx_10_10_10_255_s_32,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+
+    /*
+     * source an entry that pushes its state down the sub-tree
+     */
+    const fib_prefix_t pfx_10_10_10_16_s_28 = {
+        .fp_len = 28,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_16,
+    };
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_16_s_28,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    /*
+     * this covering entry and all those below it should have
+     * the same forwarding information.
+     */
+    adj_index_t ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                                    VNET_LINK_IP4,
+                                                    &nh_10_10_10_1,
+                                                    tm->hw[0]->sw_if_index);
+    fib_test_lb_bucket_t adj_o_10_10_10_1 = {
+       .type = FT_LB_ADJ,
+       .adj = {
+           .adj = ai_10_10_10_1,
+       },
+    };
+
+    fei = fib_table_lookup(0, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+
+    /*
+     * remove the inherting cover - covereds go back to drop
+     */
+    fib_table_entry_delete(0, &pfx_10_10_10_16_s_28, FIB_SOURCE_API);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+
+    /*
+     * source an entry that pushes its state down the sub-tree
+     */
+    const fib_prefix_t pfx_10_10_10_0_s_24 = {
+        .fp_len = 24,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_0,
+    };
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_0_s_24,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    /*
+     * whole sub-tree now covered
+     */
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+
+    /*
+     * insert a more specific into the sub-tree - expect inheritance
+     *  this one is directly covered by the root
+     */
+    fib_table_entry_special_add(0,
+                               &pfx_10_10_10_16_s_28,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+
+    /*
+     * insert a more specific into the sub-tree - expect inheritance
+     *  this one is indirectly covered by the root
+     */
+    const fib_prefix_t pfx_10_10_10_20_s_30 = {
+        .fp_len = 30,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_10_20,
+    };
+    fib_table_entry_special_add(0,
+                               &pfx_10_10_10_20_s_30,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_20_s_30);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_20_s_30);
+
+    /*
+     * remove the prefix from the middle of the sub-tree
+     *  the inherited source will be the only one remaining - expect
+     *  it to be withdrawn and hence the prefix is removed.
+     */
+    fib_table_entry_special_remove(0,
+                                   &pfx_10_10_10_20_s_30,
+                                   FIB_SOURCE_CLI);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_20_s_30);
+    FIB_TEST((FIB_NODE_INDEX_INVALID == fei),
+             "%U gone",
+             format_fib_prefix, &pfx_10_10_10_20_s_30);
+
+    /*
+     * inheriting source is modifed - expect the modification to be present
+     *  throughout the sub-tree
+     */
+    adj_index_t ai_10_10_10_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                                    VNET_LINK_IP4,
+                                                    &nh_10_10_10_2,
+                                                    tm->hw[0]->sw_if_index);
+    fib_test_lb_bucket_t adj_o_10_10_10_2 = {
+       .type = FT_LB_ADJ,
+       .adj = {
+           .adj = ai_10_10_10_2,
+       },
+    };
+
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_0_s_24,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_2,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+
+    /*
+     * add the source that replaces inherited state.
+     * inheriting source is not the best, so it doesn't push state.
+     */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_0_s_24,
+                                   FIB_SOURCE_PLUGIN_HI,
+                                   FIB_ENTRY_FLAG_NONE,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+        fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_16_s_28);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+
+    /*
+     * withdraw the higher priority source and expect the inherited to return
+     * throughout the sub-tree
+     */
+    fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_PLUGIN_HI);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+
+    /*
+     * source a covered entry in the sub-tree with the same inherting source
+     *  - expect that it now owns the sub-tree and thus over-rides its cover
+     */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_16_s_28,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+
+    /* these two unaffected by the sub-tree change */
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+
+    /*
+     * removes the more specific, expect the /24 to now re-owns the sub-tree
+     */
+    fib_table_entry_delete(0, &pfx_10_10_10_16_s_28, FIB_SOURCE_API);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+    /*
+     * modify the /24. expect the new forwarding to be pushed down
+     */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_10_0_s_24,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+
+    /*
+     * add an entry less specific to /24. it should not own the /24's tree
+     */
+    const fib_prefix_t pfx_10_10_0_0_s_16 = {
+        .fp_len = 16,
+        .fp_proto = FIB_PROTOCOL_IP4,
+        .fp_addr = nh_10_10_0_0,
+    };
+    fib_table_entry_update_one_path(0,
+                                    &pfx_10_10_0_0_s_16,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP4,
+                                    &nh_10_10_10_2,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_21_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_16_s_28);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_21_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_22_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_22_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_255_s_32);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_255_s_32);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_0_s_24);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_1),
+            "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_10_10_10_0_s_24);
+    fei = fib_table_lookup_exact_match(0, &pfx_10_10_0_0_s_16);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                    1,
+                                    &adj_o_10_10_10_2),
+            "%U via 10.10.10.2",
+             format_fib_prefix, &pfx_10_10_0_0_s_16);
+
+    /*
+     * cleanup
+     */
+    fib_table_entry_delete(0, &pfx_10_10_10_21_s_32, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_10_10_10_22_s_32, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_10_10_10_16_s_28, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_10_10_10_255_s_32, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_10_10_10_0_s_24, FIB_SOURCE_API);
+    fib_table_entry_delete(0, &pfx_10_10_0_0_s_16, FIB_SOURCE_API);
+    adj_unlock(ai_10_10_10_1);
+    adj_unlock(ai_10_10_10_2);
+
+    /*
+     * test the v6 tree walk.
+     * a /64 that covers everytinhg. a /96 that covers one /128
+     * a second /128 covered only by the /64.
+     */
+    const fib_prefix_t pfx_2001_s_64 = {
+        .fp_len = 64,
+        .fp_proto = FIB_PROTOCOL_IP6,
+        .fp_addr = {
+            .ip6 = {
+               .as_u64 = {
+                   [0] = clib_host_to_net_u64(0x2001000000000000),
+                   [1] = clib_host_to_net_u64(0x0000000000000000),
+               },
+            },
+        },
+    };
+    const fib_prefix_t pfx_2001_1_s_96 = {
+        .fp_len = 96,
+        .fp_proto = FIB_PROTOCOL_IP6,
+        .fp_addr = {
+            .ip6 = {
+               .as_u64 = {
+                   [0] = clib_host_to_net_u64(0x2001000000000000),
+                   [1] = clib_host_to_net_u64(0x1000000000000000),
+               },
+            },
+        },
+    };
+    const fib_prefix_t pfx_2001_1_1_s_128 = {
+        .fp_len = 128,
+        .fp_proto = FIB_PROTOCOL_IP6,
+        .fp_addr = {
+            .ip6 = {
+               .as_u64 = {
+                   [0] = clib_host_to_net_u64(0x2001000000000000),
+                   [1] = clib_host_to_net_u64(0x1000000000000001),
+               },
+            },
+        },
+    };
+    const fib_prefix_t pfx_2001_0_1_s_128 = {
+        .fp_len = 128,
+        .fp_proto = FIB_PROTOCOL_IP6,
+        .fp_addr = {
+            .ip6 = {
+               .as_u64 = {
+                   [0] = clib_host_to_net_u64(0x2001000000000000),
+                   [1] = clib_host_to_net_u64(0x0000000000000001),
+               },
+            },
+        },
+    };
+    const ip46_address_t nh_3000_1 = {
+        .ip6 = {
+            .as_u64 = {
+                [0] = clib_host_to_net_u64(0x3000000000000000),
+                [1] = clib_host_to_net_u64(0x0000000000000001),
+            },
+        },
+    };
+    const ip46_address_t nh_3000_2 = {
+        .ip6 = {
+            .as_u64 = {
+                [0] = clib_host_to_net_u64(0x3000000000000000),
+                [1] = clib_host_to_net_u64(0x0000000000000002),
+            },
+        },
+    };
+    adj_index_t ai_3000_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP6,
+                                                VNET_LINK_IP6,
+                                                &nh_3000_1,
+                                                tm->hw[0]->sw_if_index);
+    adj_index_t ai_3000_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP6,
+                                                VNET_LINK_IP6,
+                                                &nh_3000_2,
+                                                tm->hw[0]->sw_if_index);
+    fib_test_lb_bucket_t adj_o_3000_1 = {
+       .type = FT_LB_ADJ,
+       .adj = {
+           .adj = ai_3000_1,
+       },
+    };
+    fib_test_lb_bucket_t adj_o_3000_2 = {
+       .type = FT_LB_ADJ,
+       .adj = {
+           .adj = ai_3000_2,
+       },
+    };
+
+    fib_table_entry_special_add(0,
+                               &pfx_2001_0_1_s_128,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+    fib_table_entry_special_add(0,
+                               &pfx_2001_1_1_s_128,
+                               FIB_SOURCE_CLI,
+                               FIB_ENTRY_FLAG_DROP);
+
+    /*
+     * /96 has inherited forwarding pushed down to its covered /128
+     */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_2001_1_s_96,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP6,
+                                    &nh_3000_1,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_1_s_96);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_1),
+            "%U via 3000::1",
+             format_fib_prefix, &pfx_2001_1_s_96);
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_1_1_s_128);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_1),
+            "%U via 3000::1",
+             format_fib_prefix, &pfx_2001_1_1_s_128);
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_0_1_s_128);
+    FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+             "%U resolves via drop",
+             format_fib_prefix, &pfx_2001_0_1_s_128);
+
+    /*
+     * /64 has inherited forwarding pushed down to all, but the /96
+     * and its sub-tree remain unaffected.
+     */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_2001_s_64,
+                                   FIB_SOURCE_API,
+                                   FIB_ENTRY_FLAG_COVERED_INHERIT,
+                                   DPO_PROTO_IP6,
+                                    &nh_3000_2,
+                                   tm->hw[0]->sw_if_index,
+                                   ~0,
+                                   1,
+                                   NULL,
+                                   FIB_ROUTE_PATH_FLAG_NONE);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_s_64);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_2),
+            "%U via 3000::2",
+             format_fib_prefix, &pfx_2001_s_64);
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_0_1_s_128);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_2),
+            "%U via 3000::1",
+             format_fib_prefix, &pfx_2001_0_1_s_128);
+
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_1_s_96);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_1),
+            "%U via 3000::1",
+             format_fib_prefix, &pfx_2001_1_s_96);
+    fei = fib_table_lookup_exact_match(0, &pfx_2001_1_1_s_128);
+    FIB_TEST(fib_test_validate_entry(fei,
+                                    FIB_FORW_CHAIN_TYPE_UNICAST_IP6,
+                                    1,
+                                    &adj_o_3000_1),
+            "%U via 3000::1",
+             format_fib_prefix, &pfx_2001_1_1_s_128);
+
+    /*
+     * Cleanup
+     */
+    fib_table_entry_delete(0, &pfx_2001_0_1_s_128, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_2001_1_1_s_128, FIB_SOURCE_CLI);
+    fib_table_entry_delete(0, &pfx_2001_s_64,      FIB_SOURCE_API);
+    fib_table_entry_delete(0, &pfx_2001_1_s_96,    FIB_SOURCE_API);
+    adj_unlock(ai_3000_1);
+    adj_unlock(ai_3000_2);
+
+    /*
+     * test no-one left behind
+     */
+    FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
+    FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
+    return (0);
+}
+
+static clib_error_t *
+fib_test (vlib_main_t * vm, 
+         unformat_input_t * input,
+         vlib_cli_command_t * cmd_arg)
+{
+    int res;
+
+    res = 0;
+    fib_test_mk_intf(4);
+
+    if (unformat (input, "debug"))
+    {
+        fib_test_do_debug = 1;
+    }
+
+    if (unformat (input, "ip"))
+    {
+       res += fib_test_v4();
+       res += fib_test_v6();
+    }
+    else if (unformat (input, "label"))
+    {
+       res += fib_test_label();
+    }
+    else if (unformat (input, "ae"))
+    {
+       res += fib_test_ae();
+    }
     else if (unformat (input, "pref"))
     {
        res += fib_test_pref();
@@ -8701,6 +9721,10 @@ fib_test (vlib_main_t * vm,
     {
        res += fib_test_bfd();
     }
+    else if (unformat (input, "inherit"))
+    {
+       res += fib_test_inherit();
+    }
     else
     {
        res += fib_test_v4();
@@ -8709,6 +9733,7 @@ fib_test (vlib_main_t * vm,
        res += fib_test_bfd();
        res += fib_test_pref();
        res += fib_test_label();
+        res += fib_test_inherit();
        res += lfib_test();
 
         /*