Coverage Report

Created: 2025-12-05 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/bgpd/rfapi/rfapi_ap.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 *
4
 * Copyright 2009-2016, LabN Consulting, L.L.C.
5
 *
6
 */
7
8
#include "lib/zebra.h"
9
#include "lib/prefix.h"
10
#include "lib/agg_table.h"
11
#include "lib/vty.h"
12
#include "lib/memory.h"
13
#include "lib/routemap.h"
14
#include "lib/log.h"
15
#include "lib/linklist.h"
16
#include "lib/command.h"
17
#include "lib/stream.h"
18
19
#include "bgpd/bgpd.h"
20
#include "bgpd/bgp_ecommunity.h"
21
#include "bgpd/bgp_attr.h"
22
23
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
24
#include "bgpd/rfapi/rfapi.h"
25
#include "bgpd/rfapi/rfapi_backend.h"
26
27
#include "bgpd/bgp_route.h"
28
#include "bgpd/bgp_mplsvpn.h"
29
#include "bgpd/bgp_aspath.h"
30
#include "bgpd/bgp_advertise.h"
31
32
#include "bgpd/rfapi/rfapi_import.h"
33
#include "bgpd/rfapi/rfapi_private.h"
34
#include "bgpd/rfapi/rfapi_monitor.h"
35
#include "bgpd/rfapi/rfapi_vty.h"
36
#include "bgpd/rfapi/vnc_export_bgp.h"
37
#include "bgpd/rfapi/vnc_export_bgp_p.h"
38
#include "bgpd/rfapi/vnc_zebra.h"
39
#include "bgpd/rfapi/vnc_import_bgp.h"
40
#include "bgpd/rfapi/rfapi_rib.h"
41
42
#include "bgpd/rfapi/rfapi_ap.h"
43
#include "bgpd/rfapi/vnc_debug.h"
44
45
/*
46
 * Per-NVE Advertised prefixes
47
 *
48
 * We maintain a list of prefixes advertised by each NVE.
49
 * There are two indices: by prefix and by lifetime.
50
 *
51
 * BY-PREFIX skiplist
52
 *
53
 *  key:  ptr to struct prefix (when storing, point to prefix that
54
 *    is part of rfapi_adb).
55
 *
56
 *  value:  ptr to struct rfapi_adb
57
 *
58
 * BY-LIFETIME skiplist
59
 *
60
 *  key:  ptr to struct rfapi_adb
61
 *  value:  ptr to struct rfapi_adb
62
 *
63
 */
64
65
/*
66
 * Skiplist sort function that sorts first according to lifetime
67
 * and then according to adb pointer value. The adb pointer
68
 * is used to spread out the sort for adbs with the same lifetime
69
 * and thereby make the skip list operations more efficient.
70
 */
