aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/mfib
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/mfib')
-rw-r--r--src/vnet/mfib/ip4_mfib.c494
-rw-r--r--src/vnet/mfib/ip4_mfib.h105
-rw-r--r--src/vnet/mfib/ip6_mfib.c699
-rw-r--r--src/vnet/mfib/ip6_mfib.h121
-rw-r--r--src/vnet/mfib/mfib_entry.c1313
-rw-r--r--src/vnet/mfib/mfib_entry.h184
-rw-r--r--src/vnet/mfib/mfib_forward.c525
-rw-r--r--src/vnet/mfib/mfib_itf.c123
-rw-r--r--src/vnet/mfib/mfib_itf.h63
-rw-r--r--src/vnet/mfib/mfib_signal.c214
-rw-r--r--src/vnet/mfib/mfib_signal.h59
-rw-r--r--src/vnet/mfib/mfib_table.c647
-rw-r--r--src/vnet/mfib/mfib_table.h424
-rw-r--r--src/vnet/mfib/mfib_test.c1405
-rw-r--r--src/vnet/mfib/mfib_types.c213
-rw-r--r--src/vnet/mfib/mfib_types.h205
16 files changed, 6794 insertions, 0 deletions
diff --git a/src/vnet/mfib/ip4_mfib.c b/src/vnet/mfib/ip4_mfib.c
new file mode 100644
index 00000000..b2482580
--- /dev/null
+++ b/src/vnet/mfib/ip4_mfib.c
@@ -0,0 +1,494 @@
+/*
+ * 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_source_t src)
+{
+ 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, src);
+
+ /*
+ * 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_RPF_ID_NONE,
+ 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,
+ mfib_source_t src)
+{
+ u32 index;
+
+ index = ip4_mfib_index_from_table_id(table_id);
+ if (~0 == index)
+ return ip4_create_mfib_with_table_id(table_id, src);
+ mfib_table_lock(index, FIB_PROTOCOL_IP4, src);
+
+ 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;
+}
+
+void
+ip4_mfib_table_walk (ip4_mfib_t *mfib,
+ mfib_table_walk_fn_t fn,
+ void *ctx)
+{
+ int i;
+
+ 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,
+ ({
+ fn(p->value[0], ctx);
+ }));
+ }
+ }
+}
+
+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/%d", unformat_ip4_address, &grp, &mask))
+ {
+ memset(&src, 0, sizeof(src));
+ matching = 1;
+ }
+ else if (unformat (input, "%U", unformat_ip4_address, &grp))
+ {
+ memset(&src, 0, sizeof(src));
+ matching = 1;
+ mask = 32;
+ }
+ 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_mfib_command, static) = {
+ .path = "show ip mfib",
+ .short_help = "show ip mfib [summary] [table <table-id>] [index <fib-id>] [<grp-addr>[/<mask>]] [<grp-addr>] [<src-addr> <grp-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 00000000..e31fb744
--- /dev/null
+++ b/src/vnet/mfib/ip4_mfib.h
@@ -0,0 +1,105 @@
+/*
+ * 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,
+ mfib_source_t src);
+extern u32 ip4_mfib_table_create_and_lock(mfib_source_t src);
+
+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);
+
+/**
+ * @brief Walk the IP4 mfib table.
+ *
+ * @param mfib the table to walk
+ * @param fn The function to invoke on each entry visited
+ * @param ctx A context passed in the visit function
+ */
+extern void ip4_mfib_table_walk (ip4_mfib_t *mfib,
+ mfib_table_walk_fn_t fn,
+ void *ctx);
+
+#endif
diff --git a/src/vnet/mfib/ip6_mfib.c b/src/vnet/mfib/ip6_mfib.c
new file mode 100644
index 00000000..e4861330
--- /dev/null
+++ b/src/vnet/mfib/ip6_mfib.c
@@ -0,0 +1,699 @@
+/*
+ * 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_source_t src)
+{
+ mfib_table_t *mfib_table;
+ mfib_prefix_t pfx = {
+ .fp_proto = FIB_PROTOCOL_IP6,
+ };
+ const fib_route_path_t path_for_us = {
+ .frp_proto = DPO_PROTO_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, src);
+
+ 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_RPF_ID_NONE,
+ 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 = DPO_PROTO_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 = DPO_PROTO_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,
+ mfib_source_t src)
+{
+ u32 index;
+
+ index = ip6_mfib_index_from_table_id(table_id);
+ if (~0 == index)
+ return ip6_create_mfib_with_table_id(table_id, src);
+ mfib_table_lock(index, FIB_PROTOCOL_IP6, src);
+
+ 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->i6mn_nodes, 0, sizeof(i6mn->i6mn_nodes));
+
+ 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_ {
+ fib_node_index_t *entries;
+} ip6_mfib_show_ctx_t;
+
+
+static int
+ip6_mfib_table_collect_entries (fib_node_index_t mfei, void *arg)
+{
+ ip6_mfib_show_ctx_t *ctx = arg;
+
+ vec_add1(ctx->entries, mfei);
+
+ 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 = {
+ .entries = NULL,
+ };
+
+ ip6_mfib_table_walk(mfib,
+ 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);
+}
+
+typedef struct ip6_mfib_radix_walk_ctx_t_
+{
+ mfib_table_walk_fn_t user_fn;
+ void *user_ctx;
+} ip6_mfib_radix_walk_ctx_t;
+
+static int
+ip6_mfib_table_radix_walk (struct radix_node *rn,
+ void *arg)
+{
+ ip6_mfib_radix_walk_ctx_t *ctx = arg;
+ ip6_mfib_node_t *i6mn;
+
+ i6mn = (ip6_mfib_node_t*) rn;
+
+ ctx->user_fn(i6mn->i6mn_entry, ctx->user_ctx);
+
+ return (0);
+}
+
+void
+ip6_mfib_table_walk (ip6_mfib_t *mfib,
+ mfib_table_walk_fn_t fn,
+ void *ctx)
+{
+ ip6_mfib_radix_walk_ctx_t rn_ctx = {
+ .user_fn = fn,
+ .user_ctx = ctx,
+ };
+
+ rn_walktree(mfib->rhead,
+ ip6_mfib_table_radix_walk,
+ &rn_ctx);
+}
+
+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 = 256;
+ }
+ else if (unformat (input, "%U/%d", unformat_ip6_address, &grp, &mask))
+ {
+ memset(&src, 0, sizeof(src));
+ matching = 1;
+ }
+ else if (unformat (input, "%U", unformat_ip6_address, &grp))
+ {
+ memset(&src, 0, sizeof(src));
+ matching = 1;
+ mask = 128;
+ }
+ 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 00000000..ea81b553
--- /dev/null
+++ b/src/vnet/mfib/ip6_mfib.h
@@ -0,0 +1,121 @@
+/*
+ * 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,
+ mfib_source_t src);
+extern u32 ip6_mfib_table_create_and_lock(mfib_source_t src);
+
+
+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);
+
+/**
+ * @brief Walk the IP6 mfib table.
+ *
+ * @param mfib the table to walk
+ * @param fn The function to invoke on each entry visited
+ * @param ctx A context passed in the visit function
+ */
+extern void ip6_mfib_table_walk (ip6_mfib_t *mfib,
+ mfib_table_walk_fn_t fn,
+ void *ctx);
+
+#endif
+
diff --git a/src/vnet/mfib/mfib_entry.c b/src/vnet/mfib/mfib_entry.c
new file mode 100644
index 00000000..2302b9a1
--- /dev/null
+++ b/src/vnet/mfib/mfib_entry.c
@@ -0,0 +1,1313 @@
+/*
+ * 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
+
+/**
+ * MFIB extensions to each path
+ */
+typedef struct mfib_path_ext_t_
+{
+ mfib_itf_flags_t mfpe_flags;
+ fib_node_index_t mfpe_path;
+} mfib_path_ext_t;
+
+/**
+ * The source of an MFIB entry
+ */
+typedef struct mfib_entry_src_t_
+{
+ /**
+ * Which source this is
+ */
+ mfib_source_t mfes_src;
+
+ /**
+ * Route flags
+ */
+ mfib_entry_flags_t mfes_flags;
+
+ /**
+ * The path-list of forwarding interfaces
+ */
+ fib_node_index_t mfes_pl;
+
+ /**
+ * RPF-ID
+ */
+ fib_rpf_id_t mfes_rpf_id;
+
+ /**
+ * Hash table of path extensions
+ */
+ mfib_path_ext_t *mfes_exts;
+
+ /**
+ * The hash table of all interfaces.
+ * This is forwarding time information derived from the paths
+ * and their extensions.
+ */
+ mfib_itf_t *mfes_itfs;
+} mfib_entry_src_t;
+
+/**
+ * Pool of path extensions
+ */
+static mfib_path_ext_t *mfib_path_ext_pool;
+
+/**
+ * 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));
+}
+
+static inline mfib_path_ext_t *
+mfib_entry_path_ext_get (index_t mi)
+{
+ return (pool_elt_at_index(mfib_path_ext_pool, mi));
+}
+
+static u8 *
+format_mfib_entry_path_ext (u8 * s, va_list * args)
+{
+ mfib_path_ext_t *path_ext;
+ index_t mpi = va_arg(*args, index_t);
+
+ path_ext = mfib_entry_path_ext_get(mpi);
+ return (format(s, "path:%d flags:%U",
+ path_ext->mfpe_path,
+ format_mfib_itf_flags, path_ext->mfpe_flags));
+}
+
+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)
+ {
+ fib_node_index_t path_index, mpi;
+
+ 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);
+ }
+ s = format (s, " Extensions:\n");
+ hash_foreach(path_index, mpi, msrc->mfes_exts,
+ ({
+ s = format(s, " %U\n", format_mfib_entry_path_ext, mpi);
+ }));
+ s = format (s, " Interface-Forwarding:\n");
+ 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 RPF-ID:%d", mfib_entry->mfe_rpf_id);
+ 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);
+}
+
+int
+mfib_entry_is_sourced (fib_node_index_t mfib_entry_index,
+ mfib_source_t source)
+{
+ mfib_entry_t *mfib_entry;
+
+ mfib_entry = mfib_entry_get(mfib_entry_index);
+
+ return (NULL != mfib_entry_src_find(mfib_entry, source, NULL));
+}
+
+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));
+ }));
+ hash_free(msrc->mfes_itfs);
+ msrc->mfes_itfs = NULL;
+ fib_path_list_unlock(msrc->mfes_pl);
+}
+
+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);
+ }
+}
+
+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_aligned(mfib_entry_pool, mfib_entry, CLIB_CACHE_LINE_BYTES);
+
+ fib_node_init(&mfib_entry->mfe_node,
+ FIB_NODE_TYPE_MFIB_ENTRY);
+
+ /*
+ * Some of the members require non-default initialisation
+ * so we also init those that don't and thus save on the call to memset.
+ */
+ mfib_entry->mfe_flags = 0;
+ mfib_entry->mfe_fib_index = fib_index;
+ mfib_entry->mfe_prefix = *prefix;
+ mfib_entry->mfe_srcs = NULL;
+ mfib_entry->mfe_itfs = NULL;
+ mfib_entry->mfe_rpf_id = MFIB_RPF_ID_NONE;
+ mfib_entry->mfe_pl = 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);
+}
+
+static inline mfib_path_ext_t *
+mfib_entry_path_ext_find (mfib_path_ext_t *exts,
+ fib_node_index_t path_index)
+{
+ uword *p;
+
+ p = hash_get(exts, path_index);
+
+ if (NULL != p)
+ {
+ return (mfib_entry_path_ext_get(p[0]));
+ }
+
+ return (NULL);
+}
+
+static mfib_path_ext_t*
+mfib_path_ext_add (mfib_entry_src_t *msrc,
+ fib_node_index_t path_index,
+ mfib_itf_flags_t mfi_flags)
+{
+ mfib_path_ext_t *path_ext;
+
+ pool_get(mfib_path_ext_pool, path_ext);
+
+ path_ext->mfpe_flags = mfi_flags;
+ path_ext->mfpe_path = path_index;
+
+ hash_set(msrc->mfes_exts, path_index,
+ path_ext - mfib_path_ext_pool);
+
+ return (path_ext);
+}
+
+static void
+mfib_path_ext_remove (mfib_entry_src_t *msrc,
+ fib_node_index_t path_index)
+{
+ mfib_path_ext_t *path_ext;
+
+ path_ext = mfib_entry_path_ext_find(msrc->mfes_exts, path_index);
+
+ hash_unset(msrc->mfes_exts, path_index);
+ pool_put(mfib_path_ext_pool, path_ext);
+}
+
+typedef struct mfib_entry_collect_forwarding_ctx_t_
+{
+ load_balance_path_t * next_hops;
+ fib_forward_chain_type_t fct;
+ mfib_entry_src_t *msrc;
+} mfib_entry_collect_forwarding_ctx_t;
+
+static fib_path_list_walk_rc_t
+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 (FIB_PATH_LIST_WALK_CONTINUE);
+ }
+
+ /*
+ * If the path is not forwarding to use it
+ */
+ mfib_path_ext_t *path_ext;
+
+ path_ext = mfib_entry_path_ext_find(ctx->msrc->mfes_exts,
+ path_index);
+
+ if (NULL != path_ext &&
+ !(path_ext->mfpe_flags & MFIB_ITF_FLAG_FORWARD))
+ {
+ return (FIB_PATH_LIST_WALK_CONTINUE);
+ }
+
+ 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:
+ case FIB_FORW_CHAIN_TYPE_NSH:
+ ASSERT(0);
+ break;
+ }
+
+ return (FIB_PATH_LIST_WALK_CONTINUE);
+}
+
+static void
+mfib_entry_stack (mfib_entry_t *mfib_entry,
+ mfib_entry_src_t *msrc)
+{
+ dpo_proto_t dp;
+
+ dp = fib_proto_to_dpo(mfib_entry_get_proto(mfib_entry));
+
+ /*
+ * unlink the enty from the previous path list.
+ */
+ if (FIB_NODE_INDEX_INVALID != mfib_entry->mfe_pl)
+ {
+ fib_path_list_child_remove(mfib_entry->mfe_pl,
+ mfib_entry->mfe_sibling);
+ }
+
+ if (NULL != msrc &&
+ FIB_NODE_INDEX_INVALID != msrc->mfes_pl)
+ {
+ mfib_entry_collect_forwarding_ctx_t ctx = {
+ .next_hops = NULL,
+ .fct = mfib_entry_get_default_chain_type(mfib_entry),
+ .msrc = msrc,
+ };
+
+ fib_path_list_walk(msrc->mfes_pl,
+ mfib_entry_src_collect_forwarding,
+ &ctx);
+
+ if (!(MFIB_ENTRY_FLAG_EXCLUSIVE & mfib_entry->mfe_flags))
+ {
+ if (NULL == ctx.next_hops)
+ {
+ /*
+ * no next-hops, stack directly on the drop
+ */
+ dpo_stack(DPO_MFIB_ENTRY, dp,
+ &mfib_entry->mfe_rep,
+ drop_dpo_get(dp));
+ }
+ else
+ {
+ /*
+ * each path contirbutes a next-hop. form a replicate
+ * from those choices.
+ */
+ 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
+ {
+ /*
+ * for exclusive routes the source provided a replicate DPO
+ * we we stashed inthe special path list with one path
+ * so we can stack directly on that.
+ */
+ ASSERT(1 == vec_len(ctx.next_hops));
+
+ dpo_stack(DPO_MFIB_ENTRY, dp,
+ &mfib_entry->mfe_rep,
+ &ctx.next_hops[0].path_dpo);
+ dpo_reset(&ctx.next_hops[0].path_dpo);
+ vec_free(ctx.next_hops);
+ }
+
+ /*
+ * link the entry to the path-list.
+ * The entry needs to be a child so that we receive the back-walk
+ * updates to recalculate forwarding.
+ */
+ mfib_entry->mfe_pl = msrc->mfes_pl;
+ mfib_entry->mfe_sibling =
+ fib_path_list_child_add(mfib_entry->mfe_pl,
+ FIB_NODE_TYPE_MFIB_ENTRY,
+ mfib_entry_get_index(mfib_entry));
+ }
+ else
+ {
+ dpo_stack(DPO_MFIB_ENTRY, dp,
+ &mfib_entry->mfe_rep,
+ drop_dpo_get(dp));
+ }
+}
+
+static fib_node_index_t
+mfib_entry_src_path_add (mfib_entry_src_t *msrc,
+ const fib_route_path_t *rpath)
+{
+ fib_node_index_t path_index;
+ fib_route_path_t *rpaths;
+
+ ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_flags));
+
+ /*
+ * path-lists require a vector of paths
+ */
+ rpaths = NULL;
+ vec_add1(rpaths, rpath[0]);
+
+ if (FIB_NODE_INDEX_INVALID == msrc->mfes_pl)
+ {
+ /* A non-shared path-list */
+ msrc->mfes_pl = fib_path_list_create(FIB_PATH_LIST_FLAG_NO_URPF,
+ NULL);
+ fib_path_list_lock(msrc->mfes_pl);
+ }
+
+ path_index = fib_path_list_path_add(msrc->mfes_pl, rpaths);
+
+ vec_free(rpaths);
+
+ return (path_index);
+}
+
+static fib_node_index_t
+mfib_entry_src_path_remove (mfib_entry_src_t *msrc,
+ const fib_route_path_t *rpath)
+{
+ fib_node_index_t path_index;
+ fib_route_path_t *rpaths;
+
+ ASSERT(!(MFIB_ENTRY_FLAG_EXCLUSIVE & msrc->mfes_flags));
+
+ /*
+ * path-lists require a vector of paths
+ */
+ rpaths = NULL;
+ vec_add1(rpaths, rpath[0]);
+
+ path_index = fib_path_list_path_remove(msrc->mfes_pl, rpaths);
+
+ vec_free(rpaths);
+
+ return (path_index);
+}
+
+static void
+mfib_entry_recalculate_forwarding (mfib_entry_t *mfib_entry)
+{
+ mfib_entry_src_t *bsrc;
+
+ /*
+ * copy the forwarding data from the bast source
+ */
+ bsrc = mfib_entry_get_best_src(mfib_entry);
+
+ if (NULL != bsrc)
+ {
+ mfib_entry->mfe_flags = bsrc->mfes_flags;
+ mfib_entry->mfe_itfs = bsrc->mfes_itfs;
+ mfib_entry->mfe_rpf_id = bsrc->mfes_rpf_id;
+ }
+
+ mfib_entry_stack(mfib_entry, bsrc);
+}
+
+
+fib_node_index_t
+mfib_entry_create (u32 fib_index,
+ mfib_source_t source,
+ const mfib_prefix_t *prefix,
+ fib_rpf_id_t rpf_id,
+ 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;
+ msrc->mfes_rpf_id = rpf_id;
+
+ 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 == fib_path_list_get_n_paths(msrc->mfes_pl)));
+}
+
+int
+mfib_entry_update (fib_node_index_t mfib_entry_index,
+ mfib_source_t source,
+ mfib_entry_flags_t entry_flags,
+ fib_rpf_id_t rpf_id,
+ index_t repi)
+{
+ 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;
+ msrc->mfes_rpf_id = rpf_id;
+
+ if (INDEX_INVALID != repi)
+ {
+ /*
+ * The source is providing its own replicate DPO.
+ * Create a sepcial path-list to manage it, that way
+ * this entry and the source are equivalent to a normal
+ * entry
+ */
+ fib_node_index_t old_pl_index;
+ dpo_proto_t dp;
+ dpo_id_t dpo = DPO_INVALID;
+
+ dp = fib_proto_to_dpo(mfib_entry_get_proto(mfib_entry));
+ old_pl_index = msrc->mfes_pl;
+
+ dpo_set(&dpo, DPO_REPLICATE, dp, repi);
+
+ msrc->mfes_pl =
+ fib_path_list_create_special(dp,
+ FIB_PATH_LIST_FLAG_EXCLUSIVE,
+ &dpo);
+
+ dpo_reset(&dpo);
+ fib_path_list_lock(msrc->mfes_pl);
+ fib_path_list_unlock(old_pl_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));
+}
+
+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)
+{
+ fib_node_index_t path_index;
+ mfib_path_ext_t *path_ext;
+ mfib_itf_flags_t old, new;
+ mfib_entry_t *mfib_entry;
+ mfib_entry_src_t *msrc;
+
+ mfib_entry = mfib_entry_get(mfib_entry_index);
+ ASSERT(NULL != mfib_entry);
+ msrc = mfib_entry_src_find_or_create(mfib_entry, source);
+
+ /*
+ * add the path to the path-list. If it's a duplicate we'll get
+ * back the original path.
+ */
+ path_index = mfib_entry_src_path_add(msrc, rpath);
+
+ /*
+ * find the path extension for that path
+ */
+ path_ext = mfib_entry_path_ext_find(msrc->mfes_exts, path_index);
+
+ if (NULL == path_ext)
+ {
+ old = MFIB_ITF_FLAG_NONE;
+ path_ext = mfib_path_ext_add(msrc, path_index, itf_flags);
+ }
+ else
+ {
+ old = path_ext->mfpe_flags;
+ path_ext->mfpe_flags = itf_flags;
+ }
+
+ /*
+ * Has the path changed its contribution to the input interface set.
+ * Which only paths with interfaces can do...
+ */
+ if (~0 != rpath[0].frp_sw_if_index)
+ {
+ mfib_itf_t *mfib_itf;
+
+ new = itf_flags;
+
+ if (old != new)
+ {
+ if (MFIB_ITF_FLAG_NONE == new)
+ {
+ /*
+ * no more interface flags on this path, remove
+ * from the data-plane set
+ */
+ mfib_entry_itf_remove(msrc, rpath[0].frp_sw_if_index);
+ }
+ else if (MFIB_ITF_FLAG_NONE == old)
+ {
+ /*
+ * This interface is now contributing
+ */
+ mfib_entry_itf_add(msrc,
+ rpath[0].frp_sw_if_index,
+ mfib_itf_create(rpath[0].frp_sw_if_index,
+ itf_flags));
+ }
+ else
+ {
+ /*
+ * change of flag contributions
+ */
+ mfib_itf = mfib_entry_itf_find(msrc->mfes_itfs,
+ rpath[0].frp_sw_if_index);
+ /* Seen by packets inflight */
+ mfib_itf->mfi_flags = new;
+ }
+ }
+ }
+
+ 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)
+{
+ fib_node_index_t path_index;
+ mfib_entry_t *mfib_entry;
+ mfib_entry_src_t *msrc;
+
+ 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));
+ }
+
+ /*
+ * remove the path from the path-list. If it's not there we'll get
+ * back invalid
+ */
+ path_index = mfib_entry_src_path_remove(msrc, rpath);
+
+ if (FIB_NODE_INDEX_INVALID != path_index)
+ {
+ /*
+ * don't need the extension, nor the interface anymore
+ */
+ mfib_path_ext_remove(msrc, path_index);
+ if (~0 != rpath[0].frp_sw_if_index)
+ {
+ 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));
+}
+
+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);
+ }
+
+ 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)
+{
+ mfib_entry_recalculate_forwarding(mfib_entry_from_fib_node(node));
+
+ 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,
+};
+
+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_src_t *bsrc;
+
+ mfib_entry = mfib_entry_get(mfib_entry_index);
+ bsrc = mfib_entry_get_best_src(mfib_entry);
+
+ if (FIB_NODE_INDEX_INVALID != bsrc->mfes_pl)
+ {
+ fib_path_list_walk(bsrc->mfes_pl,
+ 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);
+}
+
+/*?
+ * This commnad displays an entry, or all entries, in the mfib tables indexed by their unique
+ * numerical indentifier.
+ ?*/
+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 00000000..96ee49f7
--- /dev/null
+++ b/src/vnet/mfib/mfib_entry.h
@@ -0,0 +1,184 @@
+/*
+ * 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;
+
+ /**
+ * A vector of sources contributing forwarding
+ */
+ struct mfib_entry_src_t_ *mfe_srcs;
+
+ /**
+ * The path-list of which this entry is a child
+ */
+ fib_node_index_t mfe_pl;
+
+ /**
+ * The sibling index on the path-list
+ */
+ u32 mfe_sibling;
+
+ /**
+ * 2nd cache line has the members used in the data plane
+ */
+ CLIB_CACHE_LINE_ALIGN_MARK(cacheline1);
+
+ /**
+ * The DPO used for forwarding; replicate, drop, etc..
+ */
+ dpo_id_t mfe_rep;
+
+ /**
+ * Route flags
+ */
+ mfib_entry_flags_t mfe_flags;
+
+ /**
+ * RPF-ID used when the packets ingress not from an interface
+ */
+ fib_rpf_id_t mfe_rpf_id;
+
+ /**
+ * 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,
+ fib_rpf_id_t rpf_id,
+ 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,
+ fib_rpf_id_t rpf_id,
+ index_t rep_dpo);
+
+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 int mfib_entry_is_sourced(fib_node_index_t fib_entry_index,
+ mfib_source_t source);
+
+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_encode(fib_node_index_t fib_entry_index,
+ fib_route_path_encode_t **api_rpaths);
+
+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 00000000..3d8f4f98
--- /dev/null
+++ b/src/vnet/mfib/mfib_forward.c
@@ -0,0 +1,525 @@
+/*
+ * 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 the mfib entry has a configured RPF-ID check that
+ * in preference to an interface based RPF
+ */
+ if (MFIB_RPF_ID_NONE != mfe0->mfe_rpf_id)
+ {
+ iflags0 = (mfe0->mfe_rpf_id == vnet_buffer(b0)->ip.rpf_id ?
+ MFIB_ITF_FLAG_ACCEPT :
+ MFIB_ITF_FLAG_NONE);
+ }
+ else
+ {
+ 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));
+ t0->entry_index = mfei0;
+ t0->itf_flags = iflags0;
+ if (NULL == mfi0)
+ {
+ t0->sw_if_index = ~0;
+ }
+ else
+ {
+ t0->sw_if_index = mfi0->mfi_sw_if_index;
+ }
+ }
+ 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] = "ip4-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] = "ip6-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 00000000..f77b40e7
--- /dev/null
+++ b/src/vnet/mfib/mfib_itf.c
@@ -0,0 +1,123 @@
+/*
+ * 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);
+}
+
+/*?
+ * This commnad displays an MFIB interface, or all interfaces, indexed by their unique
+ * numerical indentifier.
+ ?*/
+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 00000000..5f26a476
--- /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 00000000..cd486da3
--- /dev/null
+++ b/src/vnet/mfib/mfib_signal.c
@@ -0,0 +1,214 @@
+/*
+ * 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();
+}
+
+static inline void
+mfib_signal_lock_aquire (void)
+{
+ while (__sync_lock_test_and_set (&mfib_signal_pending.mip_lock, 1))
+ ;
+}
+
+static inline void
+mfib_signal_lock_release (void)
+{
+ mfib_signal_pending.mip_lock = 0;
+}
+
+#define MFIB_SIGNAL_CRITICAL_SECTION(_body) \
+{ \
+ mfib_signal_lock_aquire(); \
+ do { \
+ _body; \
+ } while (0); \
+ mfib_signal_lock_release(); \
+}
+
+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.
+ */
+ MFIB_SIGNAL_CRITICAL_SECTION(
+ ({
+ li = clib_dlist_remove_head(mfib_signal_dlist_pool,
+ mfib_signal_pending.mip_head);
+ }));
+
+ 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
+ */
+ MFIB_SIGNAL_CRITICAL_SECTION(
+ ({
+ pool_put_index(mfib_signal_pool, si);
+ pool_put_index(mfib_signal_dlist_pool, li);
+ }));
+
+ 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;
+
+ MFIB_SIGNAL_CRITICAL_SECTION(
+ ({
+ 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);
+ }));
+
+ 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
+ */
+ MFIB_SIGNAL_CRITICAL_SECTION(
+ ({
+ 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);
+ }));
+ }
+}
diff --git a/src/vnet/mfib/mfib_signal.h b/src/vnet/mfib/mfib_signal.h
new file mode 100644
index 00000000..732d8aff
--- /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 00000000..838864ff
--- /dev/null
+++ b/src/vnet/mfib/mfib_table.c
@@ -0,0 +1,647 @@
+/*
+ * 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,
+ fib_rpf_id_t rpf_id,
+ 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, rpf_id,
+ 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,
+ rpf_id,
+ INDEX_INVALID))
+ {
+ /*
+ * 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_RPF_ID_NONE,
+ 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);
+ }
+}
+
+fib_node_index_t
+mfib_table_entry_special_add (u32 fib_index,
+ const mfib_prefix_t *prefix,
+ mfib_source_t source,
+ mfib_entry_flags_t entry_flags,
+ index_t rep_dpo)
+{
+ 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_RPF_ID_NONE,
+ MFIB_ENTRY_FLAG_NONE);
+
+ mfib_table_entry_insert(mfib_table, prefix, mfib_entry_index);
+ }
+
+ mfib_entry_update(mfib_entry_index, source,
+ (MFIB_ENTRY_FLAG_EXCLUSIVE | entry_flags),
+ MFIB_RPF_ID_NONE,
+ rep_dpo);
+
+ return (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);
+}
+
+static u32
+mfib_table_find_or_create_and_lock_i (fib_protocol_t proto,
+ u32 table_id,
+ mfib_source_t src,
+ const u8 *name)
+{
+ 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, src);
+ break;
+ case FIB_PROTOCOL_IP6:
+ fi = ip6_mfib_table_find_or_create_and_lock(table_id, src);
+ break;
+ case FIB_PROTOCOL_MPLS:
+ default:
+ return (~0);
+ }
+
+ mfib_table = mfib_table_get(fi, proto);
+
+ if (NULL == mfib_table->mft_desc)
+ {
+ if (name && name[0])
+ {
+ mfib_table->mft_desc = format(NULL, "%s", name);
+ }
+ else
+ {
+ mfib_table->mft_desc = format(NULL, "%U-VRF:%d",
+ format_fib_protocol, proto,
+ table_id);
+ }
+ }
+
+ return (fi);
+}
+
+u32
+mfib_table_find_or_create_and_lock (fib_protocol_t proto,
+ u32 table_id,
+ mfib_source_t src)
+{
+ return (mfib_table_find_or_create_and_lock_i(proto, table_id,
+ src, NULL));
+}
+
+u32
+mfib_table_find_or_create_and_lock_w_name (fib_protocol_t proto,
+ u32 table_id,
+ mfib_source_t src,
+ const u8 *name)
+{
+ return (mfib_table_find_or_create_and_lock_i(proto, table_id,
+ src, name));
+}
+
+/**
+ * @brief Table flush context. Store the indicies of matching FIB entries
+ * that need to be removed.
+ */
+typedef struct mfib_table_flush_ctx_t_
+{
+ /**
+ * The list of entries to flush
+ */
+ fib_node_index_t *mftf_entries;
+
+ /**
+ * The source we are flushing
+ */
+ mfib_source_t mftf_source;
+} mfib_table_flush_ctx_t;
+
+static int
+mfib_table_flush_cb (fib_node_index_t mfib_entry_index,
+ void *arg)
+{
+ mfib_table_flush_ctx_t *ctx = arg;
+
+ if (mfib_entry_is_sourced(mfib_entry_index, ctx->mftf_source))
+ {
+ vec_add1(ctx->mftf_entries, mfib_entry_index);
+ }
+ return (1);
+}
+
+void
+mfib_table_flush (u32 mfib_index,
+ fib_protocol_t proto,
+ mfib_source_t source)
+{
+ fib_node_index_t *mfib_entry_index;
+ mfib_table_flush_ctx_t ctx = {
+ .mftf_entries = NULL,
+ .mftf_source = source,
+ };
+
+ mfib_table_walk(mfib_index, proto,
+ mfib_table_flush_cb,
+ &ctx);
+
+ vec_foreach(mfib_entry_index, ctx.mftf_entries)
+ {
+ mfib_table_entry_delete_index(*mfib_entry_index, source);
+ }
+
+ vec_free(ctx.mftf_entries);
+}
+
+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_source_t source)
+{
+ mfib_table_t *mfib_table;
+
+ mfib_table = mfib_table_get(fib_index, proto);
+ mfib_table->mft_locks[source]--;
+ mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]--;
+
+ if (0 == mfib_table->mft_locks[source])
+ {
+ /*
+ * The source no longer needs the table. flush any routes
+ * from it just in case
+ */
+ mfib_table_flush(fib_index, proto, source);
+ }
+
+ if (0 == mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS])
+ {
+ /*
+ * no more locak from any source - kill it
+ */
+ mfib_table_destroy(mfib_table);
+ }
+}
+
+void
+mfib_table_lock (u32 fib_index,
+ fib_protocol_t proto,
+ mfib_source_t source)
+{
+ mfib_table_t *mfib_table;
+
+ mfib_table = mfib_table_get(fib_index, proto);
+ mfib_table->mft_locks[source]++;
+ mfib_table->mft_locks[MFIB_TABLE_TOTAL_LOCKS]++;
+}
+
+void
+mfib_table_walk (u32 fib_index,
+ fib_protocol_t proto,
+ mfib_table_walk_fn_t fn,
+ void *ctx)
+{
+ switch (proto)
+ {
+ case FIB_PROTOCOL_IP4:
+ ip4_mfib_table_walk(ip4_mfib_get(fib_index), fn, ctx);
+ break;
+ case FIB_PROTOCOL_IP6:
+ ip6_mfib_table_walk(ip6_mfib_get(fib_index), fn, ctx);
+ break;
+ case FIB_PROTOCOL_MPLS:
+ break;
+ }
+}
+
+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 00000000..93f90dd5
--- /dev/null
+++ b/src/vnet/mfib/mfib_table.h
@@ -0,0 +1,424 @@
+/*
+ * 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/dpo/replicate_dpo.h>
+
+#include <vnet/mfib/mfib_types.h>
+
+/**
+ * Keep a lock per-source and a total
+ */
+#define MFIB_TABLE_N_LOCKS (MFIB_N_SOURCES+1)
+#define MFIB_TABLE_TOTAL_LOCKS MFIB_N_SOURCES
+
+/**
+ * @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[MFIB_TABLE_N_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,
+ fib_rpf_id_t rpf_id,
+ 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
+ * Add a 'special' entry to the mFIB that links to the DPO passed
+ * A special entry is an entry that the FIB is not expect to resolve
+ * via the usual mechanisms (i.e. recurisve or neighbour adj DB lookup).
+ * Instead the client/source provides the index of a replicate DPO to link to.
+ *
+ * @param fib_index
+ * The index of the FIB
+ *
+ * @param prefix
+ * The prefix to add
+ *
+ * @param source
+ * The ID of the client/source adding the entry.
+ *
+ * @param flags
+ * Flags for the entry.
+ *
+ * @param rep_dpo
+ * The replicate DPO index to link to.
+ *
+ * @return
+ * the index of the fib_entry_t that is created (or existed already).
+ */
+extern fib_node_index_t mfib_table_entry_special_add(u32 fib_index,
+ const mfib_prefix_t *prefix,
+ mfib_source_t source,
+ mfib_entry_flags_t flags,
+ index_t rep_dpo);
+
+/**
+ * @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,
+ mfib_source_t source);
+
+/**
+ * @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
+ *
+ * @param source
+ * The ID of the client/source.
+ */
+extern u32 mfib_table_find_or_create_and_lock(fib_protocol_t proto,
+ u32 table_id,
+ mfib_source_t source);
+
+/**
+ * @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
+ *
+ * @param source
+ * The ID of the client/source.
+ *
+ * @param name
+ * The client is choosing the name they want the table to have
+ */
+extern u32 mfib_table_find_or_create_and_lock_w_name(fib_protocol_t proto,
+ u32 table_id,
+ mfib_source_t source,
+ const u8 *name);
+
+
+/**
+ * @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)
+ *
+ * @param source
+ * The ID of the client/source.
+ */
+extern void mfib_table_unlock(u32 fib_index,
+ fib_protocol_t proto,
+ mfib_source_t source);
+
+/**
+ * @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)
+ *
+ * @param source
+ * The ID of the client/source.
+ */
+extern void mfib_table_lock(u32 fib_index,
+ fib_protocol_t proto,
+ mfib_source_t source);
+
+/**
+ * @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);
+
+/**
+ * @brief Call back function when walking entries in a FIB table
+ */
+typedef int (*mfib_table_walk_fn_t)(fib_node_index_t fei,
+ void *ctx);
+
+/**
+ * @brief Walk all entries in a FIB table
+ * N.B: This is NOT safe to deletes. If you need to delete, walk the whole
+ * table and store elements in a vector, then delete the elements
+ */
+extern void mfib_table_walk(u32 fib_index,
+ fib_protocol_t proto,
+ mfib_table_walk_fn_t fn,
+ void *ctx);
+
+#endif
diff --git a/src/vnet/mfib/mfib_test.c b/src/vnet/mfib/mfib_test.c
new file mode 100644
index 00000000..2562bc14
--- /dev/null
+++ b/src/vnet/mfib/mfib_test.c
@@ -0,0 +1,1405 @@
+/*
+ * 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/fib/fib_path_list.h>
+#include <vnet/fib/fib_test.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/mpls_fib.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 [exp:%d] stacks on %U",
+ bucket, ai,
+ 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;
+ int res;
+
+ 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);
+ res = !0;
+ }
+ else
+ {
+ dpo_id_t tmp = DPO_INVALID;
+
+ 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);
+ }
+
+ va_end(ap);
+
+ 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, n_pls;
+ 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);
+ n_pls = fib_path_list_pool_size();
+ tm = &test_main;
+
+ ai_1 = adj_mcast_add_or_lock(PROTO,
+ LINKT,
+ tm->hw[1]->sw_if_index);
+ ai_2 = adj_mcast_add_or_lock(PROTO,
+ LINKT,
+ tm->hw[2]->sw_if_index);
+ ai_3 = adj_mcast_add_or_lock(PROTO,
+ LINKT,
+ tm->hw[3]->sw_if_index);
+
+ MFIB_TEST(3 == adj_mcast_db_size(), "3 MCAST adjs");
+
+ /* Find or create FIB table 11 */
+ fib_index = mfib_table_find_or_create_and_lock(PROTO, 11, MFIB_SOURCE_API);
+
+ mfib_prefix_t pfx_dft = {
+ .fp_len = 0,
+ .fp_proto = PROTO,
+ };
+ mfei_dflt = mfib_table_lookup_exact_match(fib_index, &pfx_dft);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet");
+ MFIB_TEST(mfib_test_entry(mfei_dflt,
+ MFIB_ENTRY_FLAG_DROP,
+ 0),
+ "(*,*) no replcaitions");
+
+ MFIB_TEST(FIB_NODE_INDEX_INVALID != mfei_dflt, "(*,*) presnet");
+ MFIB_TEST(mfib_test_entry(mfei_dflt,
+ MFIB_ENTRY_FLAG_DROP,
+ 0),
+ "(*,*) no replcaitions");
+
+
+ fib_route_path_t path_via_if0 = {
+ .frp_proto = fib_proto_to_dpo(PROTO),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = tm->hw[0]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 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 = fib_proto_to_dpo(PROTO),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = tm->hw[1]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 0,
+ .frp_flags = 0,
+ };
+ fib_route_path_t path_via_if2 = {
+ .frp_proto = fib_proto_to_dpo(PROTO),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = tm->hw[2]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 0,
+ .frp_flags = 0,
+ };
+ fib_route_path_t path_via_if3 = {
+ .frp_proto = fib_proto_to_dpo(PROTO),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = tm->hw[3]->sw_if_index,
+ .frp_fib_index = ~0,
+ .frp_weight = 0,
+ .frp_flags = 0,
+ };
+ fib_route_path_t path_for_us = {
+ .frp_proto = fib_proto_to_dpo(PROTO),
+ .frp_addr = zero_addr,
+ .frp_sw_if_index = 0xffffffff,
+ .frp_fib_index = ~0,
+ .frp_weight = 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_RPF_ID_NONE,
+ MFIB_ENTRY_FLAG_SIGNAL);
+ MFIB_TEST(mfib_test_entry(mfei,
+ MFIB_ENTRY_FLAG_SIGNAL,
+ 3,
+ DPO_ADJACENCY_MCAST, ai_1,
+ DPO_ADJACENCY_MCAST, ai_2,
+ DPO_ADJACENCY_MCAST, ai_3),
+ "%U replicate OK",
+ format_mfib_prefix, pfx_s_g);
+
+ /*
+ * remove paths
+ */
+ mfib_table_entry_path_remove(fib_index,
+ pfx_s_g,
+ MFIB_SOURCE_API,
+ &path_via_if3);
+
+ MFIB_TEST(mfib_test_entry(mfei,
+ MFIB_ENTRY_FLAG_SIGNAL,
+ 2,
+ DPO_ADJACENCY_MCAST, ai_1,
+ DPO_ADJACENCY_MCAST, ai_2),
+ "%U replicate OK",
+ format_mfib_prefix, pfx_s_g);
+ MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
+ MFIB_ITF_FLAG_ACCEPT));
+ MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[1]->sw_if_index,
+ MFIB_ITF_FLAG_FORWARD));
+ MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
+ MFIB_ITF_FLAG_FORWARD));
+ MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[3]->sw_if_index));
+
+ mfib_table_entry_path_remove(fib_index,
+ pfx_s_g,
+ MFIB_SOURCE_API,
+ &path_via_if1);
+
+ MFIB_TEST(mfib_test_entry(mfei,
+ MFIB_ENTRY_FLAG_SIGNAL,
+ 1,
+ DPO_ADJACENCY_MCAST, ai_2),
+ "%U replicate OK",
+ format_mfib_prefix, pfx_s_g);
+ MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[0]->sw_if_index,
+ MFIB_ITF_FLAG_ACCEPT));
+ MFIB_TEST_NS(mfib_test_entry_itf(mfei, tm->hw[2]->sw_if_index,
+ MFIB_ITF_FLAG_FORWARD));
+ MFIB_TEST_NS(mfib_test_entry_no_itf(mfei, tm->hw[3]->sw_if_index));
+
+ /*
+ * remove 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_RPF_ID_NONE,
+ (MFIB_ENTRY_FLAG_SIGNAL |
+ MFIB_ENTRY_FLAG_CONNECTED));
+ MFIB_TEST(mfib_test_entry(mfei,
+ (MFIB_ENTRY_FLAG_SIGNAL |
+ MFIB_ENTRY_FLAG_CONNECTED),
+ 0),
+ "%U no replications",
+ format_mfib_prefix, pfx_s_g);
+
+ /*
+ * An entry with a NS interface
+ */
+ 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_RPF_ID_NONE,
+ MFIB_ENTRY_FLAG_NONE);
+ mfei = mfib_table_lookup_exact_match(fib_index,
+ pfx_s_g);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U gone",
+ format_mfib_prefix, pfx_s_g);
+
+ /*
+ * remove the last path on the no forward entry - the last entry
+ */
+ mfib_table_entry_path_remove(fib_index,
+ pfx_no_forward,
+ MFIB_SOURCE_API,
+ &path_via_if0);
+
+ mfei = mfib_table_lookup_exact_match(fib_index, pfx_no_forward);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U gone",
+ format_mfib_prefix, pfx_no_forward);
+
+ /*
+ * hard delete the (*,232.1.1.1)
+ */
+ mfib_table_entry_delete(fib_index,
+ pfx_star_g_1,
+ MFIB_SOURCE_API);
+
+ mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_1);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U gone",
+ format_mfib_prefix, pfx_star_g_1);
+ /*
+ * remove the entry whilst the signal is pending
+ */
+ mfib_table_entry_delete(fib_index,
+ pfx_star_g_2,
+ MFIB_SOURCE_API);
+
+ mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_2);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U Gone",
+ format_mfib_prefix, pfx_star_g_2);
+ mfib_table_entry_delete(fib_index,
+ pfx_star_g_3,
+ MFIB_SOURCE_API);
+
+ mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_3);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U Gone",
+ format_mfib_prefix, pfx_star_g_3);
+
+ mfib_table_entry_delete(fib_index,
+ pfx_star_g_slash_m,
+ MFIB_SOURCE_API);
+
+ mfei = mfib_table_lookup_exact_match(fib_index, pfx_star_g_slash_m);
+ MFIB_TEST(FIB_NODE_INDEX_INVALID == mfei,
+ "%U Gone",
+ format_mfib_prefix, pfx_star_g_slash_m);
+
+ /*
+ * Add a prefix as a special/exclusive route
+ */
+ dpo_id_t td = DPO_INVALID;
+ index_t repi = replicate_create(1, fib_proto_to_dpo(PROTO));
+
+ dpo_set(&td, DPO_ADJACENCY_MCAST, fib_proto_to_dpo(PROTO), ai_2);
+ replicate_set_bucket(repi, 0, &td);
+
+ mfei = mfib_table_entry_special_add(fib_index,
+ pfx_star_g_3,
+ MFIB_SOURCE_SRv6,
+ MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
+ repi);
+ MFIB_TEST(mfib_test_entry(mfei,
+ (MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF |
+ MFIB_ENTRY_FLAG_EXCLUSIVE),
+ 1,
+ DPO_ADJACENCY_MCAST, ai_2),
+ "%U exclusive replicate OK",
+ format_mfib_prefix, pfx_star_g_3);
+
+ /*
+ * update a special/exclusive route
+ */
+ index_t repi2 = replicate_create(1, fib_proto_to_dpo(PROTO));
+
+ dpo_set(&td, DPO_ADJACENCY_MCAST, fib_proto_to_dpo(PROTO), ai_1);
+ replicate_set_bucket(repi2, 0, &td);
+
+ mfei = mfib_table_entry_special_add(fib_index,
+ pfx_star_g_3,
+ MFIB_SOURCE_SRv6,
+ MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF,
+ repi2);
+ MFIB_TEST(mfib_test_entry(mfei,
+ (MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF |
+ MFIB_ENTRY_FLAG_EXCLUSIVE),
+ 1,
+ DPO_ADJACENCY_MCAST, ai_1),
+ "%U exclusive update replicate OK",
+ format_mfib_prefix, pfx_star_g_3);
+
+ mfib_table_entry_delete(fib_index,
+ pfx_star_g_3,
+ MFIB_SOURCE_SRv6);
+ dpo_reset(&td);
+
+ /*
+ * A Multicast LSP. This a mLDP head-end
+ */
+ fib_node_index_t ai_mpls_10_10_10_1, lfei;
+ ip46_address_t nh_10_10_10_1 = {
+ .ip4 = {
+ .as_u32 = clib_host_to_net_u32(0x0a0a0a01),
+ },
+ };
+ ai_mpls_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_MPLS,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index);
+
+ fib_prefix_t pfx_3500 = {
+ .fp_len = 21,
+ .fp_proto = FIB_PROTOCOL_MPLS,
+ .fp_label = 3500,
+ .fp_eos = MPLS_EOS,
+ .fp_payload_proto = DPO_PROTO_IP4,
+ };
+ fib_test_rep_bucket_t mc_0 = {
+ .type = FT_REP_LABEL_O_ADJ,
+ .label_o_adj = {
+ .adj = ai_mpls_10_10_10_1,
+ .label = 3300,
+ .eos = MPLS_EOS,
+ },
+ };
+ mpls_label_t *l3300 = NULL;
+ vec_add1(l3300, 3300);
+
+ /*
+ * MPLS enable an interface so we get the MPLS table created
+ */
+ mpls_table_create(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API, NULL);
+ mpls_sw_interface_enable_disable(&mpls_main,
+ tm->hw[0]->sw_if_index,
+ 1, 0);
+
+ lfei = fib_table_entry_update_one_path(0, // default MPLS Table
+ &pfx_3500,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_MULTICAST,
+ DPO_PROTO_IP4,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ l3300,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ MFIB_TEST(fib_test_validate_entry(lfei,
+ FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+ 1,
+ &mc_0),
+ "3500 via replicate over 10.10.10.1");
+
+ /*
+ * An (S,G) that resolves via the mLDP head-end
+ */
+ fib_route_path_t path_via_mldp = {
+ .frp_proto = DPO_PROTO_MPLS,
+ .frp_local_label = pfx_3500.fp_label,
+ .frp_eos = MPLS_EOS,
+ .frp_sw_if_index = 0xffffffff,
+ .frp_fib_index = 0,
+ .frp_weight = 1,
+ .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+ };
+ dpo_id_t mldp_dpo = DPO_INVALID;
+
+ fib_entry_contribute_forwarding(lfei,
+ FIB_FORW_CHAIN_TYPE_MPLS_EOS,
+ &mldp_dpo);
+
+ mfei = mfib_table_entry_path_update(fib_index,
+ pfx_s_g,
+ MFIB_SOURCE_API,
+ &path_via_mldp,
+ MFIB_ITF_FLAG_FORWARD);
+
+ MFIB_TEST(mfib_test_entry(mfei,
+ MFIB_ENTRY_FLAG_NONE,
+ 1,
+ DPO_REPLICATE, mldp_dpo.dpoi_index),
+ "%U over-mLDP replicate OK",
+ format_mfib_prefix, pfx_s_g);
+
+ /*
+ * add a for-us path. this tests two types of non-attached paths on one entry
+ */
+ mfei = mfib_table_entry_path_update(fib_index,
+ pfx_s_g,
+ MFIB_SOURCE_API,
+ &path_for_us,
+ MFIB_ITF_FLAG_FORWARD);
+ MFIB_TEST(mfib_test_entry(mfei,
+ MFIB_ENTRY_FLAG_NONE,
+ 2,
+ DPO_REPLICATE, mldp_dpo.dpoi_index,
+ DPO_RECEIVE, 0),
+ "%U mLDP+for-us replicate OK",
+ format_mfib_prefix, pfx_s_g);
+
+ mfib_table_entry_delete(fib_index,
+ pfx_s_g,
+ MFIB_SOURCE_API);
+ fib_table_entry_delete(0,
+ &pfx_3500,
+ FIB_SOURCE_API);
+ dpo_reset(&mldp_dpo);
+
+ /*
+ * Unlock the table - it's the last lock so should be gone thereafter
+ */
+ mfib_table_unlock(fib_index, PROTO, MFIB_SOURCE_API);
+
+ MFIB_TEST((FIB_NODE_INDEX_INVALID ==
+ mfib_table_find(PROTO, fib_index)),
+ "MFIB table %d gone", fib_index);
+
+ adj_unlock(ai_1);
+ adj_unlock(ai_2);
+ adj_unlock(ai_3);
+
+ /*
+ * MPLS disable the interface
+ */
+ mpls_sw_interface_enable_disable(&mpls_main,
+ tm->hw[0]->sw_if_index,
+ 0, 0);
+ mpls_table_delete(MPLS_FIB_DEFAULT_TABLE_ID, FIB_SOURCE_API);
+
+ /*
+ * test we've leaked no resources
+ */
+ MFIB_TEST(0 == adj_mcast_db_size(), "%d MCAST adjs", adj_mcast_db_size());
+ MFIB_TEST(n_pls == fib_path_list_pool_size(), "%d=%d path-lists",
+ n_pls, fib_path_list_pool_size());
+ MFIB_TEST(n_reps == pool_elts(replicate_pool), "%d=%d replicates",
+ n_reps, pool_elts(replicate_pool));
+ MFIB_TEST(n_entries == pool_elts(mfib_entry_pool),
+ " No more entries %d!=%d",
+ n_entries, pool_elts(mfib_entry_pool));
+ MFIB_TEST(n_itfs == pool_elts(mfib_itf_pool),
+ " No more Interfaces %d!=%d",
+ n_itfs, pool_elts(mfib_itf_pool));
+
+ 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 = "mfib unit tests - DO NOT RUN ON A LIVE SYSTEM",
+ .function = mfib_test,
+};
+
+clib_error_t *
+mfib_test_init (vlib_main_t *vm)
+{
+ return 0;
+}
+
+VLIB_INIT_FUNCTION (mfib_test_init);
diff --git a/src/vnet/mfib/mfib_types.c b/src/vnet/mfib/mfib_types.c
new file mode 100644
index 00000000..8452d86f
--- /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 displays the set of supported flags applicable to an MFIB route
+ */
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (mfib_route_flags_command, static) =
+{
+ .path = "show 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 displays the set of supported flags applicable to an MFIB interface
+ */
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (mfib_itf_flags_command, static) =
+{
+ .path = "show 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 00000000..50aede04
--- /dev/null
+++ b/src/vnet/mfib/mfib_types.h
@@ -0,0 +1,205 @@
+ /*
+ * 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,
+ /**
+ * Exclusive - like its unicast counterpart. the source has provided
+ * the forwarding DPO directly. The entry therefore does not resolve
+ * paths via a path-list
+ */
+ MFIB_ENTRY_EXCLUSIVE,
+
+ 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", \
+ [MFIB_ENTRY_EXCLUSIVE] = "E", \
+}
+
+#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", \
+ [MFIB_ENTRY_EXCLUSIVE] = "Exclusive", \
+}
+
+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_ACCEPT_ALL_ITF = (1 << MFIB_ENTRY_ACCEPT_ALL_ITF),
+ MFIB_ENTRY_FLAG_EXCLUSIVE = (1 << MFIB_ENTRY_EXCLUSIVE),
+ MFIB_ENTRY_FLAG_INHERIT_ACCEPT = (1 << MFIB_ENTRY_INHERIT_ACCEPT),
+} 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_SRv6,
+ MFIB_SOURCE_GTPU,
+ MFIB_SOURCE_VXLAN_GPE,
+ MFIB_SOURCE_RR,
+ 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_SRv6] = "SRv6", \
+ [MFIB_SOURCE_GTPU] = "GTPU", \
+ [MFIB_SOURCE_VXLAN_GPE] = "VXLAN-GPE", \
+ [MFIB_SOURCE_RR] = "Recursive-resolution", \
+ [MFIB_SOURCE_DEFAULT_ROUTE] = "Default Route", \
+}
+
+#define MFIB_N_SOURCES (MFIB_SOURCE_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