Coverage Report

Created: 2025-10-08 06:07

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