Merge pull request #8184 from opensourcerouting/bfd-bgp-napi

bgpd,lib: rework BGP BFD integration
This commit is contained in:
Igor Ryzhov 2021-03-24 10:36:26 +03:00 committed by GitHub
commit c560085f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1495 additions and 786 deletions

File diff suppressed because it is too large Load Diff

View File

@ -23,22 +23,58 @@
#ifndef _QUAGGA_BGP_BFD_H
#define _QUAGGA_BGP_BFD_H
extern void bgp_bfd_init(void);
#define PEER_IS_MULTIHOP(peer) \
((((peer)->sort == BGP_PEER_IBGP) && !(peer)->shared_network) \
|| is_ebgp_multihop_configured((peer)))
extern void bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer);
extern void bgp_bfd_init(struct thread_master *tm);
extern void bgp_bfd_register_peer(struct peer *peer);
extern void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
const char *addr);
extern void bgp_bfd_deregister_peer(struct peer *peer);
extern void bgp_bfd_reset_peer(struct peer *peer);
extern void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer,
char *addr);
extern void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json,
/**
* Show BFD information helper.
*
* \param vty the VTY pointer.
* \param peer the BGP configuration pointer.
* \param use_json unused.
* \param json_neigh JSON object when called as JSON command.
*/
extern void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
json_object *json_neigh);
extern bool bgp_bfd_is_peer_multihop(struct peer *peer);
/**
* When called on a group it applies configuration to all peers in that group,
* otherwise just applies the configuration to a single peer.
*
* This function should be called when configuration changes either on group
* or peer.
*
* \param p the BGP peer pointer.
* \param pg the BGP group to copy configuration from (it is usually
* `p->group` exception when copying new group configuration
* see `peer_group2peer_config_copy` function case).
*/
extern void bgp_peer_config_apply(struct peer *p, struct peer_group *pg);
/**
* Allocates and configure BFD session for peer. If it is already configured,
* then it does nothing.
*
* Always call `bgp_peer_config_apply` afterwards if you need the changes
* immediately applied.
*/
extern void bgp_peer_configure_bfd(struct peer *p, bool manual);
/**
* Removes BFD configuration from either peer or peer group.
*/
extern void bgp_peer_remove_bfd_config(struct peer *p);
/**
* Special function to handle the case of changing source address. This
* happens when the peer/group is configured with `neigbor X update-source Y`.
*/
extern void bgp_peer_bfd_update_source(struct peer *p);
#endif /* _QUAGGA_BGP_BFD_H */

View File

