]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_conditional_adv.c
Merge pull request #7760 from donaldsharp/DIE_IN_A_FIRE
[mirror_frr.git] / bgpd / bgp_conditional_adv.c
1 /*
2 * BGP Conditional advertisement
3 * Copyright (C) 2020 Samsung R&D Institute India - Bangalore.
4 * Madhurilatha Kuruganti
5 *
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)
9 * any later version.
10 *
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
14 * more details.
15 *
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
19 */
20
21 #include "bgpd/bgp_conditional_adv.h"
22 #include "bgpd/bgp_vty.h"
23
24 static route_map_result_t
25 bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
26 struct route_map *rmap)
27 {
28 struct attr dummy_attr = {0};
29 struct bgp_dest *dest;
30 struct bgp_path_info *pi;
31 struct bgp_path_info path = {0};
32 struct bgp_path_info_extra path_extra = {0};
33 const struct prefix *dest_p;
34 route_map_result_t ret = RMAP_DENYMATCH;
35
36 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
37 dest_p = bgp_dest_get_prefix(dest);
38 assert(dest_p);
39
40 for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
41 dummy_attr = *pi->attr;
42
43 /* Fill temp path_info */
44 prep_for_rmap_apply(&path, &path_extra, dest, pi,
45 pi->peer, &dummy_attr);
46
47 RESET_FLAG(dummy_attr.rmap_change_flags);
48
49 ret = route_map_apply(rmap, dest_p, &path);
50 if (ret != RMAP_PERMITMATCH)
51 bgp_attr_flush(&dummy_attr);
52 else {
53 bgp_dest_unlock_node(dest);
54 if (BGP_DEBUG(update, UPDATE_OUT))
55 zlog_debug(
56 "%s: Condition map routes present in BGP table",
57 __func__);
58
59 return ret;
60 }
61 }
62 }
63
64 if (BGP_DEBUG(update, UPDATE_OUT))
65 zlog_debug("%s: Condition map routes not present in BGP table",
66 __func__);
67
68 return ret;
69 }
70
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)
75 {
76 int addpath_capable;
77 struct bgp_dest *dest;
78 struct bgp_path_info *pi;
79 struct bgp_path_info path;
80 struct peer_af *paf;
81 const struct prefix *dest_p;
82 struct update_subgroup *subgrp;
83 struct attr dummy_attr = {0}, attr = {0};
84 struct bgp_path_info_extra path_extra = {0};
85
86 paf = peer_af_find(peer, afi, safi);
87 if (!paf)
88 return;
89
90 subgrp = PAF_SUBGRP(paf);
91 /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
92 if (!subgrp)
93 return;
94
95 if (BGP_DEBUG(update, UPDATE_OUT))
96 zlog_debug("%s: %s routes to/from %s for %s", __func__,
97 update_type == ADVERTISE ? "Advertise" : "Withdraw",
98 peer->host, get_afi_safi_str(afi, safi, false));
99
100 addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
101
102 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
103 dest_p = bgp_dest_get_prefix(dest);
104 assert(dest_p);
105
106 for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
107 dummy_attr = *pi->attr;
108
109 /* Fill temp path_info */
110 prep_for_rmap_apply(&path, &path_extra, dest, pi,
111 pi->peer, &dummy_attr);
112
113 RESET_FLAG(dummy_attr.rmap_change_flags);
114
115 if (route_map_apply(rmap, dest_p, &path)
116 != RMAP_PERMITMATCH) {
117 bgp_attr_flush(&dummy_attr);
118 continue;
119 }
120
121 if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
122 || (addpath_capable
123 && bgp_addpath_tx_path(
124 peer->addpath_type[afi][safi],
125 pi))) {
126
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.
133 */
134 if ((update_type == ADVERTISE)
135 && subgroup_announce_check(dest, pi, subgrp,
136 dest_p, &attr,
137 true))
138 bgp_adj_out_set_subgroup(dest, subgrp,
139 &attr, pi);
140 else {
141 /* If default originate is enabled for
142 * the peer, do not send explicit
143 * withdraw. This will prevent deletion
144 * of default route advertised through
145 * default originate.
146 */
147 if (CHECK_FLAG(
148 peer->af_flags[afi][safi],
149 PEER_FLAG_DEFAULT_ORIGINATE)
150 && is_default_prefix(dest_p))
151 break;
152
153 bgp_adj_out_unset_subgroup(
154 dest, subgrp, 1,
155 bgp_addpath_id_for_peer(
156 peer, afi, safi,
157 &pi->tx_addpath));
158 }
159 }
160 }
161 }
162 }
163
164 /* Handler of conditional advertisement timer event.
165 * Each route in the condition-map is evaluated.
166 */
167 static int bgp_conditional_adv_timer(struct thread *t)
168 {
169 afi_t afi;
170 safi_t safi;
171 int pfx_rcd_safi;
172 struct bgp *bgp = NULL;
173 struct peer *peer = NULL;
174 struct peer_af *paf = NULL;
175 struct bgp_table *table = NULL;
176 struct bgp_filter *filter = NULL;
177 struct listnode *node, *nnode = NULL;
178 struct update_subgroup *subgrp = NULL;
179 route_map_result_t ret;
180
181 bgp = THREAD_ARG(t);
182 assert(bgp);
183
184 thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
185 CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
186
187 /* loop through each peer and advertise or withdraw routes if
188 * advertise-map is configured and prefix(es) in condition-map
189 * does exist(exist-map)/not exist(non-exist-map) in BGP table
190 * based on condition(exist-map or non-exist map)
191 */
192 for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
193 if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
194 continue;
195
196 if (peer->status != Established)
197 continue;
198
199 FOREACH_AFI_SAFI (afi, safi) {
200 if (!peer->afc_nego[afi][safi])
201 continue;
202
203 /* labeled-unicast routes are installed in the unicast
204 * table so in order to display the correct PfxRcd value
205 * we must look at SAFI_UNICAST
206 */
207 pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
208 ? SAFI_UNICAST
209 : safi;
210
211 table = bgp->rib[afi][pfx_rcd_safi];
212 if (!table)
213 continue;
214
215 filter = &peer->filter[afi][safi];
216
217 if (!filter->advmap.aname || !filter->advmap.cname
218 || !filter->advmap.amap || !filter->advmap.cmap)
219 continue;
220
221 if (!peer->advmap_config_change[afi][safi]
222 && !peer->advmap_table_change)
223 continue;
224
225 if (BGP_DEBUG(update, UPDATE_OUT)) {
226 if (peer->advmap_table_change)
227 zlog_debug(
228 "%s: %s - routes changed in BGP table.",
229 __func__, peer->host);
230 if (peer->advmap_config_change[afi][safi])
231 zlog_debug(
232 "%s: %s for %s - advertise/condition map configuration is changed.",
233 __func__, peer->host,
234 get_afi_safi_str(afi, safi,
235 false));
236 }
237
238 /* cmap (route-map attached to exist-map or
239 * non-exist-map) map validation
240 */
241 ret = bgp_check_rmap_prefixes_in_bgp_table(
242 table, filter->advmap.cmap);
243
244 /* Derive conditional advertisement status from
245 * condition and return value of condition-map
246 * validation.
247 */
248 if (filter->advmap.condition == CONDITION_EXIST)
249 filter->advmap.update_type =
250 (ret == RMAP_PERMITMATCH) ? ADVERTISE
251 : WITHDRAW;
252 else
253 filter->advmap.update_type =
254 (ret == RMAP_PERMITMATCH) ? WITHDRAW
255 : ADVERTISE;
256
257 /* Send regular update as per the existing policy.
258 * There is a change in route-map, match-rule, ACLs,
259 * or route-map filter configuration on the same peer.
260 */
261 if (peer->advmap_config_change[afi][safi]) {
262
263 if (BGP_DEBUG(update, UPDATE_OUT))
264 zlog_debug(
265 "%s: Configuration is changed on peer %s for %s, send the normal update first.",
266 __func__, peer->host,
267 get_afi_safi_str(afi, safi,
268 false));
269
270 paf = peer_af_find(peer, afi, safi);
271 if (paf) {
272 update_subgroup_split_peer(paf, NULL);
273 subgrp = paf->subgroup;
274 if (subgrp && subgrp->update_group)
275 subgroup_announce_table(
276 paf->subgroup, NULL);
277 }
278 peer->advmap_config_change[afi][safi] = false;
279 }
280
281 /* Send update as per the conditional advertisement */
282 bgp_conditional_adv_routes(peer, afi, safi, table,
283 filter->advmap.amap,
284 filter->advmap.update_type);
285 }
286 peer->advmap_table_change = false;
287 }
288 return 0;
289 }
290
291 void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
292 {
293 struct bgp *bgp = peer->bgp;
294
295 assert(bgp);
296
297 /* This flag is used to monitor conditional routes status in BGP table,
298 * and advertise/withdraw routes only when there is a change in BGP
299 * table w.r.t conditional routes
300 */
301 peer->advmap_config_change[afi][safi] = true;
302
303 /* advertise-map is already configured on atleast one of its
304 * neighbors (AFI/SAFI). So just increment the counter.
305 */
306 if (++bgp->condition_filter_count > 1) {
307 if (BGP_DEBUG(update, UPDATE_OUT))
308 zlog_debug("%s: condition_filter_count %d", __func__,
309 bgp->condition_filter_count);
310
311 return;
312 }
313
314 /* Register for conditional routes polling timer */
315 thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
316 CONDITIONAL_ROUTES_POLL_TIME, &bgp->t_condition_check);
317 }
318
319 void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
320 {
321 struct bgp *bgp = peer->bgp;
322
323 assert(bgp);
324
325 /* advertise-map is not configured on any of its neighbors or
326 * it is configured on more than one neighbor(AFI/SAFI).
327 * So there's nothing to do except decrementing the counter.
328 */
329 if (--bgp->condition_filter_count != 0) {
330 if (BGP_DEBUG(update, UPDATE_OUT))
331 zlog_debug("%s: condition_filter_count %d", __func__,
332 bgp->condition_filter_count);
333
334 return;
335 }
336
337 /* Last filter removed. So cancel conditional routes polling thread. */
338 THREAD_OFF(bgp->t_condition_check);
339 }