summaryrefslogtreecommitdiffstats
path: root/src/vnet/mfib/mfib_test.c
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2016-11-22 17:07:28 +0000
committerDamjan Marion <dmarion.lists@gmail.com>2017-01-27 19:53:46 +0000
commit32e1c010b0c34fd0984f7fc45fae648a182025c5 (patch)
tree06a440bdc9dc039ad0dcf866acc9e10a6ea5e2e7 /src/vnet/mfib/mfib_test.c
parent6f692d6e5a8ffc920a728372ef773199bc5466c0 (diff)
IP Multicast FIB (mfib)
- IPv[46] mfib tables with support for (*,G/m), (*,G) and (S,G) exact and longest prefix match - Replication represented via a new replicate DPO. - RPF configuration and data-plane checking - data-plane signals sent to listening control planes. The functions of multicast forwarding entries differ from their unicast conterparts, so we introduce a new mfib_table_t and mfib_entry_t objects. However, we re-use the fib_path_list to resolve and build the entry's output list. the fib_path_list provides the service to construct a replicate DPO for multicast. 'make tests' is added to with two new suites; TEST=mfib, this is invocation of the CLI command 'test mfib' which deals with many path add/remove, flag set/unset scenarios, TEST=ip-mcast, data-plane forwarding tests. Updated applications to use the new MIFB functions; - IPv6 NS/RA. - DHCPv6 unit tests for these are undated accordingly. Change-Id: I49ec37b01f1b170335a5697541c8fd30e6d3a961 Signed-off-by: Neale Ranns <nranns@cisco.com>
Diffstat (limited to 'src/vnet/mfib/mfib_test.c')
-rw-r--r--src/vnet/mfib/mfib_test.c1225
1 files changed, 1225 insertions, 0 deletions
diff --git a/src/vnet/mfib/mfib_test.c b/src/vnet/mfib/mfib_test.c
new file mode 100644
index 00000000000..8735bfa73fc
--- /dev/null
+++ b/src/vnet/mfib/mfib_test.c
@@ -0,0 +1,1225 @@
+/*
+ * 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/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 stacks on %U",
+ bucket,
+ 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;
+
+ 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);
+ return (!0);
+ }
+ else
+ {
+ dpo_id_t tmp = DPO_INVALID;
+ int res;
+
+ 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);
+
+ 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;
+ 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);
+ 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_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 = 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 = 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 = 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 = 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 = 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_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_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_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);
+
+ /*
+ * Unlock the table - it's the last lock so should be gone thereafter
+ */
+ mfib_table_unlock(fib_index, PROTO);
+
+ 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);
+
+ /*
+ * test we've leaked no resources
+ */
+ MFIB_TEST(0 == adj_mcast_db_size(), "%d MCAST adjs", adj_mcast_db_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 = "fib 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);