Line data Source code
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 0 : bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
28 : struct route_map *rmap)
29 : {
30 0 : struct attr dummy_attr = {0};
31 0 : struct bgp_dest *dest;
32 0 : struct bgp_path_info *pi;
33 0 : struct bgp_path_info path = {0};
34 0 : struct bgp_path_info_extra path_extra = {0};
35 0 : const struct prefix *dest_p;
36 0 : route_map_result_t ret = RMAP_DENYMATCH;
37 :
38 0 : for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
39 0 : dest_p = bgp_dest_get_prefix(dest);
40 0 : assert(dest_p);
41 :
42 0 : for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
43 0 : dummy_attr = *pi->attr;
44 :
45 : /* Fill temp path_info */
46 0 : prep_for_rmap_apply(&path, &path_extra, dest, pi,
47 : pi->peer, &dummy_attr);
48 :
49 0 : RESET_FLAG(dummy_attr.rmap_change_flags);
50 :
51 0 : ret = route_map_apply(rmap, dest_p, &path);
52 0 : bgp_attr_flush(&dummy_attr);
53 :
54 0 : if (ret == RMAP_PERMITMATCH) {
55 0 : bgp_dest_unlock_node(dest);
56 0 : bgp_cond_adv_debug(
57 : "%s: Condition map routes present in BGP table",
58 : __func__);
59 :
60 0 : return ret;
61 : }
62 : }
63 : }
64 :
65 0 : bgp_cond_adv_debug("%s: Condition map routes not present in BGP table",
66 : __func__);
67 :
68 : return ret;
69 : }
70 :
71 0 : 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 0 : bool addpath_capable;
77 0 : struct bgp_dest *dest;
78 0 : struct bgp_path_info *pi;
79 0 : struct bgp_path_info path;
80 0 : struct peer_af *paf;
81 0 : const struct prefix *dest_p;
82 0 : struct update_subgroup *subgrp;
83 0 : struct attr advmap_attr = {0}, attr = {0};
84 0 : struct bgp_path_info_extra path_extra = {0};
85 0 : route_map_result_t ret;
86 :
87 0 : paf = peer_af_find(peer, afi, safi);
88 0 : if (!paf)
89 0 : return;
90 :
91 0 : subgrp = PAF_SUBGRP(paf);
92 : /* Ignore if subgroup doesn't exist (implies AF is not negotiated) */
93 0 : if (!subgrp)
94 : return;
95 :
96 0 : subgrp->pscount = 0;
97 0 : SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
98 :
99 0 : 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 0 : addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
105 :
106 0 : for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
107 0 : dest_p = bgp_dest_get_prefix(dest);
108 0 : assert(dest_p);
109 :
110 0 : for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
111 0 : advmap_attr = *pi->attr;
112 :
113 : /* Fill temp path_info */
114 0 : prep_for_rmap_apply(&path, &path_extra, dest, pi,
115 : pi->peer, &advmap_attr);
116 :
117 0 : RESET_FLAG(advmap_attr.rmap_change_flags);
118 :
119 0 : ret = route_map_apply(rmap, dest_p, &path);
120 0 : if (ret != RMAP_PERMITMATCH ||
121 0 : !bgp_check_selected(pi, peer, addpath_capable, afi,
122 : safi)) {
123 0 : bgp_attr_flush(&advmap_attr);
124 0 : 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 0 : if (update_type == UPDATE_TYPE_ADVERTISE &&
135 0 : subgroup_announce_check(dest, pi, subgrp, dest_p,
136 : &attr, &advmap_attr)) {
137 0 : 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 0 : if (CHECK_FLAG(peer->af_flags[afi][safi],
147 0 : PEER_FLAG_DEFAULT_ORIGINATE) &&
148 0 : is_default_prefix(dest_p))
149 : break;
150 :
151 0 : 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 0 : bgp_attr_flush(&advmap_attr);
158 : }
159 : }
160 0 : 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 0 : static void bgp_conditional_adv_timer(struct thread *t)
167 : {
168 0 : afi_t afi;
169 0 : safi_t safi;
170 0 : int pfx_rcd_safi;
171 0 : struct bgp *bgp = NULL;
172 0 : struct peer *peer = NULL;
173 0 : struct peer_af *paf = NULL;
174 0 : struct bgp_table *table = NULL;
175 0 : struct bgp_filter *filter = NULL;
176 0 : struct listnode *node, *nnode = NULL;
177 0 : struct update_subgroup *subgrp = NULL;
178 0 : route_map_result_t ret;
179 0 : bool advmap_table_changed = false;
180 :
181 0 : bgp = THREAD_ARG(t);
182 0 : assert(bgp);
183 :
184 0 : 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 0 : for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
195 0 : 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 0 : for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
207 0 : if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
208 0 : continue;
209 :
210 0 : if (!peer_established(peer))
211 0 : continue;
212 :
213 0 : FOREACH_AFI_SAFI (afi, safi) {
214 0 : if (!peer->afc_nego[afi][safi])
215 0 : 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 0 : pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
222 : ? SAFI_UNICAST
223 0 : : safi;
224 :
225 0 : table = bgp->rib[afi][pfx_rcd_safi];
226 0 : if (!table)
227 0 : continue;
228 :
229 0 : filter = &peer->filter[afi][safi];
230 :
231 0 : if (!filter->advmap.aname || !filter->advmap.cname
232 0 : || !filter->advmap.amap || !filter->advmap.cmap)
233 0 : continue;
234 :
235 0 : if (!peer->advmap_config_change[afi][safi] &&
236 : !advmap_table_changed)
237 0 : continue;
238 :
239 0 : if (BGP_DEBUG(cond_adv, COND_ADV)) {
240 0 : if (peer->advmap_table_change)
241 0 : zlog_debug(
242 : "%s: %s - routes changed in BGP table.",
243 : __func__, peer->host);
244 0 : if (peer->advmap_config_change[afi][safi])
245 0 : 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 0 : 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 0 : if (filter->advmap.condition == CONDITION_EXIST)
263 0 : filter->advmap.update_type =
264 : (ret == RMAP_PERMITMATCH)
265 : ? UPDATE_TYPE_ADVERTISE
266 0 : : UPDATE_TYPE_WITHDRAW;
267 : else
268 0 : filter->advmap.update_type =
269 : (ret == RMAP_PERMITMATCH)
270 : ? UPDATE_TYPE_WITHDRAW
271 0 : : 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 0 : paf = peer_af_find(peer, afi, safi);
279 0 : if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf))
280 : ->filter[afi][safi]
281 0 : .advmap.update_type !=
282 0 : filter->advmap.update_type)) {
283 : /* Handle change to peer advmap */
284 0 : bgp_cond_adv_debug(
285 : "%s: advmap.update_type changed for peer %s, adjusting update_group.",
286 : __func__, peer->host);
287 :
288 0 : 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 0 : if (peer->advmap_config_change[afi][safi]) {
296 :
297 0 : 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 0 : if (paf) {
302 0 : update_subgroup_split_peer(paf, NULL);
303 0 : subgrp = paf->subgroup;
304 :
305 0 : if (subgrp && subgrp->update_group)
306 0 : subgroup_announce_table(
307 : paf->subgroup, NULL);
308 : }
309 0 : peer->advmap_config_change[afi][safi] = false;
310 : }
311 :
312 : /* Send update as per the conditional advertisement */
313 0 : bgp_conditional_adv_routes(peer, afi, safi, table,
314 : filter->advmap.amap,
315 : filter->advmap.update_type);
316 : }
317 0 : peer->advmap_table_change = false;
318 : }
319 0 : }
320 :
321 0 : void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
322 : {
323 0 : struct bgp *bgp = peer->bgp;
324 :
325 0 : 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 0 : 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 0 : if (++bgp->condition_filter_count > 1) {
337 0 : bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
338 : bgp->condition_filter_count);
339 :
340 0 : return;
341 : }
342 :
343 : /* Register for conditional routes polling timer */
344 0 : if (!thread_is_scheduled(bgp->t_condition_check))
345 0 : thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0,
346 : &bgp->t_condition_check);
347 : }
348 :
349 0 : void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
350 : {
351 0 : struct bgp *bgp = peer->bgp;
352 :
353 0 : 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 0 : if (--bgp->condition_filter_count != 0) {
360 0 : bgp_cond_adv_debug("%s: condition_filter_count %d", __func__,
361 : bgp->condition_filter_count);
362 :
363 0 : return;
364 : }
365 :
366 : /* Last filter removed. So cancel conditional routes polling thread. */
367 0 : THREAD_OFF(bgp->t_condition_check);
368 : }
369 :
370 0 : 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 0 : struct bgp_filter *filter;
378 0 : bool filter_exists = false;
379 :
380 0 : filter = &peer->filter[afi][safi];
381 :
382 : /* advertise-map is already configured. */
383 0 : if (filter->advmap.aname) {
384 0 : filter_exists = true;
385 0 : XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
386 0 : XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
387 : }
388 :
389 0 : route_map_counter_decrement(filter->advmap.amap);
390 :
391 : /* Removed advertise-map configuration */
392 0 : if (!set) {
393 0 : 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 0 : if (filter_exists)
399 0 : bgp_conditional_adv_disable(peer, afi, safi);
400 :
401 : /* Process peer route updates. */
402 0 : peer_on_policy_change(peer, afi, safi, 1);
403 :
404 0 : return;
405 : }
406 :
407 : /* Update filter data with newly configured values. */
408 0 : filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, amap_name);
409 0 : filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, cmap_name);
410 0 : filter->advmap.amap = amap;
411 0 : filter->advmap.cmap = cmap;
412 0 : filter->advmap.condition = condition;
413 0 : route_map_counter_increment(filter->advmap.amap);
414 0 : peer->advmap_config_change[afi][safi] = true;
415 :
416 : /* Increment condition_filter_count and/or create timer. */
417 0 : if (!filter_exists) {
418 0 : filter->advmap.update_type = UPDATE_TYPE_ADVERTISE;
419 0 : bgp_conditional_adv_enable(peer, afi, safi);
420 : }
421 :
422 : /* Process peer route updates. */
423 0 : peer_on_policy_change(peer, afi, safi, 1);
424 : }
425 :
426 : /* Set advertise-map to the peer. */
427 0 : 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 0 : struct peer *member;
434 0 : struct listnode *node, *nnode;
435 :
436 : /* Set configuration on peer. */
437 0 : 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 0 : if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
443 : /* Set override-flag and process peer route updates. */
444 0 : SET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
445 : PEER_FT_ADVERTISE_MAP);
446 0 : return 0;
447 : }
448 :
449 : /*
450 : * Set configuration on all peer-group members, unless they are
451 : * explicitly overriding peer-group configuration.
452 : */
453 0 : for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
454 : /* Skip peers with overridden configuration. */
455 0 : if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
456 : PEER_FT_ADVERTISE_MAP))
457 0 : continue;
458 :
459 : /* Set configuration on peer-group member. */
460 0 : 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 0 : 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 0 : struct peer *member;
476 0 : struct listnode *node, *nnode;
477 :
478 : /* advertise-map is not configured */
479 0 : if (!peer->filter[afi][safi].advmap.aname)
480 : return 0;
481 :
482 : /* Unset override-flag unconditionally. */
483 0 : 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 0 : if (peer_group_active(peer)) {
488 0 : PEER_STR_ATTR_INHERIT(peer, peer->group,
489 : filter[afi][safi].advmap.aname,
490 : MTYPE_BGP_FILTER_NAME);
491 0 : PEER_ATTR_INHERIT(peer, peer->group,
492 : filter[afi][safi].advmap.amap);
493 : } else
494 0 : 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 0 : if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
500 : /* Process peer route updates. */
501 0 : 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 0 : return 0;
506 : }
507 :
508 : /*
509 : * Remove configuration on all peer-group members, unless they are
510 : * explicitly overriding peer-group configuration.
511 : */
512 0 : for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
513 : /* Skip peers with overridden configuration. */
514 0 : if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
515 : PEER_FT_ADVERTISE_MAP))
516 0 : continue;
517 : /* Remove configuration on peer-group member. */
518 0 : 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 0 : 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 : }
|