]> git.puffer.fish Git - matthieu/frr.git/commitdiff
bgpd: rework BFD integration
authorRafael Zalamena <rzalamena@opensourcerouting.org>
Fri, 26 Feb 2021 19:50:51 +0000 (16:50 -0300)
committerRafael Zalamena <rzalamena@opensourcerouting.org>
Tue, 23 Mar 2021 15:40:10 +0000 (12:40 -0300)
Remove old BFD API usage and replace it with the new one.

Highlights:

 - More shared code: the daemon gets notified with callbacks instead of
   having to roll its own code to find the notified sessions.

 - Less code to integrate with BFD.

 - Remove hidden commands to configure single / multi hop. Use
   protocol data instead.

   BGP can determine if a peer is single/multi hop according to the
   following criteria:

    a. If the IP address is a link-local address (single hop)

    b. The network is shared with peer (single hop)

    c. BGP is configured for eBGP multi hop / TTL security (multi hop)

 - Respect the configuration hierarchy:

    a. Peer configuration take precendence over peer-group
       configuration.

    b. When peer group configuration is removed, reset peer
       BFD configurations to defaults (unless peer had specific
       configs).

       Example:

         neighbor foo peer-group
         neighbor foo bfd profile X
         neighbor 192.168.0.2 peer-group foo
         neighbor 192.168.0.2 bfd
         ! If peer-group is removed the profile configuration gets
         ! removed from peer 192.168.0.2, but BFD will still enabled
         ! because of the neighbor specific bfd configuration.

Signed-off-by: Rafael Zalamena <rzalamena@opensourcerouting.org>
bgpd/bgp_bfd.c
bgpd/bgp_bfd.h
bgpd/bgp_fsm.c
bgpd/bgp_main.c
bgpd/bgp_vty.c
bgpd/bgpd.c
bgpd/bgpd.h

index 11e9344d1cafafec0d912a27d1a337790fe2d83e..b618fa28e75966f66ee1f2881a4c954dc8f878c9 100644 (file)
@@ -29,6 +29,7 @@
 #include "thread.h"
 #include "buffer.h"
 #include "stream.h"
+#include "vrf.h"
 #include "zclient.h"
 #include "bfd.h"
 #include "lib/json.h"
 #include "bgpd/bgp_debug.h"
 #include "bgpd/bgp_vty.h"
 
+DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data");
+
 extern struct zclient *zclient;
 
-/*
- * bgp_bfd_peer_group2peer_copy - Copy the BFD information from peer group
- * template
- *                                to peer.
- */
-void bgp_bfd_peer_group2peer_copy(struct peer *conf, struct peer *peer)
+static void bfd_session_status_update(struct bfd_session_params *bsp,
+                                     const struct bfd_session_status *bss,
+                                     void *arg)
 {
-       struct bfd_info *bfd_info;
-       struct bfd_info *conf_bfd_info;
+       struct peer *peer = arg;
 
-       if (!conf->bfd_info)
-               return;
-
-       conf_bfd_info = (struct bfd_info *)conf->bfd_info;
-       if (!peer->bfd_info)
-               peer->bfd_info = bfd_info_create();
-
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       if (BGP_DEBUG(zebra, ZEBRA))
+               zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s",
+                          __func__, peer->conf_if ? peer->conf_if : peer->host,
+                          bfd_sess_vrf(bsp), bfd_sess_vrf_id(bsp),
+                          bfd_get_status_str(bss->previous_state),
+                          bfd_get_status_str(bss->state));
+
+       if (bss->state == BSS_DOWN && bss->previous_state == BSS_UP) {
+               if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)
+                   && bfd_sess_cbit(bsp) && !bss->remote_cbit) {
+                       if (BGP_DEBUG(zebra, ZEBRA))
+                               zlog_info(
+                                       "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
+                                       peer->host);
+                       return;
+               }
+               peer->last_reset = PEER_DOWN_BFD_DOWN;
+               BGP_EVENT_ADD(peer, BGP_Stop);
+       }
 
-       /* Copy BFD parameter values */
-       bfd_info->required_min_rx = conf_bfd_info->required_min_rx;
-       bfd_info->desired_min_tx = conf_bfd_info->desired_min_tx;
-       bfd_info->detect_mult = conf_bfd_info->detect_mult;
-       bfd_info->type = conf_bfd_info->type;
+       if (bss->state == BSS_UP && bss->previous_state != BSS_UP
+           && peer->status != Established) {
+               if (!BGP_PEER_START_SUPPRESSED(peer)) {
+                       bgp_fsm_nht_update(peer, true);
+                       BGP_EVENT_ADD(peer, BGP_Start);
+               }
+       }
 }
 
-/*
- * bgp_bfd_is_peer_multihop - returns whether BFD peer is multi-hop or single
- * hop.
- */
-bool bgp_bfd_is_peer_multihop(struct peer *peer)
+void bgp_peer_config_apply(struct peer *p, struct peer_group *pg)
 {
-       struct bfd_info *bfd_info;
+       struct listnode *n;
+       struct peer *pn;
+       struct peer *gconfig;
+
+       /* When called on a group, apply to all peers. */
+       if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) {
+               for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
+                       bgp_peer_config_apply(pn, pg);
+               return;
+       }
 
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* No group, just use current configuration. */
+       if (pg == NULL || pg->conf->bfd_config == NULL) {
+               bfd_sess_set_timers(p->bfd_config->session,
+                                   p->bfd_config->detection_multiplier,
+                                   p->bfd_config->min_rx,
+                                   p->bfd_config->min_tx);
+               bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
+               bfd_sess_set_profile(p->bfd_config->session,
+                                    p->bfd_config->profile);
+               bfd_sess_install(p->bfd_config->session);
+               return;
+       }
 