71
static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2)
72
0
{
73
0
  const struct rfapi_adb *a1 = adb1;
74
0
  const struct rfapi_adb *a2 = adb2;
75
76
0
  if (a1->lifetime < a2->lifetime)
77
0
    return -1;
78
0
  if (a1->lifetime > a2->lifetime)
79
0
    return 1;
80
81
0
  if (a1 < a2)
82
0
    return -1;
83
0
  if (a1 > a2)
84
0
    return 1;
85
86
0
  return 0;
87
0
}
88
89
void rfapiApInit(struct rfapi_advertised_prefixes *ap)
90
0
{
91
0
  ap->ipN_by_prefix = skiplist_new(0, rfapi_rib_key_cmp, NULL);
92
0
  ap->ip0_by_ether = skiplist_new(0, rfapi_rib_key_cmp, NULL);
93
0
  ap->by_lifetime = skiplist_new(0, sl_adb_lifetime_cmp, NULL);
94
0
}
95
96
void rfapiApRelease(struct rfapi_advertised_prefixes *ap)
97
0
{
98
0
  struct rfapi_adb *adb;
99
100
  /* Free ADBs and lifetime items */
101
0
  while (0 == skiplist_first(ap->by_lifetime, NULL, (void **)&adb)) {
102
0
    rfapiAdbFree(adb);
103
0
    skiplist_delete_first(ap->by_lifetime);
104
0
  }
105
106
0
  while (0 == skiplist_delete_first(ap->ipN_by_prefix))
107
0
    ;
108
0
  while (0 == skiplist_delete_first(ap->ip0_by_ether))
109
0
    ;
110
111
  /* Free lists */
112
0
  skiplist_free(ap->ipN_by_prefix);
113
0
  skiplist_free(ap->ip0_by_ether);
114
0
  skiplist_free(ap->by_lifetime);
115
116
0
  ap->ipN_by_prefix = NULL;
117
0
  ap->ip0_by_ether = NULL;
118
0
  ap->by_lifetime = NULL;
119
0
}
120
121
int rfapiApCount(struct rfapi_descriptor *rfd)
122
0
{
123
0
  if (!rfd->advertised.by_lifetime)
124
0
    return 0;
125
126
0
  return skiplist_count(rfd->advertised.by_lifetime);
127
0
}
128
129
int rfapiApCountAll(struct bgp *bgp)
130
0
{
131
0
  struct rfapi *h;
132
0
  struct listnode *node;
133
0
  struct rfapi_descriptor *rfd;
134
0
  int total = 0;
135
136
0
  h = bgp->rfapi;
137
0
  if (h) {
138
0
    for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
139
0
      total += rfapiApCount(rfd);
140
0
    }
141
0
  }
142
0
  return total;
143
0
}
144
145
146
void rfapiApReadvertiseAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
147
0
{
148
0
  struct rfapi_adb *adb;
149
0
  void *cursor = NULL;
150
0
  int rc;
151
152
0
  for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
153
0
        (void **)&adb, &cursor);
154
0
       rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
155
0
           (void **)&adb, &cursor)) {
156
157
0
    struct prefix_rd prd;
158
0
    uint32_t local_pref = rfp_cost_to_localpref(adb->cost);
159
160
0
    prd = rfd->rd;
161
0
    prd.family = AF_UNSPEC;
162
0
    prd.prefixlen = 64;
163
164
    /*
165
     * TBD this is not quite right. When pfx_ip is 0/32 or 0/128,
166
     * we need to substitute the VN address as the prefix
167
     */
168
0
    add_vnc_route(rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip,
169
0
            &prd,   /* RD to use (0 for ENCAP) */
170
0
            &rfd->vn_addr, /* nexthop */
171
0
            &local_pref, &adb->lifetime, NULL,
172
0
            NULL, /* struct rfapi_un_option */
173
0
            NULL, /* struct rfapi_vn_option */
174
0
            rfd->rt_export_list, NULL, /* med */
175
0
            NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
176
0
  }
177
0
}
178
179
void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
180
0
{
181
0
  struct rfapi_adb *adb;
182
0
  void *cursor;
183
0
  int rc;
184
185
186
0
  cursor = NULL;
187
0
  for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
188
0
        (void **)&adb, &cursor);
189
0
       rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
190
0
           (void **)&adb, &cursor)) {
191
192
0
    struct prefix pfx_vn_buf;
193
0
    struct prefix *pfx_ip;
194
195
0
    if (!(RFAPI_0_PREFIX(&adb->u.s.prefix_ip)
196
0
          && RFAPI_HOST_PREFIX(&adb->u.s.prefix_ip))) {
197
198
0
      pfx_ip = &adb->u.s.prefix_ip;
199
200
0
    } else {
201
202
0
      pfx_ip = NULL;
203
204
      /*
205
       * 0/32 or 0/128 => mac advertisement
206
       */
207
0
      if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) {
208
        /*
209
         * Bad: it means we can't delete the route
210
         */
211
0
        vnc_zlog_debug_verbose(
212
0
          "%s: BAD: handle has bad vn_addr: skipping",
213
0
          __func__);
214
0
        continue;
215
0
      }
216
0
    }
