Merge pull request #13246 from opensourcerouting/rip-bfd

ripd: support BFD integration
This commit is contained in:
Russ White 2023-04-25 11:54:32 -04:00 committed by GitHub
commit 257fddaeb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 1002 additions and 39 deletions

View file

@ -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

View file

@ -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`

119
ripd/rip_bfd.c Normal file
View file

@ -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);
}

21
ripd/rip_bfd.h Normal file
View file

@ -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_ */

View file

@ -581,6 +581,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
*/
@ -979,6 +1015,66 @@ void cli_show_ip_rip_authentication_key_chain(struct vty *vty,
yang_dnode_get_string(dnode, NULL));
}
/*
* 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
*/
@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -239,6 +239,14 @@ const struct frr_yang_module_info frr_ripd_info = {
.modify = ripd_instance_version_send_modify,
},
},
{
.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 = {
@ -302,6 +310,28 @@ const struct frr_yang_module_info frr_ripd_info = {
.modify = lib_interface_rip_authentication_key_chain_modify,
},
},
{
.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 = {

View file

@ -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);

View file

@ -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
@ -905,6 +908,40 @@ int ripd_instance_version_send_modify(struct nb_cb_modify_args *args)
return NB_OK;
}
/*
* 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
*/
@ -1070,6 +1107,104 @@ int lib_interface_rip_authentication_password_destroy(
return NB_OK;
}
/*
* 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
*/

View file

@ -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;
}
}
}

View file

@ -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[] = {

View file

@ -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);
}

View file

@ -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);

View file

@ -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 \

View file

View file

@ -0,0 +1,6 @@
bfd
profile slow
receive-interval 1000
transmit-interval 1000
exit
exit

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,6 @@
bfd
profile slow
receive-interval 1000
transmit-interval 1000
exit
exit

View file

@ -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

View file

@ -0,0 +1 @@
ip route 10.254.254.100/32 lo

View file

@ -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

View file

@ -0,0 +1,6 @@
bfd
profile slow
receive-interval 1000
transmit-interval 1000
exit
exit

View file

@ -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

View file

@ -0,0 +1 @@
ip route 10.254.254.100/32 lo

View file

@ -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

View file

@ -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"];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View file

@ -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))

View file

@ -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.";
}
}
}
}

View file

@ -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);