-       if (!bfd_info)
-               return false;
+       /*
+        * Check if the group configuration was overwritten or apply group
+        * configuration.
+        */
+       gconfig = pg->conf;
+
+       /*
+        * If using default control plane independent configuration,
+        * then prefer group's (e.g. it means it wasn't manually configured).
+        */
+       if (!p->bfd_config->cbit)
+               bfd_sess_set_cbit(p->bfd_config->session,
+                                 gconfig->bfd_config->cbit);
+       else
+               bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
 
-       if ((bfd_info->type == BFD_TYPE_MULTIHOP)
-           || ((peer->sort == BGP_PEER_IBGP) && !peer->shared_network)
-           || is_ebgp_multihop_configured(peer))
-               return true;
+       /* If no profile was specified in peer, then use the group profile. */
+       if (p->bfd_config->profile[0] == 0)
+               bfd_sess_set_profile(p->bfd_config->session,
+                                    gconfig->bfd_config->profile);
        else
-               return false;
+               bfd_sess_set_profile(p->bfd_config->session,
+                                    p->bfd_config->profile);
+
+       /* If no specific timers were configured, then use the group timers. */
+       if (p->bfd_config->detection_multiplier == BFD_DEF_DETECT_MULT
+           || p->bfd_config->min_rx == BFD_DEF_MIN_RX
+           || p->bfd_config->min_tx == BFD_DEF_MIN_TX)
+               bfd_sess_set_timers(p->bfd_config->session,
+                                   gconfig->bfd_config->detection_multiplier,
+                                   gconfig->bfd_config->min_rx,
+                                   gconfig->bfd_config->min_tx);
+       else
+               bfd_sess_set_timers(p->bfd_config->session,
+                                   p->bfd_config->detection_multiplier,
+                                   p->bfd_config->min_rx,
+                                   p->bfd_config->min_tx);
+
+       bfd_sess_install(p->bfd_config->session);
 }
 
-/*
- * bgp_bfd_peer_sendmsg - Format and send a Peer register/Unregister
- *                        command to Zebra to be forwarded to BFD
- */
-static void bgp_bfd_peer_sendmsg(struct peer *peer, int command)
+void bgp_peer_bfd_update_source(struct peer *p)
 {
-       struct bfd_session_arg arg = {};
-       struct bfd_info *bfd_info;
-       int multihop;
-       vrf_id_t vrf_id;
-       size_t addrlen;
-
-       /*
-        * XXX: some pointers are dangling during shutdown, so instead of
-        * trying to send a message during signal handlers lets just wait BGP
-        * to terminate zebra's connection and BFD will automatically find
-        * out that we are no longer expecting notifications.
-        *
-        * The pointer that is causing a crash here is `peer->nexthop.ifp`.
-        * That happens because at this point of the shutdown all interfaces are
-        * already `free()`d.
-        */
-       if (bm->terminating)
+       struct bfd_session_params *session = p->bfd_config->session;
+       bool changed = false;
+       int family;
+       union {
+               struct in_addr v4;
+               struct in6_addr v6;
+       } src, dst;
+
+       /* Nothing to do for groups. */
+       if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
                return;
 
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* Update peer's source/destination addresses. */
+       bfd_sess_addresses(session, &family, &src.v6, &dst.v6);
+       if (family == AF_INET) {
+               if ((p->su_local
+                    && p->su_local->sin.sin_addr.s_addr != src.v4.s_addr)
+                   || p->su.sin.sin_addr.s_addr != dst.v4.s_addr) {
+                       if (BGP_DEBUG(zebra, ZEBRA))
+                               zlog_debug(
+                                       "%s: address [%pI4->%pI4] to [%pI4->%pI4]",
+                                       __func__, &src.v4, &dst.v4,
+                                       p->su_local ? &p->su_local->sin.sin_addr
+                                                   : &src.v4,
+                                       &p->su.sin.sin_addr);
+
+                       bfd_sess_set_ipv4_addrs(
+                               session,
+                               p->su_local ? &p->su_local->sin.sin_addr : NULL,
+                               &p->su.sin.sin_addr);
+                       changed = true;
+               }
+       } else {
+               if ((p->su_local
+                    && memcmp(&p->su_local->sin6, &src.v6, sizeof(src.v6)))
+                   || memcmp(&p->su.sin6, &dst.v6, sizeof(dst.v6))) {
+                       if (BGP_DEBUG(zebra, ZEBRA))
+                               zlog_debug(
+                                       "%s: address [%pI6->%pI6] to [%pI6->%pI6]",
+                                       __func__, &src.v6, &dst.v6,
+                                       p->su_local
+                                               ? &p->su_local->sin6.sin6_addr
+                                               : &src.v6,
+                                       &p->su.sin6.sin6_addr);
+
+                       bfd_sess_set_ipv6_addrs(
+                               session,
+                               p->su_local ? &p->su_local->sin6.sin6_addr
+                                           : NULL,
+                               &p->su.sin6.sin6_addr);
+                       changed = true;
+               }
+       }
 
-       vrf_id = peer->bgp->vrf_id;
+       /* Update interface. */
+       if (p->nexthop.ifp && bfd_sess_interface(session) == NULL) {
+               if (BGP_DEBUG(zebra, ZEBRA))
+                       zlog_debug("%s: interface none to %s", __func__,
+                                  p->nexthop.ifp->name);
 
-       if (command == ZEBRA_BFD_DEST_DEREGISTER) {
-               multihop =
-                       CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP);
-               UNSET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP);
-       } else {
-               multihop = bgp_bfd_is_peer_multihop(peer);
-               if ((command == ZEBRA_BFD_DEST_REGISTER) && multihop)
-                       SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_TYPE_MULTIHOP);
+               bfd_sess_set_interface(session, p->nexthop.ifp->name);
+               changed = true;
        }
-       /* while graceful restart with fwd path preserved
-        * and bfd controlplane check not configured is not kept
-        * keep bfd independent controlplane bit set to 1
+
+       /*
+        * Update TTL.
+        *
+        * Two cases:
+        * - We detected that the peer is a hop away from us (remove multi hop).
+        *   (this happens when `p->shared_network` is set to `true`)
+        * - eBGP multi hop / TTL security changed.
         */