217
218
0
    del_vnc_route(rfd, rfd->peer, bgp, SAFI_MPLS_VPN,
219
0
            pfx_ip ? pfx_ip : &pfx_vn_buf,
220
0
            &adb->u.s.prd, /* RD to use (0 for ENCAP) */
221
0
            ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0);
222
0
  }
223
0
}
224
225
/*
226
 * returns nonzero if tunnel readvertisement is needed, 0 otherwise
227
 */
228
static int rfapiApAdjustLifetimeStats(
229
  struct rfapi_descriptor *rfd,
230
  uint32_t *old_lifetime, /* set if removing/replacing */
231
  uint32_t *new_lifetime) /* set if replacing/adding */
232
0
{
233
0
  int advertise = 0;
234
0
  int find_max = 0;
235
0
  int find_min = 0;
236
237
0
  vnc_zlog_debug_verbose("%s: rfd=%p, pOldLife=%p, pNewLife=%p", __func__,
238
0
             rfd, old_lifetime, new_lifetime);
239
0
  if (old_lifetime)
240
0
    vnc_zlog_debug_verbose("%s: OldLife=%d", __func__,
241
0
               *old_lifetime);
242
0
  if (new_lifetime)
243
0
    vnc_zlog_debug_verbose("%s: NewLife=%d", __func__,
244
0
               *new_lifetime);
245
246
0
  if (new_lifetime) {
247
    /*
248
     * Adding new lifetime
249
     */
250
0
    if (old_lifetime) {
251
      /*
252
       * replacing existing lifetime
253
       */
254
255
256
      /* old and new are same */
257
0
      if (*old_lifetime == *new_lifetime)
258
0
        return 0;
259
260
0
      if (*old_lifetime == rfd->min_prefix_lifetime) {
261
0
        find_min = 1;
262
0
      }
263
0
      if (*old_lifetime == rfd->max_prefix_lifetime) {
264
0
        find_max = 1;
265
0
      }
266
267
      /* no need to search if new value is at or equals
268
       * min|max  */
269
0
      if (*new_lifetime <= rfd->min_prefix_lifetime) {
270
0
        rfd->min_prefix_lifetime = *new_lifetime;
271
0
        find_min = 0;
272
0
      }
273
0
      if (*new_lifetime >= rfd->max_prefix_lifetime) {
274
0
        rfd->max_prefix_lifetime = *new_lifetime;
275
0
        advertise = 1;
276
0
        find_max = 0;
277
0
      }
278
279
0
    } else {
280
      /*
281
       * Just adding new lifetime
282
       */
283
0
      if (*new_lifetime < rfd->min_prefix_lifetime) {
284
0
        rfd->min_prefix_lifetime = *new_lifetime;
285
0
      }
286
0
      if (*new_lifetime > rfd->max_prefix_lifetime) {
287
0
        advertise = 1;
288
0
        rfd->max_prefix_lifetime = *new_lifetime;
289
0
      }
290
0
    }
291
0
  } else {
292
    /*
293
     * Deleting
294
     */
295
296
    /*
297
     * See if the max prefix lifetime for this NVE has decreased.
298
     * The easy optimization: track min & max; walk the table only
299
     * if they are different.
300
     * The general optimization: index the advertised_prefixes
301
     * table by lifetime.
302
     *
303
     * Note: for a given nve_descriptor, only one of the
304
     * advertised_prefixes[] tables will be used: viz., the
305
     * address family that matches the VN address.
306
     *
307
     */
308
0
    if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) {
309
310
      /*
311
       * Common case: all lifetimes are the same. Only
312
       * thing we need to do here is check if there are
313
       * no exported routes left. In that case, reinitialize
314
       * the max and min values.
315
       */
316
0
      if (!rfapiApCount(rfd)) {
317
0
        rfd->max_prefix_lifetime = 0;
318
0
        rfd->min_prefix_lifetime = UINT32_MAX;
319
0
      }
320
321
322
0
    } else {
323
0
      if (old_lifetime) {
324
0
        if (*old_lifetime == rfd->min_prefix_lifetime) {
325
0
          find_min = 1;
326
0
        }
327
0
        if (*old_lifetime == rfd->max_prefix_lifetime) {
328
0
          find_max = 1;
329
0
        }
330
0
      }
331
0
    }
332
0
  }
