/* * 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 #include #include #include #include #include #include #include #include #include #include #define MFIB_TEST_I(_cond, _comment, _args...) \ ({ \ int _evald = (_cond); \ if (!(_evald)) { \ fformat(stderr, "FAIL:%d: " _comment "\n", \ __LINE__, ##_args); \ res = 1; \ } \ res; \ }) #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 placeholder_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 = placeholder_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; int res; u32 i; res = 0; 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]); 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; 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 (res); } #define MFIB_TEST_REP(_cond, _comment, _args...) \ { \ if (MFIB_TEST_I(_cond, _comment, ##_args)) { \ return (1); \ } \ } 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; int res; res = 0; 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 (res); } static int mfib_test_entry (fib_node_index_t fei, mfib_entry_flags_t eflags, int n_buckets, ...) { const mfib_prefix_t *pfx; const mfib_entry_t *mfe; const replicate_t *rep; va_list ap; int res; res = 0; mfe = mfib_entry_get(fei); pfx = mfib_entry_get_prefix(fei); 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); } else { dpo_id_t tmp = DPO_INVALID; mfib_entry_contribute_forwarding( fei, mfib_forw_chain_type_from_fib_proto(pfx->fp_proto), MFIB_ENTRY_FWD_FLAG_NONE, &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); va_start(ap, n_buckets); res = mfib_test_validate_rep_v(rep, n_buckets, &ap); va_end(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_prefix_t *pfx; const mfib_entry_t *mfe; const mfib_itf_t *mfi; int res; res = 0; mfe = mfib_entry_get(fei); mfi = mfib_entry_get_itf(mfe, sw_if_index); pfx = mfib_entry_get_prefix(fei); 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 (res); } static int mfib_test_entry_no_itf (fib_node_index_t fei, u32 sw_if_index) { const mfib_prefix_t *pfx; const mfib_entry_t *mfe; const mfib_itf_t *mfi; int res; res = 0; mfe = mfib_entry_get(fei); mfi = mfib_entry_get_itf(mfe, sw_if_index); pfx = mfib_entry_get_prefix(fei); MFIB_TEST_REP((NULL == mfi), "%U has no interface %d", format_mfib_prefix, pfx, sw_if_index); return (res); } 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, const fib_prefix_t *pfx_itf, const ip46_address_t *addr_nbr1, const ip46_address_t *addr_nbr2) { 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, ai_nbr1, ai_nbr2; test_main_t *tm; int res; mfib_prefix_t all_1s; clib_memset(&all_1s, 0xfd, sizeof(all_1s)); res = 0; 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); ai_nbr1 = adj_nbr_add_or_lock(PROTO, LINKT, addr_nbr1, tm->hw[0]->sw_if_index); ai_nbr2 = adj_nbr_add_or_lock(PROTO, LINKT, addr_nbr2, tm->hw[0]->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); fib_table_entry_update_one_path(0, pfx_itf, FIB_SOURCE_INTERFACE, (FIB_ENTRY_FLAG_CONNECTED | FIB_ENTRY_FLAG_ATTACHED), DPO_PROTO_IP4, NULL, tm->hw[0]->sw_if_index, ~0, // invalid fib index 1, // weight NULL, FIB_ROUTE_PATH_FLAG_NONE); 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 = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT, }; mfib_table_entry_path_update(fib_index, pfx_no_forward, MFIB_SOURCE_API, &path_via_if0); 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(!mfib_test_entry_itf(mfei_no_f, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_ACCEPT), "%U interface not accepting", format_mfib_prefix, pfx_no_forward); 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 = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; 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 = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; 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 = 1, .frp_flags = 0, .frp_mitf_flags = (MFIB_ITF_FLAG_FORWARD | MFIB_ITF_FLAG_NEGATE_SIGNAL), }; fib_route_path_t *two_paths = NULL; vec_add1(two_paths, path_via_if2); vec_add1(two_paths, path_via_if3); /* * 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_table_entry_path_update(fib_index, pfx_s_g, MFIB_SOURCE_API, &path_via_if1); mfib_table_entry_paths_update(fib_index, pfx_s_g, MFIB_SOURCE_API, two_paths); 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_table_entry_path_update(fib_index, pfx_star_g_1, MFIB_SOURCE_API, &path_via_if1); /* * 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, "[e:%d a:%d] %U found via LP match", mfei, mfei_g_1, 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. */ path_via_if2.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT; mfei_g_m = mfib_table_entry_path_update(fib_index, pfx_star_g_slash_m, MFIB_SOURCE_API, &path_via_if2); mfib_table_entry_path_update(fib_index, pfx_star_g_slash_m, MFIB_SOURCE_API, &path_via_if3); /* * 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 */ 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 = 1, .frp_flags = FIB_ROUTE_PATH_LOCAL, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; mfei = mfib_table_entry_path_update(fib_index, pfx_s_g, MFIB_SOURCE_API, &path_for_us); 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. */ path_via_if3.frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT; mfib_table_entry_path_update(fib_index, pfx_s_g, MFIB_SOURCE_API, &path_via_if3); 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 */ path_via_if3.frp_mitf_flags = (MFIB_ITF_FLAG_FORWARD | MFIB_ITF_FLAG_ACCEPT | MFIB_ITF_FLAG_NEGATE_SIGNAL); mfib_table_entry_path_update(fib_index, pfx_s_g, MFIB_SOURCE_API, &path_via_if3); 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 */ /* 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 and the accepting only interface, * the entry still has flags so it remains */ vec_reset_length(two_paths); vec_add1(two_paths, path_via_if0); vec_add1(two_paths, path_via_if2); mfib_table_entry_paths_remove(fib_index, pfx_s_g, MFIB_SOURCE_API, two_paths); 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 */ path_via_if0.frp_mitf_flags = (MFIB_ITF_FLAG_ACCEPT | MFIB_ITF_FLAG_NEGATE_SIGNAL); mfei_g_2 = mfib_table_entry_path_update(fib_index, pfx_star_g_2, MFIB_SOURCE_API, &path_via_if0); 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 */ path_via_if0.frp_mitf_flags = (MFIB_ITF_FLAG_ACCEPT | MFIB_ITF_FLAG_NEGATE_SIGNAL); mfei_g_3 = mfib_table_entry_path_update(fib_index, pfx_star_g_3, MFIB_SOURCE_API, &path_via_if0); 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_fwd_lookup( 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_fwd_lookup( 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_fwd_lookup(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_fwd_lookup(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_fwd_lookup(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_fwd_lookup(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); /* * Entries with paths via unicast next-hops */ fib_route_path_t path_via_nbr1 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = *addr_nbr1, .frp_sw_if_index = tm->hw[0]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; fib_route_path_t path_via_nbr2 = { .frp_proto = fib_proto_to_dpo(PROTO), .frp_addr = *addr_nbr2, .frp_sw_if_index = tm->hw[0]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; mfei_g_1 = mfib_table_entry_path_update(fib_index, pfx_star_g_1, MFIB_SOURCE_API, &path_via_nbr1); mfei_g_1 = mfib_table_entry_path_update(fib_index, pfx_star_g_1, MFIB_SOURCE_API, &path_via_nbr2); MFIB_TEST(!mfib_test_entry(mfei_g_1, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_INCOMPLETE, ai_nbr1, DPO_ADJACENCY_INCOMPLETE, ai_nbr2), "%U replicate OK", format_mfib_prefix, pfx_star_g_1); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_g_1, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); mfib_table_entry_path_remove(fib_index, pfx_star_g_1, MFIB_SOURCE_API, &path_via_nbr1); MFIB_TEST(!mfib_test_entry(mfei_g_1, MFIB_ENTRY_FLAG_NONE, 1, DPO_ADJACENCY_INCOMPLETE, ai_nbr2), "%U replicate OK", format_mfib_prefix, pfx_star_g_1); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_g_1, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); mfib_table_entry_path_remove(fib_index, pfx_star_g_1, MFIB_SOURCE_API, &path_via_nbr2); 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); /* * 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); mfib_entry_update(mfei, MFIB_SOURCE_SRv6, (MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF | MFIB_ENTRY_FLAG_EXCLUSIVE), MFIB_RPF_ID_NONE, 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, }, }; fib_mpls_label_t *l3300 = NULL, fml3300 = { .fml_value = 3300, }; vec_add1(l3300, fml3300); /* * 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); 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, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; 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_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_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_TEST(((PROTO == FIB_PROTOCOL_IP4 ? 3 : 5) == mfib_table_get_n_routes(fib_index, PROTO)), "1 = %d route left in the FIB", mfib_table_get_n_routes(fib_index, PROTO)); 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); adj_unlock(ai_nbr1); adj_unlock(ai_nbr2); /* * MPLS disable the interface */ mpls_sw_interface_enable_disable (&mpls_main, tm->hw[0]->sw_if_index, 0); mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API); /* * remove the connected */ fib_table_entry_delete(0, pfx_itf, FIB_SOURCE_INTERFACE); /* * 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)); vec_free(two_paths); return (res); } 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, }, }; const fib_prefix_t pfx_itf = { .fp_len = 24, .fp_proto = FIB_PROTOCOL_IP4, .fp_addr = { .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0a), }, }; const ip46_address_t nbr1 = { .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0b), }; const ip46_address_t nbr2 = { .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0c), }; 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, &pfx_itf, &nbr1, &nbr2)); } 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), }, }; const fib_prefix_t pfx_itf = { .fp_len = 64, .fp_proto = FIB_PROTOCOL_IP6, .fp_addr = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001), }, }; const ip46_address_t nbr1 = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000002), }; const ip46_address_t nbr2 = { .ip6.as_u64[0] = clib_host_to_net_u64(0x2001000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000003), }; 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, &pfx_itf, &nbr1, &nbr2)); } static int mfib_test_rr_i (fib_protocol_t FPROTO, dpo_proto_t DPROTO, vnet_link_t LINKT, const mfib_prefix_t *pfx_cover, const mfib_prefix_t *pfx_host1, const mfib_prefix_t *pfx_host2) { fib_node_index_t mfei_cover, mfei_host1, mfei_host2, ai_1, ai_2; u32 fib_index, n_entries, n_itfs, n_reps, n_pls; test_main_t *tm; int res; res = 0; 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; fib_index = 0; ai_1 = adj_mcast_add_or_lock(FPROTO, LINKT, tm->hw[1]->sw_if_index); ai_2 = adj_mcast_add_or_lock(FPROTO, LINKT, tm->hw[2]->sw_if_index); fib_route_path_t path_via_if0 = { .frp_proto = DPROTO, .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[0]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_ACCEPT, }; fib_route_path_t path_via_if1 = { .frp_proto = DPROTO, .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[1]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; fib_route_path_t path_via_if2 = { .frp_proto = DPROTO, .frp_addr = zero_addr, .frp_sw_if_index = tm->hw[2]->sw_if_index, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = 0, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; fib_route_path_t path_for_us = { .frp_proto = DPROTO, .frp_addr = zero_addr, .frp_sw_if_index = 0xffffffff, .frp_fib_index = ~0, .frp_weight = 1, .frp_flags = FIB_ROUTE_PATH_LOCAL, .frp_mitf_flags = MFIB_ITF_FLAG_FORWARD, }; /* * with only the default in place, recusre thru the /32 */ mfei_host1 = mfib_table_entry_special_add(fib_index, pfx_host1, MFIB_SOURCE_RR, MFIB_ENTRY_FLAG_NONE, INDEX_INVALID); /* * expect its forwarding to match the cover's */ MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_DROP, 0), "%U no replications OK", format_mfib_prefix, pfx_host1); /* * Insert the less specific /28 */ mfib_table_entry_path_update(fib_index, pfx_cover, MFIB_SOURCE_API, &path_via_if1); mfei_cover = mfib_table_lookup_exact_match(fib_index, pfx_cover); MFIB_TEST(!mfib_test_entry(mfei_cover, MFIB_ENTRY_FLAG_NONE, 1, DPO_ADJACENCY_MCAST, ai_1), "%U replicate OK", format_mfib_prefix, pfx_cover); /* * expect the /32 forwarding to match the new cover's */ MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 1, DPO_ADJACENCY_MCAST, ai_1), "%U replicate OK", format_mfib_prefix, pfx_host1); /* * add another path to the cover */ mfib_table_entry_path_update(fib_index, pfx_cover, MFIB_SOURCE_API, &path_via_if2); /* * expect the /32 and /28 to be via both boths */ MFIB_TEST(!mfib_test_entry(mfei_cover, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_cover); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_host1); /* * and the other host whilst all is ready */ mfei_host2 = mfib_table_entry_special_add(fib_index, pfx_host2, MFIB_SOURCE_RR, MFIB_ENTRY_FLAG_NONE, INDEX_INVALID); MFIB_TEST(!mfib_test_entry(mfei_host2, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_host2); /* * repaet multiple time to simulate multiple recursve children */ mfei_host2 = mfib_table_entry_special_add(fib_index, pfx_host2, MFIB_SOURCE_RR, MFIB_ENTRY_FLAG_NONE, INDEX_INVALID); mfei_host2 = mfib_table_entry_special_add(fib_index, pfx_host2, MFIB_SOURCE_RR, MFIB_ENTRY_FLAG_NONE, INDEX_INVALID); mfei_host2 = mfib_table_entry_special_add(fib_index, pfx_host2, MFIB_SOURCE_RR, MFIB_ENTRY_FLAG_NONE, INDEX_INVALID); /* * add an accepting path to the cover */ mfib_table_entry_path_update(fib_index, pfx_cover, MFIB_SOURCE_API, &path_via_if0); /* * expect the /32 and /28 to be via both boths */ MFIB_TEST(!mfib_test_entry(mfei_cover, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_cover); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 2, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_cover); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_host1, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_ACCEPT)); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_cover, tm->hw[0]->sw_if_index, MFIB_ITF_FLAG_ACCEPT)); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_host1, tm->hw[1]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_cover, tm->hw[1]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_host1, tm->hw[2]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); MFIB_TEST_NS(!mfib_test_entry_itf(mfei_cover, tm->hw[2]->sw_if_index, MFIB_ITF_FLAG_FORWARD)); /* * add a for-us path to the cover */ mfib_table_entry_path_update(fib_index, pfx_cover, MFIB_SOURCE_API, &path_for_us); /* * expect the /32 and /28 to be via all three paths */ MFIB_TEST(!mfib_test_entry(mfei_cover, MFIB_ENTRY_FLAG_NONE, 3, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2, DPO_RECEIVE, 0), "%U replicate OK", format_mfib_prefix, pfx_cover); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 3, DPO_ADJACENCY_MCAST, ai_1, DPO_ADJACENCY_MCAST, ai_2, DPO_RECEIVE, 0), "%U replicate OK", format_mfib_prefix, pfx_cover); /* * get the forwarding chain from the RR prefix */ replicate_t *rep; dpo_id_t dpo = DPO_INVALID; mfib_entry_contribute_forwarding( mfei_host1, mfib_forw_chain_type_from_dpo_proto(DPROTO), MFIB_ENTRY_FWD_FLAG_NONE, &dpo); rep = replicate_get(dpo.dpoi_index); MFIB_TEST((3 == rep->rep_n_buckets), "%U replicate 3 buckets", format_mfib_prefix, pfx_host1); /* * get the forwarding chain from the RR prefix without local paths */ mfib_entry_contribute_forwarding( mfei_host1, mfib_forw_chain_type_from_dpo_proto(DPROTO), MFIB_ENTRY_FWD_FLAG_NO_LOCAL, &dpo); rep = replicate_get(dpo.dpoi_index); MFIB_TEST((2 == rep->rep_n_buckets), "%U no-local replicate 2 buckets", format_mfib_prefix, pfx_host1); dpo_reset(&dpo); /* * delete the cover, expect the /32 to be via the default */ mfib_table_entry_delete(fib_index, pfx_cover, MFIB_SOURCE_API); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_DROP, 0), "%U no replications OK", format_mfib_prefix, pfx_host1); /* * source the /32 with its own path */ mfei_host1 = mfib_table_entry_path_update(fib_index, pfx_host1, MFIB_SOURCE_API, &path_via_if2); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_host1); /* * remove host2 - as many times as it was added */ mfib_table_entry_delete(fib_index, pfx_host2, MFIB_SOURCE_RR); mfib_table_entry_delete(fib_index, pfx_host2, MFIB_SOURCE_RR); mfib_table_entry_delete(fib_index, pfx_host2, MFIB_SOURCE_RR); mfib_table_entry_delete(fib_index, pfx_host2, MFIB_SOURCE_RR); /* * remove the RR source with paths present */ mfib_table_entry_delete(fib_index, pfx_host1, MFIB_SOURCE_RR); /* * add the RR back then remove the path and RR */ mfei_host1 = mfib_table_entry_path_update(fib_index, pfx_host1, MFIB_SOURCE_API, &path_via_if2); MFIB_TEST(!mfib_test_entry(mfei_host1, MFIB_ENTRY_FLAG_NONE, 1, DPO_ADJACENCY_MCAST, ai_2), "%U replicate OK", format_mfib_prefix, pfx_host1); mfib_table_entry_delete(fib_index, pfx_host1, MFIB_SOURCE_API); mfib_table_entry_delete(fib_index, pfx_host1, MFIB_SOURCE_RR); /* * test we've leaked no resources */ adj_unlock(ai_1); adj_unlock(ai_2); 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 (res); } static int mfib_test_rr_v4 (void) { /* * 2 length of prefix to play with */ const mfib_prefix_t pfx_host1 = { .fp_len = 32, .fp_proto = FIB_PROTOCOL_IP4, .fp_grp_addr = { .ip4.as_u32 = clib_host_to_net_u32(0xe0001011), }, }; const mfib_prefix_t pfx_host2 = { .fp_len = 64, .fp_proto = FIB_PROTOCOL_IP4, .fp_grp_addr = { .ip4.as_u32 = clib_host_to_net_u32(0xe0001011), }, .fp_src_addr = { .ip4.as_u32 = clib_host_to_net_u32(0x10101010), }, }; const mfib_prefix_t pfx_cover = { .fp_len = 28, .fp_proto = FIB_PROTOCOL_IP4, .fp_grp_addr = { .ip4.as_u32 = clib_host_to_net_u32(0xe0001010), }, }; return (mfib_test_rr_i(FIB_PROTOCOL_IP4, DPO_PROTO_IP4, VNET_LINK_IP4, &pfx_cover, &pfx_host1, &pfx_host2)); } static int mfib_test_rr_v6 (void) { /* * 2 length of prefix to play with */ const mfib_prefix_t pfx_host1 = { .fp_len = 128, .fp_proto = FIB_PROTOCOL_IP6, .fp_grp_addr = { .ip6.as_u64[0] = clib_host_to_net_u64(0xff03000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000001), }, }; const mfib_prefix_t pfx_host2 = { .fp_len = 256, .fp_proto = FIB_PROTOCOL_IP6, .fp_grp_addr = { .ip6.as_u64[0] = clib_host_to_net_u64(0xff03000000000000), .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_cover = { .fp_len = 64, .fp_proto = FIB_PROTOCOL_IP6, .fp_grp_addr = { .ip6.as_u64[0] = clib_host_to_net_u64(0xff03000000000000), .ip6.as_u64[1] = clib_host_to_net_u64(0x0000000000000000), }, }; return (mfib_test_rr_i(FIB_PROTOCOL_IP6, DPO_PROTO_IP6, VNET_LINK_IP6, &pfx_cover, &pfx_host1, &pfx_host2)); } 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_rr_v4(); if (res) { return clib_error_return(0, "MFIB RR V4 Unit Test Failed"); } res += mfib_test_rr_v6(); if (res) { return clib_error_return(0, "MFIB RR V6 Unit Test Failed"); } res += mfib_test_v4(); if (res) { return clib_error_return(0, "MFIB V4 Unit Test Failed"); } res += mfib_test_v6(); if (res) { return clib_error_return(0, "MFIB V6 Unit Test Failed"); } 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);