1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * BGP Conditional advertisement
4 * Copyright (C) 2020 Samsung R&D Institute India - Bangalore.
5 * Madhurilatha Kuruganti
10 #include "bgpd/bgp_conditional_adv.h"
11 #include "bgpd/bgp_vty.h"
13 static route_map_result_t
14 bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table
*table
,
15 struct route_map
*rmap
)
17 struct attr dummy_attr
= {0};
18 struct bgp_dest
*dest
;
19 struct bgp_path_info
*pi
;
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
;
25 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
26 dest_p
= bgp_dest_get_prefix(dest
);
29 for (pi
= bgp_dest_get_bgp_path_info(dest
); pi
; pi
= pi
->next
) {
30 dummy_attr
= *pi
->attr
;
32 /* Fill temp path_info */
33 prep_for_rmap_apply(&path
, &path_extra
, dest
, pi
,
34 pi
->peer
, &dummy_attr
);
36 RESET_FLAG(dummy_attr
.rmap_change_flags
);
38 ret
= route_map_apply(rmap
, dest_p
, &path
);
39 bgp_attr_flush(&dummy_attr
);
41 if (ret
== RMAP_PERMITMATCH
) {
42 bgp_dest_unlock_node(dest
);
44 "%s: Condition map routes present in BGP table",
52 bgp_cond_adv_debug("%s: Condition map routes not present in BGP table",
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
)
64 struct bgp_dest
*dest
;
65 struct bgp_path_info
*pi
;
66 struct bgp_path_info path
;
68 const struct prefix
*dest_p
;
69 struct update_subgroup
*subgrp
;
70 struct attr advmap_attr
= {0}, attr
= {0};
71 struct bgp_path_info_extra path_extra
= {0};
72 route_map_result_t ret
;
74 paf
= peer_af_find(peer
, afi
, safi
);
78 subgrp
= PAF_SUBGRP(paf
);
79 /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
84 SET_FLAG(subgrp
->sflags
, SUBGRP_STATUS_TABLE_REPARSING
);
86 bgp_cond_adv_debug("%s: %s routes to/from %s for %s", __func__
,
87 update_type
== UPDATE_TYPE_ADVERTISE
? "Advertise"
89 peer
->host
, get_afi_safi_str(afi
, safi
, false));
91 addpath_capable
= bgp_addpath_encode_tx(peer
, afi
, safi
);
93 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
94 dest_p
= bgp_dest_get_prefix(dest
);
97 for (pi
= bgp_dest_get_bgp_path_info(dest
); pi
; pi
= pi
->next
) {
98 advmap_attr
= *pi
->attr
;
100 /* Fill temp path_info */
101 prep_for_rmap_apply(&path
, &path_extra
, dest
, pi
,
102 pi
->peer
, &advmap_attr
);
104 RESET_FLAG(advmap_attr
.rmap_change_flags
);
106 ret
= route_map_apply(rmap
, dest_p
, &path
);
107 if (ret
!= RMAP_PERMITMATCH
||
108 !bgp_check_selected(pi
, peer
, addpath_capable
, afi
,
110 bgp_attr_flush(&advmap_attr
);
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.
121 if (update_type
== UPDATE_TYPE_ADVERTISE
&&
122 subgroup_announce_check(dest
, pi
, subgrp
, dest_p
,
123 &attr
, &advmap_attr
)) {
124 bgp_adj_out_set_subgroup(dest
, subgrp
, &attr
,
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
133 if (CHECK_FLAG(peer
->af_flags
[afi
][safi
],
134 PEER_FLAG_DEFAULT_ORIGINATE
) &&
135 is_default_prefix(dest_p
))
138 bgp_adj_out_unset_subgroup(
140 bgp_addpath_id_for_peer(
144 bgp_attr_flush(&advmap_attr
);
147 UNSET_FLAG(subgrp
->sflags
, SUBGRP_STATUS_TABLE_REPARSING
);
150 /* Handler of conditional advertisement timer event.
151 * Each route in the condition-map is evaluated.
153 static void bgp_conditional_adv_timer(struct thread
*t
)
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
;
165 route_map_result_t ret
;
166 bool advmap_table_changed
= false;
171 thread_add_timer(bm
->master
, bgp_conditional_adv_timer
, bgp
,
172 bgp
->condition_check_period
, &bgp
->t_condition_check
);
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.
181 for (ALL_LIST_ELEMENTS_RO(bgp
->peer
, node
, peer
)) {
182 if (peer
->advmap_table_change
) {
183 advmap_table_changed
= true;
188 /* loop through each peer and advertise or withdraw routes if
189 * advertise-map is configured and prefix(es) in condition-map
190 * does exist(exist-map)/not exist(non-exist-map) in BGP table
191 * based on condition(exist-map or non-exist map)
193 for (ALL_LIST_ELEMENTS(bgp
->peer
, node
, nnode
, peer
)) {
194 if (!CHECK_FLAG(peer
->flags
, PEER_FLAG_CONFIG_NODE
))
197 if (!peer_established(peer
))
200 FOREACH_AFI_SAFI (afi
, safi
) {
201 if (!peer
->afc_nego
[afi
][safi
])
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
208 pfx_rcd_safi
= (safi
== SAFI_LABELED_UNICAST
)
212 table
= bgp
->rib
[afi
][pfx_rcd_safi
];
216 filter
= &peer
->filter
[afi
][safi
];
218 if (!filter
->advmap
.aname
|| !filter
->advmap
.cname
219 || !filter
->advmap
.amap
|| !filter
->advmap
.cmap
)
222 if (!peer
->advmap_config_change
[afi
][safi
] &&
223 !advmap_table_changed
)
226 if (BGP_DEBUG(cond_adv
, COND_ADV
)) {
227 if (peer
->advmap_table_change
)
229 "%s: %s - routes changed in BGP table.",
230 __func__
, peer
->host
);
231 if (peer
->advmap_config_change
[afi
][safi
])
233 "%s: %s for %s - advertise/condition map configuration is changed.",
234 __func__
, peer
->host
,
235 get_afi_safi_str(afi
, safi
,
239 /* cmap (route-map attached to exist-map or
240 * non-exist-map) map validation
242 ret
= bgp_check_rmap_prefixes_in_bgp_table(
243 table
, filter
->advmap
.cmap
);
245 /* Derive conditional advertisement status from
246 * condition and return value of condition-map
249 if (filter
->advmap
.condition
== CONDITION_EXIST
)
250 filter
->advmap
.update_type
=
251 (ret
== RMAP_PERMITMATCH
)
252 ? UPDATE_TYPE_ADVERTISE
253 : UPDATE_TYPE_WITHDRAW
;
255 filter
->advmap
.update_type
=
256 (ret
== RMAP_PERMITMATCH
)
257 ? UPDATE_TYPE_WITHDRAW
258 : UPDATE_TYPE_ADVERTISE
;
261 * Update condadv update type so
262 * subgroup_announce_check() can properly apply
263 * outbound policy according to advertisement state
265 paf
= peer_af_find(peer
, afi
, safi
);
266 if (paf
&& (SUBGRP_PEER(PAF_SUBGRP(paf
))
268 .advmap
.update_type
!=
269 filter
->advmap
.update_type
)) {
270 /* Handle change to peer advmap */
272 "%s: advmap.update_type changed for peer %s, adjusting update_group.",
273 __func__
, peer
->host
);
275 update_group_adjust_peer(paf
);
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.
282 if (peer
->advmap_config_change
[afi
][safi
]) {
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));
289 update_subgroup_split_peer(paf
, NULL
);
290 subgrp
= paf
->subgroup
;
292 if (subgrp
&& subgrp
->update_group
)
293 subgroup_announce_table(
294 paf
->subgroup
, NULL
);
296 peer
->advmap_config_change
[afi
][safi
] = false;
299 /* Send update as per the conditional advertisement */
300 bgp_conditional_adv_routes(peer
, afi
, safi
, table
,
302 filter
->advmap
.update_type
);
304 peer
->advmap_table_change
= false;
308 void bgp_conditional_adv_enable(struct peer
*peer
, afi_t afi
, safi_t safi
)
310 struct bgp
*bgp
= peer
->bgp
;
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
318 peer
->advmap_config_change
[afi
][safi
] = true;
320 /* advertise-map is already configured on at least one of its
321 * neighbors (AFI/SAFI). So just increment the counter.
323 if (++bgp
->condition_filter_count
> 1) {
324 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__
,
325 bgp
->condition_filter_count
);
330 /* Register for conditional routes polling timer */
331 if (!thread_is_scheduled(bgp
->t_condition_check
))
332 thread_add_timer(bm
->master
, bgp_conditional_adv_timer
, bgp
, 0,
333 &bgp
->t_condition_check
);
336 void bgp_conditional_adv_disable(struct peer
*peer
, afi_t afi
, safi_t safi
)
338 struct bgp
*bgp
= peer
->bgp
;
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.
346 if (--bgp
->condition_filter_count
!= 0) {
347 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__
,
348 bgp
->condition_filter_count
);
353 /* Last filter removed. So cancel conditional routes polling thread. */
354 THREAD_OFF(bgp
->t_condition_check
);
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
)
364 struct bgp_filter
*filter
;
365 bool filter_exists
= false;
367 filter
= &peer
->filter
[afi
][safi
];
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
);
376 route_map_counter_decrement(filter
->advmap
.amap
);
378 /* Removed advertise-map configuration */
380 memset(&filter
->advmap
, 0, sizeof(filter
->advmap
));
382 /* decrement condition_filter_count delete timer if
383 * this is the last advertise-map to be removed.
386 bgp_conditional_adv_disable(peer
, afi
, safi
);
388 /* Process peer route updates. */
389 peer_on_policy_change(peer
, afi
, safi
, 1);
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;
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
);
409 /* Process peer route updates. */
410 peer_on_policy_change(peer
, afi
, safi
, 1);
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
)
421 struct listnode
*node
, *nnode
;
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);
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
);
437 * Set configuration on all peer-group members, unless they are
438 * explicitly overriding peer-group configuration.
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
))
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);
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
)
463 struct listnode
*node
, *nnode
;
465 /* advertise-map is not configured */
466 if (!peer
->filter
[afi
][safi
].advmap
.aname
)
469 /* Unset override-flag unconditionally. */
470 UNSET_FLAG(peer
->filter_override
[afi
][safi
][RMAP_OUT
],
471 PEER_FT_ADVERTISE_MAP
);
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
);
481 peer_advertise_map_filter_update(
482 peer
, afi
, safi
, advertise_name
, advertise_map
,
483 condition_name
, condition_map
, condition
, false);
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));
496 * Remove configuration on all peer-group members, unless they are
497 * explicitly overriding peer-group configuration.
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
))
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);
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));