blob: 3ec00a74aa8c9201a0a3a2cecd868a1d9ac20deb [file] [log] [blame]
/*
* Copyright (c) 2016 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.
*/
#include <vnet/mpls/mpls_types.h>
#include <vnet/mfib/mfib_table.h>
#include <vnet/mfib/mfib_entry.h>
#include <vnet/mfib/mfib_signal.h>
#include <vnet/mfib/ip6_mfib.h>
#include <vnet/fib/fib_path_list.h>
#include <vnet/fib/fib_test.h>
#include <vnet/fib/fib_table.h>
#include <vnet/fib/mpls_fib.h>
#include <vnet/dpo/replicate_dpo.h>
#include <vnet/adj/adj_mcast.h>
#define MFIB_TEST_I(_cond, _comment, _args...) \
({ \
int _evald = (_cond); \
if (!(_evald)) { \
fformat(stderr, "FAIL:%d: " _comment "\n", \
__LINE__, ##_args); \
} else { \
fformat(stderr, "PASS:%d: " _comment "\n", \
__LINE__, ##_args); \
} \
_evald; \
})
#define MFIB_TEST(_cond, _comment, _args...) \
{ \
if (!MFIB_TEST_I(_cond, _comment, ##_args)) { \
return 1;\
ASSERT(!("FAIL: " _comment)); \
} \
}
#define MFIB_TEST_NS(_cond) \
{ \
if (!MFIB_TEST_I(_cond, "")) { \
return 1;\
ASSERT(!("FAIL: ")); \
} \
}
/**
* A 'i'm not fussed is this is not efficient' store of test data
*/
typedef struct test_main_t_ {
/**
* HW if indicies
*/
u32 hw_if_indicies[4];
/**
* HW interfaces
*/
vnet_hw_interface_t * hw[4];
} test_main_t;
static test_main_t test_main;
/* fake ethernet device class, distinct from "fake-ethX" */
static u8 * format_test_interface_name (u8 * s, va_list * args)
{
u32 dev_instance = va_arg (*args, u32);
return format (s, "test-eth%d", dev_instance);
}
static uword dummy_interface_tx (vlib_main_t * vm,
vlib_node_runtime_t * node,
vlib_frame_t * frame)
{
clib_warning ("you shouldn't be here, leaking buffers...");
return frame->n_vectors;
}
static clib_error_t *
test_interface_admin_up_down (vnet_main_t * vnm,
u32 hw_if_index,
u32 flags)
{
u32 hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) ?
VNET_HW_INTERFACE_FLAG_LINK_UP : 0;
vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
return 0;
}
VNET_DEVICE_CLASS (test_interface_device_class,static) = {
.name = "Test interface",
.format_device_name = format_test_interface_name,
.tx_function = dummy_interface_tx,
.admin_up_down_function = test_interface_admin_up_down,
};
static u8 *hw_address;
static int
mfib_test_mk_intf (u32 ninterfaces)
{
clib_error_t * error = NULL;
test_main_t *tm = &test_main;
u8 byte;
u32 i;
ASSERT(ninterfaces <= ARRAY_LEN(tm->hw_if_indicies));
for (i=0; i<6; i++)
{
byte = 0xd0+i;
vec_add1(hw_address, byte);
}
for (i = 0; i < ninterfaces; i++)
{
hw_address[5] = i;
error = ethernet_register_interface(vnet_get_main(),
test_interface_device_class.index,
i /* instance */,
hw_address,
&tm->hw_if_indicies[i],
/* flag change */ 0);
MFIB_TEST((NULL == error), "ADD interface %d", i);
error = vnet_hw_interface_set_flags(vnet_get_main(),
tm->hw_if_indicies[i],
VNET_HW_INTERFACE_FLAG_LINK_UP);
tm->hw[i] = vnet_get_hw_interface(vnet_get_main(),
tm->hw_if_indicies[i]);
vec_validate (ip4_main.fib_index_by_sw_if_index,
tm->hw[i]->sw_if_index);
vec_validate (ip6_main.fib_index_by_sw_if_index,
tm->hw[i]->sw_if_index);
ip4_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
ip6_main.fib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
vec_validate (ip4_main.mfib_index_by_sw_if_index,
tm->hw[i]->sw_if_index);
vec_validate (ip6_main.mfib_index_by_sw_if_index,
tm->hw[i]->sw_if_index);
ip4_main.mfib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
ip6_main.mfib_index_by_sw_if_index[tm->hw[i]->sw_if_index] = 0;
error = vnet_sw_interface_set_flags(vnet_get_main(),
tm->hw[i]->sw_if_index,
VNET_SW_INTERFACE_FLAG_ADMIN_UP);
MFIB_TEST((NULL == error), "UP interface %d", i);
}
/*
* re-eval after the inevitable realloc
*/
for (i = 0; i < ninterfaces; i++)
{
tm->hw[i] = vnet_get_hw_interface(vnet_get_main(),
tm->hw_if_indicies[i]);
}
return (0);
}
#define MFIB_TEST_REP(_cond, _comment, _args...) \
{ \
if (!MFIB_TEST_I(_cond, _comment, ##_args)) { \
return (0); \
} \
}
static int
mfib_test_validate_rep_v (const replicate_t *rep,
u16 n_buckets,
va_list *ap)
{
const dpo_id_t *dpo;
adj_index_t ai;
dpo_type_t dt;
int bucket;
MFIB_TEST_REP((n_buckets == rep->rep_n_buckets),
"n_buckets = %d", rep->rep_n_buckets);
for (bucket = 0; bucket < n_buckets; bucket++)
{
dt = va_arg(*ap, int); // type promotion
ai = va_arg(*ap, adj_index_t);
dpo = replicate_get_bucket_i(rep, bucket);
MFIB_TEST_REP((dt == dpo->dpoi_type),
"bucket %d stacks on %U",
bucket,
format_dpo_type, dpo->dpoi_type);
if (DPO_RECEIVE != dt)
{
MFIB_TEST_REP((ai == dpo->dpoi_index),
"bucket %d [exp:%d] stacks on %U",
bucket, ai,
format_dpo_id, dpo, 0);
}
}
return (!0);
}
static fib_forward_chain_type_t
fib_forw_chain_type_from_fib_proto (fib_protocol_t proto)
{
switch (proto)
{
case FIB_PROTOCOL_IP4:
return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
case FIB_PROTOCOL_IP6:
return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
default:
break;
}
ASSERT(0);
return (0);
}
static int
mfib_test_entry (fib_node_index_t fei,
mfib_entry_flags_t eflags,
u16 n_buckets,
...)
{
const mfib_entry_t *mfe;
const replicate_t *rep;
mfib_prefix_t pfx;
va_list ap;
int res;
va_start(ap, n_buckets);
mfe = mfib_entry_get(fei);
mfib_entry_get_prefix(fei, &pfx);
MFIB_TEST_REP((eflags == mfe->mfe_flags),
"%U has %U expect %U",
format_mfib_prefix, &pfx,
format_mfib_entry_flags, mfe->mfe_flags,
format_mfib_entry_flags, eflags);
if (0 == n_buckets)
{
MFIB_TEST_REP((DPO_DROP == mfe->mfe_rep.dpoi_type),
"%U links to %U",
format_mfib_prefix, &pfx,
format_dpo_id, &mfe->mfe_rep, 0);
res = !0;
}
else
{
dpo_id_t tmp = DPO_INVALID;
mfib_entry_contribute_forwarding(
fei,
fib_forw_chain_type_from_fib_proto(pfx.fp_proto),
&tmp);
rep = replicate_get(tmp.dpoi_index);
MFIB_TEST_REP((DPO_REPLICATE == tmp.dpoi_type),
"%U links to %U",
format_mfib_prefix, &pfx,
format_dpo_type, tmp.dpoi_type);
res = mfib_test_validate_rep_v(rep, n_buckets, &ap);
dpo_reset(&tmp);
}
va_end(ap);
return (res);
}
static int
mfib_test_entry_itf (fib_node_index_t fei,
u32 sw_if_index,
mfib_itf_flags_t flags)
{
const mfib_entry_t *mfe;
const mfib_itf_t *mfi;
mfib_prefix_t pfx;
mfe = mfib_entry_get(fei);
mfi = mfib_entry_get_itf(mfe, sw_if_index);
mfib_entry_get_prefix(fei, &pfx);
MFIB_TEST_REP((NULL != mfi),
"%U has interface %d",
format_mfib_prefix, &pfx, sw_if_index);
MFIB_TEST_REP((flags == mfi->mfi_flags),
"%U interface %d has flags %U expect %U",
format_mfib_prefix, &pfx, sw_if_index,
format_mfib_itf_flags, flags,
format_mfib_itf_flags, mfi->mfi_flags);
return (!0);
}
static int
mfib_test_entry_no_itf (fib_node_index_t fei,
u32 sw_if_index)
{
const mfib_entry_t *mfe;
const mfib_itf_t *mfi;
mfib_prefix_t pfx;
mfe = mfib_entry_get(fei);
mfi = mfib_entry_get_itf(mfe, sw_if_index);
mfib_entry_get_prefix(fei, &pfx);
MFIB_TEST_REP((NULL == mfi),
"%U has no interface %d",
format_mfib_prefix, &pfx, sw_if_index);
return (!0);
}
static int
mfib_test_i (fib_protocol_t PROTO,
vnet_link_t LINKT,
const mfib_prefix_t *pfx_no_forward,
const mfib_prefix_t *pfx_s_g,
const mfib_prefix_t *pfx_star_g_1,
const mfib_prefix_t *pfx_star_g_2,
const mfib_prefix_t *pfx_star_g_3,
const mfib_prefix_t *pfx_star_g_slash_m)
{
fib_node_index_t mfei, mfei_dflt, mfei_no_f, mfei_s_g, mfei_g_1, mfei_g_2, mfei_g_3, mfei_g_m;
u32 fib_index, n_entries, n_itfs, n_reps, n_pls;
fib_node_index_t ai_1, ai_2, ai_3;
test_main_t *tm;
mfib_prefix_t all_1s;
memset(&all_1s, 0xfd, sizeof(all_1s));
n_entries = pool_elts(mfib_entry_pool);
n_itfs = pool_elts(mfib_itf_pool);
n_reps = pool_elts(replicate_pool);
n_pls = fib_path_list_pool_size();
tm = &test_main;
ai_1 = adj_mcast_add_or_lock(PROTO,
LINKT,
tm->hw[1]->sw_if_index);
ai_2 = adj_mcast_add_or_lock(PROTO,
LINKT,
tm->hw[2]->sw_if_index);
ai_3 = adj_mcast_add_or_lock(PROTO,
LINKT,
tm->hw[3]->sw_if_index);
MFIB_TEST(3 == adj_mcast_db_size(), "3 MCAST adjs");
/* Find or create FIB table 11 */
fib_index = mfib_table_find_or_create_and_lock(PROTO, 11, MFIB_SOURCE_API);
mfib_prefix_t pfx_dft = {
.fp_len = 0,
.fp_proto = PROTO,
};
mfei_dflt = mfib_table_lookup_exact_match(fib_index, &pfx_dft);
MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet");
MFIB_TEST(mfib_test_entry(mfei_dflt,
MFIB_ENTRY_FLAG_DROP,
0),
"(*,*) no replcaitions");
MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet");
MFIB_TEST(mfib_test_entry(mfei_dflt,
MFIB_ENTRY_FLAG_DROP,
0),
"(*,*) no replcaitions");
fib_route_path_t path_via_if0 = {
.frp_proto = fib_proto_to_dpo(PROTO),
.frp_addr = zero_addr,
.frp_sw_if_index = tm->hw[0]->sw_if_index,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = 0,
};
mfib_table_entry_path_update(fib_index,
pfx_no_forward,
MFIB_SOURCE_API,
&path_via_if0,
MFIB_ITF_FLAG_ACCEPT);
mfei_no_f = mfib_table_lookup_exact_match(fib_index, pfx_no_forward);
MFIB_TEST(mfib_test_entry(mfei_no_f,
MFIB_ENTRY_FLAG_NONE,
0),
"%U no replcaitions",
format_mfib_prefix, pfx_no_forward);
MFIB_TEST_NS(mfib_test_entry_itf(mfei_no_f, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
fib_route_path_t path_via_if1 = {
.frp_proto = fib_proto_to_dpo(PROTO),
.frp_addr = zero_addr,
.frp_sw_if_index = tm->hw[1]->sw_if_index,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = 0,
};
fib_route_path_t path_via_if2 = {
.frp_proto = fib_proto_to_dpo(PROTO),
.frp_addr = zero_addr,
.frp_sw_if_index = tm->hw[2]->sw_if_index,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = 0,
};
fib_route_path_t path_via_if3 = {
.frp_proto = fib_proto_to_dpo(PROTO),
.frp_addr = zero_addr,
.frp_sw_if_index = tm->hw[3]->sw_if_index,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = 0,
};
fib_route_path_t path_for_us = {
.frp_proto = fib_proto_to_dpo(PROTO),
.frp_addr = zero_addr,
.frp_sw_if_index = 0xffffffff,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = FIB_ROUTE_PATH_LOCAL,
};
/*
* An (S,G) with 1 accepting and 3 forwarding paths
*/
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if0,
MFIB_ITF_FLAG_ACCEPT);
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if1,
MFIB_ITF_FLAG_FORWARD);
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if2,
MFIB_ITF_FLAG_FORWARD);
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if3,
(MFIB_ITF_FLAG_FORWARD |
MFIB_ITF_FLAG_NEGATE_SIGNAL));
mfei_s_g = mfib_table_lookup_exact_match(fib_index, pfx_s_g);
MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_s_g,
"%U present",
format_mfib_prefix, pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei_s_g,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate ok",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei_s_g, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
MFIB_TEST_NS(mfib_test_entry_itf(mfei_s_g, tm->hw[1]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei_s_g, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei_s_g, tm->hw[3]->sw_if_index,
(MFIB_ITF_FLAG_FORWARD |
MFIB_ITF_FLAG_NEGATE_SIGNAL)));
/*
* A (*,G), which the same G as the (S,G).
* different paths. test our LPM.
*/
mfei_g_1 = mfib_table_entry_path_update(fib_index,
pfx_star_g_1,
MFIB_SOURCE_API,
&path_via_if0,
MFIB_ITF_FLAG_ACCEPT);
mfib_table_entry_path_update(fib_index,
pfx_star_g_1,
MFIB_SOURCE_API,
&path_via_if1,
MFIB_ITF_FLAG_FORWARD);
/*
* test we find the *,G and S,G via LPM and exact matches
*/
mfei = mfib_table_lookup_exact_match(fib_index,
pfx_star_g_1);
MFIB_TEST(mfei == mfei_g_1,
"%U found via exact match",
format_mfib_prefix, pfx_star_g_1);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_ADJACENCY_MCAST, ai_1),
"%U replicate ok",
format_mfib_prefix, pfx_star_g_1);
mfei = mfib_table_lookup(fib_index,
pfx_star_g_1);
MFIB_TEST(mfei == mfei_g_1,
"%U found via LP match",
format_mfib_prefix, pfx_star_g_1);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_ADJACENCY_MCAST, ai_1),
"%U replicate ok",
format_mfib_prefix, pfx_star_g_1);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_s_g);
MFIB_TEST(mfei == mfei_s_g,
"%U found via exact match",
format_mfib_prefix, pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
mfei = mfib_table_lookup(fib_index, pfx_s_g);
MFIB_TEST(mfei == mfei_s_g,
"%U found via LP match",
format_mfib_prefix, pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
/*
* A (*,G/m), which the same root G as the (*,G).
* different paths. test our LPM.
*/
mfei_g_m = mfib_table_entry_path_update(fib_index,
pfx_star_g_slash_m,
MFIB_SOURCE_API,
&path_via_if2,
MFIB_ITF_FLAG_ACCEPT);
mfib_table_entry_path_update(fib_index,
pfx_star_g_slash_m,
MFIB_SOURCE_API,
&path_via_if3,
MFIB_ITF_FLAG_FORWARD);
/*
* test we find the (*,G/m), (*,G) and (S,G) via LPM and exact matches
*/
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_1);
MFIB_TEST((mfei_g_1 == mfei),
"%U found via DP LPM: %d",
format_mfib_prefix, pfx_star_g_1, mfei);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_ADJACENCY_MCAST, ai_1),
"%U replicate ok",
format_mfib_prefix, pfx_star_g_1);
mfei = mfib_table_lookup(fib_index, pfx_star_g_1);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_ADJACENCY_MCAST, ai_1),
"%U replicate ok",
format_mfib_prefix, pfx_star_g_1);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
mfei = mfib_table_lookup(fib_index, pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_slash_m);
MFIB_TEST(mfei = mfei_g_m,
"%U Found via exact match",
format_mfib_prefix, pfx_star_g_slash_m);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_star_g_slash_m);
MFIB_TEST(mfei_g_m == mfib_table_lookup(fib_index, pfx_star_g_slash_m),
"%U found via LPM",
format_mfib_prefix, pfx_star_g_slash_m);
/*
* Add a for-us path
*/
mfei = mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_for_us,
MFIB_ITF_FLAG_FORWARD);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
4,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3,
DPO_RECEIVE, 0),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
/*
* remove a for-us path
*/
mfib_table_entry_path_remove(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_for_us);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
/*
* update an existing forwarding path to be only accepting
* - expect it to be removed from the replication set.
*/
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if3,
MFIB_ITF_FLAG_ACCEPT);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
2,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[1]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[3]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
/*
* Make the path forwarding again
* - expect it to be added back to the replication set
*/
mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if3,
(MFIB_ITF_FLAG_FORWARD |
MFIB_ITF_FLAG_ACCEPT |
MFIB_ITF_FLAG_NEGATE_SIGNAL));
mfei = mfib_table_lookup_exact_match(fib_index,
pfx_s_g);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[1]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[3]->sw_if_index,
(MFIB_ITF_FLAG_FORWARD |
MFIB_ITF_FLAG_ACCEPT |
MFIB_ITF_FLAG_NEGATE_SIGNAL)));
/*
* update flags on the entry
*/
mfib_table_entry_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
MFIB_RPF_ID_NONE,
MFIB_ENTRY_FLAG_SIGNAL);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_SIGNAL,
3,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2,
DPO_ADJACENCY_MCAST, ai_3),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
/*
* remove paths
*/
mfib_table_entry_path_remove(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if3);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_SIGNAL,
2,
DPO_ADJACENCY_MCAST, ai_1,
DPO_ADJACENCY_MCAST, ai_2),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[1]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[3]->sw_if_index));
mfib_table_entry_path_remove(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if1);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_SIGNAL,
1,
DPO_ADJACENCY_MCAST, ai_2),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
MFIB_ITF_FLAG_ACCEPT));
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[3]->sw_if_index));
/*
* remove the accpeting only interface
*/
mfib_table_entry_path_remove(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if0);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_SIGNAL,
1,
DPO_ADJACENCY_MCAST, ai_2),
"%U replicate OK",
format_mfib_prefix, pfx_s_g);
MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
MFIB_ITF_FLAG_FORWARD));
MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[0]->sw_if_index));
MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[1]->sw_if_index));
MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[3]->sw_if_index));
/*
* remove the last path, the entry still has flags so it remains
*/
mfib_table_entry_path_remove(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_if2);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_SIGNAL,
0),
"%U no replications",
format_mfib_prefix, pfx_s_g);
/*
* update flags on the entry
*/
mfib_table_entry_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
MFIB_RPF_ID_NONE,
(MFIB_ENTRY_FLAG_SIGNAL |
MFIB_ENTRY_FLAG_CONNECTED));
MFIB_TEST(mfib_test_entry(mfei,
(MFIB_ENTRY_FLAG_SIGNAL |
MFIB_ENTRY_FLAG_CONNECTED),
0),
"%U no replications",
format_mfib_prefix, pfx_s_g);
/*
* An entry with a NS interface
*/
mfei_g_2 = mfib_table_entry_path_update(fib_index,
pfx_star_g_2,
MFIB_SOURCE_API,
&path_via_if0,
(MFIB_ITF_FLAG_ACCEPT |
MFIB_ITF_FLAG_NEGATE_SIGNAL));
MFIB_TEST(mfib_test_entry(mfei_g_2,
MFIB_ENTRY_FLAG_NONE,
0),
"%U No replications",
format_mfib_prefix, pfx_star_g_2);
/*
* Simulate a signal from the data-plane
*/
{
mfib_entry_t *mfe;
mfib_itf_t *mfi;
mfe = mfib_entry_get(mfei_g_2);
mfi = mfib_entry_get_itf(mfe, path_via_if0.frp_sw_if_index);
mfib_signal_push(mfe, mfi, NULL);
}
/*
* An entry with a NS interface
*/
mfei_g_3 = mfib_table_entry_path_update(fib_index,
pfx_star_g_3,
MFIB_SOURCE_API,
&path_via_if0,
(MFIB_ITF_FLAG_ACCEPT |
MFIB_ITF_NEGATE_SIGNAL));
MFIB_TEST(mfib_test_entry(mfei_g_3,
MFIB_ENTRY_FLAG_NONE,
0),
"%U No replications",
format_mfib_prefix, pfx_star_g_3);
/*
* Simulate a signal from the data-plane
*/
{
mfib_entry_t *mfe;
mfib_itf_t *mfi;
mfe = mfib_entry_get(mfei_g_3);
mfi = mfib_entry_get_itf(mfe, path_via_if0.frp_sw_if_index);
mfib_signal_push(mfe, mfi, NULL);
}
if (FIB_PROTOCOL_IP6 == PROTO)
{
/*
* All the entries are present. let's ensure we can find them all
* via exact and longest prefix matches.
*/
/*
* A source address we will never match
*/
ip6_address_t src = {
.as_u64[0] = clib_host_to_net_u64(0x3001000000000000),
.as_u64[1] = clib_host_to_net_u64(0xffffffffffffffff),
};
/*
* Find the (*,G/m)
*/
MFIB_TEST((mfei_g_m == ip6_mfib_table_lookup2(
ip6_mfib_get(fib_index),
&src,
&pfx_star_g_slash_m->fp_grp_addr.ip6)),
"%U found via DP LPM grp=%U",
format_mfib_prefix, pfx_star_g_slash_m,
format_ip6_address, &pfx_star_g_slash_m->fp_grp_addr.ip6);
ip6_address_t tmp = pfx_star_g_slash_m->fp_grp_addr.ip6;
tmp.as_u8[15] = 0xff;
MFIB_TEST((mfei_g_m == ip6_mfib_table_lookup2(
ip6_mfib_get(fib_index),
&pfx_s_g->fp_src_addr.ip6,
&tmp)),
"%U found via DP LPM grp=%U",
format_mfib_prefix, pfx_star_g_slash_m,
format_ip6_address, &tmp);
/*
* Find the (S,G).
*/
mfei = ip6_mfib_table_lookup2(ip6_mfib_get(fib_index),
&pfx_s_g->fp_src_addr.ip6,
&pfx_s_g->fp_grp_addr.ip6);
MFIB_TEST((mfei_s_g == mfei),
"%U found via DP LPM: %d",
format_mfib_prefix, pfx_s_g, mfei);
/*
* Find the 3 (*,G) s
*/
mfei = ip6_mfib_table_lookup2(ip6_mfib_get(fib_index),
&src,
&pfx_star_g_1->fp_grp_addr.ip6);
MFIB_TEST((mfei_g_1 == mfei),
"%U found via DP LPM: %d",
format_mfib_prefix, pfx_star_g_1, mfei);
mfei = ip6_mfib_table_lookup2(ip6_mfib_get(fib_index),
&src,
&pfx_star_g_2->fp_grp_addr.ip6);
MFIB_TEST((mfei_g_2 == mfei),
"%U found via DP LPM: %d",
format_mfib_prefix, pfx_star_g_2, mfei);
mfei = ip6_mfib_table_lookup2(ip6_mfib_get(fib_index),
&src,
&pfx_star_g_3->fp_grp_addr.ip6);
MFIB_TEST((mfei_g_3 == mfei),
"%U found via DP LPM: %d",
format_mfib_prefix, pfx_star_g_3, mfei);
}
/*
* remove flags on the entry. This is the last of the
* state associated with the entry, so now it goes.
*/
mfib_table_entry_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
MFIB_RPF_ID_NONE,
MFIB_ENTRY_FLAG_NONE);
mfei = mfib_table_lookup_exact_match(fib_index,
pfx_s_g);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U gone",
format_mfib_prefix, pfx_s_g);
/*
* remove the last path on the no forward entry - the last entry
*/
mfib_table_entry_path_remove(fib_index,
pfx_no_forward,
MFIB_SOURCE_API,
&path_via_if0);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_no_forward);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U gone",
format_mfib_prefix, pfx_no_forward);
/*
* hard delete the (*,232.1.1.1)
*/
mfib_table_entry_delete(fib_index,
pfx_star_g_1,
MFIB_SOURCE_API);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_1);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U gone",
format_mfib_prefix, pfx_star_g_1);
/*
* remove the entry whilst the signal is pending
*/
mfib_table_entry_delete(fib_index,
pfx_star_g_2,
MFIB_SOURCE_API);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_2);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U Gone",
format_mfib_prefix, pfx_star_g_2);
mfib_table_entry_delete(fib_index,
pfx_star_g_3,
MFIB_SOURCE_API);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_3);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U Gone",
format_mfib_prefix, pfx_star_g_3);
mfib_table_entry_delete(fib_index,
pfx_star_g_slash_m,
MFIB_SOURCE_API);
mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_slash_m);
MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
"%U Gone",
format_mfib_prefix, pfx_star_g_slash_m);
/*
* Add a prefix as a special/exclusive route
*/
dpo_id_t td = DPO_INVALID;
index_t repi = replicate_create(1, fib_proto_to_dpo(PROTO));
dpo_set(&td, DPO_ADJACENCY_MCAST, fib_proto_to_dpo(PROTO), ai_2);
replicate_set_bucket(repi, 0, &td);
mfei = mfib_table_entry_special_add(fib_index,
pfx_star_g_3,
MFIB_SOURCE_SRv6,
MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
repi);
MFIB_TEST(mfib_test_entry(mfei,
(MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF |
MFIB_ENTRY_FLAG_EXCLUSIVE),
1,
DPO_ADJACENCY_MCAST, ai_2),
"%U exclusive replicate OK",
format_mfib_prefix, pfx_star_g_3);
/*
* update a special/exclusive route
*/
index_t repi2 = replicate_create(1, fib_proto_to_dpo(PROTO));
dpo_set(&td, DPO_ADJACENCY_MCAST, fib_proto_to_dpo(PROTO), ai_1);
replicate_set_bucket(repi2, 0, &td);
mfei = mfib_table_entry_special_add(fib_index,
pfx_star_g_3,
MFIB_SOURCE_SRv6,
MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
repi2);
MFIB_TEST(mfib_test_entry(mfei,
(MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF |
MFIB_ENTRY_FLAG_EXCLUSIVE),
1,
DPO_ADJACENCY_MCAST, ai_1),
"%U exclusive update replicate OK",
format_mfib_prefix, pfx_star_g_3);
mfib_table_entry_delete(fib_index,
pfx_star_g_3,
MFIB_SOURCE_SRv6);
dpo_reset(&td);
/*
* A Multicast LSP. This a mLDP head-end
*/
fib_node_index_t ai_mpls_10_10_10_1, lfei;
ip46_address_t nh_10_10_10_1 = {
.ip4 = {
.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
},
};
ai_mpls_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
VNET_LINK_MPLS,
&nh_10_10_10_1,
tm->hw[0]->sw_if_index);
fib_prefix_t pfx_3500 = {
.fp_len = 21,
.fp_proto = FIB_PROTOCOL_MPLS,
.fp_label = 3500,
.fp_eos = MPLS_EOS,
.fp_payload_proto = DPO_PROTO_IP4,
};
fib_test_rep_bucket_t mc_0 = {
.type = FT_REP_LABEL_O_ADJ,
.label_o_adj = {
.adj = ai_mpls_10_10_10_1,
.label = 3300,
.eos = MPLS_EOS,
},
};
mpls_label_t *l3300 = NULL;
vec_add1(l3300, 3300);
/*
* MPLS enable an interface so we get the MPLS table created
*/
mpls_table_create(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API, NULL);
mpls_sw_interface_enable_disable(&mpls_main,
tm->hw[0]->sw_if_index,
1, 0);
lfei = fib_table_entry_update_one_path(0, // default MPLS Table
&pfx_3500,
FIB_SOURCE_API,
FIB_ENTRY_FLAG_MULTICAST,
DPO_PROTO_IP4,
&nh_10_10_10_1,
tm->hw[0]->sw_if_index,
~0, // invalid fib index
1,
l3300,
FIB_ROUTE_PATH_FLAG_NONE);
MFIB_TEST(fib_test_validate_entry(lfei,
FIB_FORW_CHAIN_TYPE_MPLS_EOS,
1,
&mc_0),
"3500 via replicate over 10.10.10.1");
/*
* An (S,G) that resolves via the mLDP head-end
*/
fib_route_path_t path_via_mldp = {
.frp_proto = DPO_PROTO_MPLS,
.frp_local_label = pfx_3500.fp_label,
.frp_eos = MPLS_EOS,
.frp_sw_if_index = 0xffffffff,
.frp_fib_index = 0,
.frp_weight = 1,
.frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
};
dpo_id_t mldp_dpo = DPO_INVALID;
fib_entry_contribute_forwarding(lfei,
FIB_FORW_CHAIN_TYPE_MPLS_EOS,
&mldp_dpo);
mfei = mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_via_mldp,
MFIB_ITF_FLAG_FORWARD);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
1,
DPO_REPLICATE, mldp_dpo.dpoi_index),
"%U over-mLDP replicate OK",
format_mfib_prefix, pfx_s_g);
/*
* add a for-us path. this tests two types of non-attached paths on one entry
*/
mfei = mfib_table_entry_path_update(fib_index,
pfx_s_g,
MFIB_SOURCE_API,
&path_for_us,
MFIB_ITF_FLAG_FORWARD);
MFIB_TEST(mfib_test_entry(mfei,
MFIB_ENTRY_FLAG_NONE,
2,
DPO_REPLICATE, mldp_dpo.dpoi_index,
DPO_RECEIVE, 0),
"%U mLDP+for-us replicate OK",
format_mfib_prefix, pfx_s_g);
mfib_table_entry_delete(fib_index,
pfx_s_g,
MFIB_SOURCE_API);
fib_table_entry_delete(0,
&pfx_3500,
FIB_SOURCE_API);
dpo_reset(&mldp_dpo);
/*
* Unlock the table - it's the last lock so should be gone thereafter
*/
mfib_table_unlock(fib_index, PROTO, MFIB_SOURCE_API);
MFIB_TEST((FIB_NODE_INDEX_INVALID ==
mfib_table_find(PROTO, fib_index)),
"MFIB table %d gone", fib_index);
adj_unlock(ai_1);
adj_unlock(ai_2);
adj_unlock(ai_3);
/*
* MPLS disable the interface
*/
mpls_sw_interface_enable_disable(&mpls_main,
tm->hw[0]->sw_if_index,
0, 0);
mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API);
/*
* test we've leaked no resources
*/
MFIB_TEST(0 == adj_mcast_db_size(), "%d MCAST adjs", adj_mcast_db_size());
MFIB_TEST(n_pls == fib_path_list_pool_size(), "%d=%d path-lists",
n_pls, fib_path_list_pool_size());
MFIB_TEST(n_reps == pool_elts(replicate_pool), "%d=%d replicates",
n_reps, pool_elts(replicate_pool));
MFIB_TEST(n_entries == pool_elts(mfib_entry_pool),
" No more entries %d!=%d",
n_entries, pool_elts(mfib_entry_pool));
MFIB_TEST(n_itfs == pool_elts(mfib_itf_pool),
" No more Interfaces %d!=%d",
n_itfs, pool_elts(mfib_itf_pool));
return (0);
}
static int
mfib_test_v4 (void)
{
const mfib_prefix_t pfx_224_s_8 = {
.fp_len = 8,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xe0000000),
}
};
const mfib_prefix_t pfx_1_1_1_1_c_239_1_1_1 = {
.fp_len = 64,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xef010101),
},
.fp_src_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0x01010101),
},
};
const mfib_prefix_t pfx_239_1_1_1 = {
.fp_len = 32,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xef010101),
},
.fp_src_addr = {
.ip4.as_u32 = 0,
},
};
const mfib_prefix_t pfx_239_1_1_2 = {
.fp_len = 32,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xef010102),
},
.fp_src_addr = {
.ip4.as_u32 = 0,
},
};
const mfib_prefix_t pfx_239_1_1_3 = {
.fp_len = 32,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xef010103),
},
.fp_src_addr = {
.ip4.as_u32 = 0,
},
};
const mfib_prefix_t pfx_239 = {
.fp_len = 8,
.fp_proto = FIB_PROTOCOL_IP4,
.fp_grp_addr = {
.ip4.as_u32 = clib_host_to_net_u32(0xef000000),
},
.fp_src_addr = {
.ip4.as_u32 = 0,
},
};
return (mfib_test_i(FIB_PROTOCOL_IP4,
VNET_LINK_IP4,
&pfx_224_s_8,
&pfx_1_1_1_1_c_239_1_1_1,
&pfx_239_1_1_1,
&pfx_239_1_1_2,
&pfx_239_1_1_3,
&pfx_239));
}
static int
mfib_test_v6 (void)
{
const mfib_prefix_t pfx_ffd_s_12 = {
.fp_len = 12,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xffd0000000000000),
}
};
const mfib_prefix_t pfx_2001_1_c_ff_1 = {
.fp_len = 256,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xff01000000000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001),
},
.fp_src_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001),
},
};
const mfib_prefix_t pfx_ff_1 = {
.fp_len = 128,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xff01000000000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001),
},
};
const mfib_prefix_t pfx_ff_2 = {
.fp_len = 128,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xff01000000000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000002),
},
};
const mfib_prefix_t pfx_ff_3 = {
/*
* this is the ALL DHCP routers address
*/
.fp_len = 128,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xff02000100000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000002),
},
};
const mfib_prefix_t pfx_ff = {
.fp_len = 16,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6.as_u64[0] = clib_host_to_net_u64(0xff01000000000000),
.ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000000),
},
};
return (mfib_test_i(FIB_PROTOCOL_IP6,
VNET_LINK_IP6,
&pfx_ffd_s_12,
&pfx_2001_1_c_ff_1,
&pfx_ff_1,
&pfx_ff_2,
&pfx_ff_3,
&pfx_ff));
}
static clib_error_t *
mfib_test (vlib_main_t * vm,
unformat_input_t * input,
vlib_cli_command_t * cmd_arg)
{
int res = 0;
res += mfib_test_mk_intf(4);
res += mfib_test_v4();
res += mfib_test_v6();
if (res)
{
return clib_error_return(0, "MFIB Unit Test Failed");
}
else
{
return (NULL);
}
}
VLIB_CLI_COMMAND (test_fib_command, static) = {
.path = "test mfib",
.short_help = "mfib unit tests - DO NOT RUN ON A LIVE SYSTEM",
.function = mfib_test,
};
clib_error_t *
mfib_test_init (vlib_main_t *vm)
{
return 0;
}
VLIB_INIT_FUNCTION (mfib_test_init);