-       if (!CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_RESTART)
-           && !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)
-           && !CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE))
-               SET_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
-
-       /* Set all message arguments. */
-       arg.family = peer->su.sa.sa_family;
-       addrlen = arg.family == AF_INET ? sizeof(struct in_addr)
-                                       : sizeof(struct in6_addr);
-
-       if (arg.family == AF_INET)
-               memcpy(&arg.dst, &peer->su.sin.sin_addr, addrlen);
-       else
-               memcpy(&arg.dst, &peer->su.sin6.sin6_addr, addrlen);
+       if (!PEER_IS_MULTIHOP(p) && bfd_sess_hop_count(session) > 1) {
+               if (BGP_DEBUG(zebra, ZEBRA))
+                       zlog_debug("%s: TTL %d to 1", __func__,
+                                  bfd_sess_hop_count(session));
 
-       if (peer->su_local) {
-               if (arg.family == AF_INET)
-                       memcpy(&arg.src, &peer->su_local->sin.sin_addr,
-                              addrlen);
-               else
-                       memcpy(&arg.src, &peer->su_local->sin6.sin6_addr,
-                              addrlen);
+               bfd_sess_set_hop_count(session, 1);
+               changed = true;
        }
+       if (PEER_IS_MULTIHOP(p) && p->ttl != bfd_sess_hop_count(session)) {
+               if (BGP_DEBUG(zebra, ZEBRA))
+                       zlog_debug("%s: TTL %d to %d", __func__,
+                                  bfd_sess_hop_count(session), p->ttl);
 
-       if (peer->nexthop.ifp) {
-               arg.ifnamelen = strlen(peer->nexthop.ifp->name);
-               strlcpy(arg.ifname, peer->nexthop.ifp->name,
-                       sizeof(arg.ifname));
+               bfd_sess_set_hop_count(session, p->ttl);
+               changed = true;
        }
 
-       if (bfd_info->profile[0]) {
-               arg.profilelen = strlen(bfd_info->profile);
-               strlcpy(arg.profile, bfd_info->profile, sizeof(arg.profile));
+       /* Update VRF. */
+       if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) {
+               if (BGP_DEBUG(zebra, ZEBRA))
+                       zlog_debug(
+                               "%s: VRF %s(%d) to %s(%d)", __func__,
+                               bfd_sess_vrf(session), bfd_sess_vrf_id(session),
+                               vrf_id_to_name(p->bgp->vrf_id), p->bgp->vrf_id);
+
+               bfd_sess_set_vrf(session, p->bgp->vrf_id);
+               changed = true;
        }
 
-       arg.set_flag = 1;
-       arg.mhop = multihop;
-       arg.ttl = peer->ttl;
-       arg.vrf_id = vrf_id;
-       arg.command = command;
-       arg.bfd_info = bfd_info;
-       arg.min_tx = bfd_info->desired_min_tx;
-       arg.min_rx = bfd_info->required_min_rx;
-       arg.detection_multiplier = bfd_info->detect_mult;
-       arg.cbit = CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CBIT_ON);
-
-       /* Send message. */
-       zclient_bfd_command(zclient, &arg);
+       if (changed)
+               bfd_sess_install(session);
 }
 
-/*
- * bgp_bfd_register_peer - register a peer with BFD through zebra
- *                         for monitoring the peer rechahability.
+/**
+ * Reset BFD configuration data structure to its defaults settings.
  */
-void bgp_bfd_register_peer(struct peer *peer)
+static void bgp_peer_bfd_reset(struct peer *p)
 {
-       struct bfd_info *bfd_info;
-
-       if (!peer->bfd_info)
-               return;
-       bfd_info = (struct bfd_info *)peer->bfd_info;
-
-       /* Check if BFD is enabled and peer has already been registered with BFD
-        */
-       if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG))
-               return;
-
-       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER);
+       /* Set defaults. */
+       p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
+       p->bfd_config->min_rx = BFD_DEF_MIN_RX;
+       p->bfd_config->min_tx = BFD_DEF_MIN_TX;
+       p->bfd_config->cbit = false;
+       p->bfd_config->profile[0] = 0;
 }
 
-/**
- * bgp_bfd_deregister_peer - deregister a peer with BFD through zebra
- *                           for stopping the monitoring of the peer
- *                           rechahability.
- */
-void bgp_bfd_deregister_peer(struct peer *peer)
+void bgp_peer_configure_bfd(struct peer *p, bool manual)
 {
-       struct bfd_info *bfd_info;
+       /* Groups should not call this. */
+       assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
 
-       if (!peer->bfd_info)
-               return;
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* Already configured, skip it. */
+       if (p->bfd_config) {
+               /* If manually active update flag. */
+               if (!p->bfd_config->manual)
+                       p->bfd_config->manual = manual;
 
-       /* Check if BFD is eanbled and peer has not been registered */
-       if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG))
                return;
+       }
 
-       bfd_info->status = BFD_STATUS_DOWN;
-       bfd_info->last_update = bgp_clock();
-
-       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER);
-}
-
-/*
- * bgp_bfd_update_peer - update peer with BFD with new BFD paramters
- *                       through zebra.
- */
-static void bgp_bfd_update_peer(struct peer *peer)
-{
-       struct bfd_info *bfd_info;
+       /* Allocate memory for configuration overrides. */
+       p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
+       p->bfd_config->manual = manual;
 
-       if (!peer->bfd_info)
-               return;
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* Create new session and assign callback. */
+       p->bfd_config->session = bfd_sess_new(bfd_session_status_update, p);
+       bgp_peer_bfd_reset(p);
 
-       /* Check if the peer has been registered with BFD*/
-       if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG))
-               return;
+       /* Configure session with basic BGP peer data. */
+       if (p->su.sa.sa_family == AF_INET)
+               bfd_sess_set_ipv4_addrs(p->bfd_config->session,
+                                       p->su_local ? &p->su_local->sin.sin_addr
+                                                   : NULL,
+                                       &p->su.sin.sin_addr);
+       else
+               bfd_sess_set_ipv6_addrs(
+                       p->bfd_config->session,
+                       p->su_local ? &p->su_local->sin6.sin6_addr : NULL,
+                       &p->su.sin6.sin6_addr);
 
