1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * bfd.c: BFD handling routines
5 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
22 DEFINE_MTYPE_STATIC(LIB
, BFD_INFO
, "BFD info");
23 DEFINE_MTYPE_STATIC(LIB
, BFD_SOURCE
, "BFD source cache");
26 * BFD protocol integration configuration.
29 /** Events definitions. */
30 enum bfd_session_event
{
31 /** Remove the BFD session configuration. */
33 /** Install the BFD session configuration. */
38 * BFD source selection result cache.
40 * This structure will keep track of the result based on the destination
41 * prefix. When the result changes all related BFD sessions with automatic
42 * source will be updated.
44 struct bfd_source_cache
{
45 /** Address VRF belongs. */
47 /** Destination network address. */
48 struct prefix address
;
49 /** Source selected. */
51 /** Is the source address valid? */
53 /** BFD sessions using this. */
56 SLIST_ENTRY(bfd_source_cache
) entry
;
58 SLIST_HEAD(bfd_source_list
, bfd_source_cache
);
61 * Data structure to do the necessary tricks to hide the BFD protocol
62 * integration internals.
64 struct bfd_session_params
{
65 /** Contains the session parameters and more. */
66 struct bfd_session_arg args
;
67 /** Contains the session state. */
68 struct bfd_session_status bss
;
69 /** Protocol implementation status update callback. */
70 bsp_status_update updatecb
;
71 /** Protocol implementation custom data pointer. */
77 * This variable controls what action to execute when the command batch
78 * finishes. Normally we'd use `event_add_event` value, however since
79 * that function is going to be called multiple times and the value
80 * might be different we'll use this variable to keep track of it.
82 enum bfd_session_event lastev
;
84 * BFD session configuration event.
86 * Multiple actions might be asked during a command batch (either via
87 * configuration load or northbound batch), so we'll use this to
88 * install/uninstall the BFD session parameters only once.
90 struct event
*installev
;
92 /** BFD session installation state. */
95 /** Automatic source selection. */
97 /** Currently selected source. */
98 struct bfd_source_cache
*source_cache
;
100 /** Global BFD paramaters list. */
101 TAILQ_ENTRY(bfd_session_params
) entry
;
104 struct bfd_sessions_global
{
106 * Global BFD session parameters list for (re)installation and update
107 * without code duplication among daemons.
109 TAILQ_HEAD(bsplist
, bfd_session_params
) bsplist
;
110 /** BFD automatic source selection cache. */
111 struct bfd_source_list source_list
;
113 /** Pointer to FRR's event manager. */
114 struct event_loop
*tm
;
115 /** Pointer to zebra client data structure. */
118 /** Debugging state. */
120 /** Is shutting down? */
124 /** Global configuration variable. */
125 static struct bfd_sessions_global bsglobal
;
127 /** Global empty address for IPv4/IPv6. */
128 static const struct in6_addr i6a_zero
;
134 static void bfd_source_cache_get(struct bfd_session_params
*session
);
135 static void bfd_source_cache_put(struct bfd_session_params
*session
);
138 * bfd_get_peer_info - Extract the Peer information for which the BFD session
139 * went down from the message sent from Zebra to clients.
141 static struct interface
*bfd_get_peer_info(struct stream
*s
, struct prefix
*dp
,
142 struct prefix
*sp
, int *status
,
143 int *remote_cbit
, vrf_id_t vrf_id
)
145 unsigned int ifindex
;
146 struct interface
*ifp
= NULL
;
148 int local_remote_cbit
;
151 * If the ifindex lookup fails the
152 * rest of the data in the stream is
153 * not read. All examples of this function
154 * call immediately use the dp->family which
155 * is not good. Ensure we are not using
158 memset(dp
, 0, sizeof(*dp
));
159 memset(sp
, 0, sizeof(*sp
));
161 /* Get interface index. */
162 STREAM_GETL(s
, ifindex
);
166 ifp
= if_lookup_by_index(ifindex
, vrf_id
);
168 if (bsglobal
.debugging
)
170 "%s: Can't find interface by ifindex: %d ",
176 /* Fetch destination address. */
177 STREAM_GETC(s
, dp
->family
);
179 plen
= prefix_blen(dp
);
180 STREAM_GET(&dp
->u
.prefix
, s
, plen
);
181 STREAM_GETC(s
, dp
->prefixlen
);
183 /* Get BFD status. */
184 STREAM_GETL(s
, (*status
));
186 STREAM_GETC(s
, sp
->family
);
188 plen
= prefix_blen(sp
);
189 STREAM_GET(&sp
->u
.prefix
, s
, plen
);
190 STREAM_GETC(s
, sp
->prefixlen
);
192 STREAM_GETC(s
, local_remote_cbit
);
194 *remote_cbit
= local_remote_cbit
;
199 * Clean dp and sp because caller
200 * will immediately check them valid or not
202 memset(dp
, 0, sizeof(*dp
));
203 memset(sp
, 0, sizeof(*sp
));
208 * bfd_get_status_str - Convert BFD status to a display string.
210 const char *bfd_get_status_str(int status
)
213 case BFD_STATUS_DOWN
:
217 case BFD_STATUS_ADMIN_DOWN
:
219 case BFD_STATUS_UNKNOWN
:
226 * bfd_last_update - Calculate the last BFD update time and convert it
227 * into a dd:hh:mm:ss display format.
229 static void bfd_last_update(time_t last_update
, char *buf
, size_t len
)
236 /* If no BFD status update has ever been received, print `never'. */
237 if (last_update
== 0) {
238 snprintf(buf
, len
, "never");
242 /* Get current time. */
245 diff
= curr
- last_update
;
246 gmtime_r(&diff
, &tm
);
248 snprintf(buf
, len
, "%d:%02d:%02d:%02d", tm
.tm_yday
, tm
.tm_hour
,
249 tm
.tm_min
, tm
.tm_sec
);
253 * bfd_client_sendmsg - Format and send a client register
254 * command to Zebra to be forwarded to BFD
256 void bfd_client_sendmsg(struct zclient
*zclient
, int command
,
260 enum zclient_send_status ret
;
263 if (!zclient
|| zclient
->sock
< 0) {
264 if (bsglobal
.debugging
)
266 "%s: Can't send BFD client register, Zebra client not established",
273 zclient_create_header(s
, command
, vrf_id
);
275 stream_putl(s
, getpid());
277 stream_putw_at(s
, 0, stream_get_endp(s
));
279 ret
= zclient_send_message(zclient
);
281 if (ret
== ZCLIENT_SEND_FAILURE
) {
282 if (bsglobal
.debugging
)
284 "%s: %ld: zclient_send_message() failed",
285 __func__
, (long)getpid());
292 int zclient_bfd_command(struct zclient
*zc
, struct bfd_session_arg
*args
)
297 /* Individual reg/dereg messages are suppressed during shutdown. */
298 if (bsglobal
.shutting_down
) {
299 if (bsglobal
.debugging
)
301 "%s: Suppressing BFD peer reg/dereg messages",
307 if (!zc
|| zc
->sock
< 0) {
308 if (bsglobal
.debugging
)
309 zlog_debug("%s: zclient unavailable", __func__
);
316 /* Create new message. */
317 zclient_create_header(s
, args
->command
, args
->vrf_id
);
318 stream_putl(s
, getpid());
320 /* Encode destination address. */
321 stream_putw(s
, args
->family
);
322 addrlen
= (args
->family
== AF_INET
) ? sizeof(struct in_addr
)
323 : sizeof(struct in6_addr
);
324 stream_put(s
, &args
->dst
, addrlen
);
327 * For more BFD integration protocol details, see function
328 * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
331 /* Session timers. */
332 stream_putl(s
, args
->min_rx
);
333 stream_putl(s
, args
->min_tx
);
334 stream_putc(s
, args
->detection_multiplier
);
337 stream_putc(s
, args
->mhop
!= 0);
339 /* Source address. */
340 stream_putw(s
, args
->family
);
341 stream_put(s
, &args
->src
, addrlen
);
343 /* Send the expected hops. */
344 stream_putc(s
, args
->hops
);
346 /* Send interface name if any. */
348 /* Don't send interface. */
350 if (bsglobal
.debugging
&& args
->ifnamelen
)
351 zlog_debug("%s: multi hop is configured, not sending interface",
354 stream_putc(s
, args
->ifnamelen
);
356 stream_put(s
, args
->ifname
, args
->ifnamelen
);
359 /* Send the C bit indicator. */
360 stream_putc(s
, args
->cbit
);
362 /* Send profile name if any. */
363 stream_putc(s
, args
->profilelen
);
364 if (args
->profilelen
)
365 stream_put(s
, args
->profile
, args
->profilelen
);
367 /* Encode timers if this is a registration message. */
368 if (args
->command
!= ZEBRA_BFD_DEST_DEREGISTER
) {
369 stream_putl(s
, args
->min_rx
);
370 stream_putl(s
, args
->min_tx
);
371 stream_putc(s
, args
->detection_multiplier
);
375 /* Multi hop indicator. */
378 /* Multi hop always sends the source address. */
379 stream_putw(s
, args
->family
);
380 stream_put(s
, &args
->src
, addrlen
);
382 /* Send the expected hops. */
383 stream_putc(s
, args
->hops
);
385 /* Multi hop indicator. */
388 /* Single hop only sends the source address when IPv6. */
389 if (args
->family
== AF_INET6
) {
390 stream_putw(s
, args
->family
);
391 stream_put(s
, &args
->src
, addrlen
);
394 /* Send interface name if any. */
395 stream_putc(s
, args
->ifnamelen
);
397 stream_put(s
, args
->ifname
, args
->ifnamelen
);
400 /* Send the C bit indicator. */
401 stream_putc(s
, args
->cbit
);
402 #endif /* HAVE_BFDD */
404 /* Finish the message by writing the size. */
405 stream_putw_at(s
, 0, stream_get_endp(s
));
407 /* Send message to zebra. */
408 if (zclient_send_message(zc
) == ZCLIENT_SEND_FAILURE
) {
409 if (bsglobal
.debugging
)
410 zlog_debug("%s: zclient_send_message failed", __func__
);
417 struct bfd_session_params
*bfd_sess_new(bsp_status_update updatecb
, void *arg
)
419 struct bfd_session_params
*bsp
;
421 bsp
= XCALLOC(MTYPE_BFD_INFO
, sizeof(*bsp
));
423 /* Save application data. */
424 bsp
->updatecb
= updatecb
;
428 bsp
->args
.detection_multiplier
= BFD_DEF_DETECT_MULT
;
430 bsp
->args
.min_rx
= BFD_DEF_MIN_RX
;
431 bsp
->args
.min_tx
= BFD_DEF_MIN_TX
;
432 bsp
->args
.vrf_id
= VRF_DEFAULT
;
434 /* Register in global list. */
435 TAILQ_INSERT_TAIL(&bsglobal
.bsplist
, bsp
, entry
);
440 static bool _bfd_sess_valid(const struct bfd_session_params
*bsp
)
442 /* Peer/local address not configured. */
443 if (bsp
->args
.family
== 0)
446 /* Address configured but invalid. */
447 if (bsp
->args
.family
!= AF_INET
&& bsp
->args
.family
!= AF_INET6
) {
448 if (bsglobal
.debugging
)
449 zlog_debug("%s: invalid session family: %d", __func__
,
454 /* Invalid address. */
455 if (memcmp(&bsp
->args
.dst
, &i6a_zero
, sizeof(i6a_zero
)) == 0) {
456 if (bsglobal
.debugging
) {
457 if (bsp
->args
.family
== AF_INET
)
458 zlog_debug("%s: invalid address: %pI4",
460 (struct in_addr
*)&bsp
->args
.dst
);
462 zlog_debug("%s: invalid address: %pI6",
463 __func__
, &bsp
->args
.dst
);
468 /* Multi hop requires local address. */
470 && memcmp(&i6a_zero
, &bsp
->args
.src
, sizeof(i6a_zero
)) == 0) {
471 if (bsglobal
.debugging
)
473 "%s: multi hop but no local address provided",
479 if (bsp
->args
.vrf_id
== VRF_UNKNOWN
) {
480 if (bsglobal
.debugging
)
481 zlog_debug("%s: asked for unknown VRF", __func__
);
488 static void _bfd_sess_send(struct event
*t
)
490 struct bfd_session_params
*bsp
= EVENT_ARG(t
);
493 /* Validate configuration before trying to send bogus data. */
494 if (!_bfd_sess_valid(bsp
))
497 if (bsp
->lastev
== BSE_INSTALL
) {
498 bsp
->args
.command
= bsp
->installed
? ZEBRA_BFD_DEST_UPDATE
499 : ZEBRA_BFD_DEST_REGISTER
;
501 bsp
->args
.command
= ZEBRA_BFD_DEST_DEREGISTER
;
503 /* If not installed and asked for uninstall, do nothing. */
504 if (!bsp
->installed
&& bsp
->args
.command
== ZEBRA_BFD_DEST_DEREGISTER
)
507 rv
= zclient_bfd_command(bsglobal
.zc
, &bsp
->args
);
508 /* Command was sent successfully. */
510 /* Update installation status. */
511 if (bsp
->args
.command
== ZEBRA_BFD_DEST_DEREGISTER
)
512 bsp
->installed
= false;
513 else if (bsp
->args
.command
== ZEBRA_BFD_DEST_REGISTER
)
514 bsp
->installed
= true;
516 struct ipaddr src
, dst
;
518 src
.ipa_type
= bsp
->args
.family
;
519 src
.ipaddr_v6
= bsp
->args
.src
;
520 dst
.ipa_type
= bsp
->args
.family
;
521 dst
.ipaddr_v6
= bsp
->args
.dst
;
524 "%s: BFD session %pIA -> %pIA interface %s VRF %s(%u) was not %s",
525 __func__
, &src
, &dst
,
526 bsp
->args
.ifnamelen
? bsp
->args
.ifname
: "*",
527 vrf_id_to_name(bsp
->args
.vrf_id
), bsp
->args
.vrf_id
,
528 bsp
->lastev
== BSE_INSTALL
? "installed"
533 static void _bfd_sess_remove(struct bfd_session_params
*bsp
)
535 /* Cancel any pending installation request. */
536 EVENT_OFF(bsp
->installev
);
538 /* Not installed, nothing to do. */
542 /* Send request to remove any session. */
543 bsp
->lastev
= BSE_UNINSTALL
;
544 event_execute(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0);
547 void bfd_sess_free(struct bfd_session_params
**bsp
)
552 /* Remove any installed session. */
553 _bfd_sess_remove(*bsp
);
555 /* Remove from global list. */
556 TAILQ_REMOVE(&bsglobal
.bsplist
, (*bsp
), entry
);
558 bfd_source_cache_put(*bsp
);
560 /* Free the memory and point to NULL. */
561 XFREE(MTYPE_BFD_INFO
, (*bsp
));
564 static bool bfd_sess_address_changed(const struct bfd_session_params
*bsp
,
566 const struct in6_addr
*src
,
567 const struct in6_addr
*dst
)
571 if (bsp
->args
.family
!= family
)
574 addrlen
= (family
== AF_INET
) ? sizeof(struct in_addr
)
575 : sizeof(struct in6_addr
);
576 if ((src
== NULL
&& memcmp(&bsp
->args
.src
, &i6a_zero
, addrlen
))
577 || (src
&& memcmp(src
, &bsp
->args
.src
, addrlen
))
578 || memcmp(dst
, &bsp
->args
.dst
, addrlen
))
584 void bfd_sess_set_ipv4_addrs(struct bfd_session_params
*bsp
,
585 const struct in_addr
*src
,
586 const struct in_addr
*dst
)
588 if (!bfd_sess_address_changed(bsp
, AF_INET
, (struct in6_addr
*)src
,
589 (struct in6_addr
*)dst
))
592 /* If already installed, remove the old setting. */
593 _bfd_sess_remove(bsp
);
594 /* Address changed so we must reapply auto source. */
595 bfd_source_cache_put(bsp
);
597 bsp
->args
.family
= AF_INET
;
599 /* Clean memory, set zero value and avoid static analyser warnings. */
600 memset(&bsp
->args
.src
, 0, sizeof(bsp
->args
.src
));
601 memset(&bsp
->args
.dst
, 0, sizeof(bsp
->args
.dst
));
603 /* Copy the equivalent of IPv4 to arguments structure. */
605 memcpy(&bsp
->args
.src
, src
, sizeof(struct in_addr
));
608 memcpy(&bsp
->args
.dst
, dst
, sizeof(struct in_addr
));
610 if (bsp
->auto_source
)
611 bfd_source_cache_get(bsp
);
614 void bfd_sess_set_ipv6_addrs(struct bfd_session_params
*bsp
,
615 const struct in6_addr
*src
,
616 const struct in6_addr
*dst
)
618 if (!bfd_sess_address_changed(bsp
, AF_INET6
, src
, dst
))
621 /* If already installed, remove the old setting. */
622 _bfd_sess_remove(bsp
);
623 /* Address changed so we must reapply auto source. */
624 bfd_source_cache_put(bsp
);
626 bsp
->args
.family
= AF_INET6
;
628 /* Clean memory, set zero value and avoid static analyser warnings. */
629 memset(&bsp
->args
.src
, 0, sizeof(bsp
->args
.src
));
632 bsp
->args
.src
= *src
;
635 bsp
->args
.dst
= *dst
;
637 if (bsp
->auto_source
)
638 bfd_source_cache_get(bsp
);
641 void bfd_sess_set_interface(struct bfd_session_params
*bsp
, const char *ifname
)
643 if ((ifname
== NULL
&& bsp
->args
.ifnamelen
== 0)
644 || (ifname
&& strcmp(bsp
->args
.ifname
, ifname
) == 0))
647 /* If already installed, remove the old setting. */
648 _bfd_sess_remove(bsp
);
650 if (ifname
== NULL
) {
651 bsp
->args
.ifname
[0] = 0;
652 bsp
->args
.ifnamelen
= 0;
656 if (strlcpy(bsp
->args
.ifname
, ifname
, sizeof(bsp
->args
.ifname
))
657 > sizeof(bsp
->args
.ifname
))
658 zlog_warn("%s: interface name truncated: %s", __func__
, ifname
);
660 bsp
->args
.ifnamelen
= strlen(bsp
->args
.ifname
);
663 void bfd_sess_set_profile(struct bfd_session_params
*bsp
, const char *profile
)
665 if (profile
== NULL
) {
666 bsp
->args
.profile
[0] = 0;
667 bsp
->args
.profilelen
= 0;
671 if (strlcpy(bsp
->args
.profile
, profile
, sizeof(bsp
->args
.profile
))
672 > sizeof(bsp
->args
.profile
))
673 zlog_warn("%s: profile name truncated: %s", __func__
, profile
);
675 bsp
->args
.profilelen
= strlen(bsp
->args
.profile
);
678 void bfd_sess_set_vrf(struct bfd_session_params
*bsp
, vrf_id_t vrf_id
)
680 if (bsp
->args
.vrf_id
== vrf_id
)
683 /* If already installed, remove the old setting. */
684 _bfd_sess_remove(bsp
);
685 /* Address changed so we must reapply auto source. */
686 bfd_source_cache_put(bsp
);
688 bsp
->args
.vrf_id
= vrf_id
;
690 if (bsp
->auto_source
)
691 bfd_source_cache_get(bsp
);
694 void bfd_sess_set_hop_count(struct bfd_session_params
*bsp
, uint8_t hops
)
696 if (bsp
->args
.hops
== hops
)
699 /* If already installed, remove the old setting. */
700 _bfd_sess_remove(bsp
);
702 bsp
->args
.hops
= hops
;
703 bsp
->args
.mhop
= (hops
> 1);
707 void bfd_sess_set_cbit(struct bfd_session_params
*bsp
, bool enable
)
709 bsp
->args
.cbit
= enable
;
712 void bfd_sess_set_timers(struct bfd_session_params
*bsp
,
713 uint8_t detection_multiplier
, uint32_t min_rx
,
716 bsp
->args
.detection_multiplier
= detection_multiplier
;
717 bsp
->args
.min_rx
= min_rx
;
718 bsp
->args
.min_tx
= min_tx
;
721 void bfd_sess_set_auto_source(struct bfd_session_params
*bsp
, bool enable
)
723 if (bsp
->auto_source
== enable
)
726 bsp
->auto_source
= enable
;
728 bfd_source_cache_get(bsp
);
730 bfd_source_cache_put(bsp
);
733 void bfd_sess_install(struct bfd_session_params
*bsp
)
735 bsp
->lastev
= BSE_INSTALL
;
736 event_add_event(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0, &bsp
->installev
);
739 void bfd_sess_uninstall(struct bfd_session_params
*bsp
)
741 bsp
->lastev
= BSE_UNINSTALL
;
742 event_add_event(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0, &bsp
->installev
);
745 enum bfd_session_state
bfd_sess_status(const struct bfd_session_params
*bsp
)
747 return bsp
->bss
.state
;
750 uint8_t bfd_sess_hop_count(const struct bfd_session_params
*bsp
)
752 return bsp
->args
.hops
;
755 const char *bfd_sess_profile(const struct bfd_session_params
*bsp
)
757 return bsp
->args
.profilelen
? bsp
->args
.profile
: NULL
;
760 void bfd_sess_addresses(const struct bfd_session_params
*bsp
, int *family
,
761 struct in6_addr
*src
, struct in6_addr
*dst
)
763 *family
= bsp
->args
.family
;
765 *src
= bsp
->args
.src
;
767 *dst
= bsp
->args
.dst
;
770 const char *bfd_sess_interface(const struct bfd_session_params
*bsp
)
772 if (bsp
->args
.ifnamelen
)
773 return bsp
->args
.ifname
;
778 const char *bfd_sess_vrf(const struct bfd_session_params
*bsp
)
780 return vrf_id_to_name(bsp
->args
.vrf_id
);
783 vrf_id_t
bfd_sess_vrf_id(const struct bfd_session_params
*bsp
)
785 return bsp
->args
.vrf_id
;
788 bool bfd_sess_cbit(const struct bfd_session_params
*bsp
)
790 return bsp
->args
.cbit
;
793 void bfd_sess_timers(const struct bfd_session_params
*bsp
,
794 uint8_t *detection_multiplier
, uint32_t *min_rx
,
797 *detection_multiplier
= bsp
->args
.detection_multiplier
;
798 *min_rx
= bsp
->args
.min_rx
;
799 *min_tx
= bsp
->args
.min_tx
;
802 bool bfd_sess_auto_source(const struct bfd_session_params
*bsp
)
804 return bsp
->auto_source
;
807 void bfd_sess_show(struct vty
*vty
, struct json_object
*json
,
808 struct bfd_session_params
*bsp
)
810 json_object
*json_bfd
= NULL
;
818 json_bfd
= json_object_new_object();
820 json_object_string_add(json_bfd
, "type", "multi hop");
822 json_object_string_add(json_bfd
, "type", "single hop");
824 vty_out(vty
, " BFD: Type: %s\n",
825 bsp
->args
.mhop
? "multi hop" : "single hop");
827 /* Show configuration. */
829 json_object_int_add(json_bfd
, "detectMultiplier",
830 bsp
->args
.detection_multiplier
);
831 json_object_int_add(json_bfd
, "rxMinInterval",
833 json_object_int_add(json_bfd
, "txMinInterval",
837 " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
838 bsp
->args
.detection_multiplier
, bsp
->args
.min_rx
,
842 bfd_last_update(bsp
->bss
.last_event
, time_buf
, sizeof(time_buf
));
844 json_object_string_add(json_bfd
, "status",
845 bfd_get_status_str(bsp
->bss
.state
));
846 json_object_string_add(json_bfd
, "lastUpdate", time_buf
);
848 vty_out(vty
, " Status: %s, Last update: %s\n",
849 bfd_get_status_str(bsp
->bss
.state
), time_buf
);
852 json_object_object_add(json
, "peerBfdInfo", json_bfd
);
858 * Zebra communication related.
862 * Callback for reinstallation of all registered BFD sessions.
864 * Use this as `zclient` `bfd_dest_replay` callback.
866 int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS
)
868 struct bfd_session_params
*bsp
;
870 if (!zclient
->bfd_integration
)
873 /* Do nothing when shutting down. */
874 if (bsglobal
.shutting_down
)
877 if (bsglobal
.debugging
)
878 zlog_debug("%s: sending all sessions registered", __func__
);
880 /* Send the client registration */
881 bfd_client_sendmsg(zclient
, ZEBRA_BFD_CLIENT_REGISTER
, vrf_id
);
883 /* Replay all activated peers. */
884 TAILQ_FOREACH (bsp
, &bsglobal
.bsplist
, entry
) {
885 /* Skip not installed sessions. */
889 /* We are reconnecting, so we must send installation. */
890 bsp
->installed
= false;
892 /* Cancel any pending installation request. */
893 EVENT_OFF(bsp
->installev
);
895 /* Ask for installation. */
896 bsp
->lastev
= BSE_INSTALL
;
897 event_execute(bsglobal
.tm
, _bfd_sess_send
, bsp
, 0);
903 int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS
)
905 struct bfd_session_params
*bsp
, *bspn
;
906 size_t sessions_updated
= 0;
907 struct interface
*ifp
;
908 int remote_cbit
= false;
909 int state
= BFD_STATUS_UNKNOWN
;
914 char ifstr
[128], cbitstr
[32];
916 if (!zclient
->bfd_integration
)
919 /* Do nothing when shutting down. */
920 if (bsglobal
.shutting_down
)
923 ifp
= bfd_get_peer_info(zclient
->ibuf
, &dp
, &sp
, &state
, &remote_cbit
,
926 * When interface lookup fails or an invalid stream is read, we must
927 * not proceed otherwise it will trigger an assertion while checking
930 if (dp
.family
== 0 || sp
.family
== 0)
933 if (bsglobal
.debugging
) {
936 snprintf(ifstr
, sizeof(ifstr
), " (interface %s)",
939 snprintf(cbitstr
, sizeof(cbitstr
), " (CPI bit %s)",
940 remote_cbit
? "yes" : "no");
942 zlog_debug("%s: %pFX -> %pFX%s VRF %s(%u)%s: %s", __func__
, &sp
,
943 &dp
, ifstr
, vrf_id_to_name(vrf_id
), vrf_id
, cbitstr
,
944 bfd_get_status_str(state
));
949 addrlen
= sizeof(struct in_addr
);
952 addrlen
= sizeof(struct in6_addr
);
956 /* Unexpected value. */
961 /* Cache current time to avoid multiple monotime clock calls. */
962 now
= monotime(NULL
);
964 /* Notify all matching sessions about update. */
965 TAILQ_FOREACH_SAFE (bsp
, &bsglobal
.bsplist
, entry
, bspn
) {
966 /* Skip not installed entries. */
969 /* Skip different VRFs. */
970 if (bsp
->args
.vrf_id
!= vrf_id
)
972 /* Skip different families. */
973 if (bsp
->args
.family
!= dp
.family
)
975 /* Skip different interface. */
976 if (bsp
->args
.ifnamelen
&& ifp
977 && strcmp(bsp
->args
.ifname
, ifp
->name
) != 0)
979 /* Skip non matching destination addresses. */
980 if (memcmp(&bsp
->args
.dst
, &dp
.u
, addrlen
) != 0)
983 * Source comparison test:
984 * We will only compare source if BFD daemon provided the
985 * source address and the protocol set a source address in
986 * the configuration otherwise we'll just skip it.
988 if (sp
.family
&& memcmp(&bsp
->args
.src
, &i6a_zero
, addrlen
) != 0
989 && memcmp(&sp
.u
, &i6a_zero
, addrlen
) != 0
990 && memcmp(&bsp
->args
.src
, &sp
.u
, addrlen
) != 0)
992 /* No session state change. */
993 if ((int)bsp
->bss
.state
== state
)
996 bsp
->bss
.last_event
= now
;
997 bsp
->bss
.previous_state
= bsp
->bss
.state
;
998 bsp
->bss
.state
= state
;
999 bsp
->bss
.remote_cbit
= remote_cbit
;
1000 bsp
->updatecb(bsp
, &bsp
->bss
, bsp
->arg
);
1004 if (bsglobal
.debugging
)
1005 zlog_debug("%s: sessions updated: %zu", __func__
,
1012 * Frees all allocated resources and stops any activity.
1014 * Must be called after every BFD session has been successfully
1015 * unconfigured otherwise this function will `free()` any available
1016 * session causing existing pointers to dangle.
1018 * This is just a comment, in practice it will be called by the FRR
1019 * library late finish hook. \see `bfd_protocol_integration_init`.
1021 static int bfd_protocol_integration_finish(void)
1023 if (bsglobal
.zc
== NULL
)
1026 while (!TAILQ_EMPTY(&bsglobal
.bsplist
)) {
1027 struct bfd_session_params
*session
=
1028 TAILQ_FIRST(&bsglobal
.bsplist
);
1029 bfd_sess_free(&session
);
1033 * BFD source cache is linked to sessions, if all sessions are gone
1034 * then the source cache must be empty.
1036 if (!SLIST_EMPTY(&bsglobal
.source_list
))
1037 zlog_warn("BFD integration source cache not empty");
1042 void bfd_protocol_integration_init(struct zclient
*zc
, struct event_loop
*tm
)
1044 /* Initialize data structure. */
1045 TAILQ_INIT(&bsglobal
.bsplist
);
1046 SLIST_INIT(&bsglobal
.source_list
);
1048 /* Copy pointers. */
1052 /* Enable BFD callbacks. */
1053 zc
->bfd_integration
= true;
1055 /* Send the client registration */
1056 bfd_client_sendmsg(zc
, ZEBRA_BFD_CLIENT_REGISTER
, VRF_DEFAULT
);
1058 hook_register(frr_fini
, bfd_protocol_integration_finish
);
1061 void bfd_protocol_integration_set_debug(bool enable
)
1063 bsglobal
.debugging
= enable
;
1066 void bfd_protocol_integration_set_shutdown(bool enable
)
1068 bsglobal
.shutting_down
= enable
;
1071 bool bfd_protocol_integration_debug(void)
1073 return bsglobal
.debugging
;
1076 bool bfd_protocol_integration_shutting_down(void)
1078 return bsglobal
.shutting_down
;
1082 * BFD automatic source selection
1084 * This feature will use the next hop tracking (NHT) provided by zebra
1085 * to find out the source address by looking at the output interface.
1087 * When the interface address / routing table change we'll be notified
1088 * and be able to update the source address accordingly.
1092 * +-----------------+
1093 * | BFD session set |
1094 * | to auto source |
1095 * +-----------------+
1097 * \ +-----------------+
1098 * --------------> | Resolves |
1101 * +-----------------+
1103 * +-----------------+ /
1104 * | Sets resolved | <----------
1105 * | source address |
1106 * +-----------------+
1109 bfd_source_cache_session_match(const struct bfd_source_cache
*source
,
1110 const struct bfd_session_params
*session
)
1112 const struct in_addr
*address
;
1113 const struct in6_addr
*address_v6
;
1115 if (session
->args
.vrf_id
!= source
->vrf_id
)
1117 if (session
->args
.family
!= source
->address
.family
)
1120 switch (session
->args
.family
) {
1122 address
= (const struct in_addr
*)&session
->args
.dst
;
1123 if (address
->s_addr
!= source
->address
.u
.prefix4
.s_addr
)
1127 address_v6
= &session
->args
.dst
;
1128 if (memcmp(address_v6
, &source
->address
.u
.prefix6
,
1129 sizeof(struct in6_addr
)))
1139 static struct bfd_source_cache
*
1140 bfd_source_cache_find(vrf_id_t vrf_id
, const struct prefix
*prefix
)
1142 struct bfd_source_cache
*source
;
1144 SLIST_FOREACH (source
, &bsglobal
.source_list
, entry
) {
1145 if (source
->vrf_id
!= vrf_id
)
1147 if (!prefix_same(&source
->address
, prefix
))
1156 static void bfd_source_cache_get(struct bfd_session_params
*session
)
1158 struct bfd_source_cache
*source
;
1159 struct prefix target
= {};
1161 switch (session
->args
.family
) {
1163 target
.family
= AF_INET
;
1164 target
.prefixlen
= IPV4_MAX_BITLEN
;
1165 memcpy(&target
.u
.prefix4
, &session
->args
.dst
,
1166 sizeof(struct in_addr
));
1169 target
.family
= AF_INET6
;
1170 target
.prefixlen
= IPV6_MAX_BITLEN
;
1171 memcpy(&target
.u
.prefix6
, &session
->args
.dst
,
1172 sizeof(struct in6_addr
));
1178 source
= bfd_source_cache_find(session
->args
.vrf_id
, &target
);
1180 if (session
->source_cache
== source
)
1183 bfd_source_cache_put(session
);
1184 session
->source_cache
= source
;
1189 source
= XCALLOC(MTYPE_BFD_SOURCE
, sizeof(*source
));
1190 prefix_copy(&source
->address
, &target
);
1191 source
->vrf_id
= session
->args
.vrf_id
;
1192 SLIST_INSERT_HEAD(&bsglobal
.source_list
, source
, entry
);
1194 bfd_source_cache_put(session
);
1195 session
->source_cache
= source
;
1196 source
->refcount
= 1;
1201 static void bfd_source_cache_put(struct bfd_session_params
*session
)
1203 if (session
->source_cache
== NULL
)
1206 session
->source_cache
->refcount
--;
1207 if (session
->source_cache
->refcount
> 0) {
1208 session
->source_cache
= NULL
;
1212 SLIST_REMOVE(&bsglobal
.source_list
, session
->source_cache
,
1213 bfd_source_cache
, entry
);
1214 XFREE(MTYPE_BFD_SOURCE
, session
->source_cache
);
1217 /** Updates BFD running session if source address has changed. */
1219 bfd_source_cache_update_session(const struct bfd_source_cache
*source
,
1220 struct bfd_session_params
*session
)
1222 const struct in_addr
*address
;
1223 const struct in6_addr
*address_v6
;
1225 switch (session
->args
.family
) {
1227 address
= (const struct in_addr
*)&session
->args
.src
;
1228 if (memcmp(address
, &source
->source
.u
.prefix4
,
1229 sizeof(struct in_addr
)) == 0)
1232 _bfd_sess_remove(session
);
1233 memcpy(&session
->args
.src
, &source
->source
.u
.prefix4
,
1234 sizeof(struct in_addr
));
1237 address_v6
= &session
->args
.src
;
1238 if (memcmp(address_v6
, &source
->source
.u
.prefix6
,
1239 sizeof(struct in6_addr
)) == 0)
1242 _bfd_sess_remove(session
);
1243 memcpy(&session
->args
.src
, &source
->source
.u
.prefix6
,
1244 sizeof(struct in6_addr
));
1250 bfd_sess_install(session
);
1254 bfd_source_cache_update_sessions(const struct bfd_source_cache
*source
)
1256 struct bfd_session_params
*session
;
1261 TAILQ_FOREACH (session
, &bsglobal
.bsplist
, entry
) {
1262 if (!session
->auto_source
)
1264 if (!bfd_source_cache_session_match(source
, session
))
1267 bfd_source_cache_update_session(source
, session
);
1272 * Try to translate next hop information into source address.
1274 * \returns `true` if source changed otherwise `false`.
1276 static bool bfd_source_cache_update(struct bfd_source_cache
*source
,
1277 const struct zapi_route
*route
)
1281 for (nh_index
= 0; nh_index
< route
->nexthop_num
; nh_index
++) {
1282 const struct zapi_nexthop
*nh
= &route
->nexthops
[nh_index
];
1283 const struct interface
*interface
;
1284 const struct connected
*connected
;
1285 const struct listnode
*node
;
1287 interface
= if_lookup_by_index(nh
->ifindex
, nh
->vrf_id
);
1288 if (interface
== NULL
) {
1289 zlog_err("next hop interface not found (index %d)",
1294 for (ALL_LIST_ELEMENTS_RO(interface
->connected
, node
,
1296 if (source
->address
.family
!=
1297 connected
->address
->family
)
1299 if (prefix_same(connected
->address
, &source
->source
))
1302 * Skip link-local as it is only useful for single hop
1303 * and in that case no source is specified usually.
1305 if (source
->address
.family
== AF_INET6
&&
1306 IN6_IS_ADDR_LINKLOCAL(
1307 &connected
->address
->u
.prefix6
))
1310 prefix_copy(&source
->source
, connected
->address
);
1311 source
->valid
= true;
1316 memset(&source
->source
, 0, sizeof(source
->source
));
1317 source
->valid
= false;
1321 int bfd_nht_update(const struct prefix
*match
, const struct zapi_route
*route
)
1323 struct bfd_source_cache
*source
;
1325 if (bsglobal
.debugging
)
1326 zlog_debug("BFD NHT update for %pFX", &route
->prefix
);
1328 SLIST_FOREACH (source
, &bsglobal
.source_list
, entry
) {
1329 if (source
->vrf_id
!= route
->vrf_id
)
1331 if (!prefix_same(match
, &source
->address
))
1333 if (bfd_source_cache_update(source
, route
))
1334 bfd_source_cache_update_sessions(source
);