| /* |
| * mpls_fib.h: The Label/MPLS FIB |
| * |
| * Copyright (c) 2012 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. |
| */ |
| /** |
| * An MPLS_FIB table; |
| * |
| * The entries in the table are programmed wtih one or more MOIs. These MOIs |
| * may result in different forwarding actions for end-of-stack (EOS) and non-EOS |
| * packets. Whether the two actions are the same more often than they are |
| * different, or vice versa, is a function of the deployment in which the router |
| * is used and thus not predictable. |
| * The desgin choice to make with an MPLS_FIB table is: |
| * 1 - 20 bit key: label only. |
| * When the EOS and non-EOS actions differ the result is a 'EOS-choice' object. |
| * 2 - 21 bit key: label and EOS-bit. |
| * The result is then the specific action based on EOS-bit. |
| * |
| * 20 bit key: |
| * Advantages: |
| * - lower memory overhead, since there are few DB entries. |
| * Disadvantages: |
| * - slower DP performance in the case the chains differ, as more objects are |
| * encounterd in the switch path |
| * |
| * 21 bit key: |
| * Advantages: |
| * - faster DP performance |
| * Disadvantages |
| * - increased memory footprint. |
| * |
| * Switching between schemes based on observed/measured action similarity is not |
| * considered on the grounds of complexity and flip-flopping. |
| * |
| * VPP mantra - favour performance over memory. We choose a 21 bit key. |
| */ |
| |
| #include <vnet/fib/fib_table.h> |
| #include <vnet/fib/mpls_fib.h> |
| #include <vnet/dpo/load_balance.h> |
| #include <vnet/dpo/drop_dpo.h> |
| #include <vnet/dpo/punt_dpo.h> |
| #include <vnet/dpo/lookup_dpo.h> |
| #include <vnet/mpls/mpls.h> |
| |
| /** |
| * All lookups in an MPLS_FIB table must result in a DPO of type load-balance. |
| * This is the default result which links to drop |
| */ |
| static index_t mpls_fib_drop_dpo_index = INDEX_INVALID; |
| |
| static inline u32 |
| mpls_fib_entry_mk_key (mpls_label_t label, |
| mpls_eos_bit_t eos) |
| { |
| ASSERT(eos <= 1); |
| return (label << 1 | eos); |
| } |
| |
| u32 |
| mpls_fib_index_from_table_id (u32 table_id) |
| { |
| mpls_main_t *mm = &mpls_main; |
| uword * p; |
| |
| p = hash_get (mm->fib_index_by_table_id, table_id); |
| if (!p) |
| return FIB_NODE_INDEX_INVALID; |
| |
| return p[0]; |
| } |
| |
| static u32 |
| mpls_fib_create_with_table_id (u32 table_id) |
| { |
| dpo_id_t dpo = DPO_INVALID; |
| fib_table_t *fib_table; |
| mpls_eos_bit_t eos; |
| mpls_fib_t *mf; |
| int i; |
| |
| pool_get_aligned(mpls_main.fibs, fib_table, CLIB_CACHE_LINE_BYTES); |
| pool_get_aligned(mpls_main.mpls_fibs, mf, CLIB_CACHE_LINE_BYTES); |
| |
| ASSERT((fib_table - mpls_main.fibs) == |
| (mf - mpls_main.mpls_fibs)); |
| |
| memset(fib_table, 0, sizeof(*fib_table)); |
| |
| fib_table->ft_proto = FIB_PROTOCOL_MPLS; |
| fib_table->ft_index = (fib_table - mpls_main.fibs); |
| |
| hash_set (mpls_main.fib_index_by_table_id, table_id, fib_table->ft_index); |
| |
| fib_table->ft_table_id = table_id; |
| fib_table->ft_flow_hash_config = MPLS_FLOW_HASH_DEFAULT; |
| |
| fib_table_lock(fib_table->ft_index, FIB_PROTOCOL_MPLS); |
| |
| if (INDEX_INVALID == mpls_fib_drop_dpo_index) |
| { |
| mpls_fib_drop_dpo_index = load_balance_create(1, DPO_PROTO_MPLS, 0); |
| load_balance_set_bucket(mpls_fib_drop_dpo_index, |
| 0, |
| drop_dpo_get(DPO_PROTO_MPLS)); |
| } |
| |
| mf->mf_entries = hash_create(0, sizeof(fib_node_index_t)); |
| for (i = 0; i < MPLS_FIB_DB_SIZE; i++) |
| { |
| /* |
| * initialise each DPO in the data-path lookup table |
| * to be the special MPLS drop |
| */ |
| mf->mf_lbs[i] = mpls_fib_drop_dpo_index; |
| } |
| |
| /* |
| * non-default forwarding for the special labels. |
| */ |
| fib_prefix_t prefix = { |
| .fp_proto = FIB_PROTOCOL_MPLS, |
| .fp_payload_proto = DPO_PROTO_MPLS, |
| }; |
| |
| /* |
| * PUNT the router alert, both EOS and non-eos |
| */ |
| prefix.fp_label = MPLS_IETF_ROUTER_ALERT_LABEL; |
| FOR_EACH_MPLS_EOS_BIT(eos) |
| { |
| prefix.fp_eos = eos; |
| fib_table_entry_special_dpo_add(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL, |
| FIB_ENTRY_FLAG_EXCLUSIVE, |
| punt_dpo_get(DPO_PROTO_MPLS)); |
| } |
| |
| /* |
| * IPv4 explicit NULL EOS lookup in the interface's IPv4 table |
| */ |
| prefix.fp_label = MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL; |
| prefix.fp_payload_proto = DPO_PROTO_IP4; |
| prefix.fp_eos = MPLS_EOS; |
| |
| lookup_dpo_add_or_lock_w_fib_index(0, // unused |
| DPO_PROTO_IP4, |
| LOOKUP_UNICAST, |
| LOOKUP_INPUT_DST_ADDR, |
| LOOKUP_TABLE_FROM_INPUT_INTERFACE, |
| &dpo); |
| fib_table_entry_special_dpo_add(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL, |
| FIB_ENTRY_FLAG_EXCLUSIVE, |
| &dpo); |
| |
| prefix.fp_payload_proto = DPO_PROTO_MPLS; |
| prefix.fp_eos = MPLS_NON_EOS; |
| |
| lookup_dpo_add_or_lock_w_fib_index(0, //unsued |
| DPO_PROTO_MPLS, |
| LOOKUP_UNICAST, |
| LOOKUP_INPUT_DST_ADDR, |
| LOOKUP_TABLE_FROM_INPUT_INTERFACE, |
| &dpo); |
| fib_table_entry_special_dpo_add(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL, |
| FIB_ENTRY_FLAG_EXCLUSIVE, |
| &dpo); |
| |
| /* |
| * IPv6 explicit NULL EOS lookup in the interface's IPv6 table |
| */ |
| prefix.fp_label = MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL; |
| prefix.fp_payload_proto = DPO_PROTO_IP6; |
| prefix.fp_eos = MPLS_EOS; |
| |
| lookup_dpo_add_or_lock_w_fib_index(0, //unused |
| DPO_PROTO_IP6, |
| LOOKUP_UNICAST, |
| LOOKUP_INPUT_DST_ADDR, |
| LOOKUP_TABLE_FROM_INPUT_INTERFACE, |
| &dpo); |
| fib_table_entry_special_dpo_add(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL, |
| FIB_ENTRY_FLAG_EXCLUSIVE, |
| &dpo); |
| |
| prefix.fp_payload_proto = DPO_PROTO_MPLS; |
| prefix.fp_eos = MPLS_NON_EOS; |
| lookup_dpo_add_or_lock_w_fib_index(0, // unsued |
| DPO_PROTO_MPLS, |
| LOOKUP_UNICAST, |
| LOOKUP_INPUT_DST_ADDR, |
| LOOKUP_TABLE_FROM_INPUT_INTERFACE, |
| &dpo); |
| fib_table_entry_special_dpo_add(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL, |
| FIB_ENTRY_FLAG_EXCLUSIVE, |
| &dpo); |
| |
| return (fib_table->ft_index); |
| } |
| |
| u32 |
| mpls_fib_table_find_or_create_and_lock (u32 table_id) |
| { |
| u32 index; |
| |
| index = mpls_fib_index_from_table_id(table_id); |
| if (~0 == index) |
| return mpls_fib_create_with_table_id(table_id); |
| |
| fib_table_lock(index, FIB_PROTOCOL_MPLS); |
| |
| return (index); |
| } |
| u32 |
| mpls_fib_table_create_and_lock (void) |
| { |
| return (mpls_fib_create_with_table_id(~0)); |
| } |
| |
| void |
| mpls_fib_table_destroy (u32 fib_index) |
| { |
| fib_table_t *fib_table = pool_elt_at_index(mpls_main.fibs, fib_index); |
| mpls_fib_t *mf = pool_elt_at_index(mpls_main.mpls_fibs, fib_index); |
| fib_prefix_t prefix = { |
| .fp_proto = FIB_PROTOCOL_MPLS, |
| }; |
| mpls_label_t special_labels[] = { |
| MPLS_IETF_ROUTER_ALERT_LABEL, |
| MPLS_IETF_IPV6_EXPLICIT_NULL_LABEL, |
| MPLS_IETF_IPV4_EXPLICIT_NULL_LABEL, |
| }; |
| mpls_eos_bit_t eos; |
| u32 ii; |
| |
| for (ii = 0; ii < ARRAY_LEN(special_labels); ii++) |
| { |
| FOR_EACH_MPLS_EOS_BIT(eos) |
| { |
| prefix.fp_label = special_labels[ii]; |
| prefix.fp_eos = eos; |
| |
| fib_table_entry_delete(fib_table->ft_index, |
| &prefix, |
| FIB_SOURCE_SPECIAL); |
| } |
| } |
| if (~0 != fib_table->ft_table_id) |
| { |
| hash_unset(mpls_main.fib_index_by_table_id, |
| fib_table->ft_table_id); |
| } |
| hash_free(mf->mf_entries); |
| |
| pool_put(mpls_main.mpls_fibs, mf); |
| pool_put(mpls_main.fibs, fib_table); |
| } |
| |
| fib_node_index_t |
| mpls_fib_table_lookup (const mpls_fib_t *mf, |
| mpls_label_t label, |
| mpls_eos_bit_t eos) |
| { |
| uword *p; |
| |
| p = hash_get(mf->mf_entries, mpls_fib_entry_mk_key(label, eos)); |
| |
| if (NULL == p) |
| return FIB_NODE_INDEX_INVALID; |
| |
| return p[0]; |
| } |
| |
| void |
| mpls_fib_table_entry_insert (mpls_fib_t *mf, |
| mpls_label_t label, |
| mpls_eos_bit_t eos, |
| fib_node_index_t lfei) |
| { |
| hash_set(mf->mf_entries, mpls_fib_entry_mk_key(label, eos), lfei); |
| } |
| |
| void |
| mpls_fib_table_entry_remove (mpls_fib_t *mf, |
| mpls_label_t label, |
| mpls_eos_bit_t eos) |
| { |
| hash_unset(mf->mf_entries, mpls_fib_entry_mk_key(label, eos)); |
| } |
| |
| void |
| mpls_fib_forwarding_table_update (mpls_fib_t *mf, |
| mpls_label_t label, |
| mpls_eos_bit_t eos, |
| const dpo_id_t *dpo) |
| { |
| mpls_label_t key; |
| |
| ASSERT((DPO_LOAD_BALANCE == dpo->dpoi_type) || |
| (DPO_REPLICATE == dpo->dpoi_type)); |
| if (CLIB_DEBUG > 0) |
| { |
| if (DPO_REPLICATE == dpo->dpoi_type) |
| ASSERT(dpo->dpoi_index & MPLS_IS_REPLICATE); |
| if (DPO_LOAD_BALANCE == dpo->dpoi_type) |
| ASSERT(!(dpo->dpoi_index & MPLS_IS_REPLICATE)); |
| } |
| key = mpls_fib_entry_mk_key(label, eos); |
| |
| mf->mf_lbs[key] = dpo->dpoi_index; |
| } |
| |
| void |
| mpls_fib_forwarding_table_reset (mpls_fib_t *mf, |
| mpls_label_t label, |
| mpls_eos_bit_t eos) |
| { |
| mpls_label_t key; |
| |
| key = mpls_fib_entry_mk_key(label, eos); |
| |
| mf->mf_lbs[key] = mpls_fib_drop_dpo_index; |
| } |
| |
| void |
| mpls_fib_table_walk (mpls_fib_t *mpls_fib, |
| fib_table_walk_fn_t fn, |
| void *ctx) |
| { |
| fib_node_index_t lfei; |
| mpls_label_t key; |
| |
| hash_foreach(key, lfei, mpls_fib->mf_entries, |
| ({ |
| fn(lfei, ctx); |
| })); |
| } |
| |
| static void |
| mpls_fib_table_show_all (const mpls_fib_t *mpls_fib, |
| vlib_main_t * vm) |
| { |
| fib_node_index_t lfei, *lfeip, *lfeis = NULL; |
| mpls_label_t key; |
| |
| hash_foreach(key, lfei, mpls_fib->mf_entries, |
| ({ |
| vec_add1(lfeis, lfei); |
| })); |
| |
| vec_sort_with_function(lfeis, fib_entry_cmp_for_sort); |
| |
| vec_foreach(lfeip, lfeis) |
| { |
| vlib_cli_output (vm, "%U", |
| format_fib_entry, *lfeip, |
| FIB_ENTRY_FORMAT_DETAIL); |
| } |
| vec_free(lfeis); |
| } |
| |
| static void |
| mpls_fib_table_show_one (const mpls_fib_t *mpls_fib, |
| mpls_label_t label, |
| vlib_main_t * vm) |
| { |
| fib_node_index_t lfei; |
| mpls_eos_bit_t eos; |
| |
| FOR_EACH_MPLS_EOS_BIT(eos) |
| { |
| lfei = mpls_fib_table_lookup(mpls_fib, label, eos); |
| |
| if (FIB_NODE_INDEX_INVALID != lfei) |
| { |
| vlib_cli_output (vm, "%U", |
| format_fib_entry, lfei, FIB_ENTRY_FORMAT_DETAIL); |
| } |
| } |
| } |
| |
| static clib_error_t * |
| mpls_fib_show (vlib_main_t * vm, |
| unformat_input_t * input, |
| vlib_cli_command_t * cmd) |
| { |
| fib_table_t * fib_table; |
| mpls_label_t label; |
| int table_id; |
| |
| table_id = -1; |
| label = MPLS_LABEL_INVALID; |
| |
| while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) |
| { |
| /* if (unformat (input, "brief") || unformat (input, "summary") */ |
| /* || unformat (input, "sum")) */ |
| /* verbose = 0; */ |
| |
| if (unformat (input, "%d", &label)) |
| continue; |
| else if (unformat (input, "table %d", &table_id)) |
| ; |
| else |
| break; |
| } |
| |
| pool_foreach (fib_table, mpls_main.fibs, |
| ({ |
| if (table_id >= 0 && table_id != fib_table->ft_table_id) |
| continue; |
| |
| vlib_cli_output (vm, "%v, fib_index %d", |
| fib_table->ft_desc, mpls_main.fibs - fib_table); |
| |
| if (MPLS_LABEL_INVALID == label) |
| { |
| mpls_fib_table_show_all(mpls_fib_get(fib_table->ft_index), vm); |
| } |
| else |
| { |
| mpls_fib_table_show_one(mpls_fib_get(fib_table->ft_index), label, vm); |
| } |
| })); |
| |
| return 0; |
| } |
| |
| VLIB_CLI_COMMAND (mpls_fib_show_command, static) = { |
| .path = "show mpls fib", |
| .short_help = "show mpls fib [summary] [table <n>]", |
| .function = mpls_fib_show, |
| }; |