Coverage Report

Created: 2025-10-23 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/bgpd/bgp_conditional_adv.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * BGP Conditional advertisement
4
 * Copyright (C) 2020  Samsung R&D Institute India - Bangalore.
5
 *      Madhurilatha Kuruganti
6
 */
7
8
#include <zebra.h>
9
10
#include "bgpd/bgp_conditional_adv.h"
11
#include "bgpd/bgp_vty.h"
12
13
static route_map_result_t
14
bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
15
             struct route_map *rmap)
16
0
{
17
0
  struct attr dummy_attr = {0};
18
0
  struct bgp_dest *dest;
19
0
  struct bgp_path_info *pi;
20
0
  struct bgp_path_info path = {0};
21
0
  struct bgp_path_info_extra path_extra = {0};
22
0
  const struct prefix *dest_p;
23
0
  route_map_result_t ret = RMAP_DENYMATCH;
24
0
25
0
  for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
26
0
    dest_p = bgp_dest_get_prefix(dest);
27
0
    assert(dest_p);
28
0
29
0
    for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
30
0
      dummy_attr = *pi->attr;
31
0
32
0
      /* Fill temp path_info */
33
0
      prep_for_rmap_apply(&path, &path_extra, dest, pi,
34
0
              pi->peer, &dummy_attr);
35
0
36
0
      RESET_FLAG(dummy_attr.rmap_change_flags);
37
0
38
0
      ret = route_map_apply(rmap, dest_p, &path);
39
0
      bgp_attr_flush(&dummy_attr);
40
0
41
0
      if (ret == RMAP_PERMITMATCH) {
42
0
        bgp_dest_unlock_node(dest);
43
0
        bgp_cond_adv_debug(
44
0
          "%s: Condition map routes present in BGP table",
45
0
          __func__);
46
0
47
0
        return ret;
48
0
      }
49
0
    }
50
0
  }
51
0
52
0
  bgp_cond_adv_debug("%s: Condition map routes not present in BGP table",
53
0
         __func__);
54
0
55
0
  return ret;
56
0
}
57
58
static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
59
               safi_t safi, struct bgp_table *table,
60
               struct route_map *rmap,
61
               enum update_type update_type)
