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