]> git.proxmox.com Git - mirror_frr.git/blame - lib/bfd.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / lib / bfd.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
7f342629
DS
2/**
3 * bfd.c: BFD handling routines
4 *
5 * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
7f342629
DS
6 */
7
8#include <zebra.h>
9
10#include "command.h"
11#include "memory.h"
12#include "prefix.h"
24a58196 13#include "frrevent.h"
7f342629 14#include "stream.h"
a099abe5 15#include "vrf.h"
7f342629 16#include "zclient.h"
4e35b32e 17#include "libfrr.h"
7f342629
DS
18#include "table.h"
19#include "vty.h"
20#include "bfd.h"
21
bf8d3d6a 22DEFINE_MTYPE_STATIC(LIB, BFD_INFO, "BFD info");
b7ca809d 23DEFINE_MTYPE_STATIC(LIB, BFD_SOURCE, "BFD source cache");
4a1ab8e4 24
c97b34cf
IR
25/**
26 * BFD protocol integration configuration.
7f342629 27 */
7f342629 28
c97b34cf
IR
29/** Events definitions. */
30enum bfd_session_event {
31 /** Remove the BFD session configuration. */
32 BSE_UNINSTALL,
33 /** Install the BFD session configuration. */
34 BSE_INSTALL,
35};
7f342629 36
b7ca809d
RZ
37/**
38 * BFD source selection result cache.
39 *
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.
43 */
44struct bfd_source_cache {
45 /** Address VRF belongs. */
46 vrf_id_t vrf_id;
47 /** Destination network address. */
48 struct prefix address;
49 /** Source selected. */
50 struct prefix source;
51 /** Is the source address valid? */
52 bool valid;
53 /** BFD sessions using this. */
54 size_t refcount;
55
56 SLIST_ENTRY(bfd_source_cache) entry;
57};
58SLIST_HEAD(bfd_source_list, bfd_source_cache);
59
c97b34cf
IR
60/**
61 * Data structure to do the necessary tricks to hide the BFD protocol
62 * integration internals.
7f342629 63 */
c97b34cf
IR
64struct 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. */
72 void *arg;
7f342629 73
c97b34cf
IR
74 /**
75 * Next event.
76 *
77 * This variable controls what action to execute when the command batch
907a2395 78 * finishes. Normally we'd use `event_add_event` value, however since
c97b34cf
IR
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.
81 */
82 enum bfd_session_event lastev;
83 /**
84 * BFD session configuration event.
85 *
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.
89 */
e6685141 90 struct event *installev;
d62a17ae 91
c97b34cf
IR
92 /** BFD session installation state. */
93 bool installed;
d62a17ae 94
b7ca809d
RZ
95 /** Automatic source selection. */
96 bool auto_source;
97 /** Currently selected source. */
98 struct bfd_source_cache *source_cache;
99
c97b34cf
IR
100 /** Global BFD paramaters list. */
101 TAILQ_ENTRY(bfd_session_params) entry;
102};
d62a17ae 103
c97b34cf
IR
104struct bfd_sessions_global {
105 /**
106 * Global BFD session parameters list for (re)installation and update
107 * without code duplication among daemons.
108 */
109 TAILQ_HEAD(bsplist, bfd_session_params) bsplist;
b7ca809d
RZ
110 /** BFD automatic source selection cache. */
111 struct bfd_source_list source_list;
18322efd 112
c97b34cf 113 /** Pointer to FRR's event manager. */
cd9d0537 114 struct event_loop *tm;
c97b34cf
IR
115 /** Pointer to zebra client data structure. */
116 struct zclient *zc;
18322efd 117
c97b34cf
IR
118 /** Debugging state. */
119 bool debugging;
120 /** Is shutting down? */
121 bool shutting_down;
122};
18322efd 123
c97b34cf
IR
124/** Global configuration variable. */
125static struct bfd_sessions_global bsglobal;
7f342629 126
c97b34cf
IR
127/** Global empty address for IPv4/IPv6. */
128static const struct in6_addr i6a_zero;
7f342629 129
b7ca809d
RZ
130/*
131 * Prototypes
132 */
b7ca809d
RZ
133
134static void bfd_source_cache_get(struct bfd_session_params *session);
135static void bfd_source_cache_put(struct bfd_session_params *session);
136
7f342629
DS
137/*
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.
140 */
c97b34cf
IR
141static 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)
7f342629 144{
d62a17ae 145 unsigned int ifindex;
146 struct interface *ifp = NULL;
147 int plen;
9beff0bd 148 int local_remote_cbit;
d62a17ae 149
37a74717
DS
150 /*
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
156 * random data
157 */
158 memset(dp, 0, sizeof(*dp));
159 memset(sp, 0, sizeof(*sp));
160
d62a17ae 161 /* Get interface index. */
9c39d197 162 STREAM_GETL(s, ifindex);
d62a17ae 163
164 /* Lookup index. */
165 if (ifindex != 0) {
166 ifp = if_lookup_by_index(ifindex, vrf_id);
167 if (ifp == NULL) {
c97b34cf 168 if (bsglobal.debugging)
d62a17ae 169 zlog_debug(
8b3fd12c 170 "%s: Can't find interface by ifindex: %d ",
171 __func__, ifindex);
d62a17ae 172 return NULL;
173 }
174 }
175
176 /* Fetch destination address. */
9c39d197 177 STREAM_GETC(s, dp->family);
d62a17ae 178
179 plen = prefix_blen(dp);
9c39d197
RZ
180 STREAM_GET(&dp->u.prefix, s, plen);
181 STREAM_GETC(s, dp->prefixlen);
d62a17ae 182
183 /* Get BFD status. */
9c39d197 184 STREAM_GETL(s, (*status));
d62a17ae 185
9c39d197 186 STREAM_GETC(s, sp->family);
37a74717
DS
187
188 plen = prefix_blen(sp);
9c39d197
RZ
189 STREAM_GET(&sp->u.prefix, s, plen);
190 STREAM_GETC(s, sp->prefixlen);
d62a17ae 191
9c39d197 192 STREAM_GETC(s, local_remote_cbit);
9beff0bd
PG
193 if (remote_cbit)
194 *remote_cbit = local_remote_cbit;
d62a17ae 195 return ifp;
9c39d197
RZ
196
197stream_failure:
10f02a2a 198 /*
199 * Clean dp and sp because caller
200 * will immediately check them valid or not
201 */
202 memset(dp, 0, sizeof(*dp));
203 memset(sp, 0, sizeof(*sp));
9c39d197 204 return NULL;
7f342629 205}
68fe91d6 206
207/*
208 * bfd_get_status_str - Convert BFD status to a display string.
209 */
d62a17ae 210const char *bfd_get_status_str(int status)
68fe91d6 211{
d62a17ae 212 switch (status) {
213 case BFD_STATUS_DOWN:
214 return "Down";
215 case BFD_STATUS_UP:
216 return "Up";
7555dc61
S
217 case BFD_STATUS_ADMIN_DOWN:
218 return "Admin Down";
d62a17ae 219 case BFD_STATUS_UNKNOWN:
220 default:
221 return "Unknown";
222 }
68fe91d6 223}
224
225/*
226 * bfd_last_update - Calculate the last BFD update time and convert it
227 * into a dd:hh:mm:ss display format.
228 */
d62a17ae 229static void bfd_last_update(time_t last_update, char *buf, size_t len)
68fe91d6 230{
d62a17ae 231 time_t curr;
232 time_t diff;
a2700b50 233 struct tm tm;
d62a17ae 234 struct timeval tv;
235
214d8a60 236 /* If no BFD status update has ever been received, print `never'. */
d62a17ae 237 if (last_update == 0) {
238 snprintf(buf, len, "never");
239 return;
240 }
241
242 /* Get current time. */
243 monotime(&tv);
244 curr = tv.tv_sec;
245 diff = curr - last_update;
a2700b50 246 gmtime_r(&diff, &tm);
d62a17ae 247
a2700b50
MS
248 snprintf(buf, len, "%d:%02d:%02d:%02d", tm.tm_yday, tm.tm_hour,
249 tm.tm_min, tm.tm_sec);
68fe91d6 250}
251
055c4dfc 252/*
253 * bfd_client_sendmsg - Format and send a client register
254 * command to Zebra to be forwarded to BFD
255 */
0945d5ed
PG
256void bfd_client_sendmsg(struct zclient *zclient, int command,
257 vrf_id_t vrf_id)
055c4dfc 258{
d62a17ae 259 struct stream *s;
8a3f8f2e 260 enum zclient_send_status ret;
d62a17ae 261
262 /* Check socket. */
263 if (!zclient || zclient->sock < 0) {
c97b34cf 264 if (bsglobal.debugging)
d62a17ae 265 zlog_debug(
3efd0893 266 "%s: Can't send BFD client register, Zebra client not established",
15569c58 267 __func__);
d62a17ae 268 return;
269 }
270
271 s = zclient->obuf;
272 stream_reset(s);
0945d5ed 273 zclient_create_header(s, command, vrf_id);
d62a17ae 274
275 stream_putl(s, getpid());
276
277 stream_putw_at(s, 0, stream_get_endp(s));
278
279 ret = zclient_send_message(zclient);
280
8a3f8f2e 281 if (ret == ZCLIENT_SEND_FAILURE) {
c97b34cf 282 if (bsglobal.debugging)
d62a17ae 283 zlog_debug(
8b3fd12c 284 "%s: %ld: zclient_send_message() failed",
285 __func__, (long)getpid());
d62a17ae 286 return;
287 }
288
289 return;
055c4dfc 290}
18322efd
RZ
291
292int zclient_bfd_command(struct zclient *zc, struct bfd_session_arg *args)
293{
294 struct stream *s;
295 size_t addrlen;
296
bb99eb5d 297 /* Individual reg/dereg messages are suppressed during shutdown. */
c97b34cf
IR
298 if (bsglobal.shutting_down) {
299 if (bsglobal.debugging)
bb99eb5d
G
300 zlog_debug(
301 "%s: Suppressing BFD peer reg/dereg messages",
302 __func__);
303 return -1;
304 }
305
18322efd
RZ
306 /* Check socket. */
307 if (!zc || zc->sock < 0) {
c97b34cf 308 if (bsglobal.debugging)
18322efd
RZ
309 zlog_debug("%s: zclient unavailable", __func__);
310 return -1;
311 }
312
313 s = zc->obuf;
314 stream_reset(s);
315
316 /* Create new message. */
317 zclient_create_header(s, args->command, args->vrf_id);
318 stream_putl(s, getpid());
319
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);
325
4b983eef
RZ
326 /*
327 * For more BFD integration protocol details, see function
328 * `_ptm_msg_read` in `bfdd/ptm_adapter.c`.
329 */
330#if HAVE_BFDD > 0
331 /* Session timers. */
332 stream_putl(s, args->min_rx);
333 stream_putl(s, args->min_tx);
334 stream_putc(s, args->detection_multiplier);
335
336 /* Is multi hop? */
337 stream_putc(s, args->mhop != 0);
338
339 /* Source address. */
340 stream_putw(s, args->family);
341 stream_put(s, &args->src, addrlen);
342
03ff0db1 343 /* Send the expected hops. */
344 stream_putc(s, args->hops);
4b983eef
RZ
345
346 /* Send interface name if any. */
b6c87c35
IR
347 if (args->mhop) {
348 /* Don't send interface. */
349 stream_putc(s, 0);
350 if (bsglobal.debugging && args->ifnamelen)
351 zlog_debug("%s: multi hop is configured, not sending interface",
352 __func__);
353 } else {
354 stream_putc(s, args->ifnamelen);
355 if (args->ifnamelen)
356 stream_put(s, args->ifname, args->ifnamelen);
357 }
4b983eef
RZ
358
359 /* Send the C bit indicator. */
360 stream_putc(s, args->cbit);
361
362 /* Send profile name if any. */
363 stream_putc(s, args->profilelen);
364 if (args->profilelen)
365 stream_put(s, args->profile, args->profilelen);
366#else /* PTM BFD */
18322efd
RZ
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);
372 }
373
374 if (args->mhop) {
375 /* Multi hop indicator. */
376 stream_putc(s, 1);
377
378 /* Multi hop always sends the source address. */
379 stream_putw(s, args->family);
380 stream_put(s, &args->src, addrlen);
381
03ff0db1 382 /* Send the expected hops. */
383 stream_putc(s, args->hops);
18322efd
RZ
384 } else {
385 /* Multi hop indicator. */
386 stream_putc(s, 0);
387
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);
392 }
393
394 /* Send interface name if any. */
395 stream_putc(s, args->ifnamelen);
396 if (args->ifnamelen)
397 stream_put(s, args->ifname, args->ifnamelen);
398 }
4cc6d2ce
DS
399
400 /* Send the C bit indicator. */
401 stream_putc(s, args->cbit);
18322efd
RZ
402#endif /* HAVE_BFDD */
403
404 /* Finish the message by writing the size. */
405 stream_putw_at(s, 0, stream_get_endp(s));
406
407 /* Send message to zebra. */
8a3f8f2e 408 if (zclient_send_message(zc) == ZCLIENT_SEND_FAILURE) {
c97b34cf 409 if (bsglobal.debugging)
18322efd
RZ
410 zlog_debug("%s: zclient_send_message failed", __func__);
411 return -1;
412 }
413
18322efd
RZ
414 return 0;
415}
a099abe5 416
a099abe5
RZ
417struct bfd_session_params *bfd_sess_new(bsp_status_update updatecb, void *arg)
418{
419 struct bfd_session_params *bsp;
420
421 bsp = XCALLOC(MTYPE_BFD_INFO, sizeof(*bsp));
422
423 /* Save application data. */
424 bsp->updatecb = updatecb;
425 bsp->arg = arg;
426
427 /* Set defaults. */
428 bsp->args.detection_multiplier = BFD_DEF_DETECT_MULT;
03ff0db1 429 bsp->args.hops = 1;
a099abe5
RZ
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;
433
434 /* Register in global list. */
435 TAILQ_INSERT_TAIL(&bsglobal.bsplist, bsp, entry);
436
437 return bsp;
438}
439
440static bool _bfd_sess_valid(const struct bfd_session_params *bsp)
441{
442 /* Peer/local address not configured. */
443 if (bsp->args.family == 0)
444 return false;
445
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__,
450 bsp->args.family);
451 return false;
452 }
453
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",
459 __func__,
460 (struct in_addr *)&bsp->args.dst);
461 else
462 zlog_debug("%s: invalid address: %pI6",
463 __func__, &bsp->args.dst);
464 }
465 return false;
466 }
467
468 /* Multi hop requires local address. */
469 if (bsp->args.mhop
470 && memcmp(&i6a_zero, &bsp->args.src, sizeof(i6a_zero)) == 0) {
471 if (bsglobal.debugging)
472 zlog_debug(
473 "%s: multi hop but no local address provided",
474 __func__);
475 return false;
476 }
477
478 /* Check VRF ID. */
479 if (bsp->args.vrf_id == VRF_UNKNOWN) {
480 if (bsglobal.debugging)
481 zlog_debug("%s: asked for unknown VRF", __func__);
482 return false;
483 }
484
485 return true;
486}
487
e6685141 488static void _bfd_sess_send(struct event *t)
a099abe5 489{
e16d030c 490 struct bfd_session_params *bsp = EVENT_ARG(t);
a099abe5
RZ
491 int rv;
492
493 /* Validate configuration before trying to send bogus data. */
494 if (!_bfd_sess_valid(bsp))
cc9f21da 495 return;
a099abe5
RZ
496
497 if (bsp->lastev == BSE_INSTALL) {
498 bsp->args.command = bsp->installed ? ZEBRA_BFD_DEST_UPDATE
499 : ZEBRA_BFD_DEST_REGISTER;
500 } else
501 bsp->args.command = ZEBRA_BFD_DEST_DEREGISTER;
502
503 /* If not installed and asked for uninstall, do nothing. */
504 if (!bsp->installed && bsp->args.command == ZEBRA_BFD_DEST_DEREGISTER)
cc9f21da 505 return;
a099abe5
RZ
506
507 rv = zclient_bfd_command(bsglobal.zc, &bsp->args);
508 /* Command was sent successfully. */
509 if (rv == 0) {
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;
e82acdce
IR
515 } else {
516 struct ipaddr src, dst;
517
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;
522
523 zlog_err(
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"
529 : "uninstalled");
a099abe5 530 }
a099abe5
RZ
531}
532
533static void _bfd_sess_remove(struct bfd_session_params *bsp)
534{
f83431c7 535 /* Cancel any pending installation request. */
e16d030c 536 EVENT_OFF(bsp->installev);
f83431c7 537
a099abe5
RZ
538 /* Not installed, nothing to do. */
539 if (!bsp->installed)
540 return;
541
a099abe5
RZ
542 /* Send request to remove any session. */
543 bsp->lastev = BSE_UNINSTALL;
8c1186d3 544 event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
a099abe5
RZ
545}
546
547void bfd_sess_free(struct bfd_session_params **bsp)
548{
549 if (*bsp == NULL)
550 return;
551
552 /* Remove any installed session. */
553 _bfd_sess_remove(*bsp);
554
555 /* Remove from global list. */
556 TAILQ_REMOVE(&bsglobal.bsplist, (*bsp), entry);
557
b7ca809d
RZ
558 bfd_source_cache_put(*bsp);
559
a099abe5
RZ
560 /* Free the memory and point to NULL. */
561 XFREE(MTYPE_BFD_INFO, (*bsp));
562}
563
ac506cb2
RZ
564static bool bfd_sess_address_changed(const struct bfd_session_params *bsp,
565 uint32_t family,
566 const struct in6_addr *src,
567 const struct in6_addr *dst)
568{
569 size_t addrlen;
570
571 if (bsp->args.family != family)
572 return true;
573
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))
579 return true;
580
581 return false;
582}
583
a099abe5 584void bfd_sess_set_ipv4_addrs(struct bfd_session_params *bsp,
ca30ac7f
RZ
585 const struct in_addr *src,
586 const struct in_addr *dst)
a099abe5 587{
ac506cb2
RZ
588 if (!bfd_sess_address_changed(bsp, AF_INET, (struct in6_addr *)src,
589 (struct in6_addr *)dst))
590 return;
591
a099abe5
RZ
592 /* If already installed, remove the old setting. */
593 _bfd_sess_remove(bsp);
b7ca809d
RZ
594 /* Address changed so we must reapply auto source. */
595 bfd_source_cache_put(bsp);
a099abe5
RZ
596
597 bsp->args.family = AF_INET;
598
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));
602
603 /* Copy the equivalent of IPv4 to arguments structure. */
604 if (src)
605 memcpy(&bsp->args.src, src, sizeof(struct in_addr));
606
607 assert(dst);
608 memcpy(&bsp->args.dst, dst, sizeof(struct in_addr));
b7ca809d
RZ
609
610 if (bsp->auto_source)
611 bfd_source_cache_get(bsp);
a099abe5
RZ
612}
613
614void bfd_sess_set_ipv6_addrs(struct bfd_session_params *bsp,
ca30ac7f
RZ
615 const struct in6_addr *src,
616 const struct in6_addr *dst)
a099abe5 617{
ae094573 618 if (!bfd_sess_address_changed(bsp, AF_INET6, src, dst))
ac506cb2
RZ
619 return;
620
a099abe5
RZ
621 /* If already installed, remove the old setting. */
622 _bfd_sess_remove(bsp);
b7ca809d
RZ
623 /* Address changed so we must reapply auto source. */
624 bfd_source_cache_put(bsp);
a099abe5
RZ
625
626 bsp->args.family = AF_INET6;
627
628 /* Clean memory, set zero value and avoid static analyser warnings. */
629 memset(&bsp->args.src, 0, sizeof(bsp->args.src));
630
631 if (src)
632 bsp->args.src = *src;
633
634 assert(dst);
635 bsp->args.dst = *dst;
b7ca809d
RZ
636
637 if (bsp->auto_source)
638 bfd_source_cache_get(bsp);
a099abe5
RZ
639}
640
641void bfd_sess_set_interface(struct bfd_session_params *bsp, const char *ifname)
642{
ac506cb2
RZ
643 if ((ifname == NULL && bsp->args.ifnamelen == 0)
644 || (ifname && strcmp(bsp->args.ifname, ifname) == 0))
645 return;
646
a099abe5
RZ
647 /* If already installed, remove the old setting. */
648 _bfd_sess_remove(bsp);
649
650 if (ifname == NULL) {
651 bsp->args.ifname[0] = 0;
652 bsp->args.ifnamelen = 0;
653 return;
654 }
655
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);
659
660 bsp->args.ifnamelen = strlen(bsp->args.ifname);
661}
662
663void bfd_sess_set_profile(struct bfd_session_params *bsp, const char *profile)
664{
665 if (profile == NULL) {
666 bsp->args.profile[0] = 0;
667 bsp->args.profilelen = 0;
668 return;
669 }
670
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);
674
675 bsp->args.profilelen = strlen(bsp->args.profile);
676}
677
678void bfd_sess_set_vrf(struct bfd_session_params *bsp, vrf_id_t vrf_id)
679{
ac506cb2
RZ
680 if (bsp->args.vrf_id == vrf_id)
681 return;
682
a099abe5
RZ
683 /* If already installed, remove the old setting. */
684 _bfd_sess_remove(bsp);
b7ca809d
RZ
685 /* Address changed so we must reapply auto source. */
686 bfd_source_cache_put(bsp);
a099abe5
RZ
687
688 bsp->args.vrf_id = vrf_id;
b7ca809d
RZ
689
690 if (bsp->auto_source)
691 bfd_source_cache_get(bsp);
a099abe5
RZ
692}
693
92f85e65 694void bfd_sess_set_hop_count(struct bfd_session_params *bsp, uint8_t hops)
a099abe5 695{
03ff0db1 696 if (bsp->args.hops == hops)
ac506cb2
RZ
697 return;
698
a099abe5
RZ
699 /* If already installed, remove the old setting. */
700 _bfd_sess_remove(bsp);
701
03ff0db1 702 bsp->args.hops = hops;
92f85e65 703 bsp->args.mhop = (hops > 1);
a099abe5
RZ
704}
705
706
707void bfd_sess_set_cbit(struct bfd_session_params *bsp, bool enable)
708{
709 bsp->args.cbit = enable;
710}
711
712void bfd_sess_set_timers(struct bfd_session_params *bsp,
713 uint8_t detection_multiplier, uint32_t min_rx,
714 uint32_t min_tx)
715{
716 bsp->args.detection_multiplier = detection_multiplier;
717 bsp->args.min_rx = min_rx;
718 bsp->args.min_tx = min_tx;
719}
720
b7ca809d
RZ
721void bfd_sess_set_auto_source(struct bfd_session_params *bsp, bool enable)
722{
723 if (bsp->auto_source == enable)
724 return;
725
726 bsp->auto_source = enable;
727 if (enable)
728 bfd_source_cache_get(bsp);
729 else
730 bfd_source_cache_put(bsp);
731}
732
a099abe5
RZ
733void bfd_sess_install(struct bfd_session_params *bsp)
734{
a099abe5 735 bsp->lastev = BSE_INSTALL;
907a2395 736 event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
a099abe5
RZ
737}
738
739void bfd_sess_uninstall(struct bfd_session_params *bsp)
740{
741 bsp->lastev = BSE_UNINSTALL;
907a2395 742 event_add_event(bsglobal.tm, _bfd_sess_send, bsp, 0, &bsp->installev);
a099abe5
RZ
743}
744
745enum bfd_session_state bfd_sess_status(const struct bfd_session_params *bsp)
746{
747 return bsp->bss.state;
748}
749
a099abe5
RZ
750uint8_t bfd_sess_hop_count(const struct bfd_session_params *bsp)
751{
03ff0db1 752 return bsp->args.hops;
a099abe5
RZ
753}
754
755const char *bfd_sess_profile(const struct bfd_session_params *bsp)
756{
757 return bsp->args.profilelen ? bsp->args.profile : NULL;
758}
759
760void bfd_sess_addresses(const struct bfd_session_params *bsp, int *family,
761 struct in6_addr *src, struct in6_addr *dst)
762{
763 *family = bsp->args.family;
764 if (src)
765 *src = bsp->args.src;
766 if (dst)
767 *dst = bsp->args.dst;
768}
769
770const char *bfd_sess_interface(const struct bfd_session_params *bsp)
771{
772 if (bsp->args.ifnamelen)
773 return bsp->args.ifname;
774
775 return NULL;
776}
777
778const char *bfd_sess_vrf(const struct bfd_session_params *bsp)
779{
780 return vrf_id_to_name(bsp->args.vrf_id);
781}
782
783vrf_id_t bfd_sess_vrf_id(const struct bfd_session_params *bsp)
784{
785 return bsp->args.vrf_id;
786}
787
788bool bfd_sess_cbit(const struct bfd_session_params *bsp)
789{
790 return bsp->args.cbit;
791}
792
793void bfd_sess_timers(const struct bfd_session_params *bsp,
794 uint8_t *detection_multiplier, uint32_t *min_rx,
795 uint32_t *min_tx)
796{
797 *detection_multiplier = bsp->args.detection_multiplier;
798 *min_rx = bsp->args.min_rx;
799 *min_tx = bsp->args.min_tx;
800}
801
b7ca809d
RZ
802bool bfd_sess_auto_source(const struct bfd_session_params *bsp)
803{
804 return bsp->auto_source;
805}
806
a099abe5
RZ
807void bfd_sess_show(struct vty *vty, struct json_object *json,
808 struct bfd_session_params *bsp)
809{
810 json_object *json_bfd = NULL;
811 char time_buf[64];
812
83c44422
IR
813 if (!bsp)
814 return;
815
a099abe5
RZ
816 /* Show type. */
817 if (json) {
818 json_bfd = json_object_new_object();
819 if (bsp->args.mhop)
820 json_object_string_add(json_bfd, "type", "multi hop");
821 else
822 json_object_string_add(json_bfd, "type", "single hop");
823 } else
824 vty_out(vty, " BFD: Type: %s\n",
825 bsp->args.mhop ? "multi hop" : "single hop");
826
827 /* Show configuration. */
828 if (json) {
829 json_object_int_add(json_bfd, "detectMultiplier",
830 bsp->args.detection_multiplier);
831 json_object_int_add(json_bfd, "rxMinInterval",
832 bsp->args.min_rx);
833 json_object_int_add(json_bfd, "txMinInterval",
834 bsp->args.min_tx);
835 } else {
836 vty_out(vty,
837 " Detect Multiplier: %d, Min Rx interval: %d, Min Tx interval: %d\n",
838 bsp->args.detection_multiplier, bsp->args.min_rx,
839 bsp->args.min_tx);
840 }
841
842 bfd_last_update(bsp->bss.last_event, time_buf, sizeof(time_buf));
843 if (json) {
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);
847 } else
848 vty_out(vty, " Status: %s, Last update: %s\n",
849 bfd_get_status_str(bsp->bss.state), time_buf);
850
851 if (json)
852 json_object_object_add(json, "peerBfdInfo", json_bfd);
853 else
854 vty_out(vty, "\n");
855}
856
857/*
858 * Zebra communication related.
859 */
860
861/**
862 * Callback for reinstallation of all registered BFD sessions.
863 *
864 * Use this as `zclient` `bfd_dest_replay` callback.
865 */
3e51a84a 866int zclient_bfd_session_replay(ZAPI_CALLBACK_ARGS)
a099abe5
RZ
867{
868 struct bfd_session_params *bsp;
869
a243d1db
DL
870 if (!zclient->bfd_integration)
871 return 0;
872
a099abe5
RZ
873 /* Do nothing when shutting down. */
874 if (bsglobal.shutting_down)
875 return 0;
876
877 if (bsglobal.debugging)
878 zlog_debug("%s: sending all sessions registered", __func__);
879
880 /* Send the client registration */
881 bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, vrf_id);
882
883 /* Replay all activated peers. */
884 TAILQ_FOREACH (bsp, &bsglobal.bsplist, entry) {
e82acdce
IR
885 /* Skip not installed sessions. */
886 if (!bsp->installed)
a099abe5
RZ
887 continue;
888
889 /* We are reconnecting, so we must send installation. */
890 bsp->installed = false;
891
892 /* Cancel any pending installation request. */
e16d030c 893 EVENT_OFF(bsp->installev);
a099abe5
RZ
894
895 /* Ask for installation. */
896 bsp->lastev = BSE_INSTALL;
8c1186d3 897 event_execute(bsglobal.tm, _bfd_sess_send, bsp, 0);
a099abe5
RZ
898 }
899
900 return 0;
901}
902
a243d1db 903int zclient_bfd_session_update(ZAPI_CALLBACK_ARGS)
a099abe5 904{
464e6541 905 struct bfd_session_params *bsp, *bspn;
a099abe5
RZ
906 size_t sessions_updated = 0;
907 struct interface *ifp;
908 int remote_cbit = false;
909 int state = BFD_STATUS_UNKNOWN;
3e6376b8 910 time_t now;
a099abe5
RZ
911 size_t addrlen;
912 struct prefix dp;
913 struct prefix sp;
914 char ifstr[128], cbitstr[32];
915
a243d1db
DL
916 if (!zclient->bfd_integration)
917 return 0;
918
a099abe5
RZ
919 /* Do nothing when shutting down. */
920 if (bsglobal.shutting_down)
921 return 0;
922
923 ifp = bfd_get_peer_info(zclient->ibuf, &dp, &sp, &state, &remote_cbit,
924 vrf_id);
9c39d197
RZ
925 /*
926 * When interface lookup fails or an invalid stream is read, we must
927 * not proceed otherwise it will trigger an assertion while checking
928 * family type below.
929 */
930 if (dp.family == 0 || sp.family == 0)
931 return 0;
a099abe5
RZ
932
933 if (bsglobal.debugging) {
934 ifstr[0] = 0;
935 if (ifp)
936 snprintf(ifstr, sizeof(ifstr), " (interface %s)",
937 ifp->name);
938
939 snprintf(cbitstr, sizeof(cbitstr), " (CPI bit %s)",
940 remote_cbit ? "yes" : "no");
941
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));
945 }
946
947 switch (dp.family) {
948 case AF_INET:
949 addrlen = sizeof(struct in_addr);
950 break;
951 case AF_INET6:
952 addrlen = sizeof(struct in6_addr);
953 break;
954
955 default:
956 /* Unexpected value. */
957 assert(0);
958 break;
959 }
960
3e6376b8
RZ
961 /* Cache current time to avoid multiple monotime clock calls. */
962 now = monotime(NULL);
963
a099abe5 964 /* Notify all matching sessions about update. */
464e6541 965 TAILQ_FOREACH_SAFE (bsp, &bsglobal.bsplist, entry, bspn) {
e82acdce
IR
966 /* Skip not installed entries. */
967 if (!bsp->installed)
a099abe5
RZ
968 continue;
969 /* Skip different VRFs. */
970 if (bsp->args.vrf_id != vrf_id)
971 continue;
972 /* Skip different families. */
973 if (bsp->args.family != dp.family)
974 continue;
975 /* Skip different interface. */
976 if (bsp->args.ifnamelen && ifp
977 && strcmp(bsp->args.ifname, ifp->name) != 0)
978 continue;
979 /* Skip non matching destination addresses. */
980 if (memcmp(&bsp->args.dst, &dp.u, addrlen) != 0)
981 continue;
982 /*
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.
987 */
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)
991 continue;
992 /* No session state change. */
993 if ((int)bsp->bss.state == state)
994 continue;
995
3e6376b8 996 bsp->bss.last_event = now;
a099abe5
RZ
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);
1001 sessions_updated++;
1002 }
1003
1004 if (bsglobal.debugging)
1005 zlog_debug("%s: sessions updated: %zu", __func__,
1006 sessions_updated);
1007
1008 return 0;
1009}
1010
4e35b32e
RZ
1011/**
1012 * Frees all allocated resources and stops any activity.
1013 *
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.
1017 *
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`.
1020 */
1021static int bfd_protocol_integration_finish(void)
1022{
1023 if (bsglobal.zc == NULL)
1024 return 0;
1025
1026 while (!TAILQ_EMPTY(&bsglobal.bsplist)) {
1027 struct bfd_session_params *session =
1028 TAILQ_FIRST(&bsglobal.bsplist);
1029 bfd_sess_free(&session);
1030 }
1031
1032 /*
1033 * BFD source cache is linked to sessions, if all sessions are gone
1034 * then the source cache must be empty.
1035 */
1036 if (!SLIST_EMPTY(&bsglobal.source_list))
1037 zlog_warn("BFD integration source cache not empty");
1038
4e35b32e
RZ
1039 return 0;
1040}
1041
cd9d0537 1042void bfd_protocol_integration_init(struct zclient *zc, struct event_loop *tm)
a099abe5
RZ
1043{
1044 /* Initialize data structure. */
1045 TAILQ_INIT(&bsglobal.bsplist);
b7ca809d 1046 SLIST_INIT(&bsglobal.source_list);
a099abe5
RZ
1047
1048 /* Copy pointers. */
1049 bsglobal.zc = zc;
1050 bsglobal.tm = tm;
1051
a243d1db
DL
1052 /* Enable BFD callbacks. */
1053 zc->bfd_integration = true;
a099abe5
RZ
1054
1055 /* Send the client registration */
1056 bfd_client_sendmsg(zc, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
b7ca809d 1057
4e35b32e 1058 hook_register(frr_fini, bfd_protocol_integration_finish);
a099abe5
RZ
1059}
1060
1061void bfd_protocol_integration_set_debug(bool enable)
1062{
1063 bsglobal.debugging = enable;
1064}
1065
1066void bfd_protocol_integration_set_shutdown(bool enable)
1067{
1068 bsglobal.shutting_down = enable;
1069}
1070
1071bool bfd_protocol_integration_debug(void)
1072{
1073 return bsglobal.debugging;
1074}
1075
1076bool bfd_protocol_integration_shutting_down(void)
1077{
1078 return bsglobal.shutting_down;
1079}
b7ca809d
RZ
1080
1081/*
1082 * BFD automatic source selection
1083 *
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.
1086 *
1087 * When the interface address / routing table change we'll be notified
1088 * and be able to update the source address accordingly.
1089 *
1090 * <daemon> zebra
1091 * |
1092 * +-----------------+
1093 * | BFD session set |
1094 * | to auto source |
1095 * +-----------------+
1096 * |
1097 * \ +-----------------+
1098 * --------------> | Resolves |
1099 * | destination |
1100 * | address |
1101 * +-----------------+
1102 * |
1103 * +-----------------+ /
1104 * | Sets resolved | <----------
1105 * | source address |
1106 * +-----------------+
1107 */
1108static bool
1109bfd_source_cache_session_match(const struct bfd_source_cache *source,
1110 const struct bfd_session_params *session)
1111{
1112 const struct in_addr *address;
1113 const struct in6_addr *address_v6;
1114
1115 if (session->args.vrf_id != source->vrf_id)
1116 return false;
1117 if (session->args.family != source->address.family)
1118 return false;
1119
1120 switch (session->args.family) {
1121 case AF_INET:
1122 address = (const struct in_addr *)&session->args.dst;
1123 if (address->s_addr != source->address.u.prefix4.s_addr)
1124 return false;
1125 break;
1126 case AF_INET6:
1127 address_v6 = &session->args.dst;
1128 if (memcmp(address_v6, &source->address.u.prefix6,
1129 sizeof(struct in6_addr)))
1130 return false;
1131 break;
1132 default:
1133 return false;
1134 }
1135
1136 return true;
1137}
1138
1139static struct bfd_source_cache *
1140bfd_source_cache_find(vrf_id_t vrf_id, const struct prefix *prefix)
1141{
1142 struct bfd_source_cache *source;
1143
1144 SLIST_FOREACH (source, &bsglobal.source_list, entry) {
1145 if (source->vrf_id != vrf_id)
1146 continue;
1147 if (!prefix_same(&source->address, prefix))
1148 continue;
1149
1150 return source;
1151 }
1152
1153 return NULL;
1154}
1155
1156static void bfd_source_cache_get(struct bfd_session_params *session)
1157{
1158 struct bfd_source_cache *source;
1159 struct prefix target = {};
1160
1161 switch (session->args.family) {
1162 case AF_INET:
1163 target.family = AF_INET;
1164 target.prefixlen = IPV4_MAX_BITLEN;
1165 memcpy(&target.u.prefix4, &session->args.dst,
1166 sizeof(struct in_addr));
1167 break;
1168 case AF_INET6:
1169 target.family = AF_INET6;
1170 target.prefixlen = IPV6_MAX_BITLEN;
1171 memcpy(&target.u.prefix6, &session->args.dst,
1172 sizeof(struct in6_addr));
1173 break;
1174 default:
1175 return;
1176 }
1177
1178 source = bfd_source_cache_find(session->args.vrf_id, &target);
1179 if (source) {
1180 if (session->source_cache == source)
1181 return;
1182
1183 bfd_source_cache_put(session);
1184 session->source_cache = source;
1185 source->refcount++;
1186 return;
1187 }
1188
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);
1193
1194 bfd_source_cache_put(session);
1195 session->source_cache = source;
1196 source->refcount = 1;
1197
b7ca809d
RZ
1198 return;
1199}
1200
1201static void bfd_source_cache_put(struct bfd_session_params *session)
1202{
1203 if (session->source_cache == NULL)
1204 return;
1205
1206 session->source_cache->refcount--;
1207 if (session->source_cache->refcount > 0) {
1208 session->source_cache = NULL;
1209 return;
1210 }
1211
b7ca809d
RZ
1212 SLIST_REMOVE(&bsglobal.source_list, session->source_cache,
1213 bfd_source_cache, entry);
1214 XFREE(MTYPE_BFD_SOURCE, session->source_cache);
1215}
1216
1217/** Updates BFD running session if source address has changed. */
1218static void
1219bfd_source_cache_update_session(const struct bfd_source_cache *source,
1220 struct bfd_session_params *session)
1221{
1222 const struct in_addr *address;
1223 const struct in6_addr *address_v6;
1224
1225 switch (session->args.family) {
1226 case AF_INET:
1227 address = (const struct in_addr *)&session->args.src;
1228 if (memcmp(address, &source->source.u.prefix4,
1229 sizeof(struct in_addr)) == 0)
1230 return;
1231
1232 _bfd_sess_remove(session);
1233 memcpy(&session->args.src, &source->source.u.prefix4,
1234 sizeof(struct in_addr));
1235 break;
1236 case AF_INET6:
1237 address_v6 = &session->args.src;
1238 if (memcmp(address_v6, &source->source.u.prefix6,
1239 sizeof(struct in6_addr)) == 0)
1240 return;
1241
1242 _bfd_sess_remove(session);
1243 memcpy(&session->args.src, &source->source.u.prefix6,
1244 sizeof(struct in6_addr));
1245 break;
1246 default:
1247 return;
1248 }
1249
1250 bfd_sess_install(session);
1251}
1252
1253static void
1254bfd_source_cache_update_sessions(const struct bfd_source_cache *source)
1255{
1256 struct bfd_session_params *session;
1257
1258 if (!source->valid)
1259 return;
1260
1261 TAILQ_FOREACH (session, &bsglobal.bsplist, entry) {
1262 if (!session->auto_source)
1263 continue;
1264 if (!bfd_source_cache_session_match(source, session))
1265 continue;
1266
1267 bfd_source_cache_update_session(source, session);
1268 }
1269}
1270
1271/**
1272 * Try to translate next hop information into source address.
1273 *
1274 * \returns `true` if source changed otherwise `false`.
1275 */
1276static bool bfd_source_cache_update(struct bfd_source_cache *source,
1277 const struct zapi_route *route)
1278{
1279 size_t nh_index;
1280
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;
1286
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)",
1290 nh->ifindex);
1291 continue;
1292 }
1293
1294 for (ALL_LIST_ELEMENTS_RO(interface->connected, node,
1295 connected)) {
1296 if (source->address.family !=
1297 connected->address->family)
1298 continue;
1299 if (prefix_same(connected->address, &source->source))
1300 return false;
1301 /*
1302 * Skip link-local as it is only useful for single hop
1303 * and in that case no source is specified usually.
1304 */
1305 if (source->address.family == AF_INET6 &&
1306 IN6_IS_ADDR_LINKLOCAL(
1307 &connected->address->u.prefix6))
1308 continue;
1309
1310 prefix_copy(&source->source, connected->address);
1311 source->valid = true;
1312 return true;
1313 }
1314 }
1315
1316 memset(&source->source, 0, sizeof(source->source));
1317 source->valid = false;
1318 return false;
1319}
1320
a77ea81e 1321int bfd_nht_update(const struct prefix *match, const struct zapi_route *route)
b7ca809d
RZ
1322{
1323 struct bfd_source_cache *source;
b7ca809d
RZ
1324
1325 if (bsglobal.debugging)
a77ea81e 1326 zlog_debug("BFD NHT update for %pFX", &route->prefix);
b7ca809d
RZ
1327
1328 SLIST_FOREACH (source, &bsglobal.source_list, entry) {
a77ea81e 1329 if (source->vrf_id != route->vrf_id)
b7ca809d 1330 continue;
a77ea81e 1331 if (!prefix_same(match, &source->address))
b7ca809d 1332 continue;
a77ea81e 1333 if (bfd_source_cache_update(source, route))
b7ca809d
RZ
1334 bfd_source_cache_update_sessions(source);
1335 }
1336
1337 return 0;
1338}