62
0
{
63
0
  bool addpath_capable;
64
0
  struct bgp_dest *dest;
65
0
  struct bgp_path_info *pi;
66
0
  struct bgp_path_info path;
67
0
  struct peer_af *paf;
68
0
  const struct prefix *dest_p;
69
0
  struct update_subgroup *subgrp;
70
0
  struct attr advmap_attr = {0}, attr = {0};
71
0
  struct bgp_path_info_extra path_extra = {0};
72
0
  route_map_result_t ret;
73
0
74
0
  paf = peer_af_find(peer, afi, safi);
75
0
  if (!paf)
76
0
    return;
77
0
78
0
  subgrp = PAF_SUBGRP(paf);
79
0
  /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
80
0
  if (!subgrp)
81
0
    return;
82
0
83
0
  subgrp->pscount = 0;
84
0
  SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
85
0
86
0
  bgp_cond_adv_debug("%s: %s routes to/from %s for %s", __func__,
87
0
         update_type == UPDATE_TYPE_ADVERTISE ? "Advertise"
88
0
                : "Withdraw",
89
0
         peer->host, get_afi_safi_str(afi, safi, false));
90
0
91
0
  addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
92
0
93
0
  for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
94
0
    dest_p = bgp_dest_get_prefix(dest);
95
0
    assert(dest_p);
96
0
97
0
    for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
98
0
      advmap_attr = *pi->attr;
99
0
100
0
      /* Fill temp path_info */
101
0
      prep_for_rmap_apply(&path, &path_extra, dest, pi,
102
0
              pi->peer, &advmap_attr);
103
0
104
0
      RESET_FLAG(advmap_attr.rmap_change_flags);
105
0
106
0
      ret = route_map_apply(rmap, dest_p, &path);
107
0
      if (ret != RMAP_PERMITMATCH ||
108
0
          !bgp_check_selected(pi, peer, addpath_capable, afi,
109
0
            safi)) {
110
0
        bgp_attr_flush(&advmap_attr);
111
0
        continue;
112
0
      }
113
0
114
0
      /* Skip route-map checks in
115
0
       * subgroup_announce_check while executing from
116
0
       * the conditional advertise scanner process.
117
0
       * otherwise when route-map is also configured
118
0
       * on same peer, routes in advertise-map may not
119
0
       * be advertised as expected.
120
0
       */
121
0
      if (update_type == UPDATE_TYPE_ADVERTISE &&
122
0
          subgroup_announce_check(dest, pi, subgrp, dest_p,
123
0
                &attr, &advmap_attr)) {
124
0
        bgp_adj_out_set_subgroup(dest, subgrp, &attr,
125
0
               pi);
126
0
      } else {
127
0
        /* If default originate is enabled for
128
0
         * the peer, do not send explicit
129
0
         * withdraw. This will prevent deletion
130
0
         * of default route advertised through
131
0
         * default originate.
132
0
         */
133
0
        if (CHECK_FLAG(peer->af_flags[afi][safi],
134
0
                 PEER_FLAG_DEFAULT_ORIGINATE) &&
135
0
            is_default_prefix(dest_p))
136
0
          break;
137
0
138
0
        bgp_adj_out_unset_subgroup(
139
0
          dest, subgrp, 1,
140
0
          bgp_addpath_id_for_peer(
141
0
            peer, afi, safi,
142
0
            &pi->tx_addpath));
143
0
      }
144
0
      bgp_attr_flush(&advmap_attr);
145
0
    }
146
0
  }
147
0
  UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
148
0
}
149
150
/* Handler of conditional advertisement timer event.
151
 * Each route in the condition-map is evaluated.
152
 */
153
static void bgp_conditional_adv_timer(struct event *t)
154
0
{
155
0
  afi_t afi;
156
0
  safi_t safi;
157
0
  int pfx_rcd_safi;
158
0
  struct bgp *bgp = NULL;
159
0
  struct peer *peer = NULL;
160
0
  struct peer_af *paf = NULL;
161
0
  struct bgp_table *table = NULL;
162
0
  struct bgp_filter *filter = NULL;
163
0
  struct listnode *node, *nnode = NULL;
164
0
  struct update_subgroup *subgrp = NULL;
165
0
  route_map_result_t ret;
166
0
  bool advmap_table_changed = false;
167
0
168
0
  bgp = EVENT_ARG(t);
169
0
  assert(bgp);
170
0
171
0
  event_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
172
0
      bgp->condition_check_period, &bgp->t_condition_check);
173
0
174
0
  /* loop through each peer and check if we have peers with
175
0
   * advmap_table_change attribute set, to make sure we send
176
0
   * conditional advertisements properly below.
177
0
   * peer->advmap_table_change is added on incoming BGP UPDATES,
178
0
   * but here it's used for outgoing UPDATES, hence we need to
179
0
   * check if at least one peer got advmap_table_change.
180
0
   */
181
0
  for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
182
0
    if (peer->advmap_table_change) {
183
0
      advmap_table_changed = true;
184
0
      break;
185
0
    }
186
0
  }
187
0
188
0
  /* loop through each peer and advertise or withdraw routes if
189
0
   * advertise-map is configured and prefix(es) in condition-map
190
0
   * does exist(exist-map)/not exist(non-exist-map) in BGP table
191
0
   * based on condition(exist-map or non-exist map)
192
0
   */
193
0
  for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
194
0
    if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
195
0
      continue;
196
0
197
0
    if (!peer_established(peer))
198
0
      continue;
199
0
200
0
    FOREACH_AFI_SAFI (afi, safi) {
201
0
      if (!peer->afc_nego[afi][safi])
202
0
        continue;
203
0
204
0
      /* labeled-unicast routes are installed in the unicast
205
0
       * table so in order to display the correct PfxRcd value
206
0
       * we must look at SAFI_UNICAST
207
0
       */
208
0
      pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
209
0
                 ? SAFI_UNICAST
210
0
                 : safi;
211
0
212
0
      table = bgp->rib[afi][pfx_rcd_safi];
213
0
      if (!table)
214
0
        continue;
215
0
216
0
      filter = &peer->filter[afi][safi];
217
0
218
0
      if (!filter->advmap.aname || !filter->advmap.cname
219
0
          || !filter->advmap.amap || !filter->advmap.cmap)
220
0
        continue;
221
0
222
0
      if (!peer->advmap_config_change[afi][safi] &&
223
0
          !advmap_table_changed)
224
0
        continue;
225
0
226
0
      if (BGP_DEBUG(cond_adv, COND_ADV)) {
227
0
        if (peer->advmap_table_change)
228
0
          zlog_debug(
229
0
            "%s: %s - routes changed in BGP table.",
230
0
            __func__, peer->host);
231
0
        if (peer->advmap_config_change[afi][safi])
232
0
          zlog_debug(
233
0
            "%s: %s for %s - advertise/condition map configuration is changed.",
234
0
            __func__, peer->host,
235
0
            get_afi_safi_str(afi, safi,
236
0
                 false));
237
0
      }
238
0
239
0
      /* cmap (route-map attached to exist-map or
240
0
       * non-exist-map) map validation
241
0
       */
242
0
      ret = bgp_check_rmap_prefixes_in_bgp_table(
243
0
        table, filter->advmap.cmap);
244
0
245
0
      /* Derive conditional advertisement status from
246
0
       * condition and return value of condition-map
247
0
       * validation.
248
0
       */
249
0
      if (filter->advmap.condition == CONDITION_EXIST)
250
0
        filter->advmap.update_type =
251
0
          (ret == RMAP_PERMITMATCH)
252
0
            ? UPDATE_TYPE_ADVERTISE
253
0
            : UPDATE_TYPE_WITHDRAW;
254
0
      else
255
0
        filter->advmap.update_type =
256
0
          (ret == RMAP_PERMITMATCH)
257
0
            ? UPDATE_TYPE_WITHDRAW
258
0
            : UPDATE_TYPE_ADVERTISE;
259
0
260
0
      /*
261
0
       * Update condadv update type so
262
0
       * subgroup_announce_check() can properly apply
263
0
       * outbound policy according to advertisement state
264
0
       */
265
0
      paf = peer_af_find(peer, afi, safi);
266
0
      if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf))
267
0
              ->filter[afi][safi]
268
0
              .advmap.update_type !=
269
0
            filter->advmap.update_type)) {
270
0
        /* Handle change to peer advmap */
271
0
        bgp_cond_adv_debug(
272
0
          "%s: advmap.update_type changed for peer %s, adjusting update_group.",
273
0
          __func__, peer->host);
274
0
275
0
        update_group_adjust_peer(paf);
276
0
      }
277
0
278
0
      /* Send regular update as per the existing policy.
279
0
       * There is a change in route-map, match-rule, ACLs,
280
0
       * or route-map filter configuration on the same peer.
281
0
       */
282
0
      if (peer->advmap_config_change[afi][safi]) {
283
0
284
0
        bgp_cond_adv_debug(
285
0
          "%s: Configuration is changed on peer %s for %s, send the normal update first.",
286
0
          __func__, peer->host,
287
0
          get_afi_safi_str(afi, safi, false));
288
0
        if (paf) {
289
0
          update_subgroup_split_peer(paf, NULL);
290
0
          subgrp = paf->subgroup;
291
0
292
0
          if (subgrp && subgrp->update_group)
293
0
            subgroup_announce_table(
294
0
              paf->subgroup, NULL);
295
0
        }
296
0
        peer->advmap_config_change[afi][safi] = false;
297
0
      }
298
0
299
0
      /* Send update as per the conditional advertisement */
300
0
      bgp_conditional_adv_routes(peer, afi, safi, table,
301
0
               filter->advmap.amap,
302
0
               filter->advmap.update_type);
303
0
    }
304
0
    peer->advmap_table_change = false;
305
0
  }
306
0
}
307
308
void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
309
0
{
310
0
  struct bgp *bgp = peer->bgp;
311
312
0
  assert(bgp);
313
314
  /* This flag is used to monitor conditional routes status in BGP table,
315
   * and advertise/withdraw routes only when there is a change in BGP
316
   * table w.r.t conditional routes
317
   */
318
0
  peer->advmap_config_change[afi][safi] = true;
319
320
  /* advertise-map is already configured on at least one of its
321
   * neighbors (AFI/SAFI). So just increment the counter.
322
   */
323
0
  if (++bgp->condition_filter_count > 1) {
324
0
    bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
325
0
           bgp->condition_filter_count);
326
327
0
    return;
328
0
  }
329
330
  /* Register for conditional routes polling timer */
331
0
  if (!event_is_scheduled(bgp->t_condition_check))
332
0
    event_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0,
333
0
        &bgp->t_condition_check);
334
0
}
335
336
void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
337
0
{
338
0
  struct bgp *bgp = peer->bgp;
339
340
0
  assert(bgp);
341
342
  /* advertise-map is not configured on any of its neighbors or
343
   * it is configured on more than one neighbor(AFI/SAFI).
344
   * So there's nothing to do except decrementing the counter.
345
   */
346
0
  if (--bgp->condition_filter_count != 0) {
347
0
    bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
348
0
           bgp->condition_filter_count);
349
350
0
    return;
351
0
  }
352
353
  /* Last filter removed. So cancel conditional routes polling thread. */
354
0
  EVENT_OFF(bgp->t_condition_check);
355
0
}
356
357
static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi,
358
               safi_t safi, const char *amap_name,
359
               struct route_map *amap,
360
               const char *cmap_name,
361
               struct route_map *cmap,
362
               bool condition, bool set)
363
0
{
364
0
  struct bgp_filter *filter;
365
0
  bool filter_exists = false;
366
367
0
  filter = &peer->filter[afi][safi];
368
369
  /* advertise-map is already configured. */
370
0
  if (filter->advmap.aname) {
371
0
    filter_exists = true;
372
0
    XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
373
0
    XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
374
0
  }
375
376
0
  route_map_counter_decrement(filter->advmap.amap);
377
378
  /* Removed advertise-map configuration */
379
0
  if (!set) {
380
0
    memset(&filter->advmap, 0, sizeof(filter->advmap));
381
382
    /* decrement condition_filter_count delete timer if
383
     * this is the last advertise-map to be removed.
384
     */
385
0
    if (filter_exists)
386
0
      bgp_conditional_adv_disable(peer, afi, safi);
387
388
    /* Process peer route updates. */
389
0
    peer_on_policy_change(peer, afi, safi, 1);
390
391
0
    return;
392
0
  }
393
394
  /* Update filter data with newly configured values. */
395
0
  filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, amap_name);
396
0
  filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, cmap_name);
397
0
  filter->advmap.amap = amap;
398
0
  filter->advmap.cmap = cmap;
399
0
  filter->advmap.condition = condition;
400
0
  route_map_counter_increment(filter->advmap.amap);
401
0
  peer->advmap_config_change[afi][safi] = true;
402
403
  /* Increment condition_filter_count and/or create timer. */
404
0
  if (!filter_exists) {
405
0
    filter->advmap.update_type = UPDATE_TYPE_ADVERTISE;
406
0
    bgp_conditional_adv_enable(peer, afi, safi);
407
0
  }
408
409
  /* Process peer route updates. */
410
0
  peer_on_policy_change(peer, afi, safi, 1);
411
0
}
412
413
/* Set advertise-map to the peer. */
414
int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
415
         const char *advertise_name,
416
         struct route_map *advertise_map,
417
         const char *condition_name,
418
         struct route_map *condition_map, bool condition)
419
0
{
420
0
  struct peer *member;
421
0
  struct listnode *node, *nnode;
422
423
  /* Set configuration on peer. */
424
0
  peer_advertise_map_filter_update(peer, afi, safi, advertise_name,
425
0
           advertise_map, condition_name,
426
0
           condition_map, condition, true);
427
428
  /* Check if handling a regular peer & Skip peer-group mechanics. */
429
0
  if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
430
    /* Set override-flag and process peer route updates. */
431
0
    SET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
432
0
       PEER_FT_ADVERTISE_MAP);
433
0
    return 0;
434
0
  }
435
436
  /*
437
   * Set configuration on all peer-group members, unless they are
438
   * explicitly overriding peer-group configuration.
439
   */
440
0
  for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
441
    /* Skip peers with overridden configuration. */
442
0
    if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
443
0
             PEER_FT_ADVERTISE_MAP))
444
0
      continue;
445
446
    /* Set configuration on peer-group member. */
447
0
    peer_advertise_map_filter_update(
448
0
      member, afi, safi, advertise_name, advertise_map,
449
0
      condition_name, condition_map, condition, true);
450
0
  }
451
452
0
  return 0;
453
0
}
454
455
/* Unset advertise-map from the peer. */
456
int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
457
           const char *advertise_name,
458
           struct route_map *advertise_map,
459
           const char *condition_name,
460
           struct route_map *condition_map, bool condition)
461
0
{
462
0
  struct peer *member;
463
0
  struct listnode *node, *nnode;
464
465
  /* advertise-map is not configured */
466
0
  if (!peer->filter[afi][safi].advmap.aname)
467
0
    return 0;
468
469
  /* Unset override-flag unconditionally. */
470
0
  UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
471
0
       PEER_FT_ADVERTISE_MAP);
472
473
  /* Inherit configuration from peer-group if peer is member. */
474
0
  if (peer_group_active(peer)) {
475
0
    PEER_STR_ATTR_INHERIT(peer, peer->group,
476
0
              filter[afi][safi].advmap.aname,
477
0
              MTYPE_BGP_FILTER_NAME);
478
0
    PEER_ATTR_INHERIT(peer, peer->group,
479
0
          filter[afi][safi].advmap.amap);
480
0
  } else
481
0
    peer_advertise_map_filter_update(
482
0
      peer, afi, safi, advertise_name, advertise_map,
483
0
      condition_name, condition_map, condition, false);
484
485
  /* Check if handling a regular peer and skip peer-group mechanics. */
486
0
  if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
487
    /* Process peer route updates. */
488
0
    bgp_cond_adv_debug("%s: Send normal update to %s for %s",
489
0
           __func__, peer->host,
490
0
           get_afi_safi_str(afi, safi, false));
491
492
0
    return 0;
493
0
  }
494
495
  /*
496
   * Remove configuration on all peer-group members, unless they are
497
   * explicitly overriding peer-group configuration.
498
   */
499
0
  for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
500
    /* Skip peers with overridden configuration. */
501
0
    if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
502
0
             PEER_FT_ADVERTISE_MAP))
503
0
      continue;
504
    /* Remove configuration on peer-group member. */
505
0
    peer_advertise_map_filter_update(
506
0
      member, afi, safi, advertise_name, advertise_map,
507
0
      condition_name, condition_map, condition, false);
508
509
    /* Process peer route updates. */
510
0
    bgp_cond_adv_debug("%s: Send normal update to %s for %s ",
511
0
           __func__, member->host,
512
0
           get_afi_safi_str(afi, safi, false));
513
0
  }
514
515
0
  return 0;
516
0
}