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