From 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:53:30 +0200 Subject: Adding upstream version 8.4.4. Signed-off-by: Daniel Baumann --- bgpd/bgp_conditional_adv.c | 376 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 bgpd/bgp_conditional_adv.c (limited to 'bgpd/bgp_conditional_adv.c') diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c new file mode 100644 index 0000000..04a2cfc --- /dev/null +++ b/bgpd/bgp_conditional_adv.c @@ -0,0 +1,376 @@ +/* + * BGP Conditional advertisement + * Copyright (C) 2020 Samsung R&D Institute India - Bangalore. + * Madhurilatha Kuruganti + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "bgpd/bgp_conditional_adv.h" +#include "bgpd/bgp_vty.h" + +static route_map_result_t +bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table, + struct route_map *rmap) +{ + struct attr dummy_attr = {0}; + struct bgp_dest *dest; + struct bgp_path_info *pi; + struct bgp_path_info path = {0}; + struct bgp_path_info_extra path_extra = {0}; + const struct prefix *dest_p; + route_map_result_t ret = RMAP_DENYMATCH; + + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + dest_p = bgp_dest_get_prefix(dest); + assert(dest_p); + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + dummy_attr = *pi->attr; + + /* Fill temp path_info */ + prep_for_rmap_apply(&path, &path_extra, dest, pi, + pi->peer, &dummy_attr); + + RESET_FLAG(dummy_attr.rmap_change_flags); + + ret = route_map_apply(rmap, dest_p, &path); + bgp_attr_flush(&dummy_attr); + + if (ret == RMAP_PERMITMATCH) { + bgp_dest_unlock_node(dest); + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "%s: Condition map routes present in BGP table", + __func__); + + return ret; + } + } + } + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: Condition map routes not present in BGP table", + __func__); + + return ret; +} + +static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi, + safi_t safi, struct bgp_table *table, + struct route_map *rmap, + enum update_type update_type) +{ + bool addpath_capable; + struct bgp_dest *dest; + struct bgp_path_info *pi; + struct bgp_path_info path; + struct peer_af *paf; + const struct prefix *dest_p; + struct update_subgroup *subgrp; + struct attr advmap_attr = {0}, attr = {0}; + struct bgp_path_info_extra path_extra = {0}; + route_map_result_t ret; + + paf = peer_af_find(peer, afi, safi); + if (!paf) + return; + + subgrp = PAF_SUBGRP(paf); + /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */ + if (!subgrp) + return; + + subgrp->pscount = 0; + SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING); + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: %s routes to/from %s for %s", __func__, + update_type == UPDATE_TYPE_ADVERTISE ? "Advertise" + : "Withdraw", + peer->host, get_afi_safi_str(afi, safi, false)); + + addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); + + for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { + dest_p = bgp_dest_get_prefix(dest); + assert(dest_p); + + for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) { + advmap_attr = *pi->attr; + + /* Fill temp path_info */ + prep_for_rmap_apply(&path, &path_extra, dest, pi, + pi->peer, &advmap_attr); + + RESET_FLAG(advmap_attr.rmap_change_flags); + + ret = route_map_apply(rmap, dest_p, &path); + if (ret != RMAP_PERMITMATCH || + !bgp_check_selected(pi, peer, addpath_capable, afi, + safi)) { + bgp_attr_flush(&advmap_attr); + continue; + } + + /* Skip route-map checks in + * subgroup_announce_check while executing from + * the conditional advertise scanner process. + * otherwise when route-map is also configured + * on same peer, routes in advertise-map may not + * be advertised as expected. + */ + if (update_type == UPDATE_TYPE_ADVERTISE && + subgroup_announce_check(dest, pi, subgrp, dest_p, + &attr, &advmap_attr)) { + bgp_adj_out_set_subgroup(dest, subgrp, &attr, + pi); + } else { + /* If default originate is enabled for + * the peer, do not send explicit + * withdraw. This will prevent deletion + * of default route advertised through + * default originate. + */ + if (CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_DEFAULT_ORIGINATE) && + is_default_prefix(dest_p)) + break; + + bgp_adj_out_unset_subgroup( + dest, subgrp, 1, + bgp_addpath_id_for_peer( + peer, afi, safi, + &pi->tx_addpath)); + } + bgp_attr_flush(&advmap_attr); + } + } + UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING); +} + +/* Handler of conditional advertisement timer event. + * Each route in the condition-map is evaluated. + */ +static void bgp_conditional_adv_timer(struct thread *t) +{ + afi_t afi; + safi_t safi; + int pfx_rcd_safi; + struct bgp *bgp = NULL; + struct peer *peer = NULL; + struct peer_af *paf = NULL; + struct bgp_table *table = NULL; + struct bgp_filter *filter = NULL; + struct listnode *node, *nnode = NULL; + struct update_subgroup *subgrp = NULL; + route_map_result_t ret; + bool advmap_table_changed = false; + + bgp = THREAD_ARG(t); + assert(bgp); + + thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, + bgp->condition_check_period, &bgp->t_condition_check); + + /* loop through each peer and check if we have peers with + * advmap_table_change attribute set, to make sure we send + * conditional advertisements properly below. + * peer->advmap_table_change is added on incoming BGP UPDATES, + * but here it's used for outgoing UPDATES, hence we need to + * check if at least one peer got advmap_table_change. + */ + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) { + if (peer->advmap_table_change) { + advmap_table_changed = true; + break; + } + } + + /* loop through each peer and advertise or withdraw routes if + * advertise-map is configured and prefix(es) in condition-map + * does exist(exist-map)/not exist(non-exist-map) in BGP table + * based on condition(exist-map or non-exist map) + */ + for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { + if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) + continue; + + if (!peer_established(peer)) + continue; + + FOREACH_AFI_SAFI (afi, safi) { + if (!peer->afc_nego[afi][safi]) + continue; + + /* labeled-unicast routes are installed in the unicast + * table so in order to display the correct PfxRcd value + * we must look at SAFI_UNICAST + */ + pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST) + ? SAFI_UNICAST + : safi; + + table = bgp->rib[afi][pfx_rcd_safi]; + if (!table) + continue; + + filter = &peer->filter[afi][safi]; + + if (!filter->advmap.aname || !filter->advmap.cname + || !filter->advmap.amap || !filter->advmap.cmap) + continue; + + if (!peer->advmap_config_change[afi][safi] && + !advmap_table_changed) + continue; + + if (BGP_DEBUG(update, UPDATE_OUT)) { + if (peer->advmap_table_change) + zlog_debug( + "%s: %s - routes changed in BGP table.", + __func__, peer->host); + if (peer->advmap_config_change[afi][safi]) + zlog_debug( + "%s: %s for %s - advertise/condition map configuration is changed.", + __func__, peer->host, + get_afi_safi_str(afi, safi, + false)); + } + + /* cmap (route-map attached to exist-map or + * non-exist-map) map validation + */ + ret = bgp_check_rmap_prefixes_in_bgp_table( + table, filter->advmap.cmap); + + /* Derive conditional advertisement status from + * condition and return value of condition-map + * validation. + */ + if (filter->advmap.condition == CONDITION_EXIST) + filter->advmap.update_type = + (ret == RMAP_PERMITMATCH) + ? UPDATE_TYPE_ADVERTISE + : UPDATE_TYPE_WITHDRAW; + else + filter->advmap.update_type = + (ret == RMAP_PERMITMATCH) + ? UPDATE_TYPE_WITHDRAW + : UPDATE_TYPE_ADVERTISE; + + /* + * Update condadv update type so + * subgroup_announce_check() can properly apply + * outbound policy according to advertisement state + */ + paf = peer_af_find(peer, afi, safi); + if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf)) + ->filter[afi][safi] + .advmap.update_type != + filter->advmap.update_type)) { + /* Handle change to peer advmap */ + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "%s: advmap.update_type changed for peer %s, adjusting update_group.", + __func__, peer->host); + + update_group_adjust_peer(paf); + } + + /* Send regular update as per the existing policy. + * There is a change in route-map, match-rule, ACLs, + * or route-map filter configuration on the same peer. + */ + if (peer->advmap_config_change[afi][safi]) { + + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug( + "%s: Configuration is changed on peer %s for %s, send the normal update first.", + __func__, peer->host, + get_afi_safi_str(afi, safi, + false)); + if (paf) { + update_subgroup_split_peer(paf, NULL); + subgrp = paf->subgroup; + + if (subgrp && subgrp->update_group) + subgroup_announce_table( + paf->subgroup, NULL); + } + peer->advmap_config_change[afi][safi] = false; + } + + /* Send update as per the conditional advertisement */ + bgp_conditional_adv_routes(peer, afi, safi, table, + filter->advmap.amap, + filter->advmap.update_type); + } + peer->advmap_table_change = false; + } +} + +void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp *bgp = peer->bgp; + + assert(bgp); + + /* This flag is used to monitor conditional routes status in BGP table, + * and advertise/withdraw routes only when there is a change in BGP + * table w.r.t conditional routes + */ + peer->advmap_config_change[afi][safi] = true; + + /* advertise-map is already configured on at least one of its + * neighbors (AFI/SAFI). So just increment the counter. + */ + if (++bgp->condition_filter_count > 1) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: condition_filter_count %d", __func__, + bgp->condition_filter_count); + + return; + } + + /* Register for conditional routes polling timer */ + if (!thread_is_scheduled(bgp->t_condition_check)) + thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0, + &bgp->t_condition_check); +} + +void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp *bgp = peer->bgp; + + assert(bgp); + + /* advertise-map is not configured on any of its neighbors or + * it is configured on more than one neighbor(AFI/SAFI). + * So there's nothing to do except decrementing the counter. + */ + if (--bgp->condition_filter_count != 0) { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: condition_filter_count %d", __func__, + bgp->condition_filter_count); + + return; + } + + /* Last filter removed. So cancel conditional routes polling thread. */ + THREAD_OFF(bgp->t_condition_check); +} -- cgit v1.2.3