-       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_UPDATE);
-}
+       bfd_sess_set_vrf(p->bfd_config->session, p->bgp->vrf_id);
+       bfd_sess_set_hop_count(p->bfd_config->session,
+                              PEER_IS_MULTIHOP(p) ? p->ttl : 1);
 
-/**
- * bgp_bfd_reset_peer - reinitialise bfd
- * ensures that bfd state machine is restarted
- * to be synced with remote bfd
- */
-void bgp_bfd_reset_peer(struct peer *peer)
-{
-       if (!peer->bfd_info)
-               return;
+       if (p->nexthop.ifp)
+               bfd_sess_set_interface(p->bfd_config->session,
+                                      p->nexthop.ifp->name);
 
-       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER);
+       bfd_sess_enable(p->bfd_config->session, true);
+       bgp_peer_config_apply(p, p->group);
 }
 
-/*
- * bgp_bfd_update_type - update session type with BFD through zebra.
- */
-static void bgp_bfd_update_type(struct peer *peer)
+static void bgp_peer_remove_bfd(struct peer *p)
 {
-       struct bfd_info *bfd_info;
-       int multihop;
-
-       if (!peer->bfd_info)
-               return;
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* Groups should not call this. */
+       assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
 
-       /* Check if the peer has been registered with BFD*/
-       if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_REG))
+       /*
+        * Peer configuration was removed, however we must check if there
+        * is still a group configuration to keep this running.
+        */
+       if (p->group && p->group->conf->bfd_config) {
+               p->bfd_config->manual = false;
+               bgp_peer_bfd_reset(p);
+               bgp_peer_config_apply(p, p->group);
                return;
-
-       if (bfd_info->type == BFD_TYPE_NOT_CONFIGURED) {
-               multihop = bgp_bfd_is_peer_multihop(peer);
-               if ((multihop
-                    && !CHECK_FLAG(bfd_info->flags,
-                                   BFD_FLAG_BFD_TYPE_MULTIHOP))
-                   || (!multihop && CHECK_FLAG(bfd_info->flags,
-                                               BFD_FLAG_BFD_TYPE_MULTIHOP))) {
-                       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER);
-                       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER);
-               }
-       } else {
-               if ((bfd_info->type == BFD_TYPE_MULTIHOP
-                    && !CHECK_FLAG(bfd_info->flags,
-                                   BFD_FLAG_BFD_TYPE_MULTIHOP))
-                   || (bfd_info->type == BFD_TYPE_SINGLEHOP
-                       && CHECK_FLAG(bfd_info->flags,
-                                     BFD_FLAG_BFD_TYPE_MULTIHOP))) {
-                       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_DEREGISTER);
-                       bgp_bfd_peer_sendmsg(peer, ZEBRA_BFD_DEST_REGISTER);
-               }
        }
-}
-
-/*
- * bgp_bfd_dest_replay - Replay all the peers that have BFD enabled
- *                       to zebra
- */
-static int bgp_bfd_dest_replay(ZAPI_CALLBACK_ARGS)
-{
-       struct listnode *mnode, *node, *nnode;
-       struct bgp *bgp;
-       struct peer *peer;
-
-       if (BGP_DEBUG(zebra, ZEBRA))
-               zlog_debug("Zebra: BFD Dest replay request");
-
-       /* Send the client registration */
-       bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
-
-       /* Replay the peer, if BFD is enabled in BGP */
-
-       for (ALL_LIST_ELEMENTS_RO(bm->bgp, mnode, bgp))
-               for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
-                       bgp_bfd_update_peer(peer);
-               }
 
-       return 0;
+       bfd_sess_free(&p->bfd_config->session);
+       XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
 }
 
-/*
- * bgp_bfd_peer_status_update - Update the BFD status if it has changed. Bring
- *                              down the peer if the BFD session went down from
- * *                              up.
- */
-static void bgp_bfd_peer_status_update(struct peer *peer, int status,
-                                      int remote_cbit)
+static void bgp_group_configure_bfd(struct peer *p)
 {
-       struct bfd_info *bfd_info;
-       int old_status;
+       struct listnode *n;
+       struct peer *pn;
 
-       bfd_info = (struct bfd_info *)peer->bfd_info;
+       /* Peers should not call this. */
+       assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
 
-       if (bfd_info->status == status)
+       /* Already allocated: do nothing. */
+       if (p->bfd_config)
                return;
 
-       old_status = bfd_info->status;
-       BFD_SET_CLIENT_STATUS(bfd_info->status, status);
+       p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
 
-       bfd_info->last_update = bgp_clock();
+       /* Set defaults. */
+       p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
+       p->bfd_config->min_rx = BFD_DEF_MIN_RX;
+       p->bfd_config->min_tx = BFD_DEF_MIN_TX;
 
-       if (status != old_status) {
-               if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
-                       zlog_debug("[%s]: BFD %s", peer->host,
-                                  bfd_get_status_str(status));
-       }
-       if ((status == BFD_STATUS_DOWN) && (old_status == BFD_STATUS_UP)) {
-               if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE) &&
-                   CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE) &&
-                   !remote_cbit) {
-                       zlog_info("%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
-                                 peer->host);
-                       return;
-               }
-               peer->last_reset = PEER_DOWN_BFD_DOWN;
-               BGP_EVENT_ADD(peer, BGP_Stop);
-       }
-       if ((status == BFD_STATUS_UP) && (old_status == BFD_STATUS_DOWN)
-           && peer->status != Established) {
-               if (!BGP_PEER_START_SUPPRESSED(peer)) {
-                       bgp_fsm_nht_update(peer, true);
-                       BGP_EVENT_ADD(peer, BGP_Start);
-               }
-       }
+       for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
+               bgp_peer_configure_bfd(pn, false);
 }
 
