BIER API and load-balancing fixes

Change-Id: Ibda19d786070c942c75016ab568c8361de2f24af
Signed-off-by: Neale Ranns <nranns@cisco.com>
diff --git a/src/vnet/bier/bier.api b/src/vnet/bier/bier.api
index fb6923b..da2989b 100644
--- a/src/vnet/bier/bier.api
+++ b/src/vnet/bier/bier.api
@@ -72,6 +72,8 @@
     @param br_is_add - Is this a route add or delete
     @param br_is_replace - Are the paths specfied replacing those already
                            present or are they to be combined.
+			   is_replace = 1 and n_paths=0 implies delete the
+			   route and all paths;
     @param br_n_paths - The number of paths
     @param br_paths - The array of paths
 */
diff --git a/src/vnet/bier/bier_api.c b/src/vnet/bier/bier_api.c
index 93048a0..2e8fc62 100644
--- a/src/vnet/bier/bier_api.c
+++ b/src/vnet/bier/bier_api.c
@@ -204,13 +204,24 @@
         }
     }
 
-    if (mp->br_is_add)
+    if (mp->br_is_replace)
     {
-        bier_table_route_add(&bti, bp, brpaths);
+        if (0 == vec_len(brpaths))
+        {
+            bier_table_route_delete(&bti, bp);
+        }
+        else
+        {
+            bier_table_route_path_update(&bti, bp, brpaths);
+        }
+    }
+    else if (mp->br_is_add)
+    {
+        bier_table_route_path_add(&bti, bp, brpaths);
     }
     else
     {
-        bier_table_route_remove(&bti, bp, brpaths);
+        bier_table_route_path_remove(&bti, bp, brpaths);
     }
     vec_free(brpaths);
 
diff --git a/src/vnet/bier/bier_entry.c b/src/vnet/bier/bier_entry.c
index 2f8d250..80f7cfd 100644
--- a/src/vnet/bier/bier_entry.c
+++ b/src/vnet/bier/bier_entry.c
@@ -73,27 +73,6 @@
     return (bier_entry_get_index(be));
 }
 
-void
-bier_entry_delete (index_t bei)
-{
-    bier_entry_t *be;
-
-    be = bier_entry_get(bei);
-
-    /*
-     * if we still ahve a path-list, unlink from it
-     */
-    if (FIB_NODE_INDEX_INVALID != be->be_path_list)
-    {
-        fib_path_list_walk(be->be_path_list,
-                           bier_entry_unlink_walk,
-                           be);
-        fib_path_list_child_remove(be->be_path_list,
-                                   be->be_sibling_index);
-    }
-
-    pool_put(bier_entry_pool, be);
-}
 
 static void
 bier_entry_table_ecmp_walk_add_fmask (index_t btei,
@@ -161,6 +140,33 @@
 }
 
 void
+bier_entry_delete (index_t bei)
+{
+    bier_entry_t *be;
+
+    be = bier_entry_get(bei);
+
+    /*
+     * if we still ahve a path-list, unlink from it
+     */
+    if (FIB_NODE_INDEX_INVALID != be->be_path_list)
+    {
+        fib_path_list_walk(be->be_path_list,
+                           bier_entry_unlink_walk,
+                           be);
+        fib_path_list_child_remove(be->be_path_list,
+                                   be->be_sibling_index);
+
+        be->be_path_list = FIB_NODE_INDEX_INVALID;
+        bier_table_ecmp_walk(be->be_bti,
+                             bier_entry_table_ecmp_walk_add_fmask,
+                             be);
+    }
+
+    pool_put(bier_entry_pool, be);
+}
+
+void
 bier_entry_path_add (index_t bei,
                      const fib_route_path_t *rpaths)
 {
@@ -230,6 +236,62 @@
     fib_path_list_unlock(old_pl_index);
 }
 
