2 * BGP Conditional advertisement
3 * Copyright (C) 2020 Samsung R&D Institute India - Bangalore.
4 * Madhurilatha Kuruganti
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; either version 2 of the License, or (at your option)
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "bgpd/bgp_conditional_adv.h"
24 #include "bgpd/bgp_vty.h"
26 static route_map_result_t
27 bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table
*table
,
28 struct route_map
*rmap
)
30 struct attr dummy_attr
= {0};
31 struct bgp_dest
*dest
;
32 struct bgp_path_info
*pi
;
33 struct bgp_path_info path
= {0};
34 struct bgp_path_info_extra path_extra
= {0};
35 const struct prefix
*dest_p
;
36 route_map_result_t ret
= RMAP_DENYMATCH
;
38 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
39 dest_p
= bgp_dest_get_prefix(dest
);
42 for (pi
= bgp_dest_get_bgp_path_info(dest
); pi
; pi
= pi
->next
) {
43 dummy_attr
= *pi
->attr
;
45 /* Fill temp path_info */
46 prep_for_rmap_apply(&path
, &path_extra
, dest
, pi
,
47 pi
->peer
, &dummy_attr
);
49 RESET_FLAG(dummy_attr
.rmap_change_flags
);
51 ret
= route_map_apply(rmap
, dest_p
, &path
);
52 bgp_attr_flush(&dummy_attr
);
54 if (ret
== RMAP_PERMITMATCH
) {
55 bgp_dest_unlock_node(dest
);
57 "%s: Condition map routes present in BGP table",
65 bgp_cond_adv_debug("%s: Condition map routes not present in BGP table",
71 static void bgp_conditional_adv_routes(struct peer
*peer
, afi_t afi
,
72 safi_t safi
, struct bgp_table
*table
,
73 struct route_map
*rmap
,
74 enum update_type update_type
)
77 struct bgp_dest
*dest
;
78 struct bgp_path_info
*pi
;
79 struct bgp_path_info path
;
81 const struct prefix
*dest_p
;
82 struct update_subgroup
*subgrp
;
83 struct attr advmap_attr
= {0}, attr
= {0};
84 struct bgp_path_info_extra path_extra
= {0};
85 route_map_result_t ret
;
87 paf
= peer_af_find(peer
, afi
, safi
);
91 subgrp
= PAF_SUBGRP(paf
);
92 /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
97 SET_FLAG(subgrp
->sflags
, SUBGRP_STATUS_TABLE_REPARSING
);
99 bgp_cond_adv_debug("%s: %s routes to/from %s for %s", __func__
,
100 update_type
== UPDATE_TYPE_ADVERTISE
? "Advertise"
102 peer
->host
, get_afi_safi_str(afi
, safi
, false));
104 addpath_capable
= bgp_addpath_encode_tx(peer
, afi
, safi
);
106 for (dest
= bgp_table_top(table
); dest
; dest
= bgp_route_next(dest
)) {
107 dest_p
= bgp_dest_get_prefix(dest
);
110 for (pi
= bgp_dest_get_bgp_path_info(dest
); pi
; pi
= pi
->next
) {
111 advmap_attr
= *pi
->attr
;
113 /* Fill temp path_info */
114 prep_for_rmap_apply(&path
, &path_extra
, dest
, pi
,
115 pi
->peer
, &advmap_attr
);
117 RESET_FLAG(advmap_attr
.rmap_change_flags
);
119 ret
= route_map_apply(rmap
, dest_p
, &path
);
120 if (ret
!= RMAP_PERMITMATCH
||
121 !bgp_check_selected(pi
, peer
, addpath_capable
, afi
,
123 bgp_attr_flush(&advmap_attr
);
127 /* Skip route-map checks in
128 * subgroup_announce_check while executing from
129 * the conditional advertise scanner process.
130 * otherwise when route-map is also configured
131 * on same peer, routes in advertise-map may not
132 * be advertised as expected.
134 if (update_type
== UPDATE_TYPE_ADVERTISE
&&
135 subgroup_announce_check(dest
, pi
, subgrp
, dest_p
,
136 &attr
, &advmap_attr
)) {
137 bgp_adj_out_set_subgroup(dest
, subgrp
, &attr
,
140 /* If default originate is enabled for
141 * the peer, do not send explicit
142 * withdraw. This will prevent deletion
143 * of default route advertised through
146 if (CHECK_FLAG(peer
->af_flags
[afi
][safi
],
147 PEER_FLAG_DEFAULT_ORIGINATE
) &&
148 is_default_prefix(dest_p
))
151 bgp_adj_out_unset_subgroup(
153 bgp_addpath_id_for_peer(
157 bgp_attr_flush(&advmap_attr
);
160 UNSET_FLAG(subgrp
->sflags
, SUBGRP_STATUS_TABLE_REPARSING
);
163 /* Handler of conditional advertisement timer event.
164 * Each route in the condition-map is evaluated.
166 static void bgp_conditional_adv_timer(struct thread
*t
)
171 struct bgp
*bgp
= NULL
;
172 struct peer
*peer
= NULL
;
173 struct peer_af
*paf
= NULL
;
174 struct bgp_table
*table
= NULL
;
175 struct bgp_filter
*filter
= NULL
;
176 struct listnode
*node
, *nnode
= NULL
;
177 struct update_subgroup
*subgrp
= NULL
;
178 route_map_result_t ret
;
183 thread_add_timer(bm
->master
, bgp_conditional_adv_timer
, bgp
,
184 bgp
->condition_check_period
, &bgp
->t_condition_check
);
186 /* loop through each peer and advertise or withdraw routes if
187 * advertise-map is configured and prefix(es) in condition-map
188 * does exist(exist-map)/not exist(non-exist-map) in BGP table
189 * based on condition(exist-map or non-exist map)
191 for (ALL_LIST_ELEMENTS(bgp
->peer
, node
, nnode
, peer
)) {
192 if (!CHECK_FLAG(peer
->flags
, PEER_FLAG_CONFIG_NODE
))
195 if (!peer_established(peer
))
198 FOREACH_AFI_SAFI (afi
, safi
) {
199 if (!peer
->afc_nego
[afi
][safi
])
202 /* labeled-unicast routes are installed in the unicast
203 * table so in order to display the correct PfxRcd value
204 * we must look at SAFI_UNICAST
206 pfx_rcd_safi
= (safi
== SAFI_LABELED_UNICAST
)
210 table
= bgp
->rib
[afi
][pfx_rcd_safi
];
214 filter
= &peer
->filter
[afi
][safi
];
216 if (!filter
->advmap
.aname
|| !filter
->advmap
.cname
217 || !filter
->advmap
.amap
|| !filter
->advmap
.cmap
)
220 if (!peer
->advmap_config_change
[afi
][safi
]
221 && !peer
->advmap_table_change
)
224 if (BGP_DEBUG(cond_adv
, COND_ADV
)) {
225 if (peer
->advmap_table_change
)
227 "%s: %s - routes changed in BGP table.",
228 __func__
, peer
->host
);
229 if (peer
->advmap_config_change
[afi
][safi
])
231 "%s: %s for %s - advertise/condition map configuration is changed.",
232 __func__
, peer
->host
,
233 get_afi_safi_str(afi
, safi
,
237 /* cmap (route-map attached to exist-map or
238 * non-exist-map) map validation
240 ret
= bgp_check_rmap_prefixes_in_bgp_table(
241 table
, filter
->advmap
.cmap
);
243 /* Derive conditional advertisement status from
244 * condition and return value of condition-map
247 if (filter
->advmap
.condition
== CONDITION_EXIST
)
248 filter
->advmap
.update_type
=
249 (ret
== RMAP_PERMITMATCH
)
250 ? UPDATE_TYPE_ADVERTISE
251 : UPDATE_TYPE_WITHDRAW
;
253 filter
->advmap
.update_type
=
254 (ret
== RMAP_PERMITMATCH
)
255 ? UPDATE_TYPE_WITHDRAW
256 : UPDATE_TYPE_ADVERTISE
;
259 * Update condadv update type so
260 * subgroup_announce_check() can properly apply
261 * outbound policy according to advertisement state
263 paf
= peer_af_find(peer
, afi
, safi
);
264 if (paf
&& (SUBGRP_PEER(PAF_SUBGRP(paf
))
266 .advmap
.update_type
!=
267 filter
->advmap
.update_type
)) {
268 /* Handle change to peer advmap */
270 "%s: advmap.update_type changed for peer %s, adjusting update_group.",
271 __func__
, peer
->host
);
273 update_group_adjust_peer(paf
);
276 /* Send regular update as per the existing policy.
277 * There is a change in route-map, match-rule, ACLs,
278 * or route-map filter configuration on the same peer.
280 if (peer
->advmap_config_change
[afi
][safi
]) {
283 "%s: Configuration is changed on peer %s for %s, send the normal update first.",
284 __func__
, peer
->host
,
285 get_afi_safi_str(afi
, safi
, false));
287 update_subgroup_split_peer(paf
, NULL
);
288 subgrp
= paf
->subgroup
;
290 if (subgrp
&& subgrp
->update_group
)
291 subgroup_announce_table(
292 paf
->subgroup
, NULL
);
294 peer
->advmap_config_change
[afi
][safi
] = false;
297 /* Send update as per the conditional advertisement */
298 bgp_conditional_adv_routes(peer
, afi
, safi
, table
,
300 filter
->advmap
.update_type
);
302 peer
->advmap_table_change
= false;
306 void bgp_conditional_adv_enable(struct peer
*peer
, afi_t afi
, safi_t safi
)
308 struct bgp
*bgp
= peer
->bgp
;
312 /* This flag is used to monitor conditional routes status in BGP table,
313 * and advertise/withdraw routes only when there is a change in BGP
314 * table w.r.t conditional routes
316 peer
->advmap_config_change
[afi
][safi
] = true;
318 /* advertise-map is already configured on at least one of its
319 * neighbors (AFI/SAFI). So just increment the counter.
321 if (++bgp
->condition_filter_count
> 1) {
322 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__
,
323 bgp
->condition_filter_count
);
328 /* Register for conditional routes polling timer */
329 if (!thread_is_scheduled(bgp
->t_condition_check
))
330 thread_add_timer(bm
->master
, bgp_conditional_adv_timer
, bgp
, 0,
331 &bgp
->t_condition_check
);
334 void bgp_conditional_adv_disable(struct peer
*peer
, afi_t afi
, safi_t safi
)
336 struct bgp
*bgp
= peer
->bgp
;
340 /* advertise-map is not configured on any of its neighbors or
341 * it is configured on more than one neighbor(AFI/SAFI).
342 * So there's nothing to do except decrementing the counter.
344 if (--bgp
->condition_filter_count
!= 0) {
345 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__
,
346 bgp
->condition_filter_count
);
351 /* Last filter removed. So cancel conditional routes polling thread. */
352 THREAD_OFF(bgp
->t_condition_check
);
355 static void peer_advertise_map_filter_update(struct peer
*peer
, afi_t afi
,
356 safi_t safi
, const char *amap_name
,
357 struct route_map
*amap
,
358 const char *cmap_name
,
359 struct route_map
*cmap
,
360 bool condition
, bool set
)
362 struct bgp_filter
*filter
;
363 bool filter_exists
= false;
365 filter
= &peer
->filter
[afi
][safi
];
367 /* advertise-map is already configured. */
368 if (filter
->advmap
.aname
) {
369 filter_exists
= true;
370 XFREE(MTYPE_BGP_FILTER_NAME
, filter
->advmap
.aname
);
371 XFREE(MTYPE_BGP_FILTER_NAME
, filter
->advmap
.cname
);
374 route_map_counter_decrement(filter
->advmap
.amap
);
376 /* Removed advertise-map configuration */
378 memset(&filter
->advmap
, 0, sizeof(filter
->advmap
));
380 /* decrement condition_filter_count delete timer if
381 * this is the last advertise-map to be removed.
384 bgp_conditional_adv_disable(peer
, afi
, safi
);
386 /* Process peer route updates. */
387 peer_on_policy_change(peer
, afi
, safi
, 1);
392 /* Update filter data with newly configured values. */
393 filter
->advmap
.aname
= XSTRDUP(MTYPE_BGP_FILTER_NAME
, amap_name
);
394 filter
->advmap
.cname
= XSTRDUP(MTYPE_BGP_FILTER_NAME
, cmap_name
);
395 filter
->advmap
.amap
= amap
;
396 filter
->advmap
.cmap
= cmap
;
397 filter
->advmap
.condition
= condition
;
398 route_map_counter_increment(filter
->advmap
.amap
);
399 peer
->advmap_config_change
[afi
][safi
] = true;
401 /* Increment condition_filter_count and/or create timer. */
402 if (!filter_exists
) {
403 filter
->advmap
.update_type
= UPDATE_TYPE_ADVERTISE
;
404 bgp_conditional_adv_enable(peer
, afi
, safi
);
407 /* Process peer route updates. */
408 peer_on_policy_change(peer
, afi
, safi
, 1);
411 /* Set advertise-map to the peer. */
412 int peer_advertise_map_set(struct peer
*peer
, afi_t afi
, safi_t safi
,
413 const char *advertise_name
,
414 struct route_map
*advertise_map
,
415 const char *condition_name
,
416 struct route_map
*condition_map
, bool condition
)
419 struct listnode
*node
, *nnode
;
421 /* Set configuration on peer. */
422 peer_advertise_map_filter_update(peer
, afi
, safi
, advertise_name
,
423 advertise_map
, condition_name
,
424 condition_map
, condition
, true);
426 /* Check if handling a regular peer & Skip peer-group mechanics. */
427 if (!CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)) {
428 /* Set override-flag and process peer route updates. */
429 SET_FLAG(peer
->filter_override
[afi
][safi
][RMAP_OUT
],
430 PEER_FT_ADVERTISE_MAP
);
435 * Set configuration on all peer-group members, unless they are
436 * explicitly overriding peer-group configuration.
438 for (ALL_LIST_ELEMENTS(peer
->group
->peer
, node
, nnode
, member
)) {
439 /* Skip peers with overridden configuration. */
440 if (CHECK_FLAG(member
->filter_override
[afi
][safi
][RMAP_OUT
],
441 PEER_FT_ADVERTISE_MAP
))
444 /* Set configuration on peer-group member. */
445 peer_advertise_map_filter_update(
446 member
, afi
, safi
, advertise_name
, advertise_map
,
447 condition_name
, condition_map
, condition
, true);
453 /* Unset advertise-map from the peer. */
454 int peer_advertise_map_unset(struct peer
*peer
, afi_t afi
, safi_t safi
,
455 const char *advertise_name
,
456 struct route_map
*advertise_map
,
457 const char *condition_name
,
458 struct route_map
*condition_map
, bool condition
)
461 struct listnode
*node
, *nnode
;
463 /* advertise-map is not configured */
464 if (!peer
->filter
[afi
][safi
].advmap
.aname
)
467 /* Unset override-flag unconditionally. */
468 UNSET_FLAG(peer
->filter_override
[afi
][safi
][RMAP_OUT
],
469 PEER_FT_ADVERTISE_MAP
);
471 /* Inherit configuration from peer-group if peer is member. */
472 if (peer_group_active(peer
)) {
473 PEER_STR_ATTR_INHERIT(peer
, peer
->group
,
474 filter
[afi
][safi
].advmap
.aname
,
475 MTYPE_BGP_FILTER_NAME
);
476 PEER_ATTR_INHERIT(peer
, peer
->group
,
477 filter
[afi
][safi
].advmap
.amap
);
479 peer_advertise_map_filter_update(
480 peer
, afi
, safi
, advertise_name
, advertise_map
,
481 condition_name
, condition_map
, condition
, false);
483 /* Check if handling a regular peer and skip peer-group mechanics. */
484 if (!CHECK_FLAG(peer
->sflags
, PEER_STATUS_GROUP
)) {
485 /* Process peer route updates. */
486 bgp_cond_adv_debug("%s: Send normal update to %s for %s",
487 __func__
, peer
->host
,
488 get_afi_safi_str(afi
, safi
, false));
494 * Remove configuration on all peer-group members, unless they are
495 * explicitly overriding peer-group configuration.
497 for (ALL_LIST_ELEMENTS(peer
->group
->peer
, node
, nnode
, member
)) {
498 /* Skip peers with overridden configuration. */
499 if (CHECK_FLAG(member
->filter_override
[afi
][safi
][RMAP_OUT
],
500 PEER_FT_ADVERTISE_MAP
))
502 /* Remove configuration on peer-group member. */
503 peer_advertise_map_filter_update(
504 member
, afi
, safi
, advertise_name
, advertise_map
,
505 condition_name
, condition_map
, condition
, false);
507 /* Process peer route updates. */
508 bgp_cond_adv_debug("%s: Send normal update to %s for %s ",
509 __func__
, member
->host
,
510 get_afi_safi_str(afi
, safi
, false));