-/*
- * bgp_bfd_dest_update - Find the peer for which the BFD status
- *                       has changed and bring down the peer
- *                       connectivity if the BFD session went down.
- */
-static int bgp_bfd_dest_update(ZAPI_CALLBACK_ARGS)
+static void bgp_group_remove_bfd(struct peer *p)
 {
-       struct interface *ifp;
-       struct prefix dp;
-       struct prefix sp;
-       int status;
-       int remote_cbit;
+       struct listnode *n;
+       struct peer *pn;
 
-       ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &status,
-                               &remote_cbit, vrf_id);
+       /* Peers should not call this. */
+       assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
 
-       if (BGP_DEBUG(zebra, ZEBRA)) {
-               struct vrf *vrf;
+       /* Already freed: do nothing. */
+       if (p->bfd_config == NULL)
+               return;
 
-               vrf = vrf_lookup_by_id(vrf_id);
+       /* Free configuration and point to `NULL`. */
+       XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
 
-               if (ifp)
-                       zlog_debug(
-                               "Zebra: vrf %s(%u) interface %s bfd destination %pFX %s %s",
-                               VRF_LOGNAME(vrf), vrf_id, ifp->name, &dp,
-                               bfd_get_status_str(status),
-                               remote_cbit ? "(cbit on)" : "");
+       /* Now that it is `NULL` recalculate configuration for all peers. */
+       for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) {
+               if (pn->bfd_config->manual)
+                       bgp_peer_config_apply(pn, NULL);
                else
-                       zlog_debug(
-                               "Zebra: vrf %s(%u) source %pFX bfd destination %pFX %s %s",
-                               VRF_LOGNAME(vrf), vrf_id, &sp, &dp,
-                               bfd_get_status_str(status),
-                               remote_cbit ? "(cbit on)" : "");
-       }
-
-       /* Bring the peer down if BFD is enabled in BGP */
-       {
-               struct listnode *mnode, *node, *nnode;
-               struct bgp *bgp;
-               struct peer *peer;
-
-               for (ALL_LIST_ELEMENTS_RO(bm->bgp, mnode, bgp))
-                       for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
-                               if (!peer->bfd_info)
-                                       continue;
-
-                               if ((dp.family == AF_INET)
-                                   && (peer->su.sa.sa_family == AF_INET)) {
-                                       if (dp.u.prefix4.s_addr
-                                           != peer->su.sin.sin_addr.s_addr)
-                                               continue;
-                               } else if ((dp.family == AF_INET6)
-                                          && (peer->su.sa.sa_family
-                                              == AF_INET6)) {
-                                       if (memcmp(&dp.u.prefix6,
-                                                  &peer->su.sin6.sin6_addr,
-                                                  sizeof(struct in6_addr)))
-                                               continue;
-                               } else
-                                       continue;
-
-                               if (ifp && (ifp == peer->nexthop.ifp)) {
-                                       bgp_bfd_peer_status_update(peer,
-                                                                  status,
-                                                                  remote_cbit);
-                               } else {
-                                       if (!peer->su_local)
-                                               continue;
-
-                                       if ((sp.family == AF_INET)
-                                           && (peer->su_local->sa.sa_family
-                                               == AF_INET)) {
-                                               if (sp.u.prefix4.s_addr
-                                                   != peer->su_local->sin
-                                                              .sin_addr.s_addr)
-                                                       continue;
-                                       } else if ((sp.family == AF_INET6)
-                                                  && (peer->su_local->sa
-                                                              .sa_family
-                                                      == AF_INET6)) {
-                                               if (memcmp(&sp.u.prefix6,
-                                                          &peer->su_local->sin6
-                                                                   .sin6_addr,
-                                                          sizeof(struct
-                                                                 in6_addr)))
-                                                       continue;
-                                       } else
-                                               continue;
-
-                                       if ((vrf_id != VRF_DEFAULT)
-                                           && (peer->bgp->vrf_id != vrf_id))
-                                               continue;
-
-                                       bgp_bfd_peer_status_update(peer,
-                                                                  status,
-                                                                  remote_cbit);
-                               }
-                       }
-       }
-
-       return 0;
-}
-
-/*
- * bgp_bfd_peer_param_set - Set the configured BFD paramter values for peer.
- */
-static int bgp_bfd_peer_param_set(struct peer *peer, uint32_t min_rx,
-                                 uint32_t min_tx, uint8_t detect_mult,
-                                 int defaults)
-{
-       struct bfd_info *bi;
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-       int command = 0;
-
-       bfd_set_param((struct bfd_info **)&(peer->bfd_info), min_rx, min_tx,
-                     detect_mult, NULL, defaults, &command);
-
-       /* This command overrides profile if it was previously applied. */
-       bi = peer->bfd_info;
-       bi->profile[0] = 0;
-
-       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-               group = peer->group;
-               for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
-                       command = 0;
-                       bfd_set_param((struct bfd_info **)&(peer->bfd_info),
-                                     min_rx, min_tx, detect_mult, NULL,
-                                     defaults, &command);
-
-                       /*
-                        * This command overrides profile if it was previously
-                        * applied.
-                        */
-                       bi = peer->bfd_info;
-                       bi->profile[0] = 0;
-
-                       if ((peer->status == Established)
-                           && (command == ZEBRA_BFD_DEST_REGISTER))
-                               bgp_bfd_register_peer(peer);
-                       else if (command == ZEBRA_BFD_DEST_UPDATE)
-                               bgp_bfd_update_peer(peer);
-               }
-       } else {
-               if ((peer->status == Established)
-                   && (command == ZEBRA_BFD_DEST_REGISTER))
-                       bgp_bfd_register_peer(peer);
-               else if (command == ZEBRA_BFD_DEST_UPDATE)
-                       bgp_bfd_update_peer(peer);
-       }
-       return 0;
-}
-
-/*
- * bgp_bfd_peer_param_unset - Delete the configured BFD paramter values for
- * peer.
- */
-static int bgp_bfd_peer_param_unset(struct peer *peer)
-{
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-
-       if (!peer->bfd_info)
-               return 0;
-
-       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-               bfd_info_free(&(peer->bfd_info));
-               group = peer->group;
-               for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
-                       bgp_bfd_deregister_peer(peer);
-                       bfd_info_free(&(peer->bfd_info));
-               }
-       } else {
-               bgp_bfd_deregister_peer(peer);
-               bfd_info_free(&(peer->bfd_info));
+                       bgp_peer_remove_bfd(pn);
        }