+void
+bier_entry_path_update (index_t bei,
+                        const fib_route_path_t *rpaths)
+{
+    fib_node_index_t old_pl_index;
+    bier_entry_t *be;
+
+    be = bier_entry_get(bei);
+    old_pl_index = be->be_path_list;
+
+    /*
+     * lock the path-list so it does not go away before we unlink
+     * from its resolved fmasks
+     */
+    fib_path_list_lock(old_pl_index);
+
+    if (FIB_NODE_INDEX_INVALID != old_pl_index)
+    {
+        fib_path_list_child_remove(old_pl_index,
+                                   be->be_sibling_index);
+    }
+
+    be->be_path_list = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
+                                             FIB_PATH_LIST_FLAG_NO_URPF),
+                                            rpaths);
+    be->be_sibling_index = fib_path_list_child_add(be->be_path_list,
+                                                   FIB_NODE_TYPE_BIER_ENTRY,
+                                                   bier_entry_get_index(be));
+
+    /*
+     * link the entry's bit-position to each fmask in the new path-list
+     * then unlink from the old.
+     */
+    fib_path_list_walk(be->be_path_list,
+                       bier_entry_link_walk,
+                       be);
+    if (FIB_NODE_INDEX_INVALID != old_pl_index)
+    {
+        fib_path_list_walk(old_pl_index,
+                           bier_entry_unlink_walk,
+                           be);
+    }
+
+    /*
+     * update the ECNP tables with the new choice
+     */
+    bier_table_ecmp_walk(be->be_bti,
+                         bier_entry_table_ecmp_walk_add_fmask,
+                         be);
+
+    /*
+     * symmetric unlock. The old path-list may not exist hereinafter
+     */
+    fib_path_list_unlock(old_pl_index);
+}
+
 int
 bier_entry_path_remove (index_t bei,
                         const fib_route_path_t *rpaths)
@@ -279,7 +341,6 @@
     }
     fib_path_list_unlock(old_pl_index);
 
-
     /*
      * update the ECNP tables with the new choice
      */
diff --git a/src/vnet/bier/bier_entry.h b/src/vnet/bier/bier_entry.h
index e514c64..629e47a 100644
--- a/src/vnet/bier/bier_entry.h
+++ b/src/vnet/bier/bier_entry.h
@@ -75,6 +75,9 @@
                                  bier_bp_t bp);
 extern void bier_entry_delete(index_t bei);
 
+extern void bier_entry_path_update (index_t bei,
+                                    const fib_route_path_t *rpaths);
+
 extern void bier_entry_path_add(index_t bei,
                                 const fib_route_path_t *brp);
 
diff --git a/src/vnet/bier/bier_fmask.c b/src/vnet/bier/bier_fmask.c
index 73f7194..cb61681 100644
--- a/src/vnet/bier/bier_fmask.c
+++ b/src/vnet/bier/bier_fmask.c
@@ -165,14 +165,14 @@
 static void
 bier_fmask_init (bier_fmask_t *bfm,
                  const bier_fmask_id_t *fmid,
-                 const fib_route_path_t *rpaths)
+                 const fib_route_path_t *rpath)
 {
     const bier_table_id_t *btid;
+    fib_route_path_t *rpaths;
     mpls_label_t olabel;
 
-    ASSERT(1 == vec_len(rpaths));
     memset(bfm, 0, sizeof(*bfm));
-
+    
     bfm->bfm_id = clib_mem_alloc(sizeof(*bfm->bfm_id));
 
     fib_node_init(&bfm->bfm_node, FIB_NODE_TYPE_BIER_FMASK);
@@ -188,9 +188,9 @@
 
     if (!(bfm->bfm_flags & BIER_FMASK_FLAG_DISP))
     {
-        if (NULL != rpaths->frp_label_stack)
+        if (NULL != rpath->frp_label_stack)
         {
-            olabel = rpaths->frp_label_stack[0].fml_value;
+            olabel = rpath->frp_label_stack[0].fml_value;
             vnet_mpls_uc_set_label(&bfm->bfm_label, olabel);
             vnet_mpls_uc_set_exp(&bfm->bfm_label, 0);
             vnet_mpls_uc_set_s(&bfm->bfm_label, 1);
@@ -220,13 +220,15 @@
         bfm->bfm_label = clib_host_to_net_u32(bfm->bfm_label);
     }
 
+    rpaths = NULL;
+    vec_add1(rpaths, *rpath);
     bfm->bfm_pl = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED |
                                         FIB_PATH_LIST_FLAG_NO_URPF),
                                        rpaths);
     bfm->bfm_sibling = fib_path_list_child_add(bfm->bfm_pl,
                                                FIB_NODE_TYPE_BIER_FMASK,
                                                bier_fmask_get_index(bfm));
-
+    vec_free(rpaths);
     bier_fmask_stack(bfm);
 }
 
