diff options
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 Binary files differnew file mode 100644 index 0000000000..e5e362e3e0 --- /dev/null +++ b/tests/topotests/rip_bfd_topo1/test_rip_bfd_topo1.png 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); |
