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