-       return 0;
 }
 
-/*
- * bgp_bfd_peer_param_type_set - set the BFD session type (multihop or
- * singlehop)
- */
-static int bgp_bfd_peer_param_type_set(struct peer *peer,
-                                      enum bfd_sess_type type)
+void bgp_peer_remove_bfd_config(struct peer *p)
 {
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-       int command = 0;
-       struct bfd_info *bfd_info;
-
-       if (!peer->bfd_info)
-               bfd_set_param((struct bfd_info **)&(peer->bfd_info),
-                             BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
-                             BFD_DEF_DETECT_MULT, NULL, 1, &command);
-
-       bfd_info = (struct bfd_info *)peer->bfd_info;
-       bfd_info->type = type;
-
-       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-               group = peer->group;
-               for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
-                       command = 0;
-                       if (!peer->bfd_info)
-                               bfd_set_param(
-                                       (struct bfd_info **)&(peer->bfd_info),
-                                       BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
-                                       BFD_DEF_DETECT_MULT, NULL, 1, &command);
-
-                       bfd_info = (struct bfd_info *)peer->bfd_info;
-                       bfd_info->type = type;
-
-                       if (peer->status == Established) {
-                               if (command == ZEBRA_BFD_DEST_REGISTER)
-                                       bgp_bfd_register_peer(peer);
-                               else
-                                       bgp_bfd_update_type(peer);
-                       }
-               }
-       } else {
-               if (peer->status == Established) {
-                       if (command == ZEBRA_BFD_DEST_REGISTER)
-                               bgp_bfd_register_peer(peer);
-                       else
-                               bgp_bfd_update_type(peer);
-               }
-       }
-
-       return 0;
-}
-
-#if HAVE_BFDD > 0
-/**
- * Set peer BFD profile configuration.
- */
-static int bgp_bfd_peer_set_profile(struct peer *peer, const char *profile)
-{
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-       int command = 0;
-       struct bfd_info *bfd_info;
-
-       bfd_set_param((struct bfd_info **)&(peer->bfd_info), BFD_DEF_MIN_RX,
-                     BFD_DEF_MIN_TX, BFD_DEF_DETECT_MULT, NULL, 1, &command);
-
-       bfd_info = (struct bfd_info *)peer->bfd_info;
-
-       /* If profile was specified, then copy string. */
-       if (profile)
-               strlcpy(bfd_info->profile, profile, sizeof(bfd_info->profile));
-       else /* Otherwise just initialize it empty. */
-               bfd_info->profile[0] = 0;
-
-       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-               group = peer->group;
-               for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
-                       command = 0;
-                       bfd_set_param((struct bfd_info **)&(peer->bfd_info),
-                                     BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
-                                     BFD_DEF_DETECT_MULT, NULL, 1, &command);
-
-                       bfd_info = (struct bfd_info *)peer->bfd_info;
-
-                       /* If profile was specified, then copy string. */
-                       if (profile)
-                               strlcpy(bfd_info->profile, profile,
-                                       sizeof(bfd_info->profile));
-                       else /* Otherwise just initialize it empty. */
-                               bfd_info->profile[0] = 0;
-
-                       if (peer->status == Established
-                           && command == ZEBRA_BFD_DEST_REGISTER)
-                               bgp_bfd_register_peer(peer);
-                       else if (command == ZEBRA_BFD_DEST_UPDATE)
-                               bgp_bfd_update_peer(peer);
-               }
-       } else {
-               if (peer->status == Established
-                   && command == ZEBRA_BFD_DEST_REGISTER)
-                       bgp_bfd_register_peer(peer);
-               else if (command == ZEBRA_BFD_DEST_UPDATE)
-                       bgp_bfd_update_peer(peer);
-       }
-
-       return 0;
+       if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
+               bgp_group_remove_bfd(p);
+       else
+               bgp_peer_remove_bfd(p);
 }
-#endif
 
 /*
  * bgp_bfd_peer_config_write - Write the peer BFD configuration.
  */
-void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer, char *addr)
+void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
+                              const char *addr)
 {
-       struct bfd_info *bfd_info;
-
-       if (!peer->bfd_info)
-               return;
-
-       bfd_info = (struct bfd_info *)peer->bfd_info;
-
-       if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG))
+       /*
+        * Always show group BFD configuration, but peer only when explicitly
+        * configured.
+        */
+       if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+            && peer->bfd_config->manual)
+           || CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
 #if HAVE_BFDD > 0
                vty_out(vty, " neighbor %s bfd\n", addr);
 #else
                vty_out(vty, " neighbor %s bfd %d %d %d\n", addr,
-                       bfd_info->detect_mult, bfd_info->required_min_rx,
-                       bfd_info->desired_min_tx);
+                       peer->bfd_config->detection_multiplier,
+                       peer->bfd_config->min_rx, peer->bfd_config->min_tx);
 #endif /* HAVE_BFDD */
