2 * bfd.c: BFD handling routines
4 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
6 * This file is part of GNU Zebra.
8 * GNU Zebra is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2, or (at your option) any
13 * GNU Zebra is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; see the file COPYING; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37 DEFINE_MTYPE_STATIC(LIB
, BFD_INFO
, "BFD info");
38 DEFINE_MTYPE_STATIC(LIB
, BFD_SOURCE
, "BFD source cache");
41 * BFD protocol integration configuration.
44 /** Events definitions. */
45 enum bfd_session_event
{
46 /** Remove the BFD session configuration. */
48 /** Install the BFD session configuration. */
53 * BFD source selection result cache.
55 * This structure will keep track of the result based on the destination
56 * prefix. When the result changes all related BFD sessions with automatic
57 * source will be updated.
59 struct bfd_source_cache
{
60 /** Address VRF belongs. */
62 /** Destination network address. */
63 struct prefix address
;
64 /** Source selected. */
66 /** Is the source address valid? */
68 /** BFD sessions using this. */
71 SLIST_ENTRY(bfd_source_cache
) entry
;
73 SLIST_HEAD(bfd_source_list
, bfd_source_cache
);
76 * Data structure to do the necessary tricks to hide the BFD protocol
77 * integration internals.
79 struct bfd_session_params
{
80 /** Contains the session parameters and more. */
81 struct bfd_session_arg args
;
82 /** Contains the session state. */
83 struct bfd_session_status bss
;
84 /** Protocol implementation status update callback. */
85 bsp_status_update updatecb
;
86 /** Protocol implementation custom data pointer. */
92 * This variable controls what action to execute when the command batch
93 * finishes. Normally we'd use `thread_add_event` value, however since
94 * that function is going to be called multiple times and the value
95 * might be different we'll use this variable to keep track of it.
97 enum bfd_session_event lastev
;
99 * BFD session configuration event.
101 * Multiple actions might be asked during a command batch (either via
102 * configuration load or northbound batch), so we'll use this to
103 * install/uninstall the BFD session parameters only once.
105 struct thread
*installev
;
107 /** BFD session installation state. */
110 /** Automatic source selection. */
112 /** Currently selected source. */
113 struct bfd_source_cache
*source_cache
;
115 /** Global BFD paramaters list. */
116 TAILQ_ENTRY(bfd_session_params
) entry
;
119 struct bfd_sessions_global
{
121 * Global BFD session parameters list for (re)installation and update
122 * without code duplication among daemons.
124 TAILQ_HEAD(bsplist
, bfd_session_params
) bsplist
;
125 /** BFD automatic source selection cache. */
126 struct bfd_source_list source_list
;
128 /** Pointer to FRR's event manager. */
129 struct thread_master
*tm
;
130 /** Pointer to zebra client data structure. */
132 /** Zebra next hop tracking (NHT) client. */
133 struct zclient
*nht_zclient
;
135 /** Debugging state. */
137 /** Is shutting down? */
141 /** Global configuration variable. */
142 static struct bfd_sessions_global bsglobal
;
144 /** Global empty address for IPv4/IPv6. */
145 static const struct in6_addr i6a_zero
;
150 static void bfd_nht_zclient_connect(struct thread
*thread
);
152 static void bfd_nht_zclient_connected(struct zclient
*zclient
);
153 static int bfd_nht_update(ZAPI_CALLBACK_ARGS
);
155 static void bfd_source_cache_get(struct bfd_session_params
*session
);
156 static void bfd_source_cache_put(struct bfd_session_params
*session
);
159 bfd_source_cache_register(const struct bfd_source_cache
*source
)
161 zclient_send_rnh(bsglobal
.nht_zclient
, ZEBRA_NEXTHOP_REGISTER
,
162 &source
->address
, SAFI_UNICAST
, false, false,
167 bfd_source_cache_unregister(const struct bfd_source_cache
*source
)
169 zclient_send_rnh(bsglobal
.nht_zclient
, ZEBRA_NEXTHOP_UNREGISTER
,
170 &source
->address
, SAFI_UNICAST
, false, false,
176 * bfd_get_peer_info - Extract the Peer information for which the BFD session
177 * went down from the message sent from Zebra to clients.
179 static struct interface
*bfd_get_peer_info(struct stream
*s
, struct prefix
*dp
,
180 struct prefix
*sp
, int *status
,
181 int *remote_cbit
, vrf_id_t vrf_id
)
183 unsigned int ifindex
;
184 struct interface
*ifp
= NULL
;
186 int local_remote_cbit
;
189 * If the ifindex lookup fails the
190 * rest of the data in the stream is
191 * not read. All examples of this function
192 * call immediately use the dp->family which
193 * is not good. Ensure we are not using
196 memset(dp
, 0, sizeof(*dp
));
197 memset(sp
, 0, sizeof(*sp
));
199 /* Get interface index. */
200 STREAM_GETL(s
, ifindex
);
204 ifp
= if_lookup_by_index(ifindex
, vrf_id
);
206 if (bsglobal
.debugging
)
208 "%s: Can't find interface by ifindex: %d ",
214 /* Fetch destination address. */
215 STREAM_GETC(s
, dp
->family
);
217 plen
= prefix_blen(dp
);
218 STREAM_GET(&dp
->u
.prefix
, s
, plen
);
219 STREAM_GETC(s
, dp
->prefixlen
);
221 /* Get BFD status. */
222 STREAM_GETL(s
, (*status
));
224 STREAM_GETC(s
, sp
->family
);
226 plen
= prefix_blen(sp
);
227 STREAM_GET(&sp
->u
.prefix
, s
, plen
);
228 STREAM_GETC(s
, sp
->prefixlen
);
230 STREAM_GETC(s
, local_remote_cbit
);
232 *remote_cbit
= local_remote_cbit
;
237 * Clean dp and sp because caller
238 * will immediately check them valid or not
240 memset(dp
, 0, sizeof(*dp
));
241 memset(sp
, 0, sizeof(*sp
));
246 * bfd_get_status_str - Convert BFD status to a display string.
248 const char *bfd_get_status_str(int status
)
251 case BFD_STATUS_DOWN
:
255 case BFD_STATUS_ADMIN_DOWN
:
257 case BFD_STATUS_UNKNOWN
:
264 * bfd_last_update - Calculate the last BFD update time and convert it
265 * into a dd:hh:mm:ss display format.
267 static void bfd_last_update(time_t last_update
, char *buf
, size_t len
)
274 /* If no BFD status update has ever been received, print `never'. */
275 if (last_update
== 0) {
276 snprintf(buf
, len
, "never");
280 /* Get current time. */
283 diff
= curr
- last_update
;
284 gmtime_r(&diff
, &tm
);
286 snprintf(buf
, len
, "%d:%02d:%02d:%02d", tm
.tm_yday
, tm
.tm_hour
,
287 tm
.tm_min
, tm
.tm_sec
);
291 * bfd_client_sendmsg - Format and send a client register
292 * command to Zebra to be forwarded to BFD
294 void bfd_client_sendmsg(struct zclient
*zclient
, int command
,
298 enum zclient_send_status ret
;
301 if (!zclient
|| zclient
->sock
< 0) {
302 if (bsglobal
.debugging
)
304 "%s: Can't send BFD client register, Zebra client not established",
311 zclient_create_header(s
, command
, vrf_id
);
313 stream_putl(s
, getpid());
315 stream_putw_at(s
, 0, stream_get_endp(s
));
317 ret
= zclient_send_message(zclient
);
319 if (ret
== ZCLIENT_SEND_FAILURE
) {
320 if (bsglobal
.debugging
)
322 "%s: %ld: zclient_send_message() failed",
323 __func__
, (long)getpid());
330 int zclient_bfd_command(struct zclient
*zc
, struct bfd_session_arg
*args
)
335 /* Individual reg/dereg messages are suppressed during shutdown. */
336 if (bsglobal
.shutting_down
) {
337 if (bsglobal
.debugging
)
339 "%s: Suppressing BFD peer reg/dereg messages",
345 if (!zc
|| zc
->sock
< 0) {
346 if (bsglobal
.debugging
)
347 zlog_debug("%s: zclient unavailable", __func__
);
354 /* Create new message. */
355 zclient_create_header(s
, args
->command
, args
->vrf_id
);
356 stream_putl(s
, getpid());
358 /* Encode destination address. */
359 stream_putw(s
, args
->family
);
360 addrlen
= (args
->family
== AF_INET
) ? sizeof(struct in_addr
)
361 : sizeof(struct in6_addr
);
362 stream_put(s
, &args
->dst
, addrlen
);
365 * For more BFD integration protocol details, see function
366 * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
369 /* Session timers. */
370 stream_putl(s
, args
->min_rx
);
371 stream_putl(s
, args
->min_tx
);
372 stream_putc(s
, args
->detection_multiplier
);
375 stream_putc(s
, args
->mhop
!= 0);
377 /* Source address. */
378 stream_putw(s
, args
->family
);
379 stream_put(s
, &args
->src
, addrlen
);
381 /* Send the expected hops. */
382 stream_putc(s
, args
->hops
);
384 /* Send interface name if any. */
386 /* Don't send interface. */
388 if (bsglobal
.debugging
&& args
->ifnamelen
)
389 zlog_debug("%s: multi hop is configured, not sending interface",
392 stream_putc(s
, args
->ifnamelen
);
394 stream_put(s
, args
->ifname
, args
->ifnamelen
);
397 /* Send the C bit indicator. */
398 stream_putc(s
, args
->cbit
);
400 /* Send profile name if any. */
401 stream_putc(s
, args
->profilelen
);
402 if (args
->profilelen
)
403 stream_put(s
, args
->profile
, args
->profilelen
);
405 /* Encode timers if this is a registration message. */
406 if (args
->command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
407 stream_putl(s
, args
->min_rx
);
408 stream_putl(s
, args
->min_tx
);
409 stream_putc(s
, args
->detection_multiplier
);
413 /* Multi hop indicator. */
416 /* Multi hop always sends the source address. */
417 stream_putw(s
, args
->family
);
418 stream_put(s
, &args
->src
, addrlen
);
420 /* Send the expected hops. */
421 stream_putc(s
, args
->hops
);
423 /* Multi hop indicator. */
426 /* Single hop only sends the source address when IPv6. */
427 if (args
->family
== AF_INET6
) {
428 stream_putw(s
, args
->family
);
429 stream_put(s
, &args
->src
, addrlen
);
432 /* Send interface name if any. */
433 stream_putc(s
, args
->ifnamelen
);
435 stream_put(s
, args
->ifname
, args
->ifnamelen
);
438 /* Send the C bit indicator. */
439 stream_putc(s
, args
->cbit
);
440 #endif /* HAVE_BFDD */
442 /* Finish the message by writing the size. */
443 stream_putw_at(s
, 0, stream_get_endp(s
));
445 /* Send message to zebra. */
446 if (zclient_send_message(zc
) == ZCLIENT_SEND_FAILURE
) {
447 if (bsglobal
.debugging
)
448 zlog_debug("%s: zclient_send_message failed", __func__
);
455 struct bfd_session_params
*bfd_sess_new(bsp_status_update updatecb
, void *arg
)
457 struct bfd_session_params
*bsp
;
459 bsp
= XCALLOC(MTYPE_BFD_INFO
, sizeof(*bsp
));
461 /* Save application data. */
462 bsp
->updatecb
= updatecb
;
466 bsp
->args
.detection_multiplier
= BFD_DEF_DETECT_MULT
;
468 bsp
->args
.min_rx
= BFD_DEF_MIN_RX
;
469 bsp
->args
.min_tx
= BFD_DEF_MIN_TX
;
470 bsp
->args
.vrf_id
= VRF_DEFAULT
;
472 /* Register in global list. */
473 TAILQ_INSERT_TAIL(&bsglobal
.bsplist
, bsp
, entry
);
478 static bool _bfd_sess_valid(const struct bfd_session_params
*bsp
)
480 /* Peer/local address not configured. */
481 if (bsp
->args
.family
== 0)
484 /* Address configured but invalid. */
485 if (bsp
->args
.family
!= AF_INET
&& bsp
->args
.family
!= AF_INET6
) {
486 if (bsglobal
.debugging
)
487 zlog_debug("%s: invalid session family: %d", __func__
,
492 /* Invalid address. */
493 if (memcmp(&bsp
->args
.dst
, &i6a_zero
, sizeof(i6a_zero
)) == 0) {
494 if (bsglobal
.debugging
) {
495 if (bsp
->args
.family
== AF_INET
)
496 zlog_debug("%s: invalid address: %pI4",
498 (struct in_addr
*)&bsp
->args
.dst
);
500 zlog_debug("%s: invalid address: %pI6",
501 __func__
, &bsp
->args
.dst
);
506 /* Multi hop requires local address. */
508 && memcmp(&i6a_zero
, &bsp
->args
.src
, sizeof(i6a_zero
)) == 0) {
509 if (bsglobal
.debugging
)
511 "%s: multi hop but no local address provided",
517 if (bsp
->args
.vrf_id
== VRF_UNKNOWN
) {
518 if (bsglobal
.debugging
)
519 zlog_debug("%s: asked for unknown VRF", __func__
);
526 static void _bfd_sess_send(struct thread
*t
)
528 struct bfd_session_params
*bsp
= THREAD_ARG(t
);
531 /* Validate configuration before trying to send bogus data. */
532 if (!_bfd_sess_valid(bsp
))
535 if (bsp
->lastev
== BSE_INSTALL
) {
536 bsp
->args
.command
= bsp
->installed
? ZEBRA_BFD_DEST_UPDATE
537 : ZEBRA_BFD_DEST_REGISTER
;
539 bsp
->args
.command
= ZEBRA_BFD_DEST_DEREGISTER
;
541 /* If not installed and asked for uninstall, do nothing. */
542 if (!bsp
->installed
&& bsp
->args
.command
== ZEBRA_BFD_DEST_DEREGISTER
)
545 rv
= zclient_bfd_command(bsglobal
.zc
, &bsp
->args
);
546 /* Command was sent successfully. */
548 /* Update installation status. */
549 if (bsp
->args
.command
== ZEBRA_BFD_DEST_DEREGISTER
)
550 bsp
->installed
= false;
551 else if (bsp
->args
.command
== ZEBRA_BFD_DEST_REGISTER
)
552 bsp
->installed
= true;
554 struct ipaddr src
, dst
;
556 src
.ipa_type
= bsp
->args
.family
;
557 src
.ipaddr_v6
= bsp
->args
.src
;
558 dst
.ipa_type
= bsp
->args
.family
;
559 dst
.ipaddr_v6
= bsp
->args
.dst
;
562 "%s: BFD session %pIA -> %pIA interface %s VRF %s(%u) was not %s",
563 __func__
, &src
, &dst
,
564 bsp
->args
.ifnamelen
? bsp
->args
.ifname
: "*",
565 vrf_id_to_name(bsp
->args
.vrf_id
), bsp
->args
.vrf_id
,
566 bsp
->lastev
== BSE_INSTALL
? "installed"
571 static void _bfd_sess_remove(struct bfd_session_params
*bsp
)
573 /* Not installed, nothing to do. */
577 /* Cancel any pending installation request. */
578 THREAD_OFF(bsp
->installev
);
580 /* Send request to remove any session. */
581 bsp
->lastev
= BSE_UNINSTALL
;
582 thread_execute(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0);
585 void bfd_sess_free(struct bfd_session_params
**bsp
)
590 /* Remove any installed session. */
591 _bfd_sess_remove(*bsp
);
593 /* Remove from global list. */
594 TAILQ_REMOVE(&bsglobal
.bsplist
, (*bsp
), entry
);
596 bfd_source_cache_put(*bsp
);
598 /* Free the memory and point to NULL. */
599 XFREE(MTYPE_BFD_INFO
, (*bsp
));
602 static bool bfd_sess_address_changed(const struct bfd_session_params
*bsp
,
604 const struct in6_addr
*src
,
605 const struct in6_addr
*dst
)
609 if (bsp
->args
.family
!= family
)
612 addrlen
= (family
== AF_INET
) ? sizeof(struct in_addr
)
613 : sizeof(struct in6_addr
);
614 if ((src
== NULL
&& memcmp(&bsp
->args
.src
, &i6a_zero
, addrlen
))
615 || (src
&& memcmp(src
, &bsp
->args
.src
, addrlen
))
616 || memcmp(dst
, &bsp
->args
.dst
, addrlen
))
622 void bfd_sess_set_ipv4_addrs(struct bfd_session_params
*bsp
,
623 const struct in_addr
*src
,
624 const struct in_addr
*dst
)
626 if (!bfd_sess_address_changed(bsp
, AF_INET
, (struct in6_addr
*)src
,
627 (struct in6_addr
*)dst
))
630 /* If already installed, remove the old setting. */
631 _bfd_sess_remove(bsp
);
632 /* Address changed so we must reapply auto source. */
633 bfd_source_cache_put(bsp
);
635 bsp
->args
.family
= AF_INET
;
637 /* Clean memory, set zero value and avoid static analyser warnings. */
638 memset(&bsp
->args
.src
, 0, sizeof(bsp
->args
.src
));
639 memset(&bsp
->args
.dst
, 0, sizeof(bsp
->args
.dst
));
641 /* Copy the equivalent of IPv4 to arguments structure. */
643 memcpy(&bsp
->args
.src
, src
, sizeof(struct in_addr
));
646 memcpy(&bsp
->args
.dst
, dst
, sizeof(struct in_addr
));
648 if (bsp
->auto_source
)
649 bfd_source_cache_get(bsp
);
652 void bfd_sess_set_ipv6_addrs(struct bfd_session_params
*bsp
,
653 const struct in6_addr
*src
,
654 const struct in6_addr
*dst
)
656 if (!bfd_sess_address_changed(bsp
, AF_INET6
, src
, dst
))
659 /* If already installed, remove the old setting. */
660 _bfd_sess_remove(bsp
);
661 /* Address changed so we must reapply auto source. */
662 bfd_source_cache_put(bsp
);
664 bsp
->args
.family
= AF_INET6
;
666 /* Clean memory, set zero value and avoid static analyser warnings. */
667 memset(&bsp
->args
.src
, 0, sizeof(bsp
->args
.src
));
670 bsp
->args
.src
= *src
;
673 bsp
->args
.dst
= *dst
;
675 if (bsp
->auto_source
)
676 bfd_source_cache_get(bsp
);
679 void bfd_sess_set_interface(struct bfd_session_params
*bsp
, const char *ifname
)
681 if ((ifname
== NULL
&& bsp
->args
.ifnamelen
== 0)
682 || (ifname
&& strcmp(bsp
->args
.ifname
, ifname
) == 0))
685 /* If already installed, remove the old setting. */
686 _bfd_sess_remove(bsp
);
688 if (ifname
== NULL
) {
689 bsp
->args
.ifname
[0] = 0;
690 bsp
->args
.ifnamelen
= 0;
694 if (strlcpy(bsp
->args
.ifname
, ifname
, sizeof(bsp
->args
.ifname
))
695 > sizeof(bsp
->args
.ifname
))
696 zlog_warn("%s: interface name truncated: %s", __func__
, ifname
);
698 bsp
->args
.ifnamelen
= strlen(bsp
->args
.ifname
);
701 void bfd_sess_set_profile(struct bfd_session_params
*bsp
, const char *profile
)
703 if (profile
== NULL
) {
704 bsp
->args
.profile
[0] = 0;
705 bsp
->args
.profilelen
= 0;
709 if (strlcpy(bsp
->args
.profile
, profile
, sizeof(bsp
->args
.profile
))
710 > sizeof(bsp
->args
.profile
))
711 zlog_warn("%s: profile name truncated: %s", __func__
, profile
);
713 bsp
->args
.profilelen
= strlen(bsp
->args
.profile
);
716 void bfd_sess_set_vrf(struct bfd_session_params
*bsp
, vrf_id_t vrf_id
)
718 if (bsp
->args
.vrf_id
== vrf_id
)
721 /* If already installed, remove the old setting. */
722 _bfd_sess_remove(bsp
);
723 /* Address changed so we must reapply auto source. */
724 bfd_source_cache_put(bsp
);
726 bsp
->args
.vrf_id
= vrf_id
;
728 if (bsp
->auto_source
)
729 bfd_source_cache_get(bsp
);
732 void bfd_sess_set_hop_count(struct bfd_session_params
*bsp
, uint8_t hops
)
734 if (bsp
->args
.hops
== hops
)
737 /* If already installed, remove the old setting. */
738 _bfd_sess_remove(bsp
);
740 bsp
->args
.hops
= hops
;
741 bsp
->args
.mhop
= (hops
> 1);
745 void bfd_sess_set_cbit(struct bfd_session_params
*bsp
, bool enable
)
747 bsp
->args
.cbit
= enable
;
750 void bfd_sess_set_timers(struct bfd_session_params
*bsp
,
751 uint8_t detection_multiplier
, uint32_t min_rx
,
754 bsp
->args
.detection_multiplier
= detection_multiplier
;
755 bsp
->args
.min_rx
= min_rx
;
756 bsp
->args
.min_tx
= min_tx
;
759 void bfd_sess_set_auto_source(struct bfd_session_params
*bsp
, bool enable
)
761 if (bsp
->auto_source
== enable
)
764 bsp
->auto_source
= enable
;
766 bfd_source_cache_get(bsp
);
768 bfd_source_cache_put(bsp
);
771 void bfd_sess_install(struct bfd_session_params
*bsp
)
773 bsp
->lastev
= BSE_INSTALL
;
774 thread_add_event(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0, &bsp
->installev
);
777 void bfd_sess_uninstall(struct bfd_session_params
*bsp
)
779 bsp
->lastev
= BSE_UNINSTALL
;
780 thread_add_event(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0, &bsp
->installev
);
783 enum bfd_session_state
bfd_sess_status(const struct bfd_session_params
*bsp
)
785 return bsp
->bss
.state
;
788 uint8_t bfd_sess_hop_count(const struct bfd_session_params
*bsp
)
790 return bsp
->args
.hops
;
793 const char *bfd_sess_profile(const struct bfd_session_params
*bsp
)
795 return bsp
->args
.profilelen
? bsp
->args
.profile
: NULL
;
798 void bfd_sess_addresses(const struct bfd_session_params
*bsp
, int *family
,
799 struct in6_addr
*src
, struct in6_addr
*dst
)
801 *family
= bsp
->args
.family
;
803 *src
= bsp
->args
.src
;
805 *dst
= bsp
->args
.dst
;
808 const char *bfd_sess_interface(const struct bfd_session_params
*bsp
)
810 if (bsp
->args
.ifnamelen
)
811 return bsp
->args
.ifname
;
816 const char *bfd_sess_vrf(const struct bfd_session_params
*bsp
)
818 return vrf_id_to_name(bsp
->args
.vrf_id
);
821 vrf_id_t
bfd_sess_vrf_id(const struct bfd_session_params
*bsp
)
823 return bsp
->args
.vrf_id
;
826 bool bfd_sess_cbit(const struct bfd_session_params
*bsp
)
828 return bsp
->args
.cbit
;
831 void bfd_sess_timers(const struct bfd_session_params
*bsp
,
832 uint8_t *detection_multiplier
, uint32_t *min_rx
,
835 *detection_multiplier
= bsp
->args
.detection_multiplier
;
836 *min_rx
= bsp
->args
.min_rx
;
837 *min_tx
= bsp
->args
.min_tx
;
840 bool bfd_sess_auto_source(const struct bfd_session_params
*bsp
)
842 return bsp
->auto_source
;
845 void bfd_sess_show(struct vty
*vty
, struct json_object
*json
,
846 struct bfd_session_params
*bsp
)
848 json_object
*json_bfd
= NULL
;
856 json_bfd
= json_object_new_object();
858 json_object_string_add(json_bfd
, "type", "multi hop");
860 json_object_string_add(json_bfd
, "type", "single hop");
862 vty_out(vty
, " BFD: Type: %s\n",
863 bsp
->args
.mhop
? "multi hop" : "single hop");
865 /* Show configuration. */
867 json_object_int_add(json_bfd
, "detectMultiplier",
868 bsp
->args
.detection_multiplier
);
869 json_object_int_add(json_bfd
, "rxMinInterval",
871 json_object_int_add(json_bfd
, "txMinInterval",
875 " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
876 bsp
->args
.detection_multiplier
, bsp
->args
.min_rx
,
880 bfd_last_update(bsp
->bss
.last_event
, time_buf
, sizeof(time_buf
));
882 json_object_string_add(json_bfd
, "status",
883 bfd_get_status_str(bsp
->bss
.state
));
884 json_object_string_add(json_bfd
, "lastUpdate", time_buf
);
886 vty_out(vty
, " Status: %s, Last update: %s\n",
887 bfd_get_status_str(bsp
->bss
.state
), time_buf
);
890 json_object_object_add(json
, "peerBfdInfo", json_bfd
);
896 * Zebra communication related.
900 * Callback for reinstallation of all registered BFD sessions.
902 * Use this as `zclient` `bfd_dest_replay` callback.
904 int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS
)
906 struct bfd_session_params
*bsp
;
908 if (!zclient
->bfd_integration
)
911 /* Do nothing when shutting down. */
912 if (bsglobal
.shutting_down
)
915 if (bsglobal
.debugging
)
916 zlog_debug("%s: sending all sessions registered", __func__
);
918 /* Send the client registration */
919 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, vrf_id
);
921 /* Replay all activated peers. */
922 TAILQ_FOREACH (bsp
, &bsglobal
.bsplist
, entry
) {
923 /* Skip not installed sessions. */
927 /* We are reconnecting, so we must send installation. */
928 bsp
->installed
= false;
930 /* Cancel any pending installation request. */
931 THREAD_OFF(bsp
->installev
);
933 /* Ask for installation. */
934 bsp
->lastev
= BSE_INSTALL
;
935 thread_execute(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0);
941 int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS
)
943 struct bfd_session_params
*bsp
, *bspn
;
944 size_t sessions_updated
= 0;
945 struct interface
*ifp
;
946 int remote_cbit
= false;
947 int state
= BFD_STATUS_UNKNOWN
;
952 char ifstr
[128], cbitstr
[32];
954 if (!zclient
->bfd_integration
)
957 /* Do nothing when shutting down. */
958 if (bsglobal
.shutting_down
)
961 ifp
= bfd_get_peer_info(zclient
->ibuf
, &dp
, &sp
, &state
, &remote_cbit
,
964 * When interface lookup fails or an invalid stream is read, we must
965 * not proceed otherwise it will trigger an assertion while checking
968 if (dp
.family
== 0 || sp
.family
== 0)
971 if (bsglobal
.debugging
) {
974 snprintf(ifstr
, sizeof(ifstr
), " (interface %s)",
977 snprintf(cbitstr
, sizeof(cbitstr
), " (CPI bit %s)",
978 remote_cbit
? "yes" : "no");
980 zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__
, &sp
,
981 &dp
, ifstr
, vrf_id_to_name(vrf_id
), vrf_id
, cbitstr
,
982 bfd_get_status_str(state
));
987 addrlen
= sizeof(struct in_addr
);
990 addrlen
= sizeof(struct in6_addr
);
994 /* Unexpected value. */
999 /* Cache current time to avoid multiple monotime clock calls. */
1000 now
= monotime(NULL
);
1002 /* Notify all matching sessions about update. */
1003 TAILQ_FOREACH_SAFE (bsp
, &bsglobal
.bsplist
, entry
, bspn
) {
1004 /* Skip not installed entries. */
1005 if (!bsp
->installed
)
1007 /* Skip different VRFs. */
1008 if (bsp
->args
.vrf_id
!= vrf_id
)
1010 /* Skip different families. */
1011 if (bsp
->args
.family
!= dp
.family
)
1013 /* Skip different interface. */
1014 if (bsp
->args
.ifnamelen
&& ifp
1015 && strcmp(bsp
->args
.ifname
, ifp
->name
) != 0)
1017 /* Skip non matching destination addresses. */
1018 if (memcmp(&bsp
->args
.dst
, &dp
.u
, addrlen
) != 0)
1021 * Source comparison test:
1022 * We will only compare source if BFD daemon provided the
1023 * source address and the protocol set a source address in
1024 * the configuration otherwise we'll just skip it.
1026 if (sp
.family
&& memcmp(&bsp
->args
.src
, &i6a_zero
, addrlen
) != 0
1027 && memcmp(&sp
.u
, &i6a_zero
, addrlen
) != 0
1028 && memcmp(&bsp
->args
.src
, &sp
.u
, addrlen
) != 0)
1030 /* No session state change. */
1031 if ((int)bsp
->bss
.state
== state
)
1034 bsp
->bss
.last_event
= now
;
1035 bsp
->bss
.previous_state
= bsp
->bss
.state
;
1036 bsp
->bss
.state
= state
;
1037 bsp
->bss
.remote_cbit
= remote_cbit
;
1038 bsp
->updatecb(bsp
, &bsp
->bss
, bsp
->arg
);
1042 if (bsglobal
.debugging
)
1043 zlog_debug("%s: sessions updated: %zu", __func__
,
1050 * Frees all allocated resources and stops any activity.
1052 * Must be called after every BFD session has been successfully
1053 * unconfigured otherwise this function will `free()` any available
1054 * session causing existing pointers to dangle.
1056 * This is just a comment, in practice it will be called by the FRR
1057 * library late finish hook. \see `bfd_protocol_integration_init`.
1059 static int bfd_protocol_integration_finish(void)
1061 if (bsglobal
.zc
== NULL
)
1064 while (!TAILQ_EMPTY(&bsglobal
.bsplist
)) {
1065 struct bfd_session_params
*session
=
1066 TAILQ_FIRST(&bsglobal
.bsplist
);
1067 bfd_sess_free(&session
);
1071 * BFD source cache is linked to sessions, if all sessions are gone
1072 * then the source cache must be empty.
1074 if (!SLIST_EMPTY(&bsglobal
.source_list
))
1075 zlog_warn("BFD integration source cache not empty");
1077 zclient_stop(bsglobal
.nht_zclient
);
1078 zclient_free(bsglobal
.nht_zclient
);
1083 static zclient_handler
*const bfd_nht_handlers
[] = {
1084 [ZEBRA_NEXTHOP_UPDATE
] = bfd_nht_update
,
1087 void bfd_protocol_integration_init(struct zclient
*zc
, struct thread_master
*tm
)
1089 struct zclient_options bfd_nht_options
= zclient_options_default
;
1091 /* Initialize data structure. */
1092 TAILQ_INIT(&bsglobal
.bsplist
);
1093 SLIST_INIT(&bsglobal
.source_list
);
1095 /* Copy pointers. */
1099 /* Enable BFD callbacks. */
1100 zc
->bfd_integration
= true;
1102 /* Send the client registration */
1103 bfd_client_sendmsg(zc
, ZEBRA_BFD_CLIENT_REGISTER
, VRF_DEFAULT
);
1105 /* Start NHT client (for automatic source decisions). */
1106 bsglobal
.nht_zclient
=
1107 zclient_new(tm
, &bfd_nht_options
, bfd_nht_handlers
,
1108 array_size(bfd_nht_handlers
));
1109 bsglobal
.nht_zclient
->sock
= -1;
1110 bsglobal
.nht_zclient
->privs
= zc
->privs
;
1111 bsglobal
.nht_zclient
->zebra_connected
= bfd_nht_zclient_connected
;
1112 thread_add_timer(tm
, bfd_nht_zclient_connect
, bsglobal
.nht_zclient
, 1,
1113 &bsglobal
.nht_zclient
->t_connect
);
1115 hook_register(frr_fini
, bfd_protocol_integration_finish
);
1118 void bfd_protocol_integration_set_debug(bool enable
)
1120 bsglobal
.debugging
= enable
;
1123 void bfd_protocol_integration_set_shutdown(bool enable
)
1125 bsglobal
.shutting_down
= enable
;
1128 bool bfd_protocol_integration_debug(void)
1130 return bsglobal
.debugging
;
1133 bool bfd_protocol_integration_shutting_down(void)
1135 return bsglobal
.shutting_down
;
1139 * BFD automatic source selection
1141 * This feature will use the next hop tracking (NHT) provided by zebra
1142 * to find out the source address by looking at the output interface.
1144 * When the interface address / routing table change we'll be notified
1145 * and be able to update the source address accordingly.
1149 * +-----------------+
1150 * | BFD session set |
1151 * | to auto source |
1152 * +-----------------+
1154 * \ +-----------------+
1155 * --------------> | Resolves |
1158 * +-----------------+
1160 * +-----------------+ /
1161 * | Sets resolved | <----------
1162 * | source address |
1163 * +-----------------+
1166 bfd_source_cache_session_match(const struct bfd_source_cache
*source
,
1167 const struct bfd_session_params
*session
)
1169 const struct in_addr
*address
;
1170 const struct in6_addr
*address_v6
;
1172 if (session
->args
.vrf_id
!= source
->vrf_id
)
1174 if (session
->args
.family
!= source
->address
.family
)
1177 switch (session
->args
.family
) {
1179 address
= (const struct in_addr
*)&session
->args
.dst
;
1180 if (address
->s_addr
!= source
->address
.u
.prefix4
.s_addr
)
1184 address_v6
= &session
->args
.dst
;
1185 if (memcmp(address_v6
, &source
->address
.u
.prefix6
,
1186 sizeof(struct in6_addr
)))
1196 static struct bfd_source_cache
*
1197 bfd_source_cache_find(vrf_id_t vrf_id
, const struct prefix
*prefix
)
1199 struct bfd_source_cache
*source
;
1201 SLIST_FOREACH (source
, &bsglobal
.source_list
, entry
) {
1202 if (source
->vrf_id
!= vrf_id
)
1204 if (!prefix_same(&source
->address
, prefix
))
1213 static void bfd_source_cache_get(struct bfd_session_params
*session
)
1215 struct bfd_source_cache
*source
;
1216 struct prefix target
= {};
1218 switch (session
->args
.family
) {
1220 target
.family
= AF_INET
;
1221 target
.prefixlen
= IPV4_MAX_BITLEN
;
1222 memcpy(&target
.u
.prefix4
, &session
->args
.dst
,
1223 sizeof(struct in_addr
));
1226 target
.family
= AF_INET6
;
1227 target
.prefixlen
= IPV6_MAX_BITLEN
;
1228 memcpy(&target
.u
.prefix6
, &session
->args
.dst
,
1229 sizeof(struct in6_addr
));
1235 source
= bfd_source_cache_find(session
->args
.vrf_id
, &target
);
1237 if (session
->source_cache
== source
)
1240 bfd_source_cache_put(session
);
1241 session
->source_cache
= source
;
1246 source
= XCALLOC(MTYPE_BFD_SOURCE
, sizeof(*source
));
1247 prefix_copy(&source
->address
, &target
);
1248 source
->vrf_id
= session
->args
.vrf_id
;
1249 SLIST_INSERT_HEAD(&bsglobal
.source_list
, source
, entry
);
1251 bfd_source_cache_put(session
);
1252 session
->source_cache
= source
;
1253 source
->refcount
= 1;
1255 bfd_source_cache_register(source
);
1260 static void bfd_source_cache_put(struct bfd_session_params
*session
)
1262 if (session
->source_cache
== NULL
)
1265 session
->source_cache
->refcount
--;
1266 if (session
->source_cache
->refcount
> 0) {
1267 session
->source_cache
= NULL
;
1271 bfd_source_cache_unregister(session
->source_cache
);
1272 SLIST_REMOVE(&bsglobal
.source_list
, session
->source_cache
,
1273 bfd_source_cache
, entry
);
1274 XFREE(MTYPE_BFD_SOURCE
, session
->source_cache
);
1277 /** Updates BFD running session if source address has changed. */
1279 bfd_source_cache_update_session(const struct bfd_source_cache
*source
,
1280 struct bfd_session_params
*session
)
1282 const struct in_addr
*address
;
1283 const struct in6_addr
*address_v6
;
1285 switch (session
->args
.family
) {
1287 address
= (const struct in_addr
*)&session
->args
.src
;
1288 if (memcmp(address
, &source
->source
.u
.prefix4
,
1289 sizeof(struct in_addr
)) == 0)
1292 _bfd_sess_remove(session
);
1293 memcpy(&session
->args
.src
, &source
->source
.u
.prefix4
,
1294 sizeof(struct in_addr
));
1297 address_v6
= &session
->args
.src
;
1298 if (memcmp(address_v6
, &source
->source
.u
.prefix6
,
1299 sizeof(struct in6_addr
)) == 0)
1302 _bfd_sess_remove(session
);
1303 memcpy(&session
->args
.src
, &source
->source
.u
.prefix6
,
1304 sizeof(struct in6_addr
));
1310 bfd_sess_install(session
);
1314 bfd_source_cache_update_sessions(const struct bfd_source_cache
*source
)
1316 struct bfd_session_params
*session
;
1321 TAILQ_FOREACH (session
, &bsglobal
.bsplist
, entry
) {
1322 if (!session
->auto_source
)
1324 if (!bfd_source_cache_session_match(source
, session
))
1327 bfd_source_cache_update_session(source
, session
);
1332 * Try to translate next hop information into source address.
1334 * \returns `true` if source changed otherwise `false`.
1336 static bool bfd_source_cache_update(struct bfd_source_cache
*source
,
1337 const struct zapi_route
*route
)
1341 for (nh_index
= 0; nh_index
< route
->nexthop_num
; nh_index
++) {
1342 const struct zapi_nexthop
*nh
= &route
->nexthops
[nh_index
];
1343 const struct interface
*interface
;
1344 const struct connected
*connected
;
1345 const struct listnode
*node
;
1347 interface
= if_lookup_by_index(nh
->ifindex
, nh
->vrf_id
);
1348 if (interface
== NULL
) {
1349 zlog_err("next hop interface not found (index %d)",
1354 for (ALL_LIST_ELEMENTS_RO(interface
->connected
, node
,
1356 if (source
->address
.family
!=
1357 connected
->address
->family
)
1359 if (prefix_same(connected
->address
, &source
->source
))
1362 * Skip link-local as it is only useful for single hop
1363 * and in that case no source is specified usually.
1365 if (source
->address
.family
== AF_INET6
&&
1366 IN6_IS_ADDR_LINKLOCAL(
1367 &connected
->address
->u
.prefix6
))
1370 prefix_copy(&source
->source
, connected
->address
);
1371 source
->valid
= true;
1376 memset(&source
->source
, 0, sizeof(source
->source
));
1377 source
->valid
= false;
1381 static void bfd_nht_zclient_connect(struct thread
*thread
)
1383 struct zclient
*zclient
= THREAD_ARG(thread
);
1385 if (bsglobal
.debugging
)
1386 zlog_debug("BFD NHT zclient connection attempt");
1388 if (zclient_start(zclient
) == -1) {
1389 if (bsglobal
.debugging
)
1390 zlog_debug("BFD NHT zclient connection failed");
1392 thread_add_timer(bsglobal
.tm
, bfd_nht_zclient_connect
, zclient
,
1393 3, &zclient
->t_connect
);
1397 if (bsglobal
.debugging
)
1398 zlog_debug("BFD NHT zclient connection succeeded");
1401 static void bfd_nht_zclient_connected(struct zclient
*zclient
)
1403 struct bfd_source_cache
*source
;
1405 if (bsglobal
.debugging
)
1406 zlog_debug("BFD NHT zclient connected");
1408 SLIST_FOREACH (source
, &bsglobal
.source_list
, entry
)
1409 bfd_source_cache_register(source
);
1412 static int bfd_nht_update(ZAPI_CALLBACK_ARGS
)
1414 struct bfd_source_cache
*source
;
1415 struct zapi_route route
;
1416 struct prefix match
;
1418 if (!zapi_nexthop_update_decode(zclient
->ibuf
, &match
, &route
)) {
1419 zlog_warn("BFD NHT update decode failure");
1422 if (cmd
!= ZEBRA_NEXTHOP_UPDATE
)
1425 if (bsglobal
.debugging
)
1426 zlog_debug("BFD NHT update for %pFX", &route
.prefix
);
1428 SLIST_FOREACH (source
, &bsglobal
.source_list
, entry
) {
1429 if (source
->vrf_id
!= route
.vrf_id
)
1431 if (!prefix_same(&match
, &source
->address
))
1433 if (bfd_source_cache_update(source
, &route
))
1434 bfd_source_cache_update_sessions(source
);