/* * Copyright (c) 2017 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. */ /** * bier_disposition : The BIER disposition object * * A BIER disposition object is present in the IP mcast output list * and represents the disposition of a BIER bitmask. After BIER header * disposition the packet is forward within the appropriate/specified * BIER table */ #include <vnet/bier/bier_disp_entry.h> #include <vnet/bier/bier_hdr_inlines.h> #include <vnet/fib/fib_path_list.h> #include <vnet/dpo/drop_dpo.h> /** * The memory pool of all imp objects */ bier_disp_entry_t *bier_disp_entry_pool; /** * When constructing the BIER imp ID from an index and BSL, shift * the BSL this far */ #define BIER_DISP_ENTRY_ID_HLEN_SHIFT 24 static void bier_disp_entry_lock_i (bier_disp_entry_t *bde) { bde->bde_locks++; } void bier_disp_entry_lock (index_t bdei) { bier_disp_entry_lock_i(bier_disp_entry_get(bdei)); } static index_t bier_disp_entry_get_index(bier_disp_entry_t *bde) { return (bde - bier_disp_entry_pool); } index_t bier_disp_entry_add_or_lock (void) { dpo_id_t invalid = DPO_INVALID; bier_hdr_proto_id_t pproto; bier_disp_entry_t *bde; pool_get_aligned(bier_disp_entry_pool, bde, CLIB_CACHE_LINE_BYTES); bde->bde_locks = 0; FOR_EACH_BIER_HDR_PROTO(pproto) { bde->bde_fwd[pproto].bde_dpo = invalid; bde->bde_fwd[pproto].bde_rpf_id = ~0; bde->bde_pl[pproto] = FIB_NODE_INDEX_INVALID; } bier_disp_entry_lock_i(bde); return (bier_disp_entry_get_index(bde)); } void bier_disp_entry_unlock (index_t bdei) { bier_disp_entry_t *bde; if (INDEX_INVALID == bdei) { return; } bde = bier_disp_entry_get(bdei); bde->bde_locks--; if (0 == bde->bde_locks) { bier_hdr_proto_id_t pproto; FOR_EACH_BIER_HDR_PROTO(pproto) { dpo_unlock(&bde->bde_fwd[pproto].bde_dpo); bde->bde_fwd[pproto].bde_rpf_id = ~0; fib_path_list_unlock(bde->bde_pl[pproto]); } pool_put(bier_disp_entry_pool, bde); } } typedef struct bier_disp_entry_path_list_walk_ctx_t_ { u32 bdew_rpf_id; } bier_disp_entry_path_list_walk_ctx_t; static fib_path_list_walk_rc_t bier_disp_entry_path_list_walk (fib_node_index_t pl_index, fib_node_index_t path_index, void *arg) { bier_disp_entry_path_list_walk_ctx_t *ctx = arg; ctx->bdew_rpf_id = fib_path_get_rpf_id(path_index); if (~0 != ctx->bdew_rpf_id) { return (FIB_PATH_LIST_WALK_STOP); } return (FIB_PATH_LIST_WALK_CONTINUE); } static void bier_disp_entry_restack (bier_disp_entry_t *bde, bier_hdr_proto_id_t pproto) { dpo_id_t via_dpo = DPO_INVALID; fib_node_index_t pli; pli = bde->bde_pl[pproto]; if (FIB_NODE_INDEX_INVALID == pli) { dpo_copy(&via_dpo, drop_dpo_get(bier_hdr_proto_to_dpo(pproto))); } else { fib_path_list_contribute_forwarding(pli, fib_forw_chain_type_from_dpo_proto( bier_hdr_proto_to_dpo(pproto)), FIB_PATH_LIST_FWD_FLAG_COLLAPSE, &via_dpo); bier_disp_entry_path_list_walk_ctx_t ctx = { .bdew_rpf_id = ~0, }; fib_path_list_walk(pli, bier_disp_entry_path_list_walk, &ctx); bde->bde_fwd[pproto].bde_rpf_id = ctx.bdew_rpf_id; } dpo_stack(DPO_BIER_DISP_ENTRY, DPO_PROTO_BIER, &bde->bde_fwd[pproto].bde_dpo, &via_dpo); } void bier_disp_entry_path_add (index_t bdei, bier_hdr_proto_id_t pproto, const fib_route_path_t *rpaths) { fib_node_index_t *pli, old_pli; bier_disp_entry_t *bde; bde = bier_disp_entry_get(bdei); pli = &bde->bde_pl[pproto]; old_pli = *pli; /* * create a new or update the existing path-list for this * payload protocol */ if (FIB_NODE_INDEX_INVALID == *pli) { *pli = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); } else { *pli = fib_path_list_copy_and_path_add(old_pli, (FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); } fib_path_list_lock(*pli); fib_path_list_unlock(old_pli); bier_disp_entry_restack(bde, pproto); } int bier_disp_entry_path_remove (index_t bdei, bier_hdr_proto_id_t pproto, const fib_route_path_t *rpaths) { fib_node_index_t *pli, old_pli; bier_disp_entry_t *bde; bde = bier_disp_entry_get(bdei); pli = &bde->bde_pl[pproto]; old_pli = *pli; /* * update the existing path-list for this payload protocol */ if (FIB_NODE_INDEX_INVALID != *pli) { *pli = fib_path_list_copy_and_path_remove(old_pli, (FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); fib_path_list_lock(*pli); fib_path_list_unlock(old_pli); bier_disp_entry_restack(bde, pproto); } /* * if there are no path-list defined for any payload protocol * then this entry is OK for removal */ int remove = 1; FOR_EACH_BIER_HDR_PROTO(pproto) { if (FIB_NODE_INDEX_INVALID != bde->bde_pl[pproto]) { remove = 0; break; } } return (remove); } u8* format_bier_disp_entry (u8* s, va_list *args) { index_t bdei = va_arg (*args, index_t); u32 indent = va_arg(*args, u32); bier_show_flags_t flags = va_arg(*args, bier_show_flags_t); bier_hdr_proto_id_t pproto; bier_disp_entry_t *bde; bde = bier_disp_entry_get(bdei); s = format(s, "%Ubier-disp:[%d]", format_white_space, indent, bdei); FOR_EACH_BIER_HDR_PROTO(pproto) { if (INDEX_INVALID != bde->bde_pl[pproto]) { s = format(s, "\n%U%U\n", format_white_space, indent+2, format_bier_hdr_proto, pproto); s = format(s, "%U", format_fib_path_list, bde->bde_pl[pproto], indent+4); if (flags & BIER_SHOW_DETAIL) { s = format(s, "\n%UForwarding:", format_white_space, indent+4); s = format(s, "\n%Urpf-id:%d", format_white_space, indent+6, bde->bde_fwd[pproto].bde_rpf_id); s = format(s, "\n%U%U", format_white_space, indent+6, format_dpo_id, &bde->bde_fwd[pproto].bde_dpo, indent+2); } } } return (s); } void bier_disp_entry_contribute_forwarding (index_t bdei, dpo_id_t *dpo) { dpo_set(dpo, DPO_BIER_DISP_ENTRY, DPO_PROTO_BIER, bdei); } const static char* const bier_disp_entry_bier_nodes[] = { "bier-disp-dispatch", NULL, }; const static char* const * const bier_disp_entry_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_BIER] = bier_disp_entry_bier_nodes, }; static void bier_disp_entry_dpo_lock (dpo_id_t *dpo) { bier_disp_entry_lock(dpo->dpoi_index); } static void bier_disp_entry_dpo_unlock (dpo_id_t *dpo) { bier_disp_entry_unlock(dpo->dpoi_index); } static void bier_disp_entry_dpo_mem_show (void) { fib_show_memory_usage("BIER disposition", pool_elts(bier_disp_entry_pool), pool_len(bier_disp_entry_pool), sizeof(bier_disp_entry_t)); } static u8* format_bier_disp_entry_dpo (u8* s, va_list *ap) { index_t index = va_arg(*ap, index_t); u32 indent = va_arg(*ap, u32); s = format(s, "%U", format_bier_disp_entry, index, indent, BIER_SHOW_DETAIL); return (s); } const static dpo_vft_t bier_disp_entry_vft = { .dv_lock = bier_disp_entry_dpo_lock, .dv_unlock = bier_disp_entry_dpo_unlock, .dv_format = format_bier_disp_entry_dpo, .dv_mem_show = bier_disp_entry_dpo_mem_show, }; clib_error_t * bier_disp_entry_db_module_init (vlib_main_t *vm) { dpo_register(DPO_BIER_DISP_ENTRY, &bier_disp_entry_vft, bier_disp_entry_nodes); return (NULL); } VLIB_INIT_FUNCTION (bier_disp_entry_db_module_init); static clib_error_t * show_bier_disp_entry (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { index_t bdei; bdei = INDEX_INVALID; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%d", &bdei)) ; else { break; } } if (INDEX_INVALID == bdei) { return (NULL); } else { if (pool_is_free_index(bier_disp_entry_pool, bdei)) { vlib_cli_output(vm, "No such BIER disp entry: %d", bdei); } else { vlib_cli_output(vm, "%U", format_bier_disp_entry, bdei, 1, BIER_SHOW_DETAIL); } } return (NULL); } VLIB_CLI_COMMAND (show_bier_disp_entry_node, static) = { .path = "show bier disp entry", .short_help = "show bier disp entry index", .function = show_bier_disp_entry, };