@ -21,6 +21,7 @@
#include <zebra.h>
#include <lib/version.h>
#include "lib/bfd.h"
#include "lib/printfrr.h"
#include "prefix.h"
#include "linklist.h"
@ -67,6 +68,7 @@ unsigned long conf_bgp_debug_labelpool;
unsigned long conf_bgp_debug_pbr;
unsigned long conf_bgp_debug_graceful_restart;
unsigned long conf_bgp_debug_evpn_mh;
unsigned long conf_bgp_debug_bfd;
unsigned long term_bgp_debug_as4;
unsigned long term_bgp_debug_neighbor_events;
@ -86,6 +88,7 @@ unsigned long term_bgp_debug_labelpool;
unsigned long term_bgp_debug_pbr;
unsigned long term_bgp_debug_graceful_restart;
unsigned long term_bgp_debug_evpn_mh;
unsigned long term_bgp_debug_bfd;
struct list *bgp_debug_neighbor_events_peers = NULL;
struct list *bgp_debug_keepalive_peers = NULL;
@ -2093,6 +2096,31 @@ DEFUN (no_debug_bgp_labelpool,
return CMD_SUCCESS;
}
DEFPY(debug_bgp_bfd, debug_bgp_bfd_cmd,
"[no] debug bgp bfd",
NO_STR
DEBUG_STR
BGP_STR
"Bidirection Forwarding Detection\n")
{
if (vty->node == CONFIG_NODE) {
if (no) {
DEBUG_OFF(bfd, BFD_LIB);
bfd_protocol_integration_set_debug(false);
} else {
DEBUG_ON(bfd, BFD_LIB);
bfd_protocol_integration_set_debug(true);
}
} else {
if (no)
TERM_DEBUG_OFF(bfd, BFD_LIB);
else
TERM_DEBUG_ON(bfd, BFD_LIB);
}
return CMD_SUCCESS;
}
DEFUN (no_debug_bgp,
no_debug_bgp_cmd,
"no debug bgp",
@ -2136,6 +2164,7 @@ DEFUN (no_debug_bgp,
TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
TERM_DEBUG_OFF(bfd, BFD_LIB);
vty_out(vty, "All possible debugging has been turned off\n");
@ -2225,6 +2254,9 @@ DEFUN_NOSH (show_debugging_bgp,
if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
vty_out(vty, " BGP EVPN-MH route debugging is on\n");
if (BGP_DEBUG(bfd, BFD_LIB))
vty_out(vty, " BGP BFD library debugging is on\n");
vty_out(vty, "\n");
return CMD_SUCCESS;
}
@ -2350,6 +2382,11 @@ static int bgp_config_write_debug(struct vty *vty)
write++;
}
if (CONF_BGP_DEBUG(bfd, BFD_LIB)) {
vty_out(vty, "debug bgp bfd\n");
write++;
}
return write;
}
@ -2478,6 +2515,10 @@ void bgp_debug_init(void)
install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd);
install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd);
/* debug bgp bfd */
install_element(ENABLE_NODE, &debug_bgp_bfd_cmd);
install_element(CONFIG_NODE, &debug_bgp_bfd_cmd);
}
/* Return true if this prefix is on the per_prefix_list of prefixes to debug

View File

@ -78,6 +78,7 @@ extern unsigned long conf_bgp_debug_labelpool;
extern unsigned long conf_bgp_debug_pbr;
extern unsigned long conf_bgp_debug_graceful_restart;
extern unsigned long conf_bgp_debug_evpn_mh;
extern unsigned long conf_bgp_debug_bfd;
extern unsigned long term_bgp_debug_as4;
extern unsigned long term_bgp_debug_neighbor_events;
@ -95,6 +96,7 @@ extern unsigned long term_bgp_debug_labelpool;
extern unsigned long term_bgp_debug_pbr;
extern unsigned long term_bgp_debug_graceful_restart;
extern unsigned long term_bgp_debug_evpn_mh;
extern unsigned long term_bgp_debug_bfd;
extern struct list *bgp_debug_neighbor_events_peers;
extern struct list *bgp_debug_keepalive_peers;
@ -139,6 +141,8 @@ struct bgp_debug_filter {
#define BGP_DEBUG_GRACEFUL_RESTART 0x01
#define BGP_DEBUG_BFD_LIB 0x01
#define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b))
#define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b))

View File

@ -1215,8 +1215,9 @@ int bgp_stop(struct peer *peer)
peer->nsf_af_count = 0;
/* deregister peer */
if (peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE)
bgp_bfd_deregister_peer(peer);
if (peer->bfd_config
&& peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE)
bfd_sess_uninstall(peer->bfd_config->session);
if (peer_dynamic_neighbor(peer)
&& !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) {
@ -2122,7 +2123,10 @@ static int bgp_establish(struct peer *peer)
hash_release(peer->bgp->peerhash, peer);
hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
bgp_bfd_reset_peer(peer);
/* Start BFD peer if not already running. */
if (peer->bfd_config)
bgp_peer_bfd_update_source(peer);
return ret;
}

View File

@ -162,6 +162,9 @@ __attribute__((__noreturn__)) void sigint(void)
assert(bm->terminating == false);
bm->terminating = true; /* global flag that shutting down */
/* Disable BFD events to avoid wasting processing. */
bfd_protocol_integration_set_shutdown(true);
bgp_terminate();
bgp_exit(0);

View File

@ -14411,7 +14411,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, "\n");
/* BFD information. */
bgp_bfd_show_info(vty, p, use_json, json_neigh);
if (p->bfd_config)
bgp_bfd_show_info(vty, p, json_neigh);
if (use_json) {
if (p->conf_if) /* Configured interface name. */
@ -16818,11 +16819,8 @@ static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
peer->rtt_expected, peer->rtt_keepalive_conf);
/* bfd */
if (peer->bfd_info) {
if (!peer_group_active(peer) || !g_peer->bfd_info) {
bgp_bfd_peer_config_write(vty, peer, addr);
}
}
if (peer->bfd_config)
bgp_bfd_peer_config_write(vty, peer, addr);
/* password */
if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))

View File

