/src/frr/bgpd/rfapi/vnc_export_bgp.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* |
3 | | * |
4 | | * Copyright 2009-2016, LabN Consulting, L.L.C. |
5 | | * |
6 | | */ |
7 | | |
8 | | /* |
9 | | * File: vnc_export_bgp.c |
10 | | * Purpose: Export routes to BGP directly (not via zebra) |
11 | | */ |
12 | | |
13 | | #include "lib/zebra.h" |
14 | | #include "lib/prefix.h" |
15 | | #include "lib/agg_table.h" |
16 | | #include "lib/vty.h" |
17 | | #include "lib/log.h" |
18 | | #include "lib/stream.h" |
19 | | #include "lib/memory.h" |
20 | | #include "lib/linklist.h" |
21 | | #include "lib/plist.h" |
22 | | #include "lib/routemap.h" |
23 | | #include "lib/lib_errors.h" |
24 | | |
25 | | #include "bgpd/bgpd.h" |
26 | | #include "bgpd/bgp_ecommunity.h" |
27 | | #include "bgpd/bgp_attr.h" |
28 | | #include "bgpd/bgp_aspath.h" |
29 | | |
30 | | #include "bgpd/rfapi/vnc_export_bgp.h" |
31 | | #include "bgpd/rfapi/vnc_export_bgp_p.h" |
32 | | #include "bgpd/rfapi/vnc_export_table.h" |
33 | | #include "bgpd/rfapi/bgp_rfapi_cfg.h" |
34 | | #include "bgpd/rfapi/rfapi.h" |
35 | | #include "bgpd/rfapi/rfapi_import.h" |
36 | | #include "bgpd/rfapi/rfapi_private.h" |
37 | | #include "bgpd/rfapi/rfapi_backend.h" |
38 | | #include "bgpd/rfapi/rfapi_vty.h" |
39 | | #include "bgpd/rfapi/vnc_debug.h" |
40 | | |
41 | | |
42 | | static void vnc_direct_add_rn_group_rd(struct bgp *bgp, |
43 | | struct rfapi_nve_group_cfg *rfg, |
44 | | struct agg_node *rn, struct attr *attr, |
45 | | afi_t afi, |
46 | | struct rfapi_descriptor *irfd); |
47 | | |
48 | | /*********************************************************************** |
49 | | * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN |
50 | | ***********************************************************************/ |
51 | | |
52 | | /* |
53 | | * Memory allocation approach: make a ghost attr that |
54 | | * has non-interned parts for the modifications. ghost attr |
55 | | * memory is allocated by caller. |
56 | | * |
57 | | * - extract ce (=5226) EC and use as new nexthop |
58 | | * - strip Tunnel Encap attr |
59 | | * - copy all ECs |
60 | | */ |
61 | | static void encap_attr_export_ce(struct attr *new, struct attr *orig, |
62 | | struct prefix *use_nexthop) |
63 | 0 | { |
64 | | /* |
65 | | * Make "new" a ghost attr copy of "orig" |
66 | | */ |
67 | 0 | memset(new, 0, sizeof(struct attr)); |
68 | 0 | *new = *orig; |
69 | | |
70 | | /* |
71 | | * Set nexthop |
72 | | */ |
73 | 0 | switch (use_nexthop->family) { |
74 | 0 | case AF_INET: |
75 | 0 | new->nexthop = use_nexthop->u.prefix4; |
76 | 0 | new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ |
77 | 0 | new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); |
78 | 0 | break; |
79 | | |
80 | 0 | case AF_INET6: |
81 | 0 | new->mp_nexthop_global = use_nexthop->u.prefix6; |
82 | 0 | new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ |
83 | 0 | break; |
84 | | |
85 | 0 | default: |
86 | 0 | assert(0); |
87 | 0 | break; |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | * Set MED |
92 | | * |
93 | | * Note that it will be deleted when BGP sends to any eBGP |
94 | | * peer unless PEER_FLAG_MED_UNCHANGED is set: |
95 | | * |
96 | | * neighbor NEIGHBOR attribute-unchanged med |
97 | | */ |
98 | 0 | if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { |
99 | 0 | if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { |
100 | 0 | if (new->local_pref > 255) |
101 | 0 | new->med = 0; |
102 | 0 | else |
103 | 0 | new->med = 255 - new->local_pref; |
104 | 0 | } else { |
105 | 0 | new->med = 255; /* shouldn't happen */ |
106 | 0 | } |
107 | 0 | new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); |
108 | 0 | } |
109 | | |
110 | | /* |
111 | | * "new" is now a ghost attr: |
112 | | * - it owns an "extra" struct |
113 | | * - it owns any non-interned parts |
114 | | * - any references to interned parts are not counted |
115 | | * |
116 | | * Caller should, after using the attr, call: |
117 | | * - bgp_attr_flush() to free non-interned parts |
118 | | */ |
119 | 0 | } |
120 | | |
121 | | static int getce(struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce) |
122 | 0 | { |
123 | 0 | uint8_t *ecp; |
124 | 0 | uint32_t i; |
125 | 0 | uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin; |
126 | 0 | struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); |
127 | |
|
128 | 0 | for (ecp = ecomm->val, i = 0; i < ecomm->size; |
129 | 0 | ++i, ecp += ECOMMUNITY_SIZE) { |
130 | |
|
131 | 0 | if (VNC_DEBUG(EXPORT_BGP_GETCE)) { |
132 | 0 | vnc_zlog_debug_any( |
133 | 0 | "%s: %02x %02x %02x %02x %02x %02x %02x %02x", |
134 | 0 | __func__, ecp[0], ecp[1], ecp[2], ecp[3], |
135 | 0 | ecp[4], ecp[5], ecp[6], ecp[7]); |
136 | 0 | } |
137 | | |
138 | | /* |
139 | | * is it ROO? |
140 | | */ |
141 | 0 | if (ecp[0] != 1 || ecp[1] != 3) { |
142 | 0 | continue; |
143 | 0 | } |
144 | | |
145 | | /* |
146 | | * Match local admin value? |
147 | | */ |
148 | 0 | if (ecp[6] != ((localadmin & 0xff00) >> 8) |
149 | 0 | || ecp[7] != (localadmin & 0xff)) |
150 | 0 | continue; |
151 | | |
152 | 0 | memset((uint8_t *)pfx_ce, 0, sizeof(*pfx_ce)); |
153 | 0 | memcpy(&pfx_ce->u.prefix4, ecp + 2, 4); |
154 | 0 | pfx_ce->family = AF_INET; |
155 | 0 | pfx_ce->prefixlen = IPV4_MAX_BITLEN; |
156 | |
|
157 | 0 | return 0; |
158 | 0 | } |
159 | 0 | return -1; |
160 | 0 | } |
161 | | |
162 | | |
163 | | void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, |
164 | | struct bgp_path_info *bpi) |
165 | 0 | { |
166 | 0 | struct attr *attr = bpi->attr; |
167 | 0 | struct peer *peer = bpi->peer; |
168 | 0 | const struct prefix *prefix = agg_node_get_prefix(rn); |
169 | 0 | afi_t afi = family2afi(prefix->family); |
170 | 0 | struct bgp_dest *udest; |
171 | 0 | struct bgp_path_info *ubpi; |
172 | 0 | struct attr hattr; |
173 | 0 | struct attr *iattr; |
174 | 0 | struct prefix ce_nexthop; |
175 | 0 | struct prefix post_routemap_nexthop; |
176 | | |
177 | |
|
178 | 0 | if (!afi) { |
179 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", |
180 | 0 | __func__); |
181 | 0 | return; |
182 | 0 | } |
183 | | |
184 | 0 | if ((bpi->type != ZEBRA_ROUTE_BGP) |
185 | 0 | || (bpi->sub_type != BGP_ROUTE_NORMAL |
186 | 0 | && bpi->sub_type != BGP_ROUTE_RFP |
187 | 0 | && bpi->sub_type != BGP_ROUTE_STATIC)) { |
188 | |
|
189 | 0 | vnc_zlog_debug_verbose( |
190 | 0 | "%s: wrong route type/sub_type for export, skipping", |
191 | 0 | __func__); |
192 | 0 | return; |
193 | 0 | } |
194 | | |
195 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
196 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
197 | 0 | vnc_zlog_debug_verbose( |
198 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
199 | 0 | __func__); |
200 | 0 | return; |
201 | 0 | } |
202 | | |
203 | 0 | if (!bgp->rfapi_cfg) { |
204 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
205 | 0 | __func__); |
206 | 0 | return; |
207 | 0 | } |
208 | | |
209 | 0 | if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { |
210 | 0 | vnc_zlog_debug_verbose( |
211 | 0 | "%s: export-to-bgp ce mode not enabled, skipping", |
212 | 0 | __func__); |
213 | 0 | return; |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * prefix list check |
218 | | */ |
219 | 0 | if (bgp->rfapi_cfg->plist_export_bgp[afi]) { |
220 | 0 | if (prefix_list_apply(bgp->rfapi_cfg->plist_export_bgp[afi], |
221 | 0 | prefix) |
222 | 0 | == PREFIX_DENY) { |
223 | 0 | vnc_zlog_debug_verbose( |
224 | 0 | "%s: prefix list denied, skipping", __func__); |
225 | 0 | return; |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | |
230 | | /* |
231 | | * Extract CE |
232 | | * This works only for IPv4 because IPv6 addresses are too big |
233 | | * to fit in an extended community |
234 | | */ |
235 | 0 | if (getce(bgp, attr, &ce_nexthop)) { |
236 | 0 | vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping", |
237 | 0 | __func__); |
238 | 0 | return; |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * Is this route already represented in the unicast RIB? |
243 | | * (look up prefix; compare route type, sub_type, peer, nexthop) |
244 | | */ |
245 | 0 | udest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, |
246 | 0 | prefix, NULL); |
247 | 0 | for (ubpi = bgp_dest_get_bgp_path_info(udest); ubpi; |
248 | 0 | ubpi = ubpi->next) { |
249 | 0 | struct prefix unicast_nexthop; |
250 | |
|
251 | 0 | if (CHECK_FLAG(ubpi->flags, BGP_PATH_REMOVED)) |
252 | 0 | continue; |
253 | | |
254 | 0 | rfapiUnicastNexthop2Prefix(afi, ubpi->attr, &unicast_nexthop); |
255 | |
|
256 | 0 | if (ubpi->type == ZEBRA_ROUTE_VNC_DIRECT |
257 | 0 | && ubpi->sub_type == BGP_ROUTE_REDISTRIBUTE |
258 | 0 | && ubpi->peer == peer |
259 | 0 | && prefix_same(&unicast_nexthop, &ce_nexthop)) { |
260 | |
|
261 | 0 | vnc_zlog_debug_verbose( |
262 | 0 | "%s: already have matching exported unicast route, skipping", |
263 | 0 | __func__); |
264 | 0 | return; |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | /* |
269 | | * Construct new attribute set with CE addr as |
270 | | * nexthop and without Tunnel Encap attr |
271 | | */ |
272 | 0 | encap_attr_export_ce(&hattr, attr, &ce_nexthop); |
273 | 0 | if (bgp->rfapi_cfg->routemap_export_bgp) { |
274 | 0 | struct bgp_path_info info; |
275 | 0 | route_map_result_t ret; |
276 | |
|
277 | 0 | memset(&info, 0, sizeof(info)); |
278 | 0 | info.peer = peer; |
279 | 0 | info.attr = &hattr; |
280 | 0 | ret = route_map_apply(bgp->rfapi_cfg->routemap_export_bgp, |
281 | 0 | prefix, &info); |
282 | 0 | if (ret == RMAP_DENYMATCH) { |
283 | 0 | bgp_attr_flush(&hattr); |
284 | 0 | return; |
285 | 0 | } |
286 | 0 | } |
287 | | |
288 | 0 | iattr = bgp_attr_intern(&hattr); |
289 | 0 | bgp_attr_flush(&hattr); |
290 | | |
291 | | /* |
292 | | * Rule: disallow route-map alteration of next-hop, because it |
293 | | * would make it too difficult to keep track of the correspondence |
294 | | * between VPN routes and unicast routes. |
295 | | */ |
296 | 0 | rfapiUnicastNexthop2Prefix(afi, iattr, &post_routemap_nexthop); |
297 | |
|
298 | 0 | if (!prefix_same(&ce_nexthop, &post_routemap_nexthop)) { |
299 | 0 | vnc_zlog_debug_verbose( |
300 | 0 | "%s: route-map modification of nexthop not allowed, skipping", |
301 | 0 | __func__); |
302 | 0 | bgp_attr_unintern(&iattr); |
303 | 0 | return; |
304 | 0 | } |
305 | | |
306 | 0 | bgp_update(peer, prefix, 0, /* addpath_id */ |
307 | 0 | iattr, /* bgp_update copies this attr */ |
308 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
309 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ |
310 | 0 | NULL, 0, /* tag not used for unicast */ |
311 | 0 | 0, NULL); /* EVPN not used */ |
312 | 0 | bgp_attr_unintern(&iattr); |
313 | 0 | } |
314 | | |
315 | | |
316 | | /* |
317 | | * "Withdrawing a Route" export process |
318 | | */ |
319 | | void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, |
320 | | struct bgp_path_info *bpi) |
321 | 0 | { |
322 | 0 | const struct prefix *p = agg_node_get_prefix(rn); |
323 | 0 | afi_t afi = family2afi(p->family); |
324 | 0 | struct bgp_path_info *vbpi; |
325 | 0 | struct prefix ce_nexthop; |
326 | |
|
327 | 0 | if (!afi) { |
328 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi", __func__); |
329 | 0 | return; |
330 | 0 | } |
331 | | |
332 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
333 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
334 | 0 | vnc_zlog_debug_verbose( |
335 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
336 | 0 | __func__); |
337 | 0 | return; |
338 | 0 | } |
339 | | |
340 | 0 | if (!bgp->rfapi_cfg) { |
341 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
342 | 0 | __func__); |
343 | 0 | return; |
344 | 0 | } |
345 | 0 | if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { |
346 | 0 | vnc_zlog_debug_verbose( |
347 | 0 | "%s: export-to-bgp ce mode not enabled, skipping", |
348 | 0 | __func__); |
349 | 0 | return; |
350 | 0 | } |
351 | | |
352 | | /* |
353 | | * Extract CE |
354 | | * This works only for IPv4 because IPv6 addresses are too big |
355 | | * to fit in an extended community |
356 | | */ |
357 | 0 | if (getce(bgp, bpi->attr, &ce_nexthop)) { |
358 | 0 | vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping", |
359 | 0 | __func__); |
360 | 0 | return; |
361 | 0 | } |
362 | | |
363 | | /* |
364 | | * Look for other VPN routes with same prefix, same 5226 CE, |
365 | | * same peer. If at least one is present, don't remove the |
366 | | * route from the unicast RIB |
367 | | */ |
368 | | |
369 | 0 | for (vbpi = rn->info; vbpi; vbpi = vbpi->next) { |
370 | 0 | struct prefix ce; |
371 | 0 | if (bpi == vbpi) |
372 | 0 | continue; |
373 | 0 | if (bpi->peer != vbpi->peer) |
374 | 0 | continue; |
375 | 0 | if (getce(bgp, vbpi->attr, &ce)) |
376 | 0 | continue; |
377 | 0 | if (prefix_same(&ce, &ce_nexthop)) { |
378 | 0 | vnc_zlog_debug_verbose( |
379 | 0 | "%s: still have a route via CE, not deleting unicast", |
380 | 0 | __func__); |
381 | 0 | return; |
382 | 0 | } |
383 | 0 | } |
384 | | |
385 | | /* |
386 | | * withdraw the route |
387 | | */ |
388 | 0 | bgp_withdraw(bpi->peer, p, 0, /* addpath_id */ |
389 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
390 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ |
391 | 0 | NULL, 0, NULL); /* tag not used for unicast */ |
392 | 0 | } |
393 | | |
394 | | static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi) |
395 | 0 | { |
396 | 0 | struct agg_node *rn; |
397 | 0 | struct bgp_path_info *ri; |
398 | |
|
399 | 0 | vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); |
400 | |
|
401 | 0 | if (!bgp) |
402 | 0 | return; |
403 | | |
404 | 0 | if (!(bgp->rfapi_cfg)) |
405 | 0 | return; |
406 | | |
407 | 0 | if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { |
408 | 0 | vnc_zlog_debug_verbose( |
409 | 0 | "%s: export of CE routes not enabled, skipping", |
410 | 0 | __func__); |
411 | 0 | return; |
412 | 0 | } |
413 | | |
414 | 0 | if (afi != AFI_IP && afi != AFI_IP6) { |
415 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
416 | 0 | return; |
417 | 0 | } |
418 | | |
419 | | /* |
420 | | * Go through entire ce import table and export to BGP unicast. |
421 | | */ |
422 | 0 | for (rn = agg_route_top(bgp->rfapi->it_ce->imported_vpn[afi]); rn; |
423 | 0 | rn = agg_route_next(rn)) { |
424 | 0 | if (!rn->info) |
425 | 0 | continue; |
426 | | |
427 | 0 | vnc_zlog_debug_verbose("%s: checking prefix %pRN", __func__, |
428 | 0 | rn); |
429 | |
|
430 | 0 | for (ri = rn->info; ri; ri = ri->next) { |
431 | |
|
432 | 0 | vnc_zlog_debug_verbose("%s: ri->sub_type: %d", __func__, |
433 | 0 | ri->sub_type); |
434 | |
|
435 | 0 | if (ri->sub_type == BGP_ROUTE_NORMAL |
436 | 0 | || ri->sub_type == BGP_ROUTE_RFP |
437 | 0 | || ri->sub_type == BGP_ROUTE_STATIC) { |
438 | |
|
439 | 0 | vnc_direct_bgp_add_route_ce(bgp, rn, ri); |
440 | 0 | } |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) |
446 | 0 | { |
447 | 0 | struct bgp_dest *dest; |
448 | |
|
449 | 0 | vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); |
450 | |
|
451 | 0 | if (!bgp) |
452 | 0 | return; |
453 | | |
454 | 0 | if (afi != AFI_IP && afi != AFI_IP6) { |
455 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
456 | 0 | return; |
457 | 0 | } |
458 | | |
459 | | /* |
460 | | * Go through the entire BGP unicast table and remove routes that |
461 | | * originated from us |
462 | | */ |
463 | 0 | for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; |
464 | 0 | dest = bgp_route_next(dest)) { |
465 | |
|
466 | 0 | struct bgp_path_info *ri; |
467 | 0 | struct bgp_path_info *next; |
468 | |
|
469 | 0 | for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; |
470 | 0 | ri = next) { |
471 | |
|
472 | 0 | next = ri->next; |
473 | |
|
474 | 0 | if (ri->type == ZEBRA_ROUTE_VNC_DIRECT |
475 | 0 | && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { |
476 | |
|
477 | 0 | bgp_withdraw( |
478 | 0 | ri->peer, bgp_dest_get_prefix(dest), |
479 | 0 | 0, /* addpath_id */ |
480 | 0 | AFI_IP, SAFI_UNICAST, |
481 | 0 | ZEBRA_ROUTE_VNC_DIRECT, |
482 | 0 | BGP_ROUTE_REDISTRIBUTE, |
483 | 0 | NULL, /* RD not used for unicast */ |
484 | 0 | NULL, 0, |
485 | 0 | NULL); /* tag not used for unicast */ |
486 | 0 | } |
487 | 0 | } |
488 | 0 | } |
489 | 0 | } |
490 | | |
491 | | /*********************************************************************** |
492 | | * Export methods that set nexthop to CE (from 5226 roo EC) END |
493 | | ***********************************************************************/ |
494 | | |
495 | | /*********************************************************************** |
496 | | * Export methods that proxy nexthop BEGIN |
497 | | ***********************************************************************/ |
498 | | |
499 | | static struct ecommunity *vnc_route_origin_ecom(struct agg_node *rn) |
500 | 0 | { |
501 | 0 | struct ecommunity *new; |
502 | 0 | struct bgp_path_info *bpi; |
503 | |
|
504 | 0 | if (!rn->info) |
505 | 0 | return NULL; |
506 | | |
507 | 0 | new = ecommunity_new(); |
508 | |
|
509 | 0 | for (bpi = rn->info; bpi; bpi = bpi->next) { |
510 | |
|
511 | 0 | struct ecommunity_val roec; |
512 | |
|
513 | 0 | switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) { |
514 | 0 | case AF_INET: |
515 | 0 | memset(&roec, 0, sizeof(roec)); |
516 | 0 | roec.val[0] = 0x01; |
517 | 0 | roec.val[1] = 0x03; |
518 | 0 | memcpy(roec.val + 2, |
519 | 0 | &bpi->attr->mp_nexthop_global_in.s_addr, 4); |
520 | 0 | roec.val[6] = 0; |
521 | 0 | roec.val[7] = 0; |
522 | 0 | ecommunity_add_val(new, &roec, false, false); |
523 | 0 | break; |
524 | 0 | case AF_INET6: |
525 | | /* No support for IPv6 addresses in extended communities |
526 | | */ |
527 | 0 | break; |
528 | 0 | } |
529 | 0 | } |
530 | | |
531 | 0 | if (!new->size) { |
532 | 0 | ecommunity_free(&new); |
533 | 0 | new = NULL; |
534 | 0 | } |
535 | |
|
536 | 0 | return new; |
537 | 0 | } |
538 | | |
539 | | static struct ecommunity *vnc_route_origin_ecom_single(struct in_addr *origin) |
540 | 0 | { |
541 | 0 | struct ecommunity *new; |
542 | 0 | struct ecommunity_val roec; |
543 | |
|
544 | 0 | memset(&roec, 0, sizeof(roec)); |
545 | 0 | roec.val[0] = 0x01; |
546 | 0 | roec.val[1] = 0x03; |
547 | 0 | memcpy(roec.val + 2, &origin->s_addr, 4); |
548 | 0 | roec.val[6] = 0; |
549 | 0 | roec.val[7] = 0; |
550 | |
|
551 | 0 | new = ecommunity_new(); |
552 | 0 | ecommunity_add_val(new, &roec, false, false); |
553 | |
|
554 | 0 | if (!new->size) { |
555 | 0 | ecommunity_free(&new); |
556 | 0 | new = NULL; |
557 | 0 | } |
558 | |
|
559 | 0 | return new; |
560 | 0 | } |
561 | | |
562 | | |
563 | | /* |
564 | | * New memory allocation approach: make a ghost attr that |
565 | | * has non-interned parts for the modifications. ghost attr |
566 | | * memory is allocated by caller. |
567 | | */ |
568 | | static int |
569 | | encap_attr_export(struct attr *new, struct attr *orig, |
570 | | struct prefix *new_nexthop, |
571 | | struct agg_node *rn) /* for VN addrs for ecom list */ |
572 | | /* if rn is 0, use route's nexthop */ |
573 | 0 | { |
574 | 0 | struct prefix orig_nexthop; |
575 | 0 | struct prefix *use_nexthop; |
576 | 0 | static struct ecommunity *ecom_ro; |
577 | |
|
578 | 0 | if (new_nexthop) { |
579 | 0 | use_nexthop = new_nexthop; |
580 | 0 | } else { |
581 | 0 | use_nexthop = &orig_nexthop; |
582 | 0 | orig_nexthop.family = |
583 | 0 | BGP_MP_NEXTHOP_FAMILY(orig->mp_nexthop_len); |
584 | 0 | if (orig_nexthop.family == AF_INET) { |
585 | 0 | orig_nexthop.prefixlen = IPV4_MAX_BITLEN; |
586 | 0 | orig_nexthop.u.prefix4 = orig->mp_nexthop_global_in; |
587 | 0 | } else if (orig_nexthop.family == AF_INET6) { |
588 | 0 | orig_nexthop.prefixlen = IPV6_MAX_BITLEN; |
589 | 0 | orig_nexthop.u.prefix6 = orig->mp_nexthop_global; |
590 | 0 | } else { |
591 | 0 | return -1; /* FAIL - can't compute nexthop */ |
592 | 0 | } |
593 | 0 | } |
594 | | |
595 | | |
596 | | /* |
597 | | * Make "new" a ghost attr copy of "orig" |
598 | | */ |
599 | 0 | memset(new, 0, sizeof(struct attr)); |
600 | 0 | *new = *orig; |
601 | | |
602 | | /* |
603 | | * Set nexthop |
604 | | */ |
605 | 0 | switch (use_nexthop->family) { |
606 | 0 | case AF_INET: |
607 | 0 | new->nexthop = use_nexthop->u.prefix4; |
608 | 0 | new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ |
609 | 0 | new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); |
610 | 0 | break; |
611 | | |
612 | 0 | case AF_INET6: |
613 | 0 | new->mp_nexthop_global = use_nexthop->u.prefix6; |
614 | 0 | new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ |
615 | 0 | break; |
616 | | |
617 | 0 | default: |
618 | 0 | assert(0); |
619 | 0 | break; |
620 | 0 | } |
621 | | |
622 | 0 | if (rn) { |
623 | 0 | ecom_ro = vnc_route_origin_ecom(rn); |
624 | 0 | } else { |
625 | | /* TBD use lcom for IPv6 */ |
626 | 0 | ecom_ro = vnc_route_origin_ecom_single(&use_nexthop->u.prefix4); |
627 | 0 | } |
628 | 0 | if (bgp_attr_get_ecommunity(new)) { |
629 | 0 | if (ecom_ro) |
630 | 0 | bgp_attr_set_ecommunity( |
631 | 0 | new, |
632 | 0 | ecommunity_merge(ecom_ro, |
633 | 0 | bgp_attr_get_ecommunity(new))); |
634 | 0 | } else { |
635 | 0 | bgp_attr_set_ecommunity(new, ecom_ro); |
636 | 0 | } |
637 | | |
638 | | /* |
639 | | * Set MED |
640 | | * |
641 | | * Note that it will be deleted when BGP sends to any eBGP |
642 | | * peer unless PEER_FLAG_MED_UNCHANGED is set: |
643 | | * |
644 | | * neighbor NEIGHBOR attribute-unchanged med |
645 | | */ |
646 | 0 | if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { |
647 | 0 | if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { |
648 | 0 | if (new->local_pref > 255) |
649 | 0 | new->med = 0; |
650 | 0 | else |
651 | 0 | new->med = 255 - new->local_pref; |
652 | 0 | } else { |
653 | 0 | new->med = 255; /* shouldn't happen */ |
654 | 0 | } |
655 | 0 | new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); |
656 | 0 | } |
657 | | |
658 | | /* |
659 | | * "new" is now a ghost attr: |
660 | | * - it owns an "extra" struct |
661 | | * - it owns any non-interned parts |
662 | | * - any references to interned parts are not counted |
663 | | * |
664 | | * Caller should, after using the attr, call: |
665 | | * - bgp_attr_flush() to free non-interned parts |
666 | | */ |
667 | |
|
668 | 0 | return 0; |
669 | 0 | } |
670 | | |
671 | | /* |
672 | | * "Adding a Route" export process |
673 | | */ |
674 | | void vnc_direct_bgp_add_prefix(struct bgp *bgp, |
675 | | struct rfapi_import_table *import_table, |
676 | | struct agg_node *rn) |
677 | 0 | { |
678 | 0 | struct attr attr = {0}; |
679 | 0 | struct listnode *node, *nnode; |
680 | 0 | struct rfapi_rfg_name *rfgn; |
681 | 0 | const struct prefix *p = agg_node_get_prefix(rn); |
682 | 0 | afi_t afi = family2afi(p->family); |
683 | |
|
684 | 0 | if (!afi) { |
685 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", |
686 | 0 | __func__); |
687 | 0 | return; |
688 | 0 | } |
689 | | |
690 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
691 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
692 | 0 | vnc_zlog_debug_verbose( |
693 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
694 | 0 | __func__); |
695 | 0 | return; |
696 | 0 | } |
697 | | |
698 | 0 | if (!bgp->rfapi_cfg) { |
699 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
700 | 0 | __func__); |
701 | 0 | return; |
702 | 0 | } |
703 | | |
704 | 0 | if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
705 | 0 | vnc_zlog_debug_verbose( |
706 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
707 | 0 | __func__); |
708 | 0 | return; |
709 | 0 | } |
710 | | |
711 | 0 | if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) { |
712 | 0 | vnc_zlog_debug_verbose( |
713 | 0 | "%s: no bgp-direct export nve group, skipping", |
714 | 0 | __func__); |
715 | 0 | return; |
716 | 0 | } |
717 | | |
718 | 0 | bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); |
719 | | /* TBD set some configured med, see add_vnc_route() */ |
720 | |
|
721 | 0 | vnc_zlog_debug_verbose( |
722 | 0 | "%s: looping over nve-groups in direct-bgp export list", |
723 | 0 | __func__); |
724 | |
|
725 | 0 | for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
726 | 0 | nnode, rfgn)) { |
727 | |
|
728 | 0 | struct listnode *ln; |
729 | | |
730 | | /* |
731 | | * If nve group is not defined yet, skip it |
732 | | */ |
733 | 0 | if (!rfgn->rfg) |
734 | 0 | continue; |
735 | | |
736 | | /* |
737 | | * If the nve group uses a different import table, skip it |
738 | | */ |
739 | 0 | if (import_table != rfgn->rfg->rfapi_import_table) |
740 | 0 | continue; |
741 | | |
742 | | /* |
743 | | * if no NVEs currently associated with this group, skip it |
744 | | */ |
745 | 0 | if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves) |
746 | 0 | continue; |
747 | | |
748 | | /* |
749 | | * per-nve-group prefix list check |
750 | | */ |
751 | 0 | if (rfgn->rfg->plist_export_bgp[afi]) { |
752 | 0 | if (prefix_list_apply(rfgn->rfg->plist_export_bgp[afi], |
753 | 0 | p) |
754 | 0 | == PREFIX_DENY) |
755 | | |
756 | 0 | continue; |
757 | 0 | } |
758 | | |
759 | 0 | if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { |
760 | 0 | vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr, |
761 | 0 | afi, rfgn->rfg->rfd); |
762 | | /* |
763 | | * yuck! |
764 | | * - but consistent with rest of function |
765 | | */ |
766 | 0 | continue; |
767 | 0 | } |
768 | | /* |
769 | | * For each NVE that is assigned to the export nve group, |
770 | | * generate |
771 | | * a route with that NVE as its next hop |
772 | | */ |
773 | 0 | for (ln = listhead(rfgn->rfg->nves); ln; |
774 | 0 | ln = listnextnode(ln)) { |
775 | 0 | vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr, |
776 | 0 | afi, listgetdata(ln)); |
777 | 0 | } |
778 | 0 | } |
779 | | |
780 | 0 | aspath_unintern(&attr.aspath); |
781 | 0 | } |
782 | | |
783 | | /* |
784 | | * "Withdrawing a Route" export process |
785 | | */ |
786 | | void vnc_direct_bgp_del_prefix(struct bgp *bgp, |
787 | | struct rfapi_import_table *import_table, |
788 | | struct agg_node *rn) |
789 | 0 | { |
790 | 0 | struct listnode *node, *nnode; |
791 | 0 | struct rfapi_rfg_name *rfgn; |
792 | 0 | const struct prefix *p = agg_node_get_prefix(rn); |
793 | 0 | afi_t afi = family2afi(p->family); |
794 | |
|
795 | 0 | if (!afi) { |
796 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node", |
797 | 0 | __func__); |
798 | 0 | return; |
799 | 0 | } |
800 | | |
801 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
802 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
803 | 0 | vnc_zlog_debug_verbose( |
804 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
805 | 0 | __func__); |
806 | 0 | return; |
807 | 0 | } |
808 | | |
809 | 0 | if (!bgp->rfapi_cfg) { |
810 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
811 | 0 | __func__); |
812 | 0 | return; |
813 | 0 | } |
814 | | |
815 | 0 | if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
816 | 0 | vnc_zlog_debug_verbose( |
817 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
818 | 0 | __func__); |
819 | 0 | return; |
820 | 0 | } |
821 | | |
822 | 0 | if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) { |
823 | 0 | vnc_zlog_debug_verbose( |
824 | 0 | "%s: no bgp-direct export nve group, skipping", |
825 | 0 | __func__); |
826 | 0 | return; |
827 | 0 | } |
828 | | |
829 | 0 | for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
830 | 0 | nnode, rfgn)) { |
831 | |
|
832 | 0 | struct listnode *ln; |
833 | | |
834 | | /* |
835 | | * If nve group is not defined yet, skip it |
836 | | */ |
837 | 0 | if (!rfgn->rfg) |
838 | 0 | continue; |
839 | | |
840 | | /* |
841 | | * if no NVEs currently associated with this group, skip it |
842 | | */ |
843 | 0 | if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves) |
844 | 0 | continue; |
845 | | |
846 | | /* |
847 | | * If the nve group uses a different import table, |
848 | | * skip it |
849 | | */ |
850 | 0 | if (import_table != rfgn->rfg->rfapi_import_table) |
851 | 0 | continue; |
852 | | |
853 | 0 | if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { |
854 | 0 | struct prefix nhp; |
855 | 0 | struct rfapi_descriptor *irfd; |
856 | |
|
857 | 0 | irfd = rfgn->rfg->rfd; |
858 | |
|
859 | 0 | if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) |
860 | 0 | continue; |
861 | | |
862 | 0 | bgp_withdraw(irfd->peer, p, /* prefix */ |
863 | 0 | 0, /* addpath_id */ |
864 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
865 | 0 | BGP_ROUTE_REDISTRIBUTE, |
866 | 0 | NULL, /* RD not used for unicast */ |
867 | 0 | NULL, 0, |
868 | 0 | NULL); /* tag not used for unicast */ |
869 | | /* |
870 | | * yuck! |
871 | | * - but consistent with rest of function |
872 | | */ |
873 | 0 | continue; |
874 | 0 | } |
875 | | /* |
876 | | * For each NVE that is assigned to the export nve group, |
877 | | * generate |
878 | | * a route with that NVE as its next hop |
879 | | */ |
880 | 0 | for (ln = listhead(rfgn->rfg->nves); ln; |
881 | 0 | ln = listnextnode(ln)) { |
882 | |
|
883 | 0 | struct prefix nhp; |
884 | 0 | struct rfapi_descriptor *irfd; |
885 | |
|
886 | 0 | irfd = listgetdata(ln); |
887 | |
|
888 | 0 | if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) |
889 | 0 | continue; |
890 | | |
891 | 0 | bgp_withdraw(irfd->peer, p, /* prefix */ |
892 | 0 | 0, /* addpath_id */ |
893 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
894 | 0 | BGP_ROUTE_REDISTRIBUTE, |
895 | 0 | NULL, /* RD not used for unicast */ |
896 | 0 | NULL, 0, |
897 | 0 | NULL); /* tag not used for unicast */ |
898 | 0 | } |
899 | 0 | } |
900 | 0 | } |
901 | | |
902 | | void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) |
903 | 0 | { |
904 | 0 | struct listnode *node, *nnode; |
905 | 0 | struct rfapi_rfg_name *rfgn; |
906 | 0 | struct rfapi_nve_group_cfg *rfg = rfd->rfg; |
907 | 0 | afi_t afi = family2afi(rfd->vn_addr.addr_family); |
908 | |
|
909 | 0 | if (!afi) { |
910 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr", |
911 | 0 | __func__); |
912 | 0 | return; |
913 | 0 | } |
914 | | |
915 | 0 | if (!bgp) |
916 | 0 | return; |
917 | 0 | if (!bgp->rfapi_cfg) { |
918 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
919 | 0 | __func__); |
920 | 0 | return; |
921 | 0 | } |
922 | 0 | if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
923 | 0 | vnc_zlog_debug_verbose( |
924 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
925 | 0 | __func__); |
926 | 0 | return; |
927 | 0 | } |
928 | | |
929 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
930 | 0 | vnc_zlog_debug_verbose( |
931 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
932 | 0 | __func__); |
933 | 0 | return; |
934 | 0 | } |
935 | | |
936 | | /* |
937 | | * Loop over the list of NVE-Groups configured for |
938 | | * exporting to direct-bgp and see if this new NVE's |
939 | | * group is among them. |
940 | | */ |
941 | 0 | for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
942 | 0 | nnode, rfgn)) { |
943 | | |
944 | | /* |
945 | | * Yes, this NVE's group is configured for export to direct-bgp |
946 | | */ |
947 | 0 | if (rfgn->rfg == rfg) { |
948 | |
|
949 | 0 | struct agg_table *rt = NULL; |
950 | 0 | struct agg_node *rn; |
951 | 0 | struct attr attr = {0}; |
952 | 0 | struct rfapi_import_table *import_table; |
953 | | |
954 | |
|
955 | 0 | import_table = rfg->rfapi_import_table; |
956 | |
|
957 | 0 | if (afi == AFI_IP || afi == AFI_IP6) { |
958 | 0 | rt = import_table->imported_vpn[afi]; |
959 | 0 | } else { |
960 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", |
961 | 0 | __func__, afi); |
962 | 0 | return; |
963 | 0 | } |
964 | | |
965 | 0 | bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); |
966 | | /* TBD set some configured med, see add_vnc_route() */ |
967 | | |
968 | | /* |
969 | | * Walk the NVE-Group's VNC Import table |
970 | | */ |
971 | 0 | for (rn = agg_route_top(rt); rn; |
972 | 0 | rn = agg_route_next(rn)) { |
973 | |
|
974 | 0 | if (rn->info) { |
975 | |
|
976 | 0 | struct prefix nhp; |
977 | 0 | struct rfapi_descriptor *irfd = rfd; |
978 | 0 | struct attr hattr; |
979 | 0 | struct attr *iattr; |
980 | 0 | struct bgp_path_info info; |
981 | 0 | const struct prefix *p = |
982 | 0 | agg_node_get_prefix(rn); |
983 | |
|
984 | 0 | if (rfapiRaddr2Qprefix(&irfd->vn_addr, |
985 | 0 | &nhp)) |
986 | 0 | continue; |
987 | | |
988 | | /* |
989 | | * per-nve-group prefix list check |
990 | | */ |
991 | 0 | if (rfgn->rfg->plist_export_bgp[afi]) { |
992 | 0 | if (prefix_list_apply( |
993 | 0 | rfgn->rfg->plist_export_bgp |
994 | 0 | [afi], |
995 | 0 | p) |
996 | 0 | == PREFIX_DENY) |
997 | | |
998 | 0 | continue; |
999 | 0 | } |
1000 | | |
1001 | | |
1002 | | /* |
1003 | | * Construct new attribute set with |
1004 | | * NVE's VN addr as |
1005 | | * nexthop and without Tunnel Encap attr |
1006 | | */ |
1007 | 0 | if (encap_attr_export(&hattr, &attr, |
1008 | 0 | &nhp, rn)) |
1009 | 0 | continue; |
1010 | | |
1011 | 0 | if (rfgn->rfg->routemap_export_bgp) { |
1012 | 0 | route_map_result_t ret; |
1013 | 0 | info.peer = irfd->peer; |
1014 | 0 | info.attr = &hattr; |
1015 | 0 | ret = route_map_apply( |
1016 | 0 | rfgn->rfg |
1017 | 0 | ->routemap_export_bgp, |
1018 | 0 | p, &info); |
1019 | 0 | if (ret == RMAP_DENYMATCH) { |
1020 | 0 | bgp_attr_flush(&hattr); |
1021 | 0 | continue; |
1022 | 0 | } |
1023 | 0 | } |
1024 | | |
1025 | 0 | iattr = bgp_attr_intern(&hattr); |
1026 | 0 | bgp_attr_flush(&hattr); |
1027 | 0 | bgp_update( |
1028 | 0 | irfd->peer, p, /* prefix */ |
1029 | 0 | 0, /* addpath_id */ |
1030 | 0 | iattr, /* bgp_update copies |
1031 | | it */ |
1032 | 0 | afi, SAFI_UNICAST, |
1033 | 0 | ZEBRA_ROUTE_VNC_DIRECT, |
1034 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, |
1035 | | /* RD not used for unicast */ |
1036 | 0 | NULL, |
1037 | | /* tag not used for unicast */ |
1038 | 0 | 0, 0, NULL); /* EVPN not used */ |
1039 | |
|
1040 | 0 | bgp_attr_unintern(&iattr); |
1041 | 0 | } |
1042 | 0 | } |
1043 | |
|
1044 | 0 | aspath_unintern(&attr.aspath); |
1045 | 0 | } |
1046 | 0 | } |
1047 | 0 | } |
1048 | | |
1049 | | |
1050 | | void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) |
1051 | 0 | { |
1052 | 0 | struct listnode *node, *nnode; |
1053 | 0 | struct rfapi_rfg_name *rfgn; |
1054 | 0 | struct rfapi_nve_group_cfg *rfg = rfd->rfg; |
1055 | 0 | afi_t afi = family2afi(rfd->vn_addr.addr_family); |
1056 | |
|
1057 | 0 | if (!afi) { |
1058 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr", |
1059 | 0 | __func__); |
1060 | 0 | return; |
1061 | 0 | } |
1062 | | |
1063 | 0 | if (!bgp) |
1064 | 0 | return; |
1065 | 0 | if (!bgp->rfapi_cfg) { |
1066 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
1067 | 0 | __func__); |
1068 | 0 | return; |
1069 | 0 | } |
1070 | 0 | if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
1071 | 0 | vnc_zlog_debug_verbose( |
1072 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
1073 | 0 | __func__); |
1074 | 0 | return; |
1075 | 0 | } |
1076 | | |
1077 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
1078 | 0 | vnc_zlog_debug_verbose( |
1079 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
1080 | 0 | __func__); |
1081 | 0 | return; |
1082 | 0 | } |
1083 | | |
1084 | | /* |
1085 | | * Loop over the list of NVE-Groups configured for |
1086 | | * exporting to direct-bgp and see if this new NVE's |
1087 | | * group is among them. |
1088 | | */ |
1089 | 0 | for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
1090 | 0 | nnode, rfgn)) { |
1091 | | |
1092 | | /* |
1093 | | * Yes, this NVE's group is configured for export to direct-bgp |
1094 | | */ |
1095 | 0 | if (rfg && rfgn->rfg == rfg) { |
1096 | |
|
1097 | 0 | struct agg_table *rt = NULL; |
1098 | 0 | struct agg_node *rn; |
1099 | 0 | struct rfapi_import_table *import_table; |
1100 | |
|
1101 | 0 | import_table = rfg->rfapi_import_table; |
1102 | |
|
1103 | 0 | if (afi == AFI_IP || afi == AFI_IP6) { |
1104 | 0 | rt = import_table->imported_vpn[afi]; |
1105 | 0 | } else { |
1106 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", |
1107 | 0 | __func__, afi); |
1108 | 0 | return; |
1109 | 0 | } |
1110 | | |
1111 | | /* |
1112 | | * Walk the NVE-Group's VNC Import table |
1113 | | */ |
1114 | 0 | for (rn = agg_route_top(rt); rn; |
1115 | 0 | rn = agg_route_next(rn)) { |
1116 | |
|
1117 | 0 | if (rn->info) { |
1118 | 0 | const struct prefix *p = |
1119 | 0 | agg_node_get_prefix(rn); |
1120 | 0 | struct prefix nhp; |
1121 | 0 | struct rfapi_descriptor *irfd = rfd; |
1122 | |
|
1123 | 0 | if (rfapiRaddr2Qprefix(&irfd->vn_addr, |
1124 | 0 | &nhp)) |
1125 | 0 | continue; |
1126 | | |
1127 | 0 | bgp_withdraw(irfd->peer, p, /* prefix */ |
1128 | 0 | 0, /* addpath_id */ |
1129 | 0 | afi, SAFI_UNICAST, |
1130 | 0 | ZEBRA_ROUTE_VNC_DIRECT, |
1131 | 0 | BGP_ROUTE_REDISTRIBUTE, |
1132 | 0 | NULL, /* RD not used for |
1133 | | unicast */ |
1134 | 0 | NULL, 0, NULL); /* tag not |
1135 | | used for |
1136 | | unicast */ |
1137 | 0 | } |
1138 | 0 | } |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | } |
1142 | | |
1143 | | static void vnc_direct_add_rn_group_rd(struct bgp *bgp, |
1144 | | struct rfapi_nve_group_cfg *rfg, |
1145 | | struct agg_node *rn, struct attr *attr, |
1146 | | afi_t afi, struct rfapi_descriptor *irfd) |
1147 | 0 | { |
1148 | 0 | struct prefix nhp; |
1149 | 0 | struct bgp_path_info info; |
1150 | 0 | struct attr hattr; |
1151 | 0 | struct attr *iattr; |
1152 | 0 | const struct prefix *p = agg_node_get_prefix(rn); |
1153 | |
|
1154 | 0 | if (irfd == NULL && rfg->type != RFAPI_GROUP_CFG_VRF) { |
1155 | | /* need new rfapi_handle, for peer strcture |
1156 | | * -- based on vnc_add_vrf_prefi */ |
1157 | 0 | assert(rfg->rfd == NULL); |
1158 | |
|
1159 | 0 | if (!rfg->rt_export_list || !rfg->rfapi_import_table) { |
1160 | 0 | vnc_zlog_debug_verbose( |
1161 | 0 | "%s: VRF \"%s\" is missing RT import/export configuration.", |
1162 | 0 | __func__, rfg->name); |
1163 | 0 | return; |
1164 | 0 | } |
1165 | 0 | if (!rfg->rd.prefixlen) { |
1166 | 0 | vnc_zlog_debug_verbose( |
1167 | 0 | "%s: VRF \"%s\" is missing RD configuration.", |
1168 | 0 | __func__, rfg->name); |
1169 | 0 | return; |
1170 | 0 | } |
1171 | 0 | if (rfg->label > MPLS_LABEL_MAX) { |
1172 | 0 | vnc_zlog_debug_verbose( |
1173 | 0 | "%s: VRF \"%s\" is missing default label configuration.", |
1174 | 0 | __func__, rfg->name); |
1175 | 0 | return; |
1176 | 0 | } |
1177 | | |
1178 | 0 | irfd = XCALLOC(MTYPE_RFAPI_DESC, |
1179 | 0 | sizeof(struct rfapi_descriptor)); |
1180 | 0 | irfd->bgp = bgp; |
1181 | 0 | rfg->rfd = irfd; |
1182 | | /* |
1183 | | * leave most fields empty as will get from (dynamic) config |
1184 | | * when needed |
1185 | | */ |
1186 | 0 | irfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS; |
1187 | 0 | irfd->cookie = rfg; |
1188 | 0 | if (rfg->vn_prefix.family |
1189 | 0 | && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) { |
1190 | 0 | rfapiQprefix2Raddr(&rfg->vn_prefix, &irfd->vn_addr); |
1191 | 0 | } else { |
1192 | 0 | memset(&irfd->vn_addr, 0, sizeof(struct rfapi_ip_addr)); |
1193 | 0 | irfd->vn_addr.addr_family = AF_INET; |
1194 | 0 | irfd->vn_addr.addr.v4 = bgp->router_id; |
1195 | 0 | } |
1196 | 0 | irfd->un_addr = irfd->vn_addr; /* sigh, need something in UN for |
1197 | | lookups */ |
1198 | 0 | vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__, |
1199 | 0 | rfg->name); |
1200 | 0 | rfapi_init_and_open(bgp, irfd, rfg); |
1201 | 0 | } |
1202 | | |
1203 | 0 | if (irfd == NULL || rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) |
1204 | 0 | return; |
1205 | | |
1206 | | /* |
1207 | | * Construct new attribute set with NVE's VN |
1208 | | * addr as |
1209 | | * nexthop and without Tunnel Encap attr |
1210 | | */ |
1211 | 0 | if (encap_attr_export(&hattr, attr, &nhp, rn)) |
1212 | 0 | return; |
1213 | | |
1214 | 0 | if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) { |
1215 | 0 | vnc_zlog_debug_any("%s: attr follows", __func__); |
1216 | 0 | rfapiPrintAttrPtrs(NULL, attr); |
1217 | 0 | vnc_zlog_debug_any("%s: hattr follows", __func__); |
1218 | 0 | rfapiPrintAttrPtrs(NULL, &hattr); |
1219 | 0 | } |
1220 | |
|
1221 | 0 | if (rfg->routemap_export_bgp) { |
1222 | 0 | route_map_result_t ret; |
1223 | |
|
1224 | 0 | info.peer = irfd->peer; |
1225 | 0 | info.attr = &hattr; |
1226 | 0 | ret = route_map_apply(rfg->routemap_export_bgp, p, &info); |
1227 | 0 | if (ret == RMAP_DENYMATCH) { |
1228 | 0 | bgp_attr_flush(&hattr); |
1229 | 0 | vnc_zlog_debug_verbose( |
1230 | 0 | "%s: route map says DENY, so not calling bgp_update", |
1231 | 0 | __func__); |
1232 | 0 | return; |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | 0 | if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) { |
1237 | 0 | vnc_zlog_debug_any("%s: hattr after route_map_apply:", |
1238 | 0 | __func__); |
1239 | 0 | rfapiPrintAttrPtrs(NULL, &hattr); |
1240 | 0 | } |
1241 | 0 | iattr = bgp_attr_intern(&hattr); |
1242 | 0 | bgp_attr_flush(&hattr); |
1243 | |
|
1244 | 0 | bgp_update(irfd->peer, p, /* prefix */ |
1245 | 0 | 0, /* addpath_id */ |
1246 | 0 | iattr, /* bgp_update copies it */ |
1247 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
1248 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ |
1249 | 0 | NULL, /* tag not used for unicast */ |
1250 | 0 | 0, 0, NULL); /* EVPN not used */ |
1251 | |
|
1252 | 0 | bgp_attr_unintern(&iattr); |
1253 | |
|
1254 | 0 | return; |
1255 | 0 | } |
1256 | | |
1257 | | /* |
1258 | | * Caller is responsible for ensuring that the specified nve-group |
1259 | | * is actually part of the list of exported nve groups. |
1260 | | */ |
1261 | | static void vnc_direct_bgp_add_group_afi(struct bgp *bgp, |
1262 | | struct rfapi_nve_group_cfg *rfg, |
1263 | | afi_t afi) |
1264 | 0 | { |
1265 | 0 | struct agg_table *rt = NULL; |
1266 | 0 | struct agg_node *rn; |
1267 | 0 | struct attr attr = {0}; |
1268 | 0 | struct rfapi_import_table *import_table; |
1269 | |
|
1270 | 0 | vnc_zlog_debug_verbose("%s: entry", __func__); |
1271 | |
|
1272 | 0 | import_table = rfg->rfapi_import_table; |
1273 | 0 | if (!import_table) { |
1274 | 0 | vnc_zlog_debug_verbose( |
1275 | 0 | "%s: import table not defined, returning", __func__); |
1276 | 0 | return; |
1277 | 0 | } |
1278 | | |
1279 | 0 | if (afi == AFI_IP || afi == AFI_IP6) { |
1280 | 0 | rt = import_table->imported_vpn[afi]; |
1281 | 0 | } else { |
1282 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi); |
1283 | 0 | return; |
1284 | 0 | } |
1285 | | |
1286 | 0 | if (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) { |
1287 | 0 | vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__); |
1288 | 0 | return; |
1289 | 0 | } |
1290 | | |
1291 | 0 | bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); |
1292 | | /* TBD set some configured med, see add_vnc_route() */ |
1293 | | |
1294 | | /* |
1295 | | * Walk the NVE-Group's VNC Import table |
1296 | | */ |
1297 | 0 | for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { |
1298 | |
|
1299 | 0 | if (rn->info) { |
1300 | 0 | const struct prefix *p = agg_node_get_prefix(rn); |
1301 | 0 | struct listnode *ln; |
1302 | | |
1303 | | /* |
1304 | | * per-nve-group prefix list check |
1305 | | */ |
1306 | 0 | if (rfg->plist_export_bgp[afi]) { |
1307 | 0 | if (prefix_list_apply( |
1308 | 0 | rfg->plist_export_bgp[afi], p) |
1309 | 0 | == PREFIX_DENY) |
1310 | | |
1311 | 0 | continue; |
1312 | 0 | } |
1313 | 0 | if (rfg->type == RFAPI_GROUP_CFG_VRF) { |
1314 | 0 | vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr, |
1315 | 0 | afi, rfg->rfd); |
1316 | | /* |
1317 | | * yuck! |
1318 | | * - but consistent with rest of function |
1319 | | */ |
1320 | 0 | continue; |
1321 | 0 | } |
1322 | | /* |
1323 | | * For each NVE that is assigned to the export nve |
1324 | | * group, generate |
1325 | | * a route with that NVE as its next hop |
1326 | | */ |
1327 | 0 | for (ln = listhead(rfg->nves); ln; |
1328 | 0 | ln = listnextnode(ln)) { |
1329 | 0 | vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr, |
1330 | 0 | afi, |
1331 | 0 | listgetdata(ln)); |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | } |
1335 | | |
1336 | 0 | aspath_unintern(&attr.aspath); |
1337 | 0 | } |
1338 | | |
1339 | | |
1340 | | /* |
1341 | | * Caller is responsible for ensuring that the specified nve-group |
1342 | | * is actually part of the list of exported nve groups. |
1343 | | */ |
1344 | | void vnc_direct_bgp_add_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) |
1345 | 0 | { |
1346 | 0 | vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP); |
1347 | 0 | vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP6); |
1348 | 0 | } |
1349 | | |
1350 | | static void vnc_direct_del_rn_group_rd(struct bgp *bgp, |
1351 | | struct rfapi_nve_group_cfg *rfg, |
1352 | | struct agg_node *rn, afi_t afi, |
1353 | | struct rfapi_descriptor *irfd) |
1354 | 0 | { |
1355 | 0 | if (irfd == NULL) |
1356 | 0 | return; |
1357 | | |
1358 | 0 | bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), /* prefix */ |
1359 | 0 | 0, /* addpath_id */ |
1360 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, |
1361 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ |
1362 | 0 | NULL, 0, NULL); /* tag not used for unicast */ |
1363 | 0 | return; |
1364 | 0 | } |
1365 | | |
1366 | | /* |
1367 | | * Caller is responsible for ensuring that the specified nve-group |
1368 | | * was actually part of the list of exported nve groups. |
1369 | | */ |
1370 | | static void vnc_direct_bgp_del_group_afi(struct bgp *bgp, |
1371 | | struct rfapi_nve_group_cfg *rfg, |
1372 | | afi_t afi) |
1373 | 0 | { |
1374 | 0 | struct agg_table *rt = NULL; |
1375 | 0 | struct agg_node *rn; |
1376 | 0 | struct rfapi_import_table *import_table; |
1377 | |
|
1378 | 0 | vnc_zlog_debug_verbose("%s: entry", __func__); |
1379 | |
|
1380 | 0 | import_table = rfg->rfapi_import_table; |
1381 | 0 | if (!import_table) { |
1382 | 0 | vnc_zlog_debug_verbose( |
1383 | 0 | "%s: import table not defined, returning", __func__); |
1384 | 0 | return; |
1385 | 0 | } |
1386 | | |
1387 | 0 | rt = import_table->imported_vpn[afi]; |
1388 | |
|
1389 | 0 | if (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) { |
1390 | 0 | vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__); |
1391 | 0 | return; |
1392 | 0 | } |
1393 | | |
1394 | | /* |
1395 | | * Walk the NVE-Group's VNC Import table |
1396 | | */ |
1397 | 0 | for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) |
1398 | 0 | if (rn->info) { |
1399 | 0 | if (rfg->type == RFAPI_GROUP_CFG_VRF) |
1400 | 0 | vnc_direct_del_rn_group_rd(bgp, rfg, rn, afi, |
1401 | 0 | rfg->rfd); |
1402 | 0 | else { |
1403 | 0 | struct listnode *ln; |
1404 | | |
1405 | | /* |
1406 | | * For each NVE that is assigned to the export |
1407 | | * nve |
1408 | | * group, generate |
1409 | | * a route with that NVE as its next hop |
1410 | | */ |
1411 | 0 | for (ln = listhead(rfg->nves); ln; |
1412 | 0 | ln = listnextnode(ln)) |
1413 | 0 | vnc_direct_del_rn_group_rd( |
1414 | 0 | bgp, rfg, rn, afi, |
1415 | 0 | listgetdata(ln)); |
1416 | 0 | } |
1417 | 0 | } |
1418 | 0 | } |
1419 | | |
1420 | | /* |
1421 | | * Caller is responsible for ensuring that the specified nve-group |
1422 | | * was actually part of the list of exported nve groups. |
1423 | | */ |
1424 | | void vnc_direct_bgp_del_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) |
1425 | 0 | { |
1426 | 0 | vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP); |
1427 | 0 | vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP6); |
1428 | 0 | } |
1429 | | |
1430 | | void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp, |
1431 | | struct rfapi_nve_group_cfg *rfg, |
1432 | | afi_t afi) |
1433 | 0 | { |
1434 | 0 | struct listnode *node; |
1435 | 0 | struct rfapi_rfg_name *rfgn; |
1436 | |
|
1437 | 0 | if (VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
1438 | | /* |
1439 | | * look in the list of currently-exported groups |
1440 | | */ |
1441 | 0 | for (ALL_LIST_ELEMENTS_RO( |
1442 | 0 | bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
1443 | 0 | rfgn)) { |
1444 | |
|
1445 | 0 | if (rfgn->rfg == rfg) { |
1446 | | /* |
1447 | | * If it matches, reexport it |
1448 | | */ |
1449 | 0 | vnc_direct_bgp_del_group_afi(bgp, rfg, afi); |
1450 | 0 | vnc_direct_bgp_add_group_afi(bgp, rfg, afi); |
1451 | 0 | break; |
1452 | 0 | } |
1453 | 0 | } |
1454 | 0 | } |
1455 | 0 | } |
1456 | | |
1457 | | |
1458 | | static void vnc_direct_bgp_unexport_table(afi_t afi, struct agg_table *rt, |
1459 | | struct list *nve_list) |
1460 | 0 | { |
1461 | 0 | if (nve_list) { |
1462 | |
|
1463 | 0 | struct agg_node *rn; |
1464 | |
|
1465 | 0 | for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { |
1466 | |
|
1467 | 0 | if (rn->info) { |
1468 | |
|
1469 | 0 | struct listnode *hln; |
1470 | 0 | struct rfapi_descriptor *irfd; |
1471 | |
|
1472 | 0 | for (ALL_LIST_ELEMENTS_RO(nve_list, hln, |
1473 | 0 | irfd)) { |
1474 | |
|
1475 | 0 | bgp_withdraw(irfd->peer, |
1476 | 0 | agg_node_get_prefix(rn), |
1477 | 0 | 0, /* addpath_id */ |
1478 | 0 | afi, SAFI_UNICAST, |
1479 | 0 | ZEBRA_ROUTE_VNC_DIRECT, |
1480 | 0 | BGP_ROUTE_REDISTRIBUTE, |
1481 | 0 | NULL, /* RD not used for |
1482 | | unicast */ |
1483 | 0 | NULL, 0, NULL); /* tag not |
1484 | | used for |
1485 | | unicast, |
1486 | | EVPN |
1487 | | neither */ |
1488 | 0 | } |
1489 | 0 | } |
1490 | 0 | } |
1491 | 0 | } |
1492 | 0 | } |
1493 | | |
1494 | | static void import_table_to_nve_list_direct_bgp(struct bgp *bgp, |
1495 | | struct rfapi_import_table *it, |
1496 | | struct list **nves, |
1497 | | uint8_t family) |
1498 | 0 | { |
1499 | 0 | struct listnode *node; |
1500 | 0 | struct rfapi_rfg_name *rfgn; |
1501 | | |
1502 | | /* |
1503 | | * Loop over the list of NVE-Groups configured for |
1504 | | * exporting to direct-bgp. |
1505 | | * |
1506 | | * Build a list of NVEs that use this import table |
1507 | | */ |
1508 | 0 | *nves = NULL; |
1509 | 0 | for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, |
1510 | 0 | rfgn)) { |
1511 | | |
1512 | | /* |
1513 | | * If this NVE-Group's import table matches the current one |
1514 | | */ |
1515 | 0 | if (rfgn->rfg && rfgn->rfg->rfapi_import_table == it) { |
1516 | 0 | if (rfgn->rfg->nves) |
1517 | 0 | nve_group_to_nve_list(rfgn->rfg, nves, family); |
1518 | 0 | else if (rfgn->rfg->rfd |
1519 | 0 | && rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { |
1520 | 0 | if (!*nves) |
1521 | 0 | *nves = list_new(); |
1522 | 0 | listnode_add(*nves, rfgn->rfg->rfd); |
1523 | 0 | } |
1524 | 0 | } |
1525 | 0 | } |
1526 | 0 | } |
1527 | | |
1528 | | void vnc_direct_bgp_vpn_enable(struct bgp *bgp, afi_t afi) |
1529 | 0 | { |
1530 | 0 | struct listnode *rfgn; |
1531 | 0 | struct rfapi_nve_group_cfg *rfg; |
1532 | |
|
1533 | 0 | if (!bgp) |
1534 | 0 | return; |
1535 | | |
1536 | 0 | if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { |
1537 | 0 | vnc_zlog_debug_verbose( |
1538 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
1539 | 0 | __func__); |
1540 | 0 | return; |
1541 | 0 | } |
1542 | | |
1543 | 0 | if (afi != AFI_IP && afi != AFI_IP6) { |
1544 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
1545 | 0 | return; |
1546 | 0 | } |
1547 | | |
1548 | | /* |
1549 | | * Policy is applied per-nve-group, so we need to iterate |
1550 | | * over the groups to add everything. |
1551 | | */ |
1552 | 0 | for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, rfgn, |
1553 | 0 | rfg)) { |
1554 | | |
1555 | | /* |
1556 | | * contains policy management |
1557 | | */ |
1558 | 0 | vnc_direct_bgp_add_group_afi(bgp, rfg, afi); |
1559 | 0 | } |
1560 | 0 | } |
1561 | | |
1562 | | |
1563 | | void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi) |
1564 | 0 | { |
1565 | 0 | struct rfapi_import_table *it; |
1566 | 0 | uint8_t family = afi2family(afi); |
1567 | |
|
1568 | 0 | vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); |
1569 | |
|
1570 | 0 | if (!bgp) |
1571 | 0 | return; |
1572 | | |
1573 | 0 | if (!bgp->rfapi) { |
1574 | 0 | vnc_zlog_debug_verbose("%s: rfapi not initialized", __func__); |
1575 | 0 | return; |
1576 | 0 | } |
1577 | | |
1578 | 0 | if (!family || (afi != AFI_IP && afi != AFI_IP6)) { |
1579 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
1580 | 0 | return; |
1581 | 0 | } |
1582 | | |
1583 | 0 | for (it = bgp->rfapi->imports; it; it = it->next) { |
1584 | |
|
1585 | 0 | struct list *nve_list = NULL; |
1586 | |
|
1587 | 0 | import_table_to_nve_list_direct_bgp(bgp, it, &nve_list, family); |
1588 | |
|
1589 | 0 | if (nve_list) { |
1590 | 0 | vnc_direct_bgp_unexport_table( |
1591 | 0 | afi, it->imported_vpn[afi], nve_list); |
1592 | 0 | list_delete(&nve_list); |
1593 | 0 | } |
1594 | 0 | } |
1595 | 0 | } |
1596 | | |
1597 | | |
1598 | | /*********************************************************************** |
1599 | | * Export methods that proxy nexthop END |
1600 | | ***********************************************************************/ |
1601 | | |
1602 | | |
1603 | | /*********************************************************************** |
1604 | | * Export methods that preserve original nexthop BEGIN |
1605 | | * rh = "registering nve" |
1606 | | ***********************************************************************/ |
1607 | | |
1608 | | |
1609 | | /* |
1610 | | * "Adding a Route" export process |
1611 | | * TBD do we need to check bpi->type and bpi->sub_type here, or does |
1612 | | * caller do it? |
1613 | | */ |
1614 | | void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, |
1615 | | const struct prefix *prefix, struct peer *peer, |
1616 | | struct attr *attr) |
1617 | 0 | { |
1618 | 0 | struct vnc_export_info *eti; |
1619 | 0 | struct attr hattr; |
1620 | 0 | struct rfapi_cfg *hc; |
1621 | 0 | struct attr *iattr; |
1622 | |
|
1623 | 0 | if (!afi) { |
1624 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", |
1625 | 0 | __func__); |
1626 | 0 | return; |
1627 | 0 | } |
1628 | | |
1629 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
1630 | 0 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
1631 | 0 | vnc_zlog_debug_verbose( |
1632 | 0 | "%s: bgp redistribution of VNC direct routes is off", |
1633 | 0 | __func__); |
1634 | 0 | return; |
1635 | 0 | } |
1636 | | |
1637 | 0 | if (!(hc = bgp->rfapi_cfg)) { |
1638 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
1639 | 0 | __func__); |
1640 | 0 | return; |
1641 | 0 | } |
1642 | | |
1643 | 0 | if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { |
1644 | 0 | vnc_zlog_debug_verbose( |
1645 | 0 | "%s: export-to-bgp RH mode not enabled, skipping", |
1646 | 0 | __func__); |
1647 | 0 | return; |
1648 | 0 | } |
1649 | | |
1650 | | /* |
1651 | | * prefix list check |
1652 | | */ |
1653 | 0 | if (hc->plist_export_bgp[afi]) { |
1654 | 0 | if (prefix_list_apply(hc->plist_export_bgp[afi], prefix) |
1655 | 0 | == PREFIX_DENY) |
1656 | 0 | return; |
1657 | 0 | } |
1658 | | |
1659 | | /* |
1660 | | * Construct new attribute set with NVE's VN addr as |
1661 | | * nexthop and without Tunnel Encap attr |
1662 | | */ |
1663 | 0 | if (encap_attr_export(&hattr, attr, NULL, NULL)) |
1664 | 0 | return; |
1665 | 0 | if (hc->routemap_export_bgp) { |
1666 | 0 | struct bgp_path_info info; |
1667 | 0 | route_map_result_t ret; |
1668 | |
|
1669 | 0 | memset(&info, 0, sizeof(info)); |
1670 | 0 | info.peer = peer; |
1671 | 0 | info.attr = &hattr; |
1672 | 0 | ret = route_map_apply(hc->routemap_export_bgp, prefix, &info); |
1673 | 0 | if (ret == RMAP_DENYMATCH) { |
1674 | 0 | bgp_attr_flush(&hattr); |
1675 | 0 | return; |
1676 | 0 | } |
1677 | 0 | } |
1678 | | |
1679 | 0 | iattr = bgp_attr_intern(&hattr); |
1680 | 0 | bgp_attr_flush(&hattr); |
1681 | | |
1682 | | /* |
1683 | | * record route information that we will need to expire |
1684 | | * this route |
1685 | | */ |
1686 | 0 | eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer, |
1687 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); |
1688 | 0 | rfapiGetVncLifetime(attr, &eti->lifetime); |
1689 | 0 | eti->lifetime = rfapiGetHolddownFromLifetime(eti->lifetime); |
1690 | | |
1691 | | /* |
1692 | | * export expiration timer is already running on |
1693 | | * this route: cancel it |
1694 | | */ |
1695 | 0 | EVENT_OFF(eti->timer); |
1696 | |
|
1697 | 0 | bgp_update(peer, prefix, /* prefix */ |
1698 | 0 | 0, /* addpath_id */ |
1699 | 0 | iattr, /* bgp_update copies this attr */ |
1700 | 0 | afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, |
1701 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ |
1702 | 0 | NULL, /* tag not used for unicast, EVPN neither */ |
1703 | 0 | 0, 0, NULL); /* EVPN not used */ |
1704 | 0 | bgp_attr_unintern(&iattr); |
1705 | 0 | } |
1706 | | |
1707 | | static void vncExportWithdrawTimer(struct event *t) |
1708 | 0 | { |
1709 | 0 | struct vnc_export_info *eti = EVENT_ARG(t); |
1710 | 0 | const struct prefix *p = agg_node_get_prefix(eti->node); |
1711 | 0 |
|
1712 | 0 | /* |
1713 | 0 | * withdraw the route |
1714 | 0 | */ |
1715 | 0 | bgp_withdraw(eti->peer, p, 0, /* addpath_id */ |
1716 | 0 | family2afi(p->family), SAFI_UNICAST, eti->type, |
1717 | 0 | eti->subtype, NULL, /* RD not used for unicast */ |
1718 | 0 | NULL, 0, |
1719 | 0 | NULL); /* tag not used for unicast, EVPN neither */ |
1720 | 0 |
|
1721 | 0 | /* |
1722 | 0 | * Free the eti |
1723 | 0 | */ |
1724 | 0 | vnc_eti_delete(eti); |
1725 | 0 | } |
1726 | | |
1727 | | /* |
1728 | | * "Withdrawing a Route" export process |
1729 | | * TBD do we need to check bpi->type and bpi->sub_type here, or does |
1730 | | * caller do it? |
1731 | | */ |
1732 | | void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, |
1733 | | const struct prefix *prefix, struct peer *peer) |
1734 | 232 | { |
1735 | 232 | struct vnc_export_info *eti; |
1736 | | |
1737 | 232 | if (!afi) { |
1738 | 0 | flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node", |
1739 | 0 | __func__); |
1740 | 0 | return; |
1741 | 0 | } |
1742 | | |
1743 | | /* check bgp redist flag for vnc direct ("vpn") routes */ |
1744 | 232 | if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { |
1745 | 232 | vnc_zlog_debug_verbose( |
1746 | 232 | "%s: bgp redistribution of VNC direct routes is off", |
1747 | 232 | __func__); |
1748 | 232 | return; |
1749 | 232 | } |
1750 | | |
1751 | 0 | if (!bgp->rfapi_cfg) { |
1752 | 0 | vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", |
1753 | 0 | __func__); |
1754 | 0 | return; |
1755 | 0 | } |
1756 | 0 | if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { |
1757 | 0 | vnc_zlog_debug_verbose( |
1758 | 0 | "%s: export-to-bgp group mode not enabled, skipping", |
1759 | 0 | __func__); |
1760 | 0 | return; |
1761 | 0 | } |
1762 | | |
1763 | 0 | eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer, |
1764 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); |
1765 | |
|
1766 | 0 | if (!eti->timer && eti->lifetime <= INT32_MAX) { |
1767 | 0 | eti->timer = NULL; |
1768 | 0 | event_add_timer(bm->master, vncExportWithdrawTimer, eti, |
1769 | 0 | eti->lifetime, &eti->timer); |
1770 | 0 | vnc_zlog_debug_verbose( |
1771 | 0 | "%s: set expiration timer for %u seconds", __func__, |
1772 | 0 | eti->lifetime); |
1773 | 0 | } |
1774 | 0 | } |
1775 | | |
1776 | | |
1777 | | void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) |
1778 | 0 | { |
1779 | 0 | struct prefix_rd prd; |
1780 | 0 | struct bgp_dest *pdest; |
1781 | 0 | struct rfapi_cfg *hc; |
1782 | |
|
1783 | 0 | vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); |
1784 | |
|
1785 | 0 | if (!bgp) |
1786 | 0 | return; |
1787 | | |
1788 | 0 | if (!(hc = bgp->rfapi_cfg)) |
1789 | 0 | return; |
1790 | | |
1791 | 0 | if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { |
1792 | 0 | vnc_zlog_debug_verbose( |
1793 | 0 | "%s: export of RH routes not enabled, skipping", |
1794 | 0 | __func__); |
1795 | 0 | return; |
1796 | 0 | } |
1797 | | |
1798 | 0 | if (afi != AFI_IP && afi != AFI_IP6) { |
1799 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
1800 | 0 | return; |
1801 | 0 | } |
1802 | | |
1803 | | /* |
1804 | | * Go through the entire BGP VPN table and export to BGP unicast. |
1805 | | */ |
1806 | | |
1807 | 0 | vnc_zlog_debug_verbose("%s: starting RD loop", __func__); |
1808 | | |
1809 | | /* Loop over all the RDs */ |
1810 | 0 | for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest; |
1811 | 0 | pdest = bgp_route_next(pdest)) { |
1812 | |
|
1813 | 0 | struct bgp_table *table; |
1814 | 0 | struct bgp_dest *dest; |
1815 | 0 | struct bgp_path_info *ri; |
1816 | 0 | const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); |
1817 | |
|
1818 | 0 | memset(&prd, 0, sizeof(prd)); |
1819 | 0 | prd.family = AF_UNSPEC; |
1820 | 0 | prd.prefixlen = 64; |
1821 | 0 | memcpy(prd.val, pdest_p->u.val, 8); |
1822 | | |
1823 | | /* This is the per-RD table of prefixes */ |
1824 | 0 | table = bgp_dest_get_bgp_table_info(pdest); |
1825 | |
|
1826 | 0 | if (!table) |
1827 | 0 | continue; |
1828 | | |
1829 | 0 | for (dest = bgp_table_top(table); dest; |
1830 | 0 | dest = bgp_route_next(dest)) { |
1831 | 0 | const struct prefix *dest_p; |
1832 | | |
1833 | | /* |
1834 | | * skip prefix list check if no routes here |
1835 | | */ |
1836 | 0 | if (!bgp_dest_has_bgp_path_info_data(dest)) |
1837 | 0 | continue; |
1838 | | |
1839 | 0 | vnc_zlog_debug_verbose("%s: checking prefix %pBD", |
1840 | 0 | __func__, dest); |
1841 | |
|
1842 | 0 | dest_p = bgp_dest_get_prefix(dest); |
1843 | | |
1844 | | /* |
1845 | | * prefix list check |
1846 | | */ |
1847 | 0 | if (hc->plist_export_bgp[afi]) { |
1848 | 0 | if (prefix_list_apply(hc->plist_export_bgp[afi], |
1849 | 0 | dest_p) |
1850 | 0 | == PREFIX_DENY) { |
1851 | |
|
1852 | 0 | vnc_zlog_debug_verbose( |
1853 | 0 | "%s: prefix list says DENY", |
1854 | 0 | __func__); |
1855 | 0 | continue; |
1856 | 0 | } |
1857 | 0 | } |
1858 | | |
1859 | 0 | for (ri = bgp_dest_get_bgp_path_info(dest); ri; |
1860 | 0 | ri = ri->next) { |
1861 | |
|
1862 | 0 | vnc_zlog_debug_verbose("%s: ri->sub_type: %d", |
1863 | 0 | __func__, ri->sub_type); |
1864 | |
|
1865 | 0 | if (ri->sub_type == BGP_ROUTE_NORMAL |
1866 | 0 | || ri->sub_type == BGP_ROUTE_RFP) { |
1867 | |
|
1868 | 0 | struct vnc_export_info *eti; |
1869 | 0 | struct attr hattr; |
1870 | 0 | struct attr *iattr; |
1871 | | |
1872 | | /* |
1873 | | * Construct new attribute set with |
1874 | | * NVE's VN addr as |
1875 | | * nexthop and without Tunnel Encap attr |
1876 | | */ |
1877 | 0 | if (encap_attr_export(&hattr, ri->attr, |
1878 | 0 | NULL, NULL)) { |
1879 | 0 | vnc_zlog_debug_verbose( |
1880 | 0 | "%s: encap_attr_export failed", |
1881 | 0 | __func__); |
1882 | 0 | continue; |
1883 | 0 | } |
1884 | | |
1885 | 0 | if (hc->routemap_export_bgp) { |
1886 | 0 | struct bgp_path_info info; |
1887 | 0 | route_map_result_t ret; |
1888 | |
|
1889 | 0 | memset(&info, 0, sizeof(info)); |
1890 | 0 | info.peer = ri->peer; |
1891 | 0 | info.attr = &hattr; |
1892 | 0 | ret = route_map_apply( |
1893 | 0 | hc->routemap_export_bgp, |
1894 | 0 | dest_p, &info); |
1895 | 0 | if (ret == RMAP_DENYMATCH) { |
1896 | 0 | bgp_attr_flush(&hattr); |
1897 | 0 | vnc_zlog_debug_verbose( |
1898 | 0 | "%s: route map says DENY", |
1899 | 0 | __func__); |
1900 | 0 | continue; |
1901 | 0 | } |
1902 | 0 | } |
1903 | | |
1904 | 0 | iattr = bgp_attr_intern(&hattr); |
1905 | 0 | bgp_attr_flush(&hattr); |
1906 | | |
1907 | | /* |
1908 | | * record route information that we will |
1909 | | * need to expire |
1910 | | * this route |
1911 | | */ |
1912 | 0 | eti = vnc_eti_get( |
1913 | 0 | bgp, EXPORT_TYPE_BGP, dest_p, |
1914 | 0 | ri->peer, |
1915 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, |
1916 | 0 | BGP_ROUTE_REDISTRIBUTE); |
1917 | 0 | rfapiGetVncLifetime(ri->attr, |
1918 | 0 | &eti->lifetime); |
1919 | | |
1920 | | /* |
1921 | | * export expiration timer is |
1922 | | * already running on |
1923 | | * this route: cancel it |
1924 | | */ |
1925 | 0 | EVENT_OFF(eti->timer); |
1926 | |
|
1927 | 0 | vnc_zlog_debug_verbose( |
1928 | 0 | "%s: calling bgp_update", |
1929 | 0 | __func__); |
1930 | |
|
1931 | 0 | bgp_update( |
1932 | 0 | ri->peer, dest_p, /* prefix */ |
1933 | 0 | 0, /* addpath_id */ |
1934 | 0 | iattr, /* bgp_update copies |
1935 | | it */ |
1936 | 0 | AFI_IP, SAFI_UNICAST, |
1937 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, |
1938 | 0 | BGP_ROUTE_REDISTRIBUTE, NULL, |
1939 | | /* RD not used for unicast */ |
1940 | 0 | NULL, |
1941 | | /* tag not used for unicast, |
1942 | | or EVPN */ |
1943 | 0 | 0, 0, NULL); /* EVPN not used */ |
1944 | |
|
1945 | 0 | bgp_attr_unintern(&iattr); |
1946 | 0 | } |
1947 | 0 | } |
1948 | 0 | } |
1949 | 0 | } |
1950 | 0 | } |
1951 | | |
1952 | | void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) |
1953 | 0 | { |
1954 | 0 | struct bgp_dest *dest; |
1955 | |
|
1956 | 0 | vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); |
1957 | |
|
1958 | 0 | if (!bgp) |
1959 | 0 | return; |
1960 | | |
1961 | 0 | if (afi != AFI_IP && afi != AFI_IP6) { |
1962 | 0 | vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); |
1963 | 0 | return; |
1964 | 0 | } |
1965 | | |
1966 | | /* |
1967 | | * Go through the entire BGP unicast table and remove routes that |
1968 | | * originated from us |
1969 | | */ |
1970 | 0 | for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; |
1971 | 0 | dest = bgp_route_next(dest)) { |
1972 | 0 | const struct prefix *dest_p = bgp_dest_get_prefix(dest); |
1973 | 0 | struct bgp_path_info *ri; |
1974 | 0 | struct bgp_path_info *next; |
1975 | |
|
1976 | 0 | for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; |
1977 | 0 | ri = next) { |
1978 | |
|
1979 | 0 | next = ri->next; |
1980 | |
|
1981 | 0 | if (ri->type == ZEBRA_ROUTE_VNC_DIRECT_RH |
1982 | 0 | && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { |
1983 | |
|
1984 | 0 | struct vnc_export_info *eti; |
1985 | | |
1986 | | /* |
1987 | | * Delete routes immediately (no timer) |
1988 | | */ |
1989 | 0 | eti = vnc_eti_checktimer( |
1990 | 0 | bgp, EXPORT_TYPE_BGP, dest_p, ri->peer, |
1991 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, |
1992 | 0 | BGP_ROUTE_REDISTRIBUTE); |
1993 | 0 | if (eti) { |
1994 | 0 | EVENT_OFF(eti->timer); |
1995 | 0 | vnc_eti_delete(eti); |
1996 | 0 | } |
1997 | |
|
1998 | 0 | bgp_withdraw(ri->peer, dest_p, /* prefix */ |
1999 | 0 | 0, /* addpath_id */ |
2000 | 0 | AFI_IP, SAFI_UNICAST, |
2001 | 0 | ZEBRA_ROUTE_VNC_DIRECT_RH, |
2002 | 0 | BGP_ROUTE_REDISTRIBUTE, |
2003 | 0 | NULL, /* RD not used for unicast */ |
2004 | 0 | NULL, 0, NULL); /* tag not used for |
2005 | | unicast, EVPN |
2006 | | neither */ |
2007 | 0 | } |
2008 | 0 | } |
2009 | 0 | } |
2010 | 0 | } |
2011 | | |
2012 | | void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi) |
2013 | 0 | { |
2014 | 0 | if (VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { |
2015 | 0 | vnc_direct_bgp_rh_vpn_disable(bgp, afi); |
2016 | 0 | vnc_direct_bgp_rh_vpn_enable(bgp, afi); |
2017 | 0 | } |
2018 | 0 | } |
2019 | | |
2020 | | /*********************************************************************** |
2021 | | * Generic Export methods |
2022 | | ***********************************************************************/ |
2023 | | |
2024 | | /* |
2025 | | * Assumes the correct mode bits are already turned on. Thus it |
2026 | | * is OK to call this function from, e.g., bgp_redistribute_set() |
2027 | | * without caring if export is enabled or not |
2028 | | */ |
2029 | | void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi) |
2030 | 0 | { |
2031 | 0 | if (!bgp->rfapi_cfg) |
2032 | 0 | return; |
2033 | | |
2034 | 0 | switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { |
2035 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: |
2036 | 0 | break; |
2037 | | |
2038 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: |
2039 | 0 | vnc_direct_bgp_vpn_enable(bgp, afi); |
2040 | 0 | break; |
2041 | | |
2042 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: |
2043 | 0 | vnc_direct_bgp_rh_vpn_enable(bgp, afi); |
2044 | 0 | break; |
2045 | | |
2046 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: |
2047 | 0 | vnc_direct_bgp_vpn_enable_ce(bgp, afi); |
2048 | 0 | break; |
2049 | 0 | } |
2050 | 0 | } |
2051 | | |
2052 | | void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi) |
2053 | 0 | { |
2054 | 0 | if (!bgp->rfapi_cfg) |
2055 | 0 | return; |
2056 | | |
2057 | 0 | switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { |
2058 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: |
2059 | 0 | break; |
2060 | | |
2061 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: |
2062 | 0 | vnc_direct_bgp_vpn_disable(bgp, afi); |
2063 | 0 | break; |
2064 | | |
2065 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: |
2066 | 0 | vnc_direct_bgp_rh_vpn_disable(bgp, afi); |
2067 | 0 | break; |
2068 | | |
2069 | 0 | case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: |
2070 | 0 | vnc_direct_bgp_vpn_disable_ce(bgp, afi); |
2071 | 0 | break; |
2072 | 0 | } |
2073 | 0 | } |
2074 | | |
2075 | | void vnc_export_bgp_prechange(struct bgp *bgp) |
2076 | 0 | { |
2077 | 0 | vnc_export_bgp_disable(bgp, AFI_IP); |
2078 | 0 | vnc_export_bgp_disable(bgp, AFI_IP6); |
2079 | 0 | } |
2080 | | |
2081 | | void vnc_export_bgp_postchange(struct bgp *bgp) |
2082 | 0 | { |
2083 | 0 | vnc_export_bgp_enable(bgp, AFI_IP); |
2084 | 0 | vnc_export_bgp_enable(bgp, AFI_IP6); |
2085 | 0 | } |
2086 | | |
2087 | | void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi) |
2088 | 0 | { |
2089 | 0 | vnc_export_bgp_disable(bgp, afi); |
2090 | 0 | vnc_export_bgp_enable(bgp, afi); |
2091 | 0 | } |