// SPDX-License-Identifier: GPL-2.0-or-later /* * * Copyright 2009-2016, LabN Consulting, L.L.C. * */ /* * File: vnc_zebra.c * Purpose: Handle exchange of routes between VNC and Zebra */ #include "lib/zebra.h" #include "lib/prefix.h" #include "lib/agg_table.h" #include "lib/log.h" #include "lib/command.h" #include "lib/zclient.h" #include "lib/stream.h" #include "lib/ringbuf.h" #include "lib/memory.h" #include "lib/lib_errors.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_advertise.h" #include "bgpd/rfapi/bgp_rfapi_cfg.h" #include "bgpd/rfapi/rfapi.h" #include "bgpd/rfapi/rfapi_import.h" #include "bgpd/rfapi/rfapi_private.h" #include "bgpd/rfapi/vnc_zebra.h" #include "bgpd/rfapi/rfapi_vty.h" #include "bgpd/rfapi/rfapi_backend.h" #include "bgpd/rfapi/vnc_debug.h" static struct rfapi_descriptor vncHD1VR; /* Single-VR export dummy nve descr */ static struct zclient *zclient_vnc = NULL; /*********************************************************************** * REDISTRIBUTE: Zebra sends updates/withdraws to BGPD ***********************************************************************/ /* * Routes coming from zebra get added to VNC here */ static void vnc_redistribute_add(struct prefix *p, uint32_t metric, uint8_t type) { struct bgp *bgp = bgp_get_default(); struct prefix_rd prd; struct rfapi_ip_addr vnaddr; afi_t afi; uint32_t local_pref = rfp_cost_to_localpref(metric > 255 ? 255 : metric); if (!bgp) return; if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", __func__); return; } afi = family2afi(p->family); if (!afi) { vnc_zlog_debug_verbose("%s: unknown prefix address family %d", __func__, p->family); return; } if (!bgp->rfapi_cfg->redist[afi][type]) { vnc_zlog_debug_verbose( "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping", __func__, afi, type); return; } if (!bgp->rfapi_cfg->rfg_redist) { vnc_zlog_debug_verbose("%s: no redist nve group, skipping", __func__); return; } /* * Assume nve group's configured VN address prefix is a host * route which also happens to give the NVE VN address to use * for redistributing into VNC. */ vnaddr.addr_family = bgp->rfapi_cfg->rfg_redist->vn_prefix.family; switch (bgp->rfapi_cfg->rfg_redist->vn_prefix.family) { case AF_INET: if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != IPV4_MAX_BITLEN) { vnc_zlog_debug_verbose( "%s: redist nve group VN prefix len (%d) != 32, skipping", __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix .prefixlen); return; } vnaddr.addr.v4 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix4; break; case AF_INET6: if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen != IPV6_MAX_BITLEN) { vnc_zlog_debug_verbose( "%s: redist nve group VN prefix len (%d) != 128, skipping", __func__, bgp->rfapi_cfg->rfg_redist->vn_prefix .prefixlen); return; } vnaddr.addr.v6 = bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix6; break; default: vnc_zlog_debug_verbose( "%s: no redist nve group VN host prefix configured, skipping", __func__); return; } /* * Assume nve group's configured UN address prefix is a host * route which also happens to give the NVE UN address to use * for redistributing into VNC. */ /* * Set UN address in dummy nve descriptor so add_vnc_route * can use it in VNC tunnel SubTLV */ { struct rfapi_ip_prefix pfx_un; rfapiQprefix2Rprefix(&bgp->rfapi_cfg->rfg_redist->un_prefix, &pfx_un); switch (pfx_un.prefix.addr_family) { case AF_INET: if (pfx_un.length != IPV4_MAX_BITLEN) { vnc_zlog_debug_verbose( "%s: redist nve group UN prefix len (%d) != 32, skipping", __func__, pfx_un.length); return; } break; case AF_INET6: if (pfx_un.length != IPV6_MAX_BITLEN) { vnc_zlog_debug_verbose( "%s: redist nve group UN prefix len (%d) != 128, skipping", __func__, pfx_un.length); return; } break; default: vnc_zlog_debug_verbose( "%s: no redist nve group UN host prefix configured, skipping", __func__); return; } vncHD1VR.un_addr = pfx_un.prefix; if (!vncHD1VR.peer) { /* * Same setup as in rfapi_open() */ vncHD1VR.peer = peer_new(bgp); vncHD1VR.peer->connection->status = Established; /* keep bgp core happy */ bgp_peer_connection_buffers_free( vncHD1VR.peer->connection); /* base code assumes have valid host pointer */ vncHD1VR.peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, ".zebra."); /* Mark peer as belonging to HD */ SET_FLAG(vncHD1VR.peer->flags, PEER_FLAG_IS_RFAPI_HD); } } memset(&prd, 0, sizeof(prd)); prd = bgp->rfapi_cfg->rfg_redist->rd; prd.family = AF_UNSPEC; prd.prefixlen = 64; add_vnc_route(&vncHD1VR, /* cookie + UN addr */ bgp, SAFI_MPLS_VPN, p, &prd, &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), NULL, /* RFP options */ NULL, /* struct rfapi_un_option */ NULL, /* struct rfapi_vn_option */ bgp->rfapi_cfg->rfg_redist->rt_export_list, NULL, NULL, /* label: default */ type, BGP_ROUTE_REDISTRIBUTE, 0); /* flags */ } /* * Route deletions from zebra propagate to VNC here */ static void vnc_redistribute_delete(struct prefix *p, uint8_t type) { struct bgp *bgp = bgp_get_default(); struct prefix_rd prd; afi_t afi; if (!bgp) return; if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", __func__); return; } afi = family2afi(p->family); if (!afi) { vnc_zlog_debug_verbose("%s: unknown prefix address family %d", __func__, p->family); return; } if (!bgp->rfapi_cfg->redist[afi][type]) { vnc_zlog_debug_verbose( "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping", __func__, afi, type); return; } if (!bgp->rfapi_cfg->rfg_redist) { vnc_zlog_debug_verbose("%s: no redist nve group, skipping", __func__); return; } memset(&prd, 0, sizeof(prd)); prd = bgp->rfapi_cfg->rfg_redist->rd; prd.family = AF_UNSPEC; prd.prefixlen = 64; del_vnc_route(&vncHD1VR, /* use dummy ptr as cookie */ vncHD1VR.peer, bgp, SAFI_MPLS_VPN, p, &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); } /* * Flush all redistributed routes of type */ static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type) { struct prefix_rd prd; struct bgp_table *table; struct bgp_dest *pdest; struct bgp_dest *dest; vnc_zlog_debug_verbose("%s: entry", __func__); if (!bgp) return; if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", __func__); return; } /* * Loop over all the RDs */ for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest; pdest = bgp_route_next(pdest)) { const struct prefix *pdest_p = bgp_dest_get_prefix(pdest); memset(&prd, 0, sizeof(prd)); prd.family = AF_UNSPEC; prd.prefixlen = 64; memcpy(prd.val, pdest_p->u.val, 8); /* This is the per-RD table of prefixes */ table = bgp_dest_get_bgp_table_info(pdest); if (!table) continue; for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) { struct bgp_path_info *ri; for (ri = bgp_dest_get_bgp_path_info(dest); ri; ri = ri->next) { if (ri->type == type) { /* has matching redist type */ break; } } if (ri) { del_vnc_route( &vncHD1VR, /* use dummy ptr as cookie */ vncHD1VR.peer, bgp, SAFI_MPLS_VPN, bgp_dest_get_prefix(dest), &prd, type, BGP_ROUTE_REDISTRIBUTE, NULL, 0); } } } vnc_zlog_debug_verbose("%s: return", __func__); } /* * Zebra route add and delete treatment. * * Assumes 1 nexthop */ static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS) { struct zapi_route api; int add; if (zapi_route_decode(zclient->ibuf, &api) < 0) return -1; /* we completely ignore srcdest routes for now. */ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX)) return 0; add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD); if (add) vnc_redistribute_add(&api.prefix, api.metric, api.type); else vnc_redistribute_delete(&api.prefix, api.type); if (BGP_DEBUG(zebra, ZEBRA)) vnc_zlog_debug_verbose( "%s: Zebra rcvd: route delete %s %pFX metric %u", __func__, zebra_route_string(api.type), &api.prefix, api.metric); return 0; } /*********************************************************************** * vnc_bgp_zebra_*: VNC sends updates/withdraws to Zebra ***********************************************************************/ /* * low-level message builder */ static void vnc_zebra_route_msg(const struct prefix *p, unsigned int nhp_count, void *nhp_ary, int add) /* 1 = add, 0 = del */ { struct zapi_route api; struct zapi_nexthop *api_nh; int i; struct in_addr **nhp_ary4 = nhp_ary; struct in6_addr **nhp_ary6 = nhp_ary; if (!nhp_count) { vnc_zlog_debug_verbose("%s: empty nexthop list, skipping", __func__); return; } memset(&api, 0, sizeof(api)); api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_VNC; api.safi = SAFI_UNICAST; api.prefix = *p; /* Nexthops */ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = MIN(nhp_count, multipath_num); for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; api_nh->vrf_id = VRF_DEFAULT; switch (p->family) { case AF_INET: memcpy(&api_nh->gate.ipv4, nhp_ary4[i], sizeof(api_nh->gate.ipv4)); api_nh->type = NEXTHOP_TYPE_IPV4; break; case AF_INET6: memcpy(&api_nh->gate.ipv6, nhp_ary6[i], sizeof(api_nh->gate.ipv6)); api_nh->type = NEXTHOP_TYPE_IPV6; break; } } if (BGP_DEBUG(zebra, ZEBRA)) vnc_zlog_debug_verbose( "%s: Zebra send: route %s %pFX, nhp_count=%d", __func__, (add ? "add" : "del"), &api.prefix, nhp_count); zclient_route_send((add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE), zclient_vnc, &api); } static void nve_list_to_nh_array(uint8_t family, struct list *nve_list, unsigned int *nh_count_ret, void **nh_ary_ret, /* returned address array */ void **nhp_ary_ret) /* returned pointer array */ { int nve_count = listcount(nve_list); *nh_count_ret = 0; *nh_ary_ret = NULL; *nhp_ary_ret = NULL; if (!nve_count) { vnc_zlog_debug_verbose("%s: empty nve_list, skipping", __func__); return; } if (family == AF_INET) { struct listnode *ln; struct in_addr *iap; struct in_addr **v; /* * Array of nexthop addresses */ *nh_ary_ret = XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in_addr)); /* * Array of pointers to nexthop addresses */ *nhp_ary_ret = XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in_addr *)); iap = *nh_ary_ret; v = *nhp_ary_ret; for (ln = listhead(nve_list); ln; ln = listnextnode(ln)) { struct rfapi_descriptor *irfd; struct prefix nhp; irfd = listgetdata(ln); if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) continue; *iap = nhp.u.prefix4; *v = iap; vnc_zlog_debug_verbose( "%s: ipadr: (%p)<-0x%x, ptr: (%p)<-%p", __func__, iap, nhp.u.prefix4.s_addr, v, iap); ++iap; ++v; ++*nh_count_ret; } } else if (family == AF_INET6) { struct listnode *ln; *nh_ary_ret = XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in6_addr)); *nhp_ary_ret = XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in6_addr *)); for (ln = listhead(nve_list); ln; ln = listnextnode(ln)) { struct rfapi_descriptor *irfd; struct in6_addr *iap = *nh_ary_ret; struct in6_addr **v = *nhp_ary_ret; struct prefix nhp; irfd = listgetdata(ln); if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) continue; *iap = nhp.u.prefix6; *v = iap; ++iap; ++v; ++*nh_count_ret; } } } static void import_table_to_nve_list_zebra(struct bgp *bgp, struct rfapi_import_table *it, struct list **nves, uint8_t family) { struct listnode *node; struct rfapi_rfg_name *rfgn; /* * Loop over the list of NVE-Groups configured for * exporting to direct-bgp. * * Build a list of NVEs that use this import table */ *nves = NULL; for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) { /* * If this NVE-Group's import table matches the current one */ if (rfgn->rfg && rfgn->rfg->nves && rfgn->rfg->rfapi_import_table == it) { nve_group_to_nve_list(rfgn->rfg, nves, family); } } } static void vnc_zebra_add_del_prefix(struct bgp *bgp, struct rfapi_import_table *import_table, struct agg_node *rn, int add) /* !0 = add, 0 = del */ { struct list *nves; const struct prefix *p = agg_node_get_prefix(rn); unsigned int nexthop_count = 0; void *nh_ary = NULL; void *nhp_ary = NULL; vnc_zlog_debug_verbose("%s: entry, add=%d", __func__, add); if (zclient_vnc->sock < 0) return; if (p->family != AF_INET && p->family != AF_INET6) { flog_err(EC_LIB_DEVELOPMENT, "%s: invalid route node addr family", __func__); return; } if (!vrf_bitmap_check(&zclient_vnc->redist[family2afi(p->family)] [ZEBRA_ROUTE_VNC], VRF_DEFAULT)) return; if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", __func__); return; } if (!listcount(bgp->rfapi_cfg->rfg_export_zebra_l)) { vnc_zlog_debug_verbose( "%s: no zebra export nve group, skipping", __func__); return; } import_table_to_nve_list_zebra(bgp, import_table, &nves, p->family); if (nves) { nve_list_to_nh_array(p->family, nves, &nexthop_count, &nh_ary, &nhp_ary); list_delete(&nves); if (nexthop_count) vnc_zebra_route_msg(p, nexthop_count, nhp_ary, add); } XFREE(MTYPE_TMP, nhp_ary); XFREE(MTYPE_TMP, nh_ary); } void vnc_zebra_add_prefix(struct bgp *bgp, struct rfapi_import_table *import_table, struct agg_node *rn) { vnc_zebra_add_del_prefix(bgp, import_table, rn, 1); } void vnc_zebra_del_prefix(struct bgp *bgp, struct rfapi_import_table *import_table, struct agg_node *rn) { vnc_zebra_add_del_prefix(bgp, import_table, rn, 0); } static void vnc_zebra_add_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd, int add) /* 0 = del, !0 = add */ { struct listnode *node; struct rfapi_rfg_name *rfgn; struct rfapi_nve_group_cfg *rfg = rfd->rfg; afi_t afi = family2afi(rfd->vn_addr.addr_family); struct prefix nhp; void *pAddr; vnc_zlog_debug_verbose("%s: entry, add=%d", __func__, add); if (zclient_vnc->sock < 0) return; if (!vrf_bitmap_check(&zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC], VRF_DEFAULT)) return; if (afi != AFI_IP && afi != AFI_IP6) { flog_err(EC_LIB_DEVELOPMENT, "%s: invalid vn addr family", __func__); return; } if (!bgp) return; if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", __func__); return; } if (rfapiRaddr2Qprefix(&rfd->vn_addr, &nhp)) { vnc_zlog_debug_verbose("%s: can't convert vn address, skipping", __func__); return; } pAddr = &nhp.u.val; /* * Loop over the list of NVE-Groups configured for * exporting to zebra and see if this new NVE's * group is among them. */ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) { /* * Yes, this NVE's group is configured for export to zebra */ if (rfgn->rfg == rfg) { struct agg_table *rt = NULL; struct agg_node *rn; struct rfapi_import_table *import_table; import_table = rfg->rfapi_import_table; vnc_zlog_debug_verbose( "%s: this nve's group is in zebra export list", __func__); rt = import_table->imported_vpn[afi]; /* * Walk the NVE-Group's VNC Import table */ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { if (!rn->info) continue; vnc_zlog_debug_verbose("%s: sending %s", __func__, (add ? "add" : "del")); vnc_zebra_route_msg(agg_node_get_prefix(rn), 1, &pAddr, add); } } } } void vnc_zebra_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) { vnc_zebra_add_del_nve(bgp, rfd, 1); } void vnc_zebra_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) { vnc_zebra_add_del_nve(bgp, rfd, 0); } static void vnc_zebra_add_del_group_afi(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg, afi_t afi, int add) { struct agg_table *rt = NULL; struct agg_node *rn; struct rfapi_import_table *import_table; uint8_t family = afi2family(afi); struct list *nves = NULL; unsigned int nexthop_count = 0; void *nh_ary = NULL; void *nhp_ary = NULL; vnc_zlog_debug_verbose("%s: entry", __func__); import_table = rfg->rfapi_import_table; if (!import_table) { vnc_zlog_debug_verbose( "%s: import table not defined, returning", __func__); return; } if (afi == AFI_IP || afi == AFI_IP6) { rt = import_table->imported_vpn[afi]; } else { flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi); return; } if (!family) { flog_err(EC_LIB_DEVELOPMENT, "%s: computed bad family: %d", __func__, family); return; } if (!rfg->nves) { /* avoid segfault below if list doesn't exist */ vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__); return; } nve_group_to_nve_list(rfg, &nves, family); if (nves) { vnc_zlog_debug_verbose("%s: have nves", __func__); nve_list_to_nh_array(family, nves, &nexthop_count, &nh_ary, &nhp_ary); vnc_zlog_debug_verbose("%s: family: %d, nve count: %d", __func__, family, nexthop_count); list_delete(&nves); if (nexthop_count) { /* * Walk the NVE-Group's VNC Import table */ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { if (rn->info) { vnc_zebra_route_msg( agg_node_get_prefix(rn), nexthop_count, nhp_ary, add); } } } XFREE(MTYPE_TMP, nhp_ary); XFREE(MTYPE_TMP, nh_ary); } } void vnc_zebra_add_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) { vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP, 1); vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP6, 1); } void vnc_zebra_del_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) { vnc_zlog_debug_verbose("%s: entry", __func__); vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP, 0); vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP6, 0); } void vnc_zebra_reexport_group_afi(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg, afi_t afi) { struct listnode *node; struct rfapi_rfg_name *rfgn; for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, rfgn)) { if (rfgn->rfg == rfg) { vnc_zebra_add_del_group_afi(bgp, rfg, afi, 0); vnc_zebra_add_del_group_afi(bgp, rfg, afi, 1); break; } } } /*********************************************************************** * CONTROL INTERFACE ***********************************************************************/ /* Other routes redistribution into BGP. */ int vnc_redistribute_set(struct bgp *bgp, afi_t afi, int type) { if (!bgp->rfapi_cfg) { return CMD_WARNING_CONFIG_FAILED; } /* Set flag to BGP instance. */ bgp->rfapi_cfg->redist[afi][type] = 1; // bgp->redist[afi][type] = 1; /* Return if already redistribute flag is set. */ if (vrf_bitmap_check(&zclient_vnc->redist[afi][type], VRF_DEFAULT)) return CMD_WARNING_CONFIG_FAILED; vrf_bitmap_set(&zclient_vnc->redist[afi][type], VRF_DEFAULT); // vrf_bitmap_set(&zclient_vnc->redist[afi][type], VRF_DEFAULT); /* Return if zebra connection is not established. */ if (zclient_vnc->sock < 0) return CMD_WARNING_CONFIG_FAILED; if (BGP_DEBUG(zebra, ZEBRA)) vnc_zlog_debug_verbose("Zebra send: redistribute add %s", zebra_route_string(type)); /* Send distribute add message to zebra. */ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient_vnc, afi, type, 0, VRF_DEFAULT); return CMD_SUCCESS; } /* Unset redistribution. */ int vnc_redistribute_unset(struct bgp *bgp, afi_t afi, int type) { vnc_zlog_debug_verbose("%s: type=%d entry", __func__, type); if (!bgp->rfapi_cfg) { vnc_zlog_debug_verbose("%s: return (no rfapi_cfg)", __func__); return CMD_WARNING_CONFIG_FAILED; } /* Unset flag from BGP instance. */ bgp->rfapi_cfg->redist[afi][type] = 0; /* Return if zebra connection is disabled. */ if (!vrf_bitmap_check(&zclient_vnc->redist[afi][type], VRF_DEFAULT)) return CMD_WARNING_CONFIG_FAILED; vrf_bitmap_unset(&zclient_vnc->redist[afi][type], VRF_DEFAULT); if (bgp->rfapi_cfg->redist[AFI_IP][type] == 0 && bgp->rfapi_cfg->redist[AFI_IP6][type] == 0 && zclient_vnc->sock >= 0) { /* Send distribute delete message to zebra. */ if (BGP_DEBUG(zebra, ZEBRA)) vnc_zlog_debug_verbose( "Zebra send: redistribute delete %s", zebra_route_string(type)); zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient_vnc, afi, type, 0, VRF_DEFAULT); } /* Withdraw redistributed routes from current BGP's routing table. */ vnc_redistribute_withdraw(bgp, afi, type); vnc_zlog_debug_verbose("%s: return", __func__); return CMD_SUCCESS; } extern struct zebra_privs_t bgpd_privs; static zclient_handler *const vnc_handlers[] = { [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = vnc_zebra_read_route, [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = vnc_zebra_read_route, }; /* * Modeled after bgp_zebra.c'bgp_zebra_init() * Charriere asks, "Is it possible to carry two?" */ void vnc_zebra_init(struct event_loop *master) { /* Set default values. */ zclient_vnc = zclient_new(master, &zclient_options_auxiliary, vnc_handlers, array_size(vnc_handlers)); zclient_init(zclient_vnc, ZEBRA_ROUTE_VNC, 0, &bgpd_privs); } void vnc_zebra_destroy(void) { if (zclient_vnc == NULL) return; zclient_stop(zclient_vnc); zclient_free(zclient_vnc); zclient_vnc = NULL; }