]> git.proxmox.com Git - mirror_frr.git/blob - bgpd/bgp_conditional_adv.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[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 bool advmap_table_changed = false;
180
181 bgp = THREAD_ARG(t);
182 assert(bgp);
183
184 thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
185 bgp->condition_check_period, &bgp->t_condition_check);
186
187 /* loop through each peer and check if we have peers with
188 * advmap_table_change attribute set, to make sure we send
189 * conditional advertisements properly below.
190 * peer->advmap_table_change is added on incoming BGP UPDATES,
191 * but here it's used for outgoing UPDATES, hence we need to
192 * check if at least one peer got advmap_table_change.
193 */
194 for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
195 if (peer->advmap_table_change) {
196 advmap_table_changed = true;
197 break;
198 }
199 }
200
201 /* loop through each peer and advertise or withdraw routes if
202 * advertise-map is configured and prefix(es) in condition-map
203 * does exist(exist-map)/not exist(non-exist-map) in BGP table
204 * based on condition(exist-map or non-exist map)
205 */
206 for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
207 if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
208 continue;
209
210 if (!peer_established(peer))
211 continue;
212
213 FOREACH_AFI_SAFI (afi, safi) {
214 if (!peer->afc_nego[afi][safi])
215 continue;
216
217 /* labeled-unicast routes are installed in the unicast
218 * table so in order to display the correct PfxRcd value
219 * we must look at SAFI_UNICAST
220 */
221 pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
222 ? SAFI_UNICAST
223 : safi;
224
225 table = bgp->rib[afi][pfx_rcd_safi];
226 if (!table)
227 continue;
228
229 filter = &peer->filter[afi][safi];
230
231 if (!filter->advmap.aname || !filter->advmap.cname
232 || !filter->advmap.amap || !filter->advmap.cmap)
233 continue;
234
235 if (!peer->advmap_config_change[afi][safi] &&
236 !advmap_table_changed)
237 continue;
238
239 if (BGP_DEBUG(cond_adv, COND_ADV)) {
240 if (peer->advmap_table_change)
241 zlog_debug(
242 "%s: %s - routes changed in BGP table.",
243 __func__, peer->host);
244 if (peer->advmap_config_change[afi][safi])
245 zlog_debug(
246 "%s: %s for %s - advertise/condition map configuration is changed.",
247 __func__, peer->host,
248 get_afi_safi_str(afi, safi,
249 false));
250 }
251
252 /* cmap (route-map attached to exist-map or
253 * non-exist-map) map validation
254 */
255 ret = bgp_check_rmap_prefixes_in_bgp_table(
256 table, filter->advmap.cmap);
257
258 /* Derive conditional advertisement status from
259 * condition and return value of condition-map
260 * validation.
261 */
262 if (filter->advmap.condition == CONDITION_EXIST)
263 filter->advmap.update_type =
264 (ret == RMAP_PERMITMATCH)
265 ? UPDATE_TYPE_ADVERTISE
266 : UPDATE_TYPE_WITHDRAW;
267 else
268 filter->advmap.update_type =
269 (ret == RMAP_PERMITMATCH)
270 ? UPDATE_TYPE_WITHDRAW
271 : UPDATE_TYPE_ADVERTISE;
272
273 /*
274 * Update condadv update type so
275 * subgroup_announce_check() can properly apply
276 * outbound policy according to advertisement state
277 */
278 paf = peer_af_find(peer, afi, safi);
279 if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf))
280 ->filter[afi][safi]
281 .advmap.update_type !=
282 filter->advmap.update_type)) {
283 /* Handle change to peer advmap */
284 bgp_cond_adv_debug(
285 "%s: advmap.update_type changed for peer %s, adjusting update_group.",
286 __func__, peer->host);
287
288 update_group_adjust_peer(paf);
289 }
290
291 /* Send regular update as per the existing policy.
292 * There is a change in route-map, match-rule, ACLs,
293 * or route-map filter configuration on the same peer.
294 */
295 if (peer->advmap_config_change[afi][safi]) {
296
297 bgp_cond_adv_debug(
298 "%s: Configuration is changed on peer %s for %s, send the normal update first.",
299 __func__, peer->host,
300 get_afi_safi_str(afi, safi, false));
301 if (paf) {
302 update_subgroup_split_peer(paf, NULL);
303 subgrp = paf->subgroup;
304
305 if (subgrp && subgrp->update_group)
306 subgroup_announce_table(
307 paf->subgroup, NULL);
308 }
309 peer->advmap_config_change[afi][safi] = false;
310 }
311
312 /* Send update as per the conditional advertisement */
313 bgp_conditional_adv_routes(peer, afi, safi, table,
314 filter->advmap.amap,
315 filter->advmap.update_type);
316 }
317 peer->advmap_table_change = false;
318 }
319 }
320
321 void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
322 {
323 struct bgp *bgp = peer->bgp;
324
325 assert(bgp);
326
327 /* This flag is used to monitor conditional routes status in BGP table,
328 * and advertise/withdraw routes only when there is a change in BGP
329 * table w.r.t conditional routes
330 */
331 peer->advmap_config_change[afi][safi] = true;
332
333 /* advertise-map is already configured on at least one of its
334 * neighbors (AFI/SAFI). So just increment the counter.
335 */
336 if (++bgp->condition_filter_count > 1) {
337 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
338 bgp->condition_filter_count);
339
340 return;
341 }
342
343 /* Register for conditional routes polling timer */
344 if (!thread_is_scheduled(bgp->t_condition_check))
345 thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0,
346 &bgp->t_condition_check);
347 }
348
349 void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
350 {
351 struct bgp *bgp = peer->bgp;
352
353 assert(bgp);
354
355 /* advertise-map is not configured on any of its neighbors or
356 * it is configured on more than one neighbor(AFI/SAFI).
357 * So there's nothing to do except decrementing the counter.
358 */
359 if (--bgp->condition_filter_count != 0) {
360 bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
361 bgp->condition_filter_count);
362
363 return;
364 }
365
366 /* Last filter removed. So cancel conditional routes polling thread. */
367 THREAD_OFF(bgp->t_condition_check);
368 }
369
370 static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi,
371 safi_t safi, const char *amap_name,
372 struct route_map *amap,
373 const char *cmap_name,
374 struct route_map *cmap,
375 bool condition, bool set)
376 {
377 struct bgp_filter *filter;
378 bool filter_exists = false;
379
380 filter = &peer->filter[afi][safi];
381
382 /* advertise-map is already configured. */
383 if (filter->advmap.aname) {
384 filter_exists = true;
385 XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
386 XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
387 }
388
389 route_map_counter_decrement(filter->advmap.amap);
390
391 /* Removed advertise-map configuration */
392 if (!set) {
393 memset(&filter->advmap, 0, sizeof(filter->advmap));
394
395 /* decrement condition_filter_count delete timer if
396 * this is the last advertise-map to be removed.
397 */
398 if (filter_exists)
399 bgp_conditional_adv_disable(peer, afi, safi);
400
401 /* Process peer route updates. */
402 peer_on_policy_change(peer, afi, safi, 1);
403
404 return;
405 }
406
407 /* Update filter data with newly configured values. */
408 filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, amap_name);
409 filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, cmap_name);
410 filter->advmap.amap = amap;
411 filter->advmap.cmap = cmap;
412 filter->advmap.condition = condition;
413 route_map_counter_increment(filter->advmap.amap);
414 peer->advmap_config_change[afi][safi] = true;
415
416 /* Increment condition_filter_count and/or create timer. */
417 if (!filter_exists) {
418 filter->advmap.update_type = UPDATE_TYPE_ADVERTISE;
419 bgp_conditional_adv_enable(peer, afi, safi);
420 }
421
422 /* Process peer route updates. */
423 peer_on_policy_change(peer, afi, safi, 1);
424 }
425
426 /* Set advertise-map to the peer. */
427 int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
428 const char *advertise_name,
429 struct route_map *advertise_map,
430 const char *condition_name,
431 struct route_map *condition_map, bool condition)
432 {
433 struct peer *member;
434 struct listnode *node, *nnode;
435
436 /* Set configuration on peer. */
437 peer_advertise_map_filter_update(peer, afi, safi, advertise_name,
438 advertise_map, condition_name,
439 condition_map, condition, true);
440
441 /* Check if handling a regular peer & Skip peer-group mechanics. */
442 if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
443 /* Set override-flag and process peer route updates. */
444 SET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
445 PEER_FT_ADVERTISE_MAP);
446 return 0;
447 }
448
449 /*
450 * Set configuration on all peer-group members, unless they are
451 * explicitly overriding peer-group configuration.
452 */
453 for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
454 /* Skip peers with overridden configuration. */
455 if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
456 PEER_FT_ADVERTISE_MAP))
457 continue;
458
459 /* Set configuration on peer-group member. */
460 peer_advertise_map_filter_update(
461 member, afi, safi, advertise_name, advertise_map,
462 condition_name, condition_map, condition, true);
463 }
464
465 return 0;
466 }
467
468 /* Unset advertise-map from the peer. */
469 int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
470 const char *advertise_name,
471 struct route_map *advertise_map,
472 const char *condition_name,
473 struct route_map *condition_map, bool condition)
474 {
475 struct peer *member;
476 struct listnode *node, *nnode;
477
478 /* advertise-map is not configured */
479 if (!peer->filter[afi][safi].advmap.aname)
480 return 0;
481
482 /* Unset override-flag unconditionally. */
483 UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
484 PEER_FT_ADVERTISE_MAP);
485
486 /* Inherit configuration from peer-group if peer is member. */
487 if (peer_group_active(peer)) {
488 PEER_STR_ATTR_INHERIT(peer, peer->group,
489 filter[afi][safi].advmap.aname,
490 MTYPE_BGP_FILTER_NAME);
491 PEER_ATTR_INHERIT(peer, peer->group,
492 filter[afi][safi].advmap.amap);
493 } else
494 peer_advertise_map_filter_update(
495 peer, afi, safi, advertise_name, advertise_map,
496 condition_name, condition_map, condition, false);
497
498 /* Check if handling a regular peer and skip peer-group mechanics. */
499 if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
500 /* Process peer route updates. */
501 bgp_cond_adv_debug("%s: Send normal update to %s for %s",
502 __func__, peer->host,
503 get_afi_safi_str(afi, safi, false));
504
505 return 0;
506 }
507
508 /*
509 * Remove configuration on all peer-group members, unless they are
510 * explicitly overriding peer-group configuration.
511 */
512 for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
513 /* Skip peers with overridden configuration. */
514 if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
515 PEER_FT_ADVERTISE_MAP))
516 continue;
517 /* Remove configuration on peer-group member. */
518 peer_advertise_map_filter_update(
519 member, afi, safi, advertise_name, advertise_map,
520 condition_name, condition_map, condition, false);
521
522 /* Process peer route updates. */
523 bgp_cond_adv_debug("%s: Send normal update to %s for %s ",
524 __func__, member->host,
525 get_afi_safi_str(afi, safi, false));
526 }
527
528 return 0;
529 }