BIER API and load-balancing fixes 01/14801/2
authorNeale Ranns <nranns@cisco.com>
Thu, 13 Sep 2018 15:45:12 +0000 (08:45 -0700)
committerDamjan Marion <dmarion@me.com>
Fri, 14 Sep 2018 18:21:43 +0000 (18:21 +0000)
Change-Id: Ibda19d786070c942c75016ab568c8361de2f24af
Signed-off-by: Neale Ranns <nranns@cisco.com>
17 files changed:
src/vnet/bier/bier.api
src/vnet/bier/bier_api.c
src/vnet/bier/bier_entry.c
src/vnet/bier/bier_entry.h
src/vnet/bier/bier_fmask.c
src/vnet/bier/bier_fwd.h [new file with mode: 0644]
src/vnet/bier/bier_lookup.c
src/vnet/bier/bier_table.c
src/vnet/bier/bier_table.h
src/vnet/bier/bier_test.c
src/vnet/bier/bier_update.c
src/vnet/dpo/load_balance.c
src/vnet/mpls/mpls_lookup.h
test/test_bier.py
test/vpp_bier.py
test/vpp_ip_route.py
test/vpp_papi_provider.py

index fb6923b..da2989b 100644 (file)
@@ -72,6 +72,8 @@ define bier_table_details
     @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
 */
index 93048a0..2e8fc62 100644 (file)
@@ -204,13 +204,24 @@ vl_api_bier_route_add_del_t_handler (vl_api_bier_route_add_del_t * mp)
         }
     }
 
-    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);
 
index 2f8d250..80f7cfd 100644 (file)
@@ -73,27 +73,6 @@ bier_entry_create (index_t bti,
     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,
@@ -160,6 +139,33 @@ bier_entry_table_ecmp_walk_add_fmask (index_t btei,
     }
 }
 