@@ -276,7 +278,7 @@
 
 index_t
 bier_fmask_create_and_lock (const bier_fmask_id_t *fmid,
-                            const fib_route_path_t *rpaths)
+                            const fib_route_path_t *rpath)
 {
     bier_fmask_t *bfm;
     index_t bfmi;
@@ -287,7 +289,7 @@
     vlib_validate_combined_counter (&(bier_fmask_counters), bfmi);
     vlib_zero_combined_counter (&(bier_fmask_counters), bfmi);
 
-    bier_fmask_init(bfm, fmid, rpaths);
+    bier_fmask_init(bfm, fmid, rpath);
 
     bier_fmask_lock(bfmi);
 
diff --git a/src/vnet/bier/bier_fwd.h b/src/vnet/bier/bier_fwd.h
new file mode 100644
index 0000000..3b0b93b
--- /dev/null
+++ b/src/vnet/bier/bier_fwd.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __BIER_FWD_H__
+#define __BIER_FWD_H__
+
+#include <vnet/bier/bier_types.h>
+#include <vnet/bier/bier_hdr_inlines.h>
+
+static_always_inline u32
+bier_compute_flow_hash (const bier_hdr_t *hdr)
+{
+    u32 first_word = clib_net_to_host_u32(hdr->bh_first_word);
+
+    return ((first_word &
+             BIER_HDR_ENTROPY_FIELD_MASK) >>
+            BIER_HDR_ENTROPY_FIELD_SHIFT);
+}
+
+#endif
diff --git a/src/vnet/bier/bier_lookup.c b/src/vnet/bier/bier_lookup.c
index 4e544a3..d450082 100644
--- a/src/vnet/bier/bier_lookup.c
+++ b/src/vnet/bier/bier_lookup.c
@@ -250,12 +250,12 @@
                     {
                         bier_lookup_trace_t *tr;
 
-                        vlib_trace_buffer (vm, node, next0, c0, 0);
+                        if (c0 != b0)
+                            vlib_buffer_copy_trace_flag (vm, b0, ci0);
+
                         tr = vlib_add_trace (vm, node, c0, sizeof (*tr));
                         tr->bt_index = bti0;
                         tr->bfm_index = blm->blm_fmasks[thread_index][clone];
-
-                        c0->flags |= VLIB_BUFFER_IS_TRACED;
                     }
 
                     vlib_validate_buffer_enqueue_x1(vm, node, next_index,
diff --git a/src/vnet/bier/bier_table.c b/src/vnet/bier/bier_table.c
index 80231fd..3ecda10 100644
--- a/src/vnet/bier/bier_table.c
+++ b/src/vnet/bier/bier_table.c
@@ -510,9 +510,10 @@
 }
 
 void
-bier_table_route_add (const bier_table_id_t *btid,
-                      bier_bp_t bp,
-                      fib_route_path_t *brps)
+bier_table_route_path_update_i (const bier_table_id_t *btid,
+                                bier_bp_t bp,
+                                fib_route_path_t *brps,
+                                u8 is_replace)
 {
     index_t bfmi, bti, bei, *bfmip, *bfmis = NULL;
     fib_route_path_t *brp;
@@ -552,7 +553,23 @@
         bei = bier_entry_create(bti, bp);
         bier_table_insert(bt, bp, bei);
     }
-    bier_entry_path_add(bei, brps);
+
+    if (is_replace)
+    {
+        bier_entry_path_update(bei, brps);
+    }
+    else
+    {
+        fib_route_path_t *t_paths = NULL;
+
+        vec_foreach(brp, brps)
+        {
+            vec_add1(t_paths, *brp);
+            bier_entry_path_add(bei, t_paths);
+            vec_reset_length(t_paths);
+        }
+        vec_free(t_paths);
+    }
 
     vec_foreach(bfmip, bfmis)
     {
@@ -562,11 +579,51 @@
 }
 
 void
-bier_table_route_remove (const bier_table_id_t *btid,
-                         bier_bp_t bp,
-                         fib_route_path_t *brps)
+bier_table_route_path_update (const bier_table_id_t *btid,
+                              bier_bp_t bp,
+                              fib_route_path_t *brps)
 {
-    fib_route_path_t *brp = NULL;
+    bier_table_route_path_update_i(btid, bp, brps, 1);
+}
+void
+bier_table_route_path_add (const bier_table_id_t *btid,
+                           bier_bp_t bp,
+                           fib_route_path_t *brps)
+{
+    bier_table_route_path_update_i(btid, bp, brps, 0);
+}
+
+void
+bier_table_route_delete (const bier_table_id_t *btid,
+                         bier_bp_t bp)
+{
+    bier_table_t *bt;
+    index_t bei;
+
+    bt = bier_table_find(btid);
+
+    if (NULL == bt) {
+        return;
+    }
+
+    bei = bier_table_lookup(bt, bp);
+
+    if (INDEX_INVALID == bei)
+    {
+        /* no such entry */
+        return;
+    }
+
+    bier_table_remove(bt, bp);
+    bier_entry_delete(bei);
+}
+
+void
+bier_table_route_path_remove (const bier_table_id_t *btid,
+                              bier_bp_t bp,
+                              fib_route_path_t *brps)
+{
+    fib_route_path_t *brp = NULL, *t_paths = NULL;
     index_t bfmi, bti, bei;
     bier_table_t *bt;
     u32 ii;
@@ -616,12 +673,19 @@
         return;
     }
 
-    if (0 == bier_entry_path_remove(bei, brps))
+    vec_foreach(brp, brps)
     {
-        /* 0 remaining paths */
-        bier_table_remove(bt, bp);
-        bier_entry_delete(bei);
+        vec_add1(t_paths, *brp);
+        if (0 == bier_entry_path_remove(bei, t_paths))
+        {
+            /* 0 remaining paths */
+            bier_table_remove(bt, bp);
+            bier_entry_delete(bei);
+            break;
+        }
+        vec_reset_length(t_paths);
     }
+    vec_free(t_paths);
 }
 
 void