@ -283,20 +283,9 @@ static int bgp_ifp_down(struct interface *ifp)
if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) {
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
#if defined(HAVE_CUMULUS)
/* Take down directly connected EBGP peers as well as
* 1-hop BFD
* tracked (directly connected) IBGP peers.
*/
if ((peer->ttl != BGP_DEFAULT_TTL)
&& (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED)
&& (!peer->bfd_info
|| bgp_bfd_is_peer_multihop(peer)))
#else
/* Take down directly connected EBGP peers */
/* Take down directly connected peers. */
if ((peer->ttl != BGP_DEFAULT_TTL)
&& (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED))
#endif
continue;
if (ifp == peer->nexthop.ifp) {

View File

@ -1161,7 +1161,9 @@ static void peer_free(struct peer *peer)
XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
bfd_info_free(&(peer->bfd_info));
/* Remove BFD configuration. */
if (peer->bfd_config)
bgp_peer_remove_bfd_config(peer);
FOREACH_AFI_SAFI (afi, safi)
bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE);
@ -2394,7 +2396,9 @@ int peer_delete(struct peer *peer)
SET_FLAG(peer->flags, PEER_FLAG_DELETE);
bgp_bfd_deregister_peer(peer);
/* Remove BFD settings. */
if (peer->bfd_config)
bgp_peer_remove_bfd_config(peer);
/* Delete peer route flap dampening configuration. This needs to happen
* before removing the peer from peer groups.
@ -2678,7 +2682,11 @@ static void peer_group2peer_config_copy(struct peer_group *group,
/* Update GR flags for the peer. */
bgp_peer_gr_flags_update(peer);
bgp_bfd_peer_group2peer_copy(conf, peer);
/* Apply BFD settings from group to peer if it exists. */
if (conf->bfd_config) {
bgp_peer_configure_bfd(peer, false);
bgp_peer_config_apply(peer, group);
}
}
/* Peer group's remote AS configuration. */
@ -2768,7 +2776,8 @@ int peer_group_delete(struct peer_group *group)
XFREE(MTYPE_PEER_GROUP_HOST, group->name);
group->name = NULL;
bfd_info_free(&(group->conf->bfd_info));
if (group->conf->bfd_config)
bgp_peer_remove_bfd_config(group->conf);
group->conf->group = NULL;
peer_delete(group->conf);
@ -7700,7 +7709,7 @@ void bgp_init(unsigned short instance)
bgp_clist = community_list_init();
/* BFD init */
bgp_bfd_init();
bgp_bfd_init(bm->master);
bgp_lp_vty_init();

View File

@ -45,6 +45,8 @@
#include "bgp_nexthop.h"
#include "bgp_damp.h"
#include "lib/bfd.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@ -1558,8 +1560,29 @@ struct peer {
#define PEER_RMAP_TYPE_EXPORT (1U << 7) /* neighbor route-map export */
#define PEER_RMAP_TYPE_AGGREGATE (1U << 8) /* aggregate-address route-map */
/* peer specific BFD information */
struct bfd_info *bfd_info;
/** Peer overwrite configuration. */
struct bfd_session_config {
/**
* Manual configuration bit.
*
* This flag only makes sense for real peers (and not groups),
* it keeps track if the user explicitly configured BFD for a
* peer.
*/
bool manual;
/** Control Plane Independent. */
bool cbit;
/** Detection multiplier. */
uint8_t detection_multiplier;
/** Minimum required RX interval. */
uint32_t min_rx;
/** Minimum required TX interval. */
uint32_t min_tx;
/** Profile name. */
char profile[BFD_PROFILE_NAME_LEN];
/** Peer BFD session */
struct bfd_session_params *session;
} * bfd_config;
/* hostname and domainname advertised by host */
char *hostname;

View File

@ -2916,6 +2916,12 @@ Debugging
Display Listen sockets and the vrf that created them. Useful for debugging of when
listen is not working and this is considered a developer debug statement.
.. clicmd:: debug bgp bfd
Enable or disable debugging for BFD events. This will show BFD integration
library messages and BGP BFD integration messages that are mostly state
transitions and validation problems.
.. clicmd:: debug bgp neighbor-events
Enable or disable debugging for neighbor events. This provides general

638
lib/bfd.c
View File