+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 @@ bier_entry_path_add (index_t bei,
     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 @@ bier_entry_path_remove (index_t bei,
     }
     fib_path_list_unlock(old_pl_index);
 
-
     /*
      * update the ECNP tables with the new choice
      */
index e514c64..629e47a 100644 (file)
@@ -75,6 +75,9 @@ extern index_t bier_entry_create(index_t bti,
                                  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);
 
index 73f7194..cb61681 100644 (file)
@@ -165,14 +165,14 @@ bier_fmask_child_remove (fib_node_index_t bfmi,
 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 @@ bier_fmask_init (bier_fmask_t *bfm,
 
     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 @@ bier_fmask_init (bier_fmask_t *bfm,
         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 @@ bier_fmask_lock (index_t bfmi)
 
 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 @@ bier_fmask_create_and_lock (const bier_fmask_id_t *fmid,
     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 (file)
index 0000000..3b0b93b
--- /dev/null
@@ -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
index 4e544a3..d450082 100644 (file)
@@ -250,12 +250,12 @@ bier_lookup (vlib_main_t * vm,
                     {
                         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,
index 80231fd..3ecda10 100644 (file)
@@ -510,9 +510,10 @@ bier_table_remove (bier_table_t *bt,
 }
 
 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 @@ bier_table_route_add (const bier_table_id_t *btid,
         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 @@ bier_table_route_add (const bier_table_id_t *btid,
 }
 
 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)
+{
+    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;
+    fib_route_path_t *brp = NULL, *t_paths = NULL;
     index_t bfmi, bti, bei;
     bier_table_t *bt;
     u32 ii;
@@ -616,12 +673,19 @@ bier_table_route_remove (const bier_table_id_t *btid,
         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
index 5af275f..9cd9937 100644 (file)
@@ -93,12 +93,17 @@ extern index_t bier_table_add_or_lock(const bier_table_id_t *id,
                                       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);
index d4d1692..06160f6 100644 (file)
@@ -335,7 +335,7 @@ bier_test_mpls_spf (void)
     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 @@ bier_test_mpls_spf (void)
     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 @@ bier_test_mpls_spf (void)
                                    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 @@ bier_test_mpls_spf (void)
      */
     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 @@ bier_test_mpls_spf (void)
      * 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 @@ bier_test_mpls_spf (void)
      * 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 @@ bier_test_mpls_disp (void)
     };
     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_test_mpls_disp (void)
 
     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);
 
index 326f8bf..4108d09 100644 (file)
@@ -115,11 +115,11 @@ vnet_bier_route_cmd (vlib_main_t * vm,
 
     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 @@ show_bier_fib_command_fn (vlib_main_t * vm,
     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
         {
index bb38233..ae95b6e 100644 (file)
@@ -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 @@ load_balance_inline (vlib_main_t * vm,
          {
              /* 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, 
index 4311dc0..95558e0 100644 (file)
@@ -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 @@ mpls_compute_flow_hash (const mpls_unicast_header_t * hdr,
         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;
     }
index 0276f95..9a1967c 100644 (file)
@@ -202,6 +202,100 @@ class TestBier(VppTestCase):
         """BIER midpoint BSL:64"""
         self.bier_midpoint(BIERLength.BIER_LEN_64, 8, 64)
 
+    def test_bier_load_balance(self):
+        """BIER load-balance"""
+
+        #
+        # Add a BIER table for sub-domain 0, set 0, and BSL 256
+        #
+        bti = VppBierTableID(0, 0, BIERLength.BIER_LEN_64)
+        bt = VppBierTable(self, bti, 77)
+        bt.add_vpp_config()
+
+        #
+        # packets with varying entropy
+        #
+        pkts = []
+        for ii in range(257):
+            pkts.append((Ether(dst=self.pg0.local_mac,
+                               src=self.pg0.remote_mac) /
+                         MPLS(label=77, ttl=255) /
+                         BIER(length=BIERLength.BIER_LEN_64,
+                              entropy=ii,
+                              BitString=chr(255)*16) /
+                         IPv6(src=self.pg0.remote_ip6,
+                              dst=self.pg0.remote_ip6) /
+                         UDP(sport=1234, dport=1234) /
+                         Raw()))
+
+        #
+        # 4 next hops
+        #
+        nhs = [{'ip': "10.0.0.1", 'label': 201},
+               {'ip': "10.0.0.2", 'label': 202},
+               {'ip': "10.0.0.3", 'label': 203},
+               {'ip': "10.0.0.4", 'label': 204}]
+
+        for nh in nhs:
+            ipr = VppIpRoute(
+                self, nh['ip'], 32,
+                [VppRoutePath(self.pg1.remote_ip4,
+                              self.pg1.sw_if_index,
+                              labels=[VppMplsLabel(nh['label'])])])
+            ipr.add_vpp_config()
+
+        bier_route = VppBierRoute(
+            self, bti, 1,
+            [VppRoutePath(nhs[0]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)]),
+             VppRoutePath(nhs[1]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)])])
+        bier_route.add_vpp_config()
+
+        rx = self.send_and_expect(self.pg0, pkts, self.pg1)
+
+        #
+        # we should have recieved a packet from each neighbor
+        #
+        for nh in nhs[:2]:
+            self.assertTrue(sum(p[MPLS].label == nh['label'] for p in rx))
+
+        #
+        # add the other paths
+        #
+        bier_route.update_paths(
+            [VppRoutePath(nhs[0]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)]),
+             VppRoutePath(nhs[1]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)]),
+             VppRoutePath(nhs[2]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)]),
+             VppRoutePath(nhs[3]['ip'], 0xffffffff,
+                          labels=[VppMplsLabel(101)])])
+
+        rx = self.send_and_expect(self.pg0, pkts, self.pg1)
+        for nh in nhs:
+            self.assertTrue(sum(p[MPLS].label == nh['label'] for p in rx))
+
+        #
+        # remove first two paths
+        #
+        bier_route.remove_path(VppRoutePath(nhs[0]['ip'], 0xffffffff,
+                                            labels=[VppMplsLabel(101)]))
+        bier_route.remove_path(VppRoutePath(nhs[1]['ip'], 0xffffffff,
+                                            labels=[VppMplsLabel(101)]))
+
+        rx = self.send_and_expect(self.pg0, pkts, self.pg1)
+        for nh in nhs[2:]:
+            self.assertTrue(sum(p[MPLS].label == nh['label'] for p in rx))
+
+        #
+        # remove the last of the paths, deleteing the entry
+        #
+        bier_route.remove_all_paths()
+
+        self.send_and_assert_no_replies(self.pg0, pkts)
+
     def test_bier_head(self):
         """BIER head"""
 
index 88dd79c..f389ca1 100644 (file)
@@ -120,28 +120,31 @@ class VppBierRoute(VppObject):
         self.bp = bp
         self.paths = paths
 
+    def encode_path(self, p):
+        lstack = []
+        for l in p.nh_labels:
+            if type(l) == VppMplsLabel:
+                lstack.append(l.encode())
+            else:
+                lstack.append({'label': l, 'ttl': 255})
+        n_labels = len(lstack)
+        while (len(lstack) < 16):
+            lstack.append({})
+        return {'next_hop': p.nh_addr,
+                'weight': 1,
+                'afi': p.proto,
+                'sw_if_index': 0xffffffff,
+                'preference': 0,
+                'table_id': p.nh_table_id,
+                'next_hop_id': p.next_hop_id,
+                'is_udp_encap': p.is_udp_encap,
+                'n_labels': n_labels,
+                'label_stack': lstack}
+
     def encode_paths(self):
         br_paths = []
         for p in self.paths:
-            lstack = []
-            for l in p.nh_labels:
-                if type(l) == VppMplsLabel:
-                    lstack.append(l.encode())
-                else:
-                    lstack.append({'label': l, 'ttl': 255})
-            n_labels = len(lstack)
-            while (len(lstack) < 16):
-                lstack.append({})
-            br_paths.append({'next_hop': p.nh_addr,
-                             'weight': 1,
-                             'afi': p.proto,
-                             'sw_if_index': 0xffffffff,
-                             'preference': 0,
-                             'table_id': p.nh_table_id,
-                             'next_hop_id': p.next_hop_id,
-                             'is_udp_encap': p.is_udp_encap,
-                             'n_labels': n_labels,
-                             'label_stack': lstack})
+            br_paths.append(self.encode_path(p))
         return br_paths
 
     def add_vpp_config(self):
@@ -159,6 +162,42 @@ class VppBierRoute(VppObject):
             self.encode_paths(),
             is_add=0)
 
+    def update_paths(self, paths):
+        self.paths = paths
+        self._test.vapi.bier_route_add_del(
+            self.tbl_id,
+            self.bp,
+            self.encode_paths(),
+            is_replace=1)
+
+    def add_path(self, path):
+        self._test.vapi.bier_route_add_del(
+            self.tbl_id,
+            self.bp,
+            [self.encode_path(path)],
+            is_add=1,
+            is_replace=0)
+        self.paths.append(path)
+        self._test.registry.register(self, self._test.logger)
+
+    def remove_path(self, path):
+        self._test.vapi.bier_route_add_del(
+            self.tbl_id,
+            self.bp,
+            [self.encode_path(path)],
+            is_add=0,
+            is_replace=0)
+        self.paths.remove(path)
+
+    def remove_all_paths(self):
+        self._test.vapi.bier_route_add_del(
+            self.tbl_id,
+            self.bp,
+            [],
+            is_add=0,
+            is_replace=1)
+        self.paths = []
+
     def __str__(self):
         return self.object_id()
 
index 9d6bfb7..18c27ff 100644 (file)
@@ -192,6 +192,9 @@ class VppRoutePath(object):
                 'n_labels': len(self.nh_labels),
                 'label_stack': self.encode_labels()}
 
+    def __eq__(self, other):
+        return self.nh_addr == other.nh_addr
+
 
 class VppMRoutePath(VppRoutePath):
 
index 61d80b2..5ff7db9 100644 (file)
@@ -3147,7 +3147,8 @@ class VppPapiProvider(object):
                            bti,
                            bp,
                            paths,
-                           is_add=1):
+                           is_add=1,
+                           is_replace=0):
         """ BIER Route add/del """
         return self.api(
             self.papi.bier_route_add_del,
@@ -3157,7 +3158,8 @@ class VppPapiProvider(object):
              'br_bp': bp,
              'br_n_paths': len(paths),
              'br_paths': paths,
-             'br_is_add': is_add})
+             'br_is_add': is_add,
+             'br_is_replace': is_replace})
 
     def bier_route_dump(self, bti):
         return self.api(