diff --git a/src/vnet/bier/bier_table.h b/src/vnet/bier/bier_table.h
index 5af275f..9cd9937 100644
--- a/src/vnet/bier/bier_table.h
+++ b/src/vnet/bier/bier_table.h
@@ -93,12 +93,17 @@
                                       mpls_label_t ll);
 extern void bier_table_unlock(const bier_table_id_t *id);
 
-extern void bier_table_route_add(const bier_table_id_t *bti,
-                                 bier_bp_t bp,
-                                 fib_route_path_t *brp);
-extern void bier_table_route_remove(const bier_table_id_t *bti,
-                                    bier_bp_t bp,
-                                    fib_route_path_t *brp);
+extern void bier_table_route_path_add(const bier_table_id_t *bti,
+                                      bier_bp_t bp,
+                                      fib_route_path_t *brp);
+extern void bier_table_route_path_remove(const bier_table_id_t *bti,
+                                         bier_bp_t bp,
+                                         fib_route_path_t *brp);
+extern void bier_table_route_path_update(const bier_table_id_t *bti,
+                                         bier_bp_t bp,
+                                         fib_route_path_t *brp);
+extern void bier_table_route_delete(const bier_table_id_t *bti,
+                                    bier_bp_t b);
 
 extern void bier_table_show_all(vlib_main_t * vm,
                                 bier_show_flags_t flags);
diff --git a/src/vnet/bier/bier_test.c b/src/vnet/bier/bier_test.c
index d4d1692..06160f6 100644
--- a/src/vnet/bier/bier_test.c
+++ b/src/vnet/bier/bier_test.c
@@ -335,7 +335,7 @@
     index_t bei_1;
 
     input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_add(&bt_0_0_0_256, 1, input_paths_1_1_1_1);
+    bier_table_route_path_add(&bt_0_0_0_256, 1, input_paths_1_1_1_1);
     bei_1 = bier_table_lookup(bier_table_get(bti), 1);
 
     BIER_TEST((INDEX_INVALID != bei_1), "BP:1 present");
@@ -492,7 +492,7 @@
     index_t bei_2;
 
     input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_add(&bt_0_0_0_256, 2, input_paths_1_1_1_1);
+    bier_table_route_path_add(&bt_0_0_0_256, 2, input_paths_1_1_1_1);
     bei_2 = bier_table_lookup(bier_table_get(bti), 2);
 
     bier_entry_contribute_forwarding(bei_2, &dpo_bei);
@@ -541,7 +541,7 @@
                                    1,
                                    out_lbl_101,
                                    FIB_ROUTE_PATH_FLAG_NONE);
