/* * 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/fib/fib_entry.h> #include <vnet/fib/fib_table.h> #include <vnet/fib/fib_walk.h> #include <vnet/fib/fib_path_list.h> #include <vnet/bier/bier_table.h> #include <vnet/bier/bier_fmask.h> #include <vnet/bier/bier_bit_string.h> #include <vnet/bier/bier_disp_table.h> #include <vnet/mpls/mpls.h> #include <vnet/dpo/drop_dpo.h> #include <vnet/dpo/load_balance.h> /* * attributes names for formatting */ static const char *const bier_fmask_attr_names[] = BIER_FMASK_ATTR_NAMES; /* * pool of BIER fmask objects */ bier_fmask_t *bier_fmask_pool; /** * Stats for each BIER fmask object */ vlib_combined_counter_main_t bier_fmask_counters; static inline index_t bier_fmask_get_index (const bier_fmask_t *bfm) { return (bfm - bier_fmask_pool); } static void bier_fmask_bits_init (bier_fmask_bits_t *bits, bier_hdr_len_id_t hlid) { bits->bfmb_refs = clib_mem_alloc(sizeof(bits->bfmb_refs[0]) * bier_hdr_len_id_to_num_bits(hlid)); clib_memset(bits->bfmb_refs, 0, (sizeof(bits->bfmb_refs[0]) * bier_hdr_len_id_to_num_bits(hlid))); bits->bfmb_input_reset_string.bbs_len = bier_hdr_len_id_to_num_buckets(hlid); /* * The buckets are accessed in the switch path */ bits->bfmb_input_reset_string.bbs_buckets = clib_mem_alloc_aligned( sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) * bier_hdr_len_id_to_num_buckets(hlid), CLIB_CACHE_LINE_BYTES); clib_memset(bits->bfmb_input_reset_string.bbs_buckets, 0, sizeof(bits->bfmb_input_reset_string.bbs_buckets[0]) * bier_hdr_len_id_to_num_buckets(hlid)); } static void bier_fmask_stack (bier_fmask_t *bfm) { dpo_id_t via_dpo = DPO_INVALID; fib_forward_chain_type_t fct; if (bfm->bfm_flags & BIER_FMASK_FLAG_MPLS) { fct = FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS; } else { fct = FIB_FORW_CHAIN_TYPE_BIER; } fib_path_list_contribute_forwarding(bfm->bfm_pl, fct, FIB_PATH_LIST_FWD_FLAG_COLLAPSE, &via_dpo); /* * If the via PL entry provides no forwarding (i.e. a drop) * then neither does this fmask. That way children consider this fmask * unresolved and other ECMP options are used instead. */ if (dpo_is_drop(&via_dpo)) { bfm->bfm_flags &= ~BIER_FMASK_FLAG_FORWARDING; } else { bfm->bfm_flags |= BIER_FMASK_FLAG_FORWARDING; } dpo_stack(DPO_BIER_FMASK, DPO_PROTO_BIER, &bfm->bfm_dpo, &via_dpo); dpo_reset(&via_dpo); } void bier_fmask_contribute_forwarding (index_t bfmi, dpo_id_t *dpo) { bier_fmask_t *bfm; bfm = bier_fmask_get(bfmi); if (bfm->bfm_flags & BIER_FMASK_FLAG_FORWARDING) { dpo_set(dpo, DPO_BIER_FMASK, DPO_PROTO_BIER, bfmi); } else { dpo_copy(dpo, drop_dpo_get(DPO_PROTO_BIER)); } } u32 bier_fmask_child_add (fib_node_index_t bfmi, fib_node_type_t child_type, fib_node_index_t child_index) { return (fib_node_child_add(FIB_NODE_TYPE_BIER_FMASK, bfmi, child_type, child_index)); }; void bier_fmask_child_remove (fib_node_index_t bfmi, u32 sibling_index) { if (INDEX_INVALID == bfmi) { return; } fib_node_child_remove(FIB_NODE_TYPE_BIER_FMASK, bfmi, sibling_index); } static void bier_fmask_init (bier_fmask_t *bfm, const bier_fmask_id_t *fmid, const fib_route_path_t *rpath) { const bier_table_id_t *btid; fib_route_path_t *rpaths; mpls_label_t olabel; clib_memset(bfm, 0, sizeof(*bfm)); bfm->bfm_id = clib_mem_alloc(sizeof(*bfm->bfm_id)); fib_node_init(&bfm->bfm_node, FIB_NODE_TYPE_BIER_FMASK); *bfm->bfm_id = *fmid; dpo_reset(&bfm->bfm_dpo); btid = bier_table_get_id(bfm->bfm_id->bfmi_bti); bier_fmask_bits_init(&bfm->bfm_bits, btid->bti_hdr_len); if (rpath->frp_flags & FIB_ROUTE_PATH_UDP_ENCAP) { bfm->bfm_id->bfmi_nh_type = BIER_NH_UDP; } else if (ip46_address_is_zero(&(bfm->bfm_id->bfmi_nh))) { bfm->bfm_flags |= BIER_FMASK_FLAG_DISP; } if (!(bfm->bfm_flags & BIER_FMASK_FLAG_DISP)) { if (NULL != rpath->frp_label_stack) { olabel = rpath->frp_label_stack[0].fml_value; vnet_mpls_uc_set_label(&bfm->bfm_label, olabel); vnet_mpls_uc_set_exp(&bfm->bfm_label, 0); vnet_mpls_uc_set_s(&bfm->bfm_label, 1); vnet_mpls_uc_set_ttl(&bfm->bfm_label, 64); bfm->bfm_flags |= BIER_FMASK_FLAG_MPLS; } else { bier_bift_id_t id; /* * not an MPLS label */ bfm->bfm_flags &= ~BIER_FMASK_FLAG_MPLS; /* * use a label as encoded for BIFT value */ id = bier_bift_id_encode(btid->bti_set, btid->bti_sub_domain, btid->bti_hdr_len); vnet_mpls_uc_set_label(&bfm->bfm_label, id); vnet_mpls_uc_set_s(&bfm->bfm_label, 1); vnet_mpls_uc_set_exp(&bfm->bfm_label, 0); vnet_mpls_uc_set_ttl(&bfm->bfm_label, 64); } bfm->bfm_label = clib_host_to_net_u32(bfm->bfm_label); } rpaths = NULL; vec_add1(rpaths, *rpath); bfm->bfm_pl = fib_path_list_create((FIB_PATH_LIST_FLAG_SHARED | FIB_PATH_LIST_FLAG_NO_URPF), rpaths); bfm->bfm_sibling = fib_path_list_child_add(bfm->bfm_pl, FIB_NODE_TYPE_BIER_FMASK, bier_fmask_get_index(bfm)); vec_free(rpaths); bier_fmask_stack(bfm); } static void bier_fmask_destroy (bier_fmask_t *bfm) { clib_mem_free(bfm->bfm_bits.bfmb_refs); clib_mem_free(bfm->bfm_bits.bfmb_input_reset_string.bbs_buckets); bier_fmask_db_remove(bfm->bfm_id); fib_path_list_child_remove(bfm->bfm_pl, bfm->bfm_sibling); dpo_reset(&bfm->bfm_dpo); clib_mem_free(bfm->bfm_id); pool_put(bier_fmask_pool, bfm); } void bier_fmask_unlock (index_t bfmi) { bier_fmask_t *bfm; if (INDEX_INVALID == bfmi) { return; } bfm = bier_fmask_get(bfmi); fib_node_unlock(&bfm->bfm_node); } void bier_fmask_lock (index_t bfmi) { bier_fmask_t *bfm; if (INDEX_INVALID == bfmi) { return; } bfm = bier_fmask_get(bfmi); fib_node_lock(&bfm->bfm_node); } index_t bier_fmask_create_and_lock (const bier_fmask_id_t *fmid, const fib_route_path_t *rpath) { bier_fmask_t *bfm; index_t bfmi; pool_get_aligned(bier_fmask_pool, bfm, CLIB_CACHE_LINE_BYTES); bfmi = bier_fmask_get_index(bfm); vlib_validate_combined_counter (&(bier_fmask_counters), bfmi); vlib_zero_combined_counter (&(bier_fmask_counters), bfmi); bier_fmask_init(bfm, fmid, rpath); bier_fmask_lock(bfmi); return (bfmi); } void bier_fmask_link (index_t bfmi, bier_bp_t bp) { bier_fmask_t *bfm; bfm = bier_fmask_get(bfmi); if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]) { /* * 0 -> 1 transition - set the bit in the string */ bier_bit_string_set_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp); } ++bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]; ++bfm->bfm_bits.bfmb_count; } void bier_fmask_unlink (index_t bfmi, bier_bp_t bp) { bier_fmask_t *bfm; bfm = bier_fmask_get(bfmi); --bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]; --bfm->bfm_bits.bfmb_count; if (0 == bfm->bfm_bits.bfmb_refs[BIER_BP_TO_INDEX(bp)]) { /* * 1 -> 0 transition - clear the bit in the string */ bier_bit_string_clear_bit(&bfm->bfm_bits.bfmb_input_reset_string, bp); } } u8* format_bier_fmask (u8 *s, va_list *ap) { index_t bfmi = va_arg(*ap, index_t); u32 indent = va_arg(*ap, u32); bier_fmask_attributes_t attr; bier_fmask_t *bfm; vlib_counter_t to; if (pool_is_free_index(bier_fmask_pool, bfmi)) { return (format(s, "No BIER f-mask %d", bfmi)); } bfm = bier_fmask_get(bfmi); s = format(s, "fmask: nh:%U bs:%U locks:%d ", format_ip46_address, &bfm->bfm_id->bfmi_nh, IP46_TYPE_ANY, format_bier_bit_string, &bfm->bfm_bits.bfmb_input_reset_string, bfm->bfm_node.fn_locks); s = format(s, "flags:"); FOR_EACH_BIER_FMASK_ATTR(attr) { if ((1<<attr) & bfm->bfm_flags) { s = format (s, "%s,", bier_fmask_attr_names[attr]); } } vlib_get_combined_counter (&(bier_fmask_counters), bfmi, &to); s = format (s, " to:[%Ld:%Ld]]", to.packets, to.bytes); s = format(s, "\n"); s = fib_path_list_format(bfm->bfm_pl, s); if (bfm->bfm_flags & BIER_FMASK_FLAG_MPLS) { s = format(s, " output-label:%U", format_mpls_unicast_label, vnet_mpls_uc_get_label(clib_net_to_host_u32(bfm->bfm_label))); } else { s = format(s, " output-bfit:[%U]", format_bier_bift_id, vnet_mpls_uc_get_label(clib_net_to_host_u32(bfm->bfm_label))); } s = format(s, "\n %U%U", format_white_space, indent, format_dpo_id, &bfm->bfm_dpo, indent+2); return (s); } void bier_fmask_get_stats (index_t bfmi, u64 * packets, u64 * bytes) { vlib_counter_t to; vlib_get_combined_counter (&(bier_fmask_counters), bfmi, &to); *packets = to.packets; *bytes = to.bytes; } void bier_fmask_encode (index_t bfmi, bier_table_id_t *btid, fib_route_path_t *rpath) { bier_fmask_t *bfm; bfm = bier_fmask_get(bfmi); *btid = *bier_table_get_id(bfm->bfm_id->bfmi_bti); clib_memset(rpath, 0, sizeof(*rpath)); rpath->frp_sw_if_index = ~0; switch (bfm->bfm_id->bfmi_nh_type) { case BIER_NH_UDP: rpath->frp_flags = FIB_ROUTE_PATH_UDP_ENCAP; rpath->frp_udp_encap_id = bfm->bfm_id->bfmi_id; break; case BIER_NH_IP: memcpy(&rpath->frp_addr, &bfm->bfm_id->bfmi_nh, sizeof(rpath->frp_addr)); break; } } static fib_node_t * bier_fmask_get_node (fib_node_index_t index) { bier_fmask_t *bfm = bier_fmask_get(index); return (&(bfm->bfm_node)); } static bier_fmask_t* bier_fmask_get_from_node (fib_node_t *node) { return ((bier_fmask_t*)(((char*)node) - STRUCT_OFFSET_OF(bier_fmask_t, bfm_node))); } /* * bier_fmask_last_lock_gone */ static void bier_fmask_last_lock_gone (fib_node_t *node) { bier_fmask_destroy(bier_fmask_get_from_node(node)); } /* * bier_fmask_back_walk_notify * * A back walk has reached this BIER fmask */ static fib_node_back_walk_rc_t bier_fmask_back_walk_notify (fib_node_t *node, fib_node_back_walk_ctx_t *ctx) { /* * re-stack the fmask on the n-eos of the via */ bier_fmask_t *bfm = bier_fmask_get_from_node(node); bier_fmask_stack(bfm); /* * propagate further up the graph. * we can do this synchronously since the fan out is small. */ fib_walk_sync(FIB_NODE_TYPE_BIER_FMASK, bier_fmask_get_index(bfm), ctx); return (FIB_NODE_BACK_WALK_CONTINUE); } /* * The BIER fmask's graph node virtual function table */ static const fib_node_vft_t bier_fmask_vft = { .fnv_get = bier_fmask_get_node, .fnv_last_lock = bier_fmask_last_lock_gone, .fnv_back_walk = bier_fmask_back_walk_notify, }; static void bier_fmask_dpo_lock (dpo_id_t *dpo) { } static void bier_fmask_dpo_unlock (dpo_id_t *dpo) { } static void bier_fmask_dpo_mem_show (void) { fib_show_memory_usage("BIER-fmask", pool_elts(bier_fmask_pool), pool_len(bier_fmask_pool), sizeof(bier_fmask_t)); } const static dpo_vft_t bier_fmask_dpo_vft = { .dv_lock = bier_fmask_dpo_lock, .dv_unlock = bier_fmask_dpo_unlock, .dv_mem_show = bier_fmask_dpo_mem_show, .dv_format = format_bier_fmask, }; const static char *const bier_fmask_mpls_nodes[] = { "bier-output", NULL }; const static char * const * const bier_fmask_nodes[DPO_PROTO_NUM] = { [DPO_PROTO_BIER] = bier_fmask_mpls_nodes, [DPO_PROTO_MPLS] = bier_fmask_mpls_nodes, }; clib_error_t * bier_fmask_module_init (vlib_main_t * vm) { fib_node_register_type (FIB_NODE_TYPE_BIER_FMASK, &bier_fmask_vft); dpo_register(DPO_BIER_FMASK, &bier_fmask_dpo_vft, bier_fmask_nodes); return (NULL); } VLIB_INIT_FUNCTION (bier_fmask_module_init); static clib_error_t * bier_fmask_show (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { bier_fmask_t *bfm; index_t bfmi; bfmi = INDEX_INVALID; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%d", &bfmi)) { ; } else { break; } } if (INDEX_INVALID == bfmi) { pool_foreach(bfm, bier_fmask_pool, ({ vlib_cli_output (vm, "[@%d] %U", bier_fmask_get_index(bfm), format_bier_fmask, bier_fmask_get_index(bfm), 0); })); } else { vlib_cli_output (vm, "%U", format_bier_fmask, bfmi, 0); } return (NULL); } VLIB_CLI_COMMAND (show_bier_fmask, static) = { .path = "show bier fmask", .short_help = "show bier fmask", .function = bier_fmask_show, };