aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/fib/fib_bfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vnet/fib/fib_bfd.c')
-rw-r--r--src/vnet/fib/fib_bfd.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/src/vnet/fib/fib_bfd.c b/src/vnet/fib/fib_bfd.c
new file mode 100644
index 00000000..734ee8cc
--- /dev/null
+++ b/src/vnet/fib/fib_bfd.c
@@ -0,0 +1,196 @@
+/*
+ * 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);
+ 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);