fib: Changes to interpose source

Type: improvement

1) stack the interpose on any path-extensions (e.g. labels) from the
next best source
2) allow more than 1 source to contribute a DPO for a given prefix

Signed-off-by: Neale Ranns <neale@graphiant.com>
Change-Id: Idc2fbb36cfbd2387081765d8af0f1fbe61612160
diff --git a/src/plugins/unittest/fib_test.c b/src/plugins/unittest/fib_test.c
index 1a2ba4a..b9b77ba 100644
--- a/src/plugins/unittest/fib_test.c
+++ b/src/plugins/unittest/fib_test.c
@@ -43,6 +43,8 @@
 
 #include <vlib/unix/plugin.h>
 
+// clang-format off
+
 /*
  * Add debugs for passing tests
  */
@@ -446,6 +448,46 @@
 			    exp->label_stack_o_adj.adj);
 	    }
 	    break;
+	case FT_LB_LABEL_CHAIN_O_ADJ:
+	    {
+		const mpls_label_dpo_t *mld = NULL;
+                mpls_label_dpo_flags_t mf;
+                mpls_label_t hdr;
+		u32 ii;
+
+                mf = ((exp->label_chain_o_adj.mode ==
+                       FIB_MPLS_LSP_MODE_UNIFORM) ?
+                      MPLS_LABEL_DPO_FLAG_UNIFORM_MODE :
+                      MPLS_LABEL_DPO_FLAG_NONE);
+
+                for (ii = 0; ii < exp->label_chain_o_adj.label_chain_size; ii++)
+                {
+                    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);
+		    FIB_TEST_LB((vnet_mpls_uc_get_label(hdr) ==
+				 exp->label_chain_o_adj.label_chain[ii]),
+				"bucket %d stacks on label %d",
+				bucket,
+				exp->label_chain_o_adj.label_chain[ii]);
+                    dpo = &mld->mld_dpo;
+		}
+
+		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_chain_o_adj.adj == mld->mld_dpo.dpoi_index),
+			    "bucket %d label stacks on adj %d",
+			    bucket,
+			    exp->label_chain_o_adj.adj);
+	    }
+	    break;
 	case FT_LB_LABEL_O_ADJ:
 	    {
 		const mpls_label_dpo_t *mld;
@@ -9667,6 +9709,82 @@
                                       &adj_o_10_10_10_3),
              "%U via 10.10.10.1",
              format_fib_prefix, &pfx_11_11_11_11_s_32);
+    dpo_reset(&interposer);
+    fib_table_entry_delete(0, &pfx_11_11_11_11_s_32, FIB_SOURCE_API);
+
+    /*
+     * add an interposer to a source with path-extensions
+     */
+    fib_mpls_label_t *l3300 = NULL, fml_3300 = {
+        .fml_value = 3300,
+    };
+    vec_add1(l3300, fml_3300);
+    fib_table_entry_update_one_path(0,
+                                    &pfx_11_11_11_11_s_32,
+                                    FIB_SOURCE_API,
+                                    FIB_ENTRY_FLAG_NONE,
+                                    DPO_PROTO_IP4,
+                                    &nh_10_10_10_3,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0,
+                                    1,
+                                    l3300,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+
+    mpls_label_dpo_create(l99,
+                          MPLS_EOS,
+                          DPO_PROTO_IP4,
+                          MPLS_LABEL_DPO_FLAG_NONE,
+                          punt_dpo_get(DPO_PROTO_MPLS),
+                          &interposer);
+
+    adj_index_t ai_mpls_10_10_10_3 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+                                                         VNET_LINK_MPLS,
+                                                         &nh_10_10_10_3,
+                                                         tm->hw[0]->sw_if_index);
+    fib_test_lb_bucket_t l3300_o_10_10_10_3 = {
+        .type = FT_LB_LABEL_O_ADJ,
+        .label_o_adj = {
+            .adj = ai_mpls_10_10_10_3,
+            .label = 3300,
+            .eos = MPLS_EOS,
+        },
+    };
+    fib_test_lb_bucket_t lchain_o_10_10_10_3 = {
+        .type = FT_LB_LABEL_CHAIN_O_ADJ,
+        .label_chain_o_adj = {
+            .adj = ai_mpls_10_10_10_3,
+            .label_chain_size = 2,
+            .label_chain = {
+                99, 3300
+            },
+            .eos = MPLS_EOS,
+        },
+    };
+
+    fei = fib_table_entry_special_dpo_add(0,
+                                          &pfx_11_11_11_11_s_32,
+                                          FIB_SOURCE_SPECIAL,
+                                          FIB_ENTRY_FLAG_INTERPOSE,
+                                          &interposer);
+
+    FIB_TEST(!fib_test_validate_entry(fei,
+                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                      1,
+                                      &lchain_o_10_10_10_3),
+             "%U via interposer & mpls on adj",
+             format_fib_prefix, &pfx_11_11_11_11_s_32);
+
+    fib_table_entry_special_remove(0,
+                                   &pfx_11_11_11_11_s_32,
+                                   FIB_SOURCE_SPECIAL);
+    FIB_TEST(!fib_test_validate_entry(fei,
+                                      FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+                                      1,
+                                      &l3300_o_10_10_10_3),
+             "%U via 10.10.10.1",
+             format_fib_prefix, &pfx_11_11_11_11_s_32);
+    adj_unlock(ai_mpls_10_10_10_3);
 
     /*
      * remove and re-add the second best API source while the interpose
@@ -9680,11 +9798,11 @@
     FIB_TEST(!fib_test_validate_entry(fei,
                                       FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
                                       1,
-                                      &l99_o_10_10_10_3),
+                                      &lchain_o_10_10_10_3),
              "%U via interposer adj",
              format_fib_prefix,&pfx_11_11_11_11_s_32);
 
-    FIB_TEST(2 == pool_elts(mpls_label_dpo_pool),
+    FIB_TEST(3 == pool_elts(mpls_label_dpo_pool),
              "MPLS label pool: %d",
              pool_elts(mpls_label_dpo_pool));
 
@@ -9756,6 +9874,18 @@
      * multiple interpose sources on the same entry. Only the high
      * priority source gets to add the interpose.
      */
+    fib_table_entry_update_one_path(0,
+                                    &pfx_11_11_11_11_s_32,
+                                    FIB_SOURCE_API,
+                                    FIB_ENTRY_FLAG_NONE,
+                                    DPO_PROTO_IP4,
+                                    &nh_10_10_10_3,
+                                    tm->hw[0]->sw_if_index,
+                                    ~0,
+                                    1,
+                                    NULL,
+                                    FIB_ROUTE_PATH_FLAG_NONE);
+
     dpo_id_t interposer2 = DPO_INVALID;
     fib_mpls_label_t *l100 = NULL, fml_100 = {
         .fml_value = 100,
@@ -9774,10 +9904,23 @@
                                           FIB_SOURCE_CLASSIFY,
                                           FIB_ENTRY_FLAG_INTERPOSE,
                                           &interposer2);
+
+    fib_test_lb_bucket_t lc100_o_10_10_10_3 = {
+        .type = FT_LB_LABEL_CHAIN_O_ADJ,
+        .label_chain_o_adj = {
+            .adj = ai_10_10_10_3,
+            .label_chain_size = 2,
+            .label_chain = {
+                99, 100
+            },
+            .eos = MPLS_EOS,
+        },
+    };
+
     FIB_TEST(!fib_test_validate_entry(fei,
                                       FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
                                       1,
-                                      &l99_o_10_10_10_3),
+                                      &lc100_o_10_10_10_3),
              "%U via interposer label 99",
              format_fib_prefix,&pfx_11_11_11_11_s_32);
 
@@ -9800,6 +9943,7 @@
              format_fib_prefix,&pfx_11_11_11_11_s_32);
 
     fib_table_entry_delete(0, &pfx_11_11_11_0_s_24, FIB_SOURCE_API);
+    fib_table_entry_delete(0, &pfx_11_11_11_11_s_32, FIB_SOURCE_API);
     FIB_TEST(!fib_test_validate_entry(fei,
                                       FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
                                       1,
@@ -10626,3 +10770,5 @@
 }
 
 VLIB_INIT_FUNCTION (fib_test_init);
+
+// clang-format on
diff --git a/src/vnet/fib/.clang-format b/src/vnet/fib/.clang-format
new file mode 100644
index 0000000..9d15924
--- /dev/null
+++ b/src/vnet/fib/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/src/vnet/fib/fib_entry.c b/src/vnet/fib/fib_entry.c
index a5fab85..6edf31b 100644
--- a/src/vnet/fib/fib_entry.c
+++ b/src/vnet/fib/fib_entry.c
@@ -473,7 +473,7 @@
              *      then up on the right trigger is more code. i favour the latter.
              */
             fib_entry_src_mk_lb(fib_entry,
-                                fib_entry_get_best_src_i(fib_entry),
+                                fib_entry_get_best_source(fib_entry_index),
                                 fct,
                                 &tmp);
 
@@ -1435,7 +1435,7 @@
             FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
             {
                 fib_entry_src_mk_lb(fib_entry,
-                                    fib_entry_get_best_src_i(fib_entry),
+                                    fib_entry_get_best_source(entry_index),
                                     fib_entry_delegate_type_to_chain_type(fdt),
                                     &fed->fd_dpo);
 	    });
