aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeale Ranns <nranns@cisco.com>2017-04-05 08:11:14 -0700
committerDamjan Marion <dmarion.lists@gmail.com>2017-04-06 15:18:44 +0000
commit88fc83eb716bf07f4634de6de5b569f795a56418 (patch)
tree4c8037b62cb6a57209aef4e28ae273d0ba4e40e7
parent5ee51f8ed616f14f3b32ae8857d383fefa02d861 (diff)
BFD-FIB interactions
- single-hop BFD: attach a delegate to the appropriate adjacency - multi-hop BFD [not supported yet]: attach a delegate to the FIB entry. adjacency/fib_entry state tracks the BFD session state. when the state is down the object does not contribute forwarding hence and hence dependent objects will not use it. For example, if a route is ECMP via two adjacencies and one of them is BFD down, then only the other is used to forward (i.e. we don't drop half the traffic). Change-Id: I0ef53e20e73b067001a132cd0a3045408811a822 Signed-off-by: Neale Ranns <nranns@cisco.com>
-rw-r--r--src/vnet.am7
-rw-r--r--src/vnet/adj/adj.c51
-rw-r--r--src/vnet/adj/adj.h15
-rw-r--r--src/vnet/adj/adj_bfd.c184
-rw-r--r--src/vnet/adj/adj_delegate.c144
-rw-r--r--src/vnet/adj/adj_delegate.h104
-rw-r--r--src/vnet/bfd/bfd_main.c23
-rw-r--r--src/vnet/bfd/bfd_main.h44
-rw-r--r--src/vnet/bfd/bfd_udp.h8
-rw-r--r--src/vnet/fib/fib_attached_export.c75
-rw-r--r--src/vnet/fib/fib_attached_export.h4
-rw-r--r--src/vnet/fib/fib_bfd.c197
-rw-r--r--src/vnet/fib/fib_entry.c50
-rw-r--r--src/vnet/fib/fib_entry.h1
-rw-r--r--src/vnet/fib/fib_entry_cover.c45
-rw-r--r--src/vnet/fib/fib_entry_cover.h5
-rw-r--r--src/vnet/fib/fib_entry_delegate.c100
-rw-r--r--src/vnet/fib/fib_entry_delegate.h33
-rw-r--r--src/vnet/fib/fib_path.c52
-rw-r--r--src/vnet/fib/fib_test.c522
-rw-r--r--src/vnet/fib/ip4_fib.c16
-rw-r--r--src/vnet/fib/ip6_fib.c21
-rw-r--r--test/framework.py2
-rw-r--r--test/test_bfd.py104
-rw-r--r--test/vpp_papi_provider.py5
25 files changed, 1662 insertions, 150 deletions
diff --git a/src/vnet.am b/src/vnet.am
index 1d52ae10153..643ae92e3c6 100644
--- a/src/vnet.am
+++ b/src/vnet.am
@@ -936,7 +936,8 @@ libvnet_la_SOURCES += \
vnet/fib/fib_path.c \
vnet/fib/fib_path_ext.c \
vnet/fib/fib_urpf_list.c \
- vnet/fib/fib_attached_export.c
+ vnet/fib/fib_attached_export.c \
+ vnet/fib/fib_bfd.c
nobase_include_HEADERS += \
vnet/fib/fib.h \
@@ -962,7 +963,9 @@ libvnet_la_SOURCES += \
vnet/adj/adj_l2.c \
vnet/adj/adj_nsh.c \
vnet/adj/adj.c \
- vnet/adj/rewrite.c
+ vnet/adj/rewrite.c \
+ vnet/adj/adj_bfd.c \
+ vnet/adj/adj_delegate.c
nobase_include_HEADERS += \
vnet/adj/adj.h \
diff --git a/src/vnet/adj/adj.c b/src/vnet/adj/adj.c
index 7cf9e9d081d..90182006f60 100644
--- a/src/vnet/adj/adj.c
+++ b/src/vnet/adj/adj.c
@@ -18,6 +18,7 @@
#include <vnet/adj/adj_glean.h>
#include <vnet/adj/adj_midchain.h>
#include <vnet/adj/adj_mcast.h>
+#include <vnet/adj/adj_delegate.h>
#include <vnet/fib/fib_node_list.h>
/* Adjacency packet/byte counters indexed by adjacency index. */
@@ -57,13 +58,14 @@ adj_alloc (fib_protocol_t proto)
vlib_validate_combined_counter(&adjacency_counters,
adj_get_index(adj));
- adj->rewrite_header.sw_if_index = ~0;
- adj->lookup_next_index = 0;
-
fib_node_init(&adj->ia_node,
FIB_NODE_TYPE_ADJ);
+
adj->ia_nh_proto = proto;
adj->ia_flags = 0;
+ adj->rewrite_header.sw_if_index = ~0;
+ adj->lookup_next_index = 0;
+ adj->ia_delegates = NULL;
ip4_main.lookup_main.adjacency_heap = adj_pool;
ip6_main.lookup_main.adjacency_heap = adj_pool;
@@ -122,11 +124,19 @@ format_ip_adjacency (u8 * s, va_list * args)
if (fiaf & FORMAT_IP_ADJACENCY_DETAIL)
{
+ adj_delegate_type_t adt;
+ adj_delegate_t *aed;
vlib_counter_t counts;
vlib_get_combined_counter(&adjacency_counters, adj_index, &counts);
s = format (s, "\n counts:[%Ld:%Ld]", counts.packets, counts.bytes);
s = format (s, "\n locks:%d", adj->ia_node.fn_locks);
+ s = format(s, "\n delegates:\n ");
+ FOR_EACH_ADJ_DELEGATE(adj, adt, aed,
+ {
+ s = format(s, " %U\n", format_adj_deletegate, aed);
+ });
+
s = format(s, "\n children:\n ");
s = fib_node_children_format(adj->ia_node.fn_children, s);
}
@@ -173,7 +183,11 @@ adj_last_lock_gone (ip_adjacency_t *adj)
adj_mcast_remove(adj->ia_nh_proto,
adj->rewrite_header.sw_if_index);
break;
- default:
+ case IP_LOOKUP_NEXT_DROP:
+ case IP_LOOKUP_NEXT_PUNT:
+ case IP_LOOKUP_NEXT_LOCAL:
+ case IP_LOOKUP_NEXT_ICMP_ERROR:
+ case IP_LOOKUP_N_NEXT:
/*
* type not stored in any DB from which we need to remove it
*/
@@ -183,6 +197,8 @@ adj_last_lock_gone (ip_adjacency_t *adj)
vlib_worker_thread_barrier_release(vm);
fib_node_deinit(&adj->ia_node);
+ ASSERT(0 == vec_len(adj->ia_delegates));
+ vec_free(adj->ia_delegates);
pool_put(adj_pool, adj);
}
@@ -352,6 +368,33 @@ adj_get_sw_if_index (adj_index_t ai)
}
/**
+ * @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding
+ * 0 is down, !0 is up.
+ */
+int
+adj_is_up (adj_index_t ai)
+{
+ const adj_delegate_t *aed;
+
+ aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
+
+ if (NULL == aed)
+ {
+ /*
+ * no BFD tracking - resolved
+ */
+ return (!0);
+ }
+ else
+ {
+ /*
+ * defer to the state of the BFD tracking
+ */
+ return (ADJ_BFD_STATE_UP == aed->ad_bfd_state);
+ }
+}
+
+/**
* @brief Return the rewrite string of the adjacency
*/
const u8*
diff --git a/src/vnet/adj/adj.h b/src/vnet/adj/adj.h
index af7730f7086..32997c91f92 100644
--- a/src/vnet/adj/adj.h
+++ b/src/vnet/adj/adj.h
@@ -255,6 +255,15 @@ typedef struct ip_adjacency_t_
/* Rewrite in second/third cache lines */
vnet_declare_rewrite (VLIB_BUFFER_PRE_DATA_SIZE);
+
+ /**
+ * more control plane members that do not fit on the first cacheline
+ */
+ /**
+ * A sorted vector of delegates
+ */
+ struct adj_delegate_t_ *ia_delegates;
+
} ip_adjacency_t;
STATIC_ASSERT ((STRUCT_OFFSET_OF (ip_adjacency_t, cacheline0) == 0),
@@ -308,6 +317,12 @@ extern vnet_link_t adj_get_link_type (adj_index_t ai);
extern u32 adj_get_sw_if_index (adj_index_t ai);
/**
+ * @brief Return true if the adjacency is 'UP', i.e. can be used for forwarding.
+ * 0 is down, !0 is up.
+ */
+extern int adj_is_up (adj_index_t ai);
+
+/**
* @brief Return the link type of the adjacency
*/
extern const u8* adj_get_rewrite (adj_index_t ai);
diff --git a/src/vnet/adj/adj_bfd.c b/src/vnet/adj/adj_bfd.c
new file mode 100644
index 00000000000..3d294c4646e
--- /dev/null
+++ b/src/vnet/adj/adj_bfd.c
@@ -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.
+ */
+
+#include <vnet/bfd/bfd_main.h>
+
+#include <vnet/adj/adj_delegate.h>
+#include <vnet/adj/adj_nbr.h>
+#include <vnet/fib/fib_walk.h>
+
+static adj_bfd_state_t
+adj_bfd_bfd_state_to_fib (bfd_state_e bstate)
+{
+ switch (bstate)
+ {
+ case BFD_STATE_up:
+ return (ADJ_BFD_STATE_UP);
+ case BFD_STATE_down:
+ case BFD_STATE_admin_down:
+ case BFD_STATE_init:
+ return (ADJ_BFD_STATE_DOWN);
+ }
+ return (ADJ_BFD_STATE_DOWN);
+}
+
+static void
+adj_bfd_update_walk (adj_index_t ai)
+{
+ /*
+ * initiate a backwalk of dependent children
+ * to notify of the state change of this adj.
+ */
+ fib_node_back_walk_ctx_t ctx = {
+ .fnbw_reason = FIB_NODE_BW_REASON_FLAG_ADJ_UPDATE,
+ };
+ fib_walk_sync(FIB_NODE_TYPE_ADJ, ai, &ctx);
+}
+
+/**
+ * @brief Callback function registered with BFD module to receive notifications
+ * of the CRUD of BFD sessions
+ * would be static but for the fact it's called from the unit-tests
+ */
+void
+adj_bfd_notify (bfd_listen_event_e event,
+ const bfd_session_t *session)
+{
+ const bfd_udp_key_t *key;
+ fib_protocol_t fproto;
+ adj_delegate_t *aed;
+ adj_index_t ai;
+
+ if (BFD_HOP_TYPE_SINGLE != session->hop_type)
+ {
+ /*
+ * multi-hop BFD sessions attach directly to the FIB entry
+ * single-hop adj to the associate adjacency.
+ */
+ return;
+ }
+
+ key = &session->udp.key;
+
+ fproto = (ip46_address_is_ip4 (&key->peer_addr) ?
+ FIB_PROTOCOL_IP4:
+ FIB_PROTOCOL_IP6);
+
+ /*
+ * find the adj that corresponds to the BFD session.
+ */
+ ai = adj_nbr_add_or_lock(fproto,
+ fib_proto_to_link(fproto),
+ &key->peer_addr,
+ key->sw_if_index);
+
+ switch (event)
+ {
+ case BFD_LISTEN_EVENT_CREATE:
+ /*
+ * The creation of a new session
+ */
+ if ((ADJ_INDEX_INVALID != ai) &&
+ (aed = adj_delegate_get(adj_get(ai),
+ ADJ_DELEGATE_BFD)))
+ {
+ /*
+ * already got state for this adj
+ */
+ }
+ else
+ {
+ /*
+ * lock the adj. add the delegate.
+ * Lockinging the adj prevents it being removed and thus maintains
+ * the BFD derived states
+ */
+ adj_lock(ai);
+
+ aed = adj_delegate_find_or_add(adj_get(ai), ADJ_DELEGATE_BFD);
+
+ /*
+ * pretend the session is up and skip the walk.
+ * If we set it down then we get traffic loss on new children.
+ * if we walk then we lose traffic for existing children. Wait
+ * for the first BFD UP/DOWN before we let the session's state
+ * influence forwarding.
+ */
+ aed->ad_bfd_state = ADJ_BFD_STATE_UP;
+ aed->ad_bfd_index = session->bs_idx;
+ }
+ break;
+
+ case BFD_LISTEN_EVENT_UPDATE:
+ /*
+ * state change up/dowm and
+ */
+ aed = adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD);
+
+ if (NULL != aed)
+ {
+ aed->ad_bfd_state = adj_bfd_bfd_state_to_fib(session->local_state);
+ adj_bfd_update_walk(ai);
+ }
+ /*
+ * else
+ * not an adj with BFD state
+ */
+ break;
+
+ case BFD_LISTEN_EVENT_DELETE:
+ /*
+ * session has been removed.
+ */
+
+ if (adj_delegate_get(adj_get(ai), ADJ_DELEGATE_BFD))
+ {
+ /*
+ * has an associated BFD tracking delegate
+ * remove the BFD tracking deletgate, update children, then
+ * unlock the adj
+ */
+ adj_delegate_remove(adj_get(ai), ADJ_DELEGATE_BFD);
+
+ adj_bfd_update_walk(ai);
+ adj_unlock(ai);
+ }
+ /*
+ * else
+ * no BFD associated state
+ */
+ break;
+ }
+
+ /*
+ * unlock match of the add-or-lock at the start
+ */
+ adj_unlock(ai);
+}
+
+static clib_error_t *
+adj_bfd_main_init (vlib_main_t * vm)
+{
+ clib_error_t * error = NULL;
+
+ if ((error = vlib_call_init_function (vm, bfd_main_init)))
+ return (error);
+
+ bfd_register_listener(adj_bfd_notify);
+
+ return (error);
+}
+
+VLIB_INIT_FUNCTION (adj_bfd_main_init);
diff --git a/src/vnet/adj/adj_delegate.c b/src/vnet/adj/adj_delegate.c
new file mode 100644
index 00000000000..701b36e2fd9
--- /dev/null
+++ b/src/vnet/adj/adj_delegate.c
@@ -0,0 +1,144 @@
+/*
+ * 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_delegate.h>
+#include <vnet/adj/adj.h>
+#include <vnet/adj/adj_internal.h>
+
+static adj_delegate_t *
+adj_delegate_find_i (const ip_adjacency_t *adj,
+ adj_delegate_type_t type,
+ u32 *index)
+{
+ adj_delegate_t *delegate;
+ int ii;
+
+ ii = 0;
+ vec_foreach(delegate, adj->ia_delegates)
+ {
+ if (delegate->ad_type == type)
+ {
+ if (NULL != index)
+ *index = ii;
+
+ return (delegate);
+ }
+ else
+ {
+ ii++;
+ }
+ }
+
+ return (NULL);
+}
+
+adj_delegate_t *
+adj_delegate_get (const ip_adjacency_t *adj,
+ adj_delegate_type_t type)
+{
+ return (adj_delegate_find_i(adj, type, NULL));
+}
+
+void
+adj_delegate_remove (ip_adjacency_t *adj,
+ adj_delegate_type_t type)
+{
+ adj_delegate_t *aed;
+ u32 index = ~0;
+
+ aed = adj_delegate_find_i(adj, type, &index);
+
+ ASSERT(NULL != aed);
+
+ vec_del1(adj->ia_delegates, index);
+}
+
+static int
+adj_delegate_cmp_for_sort (void * v1,
+ void * v2)
+{
+ adj_delegate_t *delegate1 = v1, *delegate2 = v2;
+
+ return (delegate1->ad_type - delegate2->ad_type);
+}
+
+static void
+adj_delegate_init (ip_adjacency_t *adj,
+ adj_delegate_type_t type)
+
+{
+ adj_delegate_t delegate = {
+ .ad_adj_index = adj_get_index(adj),
+ .ad_type = type,
+ };
+
+ vec_add1(adj->ia_delegates, delegate);
+ vec_sort_with_function(adj->ia_delegates,
+ adj_delegate_cmp_for_sort);
+}
+
+adj_delegate_t *
+adj_delegate_find_or_add (ip_adjacency_t *adj,
+ adj_delegate_type_t adt)
+{
+ adj_delegate_t *delegate;
+
+ delegate = adj_delegate_get(adj, adt);
+
+ if (NULL == delegate)
+ {
+ adj_delegate_init(adj, adt);
+ }
+
+ return (adj_delegate_get(adj, adt));
+}
+
+/**
+ * typedef for printing a delegate
+ */
+typedef u8 * (*adj_delegate_format_t)(const adj_delegate_t *aed,
+ u8 *s);
+
+/**
+ * Print a delegate that represents BFD tracking
+ */
+static u8 *
+adj_delegate_fmt_bfd (const adj_delegate_t *aed,
+ u8 *s)
+{
+ s = format(s, "BFD:[state:%d index:%d]",
+ aed->ad_bfd_state,
+ aed->ad_bfd_index);
+
+ return (s);
+}
+
+/**
+ * A delegate type to formatter map
+ */
+static adj_delegate_format_t aed_formatters[] =
+{
+ [ADJ_DELEGATE_BFD] = adj_delegate_fmt_bfd,
+};
+
+u8 *
+format_adj_deletegate (u8 * s, va_list * args)
+{
+ adj_delegate_t *aed;
+
+ aed = va_arg (*args, adj_delegate_t *);
+
+ return (aed_formatters[aed->ad_type](aed, s));
+}
diff --git a/src/vnet/adj/adj_delegate.h b/src/vnet/adj/adj_delegate.h
new file mode 100644
index 00000000000..176512039d6
--- /dev/null
+++ b/src/vnet/adj/adj_delegate.h
@@ -0,0 +1,104 @@
+/*
+ * 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 __ADJ_DELEGATE_T__
+#define __ADJ_DELEGATE_T__
+
+#include <vnet/adj/adj.h>
+
+/**
+ * Delegate types
+ */
+typedef enum adj_delegate_type_t_ {
+ /**
+ * BFD session state
+ */
+ ADJ_DELEGATE_BFD,
+} adj_delegate_type_t;
+
+#define FOR_EACH_ADJ_DELEGATE(_adj, _adt, _aed, _body) \
+{ \
+ for (_adt = ADJ_DELEGATE_BFD; \
+ _adt <= ADJ_DELEGATE_BFD; \
+ _adt++) \
+ { \
+ _aed = adj_delegate_get(_adj, _adt); \
+ if (NULL != _aed) { \
+ _body; \
+ } \
+ } \
+}
+
+/**
+ * Distillation of the BFD session states into a go/no-go for using
+ * the associated tracked adjacency
+ */
+typedef enum adj_bfd_state_t_
+{
+ ADJ_BFD_STATE_DOWN,
+ ADJ_BFD_STATE_UP,
+} adj_bfd_state_t;
+
+/**
+ * A Delagate is a means to implement the Delagation design pattern;
+ * the extension of an object's functionality through the composition of,
+ * and delgation to, other objects.
+ * These 'other' objects are delegates. Delagates are thus attached to
+ * ADJ objects to extend their functionality.
+ */
+typedef struct adj_delegate_t_
+{
+ /**
+ * The ADJ entry object to which the delagate is attached
+ */
+ adj_index_t ad_adj_index;
+
+ /**
+ * The delagate type
+ */
+ adj_delegate_type_t ad_type;
+
+ /**
+ * A union of data for the different delegate types
+ */
+ union
+ {
+ /**
+ * BFD delegate daa
+ */
+ struct {
+ /**
+ * BFD session state
+ */
+ adj_bfd_state_t ad_bfd_state;
+ /**
+ * BFD session index
+ */
+ u32 ad_bfd_index;
+ };
+ };
+} adj_delegate_t;
+
+extern void adj_delegate_remove(ip_adjacency_t *adj,
+ adj_delegate_type_t type);
+
+extern adj_delegate_t *adj_delegate_find_or_add(ip_adjacency_t *adj,
+ adj_delegate_type_t fdt);
+extern adj_delegate_t *adj_delegate_get(const ip_adjacency_t *adj,
+ adj_delegate_type_t type);
+
+extern u8 *format_adj_deletegate(u8 * s, va_list * args);
+
+#endif
diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c
index 2b70a20c9fd..66b31ce502c 100644
--- a/src/vnet/bfd/bfd_main.c
+++ b/src/vnet/bfd/bfd_main.c
@@ -101,6 +101,7 @@ bfd_set_defaults (bfd_main_t * bm, bfd_session_t * bs)
bs->local_diag = BFD_DIAG_CODE_no_diag;
bs->remote_state = BFD_STATE_down;
bs->remote_discr = 0;
+ bs->hop_type = BFD_HOP_TYPE_SINGLE;
bs->config_desired_min_tx_usec = BFD_DEFAULT_DESIRED_MIN_TX_USEC;
bs->config_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
bs->effective_desired_min_tx_clocks = bm->default_desired_min_tx_clocks;
@@ -387,6 +388,17 @@ bfd_set_remote_required_min_echo_rx (bfd_main_t * bm, bfd_session_t * bs,
}
}
+static void
+bfd_notify_listeners (bfd_main_t * bm,
+ bfd_listen_event_e event, const bfd_session_t * bs)
+{
+ bfd_notify_fn_t *fn;
+ vec_foreach (fn, bm->listeners)
+ {
+ (*fn) (event, bs);
+ }
+}
+
void
bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
{
@@ -396,6 +408,7 @@ bfd_session_start (bfd_main_t * bm, bfd_session_t * bs)
bfd_recalc_tx_interval (bm, bs);
vlib_process_signal_event (bm->vlib_main, bm->bfd_process_node_index,
BFD_EVENT_NEW_SESSION, bs->bs_idx);
+ bfd_notify_listeners (bm, BFD_LISTEN_EVENT_CREATE, bs);
}
void
@@ -533,6 +546,7 @@ bfd_on_state_change (bfd_main_t * bm, bfd_session_t * bs, u64 now,
bfd_set_timer (bm, bs, now, handling_wakeup);
break;
}
+ bfd_notify_listeners (bm, BFD_LISTEN_EVENT_UPDATE, bs);
}
static void
@@ -1121,6 +1135,14 @@ bfd_hw_interface_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
VNET_HW_INTERFACE_LINK_UP_DOWN_FUNCTION (bfd_hw_interface_up_down);
+void
+bfd_register_listener (bfd_notify_fn_t fn)
+{
+ bfd_main_t *bm = &bfd_main;
+
+ vec_add1 (bm->listeners, fn);
+}
+
/*
* setup function
*/
@@ -1180,6 +1202,7 @@ bfd_get_session (bfd_main_t * bm, bfd_transport_e t)
void
bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
{
+ bfd_notify_listeners (bm, BFD_LISTEN_EVENT_DELETE, bs);
if (bs->auth.curr_key)
{
--bs->auth.curr_key->use_count;
diff --git a/src/vnet/bfd/bfd_main.h b/src/vnet/bfd/bfd_main.h
index d722a55274c..93adac3dc8d 100644
--- a/src/vnet/bfd/bfd_main.h
+++ b/src/vnet/bfd/bfd_main.h
@@ -66,6 +66,20 @@ typedef enum
#undef F
} bfd_poll_state_e;
+/**
+ * hop types
+ */
+#define foreach_bfd_hop(F) \
+ F (SINGLE, "single") \
+ F (MULTI, "multi") \
+
+typedef enum
+{
+#define F(sym, str) BFD_HOP_TYPE_##sym,
+ foreach_bfd_hop (F)
+#undef F
+} bfd_hop_type_e;
+
typedef struct bfd_session_s
{
/** index in bfd_main.sessions pool */
@@ -77,6 +91,9 @@ typedef struct bfd_session_s
/** remote session state */
bfd_state_e remote_state;
+ /** BFD hop type */
+ bfd_hop_type_e hop_type;
+
/** local diagnostics */
bfd_diag_code_e local_diag;
@@ -220,6 +237,26 @@ typedef struct bfd_session_s
};
} bfd_session_t;
+/**
+ * listener events
+ */
+#define foreach_bfd_listen_event(F) \
+ F (CREATE, "sesion-created") \
+ F (UPDATE, "session-updated") \
+ F (DELETE, "session-deleted")
+
+typedef enum
+{
+#define F(sym, str) BFD_LISTEN_EVENT_##sym,
+ foreach_bfd_listen_event (F)
+#undef F
+} bfd_listen_event_e;
+
+/**
+ * session nitification call back function type
+ */
+typedef void (*bfd_notify_fn_t) (bfd_listen_event_e, const bfd_session_t *);
+
typedef struct
{
/** pool of bfd sessions context data */
@@ -259,6 +296,8 @@ typedef struct
/** hashmap - index in pool auth_keys by conf_key_id */
u32 *auth_key_by_conf_key_id;
+ /** A vector of callback notification functions */
+ bfd_notify_fn_t *listeners;
} bfd_main_t;
extern bfd_main_t bfd_main;
@@ -345,6 +384,11 @@ const char *bfd_poll_state_string (bfd_poll_state_e state);
*/
#define BFD_REQUIRED_MIN_RX_USEC_WHILE_ECHO USEC_PER_SECOND
+/**
+ * Register a callback function to receive session notifications.
+ */
+void bfd_register_listener (bfd_notify_fn_t fn);
+
#endif /* __included_bfd_main_h__ */
/*
diff --git a/src/vnet/bfd/bfd_udp.h b/src/vnet/bfd/bfd_udp.h
index a4adbadf861..87868104f98 100644
--- a/src/vnet/bfd/bfd_udp.h
+++ b/src/vnet/bfd/bfd_udp.h
@@ -27,8 +27,12 @@
/* *INDENT-OFF* */
/** identifier of BFD session based on UDP transport only */
typedef CLIB_PACKED (struct {
- /** interface to which the session is tied */
- u32 sw_if_index;
+ union {
+ /** interface to which the session is tied - single-hop */
+ u32 sw_if_index;
+ /** the FIB index the peer is in - multi-hop*/
+ u32 fib_index;
+ };
/** local address */
ip46_address_t local_addr;
/** peer address */
diff --git a/src/vnet/fib/fib_attached_export.c b/src/vnet/fib/fib_attached_export.c
index 715e63e7246..cc8ebc8653e 100644
--- a/src/vnet/fib/fib_attached_export.c
+++ b/src/vnet/fib/fib_attached_export.c
@@ -514,67 +514,52 @@ fib_attached_export_cover_update (fib_entry_t *fib_entry)
}
u8*
-fib_ae_import_format (fib_entry_t *fib_entry,
+fib_ae_import_format (fib_node_index_t impi,
u8* s)
{
- fib_entry_delegate_t *fed;
-
- fed = fib_entry_delegate_get(fib_entry,
- FIB_ENTRY_DELEGATE_ATTACHED_IMPORT);
-
- if (NULL != fed)
- {
- fib_node_index_t *index;
- fib_ae_import_t *import;
-
- import = pool_elt_at_index(fib_ae_import_pool, fed->fd_index);
+ fib_node_index_t *index;
+ fib_ae_import_t *import;
- s = format(s, "\n Attached-Import:%d:[", (import - fib_ae_import_pool));
- s = format(s, "export-prefix:%U ", format_fib_prefix, &import->faei_prefix);
- s = format(s, "export-entry:%d ", import->faei_export_entry);
- s = format(s, "export-sibling:%d ", import->faei_export_sibling);
- s = format(s, "exporter:%d ", import->faei_exporter);
- s = format(s, "export-fib:%d ", import->faei_export_fib);
+ import = pool_elt_at_index(fib_ae_import_pool, impi);
- s = format(s, "import-entry:%d ", import->faei_import_entry);
- s = format(s, "import-fib:%d ", import->faei_import_fib);
+ s = format(s, "\n Attached-Import:%d:[", (import - fib_ae_import_pool));
+ s = format(s, "export-prefix:%U ", format_fib_prefix, &import->faei_prefix);
+ s = format(s, "export-entry:%d ", import->faei_export_entry);
+ s = format(s, "export-sibling:%d ", import->faei_export_sibling);
+ s = format(s, "exporter:%d ", import->faei_exporter);
+ s = format(s, "export-fib:%d ", import->faei_export_fib);
+
+ s = format(s, "import-entry:%d ", import->faei_import_entry);
+ s = format(s, "import-fib:%d ", import->faei_import_fib);
- s = format(s, "importeds:[");
- vec_foreach(index, import->faei_importeds)
- {
- s = format(s, "%d, ", *index);
- }
- s = format(s, "]]");
+ s = format(s, "importeds:[");
+ vec_foreach(index, import->faei_importeds)
+ {
+ s = format(s, "%d, ", *index);
}
+ s = format(s, "]]");
return (s);
}
u8*
-fib_ae_export_format (fib_entry_t *fib_entry,
+fib_ae_export_format (fib_node_index_t expi,
u8* s)
{
- fib_entry_delegate_t *fed;
-
- fed = fib_entry_delegate_get(fib_entry,
- FIB_ENTRY_DELEGATE_ATTACHED_EXPORT);
-
- if (NULL != fed)
- {
- fib_node_index_t *index;
- fib_ae_export_t *export;
+ fib_node_index_t *index;
+ fib_ae_export_t *export;
- export = pool_elt_at_index(fib_ae_export_pool, fed->fd_list);
+ export = pool_elt_at_index(fib_ae_export_pool, expi);
- s = format(s, "\n Attached-Export:%d:[", (export - fib_ae_export_pool));
- s = format(s, "export-entry:%d ", export->faee_ei);
+ s = format(s, "\n Attached-Export:%d:[", (export - fib_ae_export_pool));
+ s = format(s, "export-entry:%d ", export->faee_ei);
- s = format(s, "importers:[");
- vec_foreach(index, export->faee_importers)
- {
- s = format(s, "%d, ", *index);
- }
- s = format(s, "]]");
+ s = format(s, "importers:[");
+ vec_foreach(index, export->faee_importers)
+ {
+ s = format(s, "%d, ", *index);
}
+ s = format(s, "]]");
+
return (s);
}
diff --git a/src/vnet/fib/fib_attached_export.h b/src/vnet/fib/fib_attached_export.h
index fa28a6e13b8..d4c2b57c379 100644
--- a/src/vnet/fib/fib_attached_export.h
+++ b/src/vnet/fib/fib_attached_export.h
@@ -51,7 +51,7 @@ extern void fib_attached_export_covered_removed(fib_entry_t *cover,
extern void fib_attached_export_cover_change(fib_entry_t *fib_entry);
extern void fib_attached_export_cover_update(fib_entry_t *fib_entry);
-extern u8* fib_ae_import_format(fib_entry_t *fib_entry, u8*s);
-extern u8* fib_ae_export_format(fib_entry_t *fib_entry, u8*s);
+extern u8* fib_ae_import_format(fib_node_index_t impi, u8*s);
+extern u8* fib_ae_export_format(fib_node_index_t expi, u8*s);
#endif
diff --git a/src/vnet/fib/fib_bfd.c b/src/vnet/fib/fib_bfd.c
new file mode 100644
index 00000000000..e5affb8de49
--- /dev/null
+++ b/src/vnet/fib/fib_bfd.c
@@ -0,0 +1,197 @@
+/*
+ * 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/bfd/bfd_main.h>
+
+#include <vnet/fib/fib_entry_delegate.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_table.h>
+#include <vnet/fib/fib_walk.h>
+
+static fib_bfd_state_t
+fib_bfd_bfd_state_to_fib (bfd_state_e bstate)
+{
+ switch (bstate)
+ {
+ case BFD_STATE_up:
+ return (FIB_BFD_STATE_UP);
+ case BFD_STATE_down:
+ case BFD_STATE_admin_down:
+ case BFD_STATE_init:
+ return (FIB_BFD_STATE_DOWN);
+ }
+ return (FIB_BFD_STATE_DOWN);
+}
+
+static void
+fib_bfd_update_walk (fib_node_index_t fei)
+{
+ /*
+ * initiate a backwalk of dependent children
+ * to notify of the state change of this entry.
+ */
+ fib_node_back_walk_ctx_t ctx = {
+ .fnbw_reason = FIB_NODE_BW_REASON_FLAG_EVALUATE,
+ };
+ fib_walk_sync(FIB_NODE_TYPE_ENTRY, fei, &ctx);
+}
+
+/**
+ * @brief Callback function registered with BFD module to receive notifications
+ * of the CRUD of BFD sessions
+ * would be static but for the fact it's called from the unit-tests
+ */
+void
+fib_bfd_notify (bfd_listen_event_e event,
+ const bfd_session_t *session)
+{
+ fib_entry_delegate_t *fed;
+ const bfd_udp_key_t *key;
+ fib_node_index_t fei;
+
+ if (BFD_HOP_TYPE_MULTI != session->hop_type)
+ {
+ /*
+ * multi-hop BFD sessions attach directly to the FIB entry
+ * single-hop adj to the associate adjacency.
+ */
+ return;
+ }
+
+ key = &session->udp.key;
+
+ fib_prefix_t pfx = {
+ .fp_addr = key->peer_addr,
+ .fp_proto = (ip46_address_is_ip4 (&key->peer_addr) ?
+ FIB_PROTOCOL_IP4:
+ FIB_PROTOCOL_IP6),
+ .fp_len = (ip46_address_is_ip4 (&key->peer_addr) ?
+ 32:
+ 128),
+ };
+
+ /*
+ * get the FIB entry
+ */
+ fei = fib_table_lookup_exact_match(key->fib_index, &pfx);
+
+ switch (event)
+ {
+ case BFD_LISTEN_EVENT_CREATE:
+ /*
+ * The creation of a new session
+ */
+ if ((FIB_NODE_INDEX_INVALID != fei) &&
+ (fed = fib_entry_delegate_get(fib_entry_get(fei),
+ FIB_ENTRY_DELEGATE_BFD)))
+ {
+ /*
+ * already got state for this entry
+ */
+ }
+ else
+ {
+ /*
+ * source and lock the entry. add the delegate
+ */
+ fei = fib_table_entry_special_add(key->fib_index,
+ &pfx,
+ FIB_SOURCE_RR,
+ FIB_ENTRY_FLAG_NONE,
+ ADJ_INDEX_INVALID);
+ fib_entry_lock(fei);
+
+ fed = fib_entry_delegate_find_or_add(fib_entry_get(fei),
+ FIB_ENTRY_DELEGATE_BFD);
+
+ /*
+ * pretend the session is up and skip the walk.
+ * If we set it down then we get traffic loss on new children.
+ * if we walk then we lose traffic for existing children. Wait
+ * for the first BFD UP/DOWN before we let the session's state
+ * influence forwarding.
+ */
+ fed->fd_bfd_state = FIB_BFD_STATE_UP;
+ }
+ break;
+
+ case BFD_LISTEN_EVENT_UPDATE:
+ /*
+ * state change up/dowm and
+ */
+ ASSERT(FIB_NODE_INDEX_INVALID != fei);
+
+ fed = fib_entry_delegate_get(fib_entry_get(fei),
+ FIB_ENTRY_DELEGATE_BFD);
+
+ if (NULL != fed)
+ {
+ fed->fd_bfd_state = fib_bfd_bfd_state_to_fib(session->local_state);
+ fib_bfd_update_walk(fei);
+ }
+ /*
+ * else
+ * no BFD state
+ */
+ break;
+
+ case BFD_LISTEN_EVENT_DELETE:
+ /*
+ * session has been removed.
+ */
+ if (FIB_NODE_INDEX_INVALID == fei)
+ {
+ /*
+ * no FIB entry
+ */
+ }
+ else if (fib_entry_delegate_get(fib_entry_get(fei),
+ FIB_ENTRY_DELEGATE_BFD))
+ {
+ /*
+ * has an associated BFD tracking delegate
+ * usource the entry and remove the BFD tracking deletgate
+ */
+ fib_entry_delegate_remove(fib_entry_get(fei),
+ FIB_ENTRY_DELEGATE_BFD);
+ fib_bfd_update_walk(fei);
+
+ fib_table_entry_special_remove(key->fib_index,
+ &pfx,
+ FIB_SOURCE_RR);
+ fib_entry_unlock(fei);
+ }
+ /*
+ * else
+ * no BFD associated state
+ */
+ break;
+ }
+}
+
+static clib_error_t *
+fib_bfd_main_init (vlib_main_t * vm)
+{
+ clib_error_t * error = NULL;
+
+ if ((error = vlib_call_init_function (vm, bfd_main_init)))
+ return (error);
+
+ bfd_register_listener(fib_bfd_notify);
+
+ return (error);
+}
+
+VLIB_INIT_FUNCTION (fib_bfd_main_init);
diff --git a/src/vnet/fib/fib_entry.c b/src/vnet/fib/fib_entry.c
index 6ac5461d76c..dac1fce995f 100644
--- a/src/vnet/fib/fib_entry.c
+++ b/src/vnet/fib/fib_entry.c
@@ -99,7 +99,6 @@ format_fib_entry (u8 * s, va_list * args)
fib_entry_src_t *src;
fib_node_index_t fei;
fib_source_t source;
- u32 n_covered;
int level;
fei = va_arg (*args, fib_node_index_t);
@@ -143,14 +142,6 @@ format_fib_entry (u8 * s, va_list * args)
}
}));
- n_covered = fib_entry_cover_get_size(fib_entry);
- if (n_covered > 0) {
- s = format(s, "\n tracking %d covered: ", n_covered);
- s = fib_entry_cover_list_format(fib_entry, s);
- }
- s = fib_ae_import_format(fib_entry, s);
- s = fib_ae_export_format(fib_entry, s);
-
s = format (s, "\n forwarding: ");
}
else
@@ -179,20 +170,17 @@ format_fib_entry (u8 * s, va_list * args)
fib_entry_delegate_type_t fdt;
fib_entry_delegate_t *fed;
- FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
+ s = format (s, " Delegates:\n");
+ FOR_EACH_DELEGATE(fib_entry, fdt, fed,
{
- s = format(s, " %U-chain\n %U",
- format_fib_forw_chain_type,
- fib_entry_delegate_type_to_chain_type(fdt),
- format_dpo_id, &fed->fd_dpo, 2);
- s = format(s, "\n");
+ s = format(s, " %U\n", format_fib_entry_deletegate, fed);
});
}
}
if (level >= FIB_ENTRY_FORMAT_DETAIL2)
{
- s = format(s, "\nchildren:");
+ s = format(s, " Children:");
s = fib_node_children_format(fib_entry->fe_node.fn_children, s);
}
@@ -1339,6 +1327,36 @@ fib_entry_get_best_source (fib_node_index_t entry_index)
return (fib_entry_src_get_source(bsrc));
}
+/**
+ * Return !0 is the entry is reoslved, i.e. will return a valid forwarding
+ * chain
+ */
+int
+fib_entry_is_resolved (fib_node_index_t fib_entry_index)
+{
+ fib_entry_delegate_t *fed;
+ fib_entry_t *fib_entry;
+
+ fib_entry = fib_entry_get(fib_entry_index);
+
+ fed = fib_entry_delegate_get(fib_entry, FIB_ENTRY_DELEGATE_BFD);
+
+ if (NULL == fed)
+ {
+ /*
+ * no BFD tracking - resolved
+ */
+ return (!0);
+ }
+ else
+ {
+ /*
+ * defer to the state of the BFD tracking
+ */
+ return (FIB_BFD_STATE_UP == fed->fd_bfd_state);
+ }
+}
+
static int
fib_ip4_address_compare (const ip4_address_t * a1,
const ip4_address_t * a2)
diff --git a/src/vnet/fib/fib_entry.h b/src/vnet/fib/fib_entry.h
index 12fa9eb4e9b..a3f75e6084a 100644
--- a/src/vnet/fib/fib_entry.h
+++ b/src/vnet/fib/fib_entry.h
@@ -525,6 +525,7 @@ extern int fib_entry_is_sourced(fib_node_index_t fib_entry_index,
fib_source_t source);
extern fib_node_index_t fib_entry_get_path_list(fib_node_index_t fib_entry_index);
+extern int fib_entry_is_resolved(fib_node_index_t fib_entry_index);
extern void fib_entry_module_init(void);
diff --git a/src/vnet/fib/fib_entry_cover.c b/src/vnet/fib/fib_entry_cover.c
index 147c5daa4fd..814df578a62 100644
--- a/src/vnet/fib/fib_entry_cover.c
+++ b/src/vnet/fib/fib_entry_cover.c
@@ -106,51 +106,6 @@ fib_entry_cover_walk (fib_entry_t *cover,
&ctx);
}
-u32
-fib_entry_cover_get_size (fib_entry_t *cover)
-{
- fib_entry_delegate_t *fed;
-
- fed = fib_entry_delegate_get(cover, FIB_ENTRY_DELEGATE_COVERED);
-
- if (NULL == fed)
- return (0);
-
- return (fib_node_list_get_size(fed->fd_list));
-}
-
-typedef struct fib_entry_cover_list_format_ctx_t_ {
- u8 *s;
-} fib_entry_cover_list_format_ctx_t;
-
-static int
-fib_entry_covered_list_format_one (fib_entry_t *cover,
- fib_node_index_t covered,
- void *args)
-{
- fib_entry_cover_list_format_ctx_t * ctx = args;
-
- ctx->s = format(ctx->s, "%d, ", covered);
-
- /* continue */
- return (1);
-}
-
-u8*
-fib_entry_cover_list_format (fib_entry_t *fib_entry,
- u8 *s)
-{
- fib_entry_cover_list_format_ctx_t ctx = {
- .s = s,
- };
-
- fib_entry_cover_walk(fib_entry,
- fib_entry_covered_list_format_one,
- &ctx);
-
- return (ctx.s);
-}
-
static int
fib_entry_cover_change_one (fib_entry_t *cover,
fib_node_index_t covered,
diff --git a/src/vnet/fib/fib_entry_cover.h b/src/vnet/fib/fib_entry_cover.h
index fbbbc211dc9..500d5b33244 100644
--- a/src/vnet/fib/fib_entry_cover.h
+++ b/src/vnet/fib/fib_entry_cover.h
@@ -39,9 +39,4 @@ extern void fib_entry_cover_change_notify(fib_node_index_t cover_index,
fib_node_index_t covered_index);
extern void fib_entry_cover_update_notify(fib_entry_t *cover);
-extern u32 fib_entry_cover_get_size(fib_entry_t *cover);
-
-extern u8* fib_entry_cover_list_format(fib_entry_t *fib_entry,
- u8 *s);
-
#endif
diff --git a/src/vnet/fib/fib_entry_delegate.c b/src/vnet/fib/fib_entry_delegate.c
index 70840b160a1..41af14f2245 100644
--- a/src/vnet/fib/fib_entry_delegate.c
+++ b/src/vnet/fib/fib_entry_delegate.c
@@ -15,6 +15,7 @@
#include <vnet/fib/fib_entry_delegate.h>
#include <vnet/fib/fib_entry.h>
+#include <vnet/fib/fib_attached_export.h>
static fib_entry_delegate_t *
fib_entry_delegate_find_i (const fib_entry_t *fib_entry,
@@ -149,8 +150,107 @@ fib_entry_delegate_type_to_chain_type (fib_entry_delegate_type_t fdt)
case FIB_ENTRY_DELEGATE_COVERED:
case FIB_ENTRY_DELEGATE_ATTACHED_IMPORT:
case FIB_ENTRY_DELEGATE_ATTACHED_EXPORT:
+ case FIB_ENTRY_DELEGATE_BFD:
break;
}
ASSERT(0);
return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
}
+
+/**
+ * typedef for printing a delegate
+ */
+typedef u8 * (*fib_entry_delegate_format_t)(const fib_entry_delegate_t *fed,
+ u8 *s);
+
+/**
+ * Print a delegate that represents a forwarding chain
+ */
+static u8 *
+fib_entry_delegate_fmt_fwd_chain (const fib_entry_delegate_t *fed,
+ u8 *s)
+{
+ s = format(s, "%U-chain\n %U",
+ format_fib_forw_chain_type,
+ fib_entry_delegate_type_to_chain_type(fed->fd_type),
+ format_dpo_id, &fed->fd_dpo, 2);
+
+ return (s);
+}
+
+/**
+ * Print a delegate that represents cover tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_covered (const fib_entry_delegate_t *fed,
+ u8 *s)
+{
+ s = format(s, "covered:[");
+ s = fib_node_children_format(fed->fd_list, s);
+ s = format(s, "]");
+
+ return (s);
+}
+
+/**
+ * Print a delegate that represents attached-import tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_import (const fib_entry_delegate_t *fed,
+ u8 *s)
+{
+ s = format(s, "import:%U", fib_ae_import_format, fed->fd_index);
+
+ return (s);
+}
+
+/**
+ * Print a delegate that represents attached-export tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_export (const fib_entry_delegate_t *fed,
+ u8 *s)
+{
+ s = format(s, "export:%U", fib_ae_export_format, fed->fd_index);
+
+ return (s);
+}
+
+/**
+ * Print a delegate that represents BFD tracking
+ */
+static u8 *
+fib_entry_delegate_fmt_bfd (const fib_entry_delegate_t *fed,
+ u8 *s)
+{
+ s = format(s, "BFD:%d", fed->fd_bfd_state);
+
+ return (s);
+}
+
+/**
+ * A delegate type to formatter map
+ */
+static fib_entry_delegate_format_t fed_formatters[] =
+{
+ [FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP6] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_CHAIN_MPLS_EOS] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_CHAIN_MPLS_NON_EOS] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_CHAIN_ETHERNET] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_CHAIN_NSH] = fib_entry_delegate_fmt_fwd_chain,
+ [FIB_ENTRY_DELEGATE_COVERED] = fib_entry_delegate_fmt_covered,
+ [FIB_ENTRY_DELEGATE_ATTACHED_IMPORT] = fib_entry_delegate_fmt_import,
+ [FIB_ENTRY_DELEGATE_ATTACHED_EXPORT] = fib_entry_delegate_fmt_export,
+ [FIB_ENTRY_DELEGATE_BFD] = fib_entry_delegate_fmt_bfd,
+};
+
+u8 *
+format_fib_entry_deletegate (u8 * s, va_list * args)
+{
+ fib_entry_delegate_t *fed;
+
+ fed = va_arg (*args, fib_entry_delegate_t *);
+
+ return (fed_formatters[fed->fd_type](fed, s));
+}
diff --git a/src/vnet/fib/fib_entry_delegate.h b/src/vnet/fib/fib_entry_delegate.h
index d9183c5f181..333d357c120 100644
--- a/src/vnet/fib/fib_entry_delegate.h
+++ b/src/vnet/fib/fib_entry_delegate.h
@@ -43,6 +43,10 @@ typedef enum fib_entry_delegate_type_t_ {
*/
FIB_ENTRY_DELEGATE_COVERED,
/**
+ * BFD session state
+ */
+ FIB_ENTRY_DELEGATE_BFD,
+ /**
* Attached import/export functionality
*/
FIB_ENTRY_DELEGATE_ATTACHED_IMPORT,
@@ -61,6 +65,28 @@ typedef enum fib_entry_delegate_type_t_ {
} \
} \
}
+#define FOR_EACH_DELEGATE(_entry, _fdt, _fed, _body) \
+{ \
+ for (_fdt = FIB_ENTRY_DELEGATE_CHAIN_UNICAST_IP4; \
+ _fdt <= FIB_ENTRY_DELEGATE_ATTACHED_EXPORT; \
+ _fdt++) \
+ { \
+ _fed = fib_entry_delegate_get(_entry, _fdt); \
+ if (NULL != _fed) { \
+ _body; \
+ } \
+ } \
+}
+
+/**
+ * Distillation of the BFD session states into a go/no-go for using
+ * the associated tracked FIB entry
+ */
+typedef enum fib_bfd_state_t_
+{
+ FIB_BFD_STATE_UP,
+ FIB_BFD_STATE_DOWN,
+} fib_bfd_state_t;
/**
* A Delagate is a means to implmenet the Delagation design pattern; the extension of an
@@ -103,6 +129,11 @@ typedef struct fib_entry_delegate_t_
* For the cover tracking. The node list;
*/
fib_node_list_t fd_list;
+
+ /**
+ * BFD state
+ */
+ fib_bfd_state_t fd_bfd_state;
};
} fib_entry_delegate_t;
@@ -122,4 +153,6 @@ extern fib_forward_chain_type_t fib_entry_delegate_type_to_chain_type(
extern fib_entry_delegate_type_t fib_entry_chain_type_to_delegate_type(
fib_forward_chain_type_t type);
+extern u8 *format_fib_entry_deletegate(u8 * s, va_list * args);
+
#endif
diff --git a/src/vnet/fib/fib_path.c b/src/vnet/fib/fib_path.c
index 928a9d432be..6b202a97824 100644
--- a/src/vnet/fib/fib_path.c
+++ b/src/vnet/fib/fib_path.c
@@ -558,12 +558,6 @@ fib_path_attached_next_hop_set (fib_path_t *path)
* resolve directly via the adjacnecy discribed by the
* interface and next-hop
*/
- if (!vnet_sw_interface_is_admin_up(vnet_get_main(),
- path->attached_next_hop.fp_interface))
- {
- path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
- }
-
dpo_set(&path->fp_dpo,
DPO_ADJACENCY,
fib_proto_to_dpo(path->fp_nh_proto),
@@ -578,6 +572,13 @@ fib_path_attached_next_hop_set (fib_path_t *path)
path->fp_sibling = adj_child_add(path->fp_dpo.dpoi_index,
FIB_NODE_TYPE_PATH,
fib_path_get_index(path));
+
+ if (!vnet_sw_interface_is_admin_up(vnet_get_main(),
+ path->attached_next_hop.fp_interface) ||
+ !adj_is_up(path->fp_dpo.dpoi_index))
+ {
+ path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+ }
}
/*
@@ -653,6 +654,19 @@ fib_path_recursive_adj_update (fib_path_t *path,
load_balance_map_path_state_change(fib_path_get_index(path));
}
}
+ /*
+ * check for over-riding factors on the FIB entry itself
+ */
+ if (!fib_entry_is_resolved(path->fp_via_fib))
+ {
+ path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+ dpo_copy(&via_dpo, drop_dpo_get(fib_proto_to_dpo(path->fp_nh_proto)));
+
+ /*
+ * PIC edge trigger. let the load-balance maps know
+ */
+ load_balance_map_path_state_change(fib_path_get_index(path));
+ }
/*
* update the path's contributed DPO
@@ -855,15 +869,16 @@ FIXME comment
vnet_get_main(),
path->attached_next_hop.fp_interface);
- if (if_is_up)
- {
- path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED;
- }
-
ai = fib_path_attached_next_hop_get_adj(
path,
fib_proto_to_link(path->fp_nh_proto));
+ path->fp_oper_flags &= ~FIB_PATH_OPER_FLAG_RESOLVED;
+ if (if_is_up && adj_is_up(ai))
+ {
+ path->fp_oper_flags |= FIB_PATH_OPER_FLAG_RESOLVED;
+ }
+
dpo_set(&path->fp_dpo, DPO_ADJACENCY,
fib_proto_to_dpo(path->fp_nh_proto),
ai);
@@ -1684,11 +1699,11 @@ fib_path_contribute_urpf (fib_node_index_t path_index,
{
fib_path_t *path;
- if (!fib_path_is_resolved(path_index))
- return;
-
path = fib_path_get(path_index);
+ /*
+ * resolved and unresolved paths contribute to the RPF list.
+ */
switch (path->fp_type)
{
case FIB_PATH_TYPE_ATTACHED_NEXT_HOP:
@@ -1700,7 +1715,14 @@ fib_path_contribute_urpf (fib_node_index_t path_index,
break;
case FIB_PATH_TYPE_RECURSIVE:
- fib_entry_contribute_urpf(path->fp_via_fib, urpf);
+ if (FIB_NODE_INDEX_INVALID != path->fp_via_fib)
+ {
+ /*
+ * there's unresolved due to constraints, and there's unresolved
+ * due to ain't go no via. can't do nowt w'out via.
+ */
+ fib_entry_contribute_urpf(path->fp_via_fib, urpf);
+ }
break;
case FIB_PATH_TYPE_EXCLUSIVE:
diff --git a/src/vnet/fib/fib_test.c b/src/vnet/fib/fib_test.c
index 92141ddfce1..3c9b8a38fe1 100644
--- a/src/vnet/fib/fib_test.c
+++ b/src/vnet/fib/fib_test.c
@@ -24,6 +24,7 @@
#include <vnet/dpo/drop_dpo.h>
#include <vnet/dpo/receive_dpo.h>
#include <vnet/dpo/ip_null_dpo.h>
+#include <vnet/bfd/bfd_main.h>
#include <vnet/mpls/mpls.h>
@@ -33,6 +34,11 @@
#include <vnet/fib/fib_node_list.h>
#include <vnet/fib/fib_urpf_list.h>
+/*
+ * Add debugs for passing tests
+ */
+static int fib_test_do_debug;
+
#define FIB_TEST_I(_cond, _comment, _args...) \
({ \
int _evald = (_cond); \
@@ -40,6 +46,9 @@
fformat(stderr, "FAIL:%d: " _comment "\n", \
__LINE__, ##_args); \
} else { \
+ if (fib_test_do_debug) \
+ fformat(stderr, "PASS:%d: " _comment "\n", \
+ __LINE__, ##_args); \
} \
_evald; \
})
@@ -6735,6 +6744,509 @@ fib_test_walk (void)
return (0);
}
+/*
+ * declaration of the otherwise static callback functions
+ */
+void fib_bfd_notify (bfd_listen_event_e event,
+ const bfd_session_t *session);
+void adj_bfd_notify (bfd_listen_event_e event,
+ const bfd_session_t *session);
+
+/**
+ * Test BFD session interaction with FIB
+ */
+static int
+fib_test_bfd (void)
+{
+ fib_node_index_t fei;
+ test_main_t *tm;
+ int n_feis;
+
+ /* via 10.10.10.1 */
+ ip46_address_t nh_10_10_10_1 = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a01),
+ };
+ /* via 10.10.10.2 */
+ ip46_address_t nh_10_10_10_2 = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a02),
+ };
+ /* via 10.10.10.10 */
+ ip46_address_t nh_10_10_10_10 = {
+ .ip4.as_u32 = clib_host_to_net_u32(0x0a0a0a0a),
+ };
+ n_feis = fib_entry_pool_size();
+
+ tm = &test_main;
+
+ /*
+ * add interface routes. we'll assume this works. it's tested elsewhere
+ */
+ fib_prefix_t pfx_10_10_10_10_s_24 = {
+ .fp_len = 24,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = nh_10_10_10_10,
+ };
+
+ fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_24,
+ FIB_SOURCE_INTERFACE,
+ (FIB_ENTRY_FLAG_CONNECTED |
+ FIB_ENTRY_FLAG_ATTACHED),
+ FIB_PROTOCOL_IP4,
+ NULL,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1, // weight
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ fib_prefix_t pfx_10_10_10_10_s_32 = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = nh_10_10_10_10,
+ };
+ fib_table_entry_update_one_path(0, &pfx_10_10_10_10_s_32,
+ FIB_SOURCE_INTERFACE,
+ (FIB_ENTRY_FLAG_CONNECTED |
+ FIB_ENTRY_FLAG_LOCAL),
+ FIB_PROTOCOL_IP4,
+ NULL,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1, // weight
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ /*
+ * A BFD session via a neighbour we do not yet know
+ */
+ bfd_session_t bfd_10_10_10_1 = {
+ .udp = {
+ .key = {
+ .fib_index = 0,
+ .peer_addr = nh_10_10_10_1,
+ },
+ },
+ .hop_type = BFD_HOP_TYPE_MULTI,
+ .local_state = BFD_STATE_init,
+ };
+
+ fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+ /*
+ * A new entry will be created that forwards via the adj
+ */
+ adj_index_t ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index);
+ fib_prefix_t pfx_10_10_10_1_s_32 = {
+ .fp_addr = nh_10_10_10_1,
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
+ fib_test_lb_bucket_t adj_o_10_10_10_1 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_10_10_10_1,
+ },
+ };
+
+ fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &adj_o_10_10_10_1),
+ "BFD sourced %U via %U",
+ format_fib_prefix, &pfx_10_10_10_1_s_32,
+ format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
+
+ /*
+ * Delete the BFD session. Expect the fib_entry to be removed
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+ fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+ FIB_TEST(FIB_NODE_INDEX_INVALID == fei,
+ "BFD sourced %U removed",
+ format_fib_prefix, &pfx_10_10_10_1_s_32);
+
+ /*
+ * Add the BFD source back
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+ /*
+ * source the entry via the ADJ fib
+ */
+ fei = fib_table_entry_update_one_path(0,
+ &pfx_10_10_10_1_s_32,
+ FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_ATTACHED,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ /*
+ * Delete the BFD session. Expect the fib_entry to remain
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+ fei = fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32);
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &adj_o_10_10_10_1),
+ "BFD sourced %U remains via %U",
+ format_fib_prefix, &pfx_10_10_10_1_s_32,
+ format_ip_adjacency, ai_10_10_10_1, FORMAT_IP_ADJACENCY_NONE);
+
+ /*
+ * Add the BFD source back
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+ /*
+ * Create another ADJ FIB
+ */
+ fib_prefix_t pfx_10_10_10_2_s_32 = {
+ .fp_addr = nh_10_10_10_2,
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
+ fib_table_entry_update_one_path(0,
+ &pfx_10_10_10_2_s_32,
+ FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_ATTACHED,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_2,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ /*
+ * A BFD session for the new ADJ FIB
+ */
+ bfd_session_t bfd_10_10_10_2 = {
+ .udp = {
+ .key = {
+ .fib_index = 0,
+ .peer_addr = nh_10_10_10_2,
+ },
+ },
+ .hop_type = BFD_HOP_TYPE_MULTI,
+ .local_state = BFD_STATE_init,
+ };
+
+ fib_bfd_notify (BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_2);
+
+ /*
+ * remove the adj-fib source whilst the session is present
+ * then add it back
+ */
+ fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
+ fib_table_entry_update_one_path(0,
+ &pfx_10_10_10_2_s_32,
+ FIB_SOURCE_ADJ,
+ FIB_ENTRY_FLAG_ATTACHED,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_2,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ /*
+ * Before adding a recursive via the BFD tracked ADJ-FIBs,
+ * bring one of the sessions UP, leave the other down
+ */
+ bfd_10_10_10_1.local_state = BFD_STATE_up;
+ fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+ bfd_10_10_10_2.local_state = BFD_STATE_down;
+ fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+ /*
+ * A recursive prefix via both of the ADJ FIBs
+ */
+ fib_prefix_t pfx_200_0_0_0_s_24 = {
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_len = 32,
+ .fp_addr = {
+ .ip4.as_u32 = clib_host_to_net_u32(0xc8000000),
+ },
+ };
+ const dpo_id_t *dpo_10_10_10_1, *dpo_10_10_10_2;
+
+ dpo_10_10_10_1 =
+ fib_entry_contribute_ip_forwarding(
+ fib_table_lookup_exact_match(0, &pfx_10_10_10_1_s_32));
+ dpo_10_10_10_2 =
+ fib_entry_contribute_ip_forwarding(
+ fib_table_lookup_exact_match(0, &pfx_10_10_10_2_s_32));
+
+ fib_test_lb_bucket_t lb_o_10_10_10_1 = {
+ .type = FT_LB_O_LB,
+ .lb = {
+ .lb = dpo_10_10_10_1->dpoi_index,
+ },
+ };
+ fib_test_lb_bucket_t lb_o_10_10_10_2 = {
+ .type = FT_LB_O_LB,
+ .lb = {
+ .lb = dpo_10_10_10_2->dpoi_index,
+ },
+ };
+
+ /*
+ * A prefix via the adj-fib that is BFD down => DROP
+ */
+ fei = fib_table_entry_path_add(0,
+ &pfx_200_0_0_0_s_24,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_2,
+ ~0, // recursive
+ 0, // default fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+ "%U resolves via drop",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * add a path via the UP BFD adj-fib.
+ * we expect that the DOWN BFD ADJ FIB is not used.
+ */
+ fei = fib_table_entry_path_add(0,
+ &pfx_200_0_0_0_s_24,
+ FIB_SOURCE_API,
+ FIB_ENTRY_FLAG_NONE,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_1,
+ ~0, // recursive
+ 0, // default fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &lb_o_10_10_10_1),
+ "Recursive %U only UP BFD adj-fibs",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * Send a BFD state change to UP - both sessions are now up
+ * the recursive prefix should LB over both
+ */
+ bfd_10_10_10_2.local_state = BFD_STATE_up;
+ fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &lb_o_10_10_10_1,
+ &lb_o_10_10_10_2),
+ "Recursive %U via both UP BFD adj-fibs",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * Send a BFD state change to DOWN
+ * the recursive prefix should exclude the down
+ */
+ bfd_10_10_10_2.local_state = BFD_STATE_down;
+ fib_bfd_notify (BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_2);
+
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &lb_o_10_10_10_1),
+ "Recursive %U via only UP",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * Delete the BFD session while it is in the DOWN state.
+ * FIB should consider the entry's state as back up
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_2);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &lb_o_10_10_10_1,
+ &lb_o_10_10_10_2),
+ "Recursive %U via both UP BFD adj-fibs post down session delete",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * Delete the BFD other session while it is in the UP state.
+ */
+ fib_bfd_notify (BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &lb_o_10_10_10_1,
+ &lb_o_10_10_10_2),
+ "Recursive %U via both UP BFD adj-fibs post up session delete",
+ format_fib_prefix, &pfx_200_0_0_0_s_24);
+
+ /*
+ * cleaup
+ */
+ fib_table_entry_delete(0, &pfx_200_0_0_0_s_24, FIB_SOURCE_API);
+ fib_table_entry_delete(0, &pfx_10_10_10_1_s_32, FIB_SOURCE_ADJ);
+ fib_table_entry_delete(0, &pfx_10_10_10_2_s_32, FIB_SOURCE_ADJ);
+
+ fib_table_entry_delete(0, &pfx_10_10_10_10_s_32, FIB_SOURCE_INTERFACE);
+ fib_table_entry_delete(0, &pfx_10_10_10_10_s_24, FIB_SOURCE_INTERFACE);
+
+ adj_unlock(ai_10_10_10_1);
+ /*
+ * test no-one left behind
+ */
+ FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
+ FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
+
+ /*
+ * Single-hop BFD tests
+ */
+ bfd_10_10_10_1.hop_type = BFD_HOP_TYPE_SINGLE;
+ bfd_10_10_10_1.udp.key.sw_if_index = tm->hw[0]->sw_if_index;
+
+ adj_bfd_notify(BFD_LISTEN_EVENT_CREATE, &bfd_10_10_10_1);
+
+ ai_10_10_10_1 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index);
+ /*
+ * whilst the BFD session is not signalled, the adj is up
+ */
+ FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on uninit session");
+
+ /*
+ * bring the BFD session up
+ */
+ bfd_10_10_10_1.local_state = BFD_STATE_up;
+ adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+ FIB_TEST(adj_is_up(ai_10_10_10_1), "Adj state up on UP session");
+
+ /*
+ * bring the BFD session down
+ */
+ bfd_10_10_10_1.local_state = BFD_STATE_down;
+ adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+ FIB_TEST(!adj_is_up(ai_10_10_10_1), "Adj state down on DOWN session");
+
+
+ /*
+ * add an attached next hop FIB entry via the down adj
+ */
+ fib_prefix_t pfx_5_5_5_5_s_32 = {
+ .fp_addr = {
+ .ip4 = {
+ .as_u32 = clib_host_to_net_u32(0x05050505),
+ },
+ },
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ };
+
+ fei = fib_table_entry_path_add(0,
+ &pfx_5_5_5_5_s_32,
+ FIB_SOURCE_CLI,
+ FIB_ENTRY_FLAG_NONE,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_1,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+ FIB_TEST(load_balance_is_drop(fib_entry_contribute_ip_forwarding(fei)),
+ "%U resolves via drop",
+ format_fib_prefix, &pfx_5_5_5_5_s_32);
+
+ /*
+ * Add a path via an ADJ that is up
+ */
+ adj_index_t ai_10_10_10_2 = adj_nbr_add_or_lock(FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &nh_10_10_10_2,
+ tm->hw[0]->sw_if_index);
+
+ fib_test_lb_bucket_t adj_o_10_10_10_2 = {
+ .type = FT_LB_ADJ,
+ .adj = {
+ .adj = ai_10_10_10_2,
+ },
+ };
+ adj_o_10_10_10_1.adj.adj = ai_10_10_10_1;
+
+ fei = fib_table_entry_path_add(0,
+ &pfx_5_5_5_5_s_32,
+ FIB_SOURCE_CLI,
+ FIB_ENTRY_FLAG_NONE,
+ FIB_PROTOCOL_IP4,
+ &nh_10_10_10_2,
+ tm->hw[0]->sw_if_index,
+ ~0, // invalid fib index
+ 1,
+ NULL,
+ FIB_ROUTE_PATH_FLAG_NONE);
+
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 1,
+ &adj_o_10_10_10_2),
+ "BFD sourced %U via %U",
+ format_fib_prefix, &pfx_5_5_5_5_s_32,
+ format_ip_adjacency, ai_10_10_10_2, FORMAT_IP_ADJACENCY_NONE);
+
+ /*
+ * Bring up the down session - should now LB
+ */
+ bfd_10_10_10_1.local_state = BFD_STATE_up;
+ adj_bfd_notify(BFD_LISTEN_EVENT_UPDATE, &bfd_10_10_10_1);
+ FIB_TEST(fib_test_validate_entry(fei,
+ FIB_FORW_CHAIN_TYPE_UNICAST_IP4,
+ 2,
+ &adj_o_10_10_10_1,
+ &adj_o_10_10_10_2),
+ "BFD sourced %U via noth adjs",
+ format_fib_prefix, &pfx_5_5_5_5_s_32);
+
+ /*
+ * remove the BFD session state from the adj
+ */
+ adj_bfd_notify(BFD_LISTEN_EVENT_DELETE, &bfd_10_10_10_1);
+
+ /*
+ * clean-up
+ */
+ fib_table_entry_delete(0, &pfx_5_5_5_5_s_32, FIB_SOURCE_CLI);
+ adj_unlock(ai_10_10_10_1);
+ adj_unlock(ai_10_10_10_2);
+
+ /*
+ * test no-one left behind
+ */
+ FIB_TEST((n_feis == fib_entry_pool_size()), "Entries gone");
+ FIB_TEST(0 == adj_nbr_db_size(), "All adjacencies removed");
+ return (0);
+}
+
static int
lfib_test (void)
{
@@ -7119,6 +7631,11 @@ fib_test (vlib_main_t * vm,
res = 0;
fib_test_mk_intf(4);
+ if (unformat (input, "debug"))
+ {
+ fib_test_do_debug = 1;
+ }
+
if (unformat (input, "ip"))
{
res += fib_test_v4();
@@ -7140,6 +7657,10 @@ fib_test (vlib_main_t * vm,
{
res += fib_test_walk();
}
+ else if (unformat (input, "bfd"))
+ {
+ res += fib_test_bfd();
+ }
else
{
/*
@@ -7151,6 +7672,7 @@ fib_test (vlib_main_t * vm,
res += fib_test_v4();
res += fib_test_v6();
res += fib_test_ae();
+ res += fib_test_bfd();
res += fib_test_label();
res += lfib_test();
}
diff --git a/src/vnet/fib/ip4_fib.c b/src/vnet/fib/ip4_fib.c
index 98d4e52fa07..b03186e82d0 100644
--- a/src/vnet/fib/ip4_fib.c
+++ b/src/vnet/fib/ip4_fib.c
@@ -477,12 +477,15 @@ static void
ip4_fib_table_show_one (ip4_fib_t *fib,
vlib_main_t * vm,
ip4_address_t *address,
- u32 mask_len)
+ u32 mask_len,
+ int detail)
{
vlib_cli_output(vm, "%U",
format_fib_entry,
ip4_fib_table_lookup(fib, address, mask_len),
- FIB_ENTRY_FORMAT_DETAIL);
+ (detail ?
+ FIB_ENTRY_FORMAT_DETAIL2 :
+ FIB_ENTRY_FORMAT_DETAIL));
}
static clib_error_t *
@@ -496,6 +499,7 @@ ip4_show_fib (vlib_main_t * vm,
ip4_address_t matching_address;
u32 matching_mask = 32;
int i, table_id = -1, fib_index = ~0;
+ int detail = 0;
verbose = 1;
matching = 0;
@@ -506,6 +510,9 @@ ip4_show_fib (vlib_main_t * vm,
|| unformat (input, "sum"))
verbose = 0;
+ else if (unformat (input, "detail") || unformat (input, "det"))
+ detail = 1;
+
else if (unformat (input, "mtrie"))
mtrie = 1;
@@ -563,7 +570,8 @@ ip4_show_fib (vlib_main_t * vm,
}
else
{
- ip4_fib_table_show_one(fib, vm, &matching_address, matching_mask);
+ ip4_fib_table_show_one(fib, vm, &matching_address,
+ matching_mask, detail);
}
}));
@@ -717,7 +725,7 @@ ip4_show_fib (vlib_main_t * vm,
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip4_show_fib_command, static) = {
.path = "show ip fib",
- .short_help = "show ip fib [summary] [table <table-id>] [index <fib-id>] [<ip4-addr>[/<mask>]] [mtrie]",
+ .short_help = "show ip fib [summary] [table <table-id>] [index <fib-id>] [<ip4-addr>[/<mask>]] [mtrie] [detail]",
.function = ip4_show_fib,
};
/* *INDENT-ON* */
diff --git a/src/vnet/fib/ip6_fib.c b/src/vnet/fib/ip6_fib.c
index 0ee029d3116..002971400fe 100644
--- a/src/vnet/fib/ip6_fib.c
+++ b/src/vnet/fib/ip6_fib.c
@@ -560,12 +560,15 @@ static void
ip6_fib_table_show_one (ip6_fib_t *fib,
vlib_main_t * vm,
ip6_address_t *address,
- u32 mask_len)
+ u32 mask_len,
+ int detail)
{
vlib_cli_output(vm, "%U",
format_fib_entry,
ip6_fib_table_lookup(fib->index, address, mask_len),
- FIB_ENTRY_FORMAT_DETAIL);
+ (detail ?
+ FIB_ENTRY_FORMAT_DETAIL2:
+ FIB_ENTRY_FORMAT_DETAIL));
}
typedef struct {
@@ -573,8 +576,9 @@ typedef struct {
u64 count_by_prefix_length[129];
} count_routes_in_fib_at_prefix_length_arg_t;
-static void count_routes_in_fib_at_prefix_length
-(BVT(clib_bihash_kv) * kvp, void *arg)
+static void
+count_routes_in_fib_at_prefix_length (BVT(clib_bihash_kv) * kvp,
+ void *arg)
{
count_routes_in_fib_at_prefix_length_arg_t * ap = arg;
int mask_width;
@@ -600,6 +604,7 @@ ip6_show_fib (vlib_main_t * vm,
ip6_address_t matching_address;
u32 mask_len = 128;
int table_id = -1, fib_index = ~0;
+ int detail = 0;
verbose = 1;
matching = 0;
@@ -610,6 +615,10 @@ ip6_show_fib (vlib_main_t * vm,
unformat (input, "summary") ||
unformat (input, "sum"))
verbose = 0;
+
+ else if (unformat (input, "detail") ||
+ unformat (input, "det"))
+ detail = 1;
else if (unformat (input, "%U/%d",
unformat_ip6_address, &matching_address, &mask_len))
@@ -667,7 +676,7 @@ ip6_show_fib (vlib_main_t * vm,
}
else
{
- ip6_fib_table_show_one(fib, vm, &matching_address, mask_len);
+ ip6_fib_table_show_one(fib, vm, &matching_address, mask_len, detail);
}
}));
@@ -771,7 +780,7 @@ ip6_show_fib (vlib_main_t * vm,
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (ip6_show_fib_command, static) = {
.path = "show ip6 fib",
- .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]]",
+ .short_help = "show ip6 fib [summary] [table <table-id>] [index <fib-id>] [<ip6-addr>[/<width>]] [detail]",
.function = ip6_show_fib,
};
/* *INDENT-ON* */
diff --git a/test/framework.py b/test/framework.py
index fbd21d2370f..ce70af2e267 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -359,7 +359,7 @@ class VppTestCase(unittest.TestCase):
self._testMethodDoc))
if not self.vpp_dead:
self.logger.debug(self.vapi.cli("show trace"))
- self.logger.info(self.vapi.ppcli("show interfaces"))
+ self.logger.info(self.vapi.ppcli("show interface"))
self.logger.info(self.vapi.ppcli("show hardware"))
self.logger.info(self.vapi.ppcli("show error"))
self.logger.info(self.vapi.ppcli("show run"))
diff --git a/test/test_bfd.py b/test/test_bfd.py
index c9d0abdd91d..e8f8f33849f 100644
--- a/test/test_bfd.py
+++ b/test/test_bfd.py
@@ -16,9 +16,10 @@ from scapy.layers.inet6 import IPv6
from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \
BFDDiagCode, BFDState, BFD_vpp_echo
from framework import VppTestCase, VppTestRunner, running_extended_tests
-from vpp_pg_interface import CaptureTimeoutError
+from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc
from util import ppp
from vpp_papi_provider import UnexpectedApiReturnValueError
+from vpp_ip_route import VppIpRoute, VppRoutePath
USEC_IN_SEC = 1000000
@@ -1582,6 +1583,107 @@ class BFD6TestCase(VppTestCase):
self.test_session.send_packet()
+class BFDFIBTestCase(VppTestCase):
+ """ BFD-FIB interactions (IPv6) """
+
+ vpp_session = None
+ test_session = None
+
+ def setUp(self):
+ super(BFDFIBTestCase, self).setUp()
+ self.create_pg_interfaces(range(1))
+
+ self.vapi.want_bfd_events()
+ self.pg0.enable_capture()
+
+ for i in self.pg_interfaces:
+ i.admin_up()
+ i.config_ip6()
+ i.configure_ipv6_neighbors()
+
+ def tearDown(self):
+ if not self.vpp_dead:
+ self.vapi.want_bfd_events(enable_disable=0)
+
+ super(BFDFIBTestCase, self).tearDown()
+
+ @staticmethod
+ def pkt_is_not_data_traffic(p):
+ """ not data traffic implies BFD or the usual IPv6 ND/RA"""
+ if p.haslayer(BFD) or is_ipv6_misc(p):
+ return True
+ return False
+
+ def test_session_with_fib(self):
+ """ BFD-FIB interactions """
+
+ # packets to match against both of the routes
+ p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IPv6(src="3001::1", dst="2001::1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100)),
+ (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IPv6(src="3001::1", dst="2002::1") /
+ UDP(sport=1234, dport=1234) /
+ Raw('\xa5' * 100))]
+
+ # A recursive and a non-recursive route via a next-hop that
+ # will have a BFD session
+ ip_2001_s_64 = VppIpRoute(self, "2001::", 64,
+ [VppRoutePath(self.pg0.remote_ip6,
+ self.pg0.sw_if_index,
+ is_ip6=1)],
+ is_ip6=1)
+ ip_2002_s_64 = VppIpRoute(self, "2002::", 64,
+ [VppRoutePath(self.pg0.remote_ip6,
+ 0xffffffff,
+ is_ip6=1)],
+ is_ip6=1)
+ ip_2001_s_64.add_vpp_config()
+ ip_2002_s_64.add_vpp_config()
+
+ # bring the session up now the routes are present
+ self.vpp_session = VppBFDUDPSession(self,
+ self.pg0,
+ self.pg0.remote_ip6,
+ af=AF_INET6)
+ self.vpp_session.add_vpp_config()
+ self.vpp_session.admin_up()
+ self.test_session = BFDTestSession(self, self.pg0, AF_INET6)
+
+ # session is up - traffic passes
+ bfd_session_up(self)
+
+ self.pg0.add_stream(p)
+ self.pg_start()
+ for packet in p:
+ captured = self.pg0.wait_for_packet(
+ 1,
+ filter_out_fn=self.pkt_is_not_data_traffic)
+ self.assertEqual(captured[IPv6].dst,
+ packet[IPv6].dst)
+
+ # session is up - traffic is dropped
+ bfd_session_down(self)
+
+ self.pg0.add_stream(p)
+ self.pg_start()
+ with self.assertRaises(CaptureTimeoutError):
+ self.pg0.wait_for_packet(1, self.pkt_is_not_data_traffic)
+
+ # session is up - traffic passes
+ bfd_session_up(self)
+
+ self.pg0.add_stream(p)
+ self.pg_start()
+ for packet in p:
+ captured = self.pg0.wait_for_packet(
+ 1,
+ filter_out_fn=self.pkt_is_not_data_traffic)
+ self.assertEqual(captured[IPv6].dst,
+ packet[IPv6].dst)
+
+
class BFDSHA1TestCase(VppTestCase):
"""Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py
index 7f9e2ae1293..e8025dff68e 100644
--- a/test/vpp_papi_provider.py
+++ b/test/vpp_papi_provider.py
@@ -152,8 +152,9 @@ class VppPapiProvider(object):
raise UnexpectedApiReturnValueError(msg)
elif self._expect_api_retval == self._zero:
if hasattr(reply, 'retval') and reply.retval != expected_retval:
- msg = "API call failed, expected zero return value instead "\
- "of %d in %s" % (expected_retval, repr(reply))
+ msg = "API call failed, expected %d return value instead "\
+ "of %d in %s" % (expected_retval, reply.retval,
+ repr(reply))
self.test_class.logger.info(msg)
raise UnexpectedApiReturnValueError(msg)
else: