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