summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/user/bfd.rst28
-rw-r--r--doc/user/overview.rst2
-rw-r--r--ripd/rip_bfd.c119
-rw-r--r--ripd/rip_bfd.h21
-rw-r--r--ripd/rip_cli.c101
-rw-r--r--ripd/rip_interface.c2
-rw-r--r--ripd/rip_main.c4
-rw-r--r--ripd/rip_nb.c30
-rw-r--r--ripd/rip_nb.h15
-rw-r--r--ripd/rip_nb_config.c135
-rw-r--r--ripd/rip_peer.c68
-rw-r--r--ripd/rip_zebra.c4
-rw-r--r--ripd/ripd.c54
-rw-r--r--ripd/ripd.h31
-rw-r--r--ripd/subdir.am3
-rwxr-xr-xtests/topotests/rip_bfd_topo1/__init__.py0
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/ripd.conf17
-rw-r--r--tests/topotests/rip_bfd_topo1/r1/zebra.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/ripd.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/staticd.conf1
-rw-r--r--tests/topotests/rip_bfd_topo1/r2/zebra.conf8
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/bfdd.conf6
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/ripd.conf11
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/staticd.conf1
-rw-r--r--tests/topotests/rip_bfd_topo1/r3/zebra.conf7
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot58
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.pngbin0 -> 27400 bytes
-rw-r--r--tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py252
-rw-r--r--yang/frr-ripd.yang27
-rw-r--r--zebra/zebra_ptm.h2
32 files changed, 1002 insertions, 39 deletions
diff --git a/doc/user/bfd.rst b/doc/user/bfd.rst
index 1a42996771..776726f193 100644
--- a/doc/user/bfd.rst
+++ b/doc/user/bfd.rst
@@ -10,6 +10,7 @@ the following RFCs:
* :rfc:`5880`
* :rfc:`5881`
+* :rfc:`5882`
* :rfc:`5883`
Currently, there are two implementations of the BFD commands in FRR:
@@ -353,6 +354,33 @@ The following commands are available inside the interface configuration node.
that interface.
+.. _bfd-rip-peer-config:
+
+RIP BFD configuration
+---------------------
+
+The following commands are available inside the interface configuration node:
+
+.. clicmd:: ip rip bfd
+
+ Automatically create BFD session for each RIP peer discovered in this
+ interface. When the BFD session monitor signalize that the link is down
+ the RIP peer is removed and all the learned routes associated with that
+ peer are removed.
+
+
+.. clicmd:: ip rip bfd profile BFD_PROFILE_NAME
+
+ Selects a BFD profile for the BFD sessions created in this interface.
+
+
+The following command is available in the RIP router configuration node:
+
+.. clicmd:: bfd default-profile BFD_PROFILE_NAME
+
+ Selects a default BFD profile for all sessions without a profile specified.
+
+
.. _bfd-static-peer-config:
BFD Static Route Monitoring Configuration
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index 5ea33d62c9..33a1934628 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -465,6 +465,8 @@ BFD
:t:`Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
- :rfc:`5881`
:t:`Bidirectional Forwarding Detection (BFD) for IPv4 and IPv6 (Single Hop), D. Katz, D. Ward. June 2010`
+- :rfc:`5882`
+ :t:`Generic Application of Bidirectional Forwarding Detection (BFD), D. Katz, D. Ward. June 2010`
- :rfc:`5883`
:t:`Bidirectional Forwarding Detection (BFD) for Multihop Paths, D. Katz, D. Ward. June 2010`
diff --git a/ripd/rip_bfd.c b/ripd/rip_bfd.c
new file mode 100644
index 0000000000..ac5035f547
--- /dev/null
+++ b/ripd/rip_bfd.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#include <zebra.h>
+
+#include "lib/zclient.h"
+#include "lib/bfd.h"
+
+#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
+#include "ripd/rip_debug.h"
+
+extern struct zclient *zclient;
+
+static const char *rip_bfd_interface_profile(struct rip_interface *ri)
+{
+ struct rip *rip = ri->rip;
+
+ if (ri->bfd.profile)
+ return ri->bfd.profile;
+
+ if (rip->default_bfd_profile)
+ return rip->default_bfd_profile;
+
+ return NULL;
+}
+
+static void rip_bfd_session_change(struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss,
+ void *arg)
+{
+ struct rip_peer *rp = arg;
+
+ /* BFD peer went down. */
+ if (bss->state == BFD_STATUS_DOWN &&
+ bss->previous_state == BFD_STATUS_UP) {
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Down", __func__,
+ &rp->addr);
+
+ rip_peer_delete_routes(rp);
+ listnode_delete(rp->rip->peer_list, rp);
+ rip_peer_free(rp);
+ return;
+ }
+
+ /* BFD peer went up. */
+ if (bss->state == BSS_UP && bss->previous_state == BSS_DOWN)
+ if (IS_RIP_DEBUG_EVENT)
+ zlog_debug("%s: peer %pI4: BFD Up", __func__,
+ &rp->addr);
+}
+
+void rip_bfd_session_update(struct rip_peer *rp)
+{
+ struct rip_interface *ri = rp->ri;
+
+ /* BFD configuration was removed. */
+ if (ri == NULL || !ri->bfd.enabled) {
+ bfd_sess_free(&rp->bfd_session);
+ return;
+ }
+
+ /* New BFD session. */
+ if (rp->bfd_session == NULL) {
+ rp->bfd_session = bfd_sess_new(rip_bfd_session_change, rp);
+ bfd_sess_set_ipv4_addrs(rp->bfd_session, NULL, &rp->addr);
+ bfd_sess_set_interface(rp->bfd_session, ri->ifp->name);
+ bfd_sess_set_vrf(rp->bfd_session, rp->rip->vrf->vrf_id);
+ }
+
+ /* Set new configuration. */
+ bfd_sess_set_timers(rp->bfd_session, BFD_DEF_DETECT_MULT,
+ BFD_DEF_MIN_RX, BFD_DEF_MIN_TX);
+ bfd_sess_set_profile(rp->bfd_session, rip_bfd_interface_profile(ri));
+
+ bfd_sess_install(rp->bfd_session);
+}
+
+void rip_bfd_interface_update(struct rip_interface *ri)
+{
+ struct rip *rip;
+ struct rip_peer *rp;
+ struct listnode *node;
+
+ rip = ri->rip;
+ if (!rip)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(rip->peer_list, node, rp)) {
+ if (rp->ri != ri)
+ continue;
+
+ rip_bfd_session_update(rp);
+ }
+}
+
+void rip_bfd_instance_update(struct rip *rip)
+{
+ struct interface *ifp;
+
+ FOR_ALL_INTERFACES (rip->vrf, ifp) {
+ struct rip_interface *ri;
+
+ ri = ifp->info;
+ if (!ri)
+ continue;
+
+ rip_bfd_interface_update(ri);
+ }
+}
+
+void rip_bfd_init(struct event_loop *tm)
+{
+ bfd_protocol_integration_init(zclient, tm);
+}
diff --git a/ripd/rip_bfd.h b/ripd/rip_bfd.h
new file mode 100644
index 0000000000..d49ca15153
--- /dev/null
+++ b/ripd/rip_bfd.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RIP BFD integration.
+ * Copyright (C) 2021-2023 Network Device Education Foundation, Inc. ("NetDEF")
+ */
+
+#ifndef _RIP_BFD_
+#define _RIP_BFD_
+
+#include "frrevent.h"
+
+struct rip;
+struct rip_interface;
+struct rip_peer;
+
+void rip_bfd_session_update(struct rip_peer *rp);
+void rip_bfd_interface_update(struct rip_interface *ri);
+void rip_bfd_instance_update(struct rip *rip);
+void rip_bfd_init(struct event_loop *tm);
+
+#endif /* _RIP_BFD_ */
diff --git a/ripd/rip_cli.c b/ripd/rip_cli.c
index cac29c00d4..ac9fc4b1a8 100644
--- a/ripd/rip_cli.c
+++ b/ripd/rip_cli.c
@@ -582,6 +582,42 @@ void cli_show_rip_version(struct vty *vty, const struct lyd_node *dnode,
}
/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+DEFPY_YANG(rip_bfd_default_profile, rip_bfd_default_profile_cmd,
+ "bfd default-profile BFDPROF$profile",
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_MODIFY,
+ profile);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_rip_bfd_default_profile, no_rip_bfd_default_profile_cmd,
+ "no bfd default-profile [BFDPROF]",
+ NO_STR
+ "Bidirectional Forwarding Detection\n"
+ "BFD default profile\n"
+ "Profile name\n")
+{
+ nb_cli_enqueue_change(vty, "./default-bfd-profile", NB_OP_DESTROY,
+ NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " bfd default-profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
*/
DEFPY_YANG (ip_rip_split_horizon,
@@ -980,6 +1016,66 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
}
/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+DEFPY_YANG(ip_rip_bfd, ip_rip_bfd_cmd, "[no] ip rip bfd",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/enable", NB_OP_MODIFY,
+ no ? "false" : "true");
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd\n");
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd/profile
+ */
+DEFPY_YANG(ip_rip_bfd_profile, ip_rip_bfd_profile_cmd,
+ "[no] ip rip bfd profile BFDPROF$profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n"
+ "Profile name\n")
+{
+ if (no)
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_DESTROY, NULL);
+ else
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile",
+ NB_OP_MODIFY, profile);
+
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+DEFPY_YANG(no_ip_rip_bfd_profile, no_ip_rip_bfd_profile_cmd,
+ "no ip rip bfd profile",
+ NO_STR IP_STR
+ "Routing Information Protocol\n"
+ "Enable BFD support\n"
+ "Use a pre-configured profile\n")
+{
+ nb_cli_enqueue_change(vty, "./bfd-monitoring/profile", NB_OP_DESTROY,
+ NULL);
+ return nb_cli_apply_changes(vty, "./frr-ripd:rip");
+}
+
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " ip rip bfd profile %s\n",
+ yang_dnode_get_string(dnode, NULL));
+}
+
+/*
* XPath: /frr-ripd:clear-rip-route
*/
DEFPY_YANG (clear_ip_rip,
@@ -1078,6 +1174,8 @@ void rip_cli_init(void)
install_element(RIP_NODE, &no_rip_timers_cmd);
install_element(RIP_NODE, &rip_version_cmd);
install_element(RIP_NODE, &no_rip_version_cmd);
+ install_element(RIP_NODE, &rip_bfd_default_profile_cmd);
+ install_element(RIP_NODE, &no_rip_bfd_default_profile_cmd);
install_element(INTERFACE_NODE, &ip_rip_split_horizon_cmd);
install_element(INTERFACE_NODE, &ip_rip_v2_broadcast_cmd);
@@ -1092,6 +1190,9 @@ void rip_cli_init(void)
install_element(INTERFACE_NODE, &ip_rip_authentication_key_chain_cmd);
install_element(INTERFACE_NODE,
&no_ip_rip_authentication_key_chain_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_cmd);
+ install_element(INTERFACE_NODE, &ip_rip_bfd_profile_cmd);
+ install_element(INTERFACE_NODE, &no_ip_rip_bfd_profile_cmd);
install_element(ENABLE_NODE, &clear_ip_rip_cmd);
}
diff --git a/ripd/rip_interface.c b/ripd/rip_interface.c
index 0b92f174b1..b383be042a 100644
--- a/ripd/rip_interface.c
+++ b/ripd/rip_interface.c
@@ -457,6 +457,7 @@ static void rip_interface_reset(struct rip_interface *ri)
ri->sent_updates = 0;
ri->passive = 0;
+ XFREE(MTYPE_TMP, ri->bfd.profile);
rip_interface_clean(ri);
}
@@ -1109,6 +1110,7 @@ void rip_interface_sync(struct interface *ifp)
struct rip_interface *ri;
ri = ifp->info;
+ ri->ifp = ifp;
if (ri)
ri->rip = ifp->vrf->info;
}
diff --git a/ripd/rip_main.c b/ripd/rip_main.c
index a6e4ad776b..0e26662cdb 100644
--- a/ripd/rip_main.c
+++ b/ripd/rip_main.c
@@ -21,8 +21,10 @@
#include "if_rmap.h"
#include "libfrr.h"
#include "routemap.h"
+#include "bfd.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
#include "ripd/rip_nb.h"
#include "ripd/rip_errors.h"
@@ -65,6 +67,7 @@ static void sigint(void)
{
zlog_notice("Terminating on signal");
+ bfd_protocol_integration_set_shutdown(true);
rip_vrf_terminate();
if_rmap_terminate();
rip_zclient_stop();
@@ -162,6 +165,7 @@ int main(int argc, char **argv)
rip_if_init();
rip_cli_init();
rip_zclient_init(master);
+ rip_bfd_init(master);
frr_config_fork();
frr_run(master);
diff --git a/ripd/rip_nb.c b/ripd/rip_nb.c
index fa6652faf4..d11f1e1d34 100644
--- a/ripd/rip_nb.c
+++ b/ripd/rip_nb.c
@@ -240,6 +240,14 @@ const struct frr_yang_module_info frr_ripd_info = {
},
},
{
+ .xpath = "/frr-ripd:ripd/instance/default-bfd-profile",
+ .cbs = {
+ .modify = ripd_instance_default_bfd_profile_modify,
+ .destroy = ripd_instance_default_bfd_profile_destroy,
+ .cli_show = cli_show_ripd_instance_default_bfd_profile,
+ },
+ },
+ {
.xpath = "/frr-interface:lib/interface/frr-ripd:rip/split-horizon",
.cbs = {
.cli_show = cli_show_ip_rip_split_horizon,
@@ -303,6 +311,28 @@ const struct frr_yang_module_info frr_ripd_info = {
},
},
{
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring",
+ .cbs = {
+ .create = lib_interface_rip_bfd_create,
+ .destroy = lib_interface_rip_bfd_destroy,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_enable,
+ .modify = lib_interface_rip_bfd_enable_modify,
+ },
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile",
+ .cbs = {
+ .cli_show = cli_show_ip_rip_bfd_profile,
+ .modify = lib_interface_rip_bfd_profile_modify,
+ .destroy = lib_interface_rip_bfd_profile_destroy,
+ },
+ },
+ {
.xpath = "/frr-ripd:ripd/instance/state/neighbors/neighbor",
.cbs = {
.get_keys = ripd_instance_state_neighbors_neighbor_get_keys,
diff --git a/ripd/rip_nb.h b/ripd/rip_nb.h
index ebc60fefb4..9929e0952b 100644
--- a/ripd/rip_nb.h
+++ b/ripd/rip_nb.h
@@ -72,6 +72,8 @@ int ripd_instance_timers_holddown_interval_modify(
int ripd_instance_timers_update_interval_modify(struct nb_cb_modify_args *args);
int ripd_instance_version_receive_modify(struct nb_cb_modify_args *args);
int ripd_instance_version_send_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args);
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args);
const void *ripd_instance_state_neighbors_neighbor_get_next(
struct nb_cb_get_next_args *args);
int ripd_instance_state_neighbors_neighbor_get_keys(
@@ -151,6 +153,12 @@ int lib_interface_rip_authentication_key_chain_modify(
struct nb_cb_modify_args *args);
int lib_interface_rip_authentication_key_chain_destroy(
struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args);
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_enable_destroy(struct nb_cb_destroy_args *args);
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args);
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args);
/* Optional 'apply_finish' callbacks. */
void ripd_instance_redistribute_apply_finish(
@@ -206,6 +214,9 @@ void cli_show_ip_rip_receive_version(struct vty *vty,
bool show_defaults);
void cli_show_ip_rip_send_version(struct vty *vty, const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_ripd_instance_default_bfd_profile(struct vty *vty,
+ const struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_rip_authentication_scheme(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
@@ -215,6 +226,10 @@ void cli_show_ip_rip_authentication_string(struct vty *vty,
void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
const struct lyd_node *dnode,
bool show_defaults);
+void cli_show_ip_rip_bfd_enable(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
+void cli_show_ip_rip_bfd_profile(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults);
/* Notifications. */
extern void ripd_notif_send_auth_type_failure(const char *ifname);
diff --git a/ripd/rip_nb_config.c b/ripd/rip_nb_config.c
index d237866a41..8fe34705ca 100644
--- a/ripd/rip_nb_config.c
+++ b/ripd/rip_nb_config.c
@@ -23,6 +23,9 @@
#include "ripd/rip_nb.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
+#include "ripd/rip_bfd.h"
+
+DEFINE_MTYPE_STATIC(RIPD, RIP_BFD_PROFILE, "RIP BFD profile name");
/*
* XPath: /frr-ripd:ripd/instance
@@ -906,6 +909,40 @@ int ripd_instance_version_send_modify(struct nb_cb_modify_args *args)
}
/*
+ * XPath: /frr-ripd:ripd/instance/default-bfd-profile
+ */
+int ripd_instance_default_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip->default_bfd_profile =
+ XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
+int ripd_instance_default_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct rip *rip;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ rip = nb_running_get_entry(args->dnode, NULL, true);
+ XFREE(MTYPE_RIP_BFD_PROFILE, rip->default_bfd_profile);
+ rip_bfd_instance_update(rip);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/split-horizon
*/
int lib_interface_rip_split_horizon_modify(struct nb_cb_modify_args *args)
@@ -1071,6 +1108,104 @@ int lib_interface_rip_authentication_password_destroy(
}
/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring
+ */
+int lib_interface_rip_bfd_create(struct nb_cb_create_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, "./enable");
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ if (yang_dnode_exists(args->dnode, "./profile"))
+ ri->bfd.profile = XSTRDUP(
+ MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, "./profile"));
+
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = false;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/enable
+ */
+int lib_interface_rip_bfd_enable_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ ri->bfd.enabled = yang_dnode_get_bool(args->dnode, NULL);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-interface:lib/interface/frr-ripd:rip/bfd-monitoring/profile
+ */
+int lib_interface_rip_bfd_profile_modify(struct nb_cb_modify_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ ri->bfd.profile = XSTRDUP(MTYPE_RIP_BFD_PROFILE,
+ yang_dnode_get_string(args->dnode, NULL));
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+int lib_interface_rip_bfd_profile_destroy(struct nb_cb_destroy_args *args)
+{
+ struct interface *ifp;
+ struct rip_interface *ri;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ ifp = nb_running_get_entry(args->dnode, NULL, true);
+ ri = ifp->info;
+ XFREE(MTYPE_RIP_BFD_PROFILE, ri->bfd.profile);
+ rip_bfd_interface_update(ri);
+
+ return NB_OK;
+}
+
+/*
* XPath: /frr-interface:lib/interface/frr-ripd:rip/authentication-key-chain
*/
int lib_interface_rip_authentication_key_chain_modify(
diff --git a/ripd/rip_peer.c b/ripd/rip_peer.c
index c1ab5b14aa..3e8ddeccf2 100644
--- a/ripd/rip_peer.c
+++ b/ripd/rip_peer.c
@@ -11,8 +11,10 @@
#include "linklist.h"
#include "frrevent.h"
#include "memory.h"
+#include "table.h"
#include "ripd/ripd.h"
+#include "ripd/rip_bfd.h"
DEFINE_MTYPE_STATIC(RIPD, RIP_PEER, "RIP peer");
@@ -21,8 +23,9 @@ static struct rip_peer *rip_peer_new(void)
return XCALLOC(MTYPE_RIP_PEER, sizeof(struct rip_peer));
}
-static void rip_peer_free(struct rip_peer *peer)
+void rip_peer_free(struct rip_peer *peer)
{
+ bfd_sess_free(&peer->bfd_session);
EVENT_OFF(peer->t_timeout);
XFREE(MTYPE_RIP_PEER, peer);
}
@@ -62,7 +65,8 @@ static void rip_peer_timeout(struct event *t)
}
/* Get RIP peer. At the same time update timeout thread. */
-static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
+static struct rip_peer *rip_peer_get(struct rip *rip, struct rip_interface *ri,
+ struct in_addr *addr)
{
struct rip_peer *peer;
@@ -73,7 +77,9 @@ static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
} else {
peer = rip_peer_new();
peer->rip = rip;
+ peer->ri = ri;
peer->addr = *addr;
+ rip_bfd_session_update(peer);
listnode_add_sort(rip->peer_list, peer);
}
@@ -87,24 +93,27 @@ static struct rip_peer *rip_peer_get(struct rip *rip, struct in_addr *addr)
return peer;
}
-void rip_peer_update(struct rip *rip, struct sockaddr_in *from, uint8_t version)
+void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->version = version;
}
-void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->recv_badroutes++;
}
-void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from)
+void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from)
{
struct rip_peer *peer;
- peer = rip_peer_get(rip, &from->sin_addr);
+ peer = rip_peer_get(rip, ri, &from->sin_addr);
peer->recv_badpackets++;
}
@@ -155,3 +164,46 @@ void rip_peer_list_del(void *arg)
{
rip_peer_free(arg);
}
+
+void rip_peer_delete_routes(const struct rip_peer *peer)
+{
+ struct route_node *route_node;
+
+ for (route_node = route_top(peer->rip->table); route_node;
+ route_node = route_next(route_node)) {
+ struct rip_info *route_entry;
+ struct listnode *listnode;
+ struct listnode *listnode_next;
+ struct list *list;
+
+ list = route_node->info;
+ if (list == NULL)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(list, listnode, listnode_next,
+ route_entry)) {
+ if (!rip_route_rte(route_entry))
+ continue;
+ if (route_entry->from.s_addr != peer->addr.s_addr)
+ continue;
+
+ if (listcount(list) == 1) {
+ EVENT_OFF(route_entry->t_timeout);
+ EVENT_OFF(route_entry->t_garbage_collect);
+ listnode_delete(list, route_entry);
+ if (list_isempty(list)) {
+ list_delete((struct list **)&route_node
+ ->info);
+ route_unlock_node(route_node);
+ }
+ rip_info_free(route_entry);
+
+ /* Signal the output process to trigger an
+ * update (see section 2.5). */
+ rip_event(peer->rip, RIP_TRIGGERED_UPDATE, 0);
+ } else
+ rip_ecmp_delete(peer->rip, route_entry);
+ break;
+ }
+ }
+}
diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c
index 35c4b1f1be..698dcb982d 100644
--- a/ripd/rip_zebra.c
+++ b/ripd/rip_zebra.c
@@ -13,6 +13,7 @@
#include "zclient.h"
#include "log.h"
#include "vrf.h"
+#include "bfd.h"
#include "ripd/ripd.h"
#include "ripd/rip_debug.h"
#include "ripd/rip_interface.h"
@@ -196,6 +197,7 @@ void rip_zebra_vrf_register(struct vrf *vrf)
vrf->name, vrf->vrf_id);
zclient_send_reg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf->vrf_id);
}
void rip_zebra_vrf_deregister(struct vrf *vrf)
@@ -208,11 +210,13 @@ void rip_zebra_vrf_deregister(struct vrf *vrf)
vrf->name, vrf->vrf_id);
zclient_send_dereg_requests(zclient, vrf->vrf_id);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_DEREGISTER, vrf->vrf_id);
}
static void rip_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
}
zclient_handler *const rip_handlers[] = {
diff --git a/ripd/ripd.c b/ripd/ripd.c
index fb3d574aaa..48f266a94c 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -1123,7 +1123,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
if (from->sin_port != htons(RIP_PORT_DEFAULT)) {
zlog_info("response doesn't come from RIP port: %d",
from->sin_port);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
@@ -1137,7 +1137,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
zlog_info(
"This datagram doesn't come from a valid neighbor: %pI4",
&from->sin_addr);
- rip_peer_bad_packet(rip, from);
+ rip_peer_bad_packet(rip, ri, from);
return;
}
@@ -1147,7 +1147,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
; /* Alredy done in rip_read () */
/* Update RIP peer. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
/* Set RTE pointer. */
rte = packet->rte;
@@ -1176,7 +1176,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
if (!rip_destination_check(rte->prefix)) {
zlog_info(
"Network is net 0 or net 127 or it is not unicast network");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
@@ -1186,7 +1186,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
/* - is the metric valid (i.e., between 1 and 16, inclusive) */
if (!(rte->metric >= 1 && rte->metric <= 16)) {
zlog_info("Route's metric is not in the 1-16 range.");
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
@@ -1195,7 +1195,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
&& rte->nexthop.s_addr != INADDR_ANY) {
zlog_info("RIPv1 packet with nexthop value %pI4",
&rte->nexthop);
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
@@ -1326,7 +1326,7 @@ static void rip_response_process(struct rip_packet *packet, int size,
zlog_warn(
"RIPv2 address %pI4 is not mask /%d applied one",
&rte->prefix, ip_masklen(rte->mask));
- rip_peer_bad_route(rip, from);
+ rip_peer_bad_route(rip, ri, from);
continue;
}
@@ -1643,7 +1643,7 @@ static void rip_request_process(struct rip_packet *packet, int size,
return;
/* RIP peer update. */
- rip_peer_update(rip, from, packet->version);
+ rip_peer_update(rip, ri, from, packet->version);
lim = ((caddr_t)packet) + size;
rte = packet->rte;
@@ -1711,7 +1711,7 @@ static void rip_read(struct event *t)
socklen_t fromlen;
struct interface *ifp = NULL;
struct connected *ifc;
- struct rip_interface *ri;
+ struct rip_interface *ri = NULL;
struct prefix p;
/* Fetch socket then register myself. */
@@ -1743,8 +1743,10 @@ static void rip_read(struct event *t)
/* Which interface is this packet comes from. */
ifc = if_lookup_address((void *)&from.sin_addr, AF_INET,
rip->vrf->vrf_id);
- if (ifc)
+ if (ifc) {
ifp = ifc->ifp;
+ ri = ifp->info;
+ }
/* RIP packet received */
if (IS_RIP_DEBUG_EVENT)
@@ -1753,7 +1755,7 @@ static void rip_read(struct event *t)
ifp ? ifp->name : "unknown", rip->vrf_name);
/* If this packet come from unknown interface, ignore it. */
- if (ifp == NULL) {
+ if (ifp == NULL || ri == NULL) {
zlog_info(
"%s: cannot find interface for packet from %pI4 port %d (VRF %s)",
__func__, &from.sin_addr, ntohs(from.sin_port),
@@ -1779,13 +1781,13 @@ static void rip_read(struct event *t)
if (len < RIP_PACKET_MINSIZ) {
zlog_warn("packet size %d is smaller than minimum size %d", len,
RIP_PACKET_MINSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
if (len > RIP_PACKET_MAXSIZ) {
zlog_warn("packet size %d is larger than max size %d", len,
RIP_PACKET_MAXSIZ);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1793,7 +1795,7 @@ static void rip_read(struct event *t)
if ((len - RIP_PACKET_MINSIZ) % 20) {
zlog_warn("packet size %d is wrong for RIP packet alignment",
len);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1807,7 +1809,7 @@ static void rip_read(struct event *t)
if (packet->version == 0) {
zlog_info("version 0 with command %d received.",
packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1823,12 +1825,11 @@ static void rip_read(struct event *t)
packet->version = RIPv2;
/* Is RIP running or is this RIP neighbor ?*/
- ri = ifp->info;
if (!ri->running && !rip_neighbor_lookup(rip, &from)) {
if (IS_RIP_DEBUG_EVENT)
zlog_debug("RIP is not enabled on interface %s.",
ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1842,7 +1843,7 @@ static void rip_read(struct event *t)
zlog_debug(
" packet's v%d doesn't fit to if version spec",
packet->version);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1857,7 +1858,7 @@ static void rip_read(struct event *t)
"packet RIPv%d is dropped because authentication disabled",
packet->version);
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1893,7 +1894,7 @@ static void rip_read(struct event *t)
zlog_debug(
"RIPv1 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
} else if (ri->auth_type != RIP_NO_AUTH) {
@@ -1906,7 +1907,7 @@ static void rip_read(struct event *t)
zlog_debug(
"RIPv2 authentication failed: no auth RTE in packet");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1916,7 +1917,7 @@ static void rip_read(struct event *t)
zlog_debug(
"RIPv2 dropped because authentication enabled");
ripd_notif_send_auth_type_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
@@ -1952,7 +1953,7 @@ static void rip_read(struct event *t)
zlog_debug("RIPv2 %s authentication failure",
auth_desc);
ripd_notif_send_auth_failure(ifp->name);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
return;
}
}
@@ -1971,16 +1972,16 @@ static void rip_read(struct event *t)
zlog_info(
"Obsolete command %s received, please sent it to routed",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
case RIP_POLL_ENTRY:
zlog_info("Obsolete command %s received",
lookup_msg(rip_msg, packet->command, NULL));
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
default:
zlog_info("Unknown RIP command %d received", packet->command);
- rip_peer_bad_packet(rip, &from);
+ rip_peer_bad_packet(rip, ri, &from);
break;
}
}
@@ -3339,6 +3340,7 @@ void rip_clean(struct rip *rip)
route_table_finish(rip->distance_table);
RB_REMOVE(rip_instance_head, &rip_instances, rip);
+ XFREE(MTYPE_TMP, rip->default_bfd_profile);
XFREE(MTYPE_RIP_VRF_NAME, rip->vrf_name);
XFREE(MTYPE_RIP, rip);
}
diff --git a/ripd/ripd.h b/ripd/ripd.h
index 176a3bfc37..bba3c28069 100644
--- a/ripd/ripd.h
+++ b/ripd/ripd.h
@@ -10,6 +10,7 @@
#include "nexthop.h"
#include "distribute.h"
#include "memory.h"
+#include "bfd.h"
/* RIP version number. */
#define RIPv1 1
@@ -182,6 +183,9 @@ struct rip {
/* RIP queries. */
long queries;
} counters;
+
+ /* Default BFD profile to use with BFD sessions. */
+ char *default_bfd_profile;
};
RB_HEAD(rip_instance_head, rip);
RB_PROTOTYPE(rip_instance_head, rip, entry, rip_instance_compare)
@@ -265,6 +269,9 @@ struct rip_interface {
/* Parent routing instance. */
struct rip *rip;
+ /* Interface data from zebra. */
+ struct interface *ifp;
+
/* RIP is enabled on this interface. */
int enable_network;
int enable_interface;
@@ -318,6 +325,12 @@ struct rip_interface {
/* Passive interface. */
int passive;
+
+ /* BFD information. */
+ struct {
+ bool enabled;
+ char *profile;
+ } bfd;
};
/* RIP peer information. */
@@ -325,6 +338,9 @@ struct rip_peer {
/* Parent routing instance. */
struct rip *rip;
+ /* Back-pointer to RIP interface. */
+ struct rip_interface *ri;
+
/* Peer address. */
struct in_addr addr;
@@ -343,6 +359,9 @@ struct rip_peer {
/* Timeout thread. */
struct event *t_timeout;
+
+ /* BFD information */
+ struct bfd_session_params *bfd_session;
};
struct rip_distance {
@@ -461,16 +480,20 @@ extern void rip_if_rmap_update_interface(struct interface *ifp);
extern int rip_show_network_config(struct vty *vty, struct rip *rip);
extern void rip_show_redistribute_config(struct vty *vty, struct rip *rip);
-extern void rip_peer_update(struct rip *rip, struct sockaddr_in *from,
- uint8_t version);
-extern void rip_peer_bad_route(struct rip *rip, struct sockaddr_in *from);
-extern void rip_peer_bad_packet(struct rip *rip, struct sockaddr_in *from);
+extern void rip_peer_free(struct rip_peer *peer);
+extern void rip_peer_update(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from, uint8_t version);
+extern void rip_peer_bad_route(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
+extern void rip_peer_bad_packet(struct rip *rip, struct rip_interface *ri,
+ struct sockaddr_in *from);
extern void rip_peer_display(struct vty *vty, struct rip *rip);
extern struct rip_peer *rip_peer_lookup(struct rip *rip, struct in_addr *addr);
extern struct rip_peer *rip_peer_lookup_next(struct rip *rip,
struct in_addr *addr);
extern int rip_peer_list_cmp(struct rip_peer *p1, struct rip_peer *p2);
extern void rip_peer_list_del(void *arg);
+void rip_peer_delete_routes(const struct rip_peer *peer);
extern void rip_info_free(struct rip_info *);
extern struct rip *rip_info_get_instance(const struct rip_info *rinfo);
diff --git a/ripd/subdir.am b/ripd/subdir.am
index 98cc765c91..294a05e575 100644
--- a/ripd/subdir.am
+++ b/ripd/subdir.am
@@ -13,6 +13,7 @@ man8 += $(MANBUILD)/frr-ripd.8
endif
ripd_ripd_SOURCES = \
+ ripd/rip_bfd.c \
ripd/rip_cli.c \
ripd/rip_debug.c \
ripd/rip_errors.c \
@@ -31,10 +32,12 @@ ripd_ripd_SOURCES = \
# end
clippy_scan += \
+ ripd/rip_bfd.c \
ripd/rip_cli.c \
# end
noinst_HEADERS += \
+ ripd/rip_bfd.h \
ripd/rip_debug.h \
ripd/rip_errors.h \
ripd/rip_interface.h \
diff --git a/tests/topotests/rip_bfd_topo1/__init__.py b/tests/topotests/rip_bfd_topo1/__init__.py
new file mode 100755
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/__init__.py
diff --git a/tests/topotests/rip_bfd_topo1/r1/bfdd.conf b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf
new file mode 100644
index 0000000000..e848bda89f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/ripd.conf b/tests/topotests/rip_bfd_topo1/r1/ripd.conf
new file mode 100644
index 0000000000..6cef84692a
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/ripd.conf
@@ -0,0 +1,17 @@
+interface r1-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+interface r1-eth1
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ allow-ecmp
+ network 192.168.0.1/24
+ network 192.168.1.1/24
+ redistribute connected
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r1/zebra.conf b/tests/topotests/rip_bfd_topo1/r1/zebra.conf
new file mode 100644
index 0000000000..e3800a9c68
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r1/zebra.conf
@@ -0,0 +1,11 @@
+interface r1-eth0
+ ip address 192.168.0.1/24
+exit
+!
+interface r1-eth1
+ ip address 192.168.1.1/24
+exit
+!
+interface lo
+ ip address 10.254.254.1/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/bfdd.conf b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf
new file mode 100644
index 0000000000..e848bda89f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/ripd.conf b/tests/topotests/rip_bfd_topo1/r2/ripd.conf
new file mode 100644
index 0000000000..35e4688c58
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/ripd.conf
@@ -0,0 +1,11 @@
+interface r2-eth0
+ ip rip bfd
+exit
+!
+router rip
+ bfd default-profile slow
+ network 192.168.0.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r2/staticd.conf b/tests/topotests/rip_bfd_topo1/r2/staticd.conf
new file mode 100644
index 0000000000..6fe93748a0
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/staticd.conf
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r2/zebra.conf b/tests/topotests/rip_bfd_topo1/r2/zebra.conf
new file mode 100644
index 0000000000..cad922f6bb
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r2/zebra.conf
@@ -0,0 +1,8 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.2/32
+exit
+
diff --git a/tests/topotests/rip_bfd_topo1/r3/bfdd.conf b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf
new file mode 100644
index 0000000000..e848bda89f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile slow
+ receive-interval 1000
+ transmit-interval 1000
+ exit
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/ripd.conf b/tests/topotests/rip_bfd_topo1/r3/ripd.conf
new file mode 100644
index 0000000000..0df0bac531
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/ripd.conf
@@ -0,0 +1,11 @@
+interface r3-eth0
+ ip rip bfd
+ ip rip bfd profile slow
+exit
+!
+router rip
+ network 192.168.1.2/24
+ redistribute connected
+ redistribute static
+ timers basic 10 40 30
+exit
diff --git a/tests/topotests/rip_bfd_topo1/r3/staticd.conf b/tests/topotests/rip_bfd_topo1/r3/staticd.conf
new file mode 100644
index 0000000000..6fe93748a0
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/staticd.conf
@@ -0,0 +1 @@
+ip route 10.254.254.100/32 lo
diff --git a/tests/topotests/rip_bfd_topo1/r3/zebra.conf b/tests/topotests/rip_bfd_topo1/r3/zebra.conf
new file mode 100644
index 0000000000..12ffeca42f
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/r3/zebra.conf
@@ -0,0 +1,7 @@
+interface r3-eth0
+ ip address 192.168.1.2/24
+exit
+!
+interface lo
+ ip address 10.254.254.3/32
+exit
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot
new file mode 100644
index 0000000000..1480a8f76d
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.dot
@@ -0,0 +1,58 @@
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #fcb314 dark yellow
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+##### Colors (see http://www.color-hex.com/)
+
+graph template {
+ label="rip_bfd_topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n192.168.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s1\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="r1-eth0\n.1"];
+ r2 -- s1 [label="r2-eth0\n.2"];
+
+ r1 -- s2 [label="r1-eth1\n.1"];
+ r3 -- s2 [label="r1-eth0\n.2"];
+}
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png
new file mode 100644
index 0000000000..e5e362e3e0
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png
Binary files differ
diff --git a/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py
new file mode 100644
index 0000000000..71c90931fb
--- /dev/null
+++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.py
@@ -0,0 +1,252 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+#
+# test_rip_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2023 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+
+"""
+test_rip_bfd_topo1.py: Test RIP BFD integration.
+"""
+
+import sys
+import re
+import pytest
+
+from functools import partial
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+pytestmark = [
+ pytest.mark.bfdd,
+ pytest.mark.ripd,
+]
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r1", "r3")
+ }
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for router_name, router in router_list.items():
+ router.load_config(TopoRouter.RD_BFD, "bfdd.conf")
+ router.load_config(TopoRouter.RD_RIP, "ripd.conf")
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ if router_name in ["r2", "r3"]:
+ router.load_config(TopoRouter.RD_STATIC, "staticd.conf")
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ "Test if routers is still running otherwise skip tests"
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def show_rip_json(router):
+ "Get router 'show ip rip' JSON output"
+ output = router.vtysh_cmd("show ip rip")
+ routes = output.splitlines()[6:]
+ json = {}
+
+ for route in routes:
+ match = re.match(
+ r"(.)\((.)\)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s]+)", route)
+ if match is None:
+ continue
+
+ route_entry = {
+ "code": match[1],
+ "subCode": match[2],
+ "nextHop": match[4],
+ "metric": int(match[5]),
+ "from": match[6],
+ }
+
+ if json.get(match[3]) is None:
+ json[match[3]] = []
+
+ json[match[3]].append(route_entry)
+
+ return json
+
+
+def expect_routes(router, routes, time_amount):
+ "Expect 'routes' in 'router'."
+
+ def test_function():
+ "Internal test function."
+ return topotest.json_cmp(show_rip_json(router), routes)
+
+ _, result = topotest.run_and_expect(test_function,
+ None,
+ count=time_amount,
+ wait=1)
+ assert result is None, "Unexpected routing table in {}".format(
+ router.name)
+
+
+def expect_bfd_peers(router, peers):
+ "Expect 'peers' in 'router' BFD status."
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bfd peers json",
+ peers,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "{} BFD peer status mismatch".format(router)
+
+
+def test_rip_convergence(tgen):
+ "Test that RIP learns the neighbor routes."
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2"
+ }],
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }, {
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.2",
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r1"], [{
+ "peer": "192.168.0.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }, {
+ "peer": "192.168.1.2",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r2"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.3/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r2"], [{
+ "peer": "192.168.0.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.1.1"
+ }],
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 40)
+
+ expect_bfd_peers(tgen.gears["r3"], [{
+ "peer": "192.168.1.1",
+ "status": "up",
+ "receive-interval": 1000,
+ "transmit-interval": 1000,
+ }])
+
+
+def test_rip_bfd_convergence(tgen):
+ "Test that RIP drop the gone neighbor routes."
+
+ tgen.gears["r3"].link_enable("r3-eth0", False)
+
+ expect_routes(
+ tgen.gears["r1"], {
+ "10.254.254.2/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2"
+ }],
+ "10.254.254.3/32": None,
+ "10.254.254.100/32": [{
+ "code": "R",
+ "subCode": "n",
+ "from": "192.168.0.2",
+ }]
+ }, 6)
+
+ expect_routes(
+ tgen.gears["r3"], {
+ "10.254.254.1/32": None,
+ "10.254.254.2/32": None,
+ "10.254.254.100/32": [{
+ "code": "S",
+ "subCode": "r",
+ "from": "self"
+ }]
+ }, 6)
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/yang/frr-ripd.yang b/yang/frr-ripd.yang
index d2088e589e..a4bf50d958 100644
--- a/yang/frr-ripd.yang
+++ b/yang/frr-ripd.yang
@@ -13,6 +13,9 @@ module frr-ripd {
import frr-if-rmap {
prefix frr-if-rmap;
}
+ import frr-bfdd {
+ prefix frr-bfdd;
+ }
import frr-interface {
prefix frr-interface;
}
@@ -376,6 +379,12 @@ module frr-ripd {
}
}
+ leaf default-bfd-profile {
+ description
+ "Use this BFD profile for all peers by default.";
+ type frr-bfdd:profile-ref;
+ }
+
/*
* Operational data.
*/
@@ -678,6 +687,24 @@ module frr-ripd {
"Key-chain name.";
}
}
+
+ container bfd-monitoring {
+ presence
+ "Present if BFD is configured for RIP peers in this interface.";
+
+ leaf enable {
+ type boolean;
+ description
+ "Enable/disable BFD monitoring.";
+ default false;
+ }
+
+ leaf profile {
+ type frr-bfdd:profile-ref;
+ description
+ "BFD profile to use.";
+ }
+ }
}
}
diff --git a/zebra/zebra_ptm.h b/zebra/zebra_ptm.h
index 1b2f7a02d4..4d09474b7f 100644
--- a/zebra/zebra_ptm.h
+++ b/zebra/zebra_ptm.h
@@ -53,7 +53,7 @@ struct zebra_ptm_cb {
(protocol) == ZEBRA_ROUTE_OSPF6 || (protocol) == ZEBRA_ROUTE_ISIS || \
(protocol) == ZEBRA_ROUTE_PIM || \
(protocol) == ZEBRA_ROUTE_OPENFABRIC || \
- (protocol) == ZEBRA_ROUTE_STATIC)
+ (protocol) == ZEBRA_ROUTE_STATIC || (protocol) == ZEBRA_ROUTE_RIP)
void zebra_ptm_init(void);
void zebra_ptm_finish(void);