-
-       if (bfd_info->type != BFD_TYPE_NOT_CONFIGURED)
-               vty_out(vty, " neighbor %s bfd %s\n", addr,
-                       (bfd_info->type == BFD_TYPE_MULTIHOP) ? "multihop"
-                                                             : "singlehop");
-
-       if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_PARAM_CFG)
-           && (bfd_info->type == BFD_TYPE_NOT_CONFIGURED)) {
-               vty_out(vty, " neighbor %s bfd", addr);
-               if (bfd_info->profile[0])
-                       vty_out(vty, " profile %s", bfd_info->profile);
-               vty_out(vty, "\n");
        }
 
-       if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE))
-               vty_out(vty, " neighbor %s bfd check-control-plane-failure\n", addr);
+       if (peer->bfd_config->profile[0])
+               vty_out(vty, " neighbor %s bfd profile %s\n", addr,
+                       peer->bfd_config->profile);
+
+       if (peer->bfd_config->cbit)
+               vty_out(vty, " neighbor %s bfd check-control-plane-failure\n",
+                       addr);
 }
 
 /*
  * bgp_bfd_show_info - Show the peer BFD information.
  */
-void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json,
+void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
                       json_object *json_neigh)
 {
-       bfd_show_info(vty, (struct bfd_info *)peer->bfd_info,
-                     bgp_bfd_is_peer_multihop(peer), 0, use_json, json_neigh);
+       if (peer->bfd_config->session)
+               bfd_sess_show(vty, json_neigh, peer->bfd_config->session);
 }
 
 DEFUN (neighbor_bfd,
@@ -712,16 +438,15 @@ DEFUN (neighbor_bfd,
 {
        int idx_peer = 1;
        struct peer *peer;
-       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       ret = bgp_bfd_peer_param_set(peer, BFD_DEF_MIN_RX, BFD_DEF_MIN_TX,
-                                    BFD_DEF_DETECT_MULT, 1);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_configure_bfd(peer);
+       else
+               bgp_peer_configure_bfd(peer, true);
 
        return CMD_SUCCESS;
 }
@@ -745,89 +470,30 @@ DEFUN(
        int idx_number_1 = 3;
        int idx_number_2 = 4;
        int idx_number_3 = 5;
+       long detection_multiplier, min_rx, min_tx;
        struct peer *peer;
-       uint32_t rx_val;
-       uint32_t tx_val;
-       uint8_t dm_val;
-       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       if ((ret = bfd_validate_param(
-                    vty, argv[idx_number_1]->arg, argv[idx_number_2]->arg,
-                    argv[idx_number_3]->arg, &dm_val, &rx_val, &tx_val))
-           != CMD_SUCCESS)
-               return ret;
-
-       ret = bgp_bfd_peer_param_set(peer, rx_val, tx_val, dm_val, 0);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
-
-       return CMD_SUCCESS;
-}
-
-DEFUN_HIDDEN (neighbor_bfd_type,
-       neighbor_bfd_type_cmd,
-       "neighbor <A.B.C.D|X:X::X:X|WORD> bfd <multihop|singlehop>",
-       NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR2
-       "Enables BFD support\n"
-       "Multihop session\n"
-       "Single hop session\n")
-{
-       int idx_peer = 1;
-       int idx_hop = 3;
-       struct peer *peer;
-       enum bfd_sess_type type;
-       int ret;
-
-       peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
-       if (!peer)
-               return CMD_WARNING_CONFIG_FAILED;
+       detection_multiplier = strtol(argv[idx_number_1]->arg, NULL, 10);
+       min_rx = strtol(argv[idx_number_2]->arg, NULL, 10);
+       min_tx = strtol(argv[idx_number_3]->arg, NULL, 10);
 
-       if (strmatch(argv[idx_hop]->text, "singlehop"))
-               type = BFD_TYPE_SINGLEHOP;
-       else if (strmatch(argv[idx_hop]->text, "multihop"))
-               type = BFD_TYPE_MULTIHOP;
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_configure_bfd(peer);
        else
-               return CMD_WARNING_CONFIG_FAILED;
-
-       ret = bgp_bfd_peer_param_type_set(peer, type);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
+               bgp_peer_configure_bfd(peer, true);
 
-       return CMD_SUCCESS;
-}
-
-static int bgp_bfd_set_check_controlplane_failure_peer(struct vty *vty, struct peer *peer,
-                                                      const char *no)
-{
-       struct bfd_info *bfd_info;
+       peer->bfd_config->detection_multiplier = detection_multiplier;
+       peer->bfd_config->min_rx = min_rx;
+       peer->bfd_config->min_tx = min_tx;
+       bgp_peer_config_apply(peer, peer->group);
 
-       if (!peer->bfd_info) {
-               if (no)
-                       return CMD_SUCCESS;
-               vty_out(vty, "%% Specify bfd command first\n");
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-       bfd_info = (struct bfd_info *)peer->bfd_info;
-       if (!no) {
-               if (!CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) {
-                       SET_FLAG(bfd_info->flags,  BFD_FLAG_BFD_CHECK_CONTROLPLANE);
-                       bgp_bfd_update_peer(peer);
-               }
-       } else {
-               if (CHECK_FLAG(bfd_info->flags, BFD_FLAG_BFD_CHECK_CONTROLPLANE)) {
-                       UNSET_FLAG(bfd_info->flags,  BFD_FLAG_BFD_CHECK_CONTROLPLANE);
-                       bgp_bfd_update_peer(peer);
-               }
-       }
        return CMD_SUCCESS;
 }
 
-
 DEFUN (neighbor_bfd_check_controlplane_failure,
        neighbor_bfd_check_controlplane_failure_cmd,
        "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
@@ -840,9 +506,6 @@ DEFUN (neighbor_bfd_check_controlplane_failure,
        const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
        int idx_peer = 0;
        struct peer *peer;
-       struct peer_group *group;
-       struct listnode *node, *nnode;
-       int ret = CMD_SUCCESS;
 
        if (no)
                idx_peer = 2;
@@ -853,19 +516,16 @@ DEFUN (neighbor_bfd_check_controlplane_failure,
                vty_out(vty, "%% Specify remote-as or peer-group commands first\n");
                return CMD_WARNING_CONFIG_FAILED;
        }
-       if (!peer->bfd_info) {
-               if (no)
-                       return CMD_SUCCESS;
-               vty_out(vty, "%% Specify bfd command first\n");
-               return CMD_WARNING_CONFIG_FAILED;
-       }
-       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
-               group = peer->group;
-               for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer))
-                       ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no);
-       } else
-               ret = bgp_bfd_set_check_controlplane_failure_peer(vty, peer, no);
-       return ret;
+
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_configure_bfd(peer);
+       else
+               bgp_peer_configure_bfd(peer, true);
+
+       peer->bfd_config->cbit = no == NULL;
+       bgp_peer_config_apply(peer, peer->group);
+
+       return CMD_SUCCESS;
  }
 
 DEFUN (no_neighbor_bfd,
@@ -888,44 +548,15 @@ DEFUN (no_neighbor_bfd,
 {
        int idx_peer = 2;
        struct peer *peer;
-       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       ret = bgp_bfd_peer_param_unset(peer);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
-
-       return CMD_SUCCESS;
-}
-
-
-DEFUN_HIDDEN (no_neighbor_bfd_type,
-       no_neighbor_bfd_type_cmd,
-       "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd <multihop|singlehop>",
-       NO_STR
-       NEIGHBOR_STR
-       NEIGHBOR_ADDR_STR2
-       "Disables BFD support\n"
-       "Multihop session\n"
-       "Singlehop session\n")
-{
-       int idx_peer = 2;
-       struct peer *peer;
-       int ret;
-
-       peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
-       if (!peer)
-               return CMD_WARNING_CONFIG_FAILED;
-
-       if (!peer->bfd_info)
-               return 0;
-
-       ret = bgp_bfd_peer_param_type_set(peer, BFD_TYPE_NOT_CONFIGURED);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_remove_bfd(peer);
+       else
+               bgp_peer_remove_bfd(peer);
 
        return CMD_SUCCESS;
 }
@@ -941,15 +572,19 @@ DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd,
 {
        int idx_peer = 1, idx_prof = 4;
        struct peer *peer;
-       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       ret = bgp_bfd_peer_set_profile(peer, argv[idx_prof]->arg);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_configure_bfd(peer);
+       else
+               bgp_peer_configure_bfd(peer, true);
+
+       strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg,
+               sizeof(peer->bfd_config->profile));
+       bgp_peer_config_apply(peer, peer->group);
 
        return CMD_SUCCESS;
 }
@@ -965,38 +600,33 @@ DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd,
 {
        int idx_peer = 2;
        struct peer *peer;
-       int ret;
 
        peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
        if (!peer)
                return CMD_WARNING_CONFIG_FAILED;
 
-       if (!peer->bfd_info)
-               return 0;
+       if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+               bgp_group_configure_bfd(peer);
+       else
+               bgp_peer_configure_bfd(peer, true);
 
-       ret = bgp_bfd_peer_set_profile(peer, NULL);
-       if (ret != 0)
-               return bgp_vty_return(vty, ret);
+       peer->bfd_config->profile[0] = 0;
+       bgp_peer_config_apply(peer, peer->group);
 
        return CMD_SUCCESS;
 }
 #endif /* HAVE_BFDD */
 
-void bgp_bfd_init(void)
+void bgp_bfd_init(struct thread_master *tm)
 {
-       bfd_gbl_init();
-
        /* Initialize BFD client functions */
-       zclient->interface_bfd_dest_update = bgp_bfd_dest_update;
-       zclient->bfd_dest_replay = bgp_bfd_dest_replay;
+       bfd_protocol_integration_init(zclient, tm);
 
        /* "neighbor bfd" commands. */
        install_element(BGP_NODE, &neighbor_bfd_cmd);
        install_element(BGP_NODE, &neighbor_bfd_param_cmd);
-       install_element(BGP_NODE, &neighbor_bfd_type_cmd);
        install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd);
        install_element(BGP_NODE, &no_neighbor_bfd_cmd);
-       install_element(BGP_NODE, &no_neighbor_bfd_type_cmd);
 
 #if HAVE_BFDD > 0
        install_element(BGP_NODE, &neighbor_bfd_profile_cmd);
index f2fa959b4587502fd0d52cb3bbd5b2e62bfb87d4..ec2772b69b26a572056768565c1ccacc8b747252 100644 (file)
 #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);
+/**
+ * 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 void bgp_bfd_reset_peer(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);
 
-extern void bgp_bfd_peer_config_write(struct vty *vty, struct peer *peer,
-                                     char *addr);
+/**
+ * Allocates and configure BFD session for peer. If it is already configured,
+ * then it does nothing.
+ */
+extern void bgp_peer_configure_bfd(struct peer *p, bool manual);
 
-extern void bgp_bfd_show_info(struct vty *vty, struct peer *peer, bool use_json,
-                             json_object *json_neigh);
+/**
+ * Removes BFD configuration from either peer or peer group.
+ */
+extern void bgp_peer_remove_bfd_config(struct peer *p);
 
-extern bool bgp_bfd_is_peer_multihop(struct peer *peer);
+/**
+ * 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 */
index f099309f97600c19436707f034ce5ac5a6bd5e2b..45a856a4591fc5f0f7a834fe88acd7eaf7a73c2b 100644 (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;
 }
 
index 6b3df875156b5150474bd79a0e76e7b9961e7d25..2ddafd9a0c2dafca41900eb2986cb208f61d4a7d 100644 (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);
index a6c00d57353ff1d7446467fcbafd392815f85a3d..a016265d6e2ea21bbe3464f15751b1f2f0d1f1a7 100644 (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))
index 4eb9229b55b11dff656103db01b19c3f7f85bb48..d37b9fa48c04782f7d6a9f04dd49d696965f6705 100644 (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();
 
index 029019dd3c470b90f82c4733ed76f8b0aa21a7f3..6270542178cafa7ab9025997ddadbf3afa9a9811 100644 (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;