IP Multicast FIB (mfib)

- IPv[46] mfib tables with support for (*,G/m), (*,G) and (S,G) exact and longest prefix match
 - Replication represented via a new replicate DPO.
 - RPF configuration and data-plane checking
 - data-plane signals sent to listening control planes.

The functions of multicast forwarding entries differ from their unicast conterparts, so we introduce a new mfib_table_t and mfib_entry_t objects. However, we re-use the fib_path_list to resolve and build the entry's output list. the fib_path_list provides the service to construct a replicate DPO for multicast.

'make tests' is added to with two new suites; TEST=mfib, this is invocation of the CLI command 'test mfib' which deals with many path add/remove, flag set/unset scenarios, TEST=ip-mcast, data-plane forwarding tests.

Updated applications to use the new MIFB functions;
  - IPv6 NS/RA.
  - DHCPv6
 unit tests for these are undated accordingly.

Change-Id: I49ec37b01f1b170335a5697541c8fd30e6d3a961
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2016-11-22 17:07:28 +00:00
committed by Damjan Marion
parent 6f692d6e5a
commit 32e1c010b0
90 changed files with 11211 additions and 1767 deletions
+22
View File
@@ -0,0 +1,22 @@
packet-generator new {
name x
limit 1
node ip4-input
size 64-64
no-recycle
data {
ICMP: 1.0.0.2 -> 232.1.1.1
ICMP echo_request
incrementing 100
}
}
trace add pg-input 100
loop create
loop create
set int state loop0 up
set int state loop1 up
ip mroute add 232.1.1.1 via pg0 Accept
ip mroute add 232.1.1.1 via loop0 Forward
ip mroute add 232.1.1.1 via loop1 Forward
+173
View File
@@ -48,6 +48,7 @@
#include <vnet/span/span.h>
#include <vnet/policer/policer.h>
#include <vnet/policer/police.h>
#include <vnet/mfib/mfib_types.h>
#include "vat/json_format.h"
@@ -505,6 +506,53 @@ unformat_flow_classify_table_type (unformat_input_t * input, va_list * va)
return 1;
}
static const char *mfib_flag_names[] = MFIB_ENTRY_NAMES_SHORT;
static const char *mfib_flag_long_names[] = 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;
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_long_names[attr]))
*eflags |= (1 << attr);
}
FOR_EACH_MFIB_ATTRIBUTE (attr)
{
if (unformat (input, mfib_flag_names[attr]))
*eflags |= (1 << attr);
}
return (old == *eflags ? 0 : 1);
}
#if (VPP_API_TEST_BUILTIN==0)
u8 *
format_ip4_address (u8 * s, va_list * args)
@@ -3592,6 +3640,7 @@ _(bridge_domain_add_del_reply) \
_(sw_interface_set_l2_xconnect_reply) \
_(l2fib_add_del_reply) \
_(ip_add_del_route_reply) \
_(ip_mroute_add_del_reply) \
_(mpls_route_add_del_reply) \
_(mpls_ip_bind_unbind_reply) \
_(proxy_arp_add_del_reply) \
@@ -3792,6 +3841,7 @@ _(TAP_MODIFY_REPLY, tap_modify_reply) \
_(TAP_DELETE_REPLY, tap_delete_reply) \
_(SW_INTERFACE_TAP_DETAILS, sw_interface_tap_details) \
_(IP_ADD_DEL_ROUTE_REPLY, ip_add_del_route_reply) \
_(IP_MROUTE_ADD_DEL_REPLY, ip_mroute_add_del_reply) \
_(MPLS_ROUTE_ADD_DEL_REPLY, mpls_route_add_del_reply) \
_(MPLS_IP_BIND_UNBIND_REPLY, mpls_ip_bind_unbind_reply) \
_(PROXY_ARP_ADD_DEL_REPLY, proxy_arp_add_del_reply) \
@@ -6383,6 +6433,126 @@ api_ip_add_del_route (vat_main_t * vam)
return (vam->retval);
}
static int
api_ip_mroute_add_del (vat_main_t * vam)
{
unformat_input_t *i = vam->input;
vl_api_ip_mroute_add_del_t *mp;
f64 timeout;
u32 sw_if_index = ~0, vrf_id = 0;
u8 is_ipv6 = 0;
u8 is_local = 0;
u8 create_vrf_if_needed = 0;
u8 is_add = 1;
u8 address_set = 0;
u32 grp_address_length = 0;
ip4_address_t v4_grp_address, v4_src_address;
ip6_address_t v6_grp_address, v6_src_address;
mfib_itf_flags_t iflags = 0;
mfib_entry_flags_t eflags = 0;
/* Parse args required to build the message */
while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
{
if (unformat (i, "sw_if_index %d", &sw_if_index))
;
else if (unformat (i, "%U %U",
unformat_ip4_address, &v4_src_address,
unformat_ip4_address, &v4_grp_address))
{
grp_address_length = 64;
address_set = 1;
is_ipv6 = 0;
}
else if (unformat (i, "%U %U",
unformat_ip6_address, &v6_src_address,
unformat_ip6_address, &v6_grp_address))
{
grp_address_length = 256;
address_set = 1;
is_ipv6 = 1;
}
else if (unformat (i, "%U", unformat_ip4_address, &v4_grp_address))
{
memset (&v4_src_address, 0, sizeof (v4_src_address));
grp_address_length = 32;
address_set = 1;
is_ipv6 = 0;
}
else if (unformat (i, "%U", unformat_ip6_address, &v6_grp_address))
{
memset (&v6_src_address, 0, sizeof (v6_src_address));
grp_address_length = 128;
address_set = 1;
is_ipv6 = 1;
}
else if (unformat (i, "/%d", &grp_address_length))
;
else if (unformat (i, "local"))
{
is_local = 1;
}
else if (unformat (i, "del"))
is_add = 0;
else if (unformat (i, "add"))
is_add = 1;
else if (unformat (i, "vrf %d", &vrf_id))
;
else if (unformat (i, "create-vrf"))
create_vrf_if_needed = 1;
else if (unformat (i, "%U", unformat_mfib_itf_flags, &iflags))
;
else if (unformat (i, "%U", unformat_mfib_entry_flags, &eflags))
;
else
{
clib_warning ("parse error '%U'", format_unformat_error, i);
return -99;
}
}
if (address_set == 0)
{
errmsg ("missing addresses\n");
return -99;
}
/* Construct the API message */
M (IP_MROUTE_ADD_DEL, ip_mroute_add_del);
mp->next_hop_sw_if_index = ntohl (sw_if_index);
mp->table_id = ntohl (vrf_id);
mp->create_vrf_if_needed = create_vrf_if_needed;
mp->is_add = is_add;
mp->is_ipv6 = is_ipv6;
mp->is_local = is_local;
mp->itf_flags = ntohl (iflags);
mp->entry_flags = ntohl (eflags);
mp->grp_address_length = grp_address_length;
mp->grp_address_length = ntohs (mp->grp_address_length);
if (is_ipv6)
{
clib_memcpy (mp->grp_address, &v6_grp_address, sizeof (v6_grp_address));
clib_memcpy (mp->src_address, &v6_src_address, sizeof (v6_src_address));
}
else
{
clib_memcpy (mp->grp_address, &v4_grp_address, sizeof (v4_grp_address));
clib_memcpy (mp->src_address, &v4_src_address, sizeof (v4_src_address));
}
/* send it... */
S;
/* Wait for a reply... */
W;
/* Return the good/bad news */
return (vam->retval);
}
static int
api_mpls_route_add_del (vat_main_t * vam)
{
@@ -17512,6 +17682,9 @@ _(ip_add_del_route, \
"[<intfc> | sw_if_index <id>] [resolve-attempts <n>]\n" \
"[weight <n>] [drop] [local] [classify <n>] [del]\n" \
"[multipath] [count <n>]") \
_(ip_mroute_add_del, \
"<src> <grp>/<mask> [table-id <n>]\n" \
"[<intfc> | sw_if_index <id>] [local] [del]") \
_(mpls_route_add_del, \
"<label> <eos> via <addr> [table-id <n>]\n" \
"[<intfc> | sw_if_index <id>] [resolve-attempts <n>]\n" \
+31 -4
View File
@@ -946,16 +946,15 @@ nobase_include_HEADERS += \
libvnet_la_SOURCES += \
vnet/adj/adj_nbr.c \
vnet/adj/adj_rewrite.c \
vnet/adj/adj_glean.c \
vnet/adj/adj_midchain.c \
vnet/adj/adj_mcast.c \
vnet/adj/adj_l2.c \
vnet/adj/adj.c
nobase_include_HEADERS += \
vnet/adj/adj.h \
vnet/adj/adj_types.h \
vnet/adj/adj_rewrite.h \
vnet/adj/adj_glean.h \
vnet/adj/adj_nbr.h
@@ -971,8 +970,9 @@ libvnet_la_SOURCES += \
vnet/dpo/receive_dpo.c \
vnet/dpo/load_balance.c \
vnet/dpo/load_balance_map.c \
vnet/dpo/lookup_dpo.c \
vnet/dpo/classify_dpo.c \
vnet/dpo/lookup_dpo.c \
vnet/dpo/classify_dpo.c \
vnet/dpo/replicate_dpo.c \
vnet/dpo/mpls_label_dpo.c
nobase_include_HEADERS += \
@@ -985,6 +985,33 @@ nobase_include_HEADERS += \
vnet/dpo/ip_null_dpo.h \
vnet/dpo/dpo.h
########################################
# Multicast FIB
########################################
libvnet_la_SOURCES += \
vnet/mfib/mfib_test.c \
vnet/mfib/mfib_forward.c \
vnet/mfib/ip4_mfib.c \
vnet/mfib/ip6_mfib.c \
vnet/mfib/mfib_types.c \
vnet/mfib/mfib_signal.c \
vnet/mfib/mfib_itf.c \
vnet/mfib/mfib_entry.c \
vnet/mfib/mfib_table.c
nobase_include_HEADERS += \
vnet/mfib/ip4_mfib.h \
vnet/mfib/mfib_types.h \
vnet/mfib/mfib_table.h
########################################
# Utilities
########################################
libvnet_la_SOURCES += \
vnet/util/radix.c
########################################
# Plugin client library
########################################
+9 -2
View File
@@ -17,6 +17,7 @@
#include <vnet/adj/adj_internal.h>
#include <vnet/adj/adj_glean.h>
#include <vnet/adj/adj_midchain.h>
#include <vnet/adj/adj_mcast.h>
#include <vnet/fib/fib_node_list.h>
/*
@@ -58,8 +59,6 @@ adj_alloc (fib_protocol_t proto)
adj_get_index(adj));
adj->rewrite_header.sw_if_index = ~0;
adj->mcast_group_index = ~0;
adj->saved_lookup_next_index = 0;
adj->n_adj = 1;
adj->lookup_next_index = 0;
@@ -116,6 +115,9 @@ format_ip_adjacency (u8 * s, va_list * args)
case IP_LOOKUP_NEXT_MIDCHAIN:
s = format (s, "%U", format_adj_midchain, adj_index, 2);
break;
case IP_LOOKUP_NEXT_MCAST:
s = format (s, "%U", format_adj_mcast, adj_index, 0);
break;
default:
break;
}
@@ -179,6 +181,10 @@ adj_last_lock_gone (ip_adjacency_t *adj)
adj_glean_remove(adj->ia_nh_proto,
adj->rewrite_header.sw_if_index);
break;
case IP_LOOKUP_NEXT_MCAST:
adj_mcast_remove(adj->ia_nh_proto,
adj->rewrite_header.sw_if_index);
break;
default:
/*
* type not stored in any DB from which we need to remove it
@@ -350,6 +356,7 @@ adj_module_init (vlib_main_t * vm)
adj_nbr_module_init();
adj_glean_module_init();
adj_midchain_module_init();
adj_mcast_module_init();
/*
* one special adj to reserve index 0
-1
View File
@@ -45,7 +45,6 @@
#include <vnet/ip/lookup.h>
#include <vnet/adj/adj_types.h>
#include <vnet/adj/adj_nbr.h>
#include <vnet/adj/adj_rewrite.h>
#include <vnet/adj/adj_glean.h>
/**
+2
View File
@@ -100,5 +100,7 @@ extern void adj_nbr_remove(adj_index_t ai,
u32 sw_if_index);
extern void adj_glean_remove(fib_protocol_t proto,
u32 sw_if_index);
extern void adj_mcast_remove(fib_protocol_t proto,
u32 sw_if_index);
#endif
File diff suppressed because it is too large Load Diff
+78
View File
@@ -0,0 +1,78 @@
/*
* 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 Mcast Adjacency
*
* The multicast adjacency forwards IP traffic on an interface toward a multicast
* group address. This is a different type of adjacency to a unicast adjacency
* since the application of the MAC header is different, and so the VLIB node
* visited is also different. DPO types have different VLIB nodes.
*/
#ifndef __ADJ_MCAST_H__
#define __ADJ_MCAST_H__
#include <vnet/adj/adj_types.h>
/**
* @brief
* Add (and lock) a new or lock an existing mcast adjacency
*
* @param proto
* The protocol for the neighbours that we wish to mcast
*
* @param link_type
* A description of the protocol of the packets that will forward
* through this adj. On an ethernet interface this is the MAC header's
* ether-type
*
* @param sw_if_index
* The interface on which to mcast
*/
extern adj_index_t adj_mcast_add_or_lock(fib_protocol_t proto,
vnet_link_t link_type,
u32 sw_if_index);
/**
* @brief
* Update the rewrite string for an existing adjacecny.
*
* @param
* The index of the adj to update
*
* @param
* The new rewrite
*/
extern void adj_mcast_update_rewrite(adj_index_t adj_index,
u8 *rewrite);
/**
* @brief Format/display a mcast adjacency.
*/
extern u8* format_adj_mcast(u8* s, va_list *ap);
/**
* @brief Get the sze of the mcast adj DB. Test purposes only.
*/
extern u32 adj_mcast_db_size(void);
/**
* @brief
* Module initialisation
*/
extern void adj_mcast_module_init(void);
#endif
+1 -1
View File
@@ -162,7 +162,7 @@ adj_nbr_alloc (fib_protocol_t nh_proto,
}
/*
* adj_add_for_nbr
* adj_nbr_add_or_lock
*
* Add an adjacency for the neighbour requested.
*
-53
View File
@@ -1,53 +0,0 @@
/*
* 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/adj/adj.h>
#include <vnet/adj/adj_internal.h>
/**
* adj_rewrite_add_and_lock
*
* A rewrite sub-type has the rewrite string provided, but no key
*/
adj_index_t
adj_rewrite_add_and_lock (fib_protocol_t nh_proto,
vnet_link_t link_type,
u32 sw_if_index,
u8 *rewrite)
{
ip_adjacency_t *adj;
adj = adj_alloc(nh_proto);
adj->lookup_next_index = IP_LOOKUP_NEXT_REWRITE;
memset(&adj->sub_type.nbr.next_hop, 0, sizeof(adj->sub_type.nbr.next_hop));
adj->ia_link = link_type;
adj->ia_nh_proto = nh_proto;
adj->rewrite_header.sw_if_index = sw_if_index;
ASSERT(NULL != rewrite);
vnet_rewrite_for_sw_interface(vnet_get_main(),
link_type,
adj->rewrite_header.sw_if_index,
adj_get_rewrite_node(link_type),
rewrite,
&adj->rewrite_header,
sizeof (adj->rewrite_data));
adj_lock(adj_get_index(adj));
return (adj_get_index(adj));
}
-49
View File
@@ -1,49 +0,0 @@
/*
* 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
* A rewrite adjacency has no key, and thus cannot be 'found' from the
* FIB resolution code. the client therefore needs to maange these adjacencies
*/
#ifndef __ADJ_REWRITE_H__
#define __ADJ_REWRITE_H__
#include <vnet/adj/adj_types.h>
/**
* @brief
* Add (and lock) a new or lock an existing neighbour adjacency
*
* @param nh_proto
* The protocol for the next-hop address (v4 or v6)
*
* @param link_type
* A description of the protocol of the packets that will forward
* through this adj. On an ethernet interface this is the MAC header's
* ether-type
*
* @param sw_if_index
* The interface on which the peer resides
*
* @param rewrite
* The rewrite to prepend to packets
*/
extern adj_index_t adj_rewrite_add_and_lock(fib_protocol_t nh_proto,
vnet_link_t link_type,
u32 sw_if_index,
u8 *rewrite);
#endif
+43 -3
View File
@@ -19,6 +19,8 @@
#include <vnet/pg/pg.h>
#include <vnet/dhcpv6/proxy.h>
#include <vnet/fib/ip6_fib.h>
#include <vnet/mfib/mfib_table.h>
#include <vnet/mfib/ip6_mfib.h>
static char * dhcpv6_proxy_error_strings[] = {
#define dhcpv6_proxy_error(n,s) s,
@@ -819,7 +821,7 @@ int dhcpv6_proxy_set_server_2 (ip6_address_t *addr, ip6_address_t *src_address,
u32 server_fib_index = 0;
u32 rx_fib_index = 0;
rx_fib_index = ip6_fib_table_find_or_create_and_lock(rx_fib_id);
rx_fib_index = ip6_mfib_table_find_or_create_and_lock(rx_fib_id);
server_fib_index = ip6_fib_table_find_or_create_and_lock(server_fib_id);
if (is_del)
@@ -848,8 +850,10 @@ int dhcpv6_proxy_set_server_2 (ip6_address_t *addr, ip6_address_t *src_address,
if (rx_fib_id == 0)
{
server = pool_elt_at_index (dm->dhcp6_servers, 0);
goto initialize_it;
if (server->valid)
goto reconfigure_it;
else
goto initialize_it;
}
if (rx_fib_index < vec_len(dm->dhcp6_server_index_by_rx_fib_index))
@@ -866,6 +870,42 @@ int dhcpv6_proxy_set_server_2 (ip6_address_t *addr, ip6_address_t *src_address,
pool_get (dm->dhcp6_servers, server);
initialize_it:
{
const mfib_prefix_t all_dhcp_servers = {
.fp_len = 128,
.fp_proto = FIB_PROTOCOL_IP6,
.fp_grp_addr = {
.ip6 = dm->all_dhcpv6_server_relay_agent_address,
}
};
const fib_route_path_t path_for_us = {
.frp_proto = FIB_PROTOCOL_IP6,
.frp_addr = zero_addr,
.frp_sw_if_index = 0xffffffff,
.frp_fib_index = ~0,
.frp_weight = 0,
.frp_flags = FIB_ROUTE_PATH_LOCAL,
};
mfib_table_entry_path_update(rx_fib_index,
&all_dhcp_servers,
MFIB_SOURCE_DHCP,
&path_for_us,
MFIB_ITF_FLAG_FORWARD);
/*
* Each interface that is enabled in this table, needs to be added
* as an accepting interface, but this is not easily doable in VPP.
* So we cheat. Add a flag to the entry that indicates accept form
* any interface.
* We will still only accept on v6 enabled interfaces, since the input
* feature ensures this.
*/
mfib_table_entry_update(rx_fib_index,
&all_dhcp_servers,
MFIB_SOURCE_DHCP,
MFIB_ENTRY_FLAG_ACCEPT_ALL_ITF);
}
reconfigure_it:
copy_ip6_address(&server->dhcp6_server, addr);
copy_ip6_address(&server->dhcp6_src_address, src_address);
+2
View File
@@ -36,6 +36,7 @@
#include <vnet/dpo/punt_dpo.h>
#include <vnet/dpo/classify_dpo.h>
#include <vnet/dpo/ip_null_dpo.h>
#include <vnet/dpo/replicate_dpo.h>
/**
* Array of char* names for the DPO types and protos
@@ -449,6 +450,7 @@ dpo_module_init (vlib_main_t * vm)
classify_dpo_module_init();
lookup_dpo_module_init();
ip_null_dpo_module_init();
replicate_module_init();
return (NULL);
}
+7 -1
View File
@@ -100,15 +100,18 @@ typedef enum dpo_type_t_ {
* @brief load-balancing over a choice of [un]equal cost paths
*/
DPO_LOAD_BALANCE,
DPO_REPLICATE,
DPO_ADJACENCY,
DPO_ADJACENCY_INCOMPLETE,
DPO_ADJACENCY_MIDCHAIN,
DPO_ADJACENCY_GLEAN,
DPO_ADJACENCY_MCAST,
DPO_RECEIVE,
DPO_LOOKUP,
DPO_LISP_CP,
DPO_CLASSIFY,
DPO_MPLS_LABEL,
DPO_MFIB_ENTRY,
DPO_LAST,
} __attribute__((packed)) dpo_type_t;
@@ -123,12 +126,15 @@ typedef enum dpo_type_t_ {
[DPO_ADJACENCY_INCOMPLETE] = "dpo-adjacency-incomplete", \
[DPO_ADJACENCY_MIDCHAIN] = "dpo-adjacency-midcahin", \
[DPO_ADJACENCY_GLEAN] = "dpo-glean", \
[DPO_ADJACENCY_MCAST] = "dpo-adj-mcast", \
[DPO_RECEIVE] = "dpo-receive", \
[DPO_LOOKUP] = "dpo-lookup", \
[DPO_LOAD_BALANCE] = "dpo-load-balance", \
[DPO_REPLICATE] = "dpo-replicate", \
[DPO_LISP_CP] = "dpo-lisp-cp", \
[DPO_CLASSIFY] = "dpo-classify", \
[DPO_MPLS_LABEL] = "dpo-mpls-label" \
[DPO_MPLS_LABEL] = "dpo-mpls-label", \
[DPO_MFIB_ENTRY] = "dpo-mfib_entry" \
}
/**
+12 -1
View File
@@ -238,6 +238,17 @@ load_balance_is_drop (const dpo_id_t *dpo)
return (0);
}
void
load_balance_set_fib_entry_flags (index_t lbi,
fib_entry_flag_t flags)
{
load_balance_t *lb;
lb = load_balance_get(lbi);
lb->lb_fib_entry_flags = flags;
}
void
load_balance_set_urpf (index_t lbi,
index_t urpf)
@@ -683,7 +694,7 @@ load_balance_multipath_update (const dpo_id_t *dpo,
buckets,
n_buckets);
for (ii = old_n_buckets-n_buckets; ii < old_n_buckets; ii++)
for (ii = n_buckets; ii < old_n_buckets; ii++)
{
dpo_reset(&buckets[ii]);
}
+8
View File
@@ -36,6 +36,7 @@
#include <vnet/ip/lookup.h>
#include <vnet/dpo/dpo.h>
#include <vnet/fib/fib_types.h>
#include <vnet/fib/fib_entry.h>
/**
* Load-balance main
@@ -98,6 +99,11 @@ typedef struct load_balance_t_ {
*/
dpo_proto_t lb_proto;
/**
* Flags from the load-balance's associated fib_entry_t
*/
fib_entry_flag_t lb_fib_entry_flags;
/**
* The number of locks, which is approximately the number of users,
* of this load-balance.
@@ -167,6 +173,8 @@ extern void load_balance_set_bucket(index_t lbi,
const dpo_id_t *next);
extern void load_balance_set_urpf(index_t lbi,
index_t urpf);
extern void load_balance_set_fib_entry_flags(index_t lbi,
fib_entry_flag_t flags);
extern index_t load_balance_get_urpf(index_t lbi);
extern u8* format_load_balance(u8 * s, va_list * args);
File diff suppressed because it is too large Load Diff
+143
View File
@@ -0,0 +1,143 @@
/*
* 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
*
*/
#ifndef __REPLICATE_DPO_H__
#define __REPLICATE_DPO_H__
#include <vlib/vlib.h>
#include <vnet/ip/lookup.h>
#include <vnet/dpo/dpo.h>
#include <vnet/dpo/load_balance.h>
#include <vnet/fib/fib_types.h>
/**
* replicate main
*/
typedef struct replicate_main_t_
{
vlib_combined_counter_main_t repm_counters;
} replicate_main_t;
extern replicate_main_t replicate_main;
/**
* The number of buckets that a load-balance object can have and still
* fit in one cache-line
*/
#define REP_NUM_INLINE_BUCKETS 4
/**
* The FIB DPO provieds;
* - load-balancing over the next DPOs in the chain/graph
* - per-route counters
*/
typedef struct replicate_t_ {
/**
* number of buckets in the load-balance. always a power of 2.
*/
u16 rep_n_buckets;
/**
* The protocol of packets that traverse this REP.
* need in combination with the flow hash config to determine how to hash.
* u8.
*/
dpo_proto_t rep_proto;
/**
* The number of locks, which is approximately the number of users,
* of this load-balance.
* Load-balance objects of via-entries are heavily shared by recursives,
* so the lock count is a u32.
*/
u32 rep_locks;
/**
* Vector of buckets containing the next DPOs, sized as repo_num
*/
dpo_id_t *rep_buckets;
/**
* The rest of the cache line is used for buckets. In the common case
* where there there are less than 4 buckets, then the buckets are
* on the same cachlie and we save ourselves a pointer dereferance in
* the data-path.
*/
dpo_id_t rep_buckets_inline[REP_NUM_INLINE_BUCKETS];
} replicate_t;
STATIC_ASSERT(sizeof(replicate_t) <= CLIB_CACHE_LINE_BYTES,
"A replicate object size exceeds one cachline");
/**
* Flags controlling load-balance formatting/display
*/
typedef enum replicate_format_flags_t_ {
REPLICATE_FORMAT_NONE,
REPLICATE_FORMAT_DETAIL = (1 << 0),
} replicate_format_flags_t;
extern index_t replicate_create(u32 num_buckets,
dpo_proto_t rep_proto);
extern void replicate_multipath_update(
const dpo_id_t *dpo,
load_balance_path_t *next_hops);
extern void replicate_set_bucket(index_t repi,
u32 bucket,
const dpo_id_t *next);
extern u8* format_replicate(u8 * s, va_list * args);
extern const dpo_id_t *replicate_get_bucket(index_t repi,
u32 bucket);
extern int replicate_is_drop(const dpo_id_t *dpo);
/**
* The encapsulation breakages are for fast DP access
*/
extern replicate_t *replicate_pool;
static inline replicate_t*
replicate_get (index_t repi)
{
return (pool_elt_at_index(replicate_pool, repi));
}
#define REP_HAS_INLINE_BUCKETS(_rep) \
((_rep)->rep_n_buckets <= REP_NUM_INLINE_BUCKETS)
static inline const dpo_id_t *
replicate_get_bucket_i (const replicate_t *rep,
u32 bucket)
{
ASSERT(bucket < rep->rep_n_buckets);
if (PREDICT_TRUE(REP_HAS_INLINE_BUCKETS(rep)))
{
return (&rep->rep_buckets_inline[bucket]);
}
else
{
return (&rep->rep_buckets[bucket]);
}
}
extern void replicate_module_init(void);
#endif
+63 -21
View File
@@ -23,6 +23,7 @@
#include <vppinfra/mhash.h>
#include <vnet/fib/ip4_fib.h>
#include <vnet/adj/adj_nbr.h>
#include <vnet/adj/adj_mcast.h>
#include <vnet/mpls/mpls.h>
/**
@@ -438,33 +439,74 @@ arp_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai)
arp_int = &am->ethernet_arp_by_sw_if_index[sw_if_index];
e = arp_entry_find (arp_int, &adj->sub_type.nbr.next_hop.ip4);
if (NULL != e)
{
adj_nbr_walk_nh4 (sw_if_index,
&e->ip4_address, arp_mk_complete_walk, e);
}
else
switch (adj->lookup_next_index)
{
case IP_LOOKUP_NEXT_ARP:
case IP_LOOKUP_NEXT_GLEAN:
if (NULL != e)
{
adj_nbr_walk_nh4 (sw_if_index,
&e->ip4_address, arp_mk_complete_walk, e);
}
else
{
/*
* no matching ARP entry.
* construct the rewrite required to for an ARP packet, and stick
* that in the adj's pipe to smoke.
*/
adj_nbr_update_rewrite
(ai,
ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
ethernet_build_rewrite
(vnm,
sw_if_index,
VNET_LINK_ARP,
VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
/*
* since the FIB has added this adj for a route, it makes sense it
* may want to forward traffic sometime soon. Let's send a
* speculative ARP. just one. If we were to do periodically that
* wouldn't be bad either, but that's more code than i'm prepared to
* write at this time for relatively little reward.
*/
arp_nbr_probe (adj);
}
break;
case IP_LOOKUP_NEXT_MCAST:
/*
* no matching ARP entry.
* construct the rewire required to for an ARP packet, and stick
* that in the adj's pipe to smoke.
* Construct a partial rewrite from the known ethernet mcast dest MAC
*/
adj_nbr_update_rewrite (ai,
ADJ_NBR_REWRITE_FLAG_INCOMPLETE,
ethernet_build_rewrite (vnm,
sw_if_index,
VNET_LINK_ARP,
VNET_REWRITE_FOR_SW_INTERFACE_ADDRESS_BROADCAST));
adj_mcast_update_rewrite
(ai,
ethernet_build_rewrite (vnm,
sw_if_index,
adj->ia_link,
ethernet_ip4_mcast_dst_addr ()));
/*
* since the FIB has added this adj for a route, it makes sense it may
* want to forward traffic sometime soon. Let's send a speculative ARP.
* just one. If we were to do periodically that wouldn't be bad either,
* but that's more code than i'm prepared to write at this time for
* relatively little reward.
* Complete the remaining fields of the adj's rewrite to direct the
* complete of the rewrite at switch time by copying in the IP
* dst address's bytes.
* Ofset is 11 bytes from the end of the MAC header - which is three
* bytes into the desintation address. And we write 3 bytes.
*/
arp_nbr_probe (adj);
adj->rewrite_header.dst_mcast_offset = 11;
adj->rewrite_header.dst_mcast_n_bytes = 3;
break;
case IP_LOOKUP_NEXT_DROP:
case IP_LOOKUP_NEXT_PUNT:
case IP_LOOKUP_NEXT_LOCAL:
case IP_LOOKUP_NEXT_REWRITE:
case IP_LOOKUP_NEXT_LOAD_BALANCE:
case IP_LOOKUP_NEXT_MIDCHAIN:
case IP_LOOKUP_NEXT_ICMP_ERROR:
case IP_LOOKUP_N_NEXT:
ASSERT (0);
break;
}
}
+2
View File
@@ -547,6 +547,8 @@ void ethernet_update_adjacency (vnet_main_t * vnm, u32 sw_if_index, u32 ai);
u8 *ethernet_build_rewrite (vnet_main_t * vnm,
u32 sw_if_index,
vnet_link_t link_type, const void *dst_address);
const u8 *ethernet_ip4_mcast_dst_addr (void);
const u8 *ethernet_ip6_mcast_dst_addr (void);
extern vlib_node_registration_t ethernet_input_node;
+20
View File
@@ -51,6 +51,26 @@
* This file contains code to manage loopback interfaces.
*/
const u8 *
ethernet_ip4_mcast_dst_addr (void)
{
const static u8 ethernet_mcast_dst_mac[] = {
0x1, 0x0, 0x5e, 0x0, 0x0, 0x0,
};
return (ethernet_mcast_dst_mac);
}
const u8 *
ethernet_ip6_mcast_dst_addr (void)
{
const static u8 ethernet_mcast_dst_mac[] = {
0x33, 0x33, 0x00, 0x0, 0x0, 0x0,
};
return (ethernet_mcast_dst_mac);
}
/**
* @brief build a rewrite string to use for sending packets of type 'link_type'
* to 'dst_address'
+2 -2
View File
@@ -303,8 +303,8 @@ fib_attached_export_import (fib_entry_t *fib_entry,
* may have realloc'd.
*/
fib_entry = fib_entry_get(fei);
import->faei_export_sibling =
fib_entry_cover_track(fib_entry_get(import->faei_export_entry), fei);
import->faei_export_sibling =
fib_entry_cover_track(fib_entry_get(import->faei_export_entry), fei);
fed = fib_entry_delegate_find_or_add(fib_entry,
FIB_ENTRY_DELEGATE_ATTACHED_IMPORT);
+1 -1
View File
@@ -220,7 +220,7 @@ typedef enum fib_entry_flag_t_ {
FIB_ENTRY_FLAG_EXCLUSIVE = (1 << FIB_ENTRY_ATTRIBUTE_EXCLUSIVE),
FIB_ENTRY_FLAG_LOCAL = (1 << FIB_ENTRY_ATTRIBUTE_LOCAL),
FIB_ENTRY_FLAG_IMPORT = (1 << FIB_ENTRY_ATTRIBUTE_IMPORT),
} fib_entry_flag_t;
} __attribute__((packed)) fib_entry_flag_t;
/**
* Flags for the source data
+3
View File
@@ -119,6 +119,9 @@ fib_entry_chain_type_to_delegate_type (fib_forward_chain_type_t fct)
return (FIB_ENTRY_DELEGATE_CHAIN_MPLS_NON_EOS);
case FIB_FORW_CHAIN_TYPE_ETHERNET:
return (FIB_ENTRY_DELEGATE_CHAIN_ETHERNET);
case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
break;
}
ASSERT(0);
return (FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4);
+4
View File
@@ -313,6 +313,8 @@ fib_entry_src_collect_forwarding (fib_node_index_t pl_index,
{
case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
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
*/
@@ -458,6 +460,8 @@ fib_entry_src_mk_lb (fib_entry_t *fib_entry,
{
load_balance_set_urpf(dpo_lb->dpoi_index, ui);
}
load_balance_set_fib_entry_flags(dpo_lb->dpoi_index,
fib_entry_get_flags_i(fib_entry));
}
void
+2
View File
@@ -31,6 +31,7 @@ typedef enum fib_node_type_t_ {
*/
FIB_NODE_TYPE_WALK,
FIB_NODE_TYPE_ENTRY,
FIB_NODE_TYPE_MFIB_ENTRY,
FIB_NODE_TYPE_PATH_LIST,
FIB_NODE_TYPE_PATH,
FIB_NODE_TYPE_ADJ,
@@ -51,6 +52,7 @@ typedef enum fib_node_type_t_ {
#define FIB_NODE_TYPES { \
[FIB_NODE_TYPE_ENTRY] = "entry", \
[FIB_NODE_TYPE_MFIB_ENTRY] = "mfib-entry", \
[FIB_NODE_TYPE_WALK] = "walk", \
[FIB_NODE_TYPE_PATH_LIST] = "path-list", \
[FIB_NODE_TYPE_PATH] = "path", \
+62 -29
View File
@@ -23,6 +23,7 @@
#include <vnet/dpo/lookup_dpo.h>
#include <vnet/adj/adj.h>
#include <vnet/adj/adj_mcast.h>
#include <vnet/fib/fib_path.h>
#include <vnet/fib/fib_node.h>
@@ -960,6 +961,8 @@ fib_path_route_flags_to_cfg_flags (const fib_route_path_t *rpath)
cfg_flags |= FIB_PATH_CFG_FLAG_RESOLVE_HOST;
if (rpath->frp_flags & FIB_ROUTE_PATH_RESOLVE_VIA_ATTACHED)
cfg_flags |= FIB_PATH_CFG_FLAG_RESOLVE_ATTACHED;
if (rpath->frp_flags & FIB_ROUTE_PATH_LOCAL)
cfg_flags |= FIB_PATH_CFG_FLAG_LOCAL;
return (cfg_flags);
}
@@ -1003,28 +1006,25 @@ fib_path_create (fib_node_index_t pl_index,
/*
* deduce the path's tpye from the parementers and save what is needed.
*/
if (~0 != rpath->frp_sw_if_index)
if (path->fp_cfg_flags & FIB_PATH_CFG_FLAG_LOCAL)
{
if (flags & FIB_PATH_CFG_FLAG_LOCAL)
{
path->fp_type = FIB_PATH_TYPE_RECEIVE;
path->receive.fp_interface = rpath->frp_sw_if_index;
path->receive.fp_addr = rpath->frp_addr;
}
else
{
if (ip46_address_is_zero(&rpath->frp_addr))
{
path->fp_type = FIB_PATH_TYPE_ATTACHED;
path->attached.fp_interface = rpath->frp_sw_if_index;
}
else
{
path->fp_type = FIB_PATH_TYPE_ATTACHED_NEXT_HOP;
path->attached_next_hop.fp_interface = rpath->frp_sw_if_index;
path->attached_next_hop.fp_nh = rpath->frp_addr;
}
}
path->fp_type = FIB_PATH_TYPE_RECEIVE;
path->receive.fp_interface = rpath->frp_sw_if_index;
path->receive.fp_addr = rpath->frp_addr;
}
else if (~0 != rpath->frp_sw_if_index)
{
if (ip46_address_is_zero(&rpath->frp_addr))
{
path->fp_type = FIB_PATH_TYPE_ATTACHED;
path->attached.fp_interface = rpath->frp_sw_if_index;
}
else
{
path->fp_type = FIB_PATH_TYPE_ATTACHED_NEXT_HOP;
path->attached_next_hop.fp_interface = rpath->frp_sw_if_index;
path->attached_next_hop.fp_nh = rpath->frp_addr;
}
}
else
{
@@ -1199,7 +1199,7 @@ fib_path_cmp_i (const fib_path_t *path1,
{
res = (path1->fp_type - path2->fp_type);
}
if (path1->fp_nh_proto != path2->fp_nh_proto)
else if (path1->fp_nh_proto != path2->fp_nh_proto)
{
res = (path1->fp_nh_proto - path2->fp_nh_proto);
}
@@ -1770,8 +1770,11 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
break;
}
}
case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
break;
}
break;
case FIB_PATH_TYPE_RECURSIVE:
switch (fct)
{
@@ -1781,13 +1784,15 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
fib_path_recursive_adj_update(path, fct, dpo);
break;
case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
case FIB_FORW_CHAIN_TYPE_ETHERNET:
ASSERT(0);
break;
}
break;
case FIB_PATH_TYPE_DEAG:
switch (fct)
switch (fct)
{
case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
lookup_dpo_add_or_lock_w_table_id(MPLS_FIB_DEFAULT_TABLE_ID,
@@ -1800,7 +1805,9 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
dpo_copy(dpo, &path->fp_dpo);
break;
break;
case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
case FIB_FORW_CHAIN_TYPE_ETHERNET:
ASSERT(0);
break;
@@ -1810,12 +1817,38 @@ fib_path_contribute_forwarding (fib_node_index_t path_index,
dpo_copy(dpo, &path->exclusive.fp_ex_dpo);
break;
case FIB_PATH_TYPE_ATTACHED:
case FIB_PATH_TYPE_RECEIVE:
case FIB_PATH_TYPE_SPECIAL:
ASSERT(0);
switch (fct)
{
case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
case FIB_FORW_CHAIN_TYPE_ETHERNET:
break;
case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
{
adj_index_t ai;
/*
* Create the adj needed for sending IP multicast traffic
*/
ai = adj_mcast_add_or_lock(path->fp_nh_proto,
fib_forw_chain_type_to_link_type(fct),
path->attached.fp_interface);
dpo_set(dpo, DPO_ADJACENCY_MCAST,
fib_forw_chain_type_to_dpo_proto(fct),
ai);
adj_unlock(ai);
}
break;
}
break;
case FIB_PATH_TYPE_RECEIVE:
case FIB_PATH_TYPE_SPECIAL:
dpo_copy(dpo, &path->fp_dpo);
break;
}
}
}
+6 -3
View File
@@ -585,8 +585,11 @@ fib_path_list_resolve (fib_path_list_t *path_list)
path_list = fib_path_list_get(path_list_index);
FIB_PATH_LIST_DBG(path_list, "resovled");
fib_path_list_mk_urpf(path_list);
if (!(path_list->fpl_flags & FIB_PATH_LIST_FLAG_NO_URPF))
{
fib_path_list_mk_urpf(path_list);
}
return (path_list);
}
@@ -1025,14 +1028,14 @@ fib_path_list_copy_and_path_remove (fib_node_index_t orig_path_list_index,
*/
void
fib_path_list_contribute_forwarding (fib_node_index_t path_list_index,
fib_forward_chain_type_t type,
fib_forward_chain_type_t fct,
dpo_id_t *dpo)
{
fib_path_list_t *path_list;
path_list = fib_path_list_get(path_list_index);
fib_path_list_mk_lb(path_list, type, dpo);
fib_path_list_mk_lb(path_list, fct, dpo);
}
/*
+6
View File
@@ -60,6 +60,10 @@ typedef enum fib_path_list_attribute_t_ {
* looped path-list. one path looped implies the whole list is
*/
FIB_PATH_LIST_ATTRIBUTE_LOOPED,
/**
* no uRPF - do not generate unicast RPF list for this path-list
*/
FIB_PATH_LIST_ATTRIBUTE_NO_URPF,
/**
* Marher. Add new flags before this one, and then update it.
*/
@@ -74,6 +78,7 @@ typedef enum fib_path_list_flags_t_ {
FIB_PATH_LIST_FLAG_EXCLUSIVE = (1 << FIB_PATH_LIST_ATTRIBUTE_EXCLUSIVE),
FIB_PATH_LIST_FLAG_RESOLVED = (1 << FIB_PATH_LIST_ATTRIBUTE_RESOLVED),
FIB_PATH_LIST_FLAG_LOOPED = (1 << FIB_PATH_LIST_ATTRIBUTE_LOOPED),
FIB_PATH_LIST_FLAG_NO_URPF = (1 << FIB_PATH_LIST_ATTRIBUTE_NO_URPF),
} fib_path_list_flags_t;
#define FIB_PATH_LIST_ATTRIBUTES { \
@@ -83,6 +88,7 @@ typedef enum fib_path_list_flags_t_ {
[FIB_PATH_LIST_ATTRIBUTE_EXCLUSIVE] = "exclusive", \
[FIB_PATH_LIST_ATTRIBUTE_LOCAL] = "local", \
[FIB_PATH_LIST_ATTRIBUTE_LOOPED] = "looped", \
[FIB_PATH_LIST_ATTRIBUTE_NO_URPF] = "no-uRPF", \
}
#define FOR_EACH_PATH_LIST_ATTRIBUTE(_item) \
+67 -2
View File
@@ -1043,6 +1043,26 @@ fib_table_destroy (fib_table_t *fib_table)
break;
}
}
void
fib_table_walk (u32 fib_index,
fib_protocol_t proto,
fib_table_walk_fn_t fn,
void *ctx)
{
switch (proto)
{
case FIB_PROTOCOL_IP4:
ip4_fib_table_walk(ip4_fib_get(fib_index), fn, ctx);
break;
case FIB_PROTOCOL_IP6:
ip6_fib_table_walk(fib_index, fn, ctx);
break;
case FIB_PROTOCOL_MPLS:
mpls_fib_table_walk(mpls_fib_get(fib_index), fn, ctx);
break;
}
}
void
fib_table_unlock (u32 fib_index,
@@ -1094,11 +1114,56 @@ format_fib_table_name (u8* s, va_list ap)
return (s);
}
/**
* @brief Table flush context. Store the indicies of matching FIB entries
* that need to be removed.
*/
typedef struct fib_table_flush_ctx_t_
{
/**
* The list of entries to flush
*/
fib_node_index_t *ftf_entries;
/**
* The source we are flushing
*/
fib_source_t ftf_source;
} fib_table_flush_ctx_t;
static int
fib_table_flush_cb (fib_node_index_t fib_entry_index,
void *arg)
{
fib_table_flush_ctx_t *ctx = arg;
if (fib_entry_is_sourced(fib_entry_index, ctx->ftf_source))
{
vec_add1(ctx->ftf_entries, fib_entry_index);
}
return (1);
}
void
fib_table_flush (u32 fib_index,
fib_protocol_t proto,
fib_source_t source)
{
// FIXME
ASSERT(0);
fib_node_index_t *fib_entry_index;
fib_table_flush_ctx_t ctx = {
.ftf_entries = NULL,
.ftf_source = source,
};
fib_table_walk(fib_index, proto,
fib_table_flush_cb,
&ctx);
vec_foreach(fib_entry_index, ctx.ftf_entries)
{
fib_entry_delete(*fib_entry_index, source);
}
vec_free(ctx.ftf_entries);
}

Some files were not shown because too many files have changed in this diff Show More