summaryrefslogtreecommitdiffstats
path: root/ospf6d/ospf6_bfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ospf6d/ospf6_bfd.c')
-rw-r--r--ospf6d/ospf6_bfd.c290
1 files changed, 290 insertions, 0 deletions
diff --git a/ospf6d/ospf6_bfd.c b/ospf6d/ospf6_bfd.c
new file mode 100644
index 0000000..6379f9d
--- /dev/null
+++ b/ospf6d/ospf6_bfd.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/**
+ * ospf6_bfd.c: IPv6 OSPF BFD handling routines
+ *
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+#include "prefix.h"
+#include "frrevent.h"
+#include "buffer.h"
+#include "stream.h"
+#include "zclient.h"
+#include "vty.h"
+#include "table.h"
+#include "bfd.h"
+#include "if.h"
+#include "ospf6d.h"
+#include "ospf6_message.h"
+#include "ospf6_neighbor.h"
+#include "ospf6_interface.h"
+#include "ospf6_route.h"
+#include "ospf6_zebra.h"
+#include "ospf6_bfd.h"
+
+extern struct zclient *zclient;
+
+/*
+ * ospf6_bfd_trigger_event - Neighbor is registered/deregistered with BFD when
+ * neighbor state is changed to/from 2way.
+ */
+void ospf6_bfd_trigger_event(struct ospf6_neighbor *on, int old_state,
+ int state)
+{
+ int family;
+ struct in6_addr src, dst;
+
+ /* Skip sessions without BFD. */
+ if (on->bfd_session == NULL)
+ return;
+
+ if (old_state < OSPF6_NEIGHBOR_TWOWAY
+ && state >= OSPF6_NEIGHBOR_TWOWAY) {
+ /*
+ * Check if neighbor address changed.
+ *
+ * When the neighbor is configured BFD before having an existing
+ * connection, then the destination address will be set to `::`
+ * which will cause session installation failure. This piece of
+ * code updates the address in that case.
+ */
+ bfd_sess_addresses(on->bfd_session, &family, &src, &dst);
+ if (memcmp(&on->linklocal_addr, &dst, sizeof(dst))) {
+ bfd_sess_set_ipv6_addrs(on->bfd_session, &src,
+ &on->linklocal_addr);
+ }
+
+ bfd_sess_install(on->bfd_session);
+ } else if (old_state >= OSPF6_NEIGHBOR_TWOWAY
+ && state < OSPF6_NEIGHBOR_TWOWAY)
+ bfd_sess_uninstall(on->bfd_session);
+}
+
+/*
+ * ospf6_bfd_reg_dereg_all_nbr - Register/Deregister all neighbors associated
+ * with a interface with BFD through
+ * zebra for starting/stopping the monitoring of
+ * the neighbor rechahability.
+ */
+static void ospf6_bfd_reg_dereg_all_nbr(struct ospf6_interface *oi,
+ bool install)
+{
+ struct ospf6_neighbor *on;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(oi->neighbor_list, node, on)) {
+ /* Remove all sessions. */
+ if (!install) {
+ bfd_sess_free(&on->bfd_session);
+ continue;
+ }
+
+ /* Always allocate session data even if not enabled. */
+ ospf6_bfd_info_nbr_create(oi, on);
+
+ /*
+ * If not connected yet, don't create any session but defer it
+ * for later. See function `ospf6_bfd_trigger_event`.
+ */
+ if (on->state < OSPF6_NEIGHBOR_TWOWAY)
+ continue;
+
+ bfd_sess_install(on->bfd_session);
+ }
+}
+
+static void ospf6_bfd_callback(struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss, void *arg)
+{
+ struct ospf6_neighbor *on = arg;
+
+ if (bss->state == BFD_STATUS_DOWN
+ && bss->previous_state == BFD_STATUS_UP) {
+ EVENT_OFF(on->inactivity_timer);
+ event_add_event(master, inactivity_timer, on, 0, NULL);
+ }
+}
+
+/*
+ * ospf6_bfd_info_nbr_create - Create/update BFD information for a neighbor.
+ */
+void ospf6_bfd_info_nbr_create(struct ospf6_interface *oi,
+ struct ospf6_neighbor *on)
+{
+ if (!oi->bfd_config.enabled)
+ return;
+
+ if (on->bfd_session == NULL)
+ on->bfd_session = bfd_sess_new(ospf6_bfd_callback, on);
+
+ bfd_sess_set_timers(on->bfd_session,
+ oi->bfd_config.detection_multiplier,
+ oi->bfd_config.min_rx, oi->bfd_config.min_tx);
+ bfd_sess_set_ipv6_addrs(on->bfd_session, on->ospf6_if->linklocal_addr,
+ &on->linklocal_addr);
+ bfd_sess_set_interface(on->bfd_session, oi->interface->name);
+ bfd_sess_set_vrf(on->bfd_session, oi->interface->vrf->vrf_id);
+ bfd_sess_set_profile(on->bfd_session, oi->bfd_config.profile);
+}
+
+/*
+ * ospf6_bfd_write_config - Write the interface BFD configuration.
+ */
+void ospf6_bfd_write_config(struct vty *vty, struct ospf6_interface *oi)
+{
+ if (!oi->bfd_config.enabled)
+ return;
+
+#if HAVE_BFDD == 0
+ if (oi->bfd_config.detection_multiplier != BFD_DEF_DETECT_MULT
+ || oi->bfd_config.min_rx != BFD_DEF_MIN_RX
+ || oi->bfd_config.min_tx != BFD_DEF_MIN_TX)
+ vty_out(vty, " ipv6 ospf6 bfd %d %d %d\n",
+ oi->bfd_config.detection_multiplier,
+ oi->bfd_config.min_rx, oi->bfd_config.min_tx);
+ else
+#endif /* ! HAVE_BFDD */
+ vty_out(vty, " ipv6 ospf6 bfd\n");
+
+ if (oi->bfd_config.profile)
+ vty_out(vty, " ipv6 ospf6 bfd profile %s\n",
+ oi->bfd_config.profile);
+}
+
+DEFUN(ipv6_ospf6_bfd, ipv6_ospf6_bfd_cmd,
+ "ipv6 ospf6 bfd [profile BFDPROF]",
+ IP6_STR OSPF6_STR
+ "Enables BFD support\n"
+ "BFD Profile selection\n"
+ "BFD Profile name\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+ int prof_idx = 4;
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ oi->bfd_config.detection_multiplier = BFD_DEF_DETECT_MULT;
+ oi->bfd_config.min_rx = BFD_DEF_MIN_RX;
+ oi->bfd_config.min_tx = BFD_DEF_MIN_TX;
+ oi->bfd_config.enabled = true;
+ if (argc > prof_idx) {
+ XFREE(MTYPE_TMP, oi->bfd_config.profile);
+ oi->bfd_config.profile =
+ XSTRDUP(MTYPE_TMP, argv[prof_idx]->arg);
+ }
+
+ ospf6_bfd_reg_dereg_all_nbr(oi, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_ipv6_ospf6_bfd_profile, no_ipv6_ospf6_bfd_profile_cmd,
+ "no ipv6 ospf6 bfd profile [BFDPROF]",
+ NO_STR IP6_STR OSPF6_STR
+ "BFD support\n"
+ "BFD Profile selection\n"
+ "BFD Profile name\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ /* BFD not enabled, nothing to do. */
+ if (!oi->bfd_config.enabled)
+ return CMD_SUCCESS;
+
+ /* Remove profile and apply new configuration. */
+ XFREE(MTYPE_TMP, oi->bfd_config.profile);
+ ospf6_bfd_reg_dereg_all_nbr(oi, true);
+
+ return CMD_SUCCESS;
+}
+
+#if HAVE_BFDD > 0
+DEFUN_HIDDEN(
+#else
+DEFUN(
+#endif /* HAVE_BFDD */
+ ipv6_ospf6_bfd_param,
+ ipv6_ospf6_bfd_param_cmd,
+ "ipv6 ospf6 bfd (2-255) (50-60000) (50-60000)",
+ IP6_STR
+ OSPF6_STR
+ "Enables BFD support\n"
+ "Detect Multiplier\n"
+ "Required min receive interval\n"
+ "Desired min transmit interval\n")
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ int idx_number = 3;
+ int idx_number_2 = 4;
+ int idx_number_3 = 5;
+ struct ospf6_interface *oi;
+
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ oi->bfd_config.detection_multiplier =
+ strtoul(argv[idx_number]->arg, NULL, 10);
+ oi->bfd_config.min_rx = strtoul(argv[idx_number_2]->arg, NULL, 10);
+ oi->bfd_config.min_tx = strtoul(argv[idx_number_3]->arg, NULL, 10);
+ oi->bfd_config.enabled = true;
+
+ ospf6_bfd_reg_dereg_all_nbr(oi, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_ospf6_bfd,
+ no_ipv6_ospf6_bfd_cmd,
+ "no ipv6 ospf6 bfd",
+ NO_STR
+ IP6_STR
+ OSPF6_STR
+ "Disables BFD support\n"
+ )
+{
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ struct ospf6_interface *oi;
+ assert(ifp);
+
+ oi = (struct ospf6_interface *)ifp->info;
+ if (oi == NULL)
+ oi = ospf6_interface_create(ifp);
+ assert(oi);
+
+ oi->bfd_config.enabled = false;
+ ospf6_bfd_reg_dereg_all_nbr(oi, false);
+
+ return CMD_SUCCESS;
+}
+
+void ospf6_bfd_init(void)
+{
+ bfd_protocol_integration_init(zclient, master);
+
+ /* Install BFD command */
+ install_element(INTERFACE_NODE, &ipv6_ospf6_bfd_cmd);
+ install_element(INTERFACE_NODE, &ipv6_ospf6_bfd_param_cmd);
+ install_element(INTERFACE_NODE, &no_ipv6_ospf6_bfd_profile_cmd);
+ install_element(INTERFACE_NODE, &no_ipv6_ospf6_bfd_cmd);
+}