]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_conditional_adv.c
Merge pull request #12187 from mjstapp/mjs_fix_bgp_allowas
[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 <zebra.h>
22
23 #include "bgpd/bgp_conditional_adv.h"
24 #include "bgpd/bgp_vty.h"
25
26 static route_map_result_t
27 bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
28 struct route_map *rmap)
29 {
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;
37
38 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
39 dest_p = bgp_dest_get_prefix(dest);
40 assert(dest_p);
41
42 for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
43 dummy_attr = *pi->attr;
44
45 /* Fill temp path_info */
46 prep_for_rmap_apply(&path, &path_extra, dest, pi,
47 pi->peer, &dummy_attr);
48
49 RESET_FLAG(dummy_attr.rmap_change_flags);
50
51 ret = route_map_apply(rmap, dest_p, &path);
52 bgp_attr_flush(&dummy_attr);
53
54 if (ret == RMAP_PERMITMATCH) {
55 bgp_dest_unlock_node(dest);
56 bgp_cond_adv_debug(
57 "%s: Condition map routes present in BGP table",
58 __func__);
59
60 return ret;
61 }
62 }
63 }
64
65 bgp_cond_adv_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 bool 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 advmap_attr = {0}, attr = {0};
84 struct bgp_path_info_extra path_extra = {0};
85 route_map_result_t ret;
86
87 paf = peer_af_find(peer, afi, safi);
88 if (!paf)
89 return;
90
91 subgrp = PAF_SUBGRP(paf);
92 /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
93 if (!subgrp)
94 return;
95
96 subgrp->pscount = 0;
97 SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
98
99 bgp_cond_adv_debug("%s: %s routes to/from %s for %s", __func__,
100 update_type == UPDATE_TYPE_ADVERTISE ? "Advertise"
101 : "Withdraw",
102 peer->host, get_afi_safi_str(afi, safi, false));
103
104 addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
105
106 for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
107 dest_p = bgp_dest_get_prefix(dest);
108 assert(dest_p);
109
110 for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
111 advmap_attr = *pi->attr;
112
113 /* Fill temp path_info */
114 prep_for_rmap_apply(&path, &path_extra, dest, pi,
115 pi->peer, &advmap_attr);
116
117 RESET_FLAG(advmap_attr.rmap_change_flags);
118
119 ret = route_map_apply(rmap, dest_p, &path);
120 if (ret != RMAP_PERMITMATCH ||
121 !bgp_check_selected(pi, peer, addpath_capable, afi,
122 safi)) {
123 bgp_attr_flush(&advmap_attr);
124 continue;
125 }
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 == 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,
138 pi);
139 } else {
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
144 * default originate.
145 */
146 if (CHECK_FLAG(peer->af_flags[afi][safi],
147 PEER_FLAG_DEFAULT_ORIGINATE) &&
148 is_default_prefix(dest_p))
149 break;
150
151 bgp_adj_out_unset_subgroup(
152 dest, subgrp, 1,
153 bgp_addpath_id_for_peer(
154 peer, afi, safi,
155 &pi->tx_addpath));
156 }
157 bgp_attr_flush(&advmap_attr);
158 }
159 }
160 UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
161 }
162
163 /* Handler of conditional advertisement timer event.
164 * Each route in the condition-map is evaluated.
165 */
166 static void bgp_conditional_adv_timer(struct thread *t)
167 {
168 afi_t afi;
169 safi_t safi;
170 int pfx_rcd_safi;
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;
179
180 bgp = THREAD_ARG(t);
181 assert(bgp);
182
183 thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
184 bgp->condition_check_period, &bgp->t_condition_check);
185
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)
190 */
191 for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
192 if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
193 continue;
194
195 if (!peer_established(peer))
196 continue;
197
198 FOREACH_AFI_SAFI (afi, safi) {
199 if (!peer->afc_nego[afi][safi])
200 continue;
201
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
205 */
206 pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
207 ? SAFI_UNICAST
208 : safi;
209
210 table = bgp->rib[afi][pfx_rcd_safi];
211 if (!table)
212 continue;
213
214 filter = &peer->filter[afi][safi];
215
216 if (!filter->advmap.aname || !filter->advmap.cname
217 || !filter->advmap.amap || !filter->advmap.cmap)
218 continue;
219
220 if (!peer->advmap_config_change[afi][safi]
221 && !peer->advmap_table_change)
222 continue;
223
224 if (BGP_DEBUG(cond_adv, COND_ADV)) {
225 if (peer->advmap_table_change)
226 zlog_debug(
227 "%s: %s - routes changed in BGP table.",
228 __func__, peer->host);
229 if (peer->advmap_config_change[afi][safi])
230 zlog_debug(
231 "%s: %s for %s - advertise/condition map configuration is changed.",
232 __func__, peer->host,
233 get_afi_safi_str(afi, safi,
234 false));
235 }
236
237 /* cmap (route-map attached to exist-map or
238 * non-exist-map) map validation
239 */
240 ret = bgp_check_rmap_prefixes_in_bgp_table(
241 table, filter->advmap.cmap);
242
243 /* Derive conditional advertisement status from
244 * condition and return value of condition-map
245 * validation.
246 */
247 if (filter->advmap.condition == CONDITION_EXIST)
248 filter->advmap.update_type =
249 (ret == RMAP_PERMITMATCH)
250 ? UPDATE_TYPE_ADVERTISE
251 : UPDATE_TYPE_WITHDRAW;
252 else
253 filter->advmap.update_type =
254 (ret == RMAP_PERMITMATCH)
255 ? UPDATE_TYPE_WITHDRAW
256 : UPDATE_TYPE_ADVERTISE;
257
258 /*
259 * Update condadv update type so
260 * subgroup_announce_check() can properly apply
261 * outbound policy according to advertisement state
262 */
263 paf = peer_af_find(peer, afi, safi);
264 if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf))
265 ->filter[afi][safi]
266 .advmap.update_type !=
267 filter->advmap.update_type)) {
268 /* Handle change to peer advmap */
269 bgp_cond_adv_debug(
270 "%s: advmap.update_type changed for peer %s, adjusting update_group.",
271 __func__, peer->host);
272
273 update_group_adjust_peer(paf);
274 }
275
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.
279 */
280 if (peer->advmap_config_change[afi][safi]) {
281
282 bgp_cond_adv_debug(
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));
286 if (paf) {
287 update_subgroup_split_peer(paf, NULL);
288 subgrp = paf->subgroup;
289
290 if (subgrp && subgrp->update_group)
291 subgroup_announce_table(
292 paf->subgroup, NULL);
293 }
294 peer->advmap_config_change[afi][safi] = false;
295 }
296
297 /* Send update as per the conditional advertisement */
298 bgp_conditional_adv_routes(peer, afi, safi, table,
299 filter->advmap.amap,
300 filter->advmap.update_type);
301 }
302 peer->advmap_table_change = false;
303 }
304 }
305
306 void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
307 {
308 struct bgp *bgp = peer->bgp;
309
310 assert(bgp);
311
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
315 */
316 peer->advmap_config_change[afi][safi] = true;
317
318 /* advertise-map is already configured on at least one of its
319 * neighbors (AFI/SAFI). So just increment the counter.
320 */
321 if (++bgp->condition_filter_count > 1) {
322 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
323 bgp->condition_filter_count);
324
325 return;
326 }
327
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);
332 }
333
334 void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
335 {
336 struct bgp *bgp = peer->bgp;
337
338 assert(bgp);
339
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.
343 */
344 if (--bgp->condition_filter_count != 0) {
345 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
346 bgp->condition_filter_count);
347
348 return;
349 }
350
351 /* Last filter removed. So cancel conditional routes polling thread. */
352 THREAD_OFF(bgp->t_condition_check);
353 }
354
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)
361 {
362 struct bgp_filter *filter;
363 bool filter_exists = false;
364
365 filter = &peer->filter[afi][safi];
366
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);
372 }
373
374 route_map_counter_decrement(filter->advmap.amap);
375
376 /* Removed advertise-map configuration */
377 if (!set) {
378 memset(&filter->advmap, 0, sizeof(filter->advmap));
379
380 /* decrement condition_filter_count delete timer if
381 * this is the last advertise-map to be removed.
382 */
383 if (filter_exists)
384 bgp_conditional_adv_disable(peer, afi, safi);
385
386 /* Process peer route updates. */
387 peer_on_policy_change(peer, afi, safi, 1);
388
389 return;
390 }
391
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;
400
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);
405 }
406
407 /* Process peer route updates. */
408 peer_on_policy_change(peer, afi, safi, 1);
409 }
410
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)
417 {
418 struct peer *member;
419 struct listnode *node, *nnode;
420
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);
425
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);
431 return 0;
432 }
433
434 /*
435 * Set configuration on all peer-group members, unless they are
436 * explicitly overriding peer-group configuration.
437 */
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))
442 continue;
443
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);
448 }
449
450 return 0;
451 }
452
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)
459 {
460 struct peer *member;
461 struct listnode *node, *nnode;
462
463 /* advertise-map is not configured */
464 if (!peer->filter[afi][safi].advmap.aname)
465 return 0;
466
467 /* Unset override-flag unconditionally. */
468 UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
469 PEER_FT_ADVERTISE_MAP);
470
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);
478 } else
479 peer_advertise_map_filter_update(
480 peer, afi, safi, advertise_name, advertise_map,
481 condition_name, condition_map, condition, false);
482
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));
489
490 return 0;
491 }
492
493 /*
494 * Remove configuration on all peer-group members, unless they are
495 * explicitly overriding peer-group configuration.
496 */
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))
501 continue;
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);
506
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));
511 }
512
513 return 0;
514 }