diff --git a/src/vnet/fib/fib_entry_src.c b/src/vnet/fib/fib_entry_src.c
index 7f4db6a..503473a 100644
--- a/src/vnet/fib/fib_entry_src.c
+++ b/src/vnet/fib/fib_entry_src.c
@@ -253,7 +253,7 @@
 {
     load_balance_path_t *next_hops;
     const fib_entry_t *fib_entry;
-    const fib_entry_src_t *esrc;
+    i32 start_source_index, end_source_index;
     fib_forward_chain_type_t fct;
     int n_recursive_constrained;
     u16 preference;
@@ -263,15 +263,16 @@
  * @brief Determine whether this FIB entry should use a load-balance MAP
  * to support PIC edge fast convergence
  */
-load_balance_flags_t
-fib_entry_calc_lb_flags (fib_entry_src_collect_forwarding_ctx_t *ctx)
+static load_balance_flags_t
+fib_entry_calc_lb_flags (fib_entry_src_collect_forwarding_ctx_t *ctx,
+                         const fib_entry_src_t *esrc)
 {
     /**
      * We'll use a LB map if the path-list has multiple recursive paths.
      * recursive paths implies BGP, and hence scale.
      */
     if (ctx->n_recursive_constrained > 1 &&
-        fib_path_list_is_popular(ctx->esrc->fes_pl))
+        fib_path_list_is_popular(esrc->fes_pl))
     {
         return (LOAD_BALANCE_FLAG_USES_MAP);
     }
@@ -419,6 +420,7 @@
                                   void *arg)
 {
     fib_entry_src_collect_forwarding_ctx_t *ctx;
+    const fib_entry_src_t *esrc;
     fib_path_ext_t *path_ext;
     u32 n_nhs;
 
@@ -426,6 +428,11 @@
     n_nhs = vec_len(ctx->next_hops);
 
     /*
+     * walk the paths and extension of the best non-interpose source
+     */
+    esrc = &ctx->fib_entry->fe_srcs[ctx->end_source_index];
+
+    /*
      * if the path is not resolved, don't include it.
      */
     if (!fib_path_is_resolved(path_index))
@@ -457,7 +464,7 @@
     /*
      * get the matching path-extension for the path being visited.
      */
-    path_ext = fib_path_ext_list_find_by_path_index(&ctx->esrc->fes_path_exts,
+    path_ext = fib_path_ext_list_find_by_path_index(&esrc->fes_path_exts,
                                                     path_index);
 
     if (NULL != path_ext)
@@ -514,14 +521,26 @@
          */
         const fib_entry_src_vft_t *vft;
 
-        vft = fib_entry_src_get_vft(ctx->esrc);
+        /*
+         * roll up the sources that are interposes
+         */
+        i32 index;
 
-        if (NULL != vft->fesv_contribute_interpose)
+        for (index = ctx->end_source_index;
+             index >= ctx->start_source_index;
+             index--)
         {
             const dpo_id_t *interposer;
 
-            interposer = vft->fesv_contribute_interpose(ctx->esrc,
-                                                        ctx->fib_entry);
+            esrc = &ctx->fib_entry->fe_srcs[index];
+            vft = fib_entry_src_get_vft(esrc);
+
+            if (!(esrc->fes_flags & FIB_ENTRY_SRC_FLAG_CONTRIBUTING) ||
+                !(esrc->fes_entry_flags & FIB_ENTRY_FLAG_INTERPOSE))
+                continue;
+
+            ASSERT(vft->fesv_contribute_interpose);
+            interposer = vft->fesv_contribute_interpose(esrc, ctx->fib_entry);
 
             if (NULL != interposer)
             {
@@ -542,11 +561,40 @@
 
 void
 fib_entry_src_mk_lb (fib_entry_t *fib_entry,
-		     const fib_entry_src_t *esrc,
+                     fib_source_t source,
 		     fib_forward_chain_type_t fct,
 		     dpo_id_t *dpo_lb)
 {
+    const fib_entry_src_t *esrc;
     dpo_proto_t lb_proto;
+    u32 start, end;
+
+    /*
+     * The source passed here is the 'best', i.e. the one the client
+     * wants. however, if it's an interpose then it does not contribute
+     * the forwarding, the next best source that is not an interpose does.
+     * So roll down the sources, to find the best non-interpose
+     */
+    vec_foreach_index (start, fib_entry->fe_srcs)
+    {
+        if (source == fib_entry->fe_srcs[start].fes_src)
+            break;
+    }
+    for (end = start; end < vec_len (fib_entry->fe_srcs); end++)
+    {
+        if (!(fib_entry->fe_srcs[end].fes_entry_flags &
+              FIB_ENTRY_FLAG_INTERPOSE) &&
+            (fib_entry->fe_srcs[end].fes_flags &
+             FIB_ENTRY_SRC_FLAG_CONTRIBUTING))
+            break;
+    }
+    if (end == vec_len(fib_entry->fe_srcs))
+    {
+        /* didn't find any contributing non-interpose sources */
+        end = start;
+    }
+
+    esrc = &fib_entry->fe_srcs[end];
 
     /*
      * If the entry has path extensions then we construct a load-balance
@@ -554,12 +602,13 @@
      * Otherwise we use the load-balance of the path-list
      */
     fib_entry_src_collect_forwarding_ctx_t ctx = {
-        .esrc = esrc,
         .fib_entry = fib_entry,
         .next_hops = NULL,
         .n_recursive_constrained = 0,
         .fct = fct,
         .preference = 0xffff,
+        .start_source_index = start,
+        .end_source_index = end,
     };
 
     /*
@@ -644,7 +693,7 @@
     {
         load_balance_multipath_update(dpo_lb,
                                       ctx.next_hops,
-                                      fib_entry_calc_lb_flags(&ctx));
+                                      fib_entry_calc_lb_flags(&ctx, esrc));
         vec_free(ctx.next_hops);
 
         /*
@@ -688,11 +737,9 @@
      * tables
      */
     fib_forward_chain_type_t fct;
-    fib_entry_src_t *esrc;
     int insert;
 
     fct = fib_entry_get_default_chain_type(fib_entry);
-    esrc = fib_entry_src_find(fib_entry, source);
 
     /*
      * Every entry has its own load-balance object. All changes to the entry's
@@ -702,7 +749,7 @@
      */
     insert = !dpo_id_is_valid(&fib_entry->fe_lb);
 
-    fib_entry_src_mk_lb(fib_entry, esrc, fct, &fib_entry->fe_lb);
+    fib_entry_src_mk_lb(fib_entry, source, fct, &fib_entry->fe_lb);
 
     ASSERT(dpo_id_is_valid(&fib_entry->fe_lb));
     FIB_ENTRY_DBG(fib_entry, "install: %d", fib_entry->fe_lb);
@@ -726,7 +773,7 @@
 
     FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
     {
-        fib_entry_src_mk_lb(fib_entry, esrc,
+        fib_entry_src_mk_lb(fib_entry, source,
                             fib_entry_delegate_type_to_chain_type(fdt),
                             &fed->fd_dpo);
     });
diff --git a/src/vnet/fib/fib_entry_src.h b/src/vnet/fib/fib_entry_src.h
index 2105079..248cf6c 100644
--- a/src/vnet/fib/fib_entry_src.h
+++ b/src/vnet/fib/fib_entry_src.h
@@ -334,7 +334,7 @@
                                                            fib_forward_chain_type_t fct);
 
 extern void fib_entry_src_mk_lb (fib_entry_t *fib_entry,
-				 const fib_entry_src_t *esrc,
+				 fib_source_t source,
 				 fib_forward_chain_type_t fct,
 				 dpo_id_t *dpo_lb);
 
diff --git a/src/vnet/fib/fib_test.h b/src/vnet/fib/fib_test.h
index b2abcf5..6bedd9e 100644
--- a/src/vnet/fib/fib_test.h
+++ b/src/vnet/fib/fib_test.h
@@ -27,6 +27,7 @@
 typedef enum fib_test_lb_bucket_type_t_ {
     FT_LB_LABEL_O_ADJ,
     FT_LB_LABEL_STACK_O_ADJ,
+    FT_LB_LABEL_CHAIN_O_ADJ,
     FT_LB_LABEL_O_LB,
     FT_LB_O_LB,
     FT_LB_MPLS_DISP_PIPE_O_ADJ,
@@ -55,6 +56,15 @@
 	struct
 	{
 	    mpls_eos_bit_t eos;
+	    mpls_label_t label_chain[8];
+            fib_mpls_lsp_mode_t mode;
+	    u8 label_chain_size;
+	    u8 ttl;
+	    adj_index_t adj;
+	} label_chain_o_adj;
+	struct
+	{
+	    mpls_eos_bit_t eos;
 	    mpls_label_t label_stack[8];
             fib_mpls_lsp_mode_t mode;
 	    u8 label_stack_size;