333
334
0
  if (find_min || find_max) {
335
0
    uint32_t min = UINT32_MAX;
336
0
    uint32_t max = 0;
337
338
0
    struct rfapi_adb *adb_min;
339
0
    struct rfapi_adb *adb_max;
340
341
0
    if (!skiplist_first(rfd->advertised.by_lifetime,
342
0
            (void **)&adb_min, NULL)
343
0
        && !skiplist_last(rfd->advertised.by_lifetime,
344
0
              (void **)&adb_max, NULL)) {
345
346
      /*
347
       * This should always work
348
       */
349
0
      min = adb_min->lifetime;
350
0
      max = adb_max->lifetime;
351
352
0
    } else {
353
354
0
      void *cursor;
355
0
      struct rfapi_rib_key rk;
356
0
      struct rfapi_adb *adb;
357
0
      int rc;
358
359
0
      vnc_zlog_debug_verbose(
360
0
        "%s: walking to find new min/max", __func__);
361
362
0
      cursor = NULL;
363
0
      for (rc = skiplist_next(rfd->advertised.ipN_by_prefix,
364
0
            (void **)&rk, (void **)&adb,
365
0
            &cursor);
366
0
           !rc;
367
0
           rc = skiplist_next(rfd->advertised.ipN_by_prefix,
368
0
            (void **)&rk, (void **)&adb,
369
0
            &cursor)) {
370
371
0
        uint32_t lt = adb->lifetime;
372
373
0
        if (lt > max)
374
0
          max = lt;
375
0
        if (lt < min)
376
0
          min = lt;
377
0
      }
378
0
      cursor = NULL;
379
0
      for (rc = skiplist_next(rfd->advertised.ip0_by_ether,
380
0
            (void **)&rk, (void **)&adb,
381
0
            &cursor);
382
0
           !rc;
383
0
           rc = skiplist_next(rfd->advertised.ip0_by_ether,
384
0
            (void **)&rk, (void **)&adb,
385
0
            &cursor)) {
386
387
0
        uint32_t lt = adb->lifetime;
388
389
0
        if (lt > max)
390
0
          max = lt;
391
0
        if (lt < min)
392
0
          min = lt;
393
0
      }
394
0
    }
395
396
    /*
397
     * trigger tunnel route update
398
     * but only if we found a VPN route and it had
399
     * a lifetime greater than 0
400
     */
401
0
    if (max && rfd->max_prefix_lifetime != max)
402
0
      advertise = 1;
403
0
    rfd->max_prefix_lifetime = max;
404
0
    rfd->min_prefix_lifetime = min;
405
0
  }
406
407
0
  vnc_zlog_debug_verbose("%s: returning advertise=%d, min=%d, max=%d",
408
0
             __func__, advertise, rfd->min_prefix_lifetime,
409
0
             rfd->max_prefix_lifetime);
410
411
0
  return (advertise != 0);
412
0
}
413
414
/*
415
 * Return Value
416
 *
417
 *  0 No need to advertise tunnel route
418
 *  non-0 advertise tunnel route
419
 */
420
int rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd,
421
         struct prefix *pfx_ip, struct prefix *pfx_eth,
422
         struct prefix_rd *prd, uint32_t lifetime, uint8_t cost,
423
         struct rfapi_l2address_option *l2o) /* other options TBD */