-    bier_table_route_add(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
+    bier_table_route_path_update(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
     bei_3 = bier_table_lookup(bier_table_get(bti), 3);
 
     BIER_TEST((INDEX_INVALID != bei_3), "BP:3 present");
@@ -584,7 +584,7 @@
      */
     paths_1_1_1_1[0] = path_1_1_1_1;
     input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_add(&bt_0_0_0_256, 3, input_paths_1_1_1_1);
+    bier_table_route_path_add(&bt_0_0_0_256, 3, input_paths_1_1_1_1);
 
     BIER_TEST(!bier_test_validate_entry(bei_3, 2,
                                         &dpo_o_bfm_1_1_1_1,
@@ -653,7 +653,7 @@
      * remove the original 1.1.1.2 fmask from BP:3
      */
     input_paths_1_1_1_2 = vec_dup(paths_1_1_1_2);
-    bier_table_route_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
+    bier_table_route_path_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
     bier_entry_contribute_forwarding(bei_3, &dpo_bei);
     BIER_TEST((dpo_bei.dpoi_index == bfmi_1_1_1_1),
               "BP:3 stacks on fmask 1.1.1.1");
@@ -672,13 +672,13 @@
      * remove the routes added
      */
     input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_remove(&bt_0_0_0_256, 2, input_paths_1_1_1_1);
+    bier_table_route_path_remove(&bt_0_0_0_256, 2, input_paths_1_1_1_1);
     input_paths_1_1_1_2 = vec_dup(paths_1_1_1_2);
-    bier_table_route_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
+    bier_table_route_path_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_2);
     input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_1);
-    input_paths_1_1_1_1 = vec_dup(paths_1_1_1_1);
-    bier_table_route_remove(&bt_0_0_0_256, 1, input_paths_1_1_1_1);
+    bier_table_route_path_remove(&bt_0_0_0_256, 3, input_paths_1_1_1_1);
+
+    bier_table_route_delete(&bt_0_0_0_256, 1);
 
     /*
      * delete the table
@@ -827,7 +827,7 @@
     };
     vec_add1(paths_via_disp, path_via_disp);
 
-    bier_table_route_add(&bt_0_0_0_256, 3, paths_via_disp);
+    bier_table_route_path_add(&bt_0_0_0_256, 3, paths_via_disp);
 
     /*
      * the fmask should stack on the BIER disp table
@@ -890,7 +890,7 @@
 
     bier_disp_table_entry_path_remove(bier_disp_tbl_id, src,
                                       BIER_HDR_PROTO_IPV4, rpaths);
-    bier_table_route_remove(&bt_0_0_0_256, 3, paths_via_disp);
+    bier_table_route_path_remove(&bt_0_0_0_256, 3, paths_via_disp);
 
     bier_disp_table_unlock_w_table_id(bier_disp_tbl_id);
 
diff --git a/src/vnet/bier/bier_update.c b/src/vnet/bier/bier_update.c
index 326f8bf..4108d09 100644
--- a/src/vnet/bier/bier_update.c
+++ b/src/vnet/bier/bier_update.c
@@ -115,11 +115,11 @@
 
     if (add)
     {
-        bier_table_route_add(&bti, bp, brps);
+        bier_table_route_path_add(&bti, bp, brps);
     }
     else
     {
-        bier_table_route_remove(&bti, bp, brps);
+        bier_table_route_path_remove(&bti, bp, brps);
     }
 
 done:
@@ -149,11 +149,11 @@
     while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
         if (unformat (input, "%d %d", &bti, &bp))
         {
-             flags = BIER_SHOW_DETAIL;
+            flags = BIER_SHOW_DETAIL;
         }
         else if (unformat (input, "%d", &bti))
         {
-              flags = BIER_SHOW_DETAIL;
+            flags = BIER_SHOW_DETAIL;
         }
         else
         {
diff --git a/src/vnet/dpo/load_balance.c b/src/vnet/dpo/load_balance.c
index bb38233..ae95b6e 100644
--- a/src/vnet/dpo/load_balance.c
+++ b/src/vnet/dpo/load_balance.c
@@ -21,7 +21,7 @@
 #include <vnet/adj/adj.h>
 #include <vnet/adj/adj_internal.h>
 #include <vnet/fib/fib_urpf_list.h>
-#include <vnet/bier/bier_hdr_inlines.h>
+#include <vnet/bier/bier_fwd.h>
 
 /*
  * distribution error tolerance for load-balancing
@@ -999,7 +999,7 @@
 	  {
 	      /* it's BIER */
 	      const bier_hdr_t *bh0 = vlib_buffer_get_current(b0);
-	      vnet_buffer(b0)->ip.flow_hash = bier_hdr_get_entropy(bh0);
+	      vnet_buffer(b0)->ip.flow_hash = bier_compute_flow_hash(bh0);
 	  }
 
 	  dpo0 = load_balance_get_bucket_i(lb0, 
diff --git a/src/vnet/mpls/mpls_lookup.h b/src/vnet/mpls/mpls_lookup.h
index 4311dc0..95558e0 100644
--- a/src/vnet/mpls/mpls_lookup.h
+++ b/src/vnet/mpls/mpls_lookup.h
@@ -18,6 +18,7 @@
 
 #include <vnet/mpls/mpls.h>
 #include <vnet/ip/ip.h>
+#include <vnet/bier/bier_fwd.h>
 
 /**
  * The arc/edge from the MPLS lookup node to the MPLS replicate node
@@ -100,6 +101,10 @@
         hash ^= ip6_compute_flow_hash ((const ip6_header_t *)hdr,
                                        IP_FLOW_HASH_DEFAULT);
         break;
+    case 5:
+        /* incorporate the bier flow-hash */
+        hash ^= bier_compute_flow_hash ((const bier_hdr_t *)hdr);
+        break;
     default:
         break;
     }