@ -27,6 +27,7 @@
#include "prefix.h"
#include "thread.h"
#include "stream.h"
#include "vrf.h"
#include "zclient.h"
#include "table.h"
#include "vty.h"
@ -568,3 +569,640 @@ int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
return 0;
}
/**
* BFD protocol integration configuration.
*/
/** Events definitions. */
enum bfd_session_event {
/** Remove the BFD session configuration. */
BSE_UNINSTALL,
/** Install the BFD session configuration. */
BSE_INSTALL,
};
/**
* Data structure to do the necessary tricks to hide the BFD protocol
* integration internals.
*/
struct bfd_session_params {
/** Contains the session parameters and more. */
struct bfd_session_arg args;
/** Contains the session state. */
struct bfd_session_status bss;
/** Protocol implementation status update callback. */
bsp_status_update updatecb;
/** Protocol implementation custom data pointer. */
void *arg;
/**
* Next event.
*
* This variable controls what action to execute when the command batch
* finishes. Normally we'd use `thread_add_event` value, however since
* that function is going to be called multiple times and the value
* might be different we'll use this variable to keep track of it.
*/
enum bfd_session_event lastev;
/**
* BFD session configuration event.
*
* Multiple actions might be asked during a command batch (either via
* configuration load or northbound batch), so we'll use this to
* install/uninstall the BFD session parameters only once.
*/
struct thread *installev;
/** BFD session installation state. */
bool installed;
/** BFD session enabled. */
bool enabled;
/** Global BFD paramaters list. */
TAILQ_ENTRY(bfd_session_params) entry;
};
struct bfd_sessions_global {
/**
* Global BFD session parameters list for (re)installation and update
* without code duplication among daemons.
*/
TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
/** Pointer to FRR's event manager. */
struct thread_master *tm;
/** Pointer to zebra client data structure. */
struct zclient *zc;
/** Debugging state. */
bool debugging;
/** Is shutting down? */
bool shutting_down;
};
/** Global configuration variable. */
static struct bfd_sessions_global bsglobal;
/** Global empty address for IPv4/IPv6. */
static const struct in6_addr i6a_zero;
struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg)
{
struct bfd_session_params *bsp;
bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp));
/* Save application data. */
bsp->updatecb = updatecb;
bsp->arg = arg;
/* Set defaults. */
bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT;
bsp->args.ttl = BFD_SINGLE_HOP_TTL;
bsp->args.min_rx = BFD_DEF_MIN_RX;
bsp->args.min_tx = BFD_DEF_MIN_TX;
bsp->args.vrf_id = VRF_DEFAULT;
/* Register in global list. */
TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry);
return bsp;
}
static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
{
/* Peer/local address not configured. */
if (bsp->args.family == 0)
return false;
/* Address configured but invalid. */
if (bsp->args.family != AF_INET && bsp->args.family != AF_INET6) {
if (bsglobal.debugging)
zlog_debug("%s: invalid session family: %d", __func__,
bsp->args.family);
return false;
}
/* Invalid address. */
if (memcmp(&bsp->args.dst, &i6a_zero, sizeof(i6a_zero)) == 0) {
if (bsglobal.debugging) {
if (bsp->args.family == AF_INET)
zlog_debug("%s: invalid address: %pI4",
__func__,
(struct in_addr *)&bsp->args.dst);
else
zlog_debug("%s: invalid address: %pI6",
__func__, &bsp->args.dst);
}
return false;
}
/* Multi hop requires local address. */
if (bsp->args.mhop
&& memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) {
if (bsglobal.debugging)
zlog_debug(
"%s: multi hop but no local address provided",
__func__);
return false;
}
/* Check VRF ID. */
if (bsp->args.vrf_id == VRF_UNKNOWN) {
if (bsglobal.debugging)
zlog_debug("%s: asked for unknown VRF", __func__);
return false;
}
return true;
}
static int _bfd_sess_send(struct thread *t)
{
struct bfd_session_params *bsp = THREAD_ARG(t);
int rv;
/* Validate configuration before trying to send bogus data. */
if (!_bfd_sess_valid(bsp))
return 0;
if (bsp->lastev == BSE_INSTALL) {
bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE
: ZEBRA_BFD_DEST_REGISTER;
} else
bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER;
/* If not installed and asked for uninstall, do nothing. */
if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
return 0;
rv = zclient_bfd_command(bsglobal.zc, &bsp->args);
/* Command was sent successfully. */
if (rv == 0) {
/* Update installation status. */
if (bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
bsp->installed = false;
else if (bsp->args.command == ZEBRA_BFD_DEST_REGISTER)
bsp->installed = true;
}
return 0;
}
static void _bfd_sess_remove(struct bfd_session_params *bsp)
{
/* Not installed, nothing to do. */
if (!bsp->installed)
return;
/* Cancel any pending installation request. */
THREAD_OFF(bsp->installev);
/* Send request to remove any session. */
bsp->lastev = BSE_UNINSTALL;
thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
}
void bfd_sess_free(struct bfd_session_params **bsp)
{
if (*bsp == NULL)
return;
/* Remove any installed session. */
_bfd_sess_remove(*bsp);
/* Remove from global list. */
TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
/* Free the memory and point to NULL. */
XFREE(MTYPE_BFD_INFO, (*bsp));
}
void bfd_sess_enable(struct bfd_session_params *bsp, bool enable)
{
/* Remove the session when disabling. */
if (!enable)
_bfd_sess_remove(bsp);
bsp->enabled = enable;
}
void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
struct in_addr *src, struct in_addr *dst)
{
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
bsp->args.family = AF_INET;
/* Clean memory, set zero value and avoid static analyser warnings. */
memset(&bsp->args.src, 0, sizeof(bsp->args.src));
memset(&bsp->args.dst, 0, sizeof(bsp->args.dst));
/* Copy the equivalent of IPv4 to arguments structure. */
if (src)
memcpy(&bsp->args.src, src, sizeof(struct in_addr));
assert(dst);
memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
}
void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
struct in6_addr *src, struct in6_addr *dst)
{
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
bsp->args.family = AF_INET6;
/* Clean memory, set zero value and avoid static analyser warnings. */
memset(&bsp->args.src, 0, sizeof(bsp->args.src));
if (src)
bsp->args.src = *src;
assert(dst);
bsp->args.dst = *dst;
}
void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
{
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
if (ifname == NULL) {
bsp->args.ifname[0] = 0;
bsp->args.ifnamelen = 0;
return;
}
if (strlcpy(bsp->args.ifname, ifname, sizeof(bsp->args.ifname))
> sizeof(bsp->args.ifname))
zlog_warn("%s: interface name truncated: %s", __func__, ifname);
bsp->args.ifnamelen = strlen(bsp->args.ifname);
}
void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile)
{
if (profile == NULL) {
bsp->args.profile[0] = 0;
bsp->args.profilelen = 0;
return;
}
if (strlcpy(bsp->args.profile, profile, sizeof(bsp->args.profile))
> sizeof(bsp->args.profile))
zlog_warn("%s: profile name truncated: %s", __func__, profile);
bsp->args.profilelen = strlen(bsp->args.profile);
}
void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
{
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
bsp->args.vrf_id = vrf_id;
}
void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl)
{
assert(min_ttl != 0);
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
/* Invert TTL value: protocol expects number of hops. */
min_ttl = (BFD_SINGLE_HOP_TTL + 1) - min_ttl;
bsp->args.ttl = min_ttl;
bsp->args.mhop = (min_ttl > 1);
}
void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl)
{
/* If already installed, remove the old setting. */
_bfd_sess_remove(bsp);
bsp->args.ttl = min_ttl;
bsp->args.mhop = (min_ttl > 1);
}
void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable)
{
bsp->args.cbit = enable;
}
void bfd_sess_set_timers(struct bfd_session_params *bsp,
uint8_t detection_multiplier, uint32_t min_rx,
uint32_t min_tx)
{
bsp->args.detection_multiplier = detection_multiplier;
bsp->args.min_rx = min_rx;
bsp->args.min_tx = min_tx;
}
void bfd_sess_install(struct bfd_session_params *bsp)
{
/* Don't attempt to install/update when disabled. */
if (!bsp->enabled)
return;
bsp->lastev = BSE_INSTALL;
thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
}
void bfd_sess_uninstall(struct bfd_session_params *bsp)
{
bsp->lastev = BSE_UNINSTALL;
thread_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
}
enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
{
return bsp->bss.state;
}
uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp)
{
return ((BFD_SINGLE_HOP_TTL + 1) - bsp->args.ttl);
}
uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp)
{
return bsp->args.ttl;
}
const char *bfd_sess_profile(const struct bfd_session_params *bsp)
{
return bsp->args.profilelen ? bsp->args.profile : NULL;
}
void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
struct in6_addr *src, struct in6_addr *dst)
{
*family = bsp->args.family;
if (src)
*src = bsp->args.src;
if (dst)
*dst = bsp->args.dst;
}
const char *bfd_sess_interface(const struct bfd_session_params *bsp)
{
if (bsp->args.ifnamelen)
return bsp->args.ifname;
return NULL;
}
const char *bfd_sess_vrf(const struct bfd_session_params *bsp)
{
return vrf_id_to_name(bsp->args.vrf_id);
}
vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp)
{
return bsp->args.vrf_id;
}
bool bfd_sess_cbit(const struct bfd_session_params *bsp)
{
return bsp->args.cbit;
}
void bfd_sess_timers(const struct bfd_session_params *bsp,
uint8_t *detection_multiplier, uint32_t *min_rx,
uint32_t *min_tx)
{
*detection_multiplier = bsp->args.detection_multiplier;
*min_rx = bsp->args.min_rx;
*min_tx = bsp->args.min_tx;
}
void bfd_sess_show(struct vty *vty, struct json_object *json,
struct bfd_session_params *bsp)
{
json_object *json_bfd = NULL;
char time_buf[64];
/* Show type. */
if (json) {
json_bfd = json_object_new_object();
if (bsp->args.mhop)
json_object_string_add(json_bfd, "type", "multi hop");
else
json_object_string_add(json_bfd, "type", "single hop");
} else
vty_out(vty, " BFD: Type: %s\n",
bsp->args.mhop ? "multi hop" : "single hop");
/* Show configuration. */
if (json) {
json_object_int_add(json_bfd, "detectMultiplier",
bsp->args.detection_multiplier);
json_object_int_add(json_bfd, "rxMinInterval",
bsp->args.min_rx);
json_object_int_add(json_bfd, "txMinInterval",
bsp->args.min_tx);
} else {
vty_out(vty,
" Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
bsp->args.detection_multiplier, bsp->args.min_rx,
bsp->args.min_tx);
}
bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf));
if (json) {
json_object_string_add(json_bfd, "status",
bfd_get_status_str(bsp->bss.state));
json_object_string_add(json_bfd, "lastUpdate", time_buf);
} else
vty_out(vty, " Status: %s, Last update: %s\n",
bfd_get_status_str(bsp->bss.state), time_buf);
if (json)
json_object_object_add(json, "peerBfdInfo", json_bfd);
else
vty_out(vty, "\n");
}
/*
* Zebra communication related.
*/
/**
* Callback for reinstallation of all registered BFD sessions.
*
* Use this as `zclient` `bfd_dest_replay` callback.
*/
static int zclient_bfd_session_reply(ZAPI_CALLBACK_ARGS)
{
struct bfd_session_params *bsp;
/* Do nothing when shutting down. */
if (bsglobal.shutting_down)
return 0;
if (bsglobal.debugging)
zlog_debug("%s: sending all sessions registered", __func__);
/* Send the client registration */
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
/* Replay all activated peers. */
TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
/* Skip disabled sessions. */
if (!bsp->enabled)
continue;
/* We are reconnecting, so we must send installation. */
bsp->installed = false;
/* Cancel any pending installation request. */
THREAD_OFF(bsp->installev);
/* Ask for installation. */
bsp->lastev = BSE_INSTALL;
thread_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
}
return 0;
}
static int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
{
struct bfd_session_params *bsp;
size_t sessions_updated = 0;
struct interface *ifp;
int remote_cbit = false;
int state = BFD_STATUS_UNKNOWN;
time_t now;
size_t addrlen;
struct prefix dp;
struct prefix sp;
char ifstr[128], cbitstr[32];
/* Do nothing when shutting down. */
if (bsglobal.shutting_down)
return 0;
ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit,
vrf_id);
if (bsglobal.debugging) {
ifstr[0] = 0;
if (ifp)
snprintf(ifstr, sizeof(ifstr), " (interface %s)",
ifp->name);
snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)",
remote_cbit ? "yes" : "no");
zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__, &sp,
&dp, ifstr, vrf_id_to_name(vrf_id), vrf_id, cbitstr,
bfd_get_status_str(state));
}
switch (dp.family) {
case AF_INET:
addrlen = sizeof(struct in_addr);
break;
case AF_INET6:
addrlen = sizeof(struct in6_addr);
break;
default:
/* Unexpected value. */
assert(0);
break;
}
/* Cache current time to avoid multiple monotime clock calls. */
now = monotime(NULL);
/* Notify all matching sessions about update. */
TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
/* Skip disabled or not installed entries. */
if (!bsp->enabled || !bsp->installed)
continue;
/* Skip different VRFs. */
if (bsp->args.vrf_id != vrf_id)
continue;
/* Skip different families. */
if (bsp->args.family != dp.family)
continue;
/* Skip different interface. */
if (bsp->args.ifnamelen && ifp
&& strcmp(bsp->args.ifname, ifp->name) != 0)
continue;
/* Skip non matching destination addresses. */
if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0)
continue;
/*
* Source comparison test:
* We will only compare source if BFD daemon provided the
* source address and the protocol set a source address in
* the configuration otherwise we'll just skip it.
*/
if (sp.family && memcmp(&bsp->args.src, &i6a_zero, addrlen) != 0
&& memcmp(&sp.u, &i6a_zero, addrlen) != 0
&& memcmp(&bsp->args.src, &sp.u, addrlen) != 0)
continue;
/* No session state change. */
if ((int)bsp->bss.state == state)
continue;
bsp->bss.last_event = now;
bsp->bss.previous_state = bsp->bss.state;
bsp->bss.state = state;
bsp->bss.remote_cbit = remote_cbit;
bsp->updatecb(bsp, &bsp->bss, bsp->arg);
sessions_updated++;
}
if (bsglobal.debugging)
zlog_debug("%s: sessions updated: %zu", __func__,
sessions_updated);
return 0;
}
void bfd_protocol_integration_init(struct zclient *zc, struct thread_master *tm)
{
/* Initialize data structure. */
TAILQ_INIT(&bsglobal.bsplist);
/* Copy pointers. */
bsglobal.zc = zc;
bsglobal.tm = tm;
/* Install our callbacks. */
zc->interface_bfd_dest_update = zclient_bfd_session_update;
zc->bfd_dest_replay = zclient_bfd_session_reply;
/* Send the client registration */
bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
}
void bfd_protocol_integration_set_debug(bool enable)
{
bsglobal.debugging = enable;
}
void bfd_protocol_integration_set_shutdown(bool enable)
{
bsglobal.shutting_down = enable;
}
bool bfd_protocol_integration_debug(void)
{
return bsglobal.debugging;
}
bool bfd_protocol_integration_shutting_down(void)
{
return bsglobal.shutting_down;
}

