@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
*/
}
}
- 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);
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,
}
}
+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)
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)
}
fib_path_list_unlock(old_pl_index);
-
/*
* update the ECNP tables with the new choice
*/
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);
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);
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);
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);
}
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;
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);
--- /dev/null
+/*
+ * 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
{
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,
}
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;
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)
{
}
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;
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
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_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");
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);
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");
*/
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,
* 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");
* 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
};
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
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);
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:
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
{
#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
{
/* 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,
#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
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;
}
"""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"""
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):
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()
'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):
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,
'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(