424
0
{
425
0
  int rc;
426
0
  struct rfapi_adb *adb;
427
0
  uint32_t old_lifetime = 0;
428
0
  int use_ip0 = 0;
429
0
  struct rfapi_rib_key rk;
430
431
0
  rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
432
0
  if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
433
0
    use_ip0 = 1;
434
0
    assert(pfx_eth);
435
0
    rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
436
0
             (void **)&adb);
437
438
0
  } else {
439
440
    /* find prefix in advertised prefixes list */
441
0
    rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
442
0
             (void **)&adb);
443
0
  }
444
445
446
0
  if (rc) {
447
    /* Not found */
448
0
    adb = XCALLOC(MTYPE_RFAPI_ADB, sizeof(struct rfapi_adb));
449
0
    adb->lifetime = lifetime;
450
0
    adb->u.key = rk;
451
452
0
    if (use_ip0) {
453
0
      assert(pfx_eth);
454
0
      skiplist_insert(rfd->advertised.ip0_by_ether,
455
0
          &adb->u.key, adb);
456
0
    } else {
457
0
      skiplist_insert(rfd->advertised.ipN_by_prefix,
458
0
          &adb->u.key, adb);
459
0
    }
460
461
0
    skiplist_insert(rfd->advertised.by_lifetime, adb, adb);
462
0
  } else {
463
0
    old_lifetime = adb->lifetime;
464
0
    if (old_lifetime != lifetime) {
465
0
      assert(!skiplist_delete(rfd->advertised.by_lifetime,
466
0
            adb, NULL));
467
0
      adb->lifetime = lifetime;
468
0
      assert(!skiplist_insert(rfd->advertised.by_lifetime,
469
0
            adb, adb));
470
0
    }
471
0
  }
472
0
  adb->cost = cost;
473
0
  if (l2o)
474
0
    adb->l2o = *l2o;
475
0
  else
476
0
    memset(&adb->l2o, 0, sizeof(struct rfapi_l2address_option));
477
478
0
  if (rfapiApAdjustLifetimeStats(rfd, (rc ? NULL : &old_lifetime),
479
0
               &lifetime))
480
0
    return 1;
481
482
0
  return 0;
483
0
}
484
485
/*
486
 * After this function returns successfully, caller should call
487
 * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce()
488
 */
489
int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd,
490
      struct prefix *pfx_ip, struct prefix *pfx_eth,
491
      struct prefix_rd *prd, int *advertise_tunnel) /* out */
492
0
{
493
0
  int rc;
494
0
  struct rfapi_adb *adb;
495
0
  uint32_t old_lifetime;
496
0
  int use_ip0 = 0;
497
0
  struct rfapi_rib_key rk;
498
499
0
  if (advertise_tunnel)
500
0
    *advertise_tunnel = 0;
501
502
0
  rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
503
  /* find prefix in advertised prefixes list */
504
0
  if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
505
0
    use_ip0 = 1;
506
0
    assert(pfx_eth);
507
508
0
    rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
509
0
             (void **)&adb);
510
511
0
  } else {
512
513
    /* find prefix in advertised prefixes list */
514
0
    rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
515
0
             (void **)&adb);
516
0
  }
517
518
0
  if (rc) {
519
0
    return ENOENT;
520
0
  }
521
522
0
  old_lifetime = adb->lifetime;
523
524
0
  if (use_ip0) {
525
0
    rc = skiplist_delete(rfd->advertised.ip0_by_ether, &rk, NULL);
526
0
  } else {
527
0
    rc = skiplist_delete(rfd->advertised.ipN_by_prefix, &rk, NULL);
528
0
  }
529
0
  assert(!rc);
530
531
0
  rc = skiplist_delete(rfd->advertised.by_lifetime, adb, NULL);
532
0
  assert(!rc);
533
534
0
  rfapiAdbFree(adb);
535
536
0
  if (rfapiApAdjustLifetimeStats(rfd, &old_lifetime, NULL)) {
537
0
    if (advertise_tunnel)
538
0
      *advertise_tunnel = 1;
539
0
  }
540
541
0
  return 0;
542
0
}