diff options
author | Neale Ranns <nranns@cisco.com> | 2016-11-22 17:07:28 +0000 |
---|---|---|
committer | Damjan Marion <dmarion.lists@gmail.com> | 2017-01-27 19:53:46 +0000 |
commit | 32e1c010b0c34fd0984f7fc45fae648a182025c5 (patch) | |
tree | 06a440bdc9dc039ad0dcf866acc9e10a6ea5e2e7 /src/vnet/mfib | |
parent | 6f692d6e5a8ffc920a728372ef773199bc5466c0 (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')
-rw-r--r-- | src/vnet/mfib/ip4_mfib.c | 465 | ||||
-rw-r--r-- | src/vnet/mfib/ip4_mfib.h | 95 | ||||
-rw-r--r-- | src/vnet/mfib/ip6_mfib.c | 663 | ||||
-rw-r--r-- | src/vnet/mfib/ip6_mfib.h | 109 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_entry.c | 1096 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_entry.h | 172 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_forward.c | 512 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_itf.c | 119 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_itf.h | 63 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_signal.c | 201 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_signal.h | 59 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_table.c | 489 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_table.h | 331 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_test.c | 1225 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_types.c | 213 | ||||
-rw-r--r-- | src/vnet/mfib/mfib_types.h | 185 |
16 files changed, 5997 insertions, 0 deletions
diff --git a/src/vnet/mfib/ip4_mfib.c b/src/vnet/mfib/ip4_mfib.c new file mode 100644 index 00000000000..08001c3fa7a --- /dev/null +++ b/src/vnet/mfib/ip4_mfib.c @@ -0,0 +1,465 @@ +/* + * 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/mfib/ip4_mfib.h> + +#include <vnet/mfib/mfib_table.h> +#include <vnet/mfib/mfib_entry.h> + +static const mfib_prefix_t ip4_specials[] = { + { + /* (*,*)/0 */ + .fp_src_addr = { + .ip4.data_u32 = 0, + }, + .fp_grp_addr = { + .ip4.data_u32 = 0, + }, + .fp_len = 0, + .fp_proto = FIB_PROTOCOL_IP4, + }, +}; + +static u32 +ip4_create_mfib_with_table_id (u32 table_id) +{ + mfib_table_t *mfib_table; + + pool_get_aligned(ip4_main.mfibs, mfib_table, CLIB_CACHE_LINE_BYTES); + memset(mfib_table, 0, sizeof(*mfib_table)); + + mfib_table->mft_proto = FIB_PROTOCOL_IP4; + mfib_table->mft_index = + mfib_table->v4.index = + (mfib_table - ip4_main.mfibs); + + hash_set (ip4_main.mfib_index_by_table_id, + table_id, + mfib_table->mft_index); + + mfib_table->mft_table_id = + mfib_table->v4.table_id = + table_id; + + mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP4); + + /* + * add the special entries into the new FIB + */ + int ii; + + for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++) + { + mfib_prefix_t prefix = ip4_specials[ii]; + + prefix.fp_src_addr.ip4.data_u32 = + clib_host_to_net_u32(prefix.fp_src_addr.ip4.data_u32); + prefix.fp_grp_addr.ip4.data_u32 = + clib_host_to_net_u32(prefix.fp_grp_addr.ip4.data_u32); + + mfib_table_entry_update(mfib_table->mft_index, + &prefix, + MFIB_SOURCE_DEFAULT_ROUTE, + MFIB_ENTRY_FLAG_DROP); + } + + return (mfib_table->mft_index); +} + +void +ip4_mfib_table_destroy (ip4_mfib_t *mfib) +{ + mfib_table_t *mfib_table = (mfib_table_t*)mfib; + int ii; + + /* + * remove all the specials we added when the table was created. + */ + for (ii = 0; ii < ARRAY_LEN(ip4_specials); ii++) + { + fib_node_index_t mfei; + mfib_prefix_t prefix = ip4_specials[ii]; + + prefix.fp_src_addr.ip4.data_u32 = + clib_host_to_net_u32(prefix.fp_src_addr.ip4.data_u32); + prefix.fp_grp_addr.ip4.data_u32 = + clib_host_to_net_u32(prefix.fp_grp_addr.ip4.data_u32); + + mfei = mfib_table_lookup(mfib_table->mft_index, &prefix); + mfib_table_entry_delete_index(mfei, MFIB_SOURCE_DEFAULT_ROUTE); + } + + /* + * validate no more routes. + */ + ASSERT(0 == mfib_table->mft_total_route_counts); + ASSERT(~0 != mfib_table->mft_table_id); + + hash_unset (ip4_main.mfib_index_by_table_id, mfib_table->mft_table_id); + pool_put(ip4_main.mfibs, mfib_table); +} + +u32 +ip4_mfib_table_find_or_create_and_lock (u32 table_id) +{ + u32 index; + + index = ip4_mfib_index_from_table_id(table_id); + if (~0 == index) + return ip4_create_mfib_with_table_id(table_id); + mfib_table_lock(index, FIB_PROTOCOL_IP4); + + return (index); +} + +u32 +ip4_mfib_table_get_index_for_sw_if_index (u32 sw_if_index) +{ + if (sw_if_index >= vec_len(ip4_main.mfib_index_by_sw_if_index)) + { + /* + * This is the case for interfaces that are not yet mapped to + * a IP table + */ + return (~0); + } + return (ip4_main.mfib_index_by_sw_if_index[sw_if_index]); +} + +#define IPV4_MFIB_GRP_LEN(_len)\ + (_len > 32 ? 32 : _len) + +#define IP4_MFIB_MK_KEY(_grp, _src, _len, _key) \ +{ \ + _key = ((u64)(_grp->data_u32 & \ + ip4_main.fib_masks[IPV4_MFIB_GRP_LEN(_len)])) << 32; \ + _key |= _src->data_u32; \ +} +#define IP4_MFIB_MK_GRP_KEY(_grp, _len, _key) \ +{ \ + _key = ((u64)(_grp->data_u32 & \ + ip4_main.fib_masks[IPV4_MFIB_GRP_LEN(_len)])) << 32; \ +} + +/* + * ip4_fib_table_lookup_exact_match + * + * Exact match prefix lookup + */ +fib_node_index_t +ip4_mfib_table_lookup_exact_match (const ip4_mfib_t *mfib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len) +{ + uword * hash, * result; + u64 key; + + hash = mfib->fib_entry_by_dst_address[len]; + IP4_MFIB_MK_KEY(grp, src, len, key); + + result = hash_get(hash, key); + + if (NULL != result) { + return (result[0]); + } + return (FIB_NODE_INDEX_INVALID); +} + +/* + * ip4_fib_table_lookup + * + * Longest prefix match + */ +fib_node_index_t +ip4_mfib_table_lookup (const ip4_mfib_t *mfib, + const ip4_address_t *src, + const ip4_address_t *grp, + u32 len) +{ + uword * hash, * result; + i32 mask_len; + u64 key; + + mask_len = len; + + if (PREDICT_TRUE(64 == mask_len)) + { + hash = mfib->fib_entry_by_dst_address[mask_len]; + IP4_MFIB_MK_KEY(grp, src, mask_len, key); + + result = hash_get (hash, key); + + if (NULL != result) { + return (result[0]); + } + } + + for (mask_len = 32; mask_len >= 0; mask_len--) + { + hash = mfib->fib_entry_by_dst_address[mask_len]; + IP4_MFIB_MK_GRP_KEY(grp, mask_len, key); + + result = hash_get (hash, key); + + if (NULL != result) { + return (result[0]); + } + } + return (FIB_NODE_INDEX_INVALID); +} + +void +ip4_mfib_table_entry_insert (ip4_mfib_t *mfib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len, + fib_node_index_t fib_entry_index) +{ + uword * hash, * result; + u64 key; + + IP4_MFIB_MK_KEY(grp, src, len, key); + hash = mfib->fib_entry_by_dst_address[len]; + result = hash_get (hash, key); + + if (NULL == result) { + /* + * adding a new entry + */ + if (NULL == hash) { + hash = hash_create (32 /* elts */, sizeof (uword)); + hash_set_flags (hash, HASH_FLAG_NO_AUTO_SHRINK); + } + hash = hash_set(hash, key, fib_entry_index); + mfib->fib_entry_by_dst_address[len] = hash; + } + else + { + ASSERT(0); + } +} + +void +ip4_mfib_table_entry_remove (ip4_mfib_t *mfib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len) +{ + uword * hash, * result; + u64 key; + + IP4_MFIB_MK_KEY(grp, src, len, key); + hash = mfib->fib_entry_by_dst_address[len]; + result = hash_get (hash, key); + + if (NULL == result) + { + /* + * removing a non-existant entry. i'll allow it. + */ + } + else + { + hash_unset(hash, key); + } + + mfib->fib_entry_by_dst_address[len] = hash; +} + +static void +ip4_mfib_table_show_all (ip4_mfib_t *mfib, + vlib_main_t * vm) +{ + fib_node_index_t *mfib_entry_indicies; + fib_node_index_t *mfib_entry_index; + int i; + + mfib_entry_indicies = NULL; + + for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++) + { + uword * hash = mfib->fib_entry_by_dst_address[i]; + + if (NULL != hash) + { + hash_pair_t * p; + + hash_foreach_pair (p, hash, + ({ + vec_add1(mfib_entry_indicies, p->value[0]); + })); + } + } + + vec_sort_with_function(mfib_entry_indicies, mfib_entry_cmp_for_sort); + + vec_foreach(mfib_entry_index, mfib_entry_indicies) + { + vlib_cli_output(vm, "%U", + format_mfib_entry, + *mfib_entry_index, + MFIB_ENTRY_FORMAT_BRIEF); + } + + vec_free(mfib_entry_indicies); +} + +static void +ip4_mfib_table_show_one (ip4_mfib_t *mfib, + vlib_main_t * vm, + ip4_address_t *src, + ip4_address_t *grp, + u32 mask_len) +{ + vlib_cli_output(vm, "%U", + format_mfib_entry, + ip4_mfib_table_lookup(mfib, src, grp, mask_len), + MFIB_ENTRY_FORMAT_DETAIL); +} + +static clib_error_t * +ip4_show_mfib (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ip4_main_t * im4 = &ip4_main; + mfib_table_t *mfib_table; + int verbose, matching; + ip4_address_t grp, src = {{0}}; + u32 mask = 32; + int i, table_id = -1, fib_index = ~0; + + verbose = 1; + matching = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "brief") || unformat (input, "summary") + || unformat (input, "sum")) + verbose = 0; + + else if (unformat (input, "%U %U", + unformat_ip4_address, &src, + unformat_ip4_address, &grp)) + { + matching = 1; + mask = 64; + } + else if (unformat (input, "%U", unformat_ip4_address, &grp)) + { + matching = 1; + mask = 32; + } + else if (unformat (input, "%U/%d", + unformat_ip4_address, &grp, &mask)) + matching = 1; + else if (unformat (input, "table %d", &table_id)) + ; + else if (unformat (input, "index %d", &fib_index)) + ; + else + break; + } + + pool_foreach (mfib_table, im4->mfibs, + ({ + ip4_mfib_t *mfib = &mfib_table->v4; + + if (table_id >= 0 && table_id != (int)mfib->table_id) + continue; + if (fib_index != ~0 && fib_index != (int)mfib->index) + continue; + + vlib_cli_output (vm, "%U, fib_index %d", + format_mfib_table_name, mfib->index, FIB_PROTOCOL_IP4, + mfib->index); + + /* Show summary? */ + if (! verbose) + { + vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count"); + for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++) + { + uword * hash = mfib->fib_entry_by_dst_address[i]; + uword n_elts = hash_elts (hash); + if (n_elts > 0) + vlib_cli_output (vm, "%20d%16d", i, n_elts); + } + continue; + } + + if (!matching) + { + ip4_mfib_table_show_all(mfib, vm); + } + else + { + ip4_mfib_table_show_one(mfib, vm, &src, &grp, mask); + } + })); + + return 0; +} + +/*? + * This command displays the IPv4 MulticasrFIB Tables (VRF Tables) and + * the route entries for each table. + * + * @note This command will run for a long time when the FIB tables are + * comprised of millions of entries. For those senarios, consider displaying + * a single table or summary mode. + * + * @cliexpar + * Example of how to display all the IPv4 Multicast FIB tables: + * @cliexstart{show ip fib} + * ipv4-VRF:0, fib_index 0 + * (*, 0.0.0.0/0): flags:D, + * Interfaces: + * multicast-ip4-chain + * [@1]: dpo-drop ip4 + * (*, 232.1.1.1/32): + * Interfaces: + * test-eth1: Forward, + * test-eth2: Forward, + * test-eth0: Accept, + * multicast-ip4-chain + * [@2]: dpo-replicate: [index:1 buckets:2 to:[0:0]] + * [0] [@1]: ipv4-mcast: test-eth1: IP4: d0:d1:d2:d3:d4:01 -> 01:00:05:00:00:00 + * [1] [@1]: ipv4-mcast: test-eth2: IP4: d0:d1:d2:d3:d4:02 -> 01:00:05:00:00:00 + * + * @cliexend + * Example of how to display a summary of all IPv4 FIB tables: + * @cliexstart{show ip fib summary} + * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto + * Prefix length Count + * 0 1 + * 8 2 + * 32 4 + * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto + * Prefix length Count + * 0 1 + * 8 2 + * 24 2 + * 32 4 + * @cliexend + ?*/ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip4_show_fib_command, static) = { + .path = "show ip mfib", + .short_help = "show ip mfib [summary] [table <table-id>] [index <fib-id>] [<gre-addr>[/<mask>]] [<gre-addr>] [<src-addr> <gre-addr>]", + .function = ip4_show_mfib, +}; +/* *INDENT-ON* */ diff --git a/src/vnet/mfib/ip4_mfib.h b/src/vnet/mfib/ip4_mfib.h new file mode 100644 index 00000000000..6fc74a368bd --- /dev/null +++ b/src/vnet/mfib/ip4_mfib.h @@ -0,0 +1,95 @@ +/* + * 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. + */ +/** + * @brief The IPv4 Multicast-FIB + * + * FIXME + * + * This IPv4 FIB is used by the protocol independent FIB. So directly using + * this APIs in client code is not encouraged. However, this IPv4 FIB can be + * used if all the client wants is an IPv4 prefix data-base + */ + +#ifndef __IP4_MFIB_H__ +#define __IP4_MFIB_H__ + +#include <vlib/vlib.h> +#include <vnet/ip/ip.h> + +#include <vnet/mfib/mfib_table.h> + +extern fib_node_index_t ip4_mfib_table_lookup(const ip4_mfib_t *fib, + const ip4_address_t *src, + const ip4_address_t *grp, + u32 len); +extern fib_node_index_t ip4_mfib_table_lookup_exact_match(const ip4_mfib_t *fib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len); + +extern void ip4_mfib_table_entry_remove(ip4_mfib_t *fib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len); + +extern void ip4_mfib_table_entry_insert(ip4_mfib_t *fib, + const ip4_address_t *grp, + const ip4_address_t *src, + u32 len, + fib_node_index_t fib_entry_index); +extern void ip4_mfib_table_destroy(ip4_mfib_t *fib); + +/** + * @brief Get the FIB at the given index + */ +static inline ip4_mfib_t * +ip4_mfib_get (u32 index) +{ + return (&(pool_elt_at_index(ip4_main.mfibs, index)->v4)); +} + +/** + * @brief Get or create an IPv4 fib. + * + * Get or create an IPv4 fib with the provided table ID. + * + * @param table_id + * When set to \c ~0, an arbitrary and unused fib ID is picked + * and can be retrieved with \c ret->table_id. + * Otherwise, the fib ID to be used to retrieve or create the desired fib. + * @returns A pointer to the retrieved or created fib. + * + */ +extern u32 ip4_mfib_table_find_or_create_and_lock(u32 table_id); +extern u32 ip4_mfib_table_create_and_lock(void); + +static inline +u32 ip4_mfib_index_from_table_id (u32 table_id) +{ + ip4_main_t * im = &ip4_main; + uword * p; + + p = hash_get (im->mfib_index_by_table_id, table_id); + if (!p) + return ~0; + + return p[0]; +} + +extern u32 ip4_mfib_table_get_index_for_sw_if_index(u32 sw_if_index); + + +#endif + diff --git a/src/vnet/mfib/ip6_mfib.c b/src/vnet/mfib/ip6_mfib.c new file mode 100644 index 00000000000..0c2e4c7b796 --- /dev/null +++ b/src/vnet/mfib/ip6_mfib.c @@ -0,0 +1,663 @@ +/* + * 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/mfib/ip6_mfib.h> + +#include <vnet/mfib/mfib_table.h> +#include <vnet/mfib/mfib_entry.h> +#include <vnet/fib/ip6_fib.h> + +/** + * The number of bytes in an address/ask key in the radix tree + * First byte is the length in bytes. + */ +#define IP6_MFIB_KEY_LEN 33 + +/** + * Key and mask for radix + */ +typedef struct ip6_mfib_key_t_ +{ + u8 key[IP6_MFIB_KEY_LEN]; + u8 mask[IP6_MFIB_KEY_LEN]; +} ip6_mfib_key_t; + +/** + * An object that is inserted into the radix tree. + * Since it's in the tree and has pointers, it cannot realloc and so cannot + * come from a vlib pool. + */ +typedef struct ip6_mfib_node_t_ +{ + struct radix_node i6mn_nodes[2]; + ip6_mfib_key_t i6mn_key; + index_t i6mn_entry; +} ip6_mfib_node_t; + +static const mfib_prefix_t all_zeros = { + /* (*,*) */ + .fp_src_addr = { + .ip6.as_u64 = {0, 0}, + }, + .fp_grp_addr = { + .ip6.as_u64 = {0, 0}, + }, + .fp_len = 0, + .fp_proto = FIB_PROTOCOL_IP6, +}; + +typedef enum ip6_mfib_special_type_t_ { + IP6_MFIB_SPECIAL_TYPE_NONE, + IP6_MFIB_SPECIAL_TYPE_SOLICITED, +} ip6_mfib_special_type_t; + +typedef struct ip6_mfib_special_t_ { + /** + * @brief solicited or not + */ + ip6_mfib_special_type_t ims_type; + + /** + * @brief the Prefix length + */ + u8 ims_len; + + /** + * @brief The last byte of the mcast address + */ + u8 ims_byte; + /** + * @brief The scope of the address + */ + u8 ims_scope; +} ip6_mfib_special_t; + +static const ip6_mfib_special_t ip6_mfib_specials[] = +{ + { + /* + * Add ff02::1:ff00:0/104 via local route for all tables. + * This is required for neighbor discovery to work. + */ + .ims_type = IP6_MFIB_SPECIAL_TYPE_SOLICITED, + .ims_len = 104, + }, + { + /* + * all-routers multicast address + */ + .ims_type = IP6_MFIB_SPECIAL_TYPE_NONE, + .ims_scope = IP6_MULTICAST_SCOPE_link_local, + .ims_byte = IP6_MULTICAST_GROUP_ID_all_routers, + .ims_len = 128, + }, + { + /* + * all-nodes multicast address + */ + .ims_type = IP6_MFIB_SPECIAL_TYPE_NONE, + .ims_scope = IP6_MULTICAST_SCOPE_link_local, + .ims_byte = IP6_MULTICAST_GROUP_ID_all_hosts, + .ims_len = 128, + }, + { + /* + * Add all-mldv2 multicast address via local route for all tables + */ + .ims_type = IP6_MFIB_SPECIAL_TYPE_NONE, + .ims_len = 128, + .ims_scope = IP6_MULTICAST_SCOPE_link_local, + .ims_byte = IP6_MULTICAST_GROUP_ID_mldv2_routers, + } +}; + +#define FOR_EACH_IP6_SPECIAL(_pfx, _body) \ +{ \ + const ip6_mfib_special_t *_spec; \ + u8 _ii; \ + for (_ii = 0; \ + _ii < ARRAY_LEN(ip6_mfib_specials); \ + _ii++) \ + { \ + _spec = &ip6_mfib_specials[_ii]; \ + if (IP6_MFIB_SPECIAL_TYPE_SOLICITED == _spec->ims_type) \ + { \ + ip6_set_solicited_node_multicast_address( \ + &(_pfx)->fp_grp_addr.ip6, 0); \ + } \ + else \ + { \ + ip6_set_reserved_multicast_address ( \ + &(_pfx)->fp_grp_addr.ip6, \ + _spec->ims_scope, \ + _spec->ims_byte); \ + } \ + (_pfx)->fp_len = _spec->ims_len; \ + do { _body; } while (0); \ + } \ +} + + +static u32 +ip6_create_mfib_with_table_id (u32 table_id) +{ + mfib_table_t *mfib_table; + mfib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP6, + }; + const fib_route_path_t path_for_us = { + .frp_proto = FIB_PROTOCOL_IP6, + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = ~0, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + }; + + pool_get_aligned(ip6_main.mfibs, mfib_table, CLIB_CACHE_LINE_BYTES); + memset(mfib_table, 0, sizeof(*mfib_table)); + + mfib_table->mft_proto = FIB_PROTOCOL_IP6; + mfib_table->mft_index = + mfib_table->v6.index = + (mfib_table - ip6_main.mfibs); + + hash_set (ip6_main.mfib_index_by_table_id, + table_id, + mfib_table->mft_index); + + mfib_table->mft_table_id = + mfib_table->v6.table_id = + table_id; + + mfib_table_lock(mfib_table->mft_index, FIB_PROTOCOL_IP6); + + mfib_table->v6.rhead = + clib_mem_alloc_aligned (sizeof(*mfib_table->v6.rhead), + CLIB_CACHE_LINE_BYTES); + rn_inithead0(mfib_table->v6.rhead, 8); + + /* + * add the special entries into the new FIB + */ + mfib_table_entry_update(mfib_table->mft_index, + &all_zeros, + MFIB_SOURCE_DEFAULT_ROUTE, + MFIB_ENTRY_FLAG_DROP); + + /* + * Add each of the specials + */ + FOR_EACH_IP6_SPECIAL(&pfx, + ({ + mfib_table_entry_path_update(mfib_table->mft_index, + &pfx, + MFIB_SOURCE_SPECIAL, + &path_for_us, + MFIB_ITF_FLAG_FORWARD); + })); + + return (mfib_table->mft_index); +} + +void +ip6_mfib_table_destroy (ip6_mfib_t *mfib) +{ + mfib_table_t *mfib_table = (mfib_table_t*)mfib; + fib_node_index_t mfei; + mfib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP6, + }; + const fib_route_path_t path_for_us = { + .frp_proto = FIB_PROTOCOL_IP6, + .frp_addr = zero_addr, + .frp_sw_if_index = 0xffffffff, + .frp_fib_index = ~0, + .frp_weight = 0, + .frp_flags = FIB_ROUTE_PATH_LOCAL, + }; + + /* + * remove all the specials we added when the table was created. + */ + FOR_EACH_IP6_SPECIAL(&pfx, + { + mfib_table_entry_path_remove(mfib_table->mft_index, + &pfx, + MFIB_SOURCE_SPECIAL, + &path_for_us); + }); + + mfei = mfib_table_lookup_exact_match(mfib_table->mft_index, &all_zeros); + mfib_table_entry_delete_index(mfei, MFIB_SOURCE_DEFAULT_ROUTE); + + /* + * validate no more routes. + */ + ASSERT(0 == mfib_table->mft_total_route_counts); + ASSERT(~0 != mfib_table->mft_table_id); + + hash_unset (ip6_main.mfib_index_by_table_id, mfib_table->mft_table_id); + clib_mem_free(mfib_table->v6.rhead); + pool_put(ip6_main.mfibs, mfib_table); +} + +void +ip6_mfib_interface_enable_disable (u32 sw_if_index, int is_enable) +{ + const fib_route_path_t path = { + .frp_proto = FIB_PROTOCOL_IP6, + .frp_addr = zero_addr, + .frp_sw_if_index = sw_if_index, + .frp_fib_index = ~0, + .frp_weight = 0, + }; + mfib_prefix_t pfx = { + .fp_proto = FIB_PROTOCOL_IP6, + }; + u32 mfib_index; + + vec_validate (ip6_main.mfib_index_by_sw_if_index, sw_if_index); + mfib_index = ip6_mfib_table_get_index_for_sw_if_index(sw_if_index); + + if (is_enable) + { + FOR_EACH_IP6_SPECIAL(&pfx, + { + mfib_table_entry_path_update(mfib_index, + &pfx, + MFIB_SOURCE_SPECIAL, + &path, + MFIB_ITF_FLAG_ACCEPT); + }); + } + else + { + FOR_EACH_IP6_SPECIAL(&pfx, + { + mfib_table_entry_path_remove(mfib_index, + &pfx, + MFIB_SOURCE_SPECIAL, + &path); + }); + } +} + +u32 +ip6_mfib_table_find_or_create_and_lock (u32 table_id) +{ + u32 index; + + index = ip6_mfib_index_from_table_id(table_id); + if (~0 == index) + return ip6_create_mfib_with_table_id(table_id); + mfib_table_lock(index, FIB_PROTOCOL_IP6); + + return (index); +} + +u32 +ip6_mfib_table_get_index_for_sw_if_index (u32 sw_if_index) +{ + if (sw_if_index >= vec_len(ip6_main.mfib_index_by_sw_if_index)) + { + /* + * This is the case for interfaces that are not yet mapped to + * a IP table + */ + return (~0); + } + return (ip6_main.mfib_index_by_sw_if_index[sw_if_index]); +} + +#define IP6_MFIB_MK_KEY(_grp, _src, _key) \ +{ \ + (_key)->key[0] = 33; \ + memcpy((_key)->key+1, _grp, 16); \ + memcpy((_key)->key+17, _src, 16); \ +} + +#define IP6_MFIB_MK_KEY_MASK(_grp, _src, _len, _key) \ +{ \ + IP6_MFIB_MK_KEY(_grp, _src, _key); \ + \ + (_key)->mask[0] = 33; \ + if (_len <= 128) \ + { \ + memcpy((_key)->mask+1, &ip6_main.fib_masks[_len], 16); \ + memset((_key)->mask+17, 0, 16); \ + } \ + else \ + { \ + ASSERT(_len == 256); \ + memcpy((_key)->mask+1, &ip6_main.fib_masks[128], 16); \ + memcpy((_key)->mask+17, &ip6_main.fib_masks[128], 16); \ + } \ +} + +/* + * ip6_fib_table_lookup_exact_match + * + * Exact match prefix lookup + */ +fib_node_index_t +ip6_mfib_table_lookup_exact_match (const ip6_mfib_t *mfib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len) +{ + ip6_mfib_node_t *i6mn; + ip6_mfib_key_t key; + + IP6_MFIB_MK_KEY_MASK(grp, src, len, &key); + + i6mn = (ip6_mfib_node_t*) rn_lookup(key.key, key.mask, + (struct radix_node_head *)mfib->rhead); + + if (NULL == i6mn) + { + return (INDEX_INVALID); + } + + return (i6mn->i6mn_entry); +} + +/* + * ip6_fib_table_lookup + * + * Longest prefix match + */ +fib_node_index_t +ip6_mfib_table_lookup (const ip6_mfib_t *mfib, + const ip6_address_t *src, + const ip6_address_t *grp, + u32 len) +{ + ip6_mfib_node_t *i6mn; + ip6_mfib_key_t key; + + IP6_MFIB_MK_KEY_MASK(grp, src, len, &key); + + i6mn = (ip6_mfib_node_t*) rn_search_m(key.key, + mfib->rhead->rnh_treetop, + key.mask); + + ASSERT(NULL != i6mn); + + return (i6mn->i6mn_entry); +} + +/* + * ip6_fib_table_lookup + * + * Longest prefix match no mask + */ +fib_node_index_t +ip6_mfib_table_lookup2 (const ip6_mfib_t *mfib, + const ip6_address_t *src, + const ip6_address_t *grp) +{ + ip6_mfib_node_t *i6mn; + ip6_mfib_key_t key; + + IP6_MFIB_MK_KEY(grp, src, &key); + + i6mn = (ip6_mfib_node_t*) rn_match(key.key, + (struct radix_node_head *)mfib->rhead); // const cast + + ASSERT(NULL != i6mn); + + return (i6mn->i6mn_entry); +} + +void +ip6_mfib_table_entry_insert (ip6_mfib_t *mfib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len, + fib_node_index_t mfib_entry_index) +{ + ip6_mfib_node_t *i6mn = clib_mem_alloc(sizeof(*i6mn)); + + memset(i6mn, 0, sizeof(*i6mn)); + + IP6_MFIB_MK_KEY_MASK(grp, src, len, &i6mn->i6mn_key); + i6mn->i6mn_entry = mfib_entry_index; + + if (NULL == rn_addroute(i6mn->i6mn_key.key, + i6mn->i6mn_key.mask, + mfib->rhead, + i6mn->i6mn_nodes)) + { + ASSERT(0); + } +} + +void +ip6_mfib_table_entry_remove (ip6_mfib_t *mfib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len) +{ + ip6_mfib_node_t *i6mn; + ip6_mfib_key_t key; + + IP6_MFIB_MK_KEY_MASK(grp, src, len, &key); + + i6mn = (ip6_mfib_node_t*) rn_delete(key.key, key.mask, mfib->rhead); + + clib_mem_free(i6mn); +} + +static clib_error_t * +ip6_mfib_module_init (vlib_main_t * vm) +{ + return (NULL); +} + +VLIB_INIT_FUNCTION(ip6_mfib_module_init); + +static void +ip6_mfib_table_show_one (ip6_mfib_t *mfib, + vlib_main_t * vm, + ip6_address_t *src, + ip6_address_t *grp, + u32 mask_len) +{ + vlib_cli_output(vm, "%U", + format_mfib_entry, + ip6_mfib_table_lookup(mfib, src, grp, mask_len), + MFIB_ENTRY_FORMAT_DETAIL); +} + +typedef struct ip6_mfib_show_ctx_t_ { + u32 fib_index; + fib_node_index_t *entries; +} ip6_mfib_show_ctx_t; + + +static int +ip6_mfib_table_collect_entries (struct radix_node *rn, void *arg) +{ + ip6_mfib_show_ctx_t *ctx = arg; + ip6_mfib_node_t *i6mn; + + i6mn = (ip6_mfib_node_t*) rn; + + vec_add1(ctx->entries, i6mn->i6mn_entry); + + return (0); +} + +static void +ip6_mfib_table_show_all (ip6_mfib_t *mfib, + vlib_main_t * vm) +{ + fib_node_index_t *mfib_entry_index; + ip6_mfib_show_ctx_t ctx = { + .fib_index = mfib->index, + .entries = NULL, + }; + + rn_walktree(mfib->rhead, + ip6_mfib_table_collect_entries, + &ctx); + + vec_sort_with_function(ctx.entries, mfib_entry_cmp_for_sort); + + vec_foreach(mfib_entry_index, ctx.entries) + { + vlib_cli_output(vm, "%U", + format_mfib_entry, + *mfib_entry_index, + MFIB_ENTRY_FORMAT_BRIEF); + } + + vec_free(ctx.entries); +} + +static clib_error_t * +ip6_show_mfib (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ip6_main_t * im4 = &ip6_main; + mfib_table_t *mfib_table; + int verbose, matching; + ip6_address_t grp, src = {{0}}; + u32 mask = 32; + int table_id = -1, fib_index = ~0; + + verbose = 1; + matching = 0; + + while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (input, "brief") || unformat (input, "summary") + || unformat (input, "sum")) + verbose = 0; + + else if (unformat (input, "%U %U", + unformat_ip6_address, &src, + unformat_ip6_address, &grp)) + { + matching = 1; + mask = 64; + } + else if (unformat (input, "%U", unformat_ip6_address, &grp)) + { + matching = 1; + mask = 32; + } + else if (unformat (input, "%U/%d", + unformat_ip6_address, &grp, &mask)) + matching = 1; + else if (unformat (input, "table %d", &table_id)) + ; + else if (unformat (input, "index %d", &fib_index)) + ; + else + break; + } + + pool_foreach (mfib_table, im4->mfibs, + ({ + ip6_mfib_t *mfib = &mfib_table->v6; + + if (table_id >= 0 && table_id != (int)mfib->table_id) + continue; + if (fib_index != ~0 && fib_index != (int)mfib->index) + continue; + + vlib_cli_output (vm, "%U, fib_index %d", + format_mfib_table_name, mfib->index, FIB_PROTOCOL_IP6, + mfib->index); + + /* Show summary? */ + if (! verbose) + { + /* vlib_cli_output (vm, "%=20s%=16s", "Prefix length", "Count"); */ + /* for (i = 0; i < ARRAY_LEN (mfib->fib_entry_by_dst_address); i++) */ + /* { */ + /* uword * hash = mfib->fib_entry_by_dst_address[i]; */ + /* uword n_elts = hash_elts (hash); */ + /* if (n_elts > 0) */ + /* vlib_cli_output (vm, "%20d%16d", i, n_elts); */ + /* } */ + continue; + } + + if (!matching) + { + ip6_mfib_table_show_all(mfib, vm); + } + else + { + ip6_mfib_table_show_one(mfib, vm, &src, &grp, mask); + } + })); + + return 0; +} + +/* + * This command displays the IPv4 MulticasrFIB Tables (VRF Tables) and + * the route entries for each table. + * + * @note This command will run for a long time when the FIB tables are + * comprised of millions of entries. For those senarios, consider displaying + * a single table or summary mode. + * + * @cliexpar + * Example of how to display all the IPv4 Multicast FIB tables: + * @cliexstart{show ip fib} + * ipv4-VRF:0, fib_index 0 + * (*, 0.0.0.0/0): flags:D, + * Interfaces: + * multicast-ip6-chain + * [@1]: dpo-drop ip6 + * (*, 232.1.1.1/32): + * Interfaces: + * test-eth1: Forward, + * test-eth2: Forward, + * test-eth0: Accept, + * multicast-ip6-chain + * [@2]: dpo-replicate: [index:1 buckets:2 to:[0:0]] + * [0] [@1]: ipv4-mcast: test-eth1: IP6: d0:d1:d2:d3:d4:01 -> 01:00:05:00:00:00 + * [1] [@1]: ipv4-mcast: test-eth2: IP6: d0:d1:d2:d3:d4:02 -> 01:00:05:00:00:00 + * + * @cliexend + * Example of how to display a summary of all IPv4 FIB tables: + * @cliexstart{show ip fib summary} + * ipv4-VRF:0, fib_index 0, flow hash: src dst sport dport proto + * Prefix length Count + * 0 1 + * 8 2 + * 32 4 + * ipv4-VRF:7, fib_index 1, flow hash: src dst sport dport proto + * Prefix length Count + * 0 1 + * 8 2 + * 24 2 + * 32 4 + * @cliexend + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (ip6_show_fib_command, static) = { + .path = "show ip6 mfib", + .short_help = "show ip mfib [summary] [table <table-id>] [index <fib-id>] [<grp-addr>[/<mask>]] [<grp-addr>] [<src-addr> <grp-addr>]", + .function = ip6_show_mfib, +}; +/* *INDENT-ON* */ diff --git a/src/vnet/mfib/ip6_mfib.h b/src/vnet/mfib/ip6_mfib.h new file mode 100644 index 00000000000..d91af46dc93 --- /dev/null +++ b/src/vnet/mfib/ip6_mfib.h @@ -0,0 +1,109 @@ +/* + * 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. + */ +/** + * @brief The IPv4 Multicast-FIB + * + * FIXME + * + * This IPv4 FIB is used by the protocol independent FIB. So directly using + * this APIs in client code is not encouraged. However, this IPv4 FIB can be + * used if all the client wants is an IPv4 prefix data-base + */ + +#ifndef __IP6_MFIB_H__ +#define __IP6_MFIB_H__ + +#include <vlib/vlib.h> +#include <vnet/ip/ip.h> + +#include <vnet/mfib/mfib_table.h> + +extern fib_node_index_t ip6_mfib_table_lookup(const ip6_mfib_t *fib, + const ip6_address_t *src, + const ip6_address_t *grp, + u32 len); +extern fib_node_index_t ip6_mfib_table_lookup_exact_match(const ip6_mfib_t *fib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len); + +extern void ip6_mfib_table_entry_remove(ip6_mfib_t *fib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len); + +extern void ip6_mfib_table_entry_insert(ip6_mfib_t *fib, + const ip6_address_t *grp, + const ip6_address_t *src, + u32 len, + fib_node_index_t fib_entry_index); +extern void ip6_mfib_table_destroy(ip6_mfib_t *fib); + +/** + * @brief + * Add/remove the interface from the accepting list of the special MFIB entries + */ +extern void ip6_mfib_interface_enable_disable(u32 sw_if_index, + int is_enable); + +/** + * @brief Get the FIB at the given index + */ +static inline ip6_mfib_t * +ip6_mfib_get (u32 index) +{ + return (&(pool_elt_at_index(ip6_main.mfibs, index)->v6)); +} + +/** + * @brief Get or create an IPv4 fib. + * + * Get or create an IPv4 fib with the provided table ID. + * + * @param table_id + * When set to \c ~0, an arbitrary and unused fib ID is picked + * and can be retrieved with \c ret->table_id. + * Otherwise, the fib ID to be used to retrieve or create the desired fib. + * @returns A pointer to the retrieved or created fib. + * + */ +extern u32 ip6_mfib_table_find_or_create_and_lock(u32 table_id); +extern u32 ip6_mfib_table_create_and_lock(void); + + +static inline +u32 ip6_mfib_index_from_table_id (u32 table_id) +{ + ip6_main_t * im = &ip6_main; + uword * p; + + p = hash_get (im->mfib_index_by_table_id, table_id); + if (!p) + return ~0; + + return p[0]; +} + +extern u32 ip6_mfib_table_get_index_for_sw_if_index(u32 sw_if_index); + +/** + * @brief Data-plane lookup function + */ +extern fib_node_index_t ip6_mfib_table_lookup2(const ip6_mfib_t *mfib, + const ip6_address_t *src, + const ip6_address_t *grp); + +#endif + diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c new file mode 100644 index 00000000000..479ce5f1442 --- /dev/null +++ b/src/vnet/mfib/mfib_entry.c @@ -0,0 +1,1096 @@ +/* + * 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 <vlib/vlib.h> + +#include <vnet/mfib/mfib_entry.h> +#include <vnet/fib/fib_path_list.h> + +#include <vnet/dpo/drop_dpo.h> +#include <vnet/dpo/replicate_dpo.h> + +/** + * Debug macro + */ +#ifdef MFIB_DEBUG +#DEFIne MFIB_ENTRY_DBG(_e, _fmt, _args...) \ +{ \ + u8*__tmp = NULL; \ + __tmp = format(__tmp, "e:[%d:%U", \ + mfib_entry_get_index(_e), \ + format_ip46_address, \ + &_e->mfe_prefix.fp_grp_addr, \ + IP46_TYPE_ANY); \ + __tmp = format(__tmp, "/%d,", \ + _e->mfe_prefix.fp_len); \ + __tmp = format(__tmp, "%U]", \ + mfib_entry_get_index(_e), \ + format_ip46_address, \ + &_e->mfe_prefix.fp_src_addr, \ + IP46_TYPE_ANY); \ + __tmp = format(__tmp, _fmt, ##_args); \ + clib_warning("%s", __tmp); \ + vec_free(__tmp); \ +} +#else +#define MFIB_ENTRY_DBG(_e, _fmt, _args...) +#endif + +/** + * The source of an MFIB entry + */ +typedef struct mfib_entry_src_t_ +{ + /** + * Which source this is + */ + mfib_source_t mfes_src; + + /** + * The path-list of forwarding interfaces + */ + fib_node_index_t mfes_pl; + + /** + * Route flags + */ + mfib_entry_flags_t mfes_flags; + + /** + * The hash table of all interfaces + */ + mfib_itf_t *mfes_itfs; +} mfib_entry_src_t; + +/** + * String names for each source + */ +static const char *mfib_source_names[] = MFIB_SOURCE_NAMES; + +/* + * Pool for all fib_entries + */ +mfib_entry_t *mfib_entry_pool; + +static fib_node_t * +mfib_entry_get_node (fib_node_index_t index) +{ + return ((fib_node_t*)mfib_entry_get(index)); +} + +static fib_protocol_t +mfib_entry_get_proto (const mfib_entry_t * mfib_entry) +{ + return (mfib_entry->mfe_prefix.fp_proto); +} + +fib_forward_chain_type_t +mfib_entry_get_default_chain_type (const mfib_entry_t *mfib_entry) +{ + switch (mfib_entry->mfe_prefix.fp_proto) + { + case FIB_PROTOCOL_IP4: + return (FIB_FORW_CHAIN_TYPE_MCAST_IP4); + case FIB_PROTOCOL_IP6: + return (FIB_FORW_CHAIN_TYPE_MCAST_IP6); + case FIB_PROTOCOL_MPLS: + ASSERT(0); + break; + } + return (FIB_FORW_CHAIN_TYPE_MCAST_IP4); +} + +static u8 * +format_mfib_entry_dpo (u8 * s, va_list * args) +{ + index_t fei = va_arg(*args, index_t); + CLIB_UNUSED(u32 indent) = va_arg(*args, u32); + + return (format(s, "%U", + format_mfib_entry, fei, + MFIB_ENTRY_FORMAT_BRIEF)); +} + +u8 * +format_mfib_entry (u8 * s, va_list * args) +{ + fib_node_index_t fei, mfi; + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + u32 sw_if_index; + int level; + + fei = va_arg (*args, fib_node_index_t); + level = va_arg (*args, int); + mfib_entry = mfib_entry_get(fei); + + s = format (s, "%U", format_mfib_prefix, &mfib_entry->mfe_prefix); + s = format (s, ": %U", format_mfib_entry_flags, mfib_entry->mfe_flags); + + if (level >= MFIB_ENTRY_FORMAT_DETAIL) + { + s = format (s, "\n"); + s = format (s, " fib:%d", mfib_entry->mfe_fib_index); + s = format (s, " index:%d", mfib_entry_get_index(mfib_entry)); + s = format (s, " locks:%d\n", mfib_entry->mfe_node.fn_locks); + vec_foreach(msrc, mfib_entry->mfe_srcs) + { + s = format (s, " src:%s", mfib_source_names[msrc->mfes_src]); + s = format (s, ": %U\n", format_mfib_entry_flags, msrc->mfes_flags); + if (FIB_NODE_INDEX_INVALID != msrc->mfes_pl) + { + s = fib_path_list_format(msrc->mfes_pl, s); + } + hash_foreach(sw_if_index, mfi, msrc->mfes_itfs, + ({ + s = format(s, " %U\n", format_mfib_itf, mfi); + })); + } + } + + s = format(s, "\n Interfaces:"); + hash_foreach(sw_if_index, mfi, mfib_entry->mfe_itfs, + ({ + s = format(s, "\n %U", format_mfib_itf, mfi); + })); + + s = format(s, "\n %U-chain\n %U", + format_fib_forw_chain_type, + mfib_entry_get_default_chain_type(mfib_entry), + format_dpo_id, + &mfib_entry->mfe_rep, + 2); + s = format(s, "\n"); + + if (level >= MFIB_ENTRY_FORMAT_DETAIL2) + { + s = format(s, "\nchildren:"); + s = fib_node_children_format(mfib_entry->mfe_node.fn_children, s); + } + + return (s); +} + +static mfib_entry_t* +mfib_entry_from_fib_node (fib_node_t *node) +{ +#if CLIB_DEBUG > 0 + ASSERT(FIB_NODE_TYPE_MFIB_ENTRY == node->fn_type); +#endif + return ((mfib_entry_t*)node); +} + +static int +mfib_entry_src_cmp_for_sort (void * v1, + void * v2) +{ + mfib_entry_src_t *esrc1 = v1, *esrc2 = v2; + + return (esrc1->mfes_src - esrc2->mfes_src); +} + +static void +mfib_entry_src_init (mfib_entry_t *mfib_entry, + mfib_source_t source) + +{ + mfib_entry_src_t esrc = { + .mfes_pl = FIB_NODE_INDEX_INVALID, + .mfes_flags = MFIB_ENTRY_FLAG_NONE, + .mfes_src = source, + }; + + vec_add1(mfib_entry->mfe_srcs, esrc); + vec_sort_with_function(mfib_entry->mfe_srcs, + mfib_entry_src_cmp_for_sort); +} + +static mfib_entry_src_t * +mfib_entry_src_find (const mfib_entry_t *mfib_entry, + mfib_source_t source, + u32 *index) + +{ + mfib_entry_src_t *esrc; + int ii; + + ii = 0; + vec_foreach(esrc, mfib_entry->mfe_srcs) + { + if (esrc->mfes_src == source) + { + if (NULL != index) + { + *index = ii; + } + return (esrc); + } + else + { + ii++; + } + } + + return (NULL); +} + +static mfib_entry_src_t * +mfib_entry_src_find_or_create (mfib_entry_t *mfib_entry, + mfib_source_t source) +{ + mfib_entry_src_t *esrc; + + esrc = mfib_entry_src_find(mfib_entry, source, NULL); + + if (NULL == esrc) + { + mfib_entry_src_init(mfib_entry, source); + } + + return (mfib_entry_src_find(mfib_entry, source, NULL)); +} + +static mfib_entry_src_t* +mfib_entry_get_best_src (const mfib_entry_t *mfib_entry) +{ + mfib_entry_src_t *bsrc; + + /* + * the enum of sources is deliberately arranged in priority order + */ + if (0 == vec_len(mfib_entry->mfe_srcs)) + { + bsrc = NULL; + } + else + { + bsrc = vec_elt_at_index(mfib_entry->mfe_srcs, 0); + } + + return (bsrc); +} + +static void +mfib_entry_src_flush (mfib_entry_src_t *msrc) +{ + u32 sw_if_index; + index_t mfii; + + hash_foreach(sw_if_index, mfii, msrc->mfes_itfs, + ({ + mfib_itf_delete(mfib_itf_get(mfii)); + })); +} + +static void +mfib_entry_src_remove (mfib_entry_t *mfib_entry, + mfib_source_t source) + +{ + mfib_entry_src_t *msrc; + u32 index = ~0; + + msrc = mfib_entry_src_find(mfib_entry, source, &index); + + if (NULL != msrc) + { + mfib_entry_src_flush(msrc); + vec_del1(mfib_entry->mfe_srcs, index); + } +} + +static int +mfib_entry_src_n_itfs (const mfib_entry_src_t *msrc) +{ + return (hash_elts(msrc->mfes_itfs)); +} + + +static void +mfib_entry_last_lock_gone (fib_node_t *node) +{ + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + + mfib_entry = mfib_entry_from_fib_node(node); + + dpo_reset(&mfib_entry->mfe_rep); + + MFIB_ENTRY_DBG(mfib_entry, "last-lock"); + + vec_foreach(msrc, mfib_entry->mfe_srcs) + { + mfib_entry_src_flush(msrc); + } + + fib_path_list_unlock(mfib_entry->mfe_parent); + vec_free(mfib_entry->mfe_srcs); + + fib_node_deinit(&mfib_entry->mfe_node); + pool_put(mfib_entry_pool, mfib_entry); +} + +/* + * mfib_entry_back_walk_notify + * + * A back walk has reach this entry. + */ +static fib_node_back_walk_rc_t +mfib_entry_back_walk_notify (fib_node_t *node, + fib_node_back_walk_ctx_t *ctx) +{ + // FIXME - re-evalute + + return (FIB_NODE_BACK_WALK_CONTINUE); +} + +static void +mfib_entry_show_memory (void) +{ + fib_show_memory_usage("multicast-Entry", + pool_elts(mfib_entry_pool), + pool_len(mfib_entry_pool), + sizeof(mfib_entry_t)); +} + +/* + * The MFIB entry's graph node virtual function table + */ +static const fib_node_vft_t mfib_entry_vft = { + .fnv_get = mfib_entry_get_node, + .fnv_last_lock = mfib_entry_last_lock_gone, + .fnv_back_walk = mfib_entry_back_walk_notify, + .fnv_mem_show = mfib_entry_show_memory, +}; + +u32 +mfib_entry_child_add (fib_node_index_t mfib_entry_index, + fib_node_type_t child_type, + fib_node_index_t child_index) +{ + return (fib_node_child_add(FIB_NODE_TYPE_MFIB_ENTRY, + mfib_entry_index, + child_type, + child_index)); +}; + +void +mfib_entry_child_remove (fib_node_index_t mfib_entry_index, + u32 sibling_index) +{ + fib_node_child_remove(FIB_NODE_TYPE_MFIB_ENTRY, + mfib_entry_index, + sibling_index); +} + +static mfib_entry_t * +mfib_entry_alloc (u32 fib_index, + const mfib_prefix_t *prefix, + fib_node_index_t *mfib_entry_index) +{ + mfib_entry_t *mfib_entry; + + pool_get(mfib_entry_pool, mfib_entry); + memset(mfib_entry, 0, sizeof(*mfib_entry)); + + fib_node_init(&mfib_entry->mfe_node, + FIB_NODE_TYPE_MFIB_ENTRY); + + mfib_entry->mfe_fib_index = fib_index; + mfib_entry->mfe_prefix = *prefix; + mfib_entry->mfe_parent = FIB_NODE_INDEX_INVALID; + + dpo_reset(&mfib_entry->mfe_rep); + + *mfib_entry_index = mfib_entry_get_index(mfib_entry); + + MFIB_ENTRY_DBG(mfib_entry, "alloc"); + + return (mfib_entry); +} + +typedef struct mfib_entry_collect_forwarding_ctx_t_ +{ + load_balance_path_t * next_hops; + fib_forward_chain_type_t fct; +} mfib_entry_collect_forwarding_ctx_t; + +static int +mfib_entry_src_collect_forwarding (fib_node_index_t pl_index, + fib_node_index_t path_index, + void *arg) +{ + mfib_entry_collect_forwarding_ctx_t *ctx; + load_balance_path_t *nh; + + ctx = arg; + + /* + * if the path is not resolved, don't include it. + */ + if (!fib_path_is_resolved(path_index)) + { + return (!0); + } + + switch (ctx->fct) + { + case FIB_FORW_CHAIN_TYPE_MCAST_IP4: + case FIB_FORW_CHAIN_TYPE_MCAST_IP6: + /* + * EOS traffic with no label to stack, we need the IP Adj + */ + vec_add2(ctx->next_hops, nh, 1); + + nh->path_index = path_index; + nh->path_weight = fib_path_get_weight(path_index); + fib_path_contribute_forwarding(path_index, ctx->fct, &nh->path_dpo); + break; + + case FIB_FORW_CHAIN_TYPE_UNICAST_IP4: + case FIB_FORW_CHAIN_TYPE_UNICAST_IP6: + case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS: + case FIB_FORW_CHAIN_TYPE_MPLS_EOS: + case FIB_FORW_CHAIN_TYPE_ETHERNET: + ASSERT(0); + break; + } + + return (!0); +} + +static void +mfib_entry_stack (mfib_entry_t *mfib_entry) +{ + mfib_entry_collect_forwarding_ctx_t ctx = { + .next_hops = NULL, + .fct = mfib_entry_get_default_chain_type(mfib_entry), + }; + dpo_proto_t dp; + + dp = fib_proto_to_dpo(mfib_entry_get_proto(mfib_entry)); + + if (FIB_NODE_INDEX_INVALID != mfib_entry->mfe_parent) + { + fib_path_list_walk(mfib_entry->mfe_parent, + mfib_entry_src_collect_forwarding, + &ctx); + + if (!dpo_id_is_valid(&mfib_entry->mfe_rep) || + dpo_is_drop(&mfib_entry->mfe_rep)) + { + dpo_id_t tmp_dpo = DPO_INVALID; + + dpo_set(&tmp_dpo, + DPO_REPLICATE, dp, + replicate_create(0, dp)); + + dpo_stack(DPO_MFIB_ENTRY, dp, + &mfib_entry->mfe_rep, + &tmp_dpo); + + dpo_reset(&tmp_dpo); + } + replicate_multipath_update(&mfib_entry->mfe_rep, + ctx.next_hops); + } + else + { + dpo_stack(DPO_MFIB_ENTRY, dp, + &mfib_entry->mfe_rep, + drop_dpo_get(dp)); + } +} + +static void +mfib_entry_forwarding_path_add (mfib_entry_src_t *msrc, + const fib_route_path_t *rpath) +{ + fib_node_index_t old_pl_index; + fib_route_path_t *rpaths; + + /* + * path-lists require a vector of paths + */ + rpaths = NULL; + vec_add1(rpaths, rpath[0]); + + old_pl_index = msrc->mfes_pl; + + if (FIB_NODE_INDEX_INVALID == msrc->mfes_pl) + { + msrc->mfes_pl = + fib_path_list_create(FIB_PATH_LIST_FLAG_NO_URPF, + rpaths); + } + else + { + msrc->mfes_pl = + fib_path_list_copy_and_path_add(msrc->mfes_pl, + FIB_PATH_LIST_FLAG_NO_URPF, + rpaths); + } + fib_path_list_lock(msrc->mfes_pl); + fib_path_list_unlock(old_pl_index); + + vec_free(rpaths); +} + +static int +mfib_entry_forwarding_path_remove (mfib_entry_src_t *msrc, + const fib_route_path_t *rpath) +{ + fib_node_index_t old_pl_index; + fib_route_path_t *rpaths; + + /* + * path-lists require a vector of paths + */ + rpaths = NULL; + vec_add1(rpaths, rpath[0]); + + old_pl_index = msrc->mfes_pl; + + msrc->mfes_pl = + fib_path_list_copy_and_path_remove(msrc->mfes_pl, + FIB_PATH_LIST_FLAG_NONE, + rpaths); + + fib_path_list_lock(msrc->mfes_pl); + fib_path_list_unlock(old_pl_index); + + vec_free(rpaths); + + return (FIB_NODE_INDEX_INVALID != msrc->mfes_pl); +} + +static void +mfib_entry_recalculate_forwarding (mfib_entry_t *mfib_entry) +{ + fib_node_index_t old_pl_index; + mfib_entry_src_t *bsrc; + + old_pl_index = mfib_entry->mfe_parent; + + /* + * copy the forwarding data from the bast source + */ + bsrc = mfib_entry_get_best_src(mfib_entry); + + if (NULL == bsrc) + { + mfib_entry->mfe_parent = FIB_NODE_INDEX_INVALID; + } + else + { + mfib_entry->mfe_parent = bsrc->mfes_pl; + mfib_entry->mfe_flags = bsrc->mfes_flags; + mfib_entry->mfe_itfs = bsrc->mfes_itfs; + } + + /* + * re-stack the entry on the best forwarding info. + */ + if (old_pl_index != mfib_entry->mfe_parent || + FIB_NODE_INDEX_INVALID == old_pl_index) + { + mfib_entry_stack(mfib_entry); + + fib_path_list_lock(mfib_entry->mfe_parent); + fib_path_list_unlock(old_pl_index); + } +} + + +fib_node_index_t +mfib_entry_create (u32 fib_index, + mfib_source_t source, + const mfib_prefix_t *prefix, + mfib_entry_flags_t entry_flags) +{ + fib_node_index_t mfib_entry_index; + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + + mfib_entry = mfib_entry_alloc(fib_index, prefix, + &mfib_entry_index); + msrc = mfib_entry_src_find_or_create(mfib_entry, source); + msrc->mfes_flags = entry_flags; + + mfib_entry_recalculate_forwarding(mfib_entry); + + return (mfib_entry_index); +} + +static int +mfib_entry_ok_for_delete (mfib_entry_t *mfib_entry) +{ + return (0 == vec_len(mfib_entry->mfe_srcs)); +} + +static int +mfib_entry_src_ok_for_delete (const mfib_entry_src_t *msrc) +{ + return ((MFIB_ENTRY_FLAG_NONE == msrc->mfes_flags && + 0 == mfib_entry_src_n_itfs(msrc))); +} + +int +mfib_entry_update (fib_node_index_t mfib_entry_index, + mfib_source_t source, + mfib_entry_flags_t entry_flags) +{ + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + + mfib_entry = mfib_entry_get(mfib_entry_index); + msrc = mfib_entry_src_find_or_create(mfib_entry, source); + msrc->mfes_flags = entry_flags; + + if (mfib_entry_src_ok_for_delete(msrc)) + { + /* + * this source has no interfaces and no flags. + * it has nothing left to give - remove it + */ + mfib_entry_src_remove(mfib_entry, source); + } + + mfib_entry_recalculate_forwarding(mfib_entry); + + return (mfib_entry_ok_for_delete(mfib_entry)); +} + +static void +mfib_entry_itf_add (mfib_entry_src_t *msrc, + u32 sw_if_index, + index_t mi) +{ + hash_set(msrc->mfes_itfs, sw_if_index, mi); +} + +static void +mfib_entry_itf_remove (mfib_entry_src_t *msrc, + u32 sw_if_index) +{ + mfib_itf_t *mfi; + + mfi = mfib_entry_itf_find(msrc->mfes_itfs, sw_if_index); + + mfib_itf_delete(mfi); + + hash_unset(msrc->mfes_itfs, sw_if_index); +} + +void +mfib_entry_path_update (fib_node_index_t mfib_entry_index, + mfib_source_t source, + const fib_route_path_t *rpath, + mfib_itf_flags_t itf_flags) +{ + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + mfib_itf_t *mfib_itf; + + mfib_entry = mfib_entry_get(mfib_entry_index); + ASSERT(NULL != mfib_entry); + msrc = mfib_entry_src_find_or_create(mfib_entry, source); + + /* + * search for the interface in the current set + */ + mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs, + rpath[0].frp_sw_if_index); + + if (NULL == mfib_itf) + { + /* + * this is a path we do not yet have. If it is forwarding then we + * add it to the replication set + */ + if (itf_flags & MFIB_ITF_FLAG_FORWARD) + { + mfib_entry_forwarding_path_add(msrc, rpath); + } + /* + * construct a new ITF for this entry's list + */ + mfib_entry_itf_add(msrc, + rpath[0].frp_sw_if_index, + mfib_itf_create(rpath[0].frp_sw_if_index, + itf_flags)); + } + else + { + int was_forwarding = !!(mfib_itf->mfi_flags & MFIB_ITF_FLAG_FORWARD); + int is_forwarding = !!(itf_flags & MFIB_ITF_FLAG_FORWARD); + + if (!was_forwarding && is_forwarding) + { + mfib_entry_forwarding_path_add(msrc, rpath); + } + else if (was_forwarding && !is_forwarding) + { + mfib_entry_forwarding_path_remove(msrc, rpath); + } + /* + * packets in flight see these updates. + */ + mfib_itf->mfi_flags = itf_flags; + } + + mfib_entry_recalculate_forwarding(mfib_entry); +} + +/* + * mfib_entry_path_remove + * + * remove a path from the entry. + * return the mfib_entry's index if it is still present, INVALID otherwise. + */ +int +mfib_entry_path_remove (fib_node_index_t mfib_entry_index, + mfib_source_t source, + const fib_route_path_t *rpath) +{ + mfib_entry_t *mfib_entry; + mfib_entry_src_t *msrc; + mfib_itf_t *mfib_itf; + + mfib_entry = mfib_entry_get(mfib_entry_index); + ASSERT(NULL != mfib_entry); + msrc = mfib_entry_src_find(mfib_entry, source, NULL); + + if (NULL == msrc) + { + /* + * there are no paths left for this source + */ + return (mfib_entry_ok_for_delete(mfib_entry)); + } + + /* + * search for the interface in the current set + */ + mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs, + rpath[0].frp_sw_if_index); + + if (NULL == mfib_itf) + { + /* + * removing a path that does not exist + */ + return (mfib_entry_ok_for_delete(mfib_entry)); + } + + /* + * we have this path. If it is forwarding then we + * remove it to the replication set + */ + if (mfib_itf->mfi_flags & MFIB_ITF_FLAG_FORWARD) + { + mfib_entry_forwarding_path_remove(msrc, rpath); + } + + /* + * remove the interface/path from this entry's list + */ + mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index); + + if (mfib_entry_src_ok_for_delete(msrc)) + { + /* + * this source has no interfaces and no flags. + * it has nothing left to give - remove it + */ + mfib_entry_src_remove(mfib_entry, source); + } + + mfib_entry_recalculate_forwarding(mfib_entry); + + return (mfib_entry_ok_for_delete(mfib_entry)); +} + +/** + * mfib_entry_delete + * + * The source is withdrawing all the paths it provided + */ +int +mfib_entry_delete (fib_node_index_t mfib_entry_index, + mfib_source_t source) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + mfib_entry_src_remove(mfib_entry, source); + + mfib_entry_recalculate_forwarding(mfib_entry); + + return (mfib_entry_ok_for_delete(mfib_entry)); +} + +static int +fib_ip4_address_compare (ip4_address_t * a1, + ip4_address_t * a2) +{ + /* + * IP addresses are unsiged ints. the return value here needs to be signed + * a simple subtraction won't cut it. + * If the addresses are the same, the sort order is undefiend, so phoey. + */ + return ((clib_net_to_host_u32(a1->data_u32) > + clib_net_to_host_u32(a2->data_u32) ) ? + 1 : -1); +} + +static int +fib_ip6_address_compare (ip6_address_t * a1, + ip6_address_t * a2) +{ + int i; + for (i = 0; i < ARRAY_LEN (a1->as_u16); i++) + { + int cmp = (clib_net_to_host_u16 (a1->as_u16[i]) - + clib_net_to_host_u16 (a2->as_u16[i])); + if (cmp != 0) + return cmp; + } + return 0; +} + +static int +mfib_entry_cmp (fib_node_index_t mfib_entry_index1, + fib_node_index_t mfib_entry_index2) +{ + mfib_entry_t *mfib_entry1, *mfib_entry2; + int cmp = 0; + + mfib_entry1 = mfib_entry_get(mfib_entry_index1); + mfib_entry2 = mfib_entry_get(mfib_entry_index2); + + switch (mfib_entry1->mfe_prefix.fp_proto) + { + case FIB_PROTOCOL_IP4: + cmp = fib_ip4_address_compare(&mfib_entry1->mfe_prefix.fp_grp_addr.ip4, + &mfib_entry2->mfe_prefix.fp_grp_addr.ip4); + + if (0 == cmp) + { + cmp = fib_ip4_address_compare(&mfib_entry1->mfe_prefix.fp_src_addr.ip4, + &mfib_entry2->mfe_prefix.fp_src_addr.ip4); + } + break; + case FIB_PROTOCOL_IP6: + cmp = fib_ip6_address_compare(&mfib_entry1->mfe_prefix.fp_grp_addr.ip6, + &mfib_entry2->mfe_prefix.fp_grp_addr.ip6); + + if (0 == cmp) + { + cmp = fib_ip6_address_compare(&mfib_entry1->mfe_prefix.fp_src_addr.ip6, + &mfib_entry2->mfe_prefix.fp_src_addr.ip6); + } + break; + case FIB_PROTOCOL_MPLS: + ASSERT(0); + cmp = 0; + break; + } + + if (0 == cmp) { + cmp = (mfib_entry1->mfe_prefix.fp_len - mfib_entry2->mfe_prefix.fp_len); + } + return (cmp); +} + +int +mfib_entry_cmp_for_sort (void *i1, void *i2) +{ + fib_node_index_t *mfib_entry_index1 = i1, *mfib_entry_index2 = i2; + + return (mfib_entry_cmp(*mfib_entry_index1, + *mfib_entry_index2)); +} + +void +mfib_entry_lock (fib_node_index_t mfib_entry_index) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + + fib_node_lock(&mfib_entry->mfe_node); +} + +void +mfib_entry_unlock (fib_node_index_t mfib_entry_index) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + + fib_node_unlock(&mfib_entry->mfe_node); +} + +static void +mfib_entry_dpo_lock (dpo_id_t *dpo) +{ +} +static void +mfib_entry_dpo_unlock (dpo_id_t *dpo) +{ +} + +const static dpo_vft_t mfib_entry_dpo_vft = { + .dv_lock = mfib_entry_dpo_lock, + .dv_unlock = mfib_entry_dpo_unlock, + .dv_format = format_mfib_entry_dpo, + .dv_mem_show = mfib_entry_show_memory, +}; + +const static char* const mfib_entry_ip4_nodes[] = +{ + "ip4-mfib-forward-rpf", + NULL, +}; +const static char* const mfib_entry_ip6_nodes[] = +{ + "ip6-mfib-forward-rpf", + NULL, +}; + +const static char* const * const mfib_entry_nodes[DPO_PROTO_NUM] = +{ + [DPO_PROTO_IP4] = mfib_entry_ip4_nodes, + [DPO_PROTO_IP6] = mfib_entry_ip6_nodes, +}; + +void +mfib_entry_module_init (void) +{ + fib_node_register_type (FIB_NODE_TYPE_MFIB_ENTRY, &mfib_entry_vft); + dpo_register(DPO_MFIB_ENTRY, &mfib_entry_dpo_vft, mfib_entry_nodes); +} + +void +mfib_entry_encode (fib_node_index_t mfib_entry_index, + fib_route_path_encode_t **api_rpaths) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + fib_path_list_walk(mfib_entry->mfe_parent, fib_path_encode, api_rpaths); +} + +void +mfib_entry_get_prefix (fib_node_index_t mfib_entry_index, + mfib_prefix_t *pfx) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + *pfx = mfib_entry->mfe_prefix; +} + +u32 +mfib_entry_get_fib_index (fib_node_index_t mfib_entry_index) +{ + mfib_entry_t *mfib_entry; + + mfib_entry = mfib_entry_get(mfib_entry_index); + + return (mfib_entry->mfe_fib_index); +} + +void +mfib_entry_contribute_forwarding (fib_node_index_t mfib_entry_index, + fib_forward_chain_type_t type, + dpo_id_t *dpo) +{ + /* + * An IP mFIB entry can only provide a forwarding chain that + * is the same IP proto as the prefix. + * No use-cases (i know of) for other combinations. + */ + mfib_entry_t *mfib_entry; + dpo_proto_t dp; + + mfib_entry = mfib_entry_get(mfib_entry_index); + + dp = fib_proto_to_dpo(mfib_entry->mfe_prefix.fp_proto); + + if (type == fib_forw_chain_type_from_dpo_proto(dp)) + { + dpo_copy(dpo, &mfib_entry->mfe_rep); + } + else + { + dpo_copy(dpo, drop_dpo_get(dp)); + } +} + +u32 +mfib_entry_pool_size (void) +{ + return (pool_elts(mfib_entry_pool)); +} + +static clib_error_t * +show_mfib_entry_command (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + fib_node_index_t fei; + + if (unformat (input, "%d", &fei)) + { + /* + * show one in detail + */ + if (!pool_is_free_index(mfib_entry_pool, fei)) + { + vlib_cli_output (vm, "%d@%U", + fei, + format_mfib_entry, fei, + MFIB_ENTRY_FORMAT_DETAIL2); + } + else + { + vlib_cli_output (vm, "entry %d invalid", fei); + } + } + else + { + /* + * show all + */ + vlib_cli_output (vm, "FIB Entries:"); + pool_foreach_index(fei, mfib_entry_pool, + ({ + vlib_cli_output (vm, "%d@%U", + fei, + format_mfib_entry, fei, + MFIB_ENTRY_FORMAT_BRIEF); + })); + } + + return (NULL); +} + +VLIB_CLI_COMMAND (show_mfib_entry, static) = { + .path = "show mfib entry", + .function = show_mfib_entry_command, + .short_help = "show mfib entry", +}; diff --git a/src/vnet/mfib/mfib_entry.h b/src/vnet/mfib/mfib_entry.h new file mode 100644 index 00000000000..cc5d5326ef6 --- /dev/null +++ b/src/vnet/mfib/mfib_entry.h @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#ifndef __MFIB_ENTRY_H__ +#define __MFIB_ENTRY_H__ + +#include <vnet/fib/fib_node.h> +#include <vnet/mfib/mfib_types.h> +#include <vnet/mfib/mfib_itf.h> +#include <vnet/ip/ip.h> +#include <vnet/dpo/dpo.h> + +/** + * An entry in a FIB table. + * + * This entry represents a route added to the FIB that is stored + * in one of the FIB tables. + */ +typedef struct mfib_entry_t_ { + CLIB_CACHE_LINE_ALIGN_MARK(cacheline0); + /** + * Base class. The entry's node representation in the graph. + */ + fib_node_t mfe_node; + /** + * The prefix of the route + */ + mfib_prefix_t mfe_prefix; + /** + * The index of the FIB table this entry is in + */ + u32 mfe_fib_index; + /** + * the path-list for which this entry is a child. This is also the path-list + * that is contributing forwarding for this entry. + */ + fib_node_index_t mfe_parent; + /** + * index of this entry in the parent's child list. + * This is set when this entry is added as a child, but can also + * be changed by the parent as it manages its list. + */ + u32 mfe_sibling; + + /** + * A vector of sources contributing forwarding + */ + struct mfib_entry_src_t_ *mfe_srcs; + + /** + * 2nd cache line has the members used in the data plane + */ + CLIB_CACHE_LINE_ALIGN_MARK(cacheline1); + + /** + * The Replicate used for forwarding. + */ + dpo_id_t mfe_rep; + + /** + * Route flags + */ + mfib_entry_flags_t mfe_flags; + + /** + * A hash table of interfaces + */ + mfib_itf_t *mfe_itfs; +} mfib_entry_t; + +#define MFIB_ENTRY_FORMAT_BRIEF (0x0) +#define MFIB_ENTRY_FORMAT_DETAIL (0x1) +#define MFIB_ENTRY_FORMAT_DETAIL2 (0x2) + +extern u8 *format_mfib_entry(u8 * s, va_list * args); + + +extern fib_node_index_t mfib_entry_create(u32 fib_index, + mfib_source_t source, + const mfib_prefix_t *prefix, + mfib_entry_flags_t entry_flags); + +extern int mfib_entry_update(fib_node_index_t fib_entry_index, + mfib_source_t source, + mfib_entry_flags_t entry_flags); + +extern void mfib_entry_path_update(fib_node_index_t fib_entry_index, + mfib_source_t source, + const fib_route_path_t *rpath, + mfib_itf_flags_t itf_flags); + + +extern int mfib_entry_path_remove(fib_node_index_t fib_entry_index, + mfib_source_t source, + const fib_route_path_t *rpath); + +extern int mfib_entry_delete(fib_node_index_t mfib_entry_index, + mfib_source_t source); + +extern int mfib_entry_cmp_for_sort(void *i1, void *i2); + +extern u32 mfib_entry_child_add(fib_node_index_t mfib_entry_index, + fib_node_type_t type, + fib_node_index_t child_index); +extern void mfib_entry_child_remove(fib_node_index_t mfib_entry_index, + u32 sibling_index); + +extern void mfib_entry_lock(fib_node_index_t fib_entry_index); +extern void mfib_entry_unlock(fib_node_index_t fib_entry_index); + +extern void mfib_entry_get_prefix(fib_node_index_t fib_entry_index, + mfib_prefix_t *pfx); +extern u32 mfib_entry_get_fib_index(fib_node_index_t fib_entry_index); + +extern void mfib_entry_contribute_forwarding( + fib_node_index_t mfib_entry_index, + fib_forward_chain_type_t type, + dpo_id_t *dpo); + +extern void mfib_entry_module_init(void); + + +extern mfib_entry_t *mfib_entry_pool; + +static inline mfib_entry_t * +mfib_entry_get (fib_node_index_t index) +{ + return (pool_elt_at_index(mfib_entry_pool, index)); +} +static inline fib_node_index_t +mfib_entry_get_index (const mfib_entry_t *mfe) +{ + return (mfe - mfib_entry_pool); +} + + +static inline mfib_itf_t * +mfib_entry_itf_find (mfib_itf_t *itfs, + u32 sw_if_index) +{ + uword *p; + + p = hash_get(itfs, sw_if_index); + + if (NULL != p) + { + return (mfib_itf_get(p[0])); + } + + return (NULL); +} + +static inline mfib_itf_t * +mfib_entry_get_itf (const mfib_entry_t *mfe, + u32 sw_if_index) +{ + return (mfib_entry_itf_find(mfe->mfe_itfs, sw_if_index)); +} + +#endif diff --git a/src/vnet/mfib/mfib_forward.c b/src/vnet/mfib/mfib_forward.c new file mode 100644 index 00000000000..5fe0a57c03b --- /dev/null +++ b/src/vnet/mfib/mfib_forward.c @@ -0,0 +1,512 @@ +/* + * 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/mfib/mfib_itf.h> +#include <vnet/mfib/mfib_entry.h> +#include <vnet/dpo/replicate_dpo.h> +#include <vnet/mfib/ip4_mfib.h> +#include <vnet/mfib/ip6_mfib.h> +#include <vnet/mfib/mfib_signal.h> +#include <vnet/fib/ip4_fib.h> +#include <vnet/fib/ip6_fib.h> + +#include <vnet/ip/ip4.h> +#include <vnet/vnet.h> + +typedef struct mfib_forward_lookup_trace_t_ { + u32 entry_index; + u32 fib_index; +} mfib_forward_lookup_trace_t; + +static u8 * +format_mfib_forward_lookup_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + mfib_forward_lookup_trace_t * t = va_arg (*args, mfib_forward_lookup_trace_t *); + + s = format (s, "fib %d entry %d", t->fib_index, t->entry_index); + return s; +} + +/* Common trace function for all ip4-forward next nodes. */ +void +mfib_forward_lookup_trace (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + u32 * from, n_left; + ip4_main_t * im = &ip4_main; + + n_left = frame->n_vectors; + from = vlib_frame_vector_args (frame); + + while (n_left >= 4) + { + mfib_forward_lookup_trace_t * t0, * t1; + vlib_buffer_t * b0, * b1; + u32 bi0, bi1; + + /* Prefetch next iteration. */ + vlib_prefetch_buffer_with_index (vm, from[2], LOAD); + vlib_prefetch_buffer_with_index (vm, from[3], LOAD); + + bi0 = from[0]; + bi1 = from[1]; + + b0 = vlib_get_buffer (vm, bi0); + b1 = vlib_get_buffer (vm, bi1); + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); + t0->entry_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + t0->fib_index = vec_elt (im->mfib_index_by_sw_if_index, + vnet_buffer(b1)->sw_if_index[VLIB_RX]); + } + if (b1->flags & VLIB_BUFFER_IS_TRACED) + { + t1 = vlib_add_trace (vm, node, b1, sizeof (t1[0])); + t1->entry_index = vnet_buffer (b1)->ip.adj_index[VLIB_TX]; + t1->fib_index = vec_elt (im->mfib_index_by_sw_if_index, + vnet_buffer(b1)->sw_if_index[VLIB_RX]); + } + from += 2; + n_left -= 2; + } + + while (n_left >= 1) + { + mfib_forward_lookup_trace_t * t0; + vlib_buffer_t * b0; + u32 bi0; + + bi0 = from[0]; + + b0 = vlib_get_buffer (vm, bi0); + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); + t0->entry_index = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + t0->fib_index = vec_elt (im->mfib_index_by_sw_if_index, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + } + from += 1; + n_left -= 1; + } +} + +typedef enum mfib_forward_lookup_next_t_ { + MFIB_FORWARD_LOOKUP_NEXT_RPF, + MFIB_FORWARD_LOOKUP_N_NEXT, +} mfib_forward_lookup_next_t; + +static uword +mfib_forward_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_v4) +{ + u32 n_left_from, n_left_to_next, * from, * to_next; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, MFIB_FORWARD_LOOKUP_NEXT_RPF, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + fib_node_index_t mfei0; + vlib_buffer_t * p0; + u32 fib_index0; + u32 pi0; + + pi0 = from[0]; + to_next[0] = pi0; + from += 1; + to_next += 1; + n_left_to_next -= 1; + n_left_from -= 1; + + p0 = vlib_get_buffer (vm, pi0); + + if (is_v4) + { + ip4_header_t * ip0; + + fib_index0 = vec_elt (ip4_main.mfib_index_by_sw_if_index, + vnet_buffer(p0)->sw_if_index[VLIB_RX]); + ip0 = vlib_buffer_get_current (p0); + mfei0 = ip4_mfib_table_lookup(ip4_mfib_get(fib_index0), + &ip0->src_address, + &ip0->dst_address, + 64); + } + else + { + ip6_header_t * ip0; + + fib_index0 = vec_elt (ip6_main.mfib_index_by_sw_if_index, + vnet_buffer(p0)->sw_if_index[VLIB_RX]); + ip0 = vlib_buffer_get_current (p0); + mfei0 = ip6_mfib_table_lookup2(ip6_mfib_get(fib_index0), + &ip0->src_address, + &ip0->dst_address); + } + + vnet_buffer (p0)->ip.adj_index[VLIB_TX] = mfei0; + } + + vlib_put_next_frame(vm, node, + MFIB_FORWARD_LOOKUP_NEXT_RPF, + n_left_to_next); + } + + if (node->flags & VLIB_NODE_FLAG_TRACE) + mfib_forward_lookup_trace(vm, node, frame); + + return frame->n_vectors; +} + +static uword +ip4_mfib_forward_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (mfib_forward_lookup (vm, node, frame, 1)); +} + +VLIB_REGISTER_NODE (ip4_mfib_forward_lookup_node, static) = { + .function = ip4_mfib_forward_lookup, + .name = "ip4-mfib-forward-lookup", + .vector_size = sizeof (u32), + + .format_trace = format_mfib_forward_lookup_trace, + + .n_next_nodes = MFIB_FORWARD_LOOKUP_N_NEXT, + .next_nodes = { + [MFIB_FORWARD_LOOKUP_NEXT_RPF] = "ip4-mfib-forward-rpf", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_mfib_forward_lookup_node, + ip4_mfib_forward_lookup) + +static uword +ip6_mfib_forward_lookup (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (mfib_forward_lookup (vm, node, frame, 0)); +} + +VLIB_REGISTER_NODE (ip6_mfib_forward_lookup_node, static) = { + .function = ip6_mfib_forward_lookup, + .name = "ip6-mfib-forward-lookup", + .vector_size = sizeof (u32), + + .format_trace = format_mfib_forward_lookup_trace, + + .n_next_nodes = MFIB_FORWARD_LOOKUP_N_NEXT, + .next_nodes = { + [MFIB_FORWARD_LOOKUP_NEXT_RPF] = "ip6-mfib-forward-rpf", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_mfib_forward_lookup_node, + ip6_mfib_forward_lookup) + + +typedef struct mfib_forward_rpf_trace_t_ { + u32 entry_index; + u32 sw_if_index; + mfib_itf_flags_t itf_flags; +} mfib_forward_rpf_trace_t; + +typedef enum mfib_forward_rpf_next_t_ { + MFIB_FORWARD_RPF_NEXT_DROP, + MFIB_FORWARD_RPF_N_NEXT, +} mfib_forward_rpf_next_t; + +static u8 * +format_mfib_forward_rpf_trace (u8 * s, va_list * args) +{ + CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *); + CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *); + mfib_forward_rpf_trace_t * t = va_arg (*args, mfib_forward_rpf_trace_t *); + + s = format (s, "entry %d", t->entry_index); + s = format (s, " %d", t->sw_if_index); + s = format (s, " %U", format_mfib_itf_flags, t->itf_flags); + + return s; +} + +static int +mfib_forward_connected_check (vlib_buffer_t * b0, + u32 sw_if_index, + int is_v4) +{ + /* + * Lookup the source of the IP packet in the + * FIB. return true if the entry is attached. + */ + index_t lbi0; + + if (is_v4) + { + load_balance_t *lb0; + ip4_header_t *ip0; + + ip0 = vlib_buffer_get_current(b0); + + lbi0 = ip4_fib_forwarding_lookup( + ip4_fib_table_get_index_for_sw_if_index( + sw_if_index), + &ip0->src_address); + lb0 = load_balance_get(lbi0); + + return (FIB_ENTRY_FLAG_ATTACHED & + lb0->lb_fib_entry_flags); + } + else + { + ASSERT(0); + } + return (0); +} + +static void +mfib_forward_itf_signal (vlib_main_t *vm, + const mfib_entry_t *mfe, + mfib_itf_t *mfi, + vlib_buffer_t *b0) +{ + mfib_itf_flags_t old_flags; + + old_flags = __sync_fetch_and_or(&mfi->mfi_flags, + MFIB_ITF_FLAG_SIGNAL_PRESENT); + + if (!(old_flags & MFIB_ITF_FLAG_SIGNAL_PRESENT)) + { + /* + * we were the lucky ones to set the signal present flag + */ + if (!(old_flags & MFIB_ITF_FLAG_DONT_PRESERVE)) + { + /* + * preserve a copy of the packet for the control + * plane to examine. + * Only allow one preserved packet at at time, since + * when the signal present flag is cleared so is the + * preserved packet. + */ + mfib_signal_push(mfe, mfi, b0); + } + else + { + /* + * The control plane just wants the signal, not the packet as well + */ + mfib_signal_push(mfe, mfi, NULL); + } + } + /* + * else + * there is already a signal present on this interface that the + * control plane has not yet acknowledged + */ +} + +always_inline uword +mfib_forward_rpf (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame, + int is_v4) +{ + u32 n_left_from, n_left_to_next, * from, * to_next; + mfib_forward_rpf_next_t next; + + from = vlib_frame_vector_args (frame); + n_left_from = frame->n_vectors; + next = MFIB_FORWARD_RPF_NEXT_DROP; + + while (n_left_from > 0) + { + vlib_get_next_frame (vm, node, next, + to_next, n_left_to_next); + + while (n_left_from > 0 && n_left_to_next > 0) + { + fib_node_index_t mfei0; + const mfib_entry_t *mfe0; + mfib_itf_t *mfi0; + vlib_buffer_t * b0; + u32 pi0, next0; + mfib_itf_flags_t iflags0; + mfib_entry_flags_t eflags0; + + pi0 = from[0]; + to_next[0] = pi0; + from += 1; + to_next += 1; + n_left_to_next -= 1; + n_left_from -= 1; + + b0 = vlib_get_buffer (vm, pi0); + mfei0 = vnet_buffer (b0)->ip.adj_index[VLIB_TX]; + mfe0 = mfib_entry_get(mfei0); + mfi0 = mfib_entry_get_itf(mfe0, + vnet_buffer(b0)->sw_if_index[VLIB_RX]); + + /* + * throughout this function we are 'PREDICT' optimising + * for the case of throughput traffic that is not replicated + * to the host stack nor sets local flags + */ + if (PREDICT_TRUE(NULL != mfi0)) + { + iflags0 = mfi0->mfi_flags; + } + else + { + iflags0 = MFIB_ITF_FLAG_NONE; + } + eflags0 = mfe0->mfe_flags; + + if (PREDICT_FALSE(eflags0 & MFIB_ENTRY_FLAG_CONNECTED)) + { + /* + * lookup the source in the unicast FIB - check it + * matches a connected. + */ + if (mfib_forward_connected_check( + b0, + vnet_buffer(b0)->sw_if_index[VLIB_RX], + is_v4)) + { + mfib_forward_itf_signal(vm, mfe0, mfi0, b0); + } + } + if (PREDICT_FALSE((eflags0 & MFIB_ENTRY_FLAG_SIGNAL) ^ + (iflags0 & MFIB_ITF_FLAG_NEGATE_SIGNAL))) + { + /* + * Entry signal XOR interface negate-signal + */ + if (NULL != mfi0) + { + mfib_forward_itf_signal(vm, mfe0, mfi0, b0); + } + } + + if (PREDICT_TRUE((iflags0 & MFIB_ITF_FLAG_ACCEPT) || + (eflags0 & MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF))) + { + /* + * This interface is accepting packets for the matching entry + */ + next0 = mfe0->mfe_rep.dpoi_next_node; + + vnet_buffer(b0)->ip.adj_index[VLIB_TX] = + mfe0->mfe_rep.dpoi_index; + } + else + { + next0 = MFIB_FORWARD_RPF_NEXT_DROP; + } + + if (b0->flags & VLIB_BUFFER_IS_TRACED) + { + mfib_forward_rpf_trace_t *t0; + + t0 = vlib_add_trace (vm, node, b0, sizeof (t0[0])); + t0->entry_index = mfei0; + if (NULL == mfi0) + { + t0->sw_if_index = ~0; + t0->itf_flags = MFIB_ITF_FLAG_NONE; + } + else + { + t0->sw_if_index = mfi0->mfi_sw_if_index; + t0->itf_flags = mfi0->mfi_flags; + } + } + vlib_validate_buffer_enqueue_x1 (vm, node, next, + to_next, n_left_to_next, + pi0, next0); + } + + vlib_put_next_frame(vm, node, next, n_left_to_next); + } + + return frame->n_vectors; +} + +static uword +ip4_mfib_forward_rpf (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (mfib_forward_rpf(vm, node, frame, 1)); +} + + +VLIB_REGISTER_NODE (ip4_mfib_forward_rpf_node, static) = { + .function = ip4_mfib_forward_rpf, + .name = "ip4-mfib-forward-rpf", + .vector_size = sizeof (u32), + + .format_trace = format_mfib_forward_rpf_trace, + + .n_next_nodes = MFIB_FORWARD_RPF_N_NEXT, + .next_nodes = { + [MFIB_FORWARD_RPF_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip4_mfib_forward_rpf_node, + ip4_mfib_forward_rpf) + +static uword +ip6_mfib_forward_rpf (vlib_main_t * vm, + vlib_node_runtime_t * node, + vlib_frame_t * frame) +{ + return (mfib_forward_rpf(vm, node, frame, 1)); +} + + +VLIB_REGISTER_NODE (ip6_mfib_forward_rpf_node, static) = { + .function = ip6_mfib_forward_rpf, + .name = "ip6-mfib-forward-rpf", + .vector_size = sizeof (u32), + + .format_trace = format_mfib_forward_rpf_trace, + + .n_next_nodes = MFIB_FORWARD_RPF_N_NEXT, + .next_nodes = { + [MFIB_FORWARD_RPF_NEXT_DROP] = "error-drop", + }, +}; + +VLIB_NODE_FUNCTION_MULTIARCH (ip6_mfib_forward_rpf_node, + ip6_mfib_forward_rpf) + diff --git a/src/vnet/mfib/mfib_itf.c b/src/vnet/mfib/mfib_itf.c new file mode 100644 index 00000000000..b9fa1ec6be7 --- /dev/null +++ b/src/vnet/mfib/mfib_itf.c @@ -0,0 +1,119 @@ +/* + * 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/vnet.h> + +#include <vnet/mfib/mfib_itf.h> +#include <vnet/mfib/mfib_signal.h> + +mfib_itf_t *mfib_itf_pool; + +index_t +mfib_itf_create (u32 sw_if_index, + mfib_itf_flags_t mfi_flags) +{ + mfib_itf_t *mfib_itf; + + pool_get_aligned(mfib_itf_pool, mfib_itf, + CLIB_CACHE_LINE_BYTES); + + mfib_itf->mfi_sw_if_index = sw_if_index; + mfib_itf->mfi_flags = mfi_flags; + mfib_itf->mfi_si = INDEX_INVALID; + + return (mfib_itf - mfib_itf_pool); +} + +void +mfib_itf_delete (mfib_itf_t *mfi) +{ + mfib_signal_remove_itf(mfi); + pool_put(mfib_itf_pool, mfi); +} + +u8 * +format_mfib_itf (u8 * s, va_list * args) +{ + mfib_itf_t *mfib_itf; + vnet_main_t *vnm; + index_t mfi; + + mfi = va_arg (*args, index_t); + + vnm = vnet_get_main(); + mfib_itf = mfib_itf_get(mfi); + + if (~0 != mfib_itf->mfi_sw_if_index) + { + return (format(s, " %U: %U", + format_vnet_sw_interface_name, + vnm, + vnet_get_sw_interface(vnm, + mfib_itf->mfi_sw_if_index), + format_mfib_itf_flags, mfib_itf->mfi_flags)); + } + else + { + return (format(s, " local: %U", + format_mfib_itf_flags, mfib_itf->mfi_flags)); + } + return (s); +} + +static clib_error_t * +show_mfib_itf_command (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + index_t mfii; + + if (unformat (input, "%d", &mfii)) + { + /* + * show one in detail + */ + if (!pool_is_free_index(mfib_itf_pool, mfii)) + { + vlib_cli_output (vm, "%d@%U", + mfii, + format_mfib_itf, mfii); + } + else + { + vlib_cli_output (vm, "itf %d invalid", mfii); + } + } + else + { + /* + * show all + */ + vlib_cli_output (vm, "mFIB interfaces::"); + pool_foreach_index(mfii, mfib_itf_pool, + ({ + vlib_cli_output (vm, "%d@%U", + mfii, + format_mfib_itf, mfii); + })); + } + + return (NULL); +} + +VLIB_CLI_COMMAND (show_mfib_itf, static) = { + .path = "show mfib interface", + .function = show_mfib_itf_command, + .short_help = "show mfib interface", +}; diff --git a/src/vnet/mfib/mfib_itf.h b/src/vnet/mfib/mfib_itf.h new file mode 100644 index 00000000000..5f26a476525 --- /dev/null +++ b/src/vnet/mfib/mfib_itf.h @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef __MFIB_ITF_H__ +#define __MFIB_ITF_H__ + +#include <vlib/vlib.h> +#include <vnet/mfib/mfib_types.h> + +/** + * @brief An interface associated with a particular MFIB entry + */ +typedef struct mfib_itf_t_ +{ + /** + * @brief Falags on the entry + */ + mfib_itf_flags_t mfi_flags; + + /** + * The SW IF index that this MFIB interface represents + */ + u32 mfi_sw_if_index; + + /** + * The index of the signal in the pending list + */ + u32 mfi_si; +} mfib_itf_t; + + +extern index_t mfib_itf_create(u32 sw_if_index, + mfib_itf_flags_t mfi_flags); +extern void mfib_itf_delete(mfib_itf_t *mfi); + +extern u8 *format_mfib_itf(u8 * s, va_list * args); + +extern mfib_itf_t *mfib_itf_pool; + +static inline mfib_itf_t * +mfib_itf_get (index_t mi) +{ + return (pool_elt_at_index(mfib_itf_pool, mi)); +} +static inline index_t +mfib_itf_get_index (const mfib_itf_t *mfi) +{ + return (mfi - mfib_itf_pool); +} + +#endif diff --git a/src/vnet/mfib/mfib_signal.c b/src/vnet/mfib/mfib_signal.c new file mode 100644 index 00000000000..9f6205de419 --- /dev/null +++ b/src/vnet/mfib/mfib_signal.c @@ -0,0 +1,201 @@ +/* + * 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/vnet.h> +#include <vnet/mfib/mfib_signal.h> +#include <vppinfra/dlist.h> + +/** + * @brief Pool of signals + */ +static mfib_signal_t *mfib_signal_pool; + +/** + * @brief pool of dlist elements + */ +static dlist_elt_t *mfib_signal_dlist_pool; + +/** + * the list/set of interfaces with signals pending + */ +typedef struct mfib_signal_q_t_ +{ + /** + * the dlist indext that is the head of the list + */ + u32 mip_head; + + /** + * Spin lock to protect the list + */ + int mip_lock; +} mfib_signal_q_t; + +/** + * @brief The pending queue of signals to deliver to the control plane + */ +static mfib_signal_q_t mfib_signal_pending ; + +static void +mfib_signal_list_init (void) +{ + dlist_elt_t *head; + u32 hi; + + pool_get(mfib_signal_dlist_pool, head); + hi = head - mfib_signal_dlist_pool; + + mfib_signal_pending.mip_head = hi; + clib_dlist_init(mfib_signal_dlist_pool, hi); +} + +void +mfib_signal_module_init (void) +{ + mfib_signal_list_init(); +} + +int +mfib_signal_send_one (struct _unix_shared_memory_queue *q, + u32 context) +{ + u32 li, si; + + /* + * with the lock held, pop a signal from the q. + */ + while (__sync_lock_test_and_set (&mfib_signal_pending.mip_lock, 1)) + ; + { + li = clib_dlist_remove_head(mfib_signal_dlist_pool, + mfib_signal_pending.mip_head); + } + mfib_signal_pending.mip_lock = 0; + + if (~0 != li) + { + mfib_signal_t *mfs; + mfib_itf_t *mfi; + dlist_elt_t *elt; + + elt = pool_elt_at_index(mfib_signal_dlist_pool, li); + si = elt->value; + + mfs = pool_elt_at_index(mfib_signal_pool, si); + mfi = mfib_itf_get(mfs->mfs_itf); + mfi->mfi_si = INDEX_INVALID; + __sync_fetch_and_and(&mfi->mfi_flags, + ~MFIB_ITF_FLAG_SIGNAL_PRESENT); + + + vl_mfib_signal_send_one(q, context, mfs); + + /* + * with the lock held, return the resoruces of the signals posted + */ + while (__sync_lock_test_and_set(&mfib_signal_pending.mip_lock, 1)) + ; + { + pool_put_index(mfib_signal_pool, si); + pool_put_index(mfib_signal_dlist_pool, li); + } + mfib_signal_pending.mip_lock = 0; + + return (1); + } + return (0); +} + +void +mfib_signal_push (const mfib_entry_t *mfe, + mfib_itf_t *mfi, + vlib_buffer_t *b0) +{ + mfib_signal_t *mfs; + dlist_elt_t *elt; + u32 si, li; + + while (__sync_lock_test_and_set (&mfib_signal_pending.mip_lock, 1)) + ; + { + pool_get(mfib_signal_pool, mfs); + pool_get(mfib_signal_dlist_pool, elt); + + si = mfs - mfib_signal_pool; + li = elt - mfib_signal_dlist_pool; + + elt->value = si; + mfi->mfi_si = li; + + clib_dlist_addhead(mfib_signal_dlist_pool, + mfib_signal_pending.mip_head, + li); + } + mfib_signal_pending.mip_lock = 0; + + mfs->mfs_entry = mfib_entry_get_index(mfe); + mfs->mfs_itf = mfib_itf_get_index(mfi); + + if (NULL != b0) + { + mfs->mfs_buffer_len = b0->current_length; + memcpy(mfs->mfs_buffer, + vlib_buffer_get_current(b0), + (mfs->mfs_buffer_len > MFIB_SIGNAL_BUFFER_SIZE ? + MFIB_SIGNAL_BUFFER_SIZE : + mfs->mfs_buffer_len)); + } + else + { + mfs->mfs_buffer_len = 0; + } +} + +void +mfib_signal_remove_itf (const mfib_itf_t *mfi) +{ + u32 li; + + /* + * lock the queue to prevent further additions while we fiddle. + */ + li = mfi->mfi_si; + + if (INDEX_INVALID != li) + { + /* + * it's in the pending q + */ + while (__sync_lock_test_and_set (&mfib_signal_pending.mip_lock, 1)) + ; + { + dlist_elt_t *elt; + + /* + * with the lock held; + * - remove the signal from the pending list + * - free up the signal and list entry obejcts + */ + clib_dlist_remove(mfib_signal_dlist_pool, li); + + elt = pool_elt_at_index(mfib_signal_dlist_pool, li); + pool_put_index(mfib_signal_pool, elt->value); + pool_put(mfib_signal_dlist_pool, elt); + } + + mfib_signal_pending.mip_lock = 0; + } +} diff --git a/src/vnet/mfib/mfib_signal.h b/src/vnet/mfib/mfib_signal.h new file mode 100644 index 00000000000..732d8aff3e9 --- /dev/null +++ b/src/vnet/mfib/mfib_signal.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef __MFIB_SIGNAL_H__ +#define __MFIB_SIGNAL_H__ + +#include <vlib/vlib.h> +#include <vnet/mfib/mfib_types.h> +#include <vnet/mfib/mfib_itf.h> +#include <vnet/mfib/mfib_entry.h> + +#define MFIB_SIGNAL_BUFFER_SIZE 255 + +/** + * A pair of indicies, for the entry and interface resp. + */ +typedef struct mfib_signal_t_ +{ + fib_node_index_t mfs_entry; + index_t mfs_itf; + + /** + * @brief A buffer copied from the DP plane that triggered the signal + */ + u8 mfs_buffer[MFIB_SIGNAL_BUFFER_SIZE]; + + u8 mfs_buffer_len; +} mfib_signal_t; + + +extern void mfib_signal_push(const mfib_entry_t *mfe, + mfib_itf_t *mfi, + vlib_buffer_t *b0); +extern void mfib_signal_remove_itf(const mfib_itf_t *mfi); + +extern void mfib_signal_module_init(void); + +struct _unix_shared_memory_queue; + +extern void vl_mfib_signal_send_one(struct _unix_shared_memory_queue *q, + u32 context, + const mfib_signal_t *mfs); +extern int mfib_signal_send_one(struct _unix_shared_memory_queue *q, + u32 context); + +#endif + diff --git a/src/vnet/mfib/mfib_table.c b/src/vnet/mfib/mfib_table.c new file mode 100644 index 00000000000..e4c0936d6c9 --- /dev/null +++ b/src/vnet/mfib/mfib_table.c @@ -0,0 +1,489 @@ +/* + * 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 <vlib/vlib.h> +#include <vnet/dpo/drop_dpo.h> + +#include <vnet/mfib/mfib_table.h> +#include <vnet/mfib/ip4_mfib.h> +#include <vnet/mfib/ip6_mfib.h> +#include <vnet/mfib/mfib_entry.h> +#include <vnet/mfib/mfib_signal.h> + +mfib_table_t * +mfib_table_get (fib_node_index_t index, + fib_protocol_t proto) +{ + switch (proto) + { + case FIB_PROTOCOL_IP4: + return (pool_elt_at_index(ip4_main.mfibs, index)); + case FIB_PROTOCOL_IP6: + return (pool_elt_at_index(ip6_main.mfibs, index)); + case FIB_PROTOCOL_MPLS: + break; + } + ASSERT(0); + return (NULL); +} + +static inline fib_node_index_t +mfib_table_lookup_i (const mfib_table_t *mfib_table, + const mfib_prefix_t *prefix) +{ + switch (prefix->fp_proto) + { + case FIB_PROTOCOL_IP4: + return (ip4_mfib_table_lookup(&mfib_table->v4, + &prefix->fp_src_addr.ip4, + &prefix->fp_grp_addr.ip4, + prefix->fp_len)); + case FIB_PROTOCOL_IP6: + return (ip6_mfib_table_lookup(&mfib_table->v6, + &prefix->fp_src_addr.ip6, + &prefix->fp_grp_addr.ip6, + prefix->fp_len)); + case FIB_PROTOCOL_MPLS: + break; + } + return (FIB_NODE_INDEX_INVALID); +} + +fib_node_index_t +mfib_table_lookup (u32 fib_index, + const mfib_prefix_t *prefix) +{ + return (mfib_table_lookup_i(mfib_table_get(fib_index, prefix->fp_proto), prefix)); +} + +static inline fib_node_index_t +mfib_table_lookup_exact_match_i (const mfib_table_t *mfib_table, + const mfib_prefix_t *prefix) +{ + switch (prefix->fp_proto) + { + case FIB_PROTOCOL_IP4: + return (ip4_mfib_table_lookup_exact_match(&mfib_table->v4, + &prefix->fp_grp_addr.ip4, + &prefix->fp_src_addr.ip4, + prefix->fp_len)); + case FIB_PROTOCOL_IP6: + return (ip6_mfib_table_lookup_exact_match(&mfib_table->v6, + &prefix->fp_grp_addr.ip6, + &prefix->fp_src_addr.ip6, + prefix->fp_len)); + case FIB_PROTOCOL_MPLS: + break; + } + return (FIB_NODE_INDEX_INVALID); +} + +fib_node_index_t +mfib_table_lookup_exact_match (u32 fib_index, + const mfib_prefix_t *prefix) +{ + return (mfib_table_lookup_exact_match_i(mfib_table_get(fib_index, + prefix->fp_proto), + prefix)); +} + +static void +mfib_table_entry_remove (mfib_table_t *mfib_table, + const mfib_prefix_t *prefix, + fib_node_index_t fib_entry_index) +{ + vlib_smp_unsafe_warning(); + + mfib_table->mft_total_route_counts--; + + switch (prefix->fp_proto) + { + case FIB_PROTOCOL_IP4: + ip4_mfib_table_entry_remove(&mfib_table->v4, + &prefix->fp_grp_addr.ip4, + &prefix->fp_src_addr.ip4, + prefix->fp_len); + break; + case FIB_PROTOCOL_IP6: + ip6_mfib_table_entry_remove(&mfib_table->v6, + &prefix->fp_grp_addr.ip6, + &prefix->fp_src_addr.ip6, + prefix->fp_len); + break; + case FIB_PROTOCOL_MPLS: + ASSERT(0); + break; + } + + mfib_entry_unlock(fib_entry_index); +} + +static void +mfib_table_entry_insert (mfib_table_t *mfib_table, + const mfib_prefix_t *prefix, + fib_node_index_t mfib_entry_index) +{ + vlib_smp_unsafe_warning(); + + mfib_entry_lock(mfib_entry_index); + mfib_table->mft_total_route_counts++; + + switch (prefix->fp_proto) + { + case FIB_PROTOCOL_IP4: + ip4_mfib_table_entry_insert(&mfib_table->v4, + &prefix->fp_grp_addr.ip4, + &prefix->fp_src_addr.ip4, + prefix->fp_len, + mfib_entry_index); + break; + case FIB_PROTOCOL_IP6: + ip6_mfib_table_entry_insert(&mfib_table->v6, + &prefix->fp_grp_addr.ip6, + &prefix->fp_src_addr.ip6, + prefix->fp_len, + mfib_entry_index); + break; + case FIB_PROTOCOL_MPLS: + break; + } +} + +fib_node_index_t +mfib_table_entry_update (u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + mfib_entry_flags_t entry_flags) +{ + fib_node_index_t mfib_entry_index; + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, prefix->fp_proto); + mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix); + + if (FIB_NODE_INDEX_INVALID == mfib_entry_index) + { + if (MFIB_ENTRY_FLAG_NONE != entry_flags) + { + /* + * update to a non-existing entry with non-zero flags + */ + mfib_entry_index = mfib_entry_create(fib_index, source, + prefix, entry_flags); + + mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index); + } + /* + * else + * the entry doesn't exist and the request is to set no flags + * the result would be an entry that doesn't exist - so do nothing + */ + } + else + { + mfib_entry_lock(mfib_entry_index); + + if (mfib_entry_update(mfib_entry_index, source, entry_flags)) + { + /* + * this update means we can now remove the entry. + */ + mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index); + } + + mfib_entry_unlock(mfib_entry_index); + } + + return (mfib_entry_index); +} + +fib_node_index_t +mfib_table_entry_path_update (u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + const fib_route_path_t *rpath, + mfib_itf_flags_t itf_flags) +{ + fib_node_index_t mfib_entry_index; + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, prefix->fp_proto); + mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix); + + if (FIB_NODE_INDEX_INVALID == mfib_entry_index) + { + mfib_entry_index = mfib_entry_create(fib_index, + source, + prefix, + MFIB_ENTRY_FLAG_NONE); + + mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index); + } + + mfib_entry_path_update(mfib_entry_index, + source, + rpath, + itf_flags); + + return (mfib_entry_index); +} + +void +mfib_table_entry_path_remove (u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + const fib_route_path_t *rpath) +{ + fib_node_index_t mfib_entry_index; + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, prefix->fp_proto); + mfib_entry_index = mfib_table_lookup_exact_match_i(mfib_table, prefix); + + if (FIB_NODE_INDEX_INVALID == mfib_entry_index) + { + /* + * removing an etry that does not exist. i'll allow it. + */ + } + else + { + int no_more_sources; + + /* + * don't nobody go nowhere + */ + mfib_entry_lock(mfib_entry_index); + + no_more_sources = mfib_entry_path_remove(mfib_entry_index, + source, + rpath); + + if (no_more_sources) + { + /* + * last source gone. remove from the table + */ + mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index); + } + + mfib_entry_unlock(mfib_entry_index); + } +} + +static void +mfib_table_entry_delete_i (u32 fib_index, + fib_node_index_t mfib_entry_index, + const mfib_prefix_t *prefix, + mfib_source_t source) +{ + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, prefix->fp_proto); + + /* + * don't nobody go nowhere + */ + mfib_entry_lock(mfib_entry_index); + + if (mfib_entry_delete(mfib_entry_index, source)) + { + /* + * last source gone. remove from the table + */ + mfib_table_entry_remove(mfib_table, prefix, mfib_entry_index); + } + /* + * else + * still has sources, leave it be. + */ + + mfib_entry_unlock(mfib_entry_index); +} + +void +mfib_table_entry_delete (u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source) +{ + fib_node_index_t mfib_entry_index; + + mfib_entry_index = mfib_table_lookup_exact_match(fib_index, prefix); + + if (FIB_NODE_INDEX_INVALID == mfib_entry_index) + { + /* + * removing an etry that does not exist. + * i'll allow it, but i won't like it. + */ + clib_warning("%U not in FIB", format_mfib_prefix, prefix); + } + else + { + mfib_table_entry_delete_i(fib_index, mfib_entry_index, + prefix, source); + } +} + +void +mfib_table_entry_delete_index (fib_node_index_t mfib_entry_index, + mfib_source_t source) +{ + mfib_prefix_t prefix; + + mfib_entry_get_prefix(mfib_entry_index, &prefix); + + mfib_table_entry_delete_i(mfib_entry_get_fib_index(mfib_entry_index), + mfib_entry_index, &prefix, source); +} + +u32 +mfib_table_get_index_for_sw_if_index (fib_protocol_t proto, + u32 sw_if_index) +{ + switch (proto) + { + case FIB_PROTOCOL_IP4: + return (ip4_mfib_table_get_index_for_sw_if_index(sw_if_index)); + case FIB_PROTOCOL_IP6: + return (ip6_mfib_table_get_index_for_sw_if_index(sw_if_index)); + case FIB_PROTOCOL_MPLS: + ASSERT(0); + break; + } + return (~0); +} + +u32 +mfib_table_find (fib_protocol_t proto, + u32 table_id) +{ + switch (proto) + { + case FIB_PROTOCOL_IP4: + return (ip4_mfib_index_from_table_id(table_id)); + case FIB_PROTOCOL_IP6: + return (ip6_mfib_index_from_table_id(table_id)); + case FIB_PROTOCOL_MPLS: + ASSERT(0); + break; + } + return (~0); +} + +u32 +mfib_table_find_or_create_and_lock (fib_protocol_t proto, + u32 table_id) +{ + mfib_table_t *mfib_table; + fib_node_index_t fi; + + switch (proto) + { + case FIB_PROTOCOL_IP4: + fi = ip4_mfib_table_find_or_create_and_lock(table_id); + break; + case FIB_PROTOCOL_IP6: + fi = ip6_mfib_table_find_or_create_and_lock(table_id); + break; + case FIB_PROTOCOL_MPLS: + default: + return (~0); + } + + mfib_table = mfib_table_get(fi, proto); + + mfib_table->mft_desc = format(NULL, "%U-VRF:%d", + format_fib_protocol, proto, + table_id); + + return (fi); +} + +static void +mfib_table_destroy (mfib_table_t *mfib_table) +{ + vec_free(mfib_table->mft_desc); + + switch (mfib_table->mft_proto) + { + case FIB_PROTOCOL_IP4: + ip4_mfib_table_destroy(&mfib_table->v4); + break; + case FIB_PROTOCOL_IP6: + ip6_mfib_table_destroy(&mfib_table->v6); + break; + case FIB_PROTOCOL_MPLS: + ASSERT(0); + break; + } +} + +void +mfib_table_unlock (u32 fib_index, + fib_protocol_t proto) +{ + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, proto); + mfib_table->mft_locks--; + + if (0 == mfib_table->mft_locks) + { + mfib_table_destroy(mfib_table); + } +} + +void +mfib_table_lock (u32 fib_index, + fib_protocol_t proto) +{ + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, proto); + mfib_table->mft_locks++; +} + +u8* +format_mfib_table_name (u8* s, va_list ap) +{ + fib_node_index_t fib_index = va_arg(ap, fib_node_index_t); + fib_protocol_t proto = va_arg(ap, int); // int promotion + mfib_table_t *mfib_table; + + mfib_table = mfib_table_get(fib_index, proto); + + s = format(s, "%v", mfib_table->mft_desc); + + return (s); +} + +static clib_error_t * +mfib_module_init (vlib_main_t * vm) +{ + clib_error_t * error; + + if ((error = vlib_call_init_function (vm, fib_module_init))) + return (error); + if ((error = vlib_call_init_function (vm, rn_module_init))) + return (error); + + mfib_entry_module_init(); + mfib_signal_module_init(); + + return (error); +} + +VLIB_INIT_FUNCTION(mfib_module_init); diff --git a/src/vnet/mfib/mfib_table.h b/src/vnet/mfib/mfib_table.h new file mode 100644 index 00000000000..4faa69ee999 --- /dev/null +++ b/src/vnet/mfib/mfib_table.h @@ -0,0 +1,331 @@ +/* + * 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. + */ + +#ifndef __MFIB_TABLE_H__ +#define __MFIB_TABLE_H__ + +#include <vnet/ip/ip.h> +#include <vnet/adj/adj.h> + +#include <vnet/mfib/mfib_types.h> + +/** + * @brief + * A protocol Independent IP multicast FIB table + */ +typedef struct mfib_table_t_ +{ + /** + * A union of the protocol specific FIBs that provide the + * underlying LPM mechanism. + * This element is first in the struct so that it is in the + * first cache line. + */ + union { + ip4_mfib_t v4; + ip6_mfib_t v6; + }; + + /** + * Which protocol this table serves. Used to switch on the union above. + */ + fib_protocol_t mft_proto; + + /** + * number of locks on the table + */ + u16 mft_locks; + + /** + * Table ID (hash key) for this FIB. + */ + u32 mft_table_id; + + /** + * Index into FIB vector. + */ + fib_node_index_t mft_index; + + /** + * Total route counters + */ + u32 mft_total_route_counts; + + /** + * Table description + */ + u8* mft_desc; +} mfib_table_t; + +/** + * @brief + * Format the description/name of the table + */ +extern u8* format_mfib_table_name(u8* s, va_list ap); + +/** + * @brief + * Perfom a longest prefix match in the non-forwarding table + * + * @param fib_index + * The index of the FIB + * + * @param prefix + * The prefix to lookup + * + * @return + * The index of the fib_entry_t for the best match, which may be the default route + */ +extern fib_node_index_t mfib_table_lookup(u32 fib_index, + const mfib_prefix_t *prefix); + +/** + * @brief + * Perfom an exact match in the non-forwarding table + * + * @param fib_index + * The index of the FIB + * + * @param prefix + * The prefix to lookup + * + * @return + * The index of the fib_entry_t for the exact match, or INVALID + * is there is no match. + */ +extern fib_node_index_t mfib_table_lookup_exact_match(u32 fib_index, + const mfib_prefix_t *prefix); + +/** + * @brief + * Add a new (with no replication) or lock an existing entry + * + * @param prefix + * The prefix for the entry to add + * + * @return + * the index of the fib_entry_t that is created (or existed already). + */ +extern fib_node_index_t mfib_table_entry_update(u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + mfib_entry_flags_t flags); + +/** + * @brief + * Add n paths to an entry (aka route) in the FIB. If the entry does not + * exist, it will be created. + * See the documentation for fib_route_path_t for more descirptions of + * the path parameters. + * + * @param fib_index + * The index of the FIB + * + * @param prefix + * The prefix for the entry to add + * + * @param source + * The ID of the client/source adding the entry. + * + * @param flags + * Flags for the entry. + * + * @param rpaths + * A vector of paths. + * + * @return + * the index of the fib_entry_t that is created (or existed already). + */ +extern fib_node_index_t mfib_table_entry_path_update(u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + const fib_route_path_t *rpath, + mfib_itf_flags_t flags); + +/** + * @brief + * Remove n paths to an entry (aka route) in the FIB. If this is the entry's + * last path, then the entry will be removed, unless it has other sources. + * See the documentation for fib_route_path_t for more descirptions of + * the path parameters. + * + * @param fib_index + * The index of the FIB + * + * @param prefix + * The prefix for the entry to add + * + * @param source + * The ID of the client/source adding the entry. + * + * @param rpaths + * A vector of paths. + */ +extern void mfib_table_entry_path_remove(u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source, + const fib_route_path_t *paths); + + + +/** + * @brief + * Delete a FIB entry. If the entry has no more sources, then it is + * removed from the table. + * + * @param fib_index + * The index of the FIB + * + * @param prefix + * The prefix for the entry to remove + * + * @param source + * The ID of the client/source adding the entry. + */ +extern void mfib_table_entry_delete(u32 fib_index, + const mfib_prefix_t *prefix, + mfib_source_t source); + +/** + * @brief + * Delete a FIB entry. If the entry has no more sources, then it is + * removed from the table. + * + * @param entry_index + * The index of the FIB entry + * + * @param source + * The ID of the client/source adding the entry. + */ +extern void mfib_table_entry_delete_index(fib_node_index_t entry_index, + mfib_source_t source); + +/** + * @brief + * Flush all entries from a table for the source + * + * @param fib_index + * The index of the FIB + * + * @paran proto + * The protocol of the entries in the table + * + * @param source + * the source to flush + */ +extern void mfib_table_flush(u32 fib_index, + fib_protocol_t proto); + +/** + * @brief + * Get the index of the FIB bound to the interface + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + * + * @param sw_if_index + * The interface index + * + * @return fib_index + * The index of the FIB + */ +extern u32 mfib_table_get_index_for_sw_if_index(fib_protocol_t proto, + u32 sw_if_index); + +/** + * @brief + * Get the index of the FIB for a Table-ID. This DOES NOT create the + * FIB if it does not exist. + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + * + * @param table-id + * The Table-ID + * + * @return fib_index + * The index of the FIB, which may be INVALID. + */ +extern u32 mfib_table_find(fib_protocol_t proto, u32 table_id); + + +/** + * @brief + * Get the index of the FIB for a Table-ID. This DOES create the + * FIB if it does not exist. + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + * + * @param table-id + * The Table-ID + * + * @return fib_index + * The index of the FIB + */ +extern u32 mfib_table_find_or_create_and_lock(fib_protocol_t proto, + u32 table_id); + + +/** + * @brief + * Take a reference counting lock on the table + * + * @param fib_index + * The index of the FIB + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + */ +extern void mfib_table_unlock(u32 fib_index, + fib_protocol_t proto); + +/** + * @brief + * Release a reference counting lock on the table. When the last lock + * has gone. the FIB is deleted. + * + * @param fib_index + * The index of the FIB + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + */ +extern void mfib_table_lock(u32 fib_index, + fib_protocol_t proto); + +/** + * @brief + * Return the number of entries in the FIB added by a given source. + * + * @param fib_index + * The index of the FIB + * + * @paran proto + * The protocol of the FIB (and thus the entries therein) + * + * @return number of sourced entries. + */ +extern u32 mfib_table_get_num_entries(u32 fib_index, + fib_protocol_t proto); + +/** + * @brief + * Get a pointer to a FIB table + */ +extern mfib_table_t *mfib_table_get(fib_node_index_t index, + fib_protocol_t proto); + +#endif 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); diff --git a/src/vnet/mfib/mfib_types.c b/src/vnet/mfib/mfib_types.c new file mode 100644 index 00000000000..6d77c3d88d1 --- /dev/null +++ b/src/vnet/mfib/mfib_types.c @@ -0,0 +1,213 @@ + /* + * 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/mfib/mfib_types.h> + +#include <vnet/ip/ip.h> + +/** + * String names for each flag + */ +static const char *mfib_flag_names[] = MFIB_ENTRY_NAMES_SHORT; +static const char *mfib_flag_names_long[] = MFIB_ENTRY_NAMES_LONG; + +static const char *mfib_itf_flag_long_names[] = MFIB_ITF_NAMES_LONG; +static const char *mfib_itf_flag_names[] = MFIB_ITF_NAMES_SHORT; + +u8 * +format_mfib_prefix (u8 * s, va_list * args) +{ + mfib_prefix_t *fp = va_arg (*args, mfib_prefix_t *); + + /* + * protocol specific so it prints ::/0 correctly. + */ + switch (fp->fp_proto) + { + case FIB_PROTOCOL_IP6: + { + ip6_address_t p6 = fp->fp_grp_addr.ip6; + u32 len = (fp->fp_len > 128 ? 128 : fp->fp_len); + + ip6_address_mask(&p6, &(ip6_main.fib_masks[len])); + + if (ip6_address_is_zero(&fp->fp_src_addr.ip6)) + { + s = format(s, "(*, "); + } + else + { + s = format (s, "(%U, ", format_ip6_address, &fp->fp_src_addr.ip6); + } + s = format (s, "%U", format_ip6_address, &p6); + s = format (s, "/%d)", len); + break; + } + case FIB_PROTOCOL_IP4: + { + ip4_address_t p4 = fp->fp_grp_addr.ip4; + u32 len = (fp->fp_len > 32 ? 32 : fp->fp_len); + + p4.as_u32 &= ip4_main.fib_masks[len]; + + if (0 == fp->fp_src_addr.ip4.as_u32) + { + s = format(s, "(*, "); + } + else + { + s = format (s, "(%U, ", format_ip4_address, &fp->fp_src_addr.ip4); + } + s = format (s, "%U", format_ip4_address, &p4); + s = format (s, "/%d)", len); + break; + } + case FIB_PROTOCOL_MPLS: + break; + } + + return (s); +} + +u8 * +format_mfib_entry_flags (u8 * s, va_list * args) +{ + mfib_entry_attribute_t attr; + mfib_entry_flags_t flags; + + flags = va_arg (*args, mfib_entry_flags_t); + + if (MFIB_ENTRY_FLAG_NONE != flags) { + s = format(s, " flags:"); + FOR_EACH_MFIB_ATTRIBUTE(attr) { + if ((1<<attr) & flags) { + s = format (s, "%s,", mfib_flag_names[attr]); + } + } + } + + return (s); +} + +u8 * +format_mfib_itf_flags (u8 * s, va_list * args) +{ + mfib_itf_attribute_t attr; + mfib_itf_flags_t flags; + + flags = va_arg (*args, mfib_itf_flags_t); + + FOR_EACH_MFIB_ITF_ATTRIBUTE(attr) { + if ((1<<attr) & flags) { + s = format (s, "%s,", mfib_itf_flag_long_names[attr]); + } + } + + return (s); +} + +uword +unformat_mfib_itf_flags (unformat_input_t * input, + va_list * args) +{ + mfib_itf_flags_t old, *iflags = va_arg (*args, mfib_itf_flags_t*); + mfib_itf_attribute_t attr; + + old = *iflags; + FOR_EACH_MFIB_ITF_ATTRIBUTE(attr) { + if (unformat (input, mfib_itf_flag_long_names[attr])) + *iflags |= (1 << attr); + } + FOR_EACH_MFIB_ITF_ATTRIBUTE(attr) { + if (unformat (input, mfib_itf_flag_names[attr])) + *iflags |= (1 << attr); + } + + return (old == *iflags ? 0 : 1); +} + +uword +unformat_mfib_entry_flags (unformat_input_t * input, + va_list * args) +{ + mfib_entry_flags_t old, *eflags = va_arg (*args, mfib_entry_flags_t*); + mfib_entry_attribute_t attr; + + old = *eflags; + FOR_EACH_MFIB_ATTRIBUTE(attr) { + if (unformat (input, mfib_flag_names[attr])) + *eflags |= (1 << attr); + } + + return (old == *eflags ? 0 : 1); +} + +clib_error_t * +mfib_show_route_flags (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + mfib_entry_attribute_t attr; + + FOR_EACH_MFIB_ATTRIBUTE(attr) { + vlib_cli_output(vm, "%s = %s", + mfib_flag_names[attr], + mfib_flag_names_long[attr]); + } + + return (NULL); +} + +/*? + * This command display the set of support flags applicable to the MFIB route + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (mfib_route_flags_command, static) = +{ + .path = "sh mfib route flags", + .short_help = "Flags applicable to an MFIB route", + .function = mfib_show_route_flags, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ + +clib_error_t * +mfib_show_itf_flags (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + mfib_itf_attribute_t attr; + + FOR_EACH_MFIB_ITF_ATTRIBUTE(attr) { + vlib_cli_output(vm, "%s = %s", + mfib_itf_flag_names[attr], + mfib_itf_flag_long_names[attr]); + } + + return (NULL); +} + +/*? + * This command display the set of support flags applicable to the MFIB route + */ +/* *INDENT-OFF* */ +VLIB_CLI_COMMAND (mfib_itf_flags_command, static) = +{ + .path = "sh mfib itf flags", + .short_help = "Flags applicable to an MFIB interfaces", + .function = mfib_show_itf_flags, + .is_mp_safe = 1, +}; +/* *INDENT-ON* */ diff --git a/src/vnet/mfib/mfib_types.h b/src/vnet/mfib/mfib_types.h new file mode 100644 index 00000000000..37898a07b00 --- /dev/null +++ b/src/vnet/mfib/mfib_types.h @@ -0,0 +1,185 @@ + /* + * 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. + */ + +#ifndef __MFIB_TYPES_H__ +#define __MFIB_TYPES_H__ + +#include <vnet/fib/fib_types.h> + +/** + * Aggregrate type for a prefix + */ +typedef struct mfib_prefix_t_ { + /** + * The mask length + */ + u16 fp_len; + + /** + * protocol type + */ + fib_protocol_t fp_proto; + + /** + * Pad to keep the address 4 byte aligned + */ + u8 ___fp___pad; + + /** + * The address type is not deriveable from the fp_addr member. + * If it's v4, then the first 3 u32s of the address will be 0. + * v6 addresses (even v4 mapped ones) have at least 2 u32s assigned + * to non-zero values. true. but when it's all zero, one cannot decide. + */ + ip46_address_t fp_grp_addr; + ip46_address_t fp_src_addr; +} mfib_prefix_t; + +typedef enum mfib_entry_attribute_t_ +{ + MFIB_ENTRY_ATTRIBUTE_FIRST = 0, + /** + * The control planes needs packets mathing this entry to generate + * a signal. + */ + MFIB_ENTRY_SIGNAL = MFIB_ENTRY_ATTRIBUTE_FIRST, + /** + * Drop all traffic to this route + */ + MFIB_ENTRY_DROP, + /** + * The control plane needs to be informed of coneected sources + */ + MFIB_ENTRY_CONNECTED, + /** + * Accept packets from any incpoming interface + * Use with extreme caution + */ + MFIB_ENTRY_ACCEPT_ALL_ITF, + MFIB_ENTRY_INHERIT_ACCEPT, + MFIB_ENTRY_ATTRIBUTE_LAST = MFIB_ENTRY_INHERIT_ACCEPT, +} mfib_entry_attribute_t; + +#define FOR_EACH_MFIB_ATTRIBUTE(_item) \ + for (_item = MFIB_ENTRY_ATTRIBUTE_FIRST; \ + _item <= MFIB_ENTRY_ATTRIBUTE_LAST; \ + _item++) + +#define MFIB_ENTRY_NAMES_SHORT { \ + [MFIB_ENTRY_SIGNAL] = "S", \ + [MFIB_ENTRY_CONNECTED] = "C", \ + [MFIB_ENTRY_DROP] = "D", \ + [MFIB_ENTRY_ACCEPT_ALL_ITF] = "AA", \ + [MFIB_ENTRY_INHERIT_ACCEPT] = "IA", \ +} + +#define MFIB_ENTRY_NAMES_LONG { \ + [MFIB_ENTRY_SIGNAL] = "Signal", \ + [MFIB_ENTRY_CONNECTED] = "Connected", \ + [MFIB_ENTRY_DROP] = "Drop", \ + [MFIB_ENTRY_ACCEPT_ALL_ITF] = "Accept-all-itf", \ + [MFIB_ENTRY_INHERIT_ACCEPT] = "Inherit-Accept", \ +} + +typedef enum mfib_entry_flags_t_ +{ + MFIB_ENTRY_FLAG_NONE, + MFIB_ENTRY_FLAG_SIGNAL = (1 << MFIB_ENTRY_SIGNAL), + MFIB_ENTRY_FLAG_DROP = (1 << MFIB_ENTRY_DROP), + MFIB_ENTRY_FLAG_CONNECTED = (1 << MFIB_ENTRY_CONNECTED), + MFIB_ENTRY_FLAG_INHERIT_ACCEPT = (1 << MFIB_ENTRY_INHERIT_ACCEPT), + MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF = (1 << MFIB_ENTRY_ACCEPT_ALL_ITF), +} mfib_entry_flags_t; + +typedef enum mfib_itf_attribute_t_ +{ + MFIB_ITF_ATTRIBUTE_FIRST, + MFIB_ITF_NEGATE_SIGNAL = MFIB_ITF_ATTRIBUTE_FIRST, + MFIB_ITF_ACCEPT, + MFIB_ITF_FORWARD, + MFIB_ITF_SIGNAL_PRESENT, + MFIB_ITF_DONT_PRESERVE, + MFIB_ITF_ATTRIBUTE_LAST = MFIB_ITF_DONT_PRESERVE, +} mfib_itf_attribute_t; + +#define FOR_EACH_MFIB_ITF_ATTRIBUTE(_item) \ + for (_item = MFIB_ITF_ATTRIBUTE_FIRST; \ + _item <= MFIB_ITF_ATTRIBUTE_LAST; \ + _item++) + +#define MFIB_ITF_NAMES_SHORT { \ + [MFIB_ITF_NEGATE_SIGNAL] = "NS", \ + [MFIB_ITF_ACCEPT] = "A", \ + [MFIB_ITF_FORWARD] = "F", \ + [MFIB_ITF_SIGNAL_PRESENT] = "SP", \ + [MFIB_ITF_DONT_PRESERVE] = "DP", \ +} + +#define MFIB_ITF_NAMES_LONG { \ + [MFIB_ITF_NEGATE_SIGNAL] = "Negate-Signal", \ + [MFIB_ITF_ACCEPT] = "Accept", \ + [MFIB_ITF_FORWARD] = "Forward", \ + [MFIB_ITF_SIGNAL_PRESENT] = "Signal-Present", \ + [MFIB_ITF_DONT_PRESERVE] = "Don't-Preserve", \ +} + +typedef enum mfib_itf_flags_t_ +{ + MFIB_ITF_FLAG_NONE, + MFIB_ITF_FLAG_NEGATE_SIGNAL = (1 << MFIB_ITF_NEGATE_SIGNAL), + MFIB_ITF_FLAG_ACCEPT = (1 << MFIB_ITF_ACCEPT), + MFIB_ITF_FLAG_FORWARD = (1 << MFIB_ITF_FORWARD), + MFIB_ITF_FLAG_SIGNAL_PRESENT = (1 << MFIB_ITF_SIGNAL_PRESENT), + MFIB_ITF_FLAG_DONT_PRESERVE = (1 << MFIB_ITF_DONT_PRESERVE), +} mfib_itf_flags_t; + +/** + * Possible [control plane] sources of MFIB entries + */ +typedef enum mfib_source_t_ +{ + MFIB_SOURCE_SPECIAL, + MFIB_SOURCE_API, + MFIB_SOURCE_CLI, + MFIB_SOURCE_VXLAN, + MFIB_SOURCE_DHCP, + MFIB_SOURCE_DEFAULT_ROUTE, +} mfib_source_t; + +#define MFIB_SOURCE_NAMES { \ + [MFIB_SOURCE_SPECIAL] = "Special", \ + [MFIB_SOURCE_API] = "API", \ + [MFIB_SOURCE_CLI] = "CLI", \ + [MFIB_SOURCE_DHCP] = "DHCP", \ + [MFIB_SOURCE_VXLAN] = "VXLAN", \ + [MFIB_SOURCE_DEFAULT_ROUTE] = "Default Route", \ +} + +/** + * \brief Compare two prefixes for equality + */ +extern int mfib_prefix_cmp(const mfib_prefix_t *p1, + const mfib_prefix_t *p2); + +extern u8 * format_mfib_prefix(u8 * s, va_list * args); + +extern u8 *format_mfib_entry_flags(u8 * s, va_list * args); +extern u8 *format_mfib_itf_flags(u8 * s, va_list * args); +extern uword unformat_mfib_itf_flags(unformat_input_t * input, + va_list * args); +extern uword unformat_mfib_entry_flags(unformat_input_t * input, + va_list * args); + +#endif |