327
lib/bfd.h
View File

@ -128,6 +128,305 @@ extern void bfd_gbl_exit(void);
* BFD new API.
*/
/* Forward declaration of argument struct. */
struct bfd_session_params;
/** Session state definitions. */
enum bfd_session_state {
/** Session state is unknown or not initialized. */
BSS_UNKNOWN = BFD_STATUS_UNKNOWN,
/** Local or remote peer administratively shutdown the session. */
BSS_ADMIN_DOWN = BFD_STATUS_ADMIN_DOWN,
/** Session is down. */
BSS_DOWN = BFD_STATUS_DOWN,
/** Session is up and working correctly. */
BSS_UP = BFD_STATUS_UP,
};
/** BFD session status information */
struct bfd_session_status {
/** Current session state. */
enum bfd_session_state state;
/** Previous session state. */
enum bfd_session_state previous_state;
/** Remote Control Plane Independent bit state. */
bool remote_cbit;
/** Last event occurrence. */
time_t last_event;
};
/**
* Session status update callback.
*
* \param bsp BFD session parameters.
* \param bss BFD session status.
* \param arg application independent data.
*/
typedef void (*bsp_status_update)(struct bfd_session_params *bsp,
const struct bfd_session_status *bss,
void *arg);
/**
* Allocates and initializes the session parameters.
*
* \param updatedb status update notification callback.
* \param args application independent data.
*
* \returns pointer to configuration storage.
*/
struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *args);
/**
* Uninstall session if installed and free resources allocated by the
* parameters. Already sets pointer to `NULL` to avoid dangling references.
*
* \param bsp session parameters.
*/
void bfd_sess_free(struct bfd_session_params **bsp);
/**
* Enable/disable session installation.
*
* \param bsp session parameters.
* \param enable knob variable.
*/
void bfd_sess_enable(struct bfd_session_params *bsp, bool enable);
/**
* Set the local and peer address of the BFD session.
*
* \param bsp BFD session parameters.
* \param src local address (optional, can be `NULL`).
* \param dst remote address (mandatory).
*/
void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
struct in_addr *src, struct in_addr *dst);
/**
* Set the local and peer address of the BFD session.
*
* \param bsp BFD session parameters.
* \param src local address (optional, can be `NULL`).
* \param dst remote address (mandatory).
*/
void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
struct in6_addr *src, struct in6_addr *dst);
/**
* Configure the BFD session interface.
*
* \param bsp BFD session parameters.
* \param ifname interface name (or `NULL` to remove it).
*/
void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname);
/**
* Configure the BFD session profile name.
*
* \param bsp BFD session parameters.
* \param profile profile name (or `NULL` to remove it).
*/
void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile);
/**
* Configure the BFD session VRF.
*
* \param bsp BFD session parameters.
* \param vrf_id the VRF identification number.
*/
void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id);
/**
* Configure the BFD session single/multi hop setting.
*
* \param bsp BFD session parameters.
* \param min_ttl minimum TTL value expected (255 for single hop, 254 for
* multi hop with single hop, 253 for multi hop with two hops
* and so on). See `BFD_SINGLE_HOP_TTL` and
* `BFD_MULTI_HOP_MIN_TTL` for defaults.
*
* To simplify things if your protocol only knows the amount of hops it is
* better to use `bfd_sess_set_hops` instead.
*/
void bfd_sess_set_mininum_ttl(struct bfd_session_params *bsp, uint8_t min_ttl);
/** To use single hop the minimum TTL must be set to this. */
#define BFD_SINGLE_HOP_TTL 255
/** To use multi hop the minimum TTL must be set to this or less. */
#define BFD_MULTI_HOP_MIN_TTL 254
/**
* This function is the inverted version of `bfd_sess_set_minimum_ttl`.
* Instead of receiving the minimum expected TTL, it receives the amount of
* hops the protocol will jump.
*
* \param bsp BFD session parameters.
* \param min_ttl minimum amount of hops expected (1 for single hop, 2 or
* more for multi hop).
*/
void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t min_ttl);
/**
* Configure the BFD session to set the Control Plane Independent bit.
*
* \param bsp BFD session parameters.
* \param enable BFD Control Plane Independent state.
*/
void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable);
/**
* DEPRECATED: please avoid using timers directly and use profiles instead.
*
* Configures the BFD session timers to use. This is specially useful with
* `ptm-bfd` which does not support timers.
*
* \param bsp BFD session parameters.
* \param detection_multiplier the detection multiplier value.
* \param min_rx minimum required receive period.
* \param min_tx minimum required transmission period.
*/
void bfd_sess_set_timers(struct bfd_session_params *bsp,
uint8_t detection_multiplier, uint32_t min_rx,
uint32_t min_tx);
/**
* Installs or updates the BFD session based on the saved session arguments.
*
* \param bsp session parameters.
*/
void bfd_sess_install(struct bfd_session_params *bsp);
/**
* Uninstall the BFD session based on the saved session arguments.
*
* \param bsp session parameters.
*/
void bfd_sess_uninstall(struct bfd_session_params *bsp);
/**
* Get BFD session current status.
*
* \param bsp session parameters.
*
* \returns BFD session status data structure.
*/
enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp);
/**
* Get BFD session minimum TTL configured value.
*
* \param bsp session parameters.
*
* \returns configured minimum TTL.
*/
uint8_t bfd_sess_minimum_ttl(const struct bfd_session_params *bsp);
/**
* Inverted version of `bfd_sess_minimum_ttl`. Gets the amount of hops in the
* way to the peer.
*
* \param bsp session parameters.
*
* \returns configured amount of hops.
*/
uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp);
/**
* Get BFD session profile configured value.
*
* \param bsp session parameters.
*
* \returns configured profile name (or `NULL` if empty).
*/
const char *bfd_sess_profile(const struct bfd_session_params *bsp);
/**
* Get BFD session addresses.
*
* \param bsp session parameters.
* \param family the address family being used (AF_INET or AF_INET6).
* \param src source address (optional, may be `NULL`).
* \param dst peer address (optional, may be `NULL`).
*/
void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
struct in6_addr *src, struct in6_addr *dst);
/**
* Get BFD session interface name.
*
* \param bsp session parameters.
*
* \returns `NULL` if not set otherwise the interface name.
*/
const char *bfd_sess_interface(const struct bfd_session_params *bsp);
/**
* Get BFD session VRF name.
*
* \param bsp session parameters.
*
* \returns the VRF name.
*/
const char *bfd_sess_vrf(const struct bfd_session_params *bsp);
/**
* Get BFD session VRF ID.
*
* \param bsp session parameters.
*
* \returns the VRF name.
*/
vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp);
/**
* Get BFD session control plane independent bit configuration state.
*
* \param bsp session parameters.
*
* \returns `true` if enabled otherwise `false`.
*/
bool bfd_sess_cbit(const struct bfd_session_params *bsp);
/**
* DEPRECATED: please avoid using timers directly and use profiles instead.
*
* Gets the configured timers.
*
* \param bsp BFD session parameters.
* \param detection_multiplier the detection multiplier value.
* \param min_rx minimum required receive period.
* \param min_tx minimum required transmission period.
*/
void bfd_sess_timers(const struct bfd_session_params *bsp,
uint8_t *detection_multiplier, uint32_t *min_rx,
uint32_t *min_tx);
/**
* Show BFD session configuration and status. If `json` is provided (e.g. not
* `NULL`) then information will be inserted in object, otherwise printed to
* `vty`.
*
* \param vty Pointer to `vty` for outputting text.
* \param json (optional) JSON object pointer.
* \param bsp session parameters.
*/
void bfd_sess_show(struct vty *vty, struct json_object *json,
struct bfd_session_params *bsp);
/**
* Initializes the BFD integration library. This function executes the
* following actions:
*
* - Copy the `struct thread_master` pointer to use as "thread" to execute
* the BFD session parameters installation.
* - Copy the `struct zclient` pointer to install its callbacks.
* - Initializes internal data structures.
*
* \param tm normally the daemon main thread event manager.
* \param zc the zebra client of the daemon.
*/
void bfd_protocol_integration_init(struct zclient *zc,
struct thread_master *tm);
/**
* BFD session registration arguments.
*/
@ -205,6 +504,34 @@ struct bfd_session_arg {
*/
extern int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *arg);
/**
* Enables or disables BFD protocol integration API debugging.
*
* \param enable new API debug state.
*/
extern void bfd_protocol_integration_set_debug(bool enable);
/**
* Sets shutdown mode so no more events are processed.
*
* This is useful to avoid the event storm that happens caused by network,
* interfaces or VRFs removal. It should also avoid some crashes due hanging
* pointers left overs by protocol.
*
* \param enable new API shutdown state.
*/
extern void bfd_protocol_integration_set_shutdown(bool enable);
/**
* Get API debugging state.
*/
extern bool bfd_protocol_integration_debug(void);
/**
* Get API shutdown state.
*/
extern bool bfd_protocol_integration_shutting_down(void);
#ifdef __cplusplus
}
#endif