diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/rfapi | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/rfapi')
34 files changed, 33085 insertions, 0 deletions
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c new file mode 100644 index 0000000..5b6961d --- /dev/null +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -0,0 +1,4627 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ +#include "lib/zebra.h" + +#include "lib/command.h" +#include "lib/prefix.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/agg_table.h" +#include "lib/plist.h" +#include "lib/routemap.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" + +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_debug.h" + +#ifdef ENABLE_BGP_VNC + +#undef BGP_VNC_DEBUG_MATCH_GROUP + + +DEFINE_MGROUP(RFAPI, "rfapi"); +DEFINE_MTYPE(RFAPI, RFAPI_CFG, "NVE Configuration"); +DEFINE_MTYPE(RFAPI, RFAPI_GROUP_CFG, "NVE Group Configuration"); +DEFINE_MTYPE(RFAPI, RFAPI_L2_CFG, "RFAPI L2 Group Configuration"); +DEFINE_MTYPE(RFAPI, RFAPI_RFP_GROUP_CFG, "RFAPI RFP Group Configuration"); +DEFINE_MTYPE(RFAPI, RFAPI, "RFAPI Generic"); +DEFINE_MTYPE(RFAPI, RFAPI_DESC, "RFAPI Descriptor"); +DEFINE_MTYPE(RFAPI, RFAPI_IMPORTTABLE, "RFAPI Import Table"); +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR, "RFAPI Monitor VPN"); +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ENCAP, "RFAPI Monitor Encap"); +DEFINE_MTYPE(RFAPI, RFAPI_NEXTHOP, "RFAPI Next Hop"); +DEFINE_MTYPE(RFAPI, RFAPI_VN_OPTION, "RFAPI VN Option"); +DEFINE_MTYPE(RFAPI, RFAPI_UN_OPTION, "RFAPI UN Option"); +DEFINE_MTYPE(RFAPI, RFAPI_WITHDRAW, "RFAPI Withdraw"); +DEFINE_MTYPE(RFAPI, RFAPI_RFG_NAME, "RFAPI RFGName"); +DEFINE_MTYPE(RFAPI, RFAPI_ADB, "RFAPI Advertisement Data"); +DEFINE_MTYPE(RFAPI, RFAPI_ETI, "RFAPI Export Table Info"); +DEFINE_MTYPE(RFAPI, RFAPI_NVE_ADDR, "RFAPI NVE Address"); +DEFINE_MTYPE(RFAPI, RFAPI_PREFIX_BAG, "RFAPI Prefix Bag"); +DEFINE_MTYPE(RFAPI, RFAPI_IT_EXTRA, "RFAPI IT Extra"); +DEFINE_MTYPE(RFAPI, RFAPI_INFO, "RFAPI Info"); +DEFINE_MTYPE(RFAPI, RFAPI_ADDR, "RFAPI Addr"); +DEFINE_MTYPE(RFAPI, RFAPI_UPDATED_RESPONSE_QUEUE, "RFAPI Updated Rsp Queue"); +DEFINE_MTYPE(RFAPI, RFAPI_RECENT_DELETE, "RFAPI Recently Deleted Route"); +DEFINE_MTYPE(RFAPI, RFAPI_L2ADDR_OPT, "RFAPI L2 Address Option"); +DEFINE_MTYPE(RFAPI, RFAPI_AP, "RFAPI Advertised Prefix"); +DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ETH, "RFAPI Monitor Ethernet"); + +DEFINE_QOBJ_TYPE(rfapi_nve_group_cfg); +DEFINE_QOBJ_TYPE(rfapi_l2_group_cfg); +/*********************************************************************** + * RFAPI Support + ***********************************************************************/ + + +/* + * compaitibility to old quagga_time call + * time_t value in terms of stabilised absolute time. + * replacement for POSIX time() + */ +time_t rfapi_time(time_t *t) +{ + time_t clock = monotime(NULL); + if (t) + *t = clock; + return clock; +} + +void nve_group_to_nve_list(struct rfapi_nve_group_cfg *rfg, struct list **nves, + uint8_t family) /* AF_INET, AF_INET6 */ +{ + struct listnode *hln; + struct rfapi_descriptor *rfd; + + /* + * loop over nves in this grp, add to list + */ + for (ALL_LIST_ELEMENTS_RO(rfg->nves, hln, rfd)) { + if (rfd->vn_addr.addr_family == family) { + if (!*nves) + *nves = list_new(); + listnode_add(*nves, rfd); + } + } +} + + +struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc, + struct prefix *vn, + struct prefix *un) +{ + struct rfapi_nve_group_cfg *rfg_vn = NULL; + struct rfapi_nve_group_cfg *rfg_un = NULL; + + struct agg_table *rt_vn; + struct agg_table *rt_un; + struct agg_node *rn_vn; + struct agg_node *rn_un; + + struct rfapi_nve_group_cfg *rfg; + struct listnode *node, *nnode; + + switch (vn->family) { + case AF_INET: + rt_vn = hc->nve_groups_vn[AFI_IP]; + break; + case AF_INET6: + rt_vn = hc->nve_groups_vn[AFI_IP6]; + break; + default: + return NULL; + } + + switch (un->family) { + case AF_INET: + rt_un = hc->nve_groups_un[AFI_IP]; + break; + case AF_INET6: + rt_un = hc->nve_groups_un[AFI_IP6]; + break; + default: + return NULL; + } + + rn_vn = agg_node_match(rt_vn, vn); /* NB locks node */ + if (rn_vn) { + rfg_vn = rn_vn->info; + agg_unlock_node(rn_vn); + } + + rn_un = agg_node_match(rt_un, un); /* NB locks node */ + if (rn_un) { + rfg_un = rn_un->info; + agg_unlock_node(rn_un); + } + +#ifdef BGP_VNC_DEBUG_MATCH_GROUP + { + vnc_zlog_debug_verbose("%s: vn prefix: %pFX", __func__, vn); + vnc_zlog_debug_verbose("%s: un prefix: %pFX", __func__, un); + vnc_zlog_debug_verbose( + "%s: rn_vn=%p, rn_un=%p, rfg_vn=%p, rfg_un=%p", + __func__, rn_vn, rn_un, rfg_vn, rfg_un); + } +#endif + + + if (rfg_un == rfg_vn) /* same group */ + return rfg_un; + if (!rfg_un) /* un doesn't match, return vn-matched grp */ + return rfg_vn; + if (!rfg_vn) /* vn doesn't match, return un-matched grp */ + return rfg_un; + + /* + * Two different nve groups match: the group configured earlier wins. + * For now, just walk the sequential list and pick the first one. + * If this approach is too slow, then store serial numbers in the + * nve group structures as they are defined and just compare + * serial numbers. + */ + for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode, rfg)) { + if ((rfg == rfg_un) || (rfg == rfg_vn)) { + return rfg; + } + } + vnc_zlog_debug_verbose( + "%s: shouldn't happen, returning NULL when un and vn match", + __func__); + return NULL; /* shouldn't happen */ +} + +/*------------------------------------------ + * rfapi_get_rfp_start_val + * + * Returns value passed to rfapi on rfp_start + * + * input: + * void * bgp structure + * + * returns: + * void * + *------------------------------------------*/ +void *rfapi_get_rfp_start_val(void *bgpv) +{ + struct bgp *bgp = bgpv; + if (bgp == NULL || bgp->rfapi == NULL) + return NULL; + return bgp->rfapi->rfp; +} + +/*------------------------------------------ + * bgp_rfapi_is_vnc_configured + * + * Returns if VNC is configured + * + * input: + * bgp NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * EPERM Not Default instance (VNC operations not allowed) + * ENXIO VNC not configured + --------------------------------------------*/ +int bgp_rfapi_is_vnc_configured(struct bgp *bgp) +{ + if (bgp == NULL) + bgp = bgp_get_default(); + + if (bgp && bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) + return EPERM; + + if (bgp && bgp->rfapi_cfg) + return 0; + return ENXIO; +} + +/*********************************************************************** + * VNC Configuration/CLI + ***********************************************************************/ +#define VNC_VTY_CONFIG_CHECK(bgp) \ + { \ + switch (bgp_rfapi_is_vnc_configured(bgp)) { \ + case EPERM: \ + vty_out(vty, \ + "VNC operations only permitted on default BGP instance.\n"); \ + return CMD_WARNING_CONFIG_FAILED; \ + break; \ + case ENXIO: \ + vty_out(vty, "VNC not configured.\n"); \ + return CMD_WARNING_CONFIG_FAILED; \ + break; \ + default: \ + break; \ + } \ + } + +DEFUN (vnc_advertise_un_method, + vnc_advertise_un_method_cmd, + "vnc advertise-un-method encap-attr", + VNC_CONFIG_STR + "Method of advertising UN addresses\n" + "Via Tunnel Encap attribute (in VPN SAFI)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VNC_VTY_CONFIG_CHECK(bgp); + + if (!strncmp(argv[2]->arg, "encap-safi", 7)) { + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP; + } else { + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP; + } + + return CMD_SUCCESS; +} + +/*------------------------------------------------------------------------- + * RFG defaults + *-----------------------------------------------------------------------*/ + + +DEFUN_NOSH (vnc_defaults, + vnc_defaults_cmd, + "vnc defaults", VNC_CONFIG_STR "Configure default NVE group\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VNC_VTY_CONFIG_CHECK(bgp); + if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + vty_out(vty, "Malformed community-list value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + vty->node = BGP_VNC_DEFAULTS_NODE; + return CMD_SUCCESS; +} + +static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, + struct ecommunity **list) +{ + struct ecommunity *ecom = NULL; + struct ecommunity *ecomadd; + + for (; argc; --argc, ++argv) { + + ecomadd = ecommunity_str2com(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "Malformed community-list value\n"); + if (ecom) + ecommunity_free(&ecom); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ecom) { + ecommunity_merge(ecom, ecomadd); + ecommunity_free(&ecomadd); + } else { + ecom = ecomadd; + } + } + + if (*list) { + ecommunity_free(&*list); + } + *list = ecom; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_rt_import, + vnc_defaults_rt_import_cmd, + "rt import RTLIST...", + "Specify default route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + return set_ecom_list(vty, argc - 2, argv + 2, + &bgp->rfapi_cfg->default_rt_import_list); +} + +DEFUN (vnc_defaults_rt_export, + vnc_defaults_rt_export_cmd, + "rt export RTLIST...", + "Configure default route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + return set_ecom_list(vty, argc - 2, argv + 2, + &bgp->rfapi_cfg->default_rt_export_list); +} + +DEFUN (vnc_defaults_rt_both, + vnc_defaults_rt_both_cmd, + "rt both RTLIST...", + "Configure default route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + + rc = set_ecom_list(vty, argc - 2, argv + 2, + &bgp->rfapi_cfg->default_rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + return set_ecom_list(vty, argc - 2, argv + 2, + &bgp->rfapi_cfg->default_rt_export_list); +} + +DEFUN (vnc_defaults_rd, + vnc_defaults_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Specify default route distinguisher\n" + "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + struct prefix_rd prd; + + if (!strncmp(argv[1]->arg, "auto:vn:", 8)) { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!argv[1]->arg[8] || *end) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (value32 > 0xffff) { + vty_out(vty, "%% Malformed rd (must be less than %u\n", + 0x0ffff); + return CMD_WARNING_CONFIG_FAILED; + } + + memset(&prd, 0, sizeof(prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } else { + + /* TODO: save RD format */ + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + bgp->rfapi_cfg->default_rd = prd; + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_l2rd, + vnc_defaults_l2rd_cmd, + "l2rd <(1-255)|auto-vn>", + "Specify default Local Nve ID value to use in RD for L2 routes\n" + "Fixed value 1-255\n" + "use the low-order octet of the NVE's VN address\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + uint8_t value = 0; + + if (strmatch(argv[1]->text, "auto-vn")) { + value = 0; + } else { + char *end = NULL; + unsigned long value_l = strtoul(argv[1]->arg, &end, 10); + + value = value_l & 0xff; + if (!argv[1]->arg[0] || *end) { + vty_out(vty, "%% Malformed l2 nve ID \"%s\"\n", + argv[1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if ((value_l < 1) || (value_l > 0xff)) { + vty_out(vty, + "%% Malformed l2 nve id (must be greater than 0 and less than %u\n", + 0x100); + return CMD_WARNING_CONFIG_FAILED; + } + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_L2RD; + bgp->rfapi_cfg->default_l2rd = value; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_no_l2rd, + vnc_defaults_no_l2rd_cmd, + "no l2rd", + NO_STR + "Specify default Local Nve ID value to use in RD for L2 routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + bgp->rfapi_cfg->default_l2rd = 0; + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_defaults_responselifetime, + vnc_defaults_responselifetime_cmd, + "response-lifetime <LIFETIME|infinite>", + "Specify default response lifetime\n" + "Response lifetime in seconds\n" "Infinite response lifetime\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + uint32_t rspint; + struct rfapi *h = NULL; + struct listnode *hdnode; + struct rfapi_descriptor *rfd; + + h = bgp->rfapi; + if (!h) + return CMD_WARNING_CONFIG_FAILED; + + if (strmatch(argv[1]->text, "infinite")) { + rspint = RFAPI_INFINITE_LIFETIME; + } else { + rspint = strtoul(argv[1]->arg, NULL, 10); + if (rspint > INT32_MAX) + rspint = INT32_MAX; /* is really an int, not an unsigned + int */ + } + + bgp->rfapi_cfg->default_response_lifetime = rspint; + + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, hdnode, rfd)) + if (rfd->rfg + && !(rfd->rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME)) + rfd->response_lifetime = rfd->rfg->response_lifetime = + rspint; + + return CMD_SUCCESS; +} + +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_byname(struct bgp *bgp, const char *name, + rfapi_group_cfg_type_t type) /* _MAX = any */ +{ + struct rfapi_nve_group_cfg *rfg; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->nve_groups_sequential, node, + nnode, rfg)) { + if ((type == RFAPI_GROUP_CFG_MAX || type == rfg->type) + && !strcmp(rfg->name, name)) + return rfg; + } + return NULL; +} + +static struct rfapi_nve_group_cfg * +rfapi_group_new(struct bgp *bgp, rfapi_group_cfg_type_t type, const char *name) +{ + struct rfapi_nve_group_cfg *rfg; + + rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG, + sizeof(struct rfapi_nve_group_cfg)); + rfg->type = type; + rfg->name = strdup(name); + /* add to tail of list */ + listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg); + rfg->label = MPLS_LABEL_NONE; + + QOBJ_REG(rfg, rfapi_nve_group_cfg); + + return rfg; +} + +static struct rfapi_l2_group_cfg *rfapi_l2_group_lookup_byname(struct bgp *bgp, + const char *name) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node, *nnode; + + if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */ + bgp->rfapi_cfg->l2_groups = list_new(); + + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->l2_groups, node, nnode, rfg)) { + if (!strcmp(rfg->name, name)) + return rfg; + } + return NULL; +} + +static struct rfapi_l2_group_cfg *rfapi_l2_group_new(void) +{ + struct rfapi_l2_group_cfg *rfg; + + rfg = XCALLOC(MTYPE_RFAPI_L2_CFG, sizeof(struct rfapi_l2_group_cfg)); + QOBJ_REG(rfg, rfapi_l2_group_cfg); + + return rfg; +} + +static void rfapi_l2_group_del(struct rfapi_l2_group_cfg *rfg) +{ + QOBJ_UNREG(rfg); + XFREE(MTYPE_RFAPI_L2_CFG, rfg); +} + +static int rfapi_str2route_type(const char *l3str, const char *pstr, afi_t *afi, + int *type) +{ + if (!l3str || !pstr) + return EINVAL; + + if (!strcmp(l3str, "ipv4")) { + *afi = AFI_IP; + } else { + if (!strcmp(l3str, "ipv6")) + *afi = AFI_IP6; + else + return ENOENT; + } + + if (!strcmp(pstr, "connected")) + *type = ZEBRA_ROUTE_CONNECT; + if (!strcmp(pstr, "kernel")) + *type = ZEBRA_ROUTE_KERNEL; + if (!strcmp(pstr, "static")) + *type = ZEBRA_ROUTE_STATIC; + if (!strcmp(pstr, "bgp")) + *type = ZEBRA_ROUTE_BGP; + if (!strcmp(pstr, "bgp-direct")) + *type = ZEBRA_ROUTE_BGP_DIRECT; + if (!strcmp(pstr, "bgp-direct-to-nve-groups")) + *type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + + if (!strcmp(pstr, "rip")) { + if (*afi == AFI_IP) + *type = ZEBRA_ROUTE_RIP; + else + *type = ZEBRA_ROUTE_RIPNG; + } + + if (!strcmp(pstr, "ripng")) { + if (*afi == AFI_IP) + return EAFNOSUPPORT; + *type = ZEBRA_ROUTE_RIPNG; + } + + if (!strcmp(pstr, "ospf")) { + if (*afi == AFI_IP) + *type = ZEBRA_ROUTE_OSPF; + else + *type = ZEBRA_ROUTE_OSPF6; + } + + if (!strcmp(pstr, "ospf6")) { + if (*afi == AFI_IP) + return EAFNOSUPPORT; + *type = ZEBRA_ROUTE_OSPF6; + } + + return 0; +} + +/*------------------------------------------------------------------------- + * redistribute + *-----------------------------------------------------------------------*/ + +#define VNC_REDIST_ENABLE(bgp, afi, type) \ + do { \ + switch (type) { \ + case ZEBRA_ROUTE_BGP_DIRECT: \ + vnc_import_bgp_redist_enable((bgp), (afi)); \ + break; \ + case ZEBRA_ROUTE_BGP_DIRECT_EXT: \ + vnc_import_bgp_exterior_redist_enable((bgp), (afi)); \ + break; \ + default: \ + if ((type) < ZEBRA_ROUTE_MAX) \ + vnc_redistribute_set((bgp), (afi), (type)); \ + break; \ + } \ + } while (0) + +#define VNC_REDIST_DISABLE(bgp, afi, type) \ + do { \ + switch (type) { \ + case ZEBRA_ROUTE_BGP_DIRECT: \ + vnc_import_bgp_redist_disable((bgp), (afi)); \ + break; \ + case ZEBRA_ROUTE_BGP_DIRECT_EXT: \ + vnc_import_bgp_exterior_redist_disable((bgp), (afi)); \ + break; \ + default: \ + if ((type) < ZEBRA_ROUTE_MAX) \ + vnc_redistribute_unset((bgp), (afi), (type)); \ + break; \ + } \ + } while (0) + +static uint8_t redist_was_enabled[AFI_MAX][ZEBRA_ROUTE_MAX]; + +static void vnc_redistribute_prechange(struct bgp *bgp) +{ + afi_t afi; + int type; + + vnc_zlog_debug_verbose("%s: entry", __func__); + memset(redist_was_enabled, 0, sizeof(redist_was_enabled)); + + /* + * Look to see if we have any redistribution enabled. If so, flush + * the corresponding routes and turn off redistribution temporarily. + * We need to do it because the RD's used for the redistributed + * routes depend on the nve group. + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) { + if (bgp->rfapi_cfg->redist[afi][type]) { + redist_was_enabled[afi][type] = 1; + VNC_REDIST_DISABLE(bgp, afi, type); + } + } + } + vnc_zlog_debug_verbose("%s: return", __func__); +} + +static void vnc_redistribute_postchange(struct bgp *bgp) +{ + afi_t afi; + int type; + + vnc_zlog_debug_verbose("%s: entry", __func__); + /* + * If we turned off redistribution above, turn it back on. Doing so + * will tell zebra to resend the routes to us + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) { + if (redist_was_enabled[afi][type]) { + VNC_REDIST_ENABLE(bgp, afi, type); + } + } + } + vnc_zlog_debug_verbose("%s: return", __func__); +} + +DEFUN (vnc_redistribute_rh_roo_localadmin, + vnc_redistribute_rh_roo_localadmin_cmd, + "vnc redistribute resolve-nve roo-ec-local-admin (0-65535)", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "Resolve-NVE mode\n" + "Route Origin Extended Community Local Admin Field\n" "Field value\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + uint32_t localadmin; + char *endptr; + + VNC_VTY_CONFIG_CHECK(bgp); + + localadmin = strtoul(argv[4]->arg, &endptr, 0); + if (!argv[4]->arg[0] || *endptr) { + vty_out(vty, "%% Malformed value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (localadmin > 0xffff) { + vty_out(vty, "%% Value out of range (0-%d)\n", 0xffff); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->resolve_nve_roo_local_admin == localadmin) + return CMD_SUCCESS; + + if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) + == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) { + + vnc_export_bgp_prechange(bgp); + } + vnc_redistribute_prechange(bgp); + + bgp->rfapi_cfg->resolve_nve_roo_local_admin = localadmin; + + if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) + == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) { + + vnc_export_bgp_postchange(bgp); + } + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + + +DEFUN (vnc_redistribute_mode, + vnc_redistribute_mode_cmd, + "vnc redistribute mode <nve-group|plain|resolve-nve>", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "Redistribution mode\n" + "Based on redistribute nve-group\n" + "Unmodified\n" "Resolve each nexthop to connected NVEs\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + vnc_redist_mode_t newmode; + + VNC_VTY_CONFIG_CHECK(bgp); + + switch (argv[3]->arg[0]) { + case 'n': + newmode = VNC_REDIST_MODE_RFG; + break; + + case 'p': + newmode = VNC_REDIST_MODE_PLAIN; + break; + + case 'r': + newmode = VNC_REDIST_MODE_RESOLVE_NVE; + break; + + default: + vty_out(vty, "unknown redistribute mode\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (newmode != bgp->rfapi_cfg->redist_mode) { + vnc_redistribute_prechange(bgp); + bgp->rfapi_cfg->redist_mode = newmode; + vnc_redistribute_postchange(bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_protocol, + vnc_redistribute_protocol_cmd, + "vnc redistribute <ipv4|ipv6> <bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static>", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP\n" + "From BGP without Zebra\n" + "From BGP without Zebra, only to configured NVE groups\n" + "Connected interfaces\n" + "From kernel routes\n" + "From Open Shortest Path First (OSPF)\n" + "From Routing Information Protocol (RIP)\n" "From Static routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int type = ZEBRA_ROUTE_MAX; /* init to bogus value */ + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + if (rfapi_str2route_type(argv[2]->arg, argv[3]->arg, &afi, &type)) { + vty_out(vty, "%% Invalid route type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) { + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) { + VNC_REDIST_DISABLE(bgp, afi, + type); /* disabled view implicitly */ + free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; + } + bgp->rfapi_cfg->redist_bgp_exterior_view = bgp; + } + + VNC_REDIST_ENABLE(bgp, afi, type); + + return CMD_SUCCESS; +} + +DEFUN (vnc_no_redistribute_protocol, + vnc_no_redistribute_protocol_cmd, + "no vnc redistribute <ipv4|ipv6> <bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static>", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP\n" + "From BGP without Zebra\n" + "From BGP without Zebra, only to configured NVE groups\n" + "Connected interfaces\n" + "From kernel routes\n" + "From Open Shortest Path First (OSPF)\n" + "From Routing Information Protocol (RIP)\n" "From Static routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int type; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + if (rfapi_str2route_type(argv[3]->arg, argv[4]->arg, &afi, &type)) { + vty_out(vty, "%% Invalid route type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + VNC_REDIST_DISABLE(bgp, afi, type); + + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) { + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) { + free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL; + } + bgp->rfapi_cfg->redist_bgp_exterior_view = NULL; + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_bgp_exterior, + vnc_redistribute_bgp_exterior_cmd, + "vnc redistribute <ipv4|ipv6> bgp-direct-to-nve-groups view NAME", + VNC_CONFIG_STR + "Redistribute routes into VNC\n" + "IPv4 routes\n" + "IPv6 routes\n" + "From BGP without Zebra, only to configured NVE groups\n" + "From BGP view\n" "BGP view name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int type; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + if (rfapi_str2route_type(argv[2]->arg, "bgp-direct-to-nve-groups", &afi, + &type)) { + vty_out(vty, "%% Invalid route type\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) + free(bgp->rfapi_cfg->redist_bgp_exterior_view_name); + bgp->rfapi_cfg->redist_bgp_exterior_view_name = strdup(argv[5]->arg); + /* could be NULL if name is not defined yet */ + bgp->rfapi_cfg->redist_bgp_exterior_view = + bgp_lookup_by_name(argv[5]->arg); + + VNC_REDIST_ENABLE(bgp, afi, type); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_nvegroup, + vnc_redistribute_nvegroup_cmd, + "vnc redistribute nve-group NAME", + VNC_CONFIG_STR + "Assign a NVE group to routes redistributed from another routing protocol\n" + "NVE group\n" "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VNC_VTY_CONFIG_CHECK(bgp); + + vnc_redistribute_prechange(bgp); + + /* + * OK if nve group doesn't exist yet; we'll set the pointer + * when the group is defined later + */ + bgp->rfapi_cfg->rfg_redist = bgp_rfapi_cfg_match_byname( + bgp, argv[3]->arg, RFAPI_GROUP_CFG_NVE); + if (bgp->rfapi_cfg->rfg_redist_name) + free(bgp->rfapi_cfg->rfg_redist_name); + bgp->rfapi_cfg->rfg_redist_name = strdup(argv[3]->arg); + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redistribute_no_nvegroup, + vnc_redistribute_no_nvegroup_cmd, + "no vnc redistribute nve-group", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Assign a NVE group to routes redistributed from another routing protocol\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + VNC_VTY_CONFIG_CHECK(bgp); + + vnc_redistribute_prechange(bgp); + + bgp->rfapi_cfg->rfg_redist = NULL; + if (bgp->rfapi_cfg->rfg_redist_name) + free(bgp->rfapi_cfg->rfg_redist_name); + bgp->rfapi_cfg->rfg_redist_name = NULL; + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + + +DEFUN (vnc_redistribute_lifetime, + vnc_redistribute_lifetime_cmd, + "vnc redistribute lifetime <LIFETIME|infinite>", + VNC_CONFIG_STR + "Redistribute\n" + "Assign a lifetime to routes redistributed from another routing protocol\n" + "lifetime value (32 bit)\n" + "Allow lifetime to never expire\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VNC_VTY_CONFIG_CHECK(bgp); + + vnc_redistribute_prechange(bgp); + + if (strmatch(argv[3]->text, "infinite")) { + bgp->rfapi_cfg->redist_lifetime = RFAPI_INFINITE_LIFETIME; + } else { + bgp->rfapi_cfg->redist_lifetime = + strtoul(argv[3]->arg, NULL, 10); + } + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, non-nvegroup start --*/ + +DEFUN (vnc_redist_bgpdirect_no_prefixlist, + vnc_redist_bgpdirect_no_prefixlist_cmd, + "no vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> <ipv4|ipv6> prefix-list", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "IPv4 routes\n" + "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + afi_t afi; + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[3]->text, "bgp-direct")) { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } else { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + if (strmatch(argv[4]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + vnc_redistribute_prechange(bgp); + + if (hc->plist_redist_name[route_type][afi]) + free(hc->plist_redist_name[route_type][afi]); + hc->plist_redist_name[route_type][afi] = NULL; + hc->plist_redist[route_type][afi] = NULL; + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_prefixlist, + vnc_redist_bgpdirect_prefixlist_cmd, + "vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> <ipv4|ipv6> prefix-list NAME", + VNC_CONFIG_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering redistributed routes\n" + "prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + afi_t afi; + uint8_t route_type = 0; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[2]->text, "bgp-direct")) { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } else { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + if (strmatch(argv[3]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + vnc_redistribute_prechange(bgp); + + if (hc->plist_redist_name[route_type][afi]) + free(hc->plist_redist_name[route_type][afi]); + hc->plist_redist_name[route_type][afi] = strdup(argv[5]->arg); + hc->plist_redist[route_type][afi] = + prefix_list_lookup(afi, argv[5]->arg); + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_no_routemap, + vnc_redist_bgpdirect_no_routemap_cmd, + "no vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> route-map", + NO_STR + VNC_CONFIG_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "Route-map for filtering redistributed routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[3]->text, "bgp-direct")) { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } else { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + vnc_redistribute_prechange(bgp); + + if (hc->routemap_redist_name[route_type]) + free(hc->routemap_redist_name[route_type]); + hc->routemap_redist_name[route_type] = NULL; + hc->routemap_redist[route_type] = NULL; + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_redist_bgpdirect_routemap, + vnc_redist_bgpdirect_routemap_cmd, + "vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> route-map NAME", + VNC_CONFIG_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Redistribute from BGP without Zebra, only to configured NVE groups\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + uint8_t route_type = 0; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[2]->text, "bgp-direct")) { + route_type = ZEBRA_ROUTE_BGP_DIRECT; + } else { + route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT; + } + + vnc_redistribute_prechange(bgp); + + if (hc->routemap_redist_name[route_type]) + free(hc->routemap_redist_name[route_type]); + + /* If the old route map config overwrite with new + * route map config , old routemap counter have to be + * reduced. + */ + route_map_counter_decrement(hc->routemap_redist[route_type]); + hc->routemap_redist_name[route_type] = strdup(argv[4]->arg); + hc->routemap_redist[route_type] = + route_map_lookup_by_name(argv[4]->arg); + route_map_counter_increment(hc->routemap_redist[route_type]); + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, non-nvegroup end --*/ + +/*-- redist policy, nvegroup start --*/ + +DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist, + vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd, + "no redistribute bgp-direct <ipv4|ipv6> prefix-list", + NO_STR + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering redistributed routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg) + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strmatch(argv[3]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + vnc_redistribute_prechange(bgp); + + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL; + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist, + vnc_nve_group_redist_bgpdirect_prefixlist_cmd, + "redistribute bgp-direct <ipv4|ipv6> prefix-list NAME", + "Redistribute from other protocol\n" + "Redistribute from BGP directly\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering redistributed routes\n" + "prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strmatch(argv[2]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + vnc_redistribute_prechange(bgp); + + if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) + free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]); + rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = + strdup(argv[4]->arg); + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = + prefix_list_lookup(afi, argv[4]->arg); + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap, + vnc_nve_group_redist_bgpdirect_no_routemap_cmd, + "no redistribute bgp-direct route-map", + NO_STR + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Route-map for filtering redistributed routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + vnc_redistribute_prechange(bgp); + + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + route_map_counter_decrement( + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]); + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = NULL; + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = NULL; + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_redist_bgpdirect_routemap, + vnc_nve_group_redist_bgpdirect_routemap_cmd, + "redistribute bgp-direct route-map NAME", + "Redistribute from other protocols\n" + "Redistribute from BGP directly\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + vnc_redistribute_prechange(bgp); + + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) + free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + route_map_counter_decrement( + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]); + rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = + strdup(argv[3]->arg); + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = + route_map_lookup_by_name(argv[3]->arg); + route_map_counter_increment( + rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]); + + vnc_redistribute_postchange(bgp); + + return CMD_SUCCESS; +} + +/*-- redist policy, nvegroup end --*/ + +/*------------------------------------------------------------------------- + * export + *-----------------------------------------------------------------------*/ + +DEFUN (vnc_export_mode, + vnc_export_mode_cmd, + "vnc export <bgp|zebra> mode <group-nve|ce|none|registering-nve>", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Select export mode\n" + "Export routes with nve-group next-hops\n" + "Export routes with NVE connected router next-hops\n" + "Disable export\n" "Export routes with registering NVE as next-hop\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + uint32_t oldmode = 0; + uint32_t newmode = 0; + + VNC_VTY_CONFIG_CHECK(bgp); + + if (argv[2]->arg[0] == 'b') { + oldmode = bgp->rfapi_cfg->flags + & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS; + switch (argv[4]->arg[0]) { + case 'g': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP; + break; + case 'c': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE; + break; + case 'n': + newmode = 0; + break; + case 'r': + newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH; + break; + default: + vty_out(vty, "Invalid mode specified\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (newmode == oldmode) { + vty_out(vty, "Mode unchanged\n"); + return CMD_SUCCESS; + } + + vnc_export_bgp_prechange(bgp); + + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS; + bgp->rfapi_cfg->flags |= newmode; + + vnc_export_bgp_postchange(bgp); + + + } else { + /* + * export to zebra with RH mode is not yet implemented + */ + vty_out(vty, + "Changing modes for zebra export not implemented yet\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +static struct rfapi_rfg_name *rfgn_new(void) +{ + return XCALLOC(MTYPE_RFAPI_RFG_NAME, sizeof(struct rfapi_rfg_name)); +} + +static void rfgn_free(struct rfapi_rfg_name *rfgn) +{ + XFREE(MTYPE_RFAPI_RFG_NAME, rfgn); +} + +DEFUN (vnc_export_nvegroup, + vnc_export_nvegroup_cmd, + "vnc export <bgp|zebra> group-nve group NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "NVE group, used in 'group-nve' export mode\n" + "NVE group\n" "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_nve_group_cfg *rfg_new; + + VNC_VTY_CONFIG_CHECK(bgp); + + rfg_new = bgp_rfapi_cfg_match_byname(bgp, argv[5]->arg, + RFAPI_GROUP_CFG_NVE); + if (rfg_new == NULL) { + rfg_new = bgp_rfapi_cfg_match_byname(bgp, argv[5]->arg, + RFAPI_GROUP_CFG_VRF); + if (rfg_new) + vnc_add_vrf_opener(bgp, rfg_new); + } + + if (rfg_new == NULL) { + vty_out(vty, "Can't find group named \"%s\".\n", argv[5]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[2]->arg[0] == 'b') { + + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Set group for export to BGP Direct + */ + + /* see if group is already included in export list */ + for (ALL_LIST_ELEMENTS_RO( + bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (!strcmp(rfgn->name, argv[5]->arg)) { + /* already in the list: we're done */ + return CMD_SUCCESS; + } + } + + rfgn = rfgn_new(); + rfgn->name = strdup(argv[5]->arg); + rfgn->rfg = rfg_new; /* OK if not set yet */ + + listnode_add(bgp->rfapi_cfg->rfg_export_direct_bgp_l, rfgn); + + vnc_zlog_debug_verbose("%s: testing rfg_new", __func__); + if (rfg_new) { + vnc_zlog_debug_verbose( + "%s: testing bgp grp mode enabled", __func__); + if (VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) + vnc_zlog_debug_verbose( + "%s: calling vnc_direct_bgp_add_group", + __func__); + vnc_direct_bgp_add_group(bgp, rfg_new); + } + + } else { + + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* + * Set group for export to Zebra + */ + + /* see if group is already included in export list */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, + node, rfgn)) { + + if (!strcmp(rfgn->name, argv[5]->arg)) { + /* already in the list: we're done */ + return CMD_SUCCESS; + } + } + + rfgn = rfgn_new(); + rfgn->name = strdup(argv[5]->arg); + rfgn->rfg = rfg_new; /* OK if not set yet */ + + listnode_add(bgp->rfapi_cfg->rfg_export_zebra_l, rfgn); + + if (rfg_new) { + if (VNC_EXPORT_ZEBRA_GRP_ENABLED(bgp->rfapi_cfg)) + vnc_zebra_add_group(bgp, rfg_new); + } + } + + return CMD_SUCCESS; +} + +/* + * This command applies to routes exported from VNC to BGP directly + * without going though zebra + */ +DEFUN (vnc_no_export_nvegroup, + vnc_no_export_nvegroup_cmd, + "vnc export <bgp|zebra> group-nve no group NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "NVE group, used in 'group-nve' export mode\n" + "Disable export of VNC routes\n" "NVE group\n" "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + VNC_VTY_CONFIG_CHECK(bgp); + + if (argv[2]->arg[0] == 'b') { + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) { + + if (rfgn->name && !strcmp(rfgn->name, argv[6]->arg)) { + vnc_zlog_debug_verbose("%s: matched \"%s\"", + __func__, rfgn->name); + if (rfgn->rfg) + vnc_direct_bgp_del_group(bgp, + rfgn->rfg); + free(rfgn->name); + list_delete_node( + bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node); + rfgn_free(rfgn); + break; + } + } + } else { + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_zebra_l, node, + nnode, rfgn)) { + + vnc_zlog_debug_verbose("does rfg \"%s\" match?", + rfgn->name); + if (rfgn->name && !strcmp(rfgn->name, argv[6]->arg)) { + if (rfgn->rfg) + vnc_zebra_del_group(bgp, rfgn->rfg); + free(rfgn->name); + list_delete_node( + bgp->rfapi_cfg->rfg_export_zebra_l, + node); + rfgn_free(rfgn); + break; + } + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_export_no_prefixlist, + vnc_nve_group_export_no_prefixlist_cmd, + "no export <bgp|zebra> <ipv4|ipv6> prefix-list [NAME]", + NO_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int idx = 0; + int is_bgp = 1; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + vty_out(vty, "%% Malformed Address Family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[idx - 1]->text[0] == 'z') + is_bgp = 0; + idx += 2; /* skip afi and keyword */ + + if (is_bgp) { + if (idx == argc + || (rfg->plist_export_bgp_name[afi] + && strmatch(argv[idx]->arg, + rfg->plist_export_bgp_name[afi]))) { + if (rfg->plist_export_bgp_name[afi]) + free(rfg->plist_export_bgp_name[afi]); + rfg->plist_export_bgp_name[afi] = NULL; + rfg->plist_export_bgp[afi] = NULL; + + vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi); + } + } else { + if (idx == argc + || (rfg->plist_export_zebra_name[afi] + && strmatch(argv[idx]->arg, + rfg->plist_export_zebra_name[afi]))) { + if (rfg->plist_export_zebra_name[afi]) + free(rfg->plist_export_zebra_name[afi]); + rfg->plist_export_zebra_name[afi] = NULL; + rfg->plist_export_zebra[afi] = NULL; + + vnc_zebra_reexport_group_afi(bgp, rfg, afi); + } + } + return CMD_SUCCESS; +} + +ALIAS (vnc_nve_group_export_no_prefixlist, + vnc_vrf_policy_export_no_prefixlist_cmd, + "no export <ipv4|ipv6> prefix-list [NAME]", + NO_STR + "Export to VRF\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") + +DEFUN (vnc_nve_group_export_prefixlist, + vnc_nve_group_export_prefixlist_cmd, + "export <bgp|zebra> <ipv4|ipv6> prefix-list NAME", + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int idx = 0; + int is_bgp = 1; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + vty_out(vty, "%% Malformed Address Family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[idx - 1]->text[0] == 'z') + is_bgp = 0; + idx = argc - 1; + + if (is_bgp) { + if (rfg->plist_export_bgp_name[afi]) + free(rfg->plist_export_bgp_name[afi]); + rfg->plist_export_bgp_name[afi] = strdup(argv[idx]->arg); + rfg->plist_export_bgp[afi] = + prefix_list_lookup(afi, argv[idx]->arg); + + vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi); + + } else { + if (rfg->plist_export_zebra_name[afi]) + free(rfg->plist_export_zebra_name[afi]); + rfg->plist_export_zebra_name[afi] = strdup(argv[idx]->arg); + rfg->plist_export_zebra[afi] = + prefix_list_lookup(afi, argv[idx]->arg); + + vnc_zebra_reexport_group_afi(bgp, rfg, afi); + } + return CMD_SUCCESS; +} + +ALIAS (vnc_nve_group_export_prefixlist, + vnc_vrf_policy_export_prefixlist_cmd, + "export <ipv4|ipv6> prefix-list NAME", + "Export to VRF\n" + "IPv4 routes\n" + "IPv6 routes\n" + "Prefix-list for filtering exported routes\n" "prefix list name\n") + +DEFUN (vnc_nve_group_export_no_routemap, + vnc_nve_group_export_no_routemap_cmd, + "no export <bgp|zebra> route-map [NAME]", + NO_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int idx = 2; + int is_bgp = 1; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + switch (argv[idx]->text[0]) { + case 'z': + is_bgp = 0; + /* fall thru */ + case 'b': + idx += 2; + break; + default: /* route-map */ + idx++; + break; + } + + if (is_bgp) { + if (idx == argc + || (rfg->routemap_export_bgp_name + && strmatch(argv[idx]->arg, + rfg->routemap_export_bgp_name))) { + if (rfg->routemap_export_bgp_name) + free(rfg->routemap_export_bgp_name); + route_map_counter_decrement(rfg->routemap_export_bgp); + rfg->routemap_export_bgp_name = NULL; + rfg->routemap_export_bgp = NULL; + + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6); + } + } else { + if (idx == argc + || (rfg->routemap_export_zebra_name + && strmatch(argv[idx]->arg, + rfg->routemap_export_zebra_name))) { + if (rfg->routemap_export_zebra_name) + free(rfg->routemap_export_zebra_name); + route_map_counter_decrement(rfg->routemap_export_zebra); + rfg->routemap_export_zebra_name = NULL; + rfg->routemap_export_zebra = NULL; + + vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP); + vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP6); + } + } + return CMD_SUCCESS; +} + +ALIAS (vnc_nve_group_export_no_routemap, + vnc_vrf_policy_export_no_routemap_cmd, + "no export route-map [NAME]", + NO_STR + "Export to VRF\n" + "Route-map for filtering exported routes\n" "route map name\n") + +DEFUN (vnc_nve_group_export_routemap, + vnc_nve_group_export_routemap_cmd, + "export <bgp|zebra> route-map NAME", + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int idx = 0; + int is_bgp = 1; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[1]->text[0] == 'z') + is_bgp = 0; + idx = argc - 1; + + if (is_bgp) { + if (rfg->routemap_export_bgp_name) + free(rfg->routemap_export_bgp_name); + route_map_counter_decrement(rfg->routemap_export_bgp); + rfg->routemap_export_bgp_name = strdup(argv[idx]->arg); + rfg->routemap_export_bgp = + route_map_lookup_by_name(argv[idx]->arg); + route_map_counter_increment(rfg->routemap_export_bgp); + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6); + } else { + if (rfg->routemap_export_zebra_name) + free(rfg->routemap_export_zebra_name); + route_map_counter_decrement(rfg->routemap_export_zebra); + rfg->routemap_export_zebra_name = strdup(argv[idx]->arg); + rfg->routemap_export_zebra = + route_map_lookup_by_name(argv[idx]->arg); + route_map_counter_increment(rfg->routemap_export_zebra); + vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP); + vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP6); + } + return CMD_SUCCESS; +} + +ALIAS (vnc_nve_group_export_routemap, + vnc_vrf_policy_export_routemap_cmd, + "export route-map NAME", + "Export to VRF\n" + "Route-map for filtering exported routes\n" "route map name\n") + +DEFUN (vnc_nve_export_no_prefixlist, + vnc_nve_export_no_prefixlist_cmd, + "no vnc export <bgp|zebra> <ipv4|ipv6> prefix-list [NAME]", + NO_STR + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 prefixes\n" + "IPv6 prefixes\n" + "Prefix-list for filtering exported routes\n" "Prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[4]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + if (argv[3]->arg[0] == 'b') { + if (((argc > 6) && hc->plist_export_bgp_name[afi] + && strmatch(argv[6]->text, hc->plist_export_bgp_name[afi])) + || (argc <= 6)) { + + free(hc->plist_export_bgp_name[afi]); + hc->plist_export_bgp_name[afi] = NULL; + hc->plist_export_bgp[afi] = NULL; + vnc_direct_bgp_reexport(bgp, afi); + } + } else { + if (((argc > 6) && hc->plist_export_zebra_name[afi] + && strmatch(argv[6]->text, + hc->plist_export_zebra_name[afi])) + || (argc <= 6)) { + + free(hc->plist_export_zebra_name[afi]); + hc->plist_export_zebra_name[afi] = NULL; + hc->plist_export_zebra[afi] = NULL; + /* TBD vnc_zebra_rh_reexport(bgp, afi); */ + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_prefixlist, + vnc_nve_export_prefixlist_cmd, + "vnc export <bgp|zebra> <ipv4|ipv6> prefix-list NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "IPv4 prefixes\n" + "IPv6 prefixes\n" + "Prefix-list for filtering exported routes\n" "Prefix list name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + afi_t afi; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (strmatch(argv[3]->text, "ipv4")) { + afi = AFI_IP; + } else { + afi = AFI_IP6; + } + + if (argv[2]->arg[0] == 'b') { + if (hc->plist_export_bgp_name[afi]) + free(hc->plist_export_bgp_name[afi]); + hc->plist_export_bgp_name[afi] = strdup(argv[5]->arg); + hc->plist_export_bgp[afi] = + prefix_list_lookup(afi, argv[5]->arg); + vnc_direct_bgp_reexport(bgp, afi); + } else { + if (hc->plist_export_zebra_name[afi]) + free(hc->plist_export_zebra_name[afi]); + hc->plist_export_zebra_name[afi] = strdup(argv[5]->arg); + hc->plist_export_zebra[afi] = + prefix_list_lookup(afi, argv[5]->arg); + /* TBD vnc_zebra_rh_reexport(bgp, afi); */ + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_no_routemap, + vnc_nve_export_no_routemap_cmd, + "no vnc export <bgp|zebra> route-map [NAME]", + NO_STR + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "Route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (argv[3]->arg[0] == 'b') { + if (((argc > 5) && hc->routemap_export_bgp_name + && strmatch(argv[5]->text, hc->routemap_export_bgp_name)) + || (argc <= 5)) { + + free(hc->routemap_export_bgp_name); + route_map_counter_decrement(hc->routemap_export_bgp); + hc->routemap_export_bgp_name = NULL; + hc->routemap_export_bgp = NULL; + vnc_direct_bgp_reexport(bgp, AFI_IP); + vnc_direct_bgp_reexport(bgp, AFI_IP6); + } + } else { + if (((argc > 5) && hc->routemap_export_zebra_name + && strmatch(argv[5]->text, hc->routemap_export_zebra_name)) + || (argc <= 5)) { + + free(hc->routemap_export_zebra_name); + route_map_counter_decrement(hc->routemap_export_zebra); + hc->routemap_export_zebra_name = NULL; + hc->routemap_export_zebra = NULL; + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + } + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_export_routemap, + vnc_nve_export_routemap_cmd, + "vnc export <bgp|zebra> route-map NAME", + VNC_CONFIG_STR + "Export to other protocols\n" + "Export to BGP\n" + "Export to Zebra (experimental)\n" + "Route-map for filtering exported routes\n" "Route map name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_cfg *hc; + + VNC_VTY_CONFIG_CHECK(bgp); + hc = bgp->rfapi_cfg; + + if (argv[2]->arg[0] == 'b') { + if (hc->routemap_export_bgp_name) + free(hc->routemap_export_bgp_name); + route_map_counter_decrement(hc->routemap_export_bgp); + hc->routemap_export_bgp_name = strdup(argv[4]->arg); + hc->routemap_export_bgp = + route_map_lookup_by_name(argv[4]->arg); + route_map_counter_increment(hc->routemap_export_bgp); + vnc_direct_bgp_reexport(bgp, AFI_IP); + vnc_direct_bgp_reexport(bgp, AFI_IP6); + } else { + if (hc->routemap_export_zebra_name) + free(hc->routemap_export_zebra_name); + route_map_counter_decrement(hc->routemap_export_zebra); + hc->routemap_export_zebra_name = strdup(argv[4]->arg); + hc->routemap_export_zebra = + route_map_lookup_by_name(argv[4]->arg); + route_map_counter_increment(hc->routemap_export_zebra); + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + } + return CMD_SUCCESS; +} + + +/* + * respond to changes in the global prefix list configuration + */ +void vnc_prefix_list_update(struct bgp *bgp) +{ + afi_t afi; + struct listnode *n; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc; + int i; + + if (!bgp) { + vnc_zlog_debug_verbose("%s: No BGP process is configured", + __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: rfapi not configured", __func__); + return; + } + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + /* + * Loop over nve groups + */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, + n, rfg)) { + + if (rfg->plist_export_bgp_name[afi]) { + rfg->plist_export_bgp[afi] = prefix_list_lookup( + afi, rfg->plist_export_bgp_name[afi]); + } + if (rfg->plist_export_zebra_name[afi]) { + rfg->plist_export_zebra + [afi] = prefix_list_lookup( + afi, rfg->plist_export_zebra_name[afi]); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) { + if (rfg->plist_redist_name[i][afi]) { + rfg->plist_redist + [i][afi] = prefix_list_lookup( + afi, + rfg->plist_redist_name[i][afi]); + } + } + + vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi); + /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */ + } + + /* + * RH config, too + */ + if (hc->plist_export_bgp_name[afi]) { + hc->plist_export_bgp[afi] = prefix_list_lookup( + afi, hc->plist_export_bgp_name[afi]); + } + if (hc->plist_export_zebra_name[afi]) { + hc->plist_export_zebra[afi] = prefix_list_lookup( + afi, hc->plist_export_zebra_name[afi]); + } + + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) { + if (hc->plist_redist_name[i][afi]) { + hc->plist_redist[i][afi] = prefix_list_lookup( + afi, hc->plist_redist_name[i][afi]); + } + } + } + + vnc_direct_bgp_reexport(bgp, AFI_IP); + vnc_direct_bgp_reexport(bgp, AFI_IP6); + + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + + vnc_redistribute_prechange(bgp); + vnc_redistribute_postchange(bgp); +} + +/* + * respond to changes in the global route map configuration + */ +void vnc_routemap_update(struct bgp *bgp, const char *unused) +{ + struct listnode *n; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc; + int i; + struct route_map *old = NULL; + + vnc_zlog_debug_verbose("%s(arg=%s)", __func__, unused); + + if (!bgp) { + vnc_zlog_debug_verbose("%s: No BGP process is configured", + __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: rfapi not configured", __func__); + return; + } + + /* + * Loop over nve groups + */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, n, + rfg)) { + + if (rfg->routemap_export_bgp_name) { + old = rfg->routemap_export_bgp; + rfg->routemap_export_bgp = route_map_lookup_by_name( + rfg->routemap_export_bgp_name); + /* old is NULL. i.e Route map creation event. + * So update applied_counter. + * If Old is not NULL, i.e It may be routemap + * updation or deletion. + * So no need to update the counter. + */ + if (!old) + route_map_counter_increment( + rfg->routemap_export_bgp); + } + if (rfg->routemap_export_zebra_name) { + old = rfg->routemap_export_bgp; + rfg->routemap_export_bgp = route_map_lookup_by_name( + rfg->routemap_export_zebra_name); + if (!old) + route_map_counter_increment( + rfg->routemap_export_bgp); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) { + if (rfg->routemap_redist_name[i]) { + old = rfg->routemap_redist[i]; + rfg->routemap_redist[i] = + route_map_lookup_by_name( + rfg->routemap_redist_name[i]); + if (!old) + route_map_counter_increment( + rfg->routemap_redist[i]); + } + } + + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP); + vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6); + /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */ + } + + /* + * RH config, too + */ + if (hc->routemap_export_bgp_name) { + old = hc->routemap_export_bgp; + hc->routemap_export_bgp = + route_map_lookup_by_name(hc->routemap_export_bgp_name); + if (!old) + route_map_counter_increment(hc->routemap_export_bgp); + } + if (hc->routemap_export_zebra_name) { + old = hc->routemap_export_bgp; + hc->routemap_export_bgp = route_map_lookup_by_name( + hc->routemap_export_zebra_name); + if (!old) + route_map_counter_increment(hc->routemap_export_bgp); + } + for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) { + if (hc->routemap_redist_name[i]) { + old = hc->routemap_redist[i]; + hc->routemap_redist[i] = route_map_lookup_by_name( + hc->routemap_redist_name[i]); + if (!old) + route_map_counter_increment( + hc->routemap_redist[i]); + } + } + + vnc_direct_bgp_reexport(bgp, AFI_IP); + vnc_direct_bgp_reexport(bgp, AFI_IP6); + + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */ + /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */ + + vnc_redistribute_prechange(bgp); + vnc_redistribute_postchange(bgp); + + vnc_zlog_debug_verbose("%s done", __func__); +} + +/*------------------------------------------------------------------------- + * nve-group + *-----------------------------------------------------------------------*/ + + +DEFUN_NOSH (vnc_nve_group, + vnc_nve_group_cmd, + "vnc nve-group NAME", + VNC_CONFIG_STR "Configure a NVE group\n" "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct rfapi_nve_group_cfg *rfg; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + VNC_VTY_CONFIG_CHECK(bgp); + + /* Search for name */ + rfg = bgp_rfapi_cfg_match_byname(bgp, argv[2]->arg, + RFAPI_GROUP_CFG_NVE); + + if (!rfg) { + rfg = rfapi_group_new(bgp, RFAPI_GROUP_CFG_NVE, argv[2]->arg); + if (!rfg) { + /* Error out of memory */ + vty_out(vty, "Can't allocate memory for NVE group\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Copy defaults from struct rfapi_cfg */ + rfg->rd = bgp->rfapi_cfg->default_rd; + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_L2RD) { + rfg->l2rd = bgp->rfapi_cfg->default_l2rd; + rfg->flags |= RFAPI_RFG_L2RD; + } + rfg->rd = bgp->rfapi_cfg->default_rd; + rfg->response_lifetime = + bgp->rfapi_cfg->default_response_lifetime; + + if (bgp->rfapi_cfg->default_rt_export_list) { + rfg->rt_export_list = ecommunity_dup( + bgp->rfapi_cfg->default_rt_export_list); + } + + if (bgp->rfapi_cfg->default_rt_import_list) { + rfg->rt_import_list = ecommunity_dup( + bgp->rfapi_cfg->default_rt_import_list); + rfg->rfapi_import_table = rfapiImportTableRefAdd( + bgp, rfg->rt_import_list, rfg); + } + + /* + * If a redist nve group was named but the group was not + * defined, + * make the linkage now + */ + if (!bgp->rfapi_cfg->rfg_redist) { + if (bgp->rfapi_cfg->rfg_redist_name + && !strcmp(bgp->rfapi_cfg->rfg_redist_name, + rfg->name)) { + + vnc_redistribute_prechange(bgp); + bgp->rfapi_cfg->rfg_redist = rfg; + vnc_redistribute_postchange(bgp); + } + } + + /* + * Same treatment for bgp-direct export group + */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, + node, nnode, rfgn)) { + + if (!strcmp(rfgn->name, rfg->name)) { + rfgn->rfg = rfg; + vnc_direct_bgp_add_group(bgp, rfg); + break; + } + } + + /* + * Same treatment for zebra export group + */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_zebra_l, node, + nnode, rfgn)) { + + vnc_zlog_debug_verbose( + "%s: ezport zebra: checking if \"%s\" == \"%s\"", + __func__, rfgn->name, rfg->name); + if (!strcmp(rfgn->name, rfg->name)) { + rfgn->rfg = rfg; + vnc_zebra_add_group(bgp, rfg); + break; + } + } + } + + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + VTY_PUSH_CONTEXT_SUB(BGP_VNC_NVE_GROUP_NODE, rfg); + + return CMD_SUCCESS; +} + +static void bgp_rfapi_delete_nve_group(struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg) +{ + struct list *orphaned_nves = NULL; + struct listnode *node, *nnode; + + /* + * If there are currently-open NVEs that belong to this group, + * zero out their references to this group structure. + */ + if (rfg->nves) { + struct rfapi_descriptor *rfd; + orphaned_nves = list_new(); + while ((rfd = listnode_head(rfg->nves))) { + rfd->rfg = NULL; + listnode_delete(rfg->nves, rfd); + listnode_add(orphaned_nves, rfd); + } + list_delete(&rfg->nves); + } + + /* delete it */ + free(rfg->name); + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); + if (rfg->rt_import_list) + ecommunity_free(&rfg->rt_import_list); + if (rfg->rt_export_list) + ecommunity_free(&rfg->rt_export_list); + + if (rfg->vn_node) { + rfg->vn_node->info = NULL; + agg_unlock_node(rfg->vn_node); /* frees */ + } + if (rfg->un_node) { + rfg->un_node->info = NULL; + agg_unlock_node(rfg->un_node); /* frees */ + } + if (rfg->rfp_cfg) + XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg); + listnode_delete(bgp->rfapi_cfg->nve_groups_sequential, rfg); + + QOBJ_UNREG(rfg); + XFREE(MTYPE_RFAPI_GROUP_CFG, rfg); + + /* + * Attempt to reassign the orphaned nves to a new group. If + * a NVE can not be reassigned, its rfd->rfg will remain NULL + * and it will become a zombie until released by rfapi_close(). + */ + if (orphaned_nves) { + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS(orphaned_nves, node, nnode, rfd)) { + /* + * 1. rfapi_close() equivalent except: + * a. don't free original descriptor + * b. remember query list + * c. remember advertised route list + * 2. rfapi_open() equivalent except: + * a. reuse original descriptor + * 3. rfapi_register() on remembered advertised route + * list + * 4. rfapi_query on rememebred query list + */ + + int rc; + + rc = rfapi_reopen(rfd, bgp); + + if (!rc) { + list_delete_node(orphaned_nves, node); + if (vty) + vty_out(vty, + "WARNING: reassigned NVE vn="); + rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr); + if (vty) + vty_out(vty, " un="); + rfapiPrintRfapiIpAddr(vty, &rfd->un_addr); + if (vty) + vty_out(vty, " to new group \"%s\"\n", + rfd->rfg->name); + } + } + + for (ALL_LIST_ELEMENTS_RO(orphaned_nves, node, rfd)) { + if (vty) + vty_out(vty, "WARNING: orphaned NVE vn="); + rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr); + if (vty) + vty_out(vty, " un="); + rfapiPrintRfapiIpAddr(vty, &rfd->un_addr); + if (vty) + vty_out(vty, "\n"); + } + list_delete(&orphaned_nves); + } +} + +static int +bgp_rfapi_delete_named_nve_group(struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + const char *rfg_name, /* NULL = any */ + rfapi_group_cfg_type_t type) /* _MAX = any */ +{ + struct rfapi_nve_group_cfg *rfg = NULL; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + + /* Search for name */ + if (rfg_name) { + rfg = bgp_rfapi_cfg_match_byname(bgp, rfg_name, type); + if (!rfg) { + if (vty) + vty_out(vty, "No NVE group named \"%s\"\n", + rfg_name); + return CMD_WARNING_CONFIG_FAILED; + } + } + + /* + * If this group is the redist nve group, unlink it + */ + if (rfg_name == NULL || bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + bgp->rfapi_cfg->rfg_redist = NULL; + vnc_redistribute_postchange(bgp); + } + + + /* + * remove reference from bgp direct export list + */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + if (rfgn->rfg == rfg) { + rfgn->rfg = NULL; + /* remove exported routes from this group */ + vnc_direct_bgp_del_group(bgp, rfg); + break; + } + } + + /* + * remove reference from zebra export list + */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, + rfgn)) { + if (rfgn->rfg == rfg) { + rfgn->rfg = NULL; + /* remove exported routes from this group */ + vnc_zebra_del_group(bgp, rfg); + break; + } + } + if (rfg) { + if (rfg->rfd) + clear_vnc_vrf_closer(rfg); + bgp_rfapi_delete_nve_group(vty, bgp, rfg); + } else /* must be delete all */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->nve_groups_sequential, + node, nnode, rfg)) { + if (rfg->rfd) + clear_vnc_vrf_closer(rfg); + bgp_rfapi_delete_nve_group(vty, bgp, rfg); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_no_nve_group, + vnc_no_nve_group_cmd, + "no vnc nve-group NAME", + NO_STR + VNC_CONFIG_STR + "Configure a NVE group\n" + "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[3]->arg, + RFAPI_GROUP_CFG_NVE); +} + +DEFUN (vnc_nve_group_prefix, + vnc_nve_group_prefix_cmd, + "prefix <vn|un> <A.B.C.D/M|X:X::X:X/M>", + "Specify prefixes matching NVE VN or UN interfaces\n" + "VN prefix\n" + "UN prefix\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + struct prefix p; + afi_t afi; + struct agg_table *rt; + struct agg_node *rn; + int is_un_prefix = 0; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!str2prefix(argv[2]->arg, &p)) { + vty_out(vty, "Malformed prefix \"%s\"\n", argv[2]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + afi = family2afi(p.family); + if (!afi) { + vty_out(vty, "Unsupported address family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[1]->arg[0] == 'u') { + rt = bgp->rfapi_cfg->nve_groups_un[afi]; + is_un_prefix = 1; + } else { + rt = bgp->rfapi_cfg->nve_groups_vn[afi]; + } + + rn = agg_node_get(rt, &p); /* NB locks node */ + if (rn->info) { + /* + * There is already a group with this prefix + */ + agg_unlock_node(rn); + if (rn->info != rfg) { + /* + * different group name: fail + */ + vty_out(vty, + "nve group \"%s\" already has \"%s\" prefix %s\n", + ((struct rfapi_nve_group_cfg *)(rn->info)) + ->name, + argv[1]->arg, argv[2]->arg); + return CMD_WARNING_CONFIG_FAILED; + } else { + /* + * same group name: it's already in the correct place + * in the table, so we're done. + * + * Implies rfg->(vn|un)_prefix is already correct. + */ + return CMD_SUCCESS; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + /* New prefix, new node */ + + if (is_un_prefix) { + + /* detach rfg from previous route table location */ + if (rfg->un_node) { + rfg->un_node->info = NULL; + agg_unlock_node(rfg->un_node); /* frees */ + } + rfg->un_node = rn; /* back ref */ + rfg->un_prefix = p; + + } else { + + /* detach rfg from previous route table location */ + if (rfg->vn_node) { + rfg->vn_node->info = NULL; + agg_unlock_node(rfg->vn_node); /* frees */ + } + rfg->vn_node = rn; /* back ref */ + rfg->vn_prefix = p; + } + + /* attach */ + rn->info = rfg; + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rt_import, + vnc_nve_group_rt_import_cmd, + "rt import RTLIST...", + "Specify route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int rc; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + int is_export_bgp = 0; + int is_export_zebra = 0; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group(bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + vnc_zebra_del_group(bgp, rfg); + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = + rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group(bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group(bgp, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rt_export, + vnc_nve_group_rt_export_cmd, + "rt export RTLIST...", + "Specify route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int rc; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return rc; +} + +DEFUN (vnc_nve_group_rt_both, + vnc_nve_group_rt_both_cmd, + "rt both RTLIST...", + "Specify route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + int rc; + int is_export_bgp = 0; + int is_export_zebra = 0; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group(bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) { + vnc_zlog_debug_verbose("%s: is_export_zebra", __func__); + vnc_zebra_del_group(bgp, rfg); + } + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = + rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group(bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group(bgp, rfg); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return rc; +} + +DEFUN (vnc_nve_group_l2rd, + vnc_nve_group_l2rd_cmd, + "l2rd <(1-255)|auto-vn>", + "Specify default Local Nve ID value to use in RD for L2 routes\n" + "Fixed value 1-255\n" + "use the low-order octet of the NVE's VN address\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strmatch(argv[1]->text, "auto:vn")) { + rfg->l2rd = 0; + } else { + char *end = NULL; + unsigned long value_l = strtoul(argv[1]->arg, &end, 10); + uint8_t value = value_l & 0xff; + + if (!argv[1]->arg[0] || *end) { + vty_out(vty, "%% Malformed l2 nve ID \"%s\"\n", + argv[1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if ((value_l < 1) || (value_l > 0xff)) { + vty_out(vty, + "%% Malformed l2 nve id (must be greater than 0 and less than %u\n", + 0x100); + return CMD_WARNING_CONFIG_FAILED; + } + + rfg->l2rd = value; + } + rfg->flags |= RFAPI_RFG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_no_l2rd, + vnc_nve_group_no_l2rd_cmd, + "no l2rd", + NO_STR + "Specify default Local Nve ID value to use in RD for L2 routes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rfg->l2rd = 0; + rfg->flags &= ~RFAPI_RFG_L2RD; + + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_rd, + vnc_nve_group_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Specify route distinguisher\n" + "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + struct prefix_rd prd; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!strncmp(argv[1]->arg, "auto:vn:", 8)) { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!argv[1]->arg[8] || *end) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (value32 > 0xffff) { + vty_out(vty, "%% Malformed rd (must be less than %u\n", + 0x0ffff); + return CMD_WARNING_CONFIG_FAILED; + } + + memset(&prd, 0, sizeof(prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } else { + + /* TODO: save RD format */ + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rfg->rd = prd; + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_nve_group_responselifetime, + vnc_nve_group_responselifetime_cmd, + "response-lifetime <LIFETIME|infinite>", + "Specify response lifetime\n" + "Response lifetime in seconds\n" "Infinite response lifetime\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + unsigned int rspint; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + struct rfapi_descriptor *rfd; + struct listnode *hdnode; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strmatch(argv[1]->text, "infinite")) { + rspint = RFAPI_INFINITE_LIFETIME; + } else { + rspint = strtoul(argv[1]->arg, NULL, 10); + } + + rfg->response_lifetime = rspint; + rfg->flags |= RFAPI_RFG_RESPONSE_LIFETIME; + if (rfg->nves) + for (ALL_LIST_ELEMENTS_RO(rfg->nves, hdnode, rfd)) + rfd->response_lifetime = rspint; + return CMD_SUCCESS; +} + +/* + * Sigh. This command, like exit-address-family, is a hack to deal + * with the lack of rigorous level control in the command handler. + * TBD fix command handler. + */ +DEFUN_NOSH (exit_vnc, + exit_vnc_cmd, + "exit-vnc", + "Exit VNC configuration mode\n") +{ + if (vty->node == BGP_VNC_DEFAULTS_NODE + || vty->node == BGP_VNC_NVE_GROUP_NODE + || vty->node == BGP_VNC_L2_GROUP_NODE) { + + vty->node = BGP_NODE; + } + return CMD_SUCCESS; +} + +static struct cmd_node bgp_vnc_defaults_node = { + .name = "bgp vnc defaults", + .node = BGP_VNC_DEFAULTS_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-defaults)# ", +}; + +static struct cmd_node bgp_vnc_nve_group_node = { + .name = "bgp vnc nve", + .node = BGP_VNC_NVE_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-nve-group)# ", +}; + +/*------------------------------------------------------------------------- + * VNC nve-group + * Note there are two types of NVEs, one for VPNs one for RFP NVEs + *-----------------------------------------------------------------------*/ + +DEFUN_NOSH (vnc_vrf_policy, + vnc_vrf_policy_cmd, + "vrf-policy NAME", + "Configure a VRF policy group\n" + "VRF name\n") +{ + struct rfapi_nve_group_cfg *rfg; + VTY_DECLVAR_CONTEXT(bgp, bgp); + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { + vty_out(vty, + "Can't configure vrf-policy within a BGP VRF instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* Search for name */ + rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg, + RFAPI_GROUP_CFG_VRF); + + if (!rfg) { + rfg = rfapi_group_new(bgp, RFAPI_GROUP_CFG_VRF, argv[1]->arg); + if (!rfg) { + /* Error out of memory */ + vty_out(vty, "Can't allocate memory for NVE group\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + VTY_PUSH_CONTEXT_SUB(BGP_VRF_POLICY_NODE, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_no_vrf_policy, + vnc_no_vrf_policy_cmd, + "no vrf-policy NAME", + NO_STR + "Remove a VRF policy group\n" + "VRF name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* silently return */ + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + return CMD_SUCCESS; + + return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg, + RFAPI_GROUP_CFG_VRF); +} + +DEFUN (vnc_vrf_policy_label, + vnc_vrf_policy_label_cmd, + "label (0-1048575)", + "Default label value for VRF\n" + "Label Value <0-1048575>\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + + uint32_t label; + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + label = strtoul(argv[1]->arg, NULL, 10); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rfg->label = label; + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_no_label, + vnc_vrf_policy_no_label_cmd, + "no label", + NO_STR + "Remove VRF default label\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current VRF group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rfg->label = MPLS_LABEL_NONE; + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_nexthop, + vnc_vrf_policy_nexthop_cmd, + "nexthop <A.B.C.D|X:X::X:X|self>", + "Specify next hop to use for VRF advertised prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Use configured router-id (default)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + struct prefix p; + + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current VRF no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + if (!str2prefix(argv[1]->arg, &p) && p.family) { + // vty_out (vty, "Nexthop set to self\n"); + SET_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF); + memset(&rfg->vn_prefix, 0, sizeof(struct prefix)); + } else { + UNSET_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF); + rfg->vn_prefix = p; + rfg->un_prefix = p; + } + + /* TBD handle router-id/ nexthop changes when have advertised prefixes + */ + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return CMD_SUCCESS; +} + +/* The RT code should be refactored/simplified with above... */ +DEFUN (vnc_vrf_policy_rt_import, + vnc_vrf_policy_rt_import_cmd, + "rt import RTLIST...", + "Specify route targets\n" + "Import filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + int is_export_bgp = 0; + int is_export_zebra = 0; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group(bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) + vnc_zebra_del_group(bgp, rfg); + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = + rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group(bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group(bgp, rfg); + + return CMD_SUCCESS; +} + +DEFUN (vnc_vrf_policy_rt_export, + vnc_vrf_policy_rt_export_cmd, + "rt export RTLIST...", + "Specify route targets\n" + "Export filter\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return rc; +} + +DEFUN (vnc_vrf_policy_rt_both, + vnc_vrf_policy_rt_both_cmd, + "rt both RTLIST...", + "Specify route targets\n" + "Export+import filters\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc; + int is_export_bgp = 0; + int is_export_zebra = 0; + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list); + if (rc != CMD_SUCCESS) + return rc; + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_bgp = 1; + break; + } + } + + if (is_export_bgp) + vnc_direct_bgp_del_group(bgp, rfg); + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + is_export_zebra = 1; + break; + } + } + + if (is_export_zebra) { + vnc_zlog_debug_verbose("%s: is_export_zebra", __func__); + vnc_zebra_del_group(bgp, rfg); + } + + /* + * stop referencing old import table, now reference new one + */ + if (rfg->rfapi_import_table) + rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table); + rfg->rfapi_import_table = + rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg); + + if (is_export_bgp) + vnc_direct_bgp_add_group(bgp, rfg); + + if (is_export_zebra) + vnc_zebra_add_group(bgp, rfg); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list); + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + + return rc; +} + +DEFUN (vnc_vrf_policy_rd, + vnc_vrf_policy_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Specify default VRF route distinguisher\n" + "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:nh:<number> )\n") +{ + int ret; + struct prefix_rd prd; + VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!strncmp(argv[1]->arg, "auto:nh:", 8)) { + /* + * use AF_UNIX to designate automatically-assigned RD + * auto:vn:nn where nn is a 2-octet quantity + */ + char *end = NULL; + uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10); + uint16_t value = value32 & 0xffff; + + if (!*(argv[1]->arg + 5) || *end) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (value32 > 0xffff) { + vty_out(vty, "%% Malformed rd (must be less than %u\n", + 0x0ffff); + return CMD_WARNING_CONFIG_FAILED; + } + + memset(&prd, 0, sizeof(prd)); + prd.family = AF_UNIX; + prd.prefixlen = 64; + prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff; + prd.val[1] = RD_TYPE_IP & 0x0ff; + prd.val[6] = (value >> 8) & 0x0ff; + prd.val[7] = value & 0x0ff; + + } else { + + /* TODO: save RD format */ + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_prechange(bgp); + } + + rfg->rd = prd; + + if (bgp->rfapi_cfg->rfg_redist == rfg) { + vnc_redistribute_postchange(bgp); + } + return CMD_SUCCESS; +} + +DEFUN_NOSH (exit_vrf_policy, + exit_vrf_policy_cmd, + "exit-vrf-policy", + "Exit VRF policy configuration mode\n") +{ + if (vty->node == BGP_VRF_POLICY_NODE) { + vty->node = BGP_NODE; + } + return CMD_SUCCESS; +} + +static struct cmd_node bgp_vrf_policy_node = { + .name = "bgp vrf policy", + .node = BGP_VRF_POLICY_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vrf-policy)# ", +}; + +/*------------------------------------------------------------------------- + * vnc-l2-group + *-----------------------------------------------------------------------*/ + + +DEFUN_NOSH (vnc_l2_group, + vnc_l2_group_cmd, + "vnc l2-group NAME", + VNC_CONFIG_STR "Configure a L2 group\n" "Group name\n") +{ + struct rfapi_l2_group_cfg *rfg; + VTY_DECLVAR_CONTEXT(bgp, bgp); + VNC_VTY_CONFIG_CHECK(bgp); + + /* Search for name */ + rfg = rfapi_l2_group_lookup_byname(bgp, argv[2]->arg); + + if (!rfg) { + rfg = rfapi_l2_group_new(); + if (!rfg) { + /* Error out of memory */ + vty_out(vty, "Can't allocate memory for L2 group\n"); + return CMD_WARNING_CONFIG_FAILED; + } + rfg->name = strdup(argv[2]->arg); + /* add to tail of list */ + listnode_add(bgp->rfapi_cfg->l2_groups, rfg); + } + + /* + * XXX subsequent calls will need to make sure this item is still + * in the linked list and has the same name + */ + VTY_PUSH_CONTEXT_SUB(BGP_VNC_L2_GROUP_NODE, rfg); + return CMD_SUCCESS; +} + +static void bgp_rfapi_delete_l2_group(struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + struct rfapi_l2_group_cfg *rfg) +{ + /* delete it */ + free(rfg->name); + if (rfg->rt_import_list) + ecommunity_free(&rfg->rt_import_list); + if (rfg->rt_export_list) + ecommunity_free(&rfg->rt_export_list); + if (rfg->labels) + list_delete(&rfg->labels); + XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg); + listnode_delete(bgp->rfapi_cfg->l2_groups, rfg); + + rfapi_l2_group_del(rfg); +} + +static int +bgp_rfapi_delete_named_l2_group(struct vty *vty, /* NULL = no output */ + struct bgp *bgp, + const char *rfg_name) /* NULL = any */ +{ + struct rfapi_l2_group_cfg *rfg = NULL; + struct listnode *node, *nnode; + + /* Search for name */ + if (rfg_name) { + rfg = rfapi_l2_group_lookup_byname(bgp, rfg_name); + if (!rfg) { + if (vty) + vty_out(vty, "No L2 group named \"%s\"\n", + rfg_name); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (rfg) + bgp_rfapi_delete_l2_group(vty, bgp, rfg); + else /* must be delete all */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->l2_groups, node, nnode, + rfg)) + bgp_rfapi_delete_l2_group(vty, bgp, rfg); + return CMD_SUCCESS; +} + +DEFUN (vnc_no_l2_group, + vnc_no_l2_group_cmd, + "no vnc l2-group NAME", + NO_STR + VNC_CONFIG_STR + "Configure a L2 group\n" + "Group name\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + + return bgp_rfapi_delete_named_l2_group(vty, bgp, argv[3]->arg); +} + + +DEFUN (vnc_l2_group_lni, + vnc_l2_group_lni_cmd, + "logical-network-id (0-4294967295)", + "Specify Logical Network ID associated with group\n" + "value\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current L2 group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rfg->logical_net_id = strtoul(argv[1]->arg, NULL, 10); + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_labels, + vnc_l2_group_labels_cmd, + "labels (0-1048575)...", + "Specify label values associated with group\n" + "Space separated list of label values <0-1048575>\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct list *ll; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current L2 group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ll = rfg->labels; + if (ll == NULL) { + ll = list_new(); + rfg->labels = ll; + } + argc--; + argv++; + for (; argc; --argc, ++argv) { + uint32_t label; + label = strtoul(argv[0]->arg, NULL, 10); + if (!listnode_lookup(ll, (void *)(uintptr_t)label)) + listnode_add(ll, (void *)(uintptr_t)label); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_no_labels, + vnc_l2_group_no_labels_cmd, + "no labels (0-1048575)...", + NO_STR + "Specify label values associated with L2 group\n" + "Space separated list of label values <0-1048575>\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct list *ll; + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current L2 group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ll = rfg->labels; + if (ll == NULL) { + vty_out(vty, "Label no longer associated with group\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + argc -= 2; + argv += 2; + for (; argc; --argc, ++argv) { + uint32_t label; + label = strtoul(argv[0]->arg, NULL, 10); + listnode_delete(ll, (void *)(uintptr_t)label); + } + + return CMD_SUCCESS; +} + +DEFUN (vnc_l2_group_rt, + vnc_l2_group_rt_cmd, + "rt <both|export|import> ASN:NN_OR_IP-ADDRESS:NN", + "Specify route targets\n" + "Export+import filters\n" + "Export filters\n" + "Import filters\n" + "A route target\n") +{ + VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg); + VTY_DECLVAR_CONTEXT(bgp, bgp); + int rc = CMD_SUCCESS; + int do_import = 0; + int do_export = 0; + + switch (argv[1]->arg[0]) { + case 'b': + do_export = 1; /* fall through */ + case 'i': + do_import = 1; + break; + case 'e': + do_export = 1; + break; + default: + vty_out(vty, "Unknown option, %s\n", argv[1]->arg); + return CMD_ERR_NO_MATCH; + } + + /* make sure it's still in list */ + if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current L2 group no longer exists\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (do_import) + rc = set_ecom_list(vty, argc - 2, argv + 2, + &rfg->rt_import_list); + if (rc == CMD_SUCCESS && do_export) + rc = set_ecom_list(vty, argc - 2, argv + 2, + &rfg->rt_export_list); + return rc; +} + + +static struct cmd_node bgp_vnc_l2_group_node = { + .name = "bgp vnc l2", + .node = BGP_VNC_L2_GROUP_NODE, + .parent_node = BGP_NODE, + .prompt = "%s(config-router-vnc-l2-group)# ", +}; + +struct rfapi_l2_group_cfg * +bgp_rfapi_get_group_by_lni_label(struct bgp *bgp, uint32_t logical_net_id, + uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */ + return NULL; + + label = label & 0xfffff; /* label is 20 bits! */ + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->l2_groups, node, rfg)) { + if (rfg->logical_net_id == logical_net_id) { + struct listnode *lnode; + void *data; + for (ALL_LIST_ELEMENTS_RO(rfg->labels, lnode, data)) + if (((uint32_t)((uintptr_t)data)) + == label) { /* match! */ + return rfg; + } + } + } + return NULL; +} + +struct list *bgp_rfapi_get_labellist_by_lni_label(struct bgp *bgp, + uint32_t logical_net_id, + uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + rfg = bgp_rfapi_get_group_by_lni_label(bgp, logical_net_id, label); + if (rfg) { + return rfg->labels; + } + return NULL; +} + +struct ecommunity * +bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import, + uint32_t logical_net_id, uint32_t label) +{ + struct rfapi_l2_group_cfg *rfg; + rfg = bgp_rfapi_get_group_by_lni_label(bgp, logical_net_id, label); + if (rfg) { + if (is_import) + return rfg->rt_import_list; + else + return rfg->rt_export_list; + } + return NULL; +} + +void bgp_rfapi_cfg_init(void) +{ + install_node(&bgp_vnc_defaults_node); + install_node(&bgp_vnc_nve_group_node); + install_node(&bgp_vrf_policy_node); + install_node(&bgp_vnc_l2_group_node); + install_default(BGP_VRF_POLICY_NODE); + install_default(BGP_VNC_DEFAULTS_NODE); + install_default(BGP_VNC_NVE_GROUP_NODE); + install_default(BGP_VNC_L2_GROUP_NODE); + + /* + * Add commands + */ + install_element(BGP_NODE, &vnc_defaults_cmd); + install_element(BGP_NODE, &vnc_nve_group_cmd); + install_element(BGP_NODE, &vnc_no_nve_group_cmd); + install_element(BGP_NODE, &vnc_vrf_policy_cmd); + install_element(BGP_NODE, &vnc_no_vrf_policy_cmd); + install_element(BGP_NODE, &vnc_l2_group_cmd); + install_element(BGP_NODE, &vnc_no_l2_group_cmd); + install_element(BGP_NODE, &vnc_advertise_un_method_cmd); + install_element(BGP_NODE, &vnc_export_mode_cmd); + + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_import_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_export_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_both_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rd_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_l2rd_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_no_l2rd_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, + &vnc_defaults_responselifetime_cmd); + install_element(BGP_VNC_DEFAULTS_NODE, &exit_vnc_cmd); + + install_element(BGP_NODE, &vnc_redistribute_protocol_cmd); + install_element(BGP_NODE, &vnc_no_redistribute_protocol_cmd); + install_element(BGP_NODE, &vnc_redistribute_nvegroup_cmd); + install_element(BGP_NODE, &vnc_redistribute_no_nvegroup_cmd); + install_element(BGP_NODE, &vnc_redistribute_lifetime_cmd); + install_element(BGP_NODE, &vnc_redistribute_rh_roo_localadmin_cmd); + install_element(BGP_NODE, &vnc_redistribute_mode_cmd); + install_element(BGP_NODE, &vnc_redistribute_bgp_exterior_cmd); + + install_element(BGP_NODE, &vnc_redist_bgpdirect_no_prefixlist_cmd); + install_element(BGP_NODE, &vnc_redist_bgpdirect_prefixlist_cmd); + install_element(BGP_NODE, &vnc_redist_bgpdirect_no_routemap_cmd); + install_element(BGP_NODE, &vnc_redist_bgpdirect_routemap_cmd); + + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_prefixlist_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_no_routemap_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_redist_bgpdirect_routemap_cmd); + + install_element(BGP_NODE, &vnc_export_nvegroup_cmd); + install_element(BGP_NODE, &vnc_no_export_nvegroup_cmd); + install_element(BGP_NODE, &vnc_nve_export_prefixlist_cmd); + install_element(BGP_NODE, &vnc_nve_export_routemap_cmd); + install_element(BGP_NODE, &vnc_nve_export_no_prefixlist_cmd); + install_element(BGP_NODE, &vnc_nve_export_no_routemap_cmd); + + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_l2rd_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_no_l2rd_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_prefix_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_import_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_export_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_both_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rd_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_responselifetime_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_prefixlist_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_routemap_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_no_prefixlist_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, + &vnc_nve_group_export_no_routemap_cmd); + install_element(BGP_VNC_NVE_GROUP_NODE, &exit_vnc_cmd); + + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_label_cmd); + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_no_label_cmd); + // Reenable to support VRF controller use case and testing + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_nexthop_cmd); + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_import_cmd); + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_export_cmd); + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_both_cmd); + install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rd_cmd); + install_element(BGP_VRF_POLICY_NODE, + &vnc_vrf_policy_export_prefixlist_cmd); + install_element(BGP_VRF_POLICY_NODE, + &vnc_vrf_policy_export_routemap_cmd); + install_element(BGP_VRF_POLICY_NODE, + &vnc_vrf_policy_export_no_prefixlist_cmd); + install_element(BGP_VRF_POLICY_NODE, + &vnc_vrf_policy_export_no_routemap_cmd); + install_element(BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd); + + install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_lni_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_labels_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_no_labels_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_rt_cmd); + install_element(BGP_VNC_L2_GROUP_NODE, &exit_vnc_cmd); +} + +struct rfapi_cfg *bgp_rfapi_cfg_new(struct rfapi_rfp_cfg *cfg) +{ + struct rfapi_cfg *h; + afi_t afi; + + h = XCALLOC(MTYPE_RFAPI_CFG, sizeof(struct rfapi_cfg)); + assert(h); + + h->nve_groups_sequential = list_new(); + assert(h->nve_groups_sequential); + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + h->nve_groups_vn[afi] = agg_table_init(); + h->nve_groups_un[afi] = agg_table_init(); + } + h->default_response_lifetime = + BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT; + h->rfg_export_direct_bgp_l = list_new(); + h->rfg_export_zebra_l = list_new(); + h->resolve_nve_roo_local_admin = + BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT; + + SET_FLAG(h->flags, BGP_VNC_CONFIG_FLAGS_DEFAULT); + + if (cfg == NULL) { + h->rfp_cfg.download_type = RFAPI_RFP_DOWNLOAD_PARTIAL; + h->rfp_cfg.ftd_advertisement_interval = + RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL; + h->rfp_cfg.holddown_factor = + RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; + h->rfp_cfg.use_updated_response = 0; + h->rfp_cfg.use_removes = 0; + } else { + h->rfp_cfg.download_type = cfg->download_type; + h->rfp_cfg.ftd_advertisement_interval = + cfg->ftd_advertisement_interval; + h->rfp_cfg.holddown_factor = cfg->holddown_factor; + h->rfp_cfg.use_updated_response = cfg->use_updated_response; + h->rfp_cfg.use_removes = cfg->use_removes; + if (cfg->use_updated_response) + h->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE; + else + h->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE; + if (cfg->use_removes) + h->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; + else + h->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; + } + return h; +} + +static void bgp_rfapi_rfgn_list_delete(void *data) +{ + struct rfapi_rfg_name *rfgn = data; + free(rfgn->name); + rfgn_free(rfgn); +} + +void bgp_rfapi_cfg_destroy(struct bgp *bgp, struct rfapi_cfg *h) +{ + afi_t afi; + if (h == NULL) + return; + + bgp_rfapi_delete_named_nve_group(NULL, bgp, NULL, RFAPI_GROUP_CFG_MAX); + bgp_rfapi_delete_named_l2_group(NULL, bgp, NULL); + if (h->l2_groups != NULL) + list_delete(&h->l2_groups); + list_delete(&h->nve_groups_sequential); + + h->rfg_export_direct_bgp_l->del = bgp_rfapi_rfgn_list_delete; + list_delete(&h->rfg_export_direct_bgp_l); + + h->rfg_export_zebra_l->del = bgp_rfapi_rfgn_list_delete; + list_delete(&h->rfg_export_zebra_l); + + if (h->default_rt_export_list) + ecommunity_free(&h->default_rt_export_list); + if (h->default_rt_import_list) + ecommunity_free(&h->default_rt_import_list); + XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, h->default_rfp_cfg); + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + agg_table_finish(h->nve_groups_vn[afi]); + agg_table_finish(h->nve_groups_un[afi]); + } + XFREE(MTYPE_RFAPI_CFG, h); +} + +int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp) +{ + struct listnode *node, *nnode; + struct rfapi_nve_group_cfg *rfg; + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct rfapi_rfg_name *rfgn; + int write = 0; + afi_t afi; + int type; + if (bgp->rfapi == NULL || hc == NULL) + return write; + + vty_out(vty, "!\n"); + for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode, rfg)) + if (rfg->type == RFAPI_GROUP_CFG_VRF) { + ++write; + vty_out(vty, " vrf-policy %s\n", rfg->name); + if (rfg->label <= MPLS_LABEL_MAX) { + vty_out(vty, " label %u\n", rfg->label); + } + if (CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) { + vty_out(vty, " nexthop self\n"); + + } else { + if (rfg->vn_prefix.family) { + char buf[BUFSIZ]; + buf[0] = buf[BUFSIZ - 1] = 0; + inet_ntop(rfg->vn_prefix.family, + &rfg->vn_prefix.u.prefix, buf, + sizeof(buf)); + if (!buf[0] || buf[BUFSIZ - 1]) { + // vty_out (vty, "nexthop + // self\n"); + } else { + vty_out(vty, " nexthop %s\n", + buf); + } + } + } + + if (rfg->rd.prefixlen) { + if (AF_UNIX == rfg->rd.family) { + + uint16_t value = 0; + + value = ((rfg->rd.val[6] << 8) + & 0x0ff00) + | (rfg->rd.val[7] & 0x0ff); + + vty_out(vty, " rd auto:nh:%d\n", + value); + + } else + vty_out(vty, " rd %pRDP\n", &rfg->rd); + } + + if (rfg->rt_import_list && rfg->rt_export_list + && ecommunity_cmp(rfg->rt_import_list, + rfg->rt_export_list)) { + char *b = ecommunity_ecom2str( + rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt both %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (rfg->rt_import_list) { + char *b = ecommunity_ecom2str( + rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt import %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (rfg->rt_export_list) { + char *b = ecommunity_ecom2str( + rfg->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt export %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + const char *afistr = + (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (rfg->plist_export_bgp_name[afi]) { + vty_out(vty, + " export %s%s prefix-list %s\n", + (rfg->type == RFAPI_GROUP_CFG_VRF + ? "" + : "bgp "), + afistr, + rfg->plist_export_bgp_name + [afi]); + } + if (rfg->plist_export_zebra_name[afi]) { + vty_out(vty, + " export %s%s prefix-list %s\n", + (rfg->type == RFAPI_GROUP_CFG_VRF + ? "" + : "zebra "), + afistr, + rfg->plist_export_zebra_name + [afi]); + } + /* + * currently we only support redist plists for + * bgp-direct. + * If we later add plist support for + * redistributing other + * protocols, we'll need to loop over protocols + * here + */ + if (rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vty_out(vty, + " redistribute bgp-direct %s prefix-list %s\n", + afistr, + rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT] + [afi]); + } + if (rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT][afi]) { + vty_out(vty, + " redistribute bgp-direct-to-nve-groups %s prefix-list %s\n", + afistr, + rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT] + [afi]); + } + } + + if (rfg->routemap_export_bgp_name) { + vty_out(vty, " export %sroute-map %s\n", + (rfg->type == RFAPI_GROUP_CFG_VRF + ? "" + : "bgp "), + rfg->routemap_export_bgp_name); + } + if (rfg->routemap_export_zebra_name) { + vty_out(vty, " export %sroute-map %s\n", + (rfg->type == RFAPI_GROUP_CFG_VRF + ? "" + : "zebra "), + rfg->routemap_export_zebra_name); + } + if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) { + vty_out(vty, + " redistribute bgp-direct route-map %s\n", + rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT]); + } + if (rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vty_out(vty, + " redistribute bgp-direct-to-nve-groups route-map %s\n", + rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT]); + } + vty_out(vty, " exit-vrf-policy\n"); + vty_out(vty, "!\n"); + } + if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) { + vty_out(vty, " vnc advertise-un-method encap-safi\n"); + write++; + } + + { /* was based on listen ports */ + /* for now allow both old and new */ + if (bgp->rfapi->rfp_methods.cfg_cb) + write += (bgp->rfapi->rfp_methods.cfg_cb)( + vty, bgp->rfapi->rfp); + + if (write) + vty_out(vty, "!\n"); + + if (hc->l2_groups) { + struct rfapi_l2_group_cfg *rfgc = NULL; + struct listnode *gnode; + for (ALL_LIST_ELEMENTS_RO(hc->l2_groups, gnode, rfgc)) { + struct listnode *lnode; + void *data; + ++write; + vty_out(vty, " vnc l2-group %s\n", rfgc->name); + if (rfgc->logical_net_id != 0) + vty_out(vty, + " logical-network-id %u\n", + rfgc->logical_net_id); + if (rfgc->labels != NULL + && listhead(rfgc->labels) != NULL) { + vty_out(vty, " labels "); + for (ALL_LIST_ELEMENTS_RO(rfgc->labels, + lnode, + data)) { + vty_out(vty, "%hu ", + (uint16_t)( + (uintptr_t) + data)); + } + vty_out(vty, "\n"); + } + + if (rfgc->rt_import_list && rfgc->rt_export_list + && ecommunity_cmp(rfgc->rt_import_list, + rfgc->rt_export_list)) { + char *b = ecommunity_ecom2str( + rfgc->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt both %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (rfgc->rt_import_list) { + char *b = ecommunity_ecom2str( + rfgc->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt import %s\n", + b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (rfgc->rt_export_list) { + char *b = ecommunity_ecom2str( + rfgc->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt export %s\n", + b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += (bgp->rfapi->rfp_methods + .cfg_group_cb)( + vty, bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_L2, + rfgc->name, rfgc->rfp_cfg); + vty_out(vty, " exit-vnc\n"); + vty_out(vty, "!\n"); + } + } + + if (hc->default_rd.prefixlen + || hc->default_response_lifetime + != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT + || hc->default_rt_import_list || hc->default_rt_export_list + || hc->nve_groups_sequential->count) { + + + ++write; + vty_out(vty, " vnc defaults\n"); + + if (hc->default_rd.prefixlen) { + if (AF_UNIX == hc->default_rd.family) { + uint16_t value = 0; + + value = ((hc->default_rd.val[6] << 8) + & 0x0ff00) + | (hc->default_rd.val[7] + & 0x0ff); + + vty_out(vty, " rd auto:vn:%d\n", + value); + + } else + vty_out(vty, " rd %pRDP\n", + &hc->default_rd); + } + if (hc->default_response_lifetime + != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) { + vty_out(vty, " response-lifetime "); + if (hc->default_response_lifetime != UINT32_MAX) + vty_out(vty, "%d", + hc->default_response_lifetime); + else + vty_out(vty, "infinite"); + vty_out(vty, "\n"); + } + if (hc->default_rt_import_list + && hc->default_rt_export_list + && ecommunity_cmp(hc->default_rt_import_list, + hc->default_rt_export_list)) { + char *b = ecommunity_ecom2str( + hc->default_rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt both %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (hc->default_rt_import_list) { + char *b = ecommunity_ecom2str( + hc->default_rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt import %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (hc->default_rt_export_list) { + char *b = ecommunity_ecom2str( + hc->default_rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt export %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += (bgp->rfapi->rfp_methods.cfg_group_cb)( + vty, bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_DEFAULT, NULL, + bgp->rfapi_cfg->default_rfp_cfg); + vty_out(vty, " exit-vnc\n"); + vty_out(vty, "!\n"); + } + + for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode, + rfg)) + if (rfg->type == RFAPI_GROUP_CFG_NVE) { + ++write; + vty_out(vty, " vnc nve-group %s\n", rfg->name); + + if (rfg->vn_prefix.family && rfg->vn_node) + vty_out(vty, " prefix %s %pFX\n", "vn", + &rfg->vn_prefix); + + if (rfg->un_prefix.family && rfg->un_node) + vty_out(vty, " prefix %s %pFX\n", "un", + &rfg->un_prefix); + + + if (rfg->rd.prefixlen) { + if (AF_UNIX == rfg->rd.family) { + + uint16_t value = 0; + + value = ((rfg->rd.val[6] << 8) + & 0x0ff00) + | (rfg->rd.val[7] + & 0x0ff); + + vty_out(vty, + " rd auto:vn:%d\n", + value); + + } else + vty_out(vty, " rd %pRDP\n", + &rfg->rd); + } + if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) { + vty_out(vty, " response-lifetime "); + if (rfg->response_lifetime + != UINT32_MAX) + vty_out(vty, "%d", + rfg->response_lifetime); + else + vty_out(vty, "infinite"); + vty_out(vty, "\n"); + } + + if (rfg->rt_import_list && rfg->rt_export_list + && ecommunity_cmp(rfg->rt_import_list, + rfg->rt_export_list)) { + char *b = ecommunity_ecom2str( + rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt both %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (rfg->rt_import_list) { + char *b = ecommunity_ecom2str( + rfg->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt import %s\n", + b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (rfg->rt_export_list) { + char *b = ecommunity_ecom2str( + rfg->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt export %s\n", + b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + const char *afistr = (afi == AFI_IP) + ? "ipv4" + : "ipv6"; + + if (rfg->plist_export_bgp_name[afi]) { + vty_out(vty, + " export bgp %s prefix-list %s\n", + afistr, + rfg->plist_export_bgp_name + [afi]); + } + if (rfg->plist_export_zebra_name[afi]) { + vty_out(vty, + " export zebra %s prefix-list %s\n", + afistr, + rfg->plist_export_zebra_name + [afi]); + } + /* + * currently we only support redist + * plists for bgp-direct. + * If we later add plist support for + * redistributing other + * protocols, we'll need to loop over + * protocols here + */ + if (rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT] + [afi]) { + vty_out(vty, + " redistribute bgp-direct %s prefix-list %s\n", + afistr, + rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT] + [afi]); + } + if (rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT] + [afi]) { + vty_out(vty, + " redistribute bgp-direct-to-nve-groups %s prefix-list %s\n", + afistr, + rfg->plist_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT] + [afi]); + } + } + + if (rfg->routemap_export_bgp_name) { + vty_out(vty, + " export bgp route-map %s\n", + rfg->routemap_export_bgp_name); + } + if (rfg->routemap_export_zebra_name) { + vty_out(vty, + " export zebra route-map %s\n", + rfg->routemap_export_zebra_name); + } + if (rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT]) { + vty_out(vty, + " redistribute bgp-direct route-map %s\n", + rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT]); + } + if (rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vty_out(vty, + " redistribute bgp-direct-to-nve-groups route-map %s\n", + rfg->routemap_redist_name + [ZEBRA_ROUTE_BGP_DIRECT_EXT]); + } + if (bgp->rfapi->rfp_methods.cfg_group_cb) + write += (bgp->rfapi->rfp_methods + .cfg_group_cb)( + vty, bgp->rfapi->rfp, + RFAPI_RFP_CFG_GROUP_NVE, + rfg->name, rfg->rfp_cfg); + vty_out(vty, " exit-vnc\n"); + vty_out(vty, "!\n"); + } + } /* have listen ports */ + + /* + * route export to other protocols + */ + if (VNC_EXPORT_BGP_GRP_ENABLED(hc)) { + vty_out(vty, " vnc export bgp mode group-nve\n"); + } else if (VNC_EXPORT_BGP_RH_ENABLED(hc)) { + vty_out(vty, " vnc export bgp mode registering-nve\n"); + } else if (VNC_EXPORT_BGP_CE_ENABLED(hc)) { + vty_out(vty, " vnc export bgp mode ce\n"); + } + + if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) { + vty_out(vty, " vnc export zebra mode group-nve\n"); + } else if (VNC_EXPORT_ZEBRA_RH_ENABLED(hc)) { + vty_out(vty, " vnc export zebra mode registering-nve\n"); + } + + if (hc->rfg_export_direct_bgp_l) { + for (ALL_LIST_ELEMENTS(hc->rfg_export_direct_bgp_l, node, nnode, + rfgn)) { + + vty_out(vty, " vnc export bgp group-nve group %s\n", + rfgn->name); + } + } + + if (hc->rfg_export_zebra_l) { + for (ALL_LIST_ELEMENTS(hc->rfg_export_zebra_l, node, nnode, + rfgn)) { + + vty_out(vty, " vnc export zebra group-nve group %s\n", + rfgn->name); + } + } + + + if (hc->rfg_redist_name) { + vty_out(vty, " vnc redistribute nve-group %s\n", + hc->rfg_redist_name); + } + if (hc->redist_lifetime) { + vty_out(vty, " vnc redistribute lifetime %d\n", + hc->redist_lifetime); + } + if (hc->resolve_nve_roo_local_admin + != BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT) { + + vty_out(vty, + " vnc redistribute resolve-nve roo-ec-local-admin %d\n", + hc->resolve_nve_roo_local_admin); + } + + if (hc->redist_mode) /* ! default */ + { + const char *s = ""; + + switch (hc->redist_mode) { + case VNC_REDIST_MODE_PLAIN: + s = "plain"; + break; + case VNC_REDIST_MODE_RFG: + s = "nve-group"; + break; + case VNC_REDIST_MODE_RESOLVE_NVE: + s = "resolve-nve"; + break; + } + if (s) { + vty_out(vty, " vnc redistribute mode %s\n", s); + } + } + + /* + * route filtering: prefix-lists and route-maps + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6"; + + if (hc->plist_export_bgp_name[afi]) { + vty_out(vty, " vnc export bgp %s prefix-list %s\n", + afistr, hc->plist_export_bgp_name[afi]); + } + if (hc->plist_export_zebra_name[afi]) { + vty_out(vty, " vnc export zebra %s prefix-list %s\n", + afistr, hc->plist_export_zebra_name[afi]); + } + if (hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vty_out(vty, + " vnc redistribute bgp-direct %s prefix-list %s\n", + afistr, + hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT] + [afi]); + } + } + + if (hc->routemap_export_bgp_name) { + vty_out(vty, " vnc export bgp route-map %s\n", + hc->routemap_export_bgp_name); + } + if (hc->routemap_export_zebra_name) { + vty_out(vty, " vnc export zebra route-map %s\n", + hc->routemap_export_zebra_name); + } + if (hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) { + vty_out(vty, " vnc redistribute bgp-direct route-map %s\n", + hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]); + } + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) { + if (hc->redist[afi][type]) { + if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT + && hc->redist_bgp_exterior_view_name) { + vty_out(vty, + " vnc redistribute %s %s view %s\n", + ((afi == AFI_IP) ? "ipv4" + : "ipv6"), + zebra_route_string(type), + hc->redist_bgp_exterior_view_name); + } else { + vty_out(vty, + " vnc redistribute %s %s\n", + ((afi == AFI_IP) ? "ipv4" + : "ipv6"), + zebra_route_string(type)); + } + } + } + } + return write; +} + +void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty) +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + afi_t afi; + int type, redist = 0; + char tmp[40]; + if (hc == NULL) + return; + + vty_out(vty, "%-39s %-19s %s\n", "VNC Advertise method:", + (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP + ? "Encapsulation SAFI" + : "Tunnel Encap attribute"), + ((hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) + == (BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP + & BGP_VNC_CONFIG_FLAGS_DEFAULT) + ? "(default)" + : "")); + /* export */ + vty_out(vty, "%-39s ", "Export from VNC:"); + /* + * route export to other protocols + */ + if (VNC_EXPORT_BGP_GRP_ENABLED(hc)) { + redist++; + vty_out(vty, "ToBGP Groups={"); + if (hc->rfg_export_direct_bgp_l) { + int cnt = 0; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + for (ALL_LIST_ELEMENTS(hc->rfg_export_direct_bgp_l, + node, nnode, rfgn)) { + if (cnt++ != 0) + vty_out(vty, ","); + + vty_out(vty, "%s", rfgn->name); + } + } + vty_out(vty, "}"); + } else if (VNC_EXPORT_BGP_RH_ENABLED(hc)) { + redist++; + vty_out(vty, "ToBGP {Registering NVE}"); + /* note filters, route-maps not shown */ + } else if (VNC_EXPORT_BGP_CE_ENABLED(hc)) { + redist++; + vty_out(vty, "ToBGP {NVE connected router:%d}", + hc->resolve_nve_roo_local_admin); + /* note filters, route-maps not shown */ + } + + if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) { + redist++; + vty_out(vty, "%sToZebra Groups={", (redist == 1 ? "" : " ")); + if (hc->rfg_export_zebra_l) { + int cnt = 0; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + for (ALL_LIST_ELEMENTS(hc->rfg_export_zebra_l, node, + nnode, rfgn)) { + if (cnt++ != 0) + vty_out(vty, ","); + vty_out(vty, "%s", rfgn->name); + } + } + vty_out(vty, "}"); + } else if (VNC_EXPORT_ZEBRA_RH_ENABLED(hc)) { + redist++; + vty_out(vty, "%sToZebra {Registering NVE}", + (redist == 1 ? "" : " ")); + /* note filters, route-maps not shown */ + } + vty_out(vty, "%-19s %s\n", (redist ? "" : "Off"), + (redist ? "" : "(default)")); + + /* Redistribution */ + redist = 0; + vty_out(vty, "%-39s ", "Redistribution into VNC:"); + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) { + if (hc->redist[afi][type]) { + vty_out(vty, "{%s,%s} ", + ((afi == AFI_IP) ? "ipv4" : "ipv6"), + zebra_route_string(type)); + redist++; + } + } + } + vty_out(vty, "%-19s %s\n", (redist ? "" : "Off"), + (redist ? "" : "(default)")); + + vty_out(vty, "%-39s %3u%-16s %s\n", + "RFP Registration Hold-Down Factor:", + hc->rfp_cfg.holddown_factor, "%", + (hc->rfp_cfg.holddown_factor + == RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR + ? "(default)" + : "")); + vty_out(vty, "%-39s %-19s %s\n", "RFP Updated responses:", + (hc->rfp_cfg.use_updated_response == 0 ? "Off" : "On"), + (hc->rfp_cfg.use_updated_response == 0 ? "(default)" : "")); + vty_out(vty, "%-39s %-19s %s\n", "RFP Removal responses:", + (hc->rfp_cfg.use_removes == 0 ? "Off" : "On"), + (hc->rfp_cfg.use_removes == 0 ? "(default)" : "")); + vty_out(vty, "%-39s %-19s %s\n", "RFP Full table download:", + (hc->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL ? "On" + : "Off"), + (hc->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_PARTIAL + ? "(default)" + : "")); + snprintf(tmp, sizeof(tmp), "%u seconds", + hc->rfp_cfg.ftd_advertisement_interval); + vty_out(vty, "%-39s %-19s %s\n", " Advertisement Interval:", tmp, + (hc->rfp_cfg.ftd_advertisement_interval + == RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL + ? "(default)" + : "")); + vty_out(vty, "%-39s %d seconds\n", "Default RFP response lifetime:", + hc->default_response_lifetime); + vty_out(vty, "\n"); + return; +} + +struct rfapi_cfg *bgp_rfapi_get_config(struct bgp *bgp) +{ + struct rfapi_cfg *hc = NULL; + if (bgp == NULL) + bgp = bgp_get_default(); + if (bgp != NULL) + hc = bgp->rfapi_cfg; + return hc; +} + +#endif /* ENABLE_BGP_VNC */ diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h new file mode 100644 index 0000000..77549ad --- /dev/null +++ b/bgpd/rfapi/bgp_rfapi_cfg.h @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_CFG_H +#define _QUAGGA_BGP_RFAPI_CFG_H + +#include "lib/table.h" +#include "lib/routemap.h" + +#ifdef ENABLE_BGP_VNC +#include "rfapi.h" + +struct rfapi_l2_group_cfg { + char *name; + uint32_t logical_net_id; + struct list *labels; /* list of uint32_t */ + struct ecommunity *rt_import_list; + struct ecommunity *rt_export_list; + void *rfp_cfg; /* rfp owned group config */ + + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(rfapi_l2_group_cfg); + +typedef enum { + RFAPI_GROUP_CFG_NVE = 1, + RFAPI_GROUP_CFG_VRF, + RFAPI_GROUP_CFG_L2, + RFAPI_GROUP_CFG_MAX +} rfapi_group_cfg_type_t; + +struct rfapi_nve_group_cfg { + struct agg_node *vn_node; /* backref */ + struct agg_node *un_node; /* backref */ + + rfapi_group_cfg_type_t type; /* NVE|VPN */ + char *name; /* unique by type! */ + struct prefix vn_prefix; + struct prefix un_prefix; + + struct prefix_rd rd; + uint8_t l2rd; /* 0 = VN addr LSB */ + uint32_t response_lifetime; + uint32_t flags; +#define RFAPI_RFG_RESPONSE_LIFETIME 0x01 /* bits */ +#define RFAPI_RFG_L2RD 0x02 +#define RFAPI_RFG_VPN_NH_SELF 0x04 + struct ecommunity *rt_import_list; + struct ecommunity *rt_export_list; + struct rfapi_import_table *rfapi_import_table; + + void *rfp_cfg; /* rfp owned group config */ + /* + * List of NVE descriptors that are assigned to this NVE group + * + * Currently (Mar 2010) this list is used only by the route + * export code to generate per-NVE nexthops for each route. + * + * The nve descriptors listed here have pointers back to + * this nve group config structure to enable them to delete + * their own list entries when they are closed. Consequently, + * if an instance of this nve group config structure is deleted, + * we must first set the nve descriptor references to it to NULL. + */ + struct list *nves; + + /* + * Route filtering + * + * Prefix lists are segregated by afi (part of the base plist code) + * Route-maps are not segregated + */ + char *plist_export_bgp_name[AFI_MAX]; + struct prefix_list *plist_export_bgp[AFI_MAX]; + + char *plist_export_zebra_name[AFI_MAX]; + struct prefix_list *plist_export_zebra[AFI_MAX]; + + char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX]; + struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX]; + + char *routemap_export_bgp_name; + struct route_map *routemap_export_bgp; + + char *routemap_export_zebra_name; + struct route_map *routemap_export_zebra; + + char *routemap_redist_name[ZEBRA_ROUTE_MAX]; + struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; + + /* for VRF type groups */ + uint32_t label; + struct rfapi_descriptor *rfd; + QOBJ_FIELDS; +}; +DECLARE_QOBJ_TYPE(rfapi_nve_group_cfg); + +struct rfapi_rfg_name { + struct rfapi_nve_group_cfg *rfg; + char *name; +}; + +typedef enum { + VNC_REDIST_MODE_PLAIN = 0, /* 0 = default */ + VNC_REDIST_MODE_RFG, + VNC_REDIST_MODE_RESOLVE_NVE +} vnc_redist_mode_t; + +struct rfapi_cfg { + struct prefix_rd default_rd; + uint8_t default_l2rd; + struct ecommunity *default_rt_import_list; + struct ecommunity *default_rt_export_list; + uint32_t default_response_lifetime; +#define BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT 3600 + void *default_rfp_cfg; /* rfp owned group config */ + + struct list *l2_groups; /* rfapi_l2_group_cfg list */ + /* three views into the same collection of rfapi_nve_group_cfg */ + struct list *nve_groups_sequential; + struct agg_table *nve_groups_vn[AFI_MAX]; + struct agg_table *nve_groups_un[AFI_MAX]; + + /* + * For Single VRF export to ordinary routing protocols. This is + * the nve-group that the ordinary protocols belong to. We use it + * to set the RD when sending unicast Zebra routes to VNC + */ + uint8_t redist[AFI_MAX][ZEBRA_ROUTE_MAX]; + uint32_t redist_lifetime; + vnc_redist_mode_t redist_mode; + + /* + * view name of BGP unicast instance that holds + * exterior routes + */ + char *redist_bgp_exterior_view_name; + struct bgp *redist_bgp_exterior_view; + + /* + * nve group for redistribution of routes from zebra to VNC + * (which is probably not useful for production networks) + */ + char *rfg_redist_name; + struct rfapi_nve_group_cfg *rfg_redist; + + /* + * List of NVE groups on whose behalf we will export VNC + * routes to zebra. ((NB: it's actually a list of <struct + * rfapi_rfg_name>) + * This list is used when BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS is + * BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP + */ + struct list *rfg_export_zebra_l; + + /* + * List of NVE groups on whose behalf we will export VNC + * routes directly to the bgp unicast RIB. (NB: it's actually + * a list of <struct rfapi_rfg_name>) + * This list is used when BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS is + * BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP + */ + struct list *rfg_export_direct_bgp_l; + + /* + * Exported Route filtering + * + * Prefix lists are segregated by afi (part of the base plist code) + * Route-maps are not segregated + */ + char *plist_export_bgp_name[AFI_MAX]; + struct prefix_list *plist_export_bgp[AFI_MAX]; + + char *plist_export_zebra_name[AFI_MAX]; + struct prefix_list *plist_export_zebra[AFI_MAX]; + + char *routemap_export_bgp_name; + struct route_map *routemap_export_bgp; + + char *routemap_export_zebra_name; + struct route_map *routemap_export_zebra; + + /* + * Redistributed route filtering (routes from other + * protocols into VNC) + */ + char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX]; + struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX]; + + char *routemap_redist_name[ZEBRA_ROUTE_MAX]; + struct route_map *routemap_redist[ZEBRA_ROUTE_MAX]; + + /* + * For importing bgp unicast routes to VNC, we encode the CE + * (route nexthop) in a Route Origin extended community. The + * local part (16-bit) is user-configurable. + */ + uint16_t resolve_nve_roo_local_admin; +#define BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT 5226 + + uint32_t flags; +#define BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP 0x00000001 +#define BGP_VNC_CONFIG_CALLBACK_DISABLE 0x00000002 +#define BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE 0x00000004 + +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS 0x000000f0 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS 0x00000f00 + +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE 0x00000000 +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP 0x00000010 +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH 0x00000020 /* registerd nve */ +#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE 0x00000040 + +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_NONE 0x00000000 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP 0x00000100 +#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH 0x00000200 + +#define BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP 0x00001000 +#define BGP_VNC_CONFIG_L2RD 0x00002000 + +/* Use new NVE RIB to filter callback routes */ +/* Filter querying NVE's registrations from responses */ +/* Default to updated-responses off */ +/* Default to removal-responses off */ +#define BGP_VNC_CONFIG_FLAGS_DEFAULT \ + (BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP | BGP_VNC_CONFIG_CALLBACK_DISABLE \ + | BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE) + + struct rfapi_rfp_cfg rfp_cfg; /* rfp related configuration */ +}; + +#define VNC_EXPORT_ZEBRA_GRP_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) \ + == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP) + +#define VNC_EXPORT_ZEBRA_RH_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) \ + == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH) + +#define VNC_EXPORT_BGP_GRP_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \ + == BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP) + +#define VNC_EXPORT_BGP_RH_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \ + == BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH) + +#define VNC_EXPORT_BGP_CE_ENABLED(hc) \ + (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \ + == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) + + +void bgp_rfapi_cfg_init(void); + +struct rfapi_cfg *bgp_rfapi_cfg_new(struct rfapi_rfp_cfg *cfg); + +void bgp_rfapi_cfg_destroy(struct bgp *bgp, struct rfapi_cfg *h); + +int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp); + +extern int bgp_rfapi_is_vnc_configured(struct bgp *bgp); + +extern void nve_group_to_nve_list(struct rfapi_nve_group_cfg *rfg, + struct list **nves, + uint8_t family); /* AF_INET, AF_INET6 */ + +struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc, + struct prefix *vn, + struct prefix *un); + +struct rfapi_nve_group_cfg * +bgp_rfapi_cfg_match_byname(struct bgp *bgp, const char *name, + rfapi_group_cfg_type_t type); /* _MAX = any */ + +extern void vnc_prefix_list_update(struct bgp *bgp); + +extern void vnc_routemap_update(struct bgp *bgp, const char *unused); + +extern void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty); + +extern struct rfapi_cfg *bgp_rfapi_get_config(struct bgp *bgp); + +extern struct rfapi_l2_group_cfg * +bgp_rfapi_get_group_by_lni_label(struct bgp *bgp, uint32_t logical_net_id, + uint32_t label); + +extern struct ecommunity * +bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import, + uint32_t logical_net_id, + uint32_t label); /* note, 20bit label! */ + +extern struct list * +bgp_rfapi_get_labellist_by_lni_label(struct bgp *bgp, uint32_t logical_net_id, + uint32_t label); /* note, 20bit label! */ + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_CFG_H */ diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c new file mode 100644 index 0000000..ff7137b --- /dev/null +++ b/bgpd/rfapi/rfapi.c @@ -0,0 +1,4060 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" +#include "lib/ringbuf.h" +#include "lib/lib_errors.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" + +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" + +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_vnc_types.h" +#include "bgpd/bgp_zebra.h" + +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_ap.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" + +#ifdef HAVE_GLIBC_BACKTRACE +/* for backtrace and friends */ +#include <execinfo.h> +#endif /* HAVE_GLIBC_BACKTRACE */ + +#define DEBUG_CLEANUP 0 + +struct ethaddr rfapi_ethaddr0 = {{0}}; + +#define DEBUG_RFAPI_STR "RF API debugging/testing command\n" + +const char *rfapi_error_str(int code) +{ + switch (code) { + case 0: + return "Success"; + case ENXIO: + return "BGP or VNC not configured"; + case ENOENT: + return "No match"; + case EEXIST: + return "Handle already open"; + case ENOMSG: + return "Incomplete configuration"; + case EAFNOSUPPORT: + return "Invalid address family"; + case EDEADLK: + return "Called from within a callback procedure"; + case EBADF: + return "Invalid handle"; + case EINVAL: + return "Invalid argument"; + case ESTALE: + return "Stale descriptor"; + default: + return "Unknown error"; + } +} + +/*------------------------------------------ + * rfapi_get_response_lifetime_default + * + * Returns the default lifetime for a response. + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * input: + * None + * + * output: + * + * return value: The bgp instance default lifetime for a response. + --------------------------------------------*/ +int rfapi_get_response_lifetime_default(void *rfp_start_val) +{ + struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (bgp) + return bgp->rfapi_cfg->default_response_lifetime; + return BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT; +} + +/*------------------------------------------ + * rfapi_is_vnc_configured + * + * Returns if VNC is configured + * + * input: + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * ENXIO VNC not configured + --------------------------------------------*/ +int rfapi_is_vnc_configured(void *rfp_start_val) +{ + struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (bgp_rfapi_is_vnc_configured(bgp) == 0) + return 0; + return ENXIO; +} + + +/*------------------------------------------ + * rfapi_get_vn_addr + * + * Get the virtual network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * vn NVE virtual network address + *------------------------------------------*/ +struct rfapi_ip_addr *rfapi_get_vn_addr(void *rfd) +{ + struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd; + return &rrfd->vn_addr; +} + +/*------------------------------------------ + * rfapi_get_un_addr + * + * Get the underlay network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * un NVE underlay network address + *------------------------------------------*/ +struct rfapi_ip_addr *rfapi_get_un_addr(void *rfd) +{ + struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd; + return &rrfd->un_addr; +} + +int rfapi_ip_addr_cmp(struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2) +{ + if (a1->addr_family != a2->addr_family) + return a1->addr_family - a2->addr_family; + + if (a1->addr_family == AF_INET) { + return IPV4_ADDR_CMP(&a1->addr.v4, &a2->addr.v4); + } + + if (a1->addr_family == AF_INET6) { + return IPV6_ADDR_CMP(&a1->addr.v6, &a2->addr.v6); + } + + assert(1); + /* NOTREACHED */ + return 1; +} + +static int rfapi_find_node(struct bgp *bgp, struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + struct agg_node **node) +{ + struct rfapi *h; + struct prefix p; + struct agg_node *rn; + int rc; + afi_t afi; + + if (!bgp) { + return ENXIO; + } + + h = bgp->rfapi; + if (!h) { + return ENXIO; + } + + afi = family2afi(un_addr->addr_family); + if (!afi) { + return EAFNOSUPPORT; + } + + if ((rc = rfapiRaddr2Qprefix(un_addr, &p))) + return rc; + + rn = agg_node_lookup(h->un[afi], &p); + + if (!rn) + return ENOENT; + + agg_unlock_node(rn); + + *node = rn; + + return 0; +} + + +int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, struct rfapi_descriptor **rfd) +{ + struct agg_node *rn; + int rc; + + rc = rfapi_find_node(bgp, vn_addr, un_addr, &rn); + + if (rc) + return rc; + + for (*rfd = (struct rfapi_descriptor *)(rn->info); *rfd; + *rfd = (*rfd)->next) { + if (!rfapi_ip_addr_cmp(&(*rfd)->vn_addr, vn_addr)) + break; + } + + if (!*rfd) + return ENOENT; + + return 0; +} + +/*------------------------------------------ + * rfapi_find_handle + * + * input: + * un underlay network address + * vn virtual network address + * + * output: + * pHandle pointer to location to store handle + * + * return value: + * 0 Success + * ENOENT no matching handle + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +static int rfapi_find_handle(struct bgp *bgp, struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + rfapi_handle *handle) +{ + struct rfapi_descriptor **rfd; + + rfd = (struct rfapi_descriptor **)handle; + + return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd); +} + +static int rfapi_find_handle_vty(struct vty *vty, struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + rfapi_handle *handle) +{ + struct bgp *bgp; + struct rfapi_descriptor **rfd; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + + rfd = (struct rfapi_descriptor **)handle; + + return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd); +} + +static int is_valid_rfd(struct rfapi_descriptor *rfd) +{ + rfapi_handle hh; + + if (!rfd || rfd->bgp == NULL) + return 0; + + if (CHECK_FLAG( + rfd->flags, + RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */ + return 1; + + if (rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh)) + return 0; + + if (rfd != hh) + return 0; + + return 1; +} + +/* + * check status of descriptor + */ +int rfapi_check(void *handle) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + rfapi_handle hh; + int rc; + + if (!rfd || rfd->bgp == NULL) + return EINVAL; + + if (CHECK_FLAG( + rfd->flags, + RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */ + return 0; + + if ((rc = rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr, + &hh))) + return rc; + + if (rfd != hh) + return ENOENT; + + if (!rfd->rfg) + return ESTALE; + + return 0; +} + + +void del_vnc_route(struct rfapi_descriptor *rfd, + struct peer *peer, /* rfd->peer for RFP regs */ + struct bgp *bgp, safi_t safi, const struct prefix *p, + struct prefix_rd *prd, uint8_t type, uint8_t sub_type, + struct rfapi_nexthop *lnh, int kill) +{ + afi_t afi; /* of the VN address */ + struct bgp_dest *bn; + struct bgp_path_info *bpi; + struct prefix_rd prd0; + + afi = family2afi(p->family); + assert(afi == AFI_IP || afi == AFI_IP6); + + if (safi == SAFI_ENCAP) { + memset(&prd0, 0, sizeof(prd0)); + prd0.family = AF_UNSPEC; + prd0.prefixlen = 64; + prd = &prd0; + } + bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + + vnc_zlog_debug_verbose( + "%s: peer=%p, prefix=%pFX, prd=%pRDP afi=%d, safi=%d bn=%p, bn->info=%p", + __func__, peer, p, prd, afi, safi, bn, + (bn ? bgp_dest_get_bgp_path_info(bn) : NULL)); + + for (bpi = (bn ? bgp_dest_get_bgp_path_info(bn) : NULL); bpi; + bpi = bpi->next) { + + vnc_zlog_debug_verbose( + "%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%" PRIu64, + __func__, bpi, bpi->peer, bpi->type, bpi->sub_type, + (bpi->extra ? bpi->extra->vnc.export.rfapi_handle + : NULL), + CHECK_FLAG(bpi->attr->flag, + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF) + ? bpi->attr->local_pref : 0)); + + if (bpi->peer == peer && bpi->type == type + && bpi->sub_type == sub_type && bpi->extra + && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { + + vnc_zlog_debug_verbose("%s: matched it", __func__); + + break; + } + } + + if (lnh) { + /* + * lnh set means to JUST delete the local nexthop from this + * route. Leave the route itself in place. + * TBD add return code reporting of success/failure + */ + if (!bpi || !bpi->extra + || !bpi->extra->vnc.export.local_nexthops) { + /* + * no local nexthops + */ + vnc_zlog_debug_verbose( + "%s: lnh list already empty at prefix %pFX", + __func__, p); + goto done; + } + + /* + * look for it + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc.export.local_nexthops, + node, pLnh)) { + + if (prefix_same(&pLnh->addr, &lnh->addr)) { + break; + } + } + + if (pLnh) { + listnode_delete(bpi->extra->vnc.export.local_nexthops, + pLnh); + + /* silly rabbit, listnode_delete doesn't invoke + * list->del on data */ + rfapi_nexthop_free(pLnh); + } else { + vnc_zlog_debug_verbose("%s: desired lnh not found %pFX", + __func__, p); + } + goto done; + } + + /* + * loop back to import tables + * Do this before removing from BGP RIB because rfapiProcessWithdraw + * might refer to it + */ + rfapiProcessWithdraw(peer, rfd, p, prd, NULL, afi, safi, type, kill); + + if (bpi) { + vnc_zlog_debug_verbose( + "%s: Found route (safi=%d) to delete at prefix %pFX", + __func__, safi, p); + + if (safi == SAFI_MPLS_VPN) { + struct bgp_dest *pdest = NULL; + struct bgp_table *table = NULL; + + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); + if (table) + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + bgp, prd, table, p, bpi); + bgp_dest_unlock_node(pdest); + } + + /* + * Delete local_nexthops list + */ + if (bpi->extra && bpi->extra->vnc.export.local_nexthops) + list_delete(&bpi->extra->vnc.export.local_nexthops); + + bgp_aggregate_decrement(bgp, p, bpi, afi, safi); + bgp_path_info_delete(bn, bpi); + bgp_process(bgp, bn, afi, safi); + } else { + vnc_zlog_debug_verbose( + "%s: Couldn't find route (safi=%d) at prefix %pFX", + __func__, safi, p); + } +done: + bgp_dest_unlock_node(bn); +} + +struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme) +{ + struct rfapi_nexthop *new = + XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_nexthop)); + if (copyme) + *new = *copyme; + return new; +} + +void rfapi_nexthop_free(void *p) +{ + struct rfapi_nexthop *goner = p; + XFREE(MTYPE_RFAPI_NEXTHOP, goner); +} + +struct rfapi_vn_option *rfapi_vn_options_dup(struct rfapi_vn_option *existing) +{ + struct rfapi_vn_option *p; + struct rfapi_vn_option *head = NULL; + struct rfapi_vn_option *tail = NULL; + + for (p = existing; p; p = p->next) { + struct rfapi_vn_option *new; + + new = XCALLOC(MTYPE_RFAPI_VN_OPTION, + sizeof(struct rfapi_vn_option)); + *new = *p; + new->next = NULL; + if (tail) + (tail)->next = new; + tail = new; + if (!head) { + head = new; + } + } + return head; +} + +void rfapi_un_options_free(struct rfapi_un_option *p) +{ + struct rfapi_un_option *next; + + while (p) { + next = p->next; + XFREE(MTYPE_RFAPI_UN_OPTION, p); + p = next; + } +} + +void rfapi_vn_options_free(struct rfapi_vn_option *p) +{ + struct rfapi_vn_option *next; + + while (p) { + next = p->next; + XFREE(MTYPE_RFAPI_VN_OPTION, p); + p = next; + } +} + +/* Based on bgp_redistribute_add() */ +void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */ + struct bgp *bgp, int safi, const struct prefix *p, + struct prefix_rd *prd, struct rfapi_ip_addr *nexthop, + uint32_t *local_pref, + uint32_t *lifetime, /* NULL => dont send lifetime */ + struct bgp_tea_options *rfp_options, + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + struct ecommunity *rt_export_list, /* Copied, not consumed */ + uint32_t *med, /* NULL => don't set med */ + uint32_t *label, /* low order 3 bytes */ + uint8_t type, uint8_t sub_type, /* RFP, NORMAL or REDIST */ + int flags) +{ + afi_t afi; /* of the VN address */ + struct bgp_path_info *new; + struct bgp_path_info *bpi; + struct bgp_dest *bn; + + struct attr attr = {0}; + struct attr *new_attr; + uint32_t label_val; + + struct bgp_attr_encap_subtlv *encaptlv; + char buf[PREFIX_STRLEN]; + + struct rfapi_nexthop *lnh = NULL; /* local nexthop */ + struct rfapi_vn_option *vo; + struct rfapi_l2address_option *l2o = NULL; + struct rfapi_ip_addr *un_addr = &rfd->un_addr; + + bgp_encap_types TunnelType = BGP_ENCAP_TYPE_RESERVED; + struct bgp_redist *red; + + if (safi == SAFI_ENCAP + && !(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)) { + + /* + * Encap mode not enabled. UN addresses will be communicated + * via VNC Tunnel subtlv instead. + */ + vnc_zlog_debug_verbose( + "%s: encap mode not enabled, not adding SAFI_ENCAP route", + __func__); + return; + } + + for (vo = options_vn; vo; vo = vo->next) { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) { + l2o = &vo->v.l2addr; + if (RFAPI_0_ETHERADDR(&l2o->macaddr)) + l2o = NULL; /* not MAC resolution */ + } + if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) { + lnh = &vo->v.local_nexthop; + } + } + + if (label) + label_val = *label; + else + label_val = MPLS_LABEL_IMPLICIT_NULL; + + afi = family2afi(p->family); + assert(afi == AFI_IP || afi == AFI_IP6); + + vnc_zlog_debug_verbose("%s: afi=%s, safi=%s", __func__, afi2str(afi), + safi2str(safi)); + + /* Make default attribute. Produces already-interned attr.aspath */ + /* Cripes, the memory management of attributes is byzantine */ + + bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * aspath: points to interned hash from aspath hash table + */ + + + /* + * Route-specific un_options get added to the VPN SAFI + * advertisement tunnel encap attribute. (the per-NVE + * "default" un_options are put into the 1-per-NVE ENCAP + * SAFI advertisement). The VPN SAFI also gets the + * default un_options if there are no route-specific options. + */ + if (options_un) { + struct rfapi_un_option *uo; + + for (uo = options_un; uo; uo = uo->next) { + if (RFAPI_UN_OPTION_TYPE_TUNNELTYPE == uo->type) { + TunnelType = rfapi_tunneltype_option_to_tlv( + bgp, un_addr, &uo->v.tunnel, &attr, + l2o != NULL); + } + } + } else { + /* + * Add encap attr + * These are the NVE-specific "default" un_options which are + * put into the 1-per-NVE ENCAP advertisement. + */ + if (rfd->default_tunneltype_option.type) { + TunnelType = rfapi_tunneltype_option_to_tlv( + bgp, un_addr, &rfd->default_tunneltype_option, + &attr, l2o != NULL); + } else /* create default for local addse */ + if (type == ZEBRA_ROUTE_BGP + && sub_type == BGP_ROUTE_RFP) + TunnelType = rfapi_tunneltype_option_to_tlv( + bgp, un_addr, NULL, &attr, l2o != NULL); + } + + if (TunnelType == BGP_ENCAP_TYPE_MPLS) { + if (safi == SAFI_ENCAP) { + /* Encap SAFI not used with MPLS */ + vnc_zlog_debug_verbose( + "%s: mpls tunnel type, encap safi omitted", + __func__); + aspath_unintern(&attr.aspath); /* Unintern original. */ + return; + } + } + + if (local_pref) { + attr.local_pref = *local_pref; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + } + + if (med) { + attr.med = *med; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + } + + /* override default weight assigned by bgp_attr_default_set() */ + attr.weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0; + + /* + * NB: ticket 81: do not reset attr.aspath here because it would + * cause iBGP peers to drop route + */ + + /* + * Set originator ID for routes imported from BGP directly. + * These routes could be synthetic, and therefore could + * reuse the peer pointers of the routes they are derived + * from. Setting the originator ID to "us" prevents the + * wrong originator ID from being sent when this route is + * sent from a route reflector. + */ + if (type == ZEBRA_ROUTE_BGP_DIRECT + || type == ZEBRA_ROUTE_BGP_DIRECT_EXT) { + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID); + attr.originator_id = bgp->router_id; + } + + + /* Set up vnc attribute (sub-tlv for Prefix Lifetime) */ + if (lifetime && *lifetime != RFAPI_INFINITE_LIFETIME) { + uint32_t lt; + + encaptlv = XCALLOC(MTYPE_ENCAP_TLV, + sizeof(struct bgp_attr_encap_subtlv) + 4); + encaptlv->type = + BGP_VNC_SUBTLV_TYPE_LIFETIME; /* prefix lifetime */ + encaptlv->length = 4; + lt = htonl(*lifetime); + memcpy(encaptlv->value, <, 4); + bgp_attr_set_vnc_subtlvs(&attr, encaptlv); + vnc_zlog_debug_verbose( + "%s: set Encap Attr Prefix Lifetime to %d", __func__, + *lifetime); + } + + /* add rfp options to vnc attr */ + if (rfp_options) { + + if (flags & RFAPI_AHR_RFPOPT_IS_VNCTLV) { + struct bgp_attr_encap_subtlv *vnc_subtlvs = + bgp_attr_get_vnc_subtlvs(&attr); + /* + * this flag means we're passing a pointer to an + * existing encap tlv chain which we should copy. + * It's a hack to avoid adding yet another argument + * to add_vnc_route() + */ + encaptlv = encap_tlv_dup( + (struct bgp_attr_encap_subtlv *)rfp_options); + if (vnc_subtlvs) + vnc_subtlvs->next = encaptlv; + else + bgp_attr_set_vnc_subtlvs(&attr, encaptlv); + } else { + struct bgp_tea_options *hop; + /* XXX max of one tlv present so far from above code */ + struct bgp_attr_encap_subtlv *tail = + bgp_attr_get_vnc_subtlvs(&attr); + + for (hop = rfp_options; hop; hop = hop->next) { + + /* + * Construct subtlv + */ + encaptlv = XCALLOC( + MTYPE_ENCAP_TLV, + sizeof(struct bgp_attr_encap_subtlv) + 2 + + hop->length); + encaptlv->type = + BGP_VNC_SUBTLV_TYPE_RFPOPTION; /* RFP + option + */ + encaptlv->length = 2 + hop->length; + *((uint8_t *)(encaptlv->value) + 0) = hop->type; + *((uint8_t *)(encaptlv->value) + 1) = + hop->length; + memcpy(((uint8_t *)encaptlv->value) + 2, + hop->value, hop->length); + + /* + * add to end of subtlv chain + */ + if (tail) + tail->next = encaptlv; + else + bgp_attr_set_vnc_subtlvs(&attr, + encaptlv); + tail = encaptlv; + } + } + } + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * aspath: points to interned hash from aspath hash table + */ + + + bgp_attr_set_ecommunity(&attr, ecommunity_new()); + assert(bgp_attr_get_ecommunity(&attr)); + + if (TunnelType != BGP_ENCAP_TYPE_MPLS + && TunnelType != BGP_ENCAP_TYPE_RESERVED) { + /* + * Add BGP Encapsulation Extended Community. Format described in + * section 4.5 of RFC 5512. + * Always include when not MPLS type, to disambiguate this case. + */ + struct ecommunity_val beec; + + memset(&beec, 0, sizeof(beec)); + beec.val[0] = ECOMMUNITY_ENCODE_OPAQUE; + beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP; + beec.val[6] = ((TunnelType) >> 8) & 0xff; + beec.val[7] = (TunnelType)&0xff; + ecommunity_add_val(bgp_attr_get_ecommunity(&attr), &beec, false, + false); + } + + /* + * Add extended community attributes to match rt export list + */ + if (rt_export_list) { + bgp_attr_set_ecommunity( + &attr, ecommunity_merge(bgp_attr_get_ecommunity(&attr), + rt_export_list)); + } + + struct ecommunity *ecomm = bgp_attr_get_ecommunity(&attr); + + if (!ecomm->size) { + ecommunity_free(&ecomm); + bgp_attr_set_ecommunity(&attr, NULL); + } + vnc_zlog_debug_verbose("%s: attr.ecommunity=%p", __func__, ecomm); + + + /* + * At this point: + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: dynamic 2-part + * aspath: points to interned hash from aspath hash table + */ + + /* stuff nexthop in attr_extra; which field depends on IPv4 or IPv6 */ + switch (nexthop->addr_family) { + case AF_INET: + /* + * set this field to prevent bgp_route.c code from setting + * mp_nexthop_global_in to self + */ + attr.nexthop.s_addr = nexthop->addr.v4.s_addr; + + attr.mp_nexthop_global_in = nexthop->addr.v4; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + break; + + case AF_INET6: + attr.mp_nexthop_global = nexthop->addr.v6; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; + break; + + default: + assert(0); + } + + + prefix2str(p, buf, sizeof(buf)); + + /* + * At this point: + * + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: dynamic 2-part + * aspath: points to interned hash from aspath hash table + */ + + red = bgp_redist_lookup(bgp, afi, type, 0); + + if (red && red->redist_metric_flag) { + attr.med = red->redist_metric; + attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + } + + bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + + /* + * bgp_attr_intern creates a new reference to a cached + * attribute, but leaves the following bits of trash: + * - old attr + * - old attr->extra (free via bgp_attr_extra_free(attr)) + * + * Note that it frees the original attr->extra->ecommunity + * but leaves the new attribute pointing to the ORIGINAL + * vnc options (which therefore we needn't free from the + * static attr) + */ + new_attr = bgp_attr_intern(&attr); + + aspath_unintern(&attr.aspath); /* Unintern original. */ + + /* + * At this point: + * + * attr: static + * extra: dynamically allocated, owned by attr + * vnc_subtlvs: dynamic chain, length 1 + * ecommunity: POINTS TO INTERNED ecom, THIS REF NOT COUNTED + * + * new_attr: an attr that is part of the hash table, distinct + * from attr which is static. + * extra: dynamically allocated, owned by new_attr (in hash table) + * vnc_subtlvs: POINTS TO SAME dynamic chain AS attr + * ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr + * aspath: POINTS TO interned/refcounted hashed block + */ + for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) { + /* probably only need to check + * bpi->extra->vnc.export.rfapi_handle */ + if (bpi->peer == rfd->peer && bpi->type == type + && bpi->sub_type == sub_type && bpi->extra + && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { + + break; + } + } + + if (bpi) { + + /* + * Adding new local_nexthop, which does not by itself change + * what is advertised via BGP + */ + if (lnh) { + if (!bpi->extra->vnc.export.local_nexthops) { + /* TBD make arrangements to free when needed */ + bpi->extra->vnc.export.local_nexthops = + list_new(); + bpi->extra->vnc.export.local_nexthops->del = + rfapi_nexthop_free; + } + + /* + * already present? + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO( + bpi->extra->vnc.export.local_nexthops, + node, pLnh)) { + + if (prefix_same(&pLnh->addr, &lnh->addr)) { + break; + } + } + + /* + * Not present, add new one + */ + if (!pLnh) { + pLnh = rfapi_nexthop_new(lnh); + listnode_add( + bpi->extra->vnc.export.local_nexthops, + pLnh); + } + } + + if (attrhash_cmp(bpi->attr, new_attr) + && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + bgp_attr_unintern(&new_attr); + bgp_dest_unlock_node(bn); + + vnc_zlog_debug_any( + "%s: Found route (safi=%d) at prefix %s, no change", + __func__, safi, buf); + + goto done; + } else { + /* The attribute is changed. */ + bgp_path_info_set_flag(bn, bpi, BGP_PATH_ATTR_CHANGED); + + if (safi == SAFI_MPLS_VPN) { + struct bgp_dest *pdest = NULL; + struct bgp_table *table = NULL; + + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); + if (table) + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + bgp, prd, table, p, bpi); + bgp_dest_unlock_node(pdest); + } + + /* Rewrite BGP route information. */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + bgp_path_info_restore(bn, bpi); + else + bgp_aggregate_decrement(bgp, p, bpi, afi, safi); + bgp_attr_unintern(&bpi->attr); + bpi->attr = new_attr; + bpi->uptime = monotime(NULL); + + + if (safi == SAFI_MPLS_VPN) { + struct bgp_dest *pdest = NULL; + struct bgp_table *table = NULL; + + pdest = bgp_node_get(bgp->rib[afi][safi], + (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); + if (table) + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + bgp, prd, table, p, bpi); + bgp_dest_unlock_node(pdest); + } + + /* Process change. */ + bgp_aggregate_increment(bgp, p, bpi, afi, safi); + bgp_process(bgp, bn, afi, safi); + bgp_dest_unlock_node(bn); + + vnc_zlog_debug_any( + "%s: Found route (safi=%d) at prefix %s, changed attr", + __func__, safi, buf); + + goto done; + } + } + + new = info_make(type, sub_type, 0, rfd->peer, new_attr, NULL); + SET_FLAG(new->flags, BGP_PATH_VALID); + + /* save backref to rfapi handle */ + bgp_path_info_extra_get(new); + new->extra->vnc.export.rfapi_handle = (void *)rfd; + encode_label(label_val, &new->extra->label[0]); + + /* debug */ + + if (VNC_DEBUG(VERBOSE)) { + vnc_zlog_debug_verbose("%s: printing BPI", __func__); + rfapiPrintBi(NULL, new); + } + + bgp_aggregate_increment(bgp, p, new, afi, safi); + bgp_path_info_add(bn, new); + + if (safi == SAFI_MPLS_VPN) { + struct bgp_dest *pdest = NULL; + struct bgp_table *table = NULL; + + pdest = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd); + table = bgp_dest_get_bgp_table_info(pdest); + if (table) + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + bgp, prd, table, p, new); + bgp_dest_unlock_node(pdest); + encode_label(label_val, &bn->local_label); + } + + bgp_dest_unlock_node(bn); + bgp_process(bgp, bn, afi, safi); + + vnc_zlog_debug_any( + "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRDP)", + __func__, safi2str(safi), buf, bn, prd); + +done: + /* Loop back to import tables */ + rfapiProcessUpdate(rfd->peer, rfd, p, prd, new_attr, afi, safi, type, + sub_type, &label_val); + vnc_zlog_debug_verbose("%s: looped back import route (safi=%d)", + __func__, safi); +} + +uint32_t rfp_cost_to_localpref(uint8_t cost) +{ + return 255 - cost; +} + +static void rfapiTunnelRouteAnnounce(struct bgp *bgp, + struct rfapi_descriptor *rfd, + uint32_t *pLifetime) +{ + struct prefix_rd prd; + struct prefix pfx_vn; + int rc; + uint32_t local_pref = rfp_cost_to_localpref(0); + + rc = rfapiRaddr2Qprefix(&(rfd->vn_addr), &pfx_vn); + assert(!rc); + + /* + * Construct route distinguisher = 0 + */ + memset(&prd, 0, sizeof(prd)); + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + add_vnc_route(rfd, /* rfapi descr, for export list & backref */ + bgp, /* which bgp instance */ + SAFI_ENCAP, /* which SAFI */ + &pfx_vn, /* prefix to advertise */ + &prd, /* route distinguisher to use */ + &rfd->un_addr, /* nexthop */ + &local_pref, + pLifetime, /* max lifetime of child VPN routes */ + NULL, /* no rfp options for ENCAP safi */ + NULL, /* rfp un options */ + NULL, /* rfp vn options */ + rfd->rt_export_list, NULL, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0); +} + + +/*********************************************************************** + * RFP processing behavior configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_set_configuration + * + * This is used to change rfapi's processing behavior based on + * RFP requirements. + * + * input: + * rfp_start_val value returned by rfp_start + * rfapi_rfp_cfg Pointer to configuration structure + * + * output: + * none + * + * return value: + * 0 Success + * ENXIO Unabled to locate configured BGP/VNC +--------------------------------------------*/ +int rfapi_rfp_set_configuration(void *rfp_start_val, struct rfapi_rfp_cfg *new) +{ + struct rfapi_rfp_cfg *rcfg; + struct bgp *bgp; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + + if (!new || !bgp || !bgp->rfapi_cfg) + return ENXIO; + + rcfg = &bgp->rfapi_cfg->rfp_cfg; + rcfg->download_type = new->download_type; + rcfg->ftd_advertisement_interval = new->ftd_advertisement_interval; + rcfg->holddown_factor = new->holddown_factor; + + if (rcfg->use_updated_response != new->use_updated_response) { + rcfg->use_updated_response = new->use_updated_response; + if (rcfg->use_updated_response) + rfapiMonitorCallbacksOn(bgp); + else + rfapiMonitorCallbacksOff(bgp); + } + if (rcfg->use_removes != new->use_removes) { + rcfg->use_removes = new->use_removes; + if (rcfg->use_removes) + rfapiMonitorResponseRemovalOn(bgp); + else + rfapiMonitorResponseRemovalOff(bgp); + } + return 0; +} + +/*------------------------------------------ + * rfapi_rfp_set_cb_methods + * + * Change registered callback functions for asynchronous notifications + * from RFAPI to the RFP client. + * + * input: + * rfp_start_val value returned by rfp_start + * methods Pointer to struct rfapi_rfp_cb_methods containing + * pointers to callback methods as described above + * + * return value: + * 0 Success + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +int rfapi_rfp_set_cb_methods(void *rfp_start_val, + struct rfapi_rfp_cb_methods *methods) +{ + struct rfapi *h; + struct bgp *bgp; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + h->rfp_methods = *methods; + + return 0; +} + +/*********************************************************************** + * NVE Sessions + ***********************************************************************/ +/* + * Caller must supply an already-allocated rfd with the "caller" + * fields already set (vn_addr, un_addr, callback, cookie) + * The advertised_prefixes[] array elements should be NULL to + * have this function set them to newly-allocated radix trees. + */ +static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp, + struct rfapi *h, struct rfapi_nve_group_cfg *rfg) +{ + int ret; + + if (h->flags & RFAPI_INCALLBACK) + return EDEADLK; + + /* + * Fill in configured fields + */ + + /* + * If group's RD is specified as "auto", then fill in based + * on NVE's VN address + */ + rfd->rd = rfg->rd; + + if (rfd->rd.family == AF_UNIX) { + ret = rfapi_set_autord_from_vn(&rfd->rd, &rfd->vn_addr); + if (ret != 0) + return ret; + } + rfd->rt_export_list = (rfg->rt_export_list) + ? ecommunity_dup(rfg->rt_export_list) + : NULL; + rfd->response_lifetime = rfg->response_lifetime; + rfd->rfg = rfg; + + /* + * Fill in BGP peer structure + */ + rfd->peer = peer_new(bgp); + rfd->peer->connection->status = Established; /* keep bgp core happy */ + + bgp_peer_connection_buffers_free(rfd->peer->connection); + + { /* base code assumes have valid host pointer */ + char buf[INET6_ADDRSTRLEN]; + buf[0] = 0; + + if (rfd->vn_addr.addr_family == AF_INET) { + inet_ntop(AF_INET, &rfd->vn_addr.addr.v4, buf, + sizeof(buf)); + } else if (rfd->vn_addr.addr_family == AF_INET6) { + inet_ntop(AF_INET6, &rfd->vn_addr.addr.v6, buf, + sizeof(buf)); + } + rfd->peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, buf); + } + /* Mark peer as belonging to HD */ + SET_FLAG(rfd->peer->flags, PEER_FLAG_IS_RFAPI_HD); + + /* + * Set min prefix lifetime to max value so it will get set + * upon first rfapi_register() + */ + rfd->min_prefix_lifetime = UINT32_MAX; + +/* + * Allocate response tables if needed + */ +#define RFD_RTINIT_AFI(rh, ary, afi) \ + do { \ + if (!ary[afi]) { \ + ary[afi] = agg_table_init(); \ + agg_set_table_info(ary[afi], rh); \ + } \ + } while (0) + +#define RFD_RTINIT(rh, ary) \ + do { \ + RFD_RTINIT_AFI(rh, ary, AFI_IP); \ + RFD_RTINIT_AFI(rh, ary, AFI_IP6); \ + RFD_RTINIT_AFI(rh, ary, AFI_L2VPN); \ + } while (0) + + RFD_RTINIT(rfd, rfd->rib); + RFD_RTINIT(rfd, rfd->rib_pending); + RFD_RTINIT(rfd, rfd->rsp_times); + + /* + * Link to Import Table + */ + rfd->import_table = rfg->rfapi_import_table; + rfd->import_table->refcount += 1; + + rfapiApInit(&rfd->advertised); + + /* + * add this NVE descriptor to the list of NVEs in the NVE group + */ + if (!rfg->nves) { + rfg->nves = list_new(); + } + listnode_add(rfg->nves, rfd); + + vnc_direct_bgp_add_nve(bgp, rfd); + vnc_zebra_add_nve(bgp, rfd); + + return 0; +} + +/* moved from rfapi_register */ +int rfapi_init_and_open(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_nve_group_cfg *rfg) +{ + struct rfapi *h = bgp->rfapi; + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + afi_t afi_vn, afi_un; + struct prefix pfx_un; + struct agg_node *rn; + + rfd->open_time = monotime(NULL); + + if (rfg->type == RFAPI_GROUP_CFG_VRF) + SET_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF); + + rfapiRfapiIpAddr2Str(&rfd->vn_addr, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str(&rfd->un_addr, buf_un, BUFSIZ); + + vnc_zlog_debug_verbose("%s: new RFD with VN=%s UN=%s cookie=%p", + __func__, buf_vn, buf_un, rfd->cookie); + + if (rfg->type != RFAPI_GROUP_CFG_VRF) /* unclear if needed for VRF */ + { + listnode_add(&h->descriptors, rfd); + if (h->descriptors.count > h->stat.max_descriptors) { + h->stat.max_descriptors = h->descriptors.count; + } + + /* + * attach to UN radix tree + */ + afi_vn = family2afi(rfd->vn_addr.addr_family); + afi_un = family2afi(rfd->un_addr.addr_family); + assert(afi_vn && afi_un); + assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un)); + + rn = agg_node_get(h->un[afi_un], &pfx_un); + assert(rn); + rfd->next = rn->info; + rn->info = rfd; + rfd->un_node = rn; + } + return rfapi_open_inner(rfd, bgp, h, rfg); +} + +struct rfapi_vn_option *rfapiVnOptionsDup(struct rfapi_vn_option *orig) +{ + struct rfapi_vn_option *head = NULL; + struct rfapi_vn_option *tail = NULL; + struct rfapi_vn_option *vo = NULL; + + for (vo = orig; vo; vo = vo->next) { + struct rfapi_vn_option *new; + + new = XCALLOC(MTYPE_RFAPI_VN_OPTION, + sizeof(struct rfapi_vn_option)); + memcpy(new, vo, sizeof(struct rfapi_vn_option)); + new->next = NULL; + + if (tail) { + tail->next = new; + } else { + head = tail = new; + } + } + return head; +} + +struct rfapi_un_option *rfapiUnOptionsDup(struct rfapi_un_option *orig) +{ + struct rfapi_un_option *head = NULL; + struct rfapi_un_option *tail = NULL; + struct rfapi_un_option *uo = NULL; + + for (uo = orig; uo; uo = uo->next) { + struct rfapi_un_option *new; + + new = XCALLOC(MTYPE_RFAPI_UN_OPTION, + sizeof(struct rfapi_un_option)); + memcpy(new, uo, sizeof(struct rfapi_un_option)); + new->next = NULL; + + if (tail) { + tail->next = new; + } else { + head = tail = new; + } + } + return head; +} + +struct bgp_tea_options *rfapiOptionsDup(struct bgp_tea_options *orig) +{ + struct bgp_tea_options *head = NULL; + struct bgp_tea_options *tail = NULL; + struct bgp_tea_options *hop = NULL; + + for (hop = orig; hop; hop = hop->next) { + struct bgp_tea_options *new; + + new = XCALLOC(MTYPE_BGP_TEA_OPTIONS, + sizeof(struct bgp_tea_options)); + memcpy(new, hop, sizeof(struct bgp_tea_options)); + new->next = NULL; + if (hop->value) { + new->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE, + hop->length); + memcpy(new->value, hop->value, hop->length); + } + if (tail) { + tail->next = new; + } else { + head = tail = new; + } + } + return head; +} + +void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p) +{ + struct bgp_tea_options *next; + + while (p) { + next = p->next; + + XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); + XFREE(MTYPE_BGP_TEA_OPTIONS, p); + + p = next; + } +} + +void rfapiAdbFree(struct rfapi_adb *adb) +{ + XFREE(MTYPE_RFAPI_ADB, adb); +} + +static int +rfapi_query_inner(void *handle, struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, /* may be NULL */ + struct rfapi_next_hop_entry **ppNextHopEntry) +{ + afi_t afi; + struct prefix p; + struct prefix p_original; + struct agg_node *rn; + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + struct bgp *bgp = rfd->bgp; + struct rfapi_next_hop_entry *pNHE = NULL; + struct rfapi_ip_addr *self_vn_addr = NULL; + int eth_is_0 = 0; + int use_eth_resolution = 0; + struct rfapi_next_hop_entry *i_nhe; + + /* preemptive */ + if (!bgp) { + vnc_zlog_debug_verbose("%s: No BGP instance, returning ENXIO", + __func__); + return ENXIO; + } + if (!bgp->rfapi) { + vnc_zlog_debug_verbose("%s: No RFAPI instance, returning ENXIO", + __func__); + return ENXIO; + } + if (bgp->rfapi->flags & RFAPI_INCALLBACK) { + vnc_zlog_debug_verbose( + "%s: Called during calback, returning EDEADLK", + __func__); + return EDEADLK; + } + + if (!is_valid_rfd(rfd)) { + vnc_zlog_debug_verbose("%s: invalid handle, returning EBADF", + __func__); + return EBADF; + } + + rfd->rsp_counter++; /* dedup: identify this generation */ + rfd->rsp_time = monotime(NULL); /* response content dedup */ + rfd->ftd_last_allowed_time = + monotime(NULL) - + bgp->rfapi_cfg->rfp_cfg.ftd_advertisement_interval; + + if (l2o) { + if (!memcmp(l2o->macaddr.octet, rfapi_ethaddr0.octet, + ETH_ALEN)) { + eth_is_0 = 1; + } + /* per t/c Paul/Lou 151022 */ + if (!eth_is_0 || l2o->logical_net_id) { + use_eth_resolution = 1; + } + } + + if (ppNextHopEntry) + *ppNextHopEntry = NULL; + + /* + * Save original target in prefix form. In case of L2-based queries, + * p_original will be modified to reflect the L2 target + */ + assert(!rfapiRaddr2Qprefix(target, &p_original)); + + if (bgp->rfapi_cfg->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL) { + /* convert query to 0/0 when full-table download is enabled */ + memset((char *)&p, 0, sizeof(p)); + p.family = target->addr_family; + } else { + p = p_original; + } + + { + char *s; + + vnc_zlog_debug_verbose("%s(rfd=%p, target=%pFX, ppNextHop=%p)", + __func__, rfd, &p, ppNextHopEntry); + + s = ecommunity_ecom2str(rfd->import_table->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vnc_zlog_debug_verbose( + "%s rfd->import_table=%p, rfd->import_table->rt_import_list: %s", + __func__, rfd->import_table, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + afi = family2afi(p.family); + assert(afi); + + if (CHECK_FLAG(bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) { + self_vn_addr = &rfd->vn_addr; + } + + if (use_eth_resolution) { + uint32_t logical_net_id = l2o->logical_net_id; + struct ecommunity *l2com; + + /* + * fix up p_original to contain L2 address + */ + rfapiL2o2Qprefix(l2o, &p_original); + + l2com = bgp_rfapi_get_ecommunity_by_lni_label( + bgp, 1, logical_net_id, l2o->label); + if (l2com) { + uint8_t *v = l2com->val; + logical_net_id = (v[5] << 16) + (v[6] << 8) + (v[7]); + } + /* + * Ethernet/L2-based lookup + * + * Always returns IT node corresponding to route + */ + + if (RFAPI_RFP_DOWNLOAD_FULL + == bgp->rfapi_cfg->rfp_cfg.download_type) { + eth_is_0 = 1; + } + + rn = rfapiMonitorEthAdd( + bgp, rfd, (eth_is_0 ? &rfapi_ethaddr0 : &l2o->macaddr), + logical_net_id); + + if (eth_is_0) { + struct rfapi_ip_prefix rprefix; + + memset(&rprefix, 0, sizeof(rprefix)); + rprefix.prefix.addr_family = target->addr_family; + if (target->addr_family == AF_INET) { + rprefix.length = IPV4_MAX_BITLEN; + } else { + rprefix.length = IPV6_MAX_BITLEN; + } + + pNHE = rfapiEthRouteTable2NextHopList( + logical_net_id, &rprefix, + rfd->response_lifetime, self_vn_addr, + rfd->rib[afi], &p_original); + goto done; + } + + } else { + + /* + * IP-based lookup + */ + + rn = rfapiMonitorAdd(bgp, rfd, &p); + + /* + * If target address is 0, this request is special: means to + * return ALL routes in the table + * + * Monitors for All-Routes queries get put on a special list, + * not in the VPN tree + */ + if (RFAPI_0_PREFIX(&p)) { + + vnc_zlog_debug_verbose("%s: 0-prefix", __func__); + + /* + * Generate nexthop list for caller + */ + pNHE = rfapiRouteTable2NextHopList( + rfd->import_table->imported_vpn[afi], + rfd->response_lifetime, self_vn_addr, + rfd->rib[afi], &p_original); + goto done; + } + + if (rn) { + agg_lock_node(rn); /* so we can unlock below */ + } else { + /* + * returns locked node. Don't unlock yet because the + * unlock + * might free it before we're done with it. This + * situation + * could occur when rfapiMonitorGetAttachNode() returns + * a + * newly-created default node. + */ + rn = rfapiMonitorGetAttachNode(rfd, &p); + } + } + + assert(rn); + if (!rn->info) { + agg_unlock_node(rn); + vnc_zlog_debug_verbose( + "%s: VPN route not found, returning ENOENT", __func__); + return ENOENT; + } + + if (VNC_DEBUG(RFAPI_QUERY)) { + rfapiShowImportTable(NULL, "query", + rfd->import_table->imported_vpn[afi], 1); + } + + if (use_eth_resolution) { + + struct rfapi_ip_prefix rprefix; + + memset(&rprefix, 0, sizeof(rprefix)); + rprefix.prefix.addr_family = target->addr_family; + if (target->addr_family == AF_INET) { + rprefix.length = IPV4_MAX_BITLEN; + } else { + rprefix.length = IPV6_MAX_BITLEN; + } + + pNHE = rfapiEthRouteNode2NextHopList( + rn, &rprefix, rfd->response_lifetime, self_vn_addr, + rfd->rib[afi], &p_original); + + + } else { + /* + * Generate answer to query + */ + pNHE = rfapiRouteNode2NextHopList(rn, rfd->response_lifetime, + self_vn_addr, rfd->rib[afi], + &p_original); + } + + agg_unlock_node(rn); + +done: + if (ppNextHopEntry) { + /* only count if caller gets it */ + ++bgp->rfapi->response_immediate_count; + } + + if (!pNHE) { + vnc_zlog_debug_verbose("%s: NO NHEs, returning ENOENT", + __func__); + return ENOENT; + } + + /* + * count nexthops for statistics + */ + for (i_nhe = pNHE; i_nhe; i_nhe = i_nhe->next) { + ++rfd->stat_count_nh_reachable; + } + + if (ppNextHopEntry) { + *ppNextHopEntry = pNHE; + } else { + rfapi_free_next_hop_list(pNHE); + } + + vnc_zlog_debug_verbose("%s: success", __func__); + return 0; +} + +/* + * support on-the-fly reassignment of an already-open nve to a new + * nve-group in the event that its original nve-group is + * administratively deleted. + */ +static int rfapi_open_rfd(struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + struct prefix pfx_vn; + struct prefix pfx_un; + struct rfapi_nve_group_cfg *rfg; + struct rfapi *h; + struct rfapi_cfg *hc; + int rc; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + hc = bgp->rfapi_cfg; + if (!hc) + return ENXIO; + + rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn); + assert(!rc); + + rc = rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un); + assert(!rc); + + /* + * Find the matching nve group config block + */ + rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un); + if (!rfg) { + return ENOENT; + } + + /* + * check nve group config block for required values + */ + if (!rfg->rt_export_list || !rfg->rfapi_import_table) { + + return ENOMSG; + } + + rc = rfapi_open_inner(rfd, bgp, h, rfg); + if (rc) { + return rc; + } + + /* + * re-advertise registered routes, this time as part of new NVE-group + */ + rfapiApReadvertiseAll(bgp, rfd); + + /* + * re-attach callbacks to import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + rfapiMonitorAttachImportHd(rfd); + } + + return 0; +} + +/*------------------------------------------ + * rfapi_open + * + * This function initializes a NVE record and associates it with + * the specified VN and underlay network addresses + * + * input: + * rfp_start_val value returned by rfp_start + * vn NVE virtual network address + * + * un NVE underlay network address + * + * default_options Default options to use on registrations. + * For now only tunnel type is supported. + * May be overridden per-prefix in rfapi_register(). + * Caller owns (rfapi_open() does not free) + * + * response_cb Pointer to next hop list update callback function or + * NULL when no callbacks are desired. + * + * userdata Passed to subsequent response_cb invocations. + * + * output: + * response_lifetime The length of time that responses sent to this + * NVE are valid. + * + * pHandle pointer to location to store rfapi handle. The + * handle must be passed on subsequent rfapi_ calls. + * + * + * return value: + * 0 Success + * EEXIST NVE with this {vn,un} already open + * ENOENT No matching nve group config + * ENOMSG Matched nve group config was incomplete + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD, + * but underlay network address is not IPv4 + * EDEADLK Called from within a callback procedure + *------------------------------------------*/ +int rfapi_open(void *rfp_start_val, struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un, + struct rfapi_un_option *default_options, + uint32_t *response_lifetime, + void *userdata, /* callback cookie */ + rfapi_handle *pHandle) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_descriptor *rfd; + struct rfapi_cfg *hc; + struct rfapi_nve_group_cfg *rfg; + + struct prefix pfx_vn; + struct prefix pfx_un; + + int rc; + rfapi_handle hh = NULL; + int reusing_provisional = 0; + + { + char buf[2][INET_ADDRSTRLEN]; + vnc_zlog_debug_verbose( + "%s: VN=%s UN=%s", __func__, + rfapiRfapiIpAddr2Str(vn, buf[0], INET_ADDRSTRLEN), + rfapiRfapiIpAddr2Str(un, buf[1], INET_ADDRSTRLEN)); + } + + assert(pHandle); + *pHandle = NULL; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + hc = bgp->rfapi_cfg; + if (!hc) + return ENXIO; + + if (h->flags & RFAPI_INCALLBACK) + return EDEADLK; + + rc = rfapiRaddr2Qprefix(vn, &pfx_vn); + assert(!rc); + + rc = rfapiRaddr2Qprefix(un, &pfx_un); + assert(!rc); + + /* + * already have a descriptor with VN and UN? + */ + if (!rfapi_find_handle(bgp, vn, un, &hh)) { + /* + * we might have set up a handle for static routes before + * this NVE was opened. In that case, reuse the handle + */ + rfd = hh; + if (!CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_PROVISIONAL)) { + return EEXIST; + } + + /* + * reuse provisional descriptor + * hh is not NULL + */ + reusing_provisional = 1; + } + + /* + * Find the matching nve group config block + */ + rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un); + if (!rfg) { + ++h->stat.count_unknown_nves; + { + char buf[2][INET_ADDRSTRLEN]; + zlog_notice("%s: no matching group VN=%s UN=%s", + __func__, + rfapiRfapiIpAddr2Str(vn, buf[0], + INET_ADDRSTRLEN), + rfapiRfapiIpAddr2Str(un, buf[1], + INET_ADDRSTRLEN)); + } + return ENOENT; + } + + /* + * check nve group config block for required values + */ + if (!rfg->rt_export_list || !rfg->rfapi_import_table) { + + ++h->stat.count_unknown_nves; + return ENOMSG; + } + + /* + * If group config specifies auto-rd assignment, check that + * VN address is IPv4|v6 so we don't fail in rfapi_open_inner(). + * Check here so we don't need to unwind memory allocations, &c. + */ + if ((rfg->rd.family == AF_UNIX) && (vn->addr_family != AF_INET) + && (vn->addr_family != AF_INET6)) { + return EAFNOSUPPORT; + } + + if (hh) { + /* + * reusing provisional rfd + */ + rfd = hh; + } else { + rfd = XCALLOC(MTYPE_RFAPI_DESC, + sizeof(struct rfapi_descriptor)); + } + + rfd->bgp = bgp; + if (default_options) { + struct rfapi_un_option *p; + + for (p = default_options; p; p = p->next) { + if ((RFAPI_UN_OPTION_TYPE_PROVISIONAL == p->type)) { + rfd->flags |= RFAPI_HD_FLAG_PROVISIONAL; + } + if ((RFAPI_UN_OPTION_TYPE_TUNNELTYPE == p->type)) { + rfd->default_tunneltype_option = p->v.tunnel; + } + } + } + + /* + * Fill in caller fields + */ + rfd->vn_addr = *vn; + rfd->un_addr = *un; + rfd->cookie = userdata; + + if (!reusing_provisional) { + rc = rfapi_init_and_open(bgp, rfd, rfg); + /* + * This can fail only if the VN address is IPv6 and the group + * specified auto-assignment of RDs, which only works for v4, + * and the check above should catch it. + * + * Another failure possibility is that we were called + * during an rfapi callback. Also checked above. + */ + assert(!rc); + } + + if (response_lifetime) + *response_lifetime = rfd->response_lifetime; + *pHandle = rfd; + return 0; +} + +/* + * For use with debug functions + */ +static int rfapi_set_response_cb(struct rfapi_descriptor *rfd, + rfapi_response_cb_t *response_cb) +{ + if (!is_valid_rfd(rfd)) + return EBADF; + rfd->response_cb = response_cb; + return 0; +} + +/* + * rfapi_close_inner + * + * Does almost all the work of rfapi_close, except: + * 1. preserves the descriptor (doesn't free it) + * 2. preserves the prefix query list (i.e., rfd->mon list) + * 3. preserves the advertised prefix list (rfd->advertised) + * 4. preserves the rib and rib_pending tables + * + * The purpose of organizing it this way is to support on-the-fly + * reassignment of an already-open nve to a new nve-group in the + * event that its original nve-group is administratively deleted. + */ +static int rfapi_close_inner(struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + int rc; + struct prefix pfx_vn; + struct prefix_rd prd; /* currently always 0 for VN->UN */ + + if (!is_valid_rfd(rfd)) + return EBADF; + + rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn); + assert(!rc); /* should never have bad AF in stored vn address */ + + /* + * update exported routes to reflect disappearance of this NVE as + * nexthop + */ + vnc_direct_bgp_del_nve(bgp, rfd); + vnc_zebra_del_nve(bgp, rfd); + + /* + * unlink this HD's monitors from import table + */ + rfapiMonitorDetachImportHd(rfd); + + /* + * Unlink from Import Table + * NB rfd->import_table will be NULL if we are closing a stale + * descriptor + */ + if (rfd->import_table) + rfapiImportTableRefDelByIt(bgp, rfd->import_table); + rfd->import_table = NULL; + + /* + * Construct route distinguisher + */ + memset(&prd, 0, sizeof(prd)); + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * withdraw tunnel + */ + del_vnc_route(rfd, rfd->peer, bgp, SAFI_ENCAP, + &pfx_vn, /* prefix being advertised */ + &prd, /* route distinguisher to use (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); /* no kill */ + + /* + * Construct route distinguisher for VPN routes + */ + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * find all VPN routes associated with this rfd and delete them, too + */ + rfapiApWithdrawAll(bgp, rfd); + + /* + * remove this nve descriptor from the list of nves + * associated with the nve group + */ + if (rfd->rfg) { + listnode_delete(rfd->rfg->nves, rfd); + rfd->rfg = NULL; /* XXX mark as orphaned/stale */ + } + + if (rfd->rt_export_list) + ecommunity_free(&rfd->rt_export_list); + rfd->rt_export_list = NULL; + + /* + * free peer structure (possibly delayed until its + * refcount reaches zero) + */ + if (rfd->peer) { + vnc_zlog_debug_verbose("%s: calling peer_delete(%p), #%d", + __func__, rfd->peer, rfd->peer->lock); + peer_delete(rfd->peer); + } + rfd->peer = NULL; + + return 0; +} + +int rfapi_close(void *handle) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + int rc; + struct agg_node *node; + struct bgp *bgp; + struct rfapi *h; + + vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); + +#ifdef RFAPI_WHO_IS_CALLING_ME +#ifdef HAVE_GLIBC_BACKTRACE +#define RFAPI_DEBUG_BACKTRACE_NENTRIES 5 + { + void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; + char **syms; + int i; + size_t size; + + size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); + syms = backtrace_symbols(buf, size); + for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; + ++i) { + vnc_zlog_debug_verbose("backtrace[%2d]: %s", i, + syms[i]); + } + free(syms); + } +#endif +#endif + + bgp = rfd->bgp; + if (!bgp) + return ENXIO; + + h = bgp->rfapi; + if (!h) + return ENXIO; + + if (!is_valid_rfd(rfd)) + return EBADF; + + if (h->flags & RFAPI_INCALLBACK) { + /* + * Queue these close requests for processing after callback + * is finished + */ + if (!CHECK_FLAG(rfd->flags, + RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) { + work_queue_add(h->deferred_close_q, handle); + vnc_zlog_debug_verbose( + "%s: added handle %p to deferred close queue", + __func__, handle); + } + return 0; + } + + if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) { + + vnc_zlog_debug_verbose("%s administrative close rfd=%p", + __func__, rfd); + + if (h->rfp_methods.close_cb) { + vnc_zlog_debug_verbose( + "%s calling close callback rfd=%p", __func__, + rfd); + + /* + * call the callback fairly early so that it can still + * lookup un/vn + * from handle, etc. + * + * NB RFAPI_INCALLBACK is tested above, so if we reach + * this point + * we are not already in the context of a callback. + */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.close_cb)(handle, EIDRM); + h->flags &= ~RFAPI_INCALLBACK; + } + } + + if (rfd->rfg) { + /* + * Orphaned descriptors have already done this part, so do + * only for non-orphaned descriptors. + */ + if ((rc = rfapi_close_inner(rfd, bgp))) + return rc; + } + + /* + * Remove descriptor from UN index + * (remove from chain at node) + */ + rc = rfapi_find_node(bgp, &rfd->vn_addr, &rfd->un_addr, &node); + if (!rc) { + struct rfapi_descriptor *hh; + + if (node->info == rfd) { + node->info = rfd->next; + } else { + + for (hh = node->info; hh; hh = hh->next) { + if (hh->next == rfd) { + hh->next = rfd->next; + break; + } + } + } + agg_unlock_node(node); + } + + /* + * remove from descriptor list + */ + listnode_delete(&h->descriptors, rfd); + + /* + * Delete monitor list items and free monitor structures + */ + (void)rfapiMonitorDelHd(rfd); + + /* + * release advertised prefix data + */ + rfapiApRelease(&rfd->advertised); + + /* + * Release RFP callback RIB + */ + rfapiRibFree(rfd); + + /* + * free descriptor + */ + memset(rfd, 0, sizeof(struct rfapi_descriptor)); + XFREE(MTYPE_RFAPI_DESC, rfd); + + return 0; +} + +/* + * Reopen a nve descriptor. If the descriptor's NVE-group + * does not exist (e.g., if it has been administratively removed), + * reassignment to a new NVE-group is attempted. + * + * If NVE-group reassignment fails, the descriptor becomes "stale" + * (rfd->rfg == NULL implies "stale:). The only permissible API operation + * on a stale descriptor is rfapi_close(). Any other rfapi_* API operation + * on the descriptor will return ESTALE. + * + * Reopening a descriptor is a potentially expensive operation, because + * it involves withdrawing any routes advertised by the NVE, withdrawing + * the NVE's route queries, and then re-adding them all after a new + * NVE-group is assigned. There are also possible route-export affects + * caused by deleting and then adding the NVE: advertised prefixes + * and nexthop lists for exported routes can turn over. + */ +int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp) +{ + struct rfapi *h; + int rc; + + if ((rc = rfapi_close_inner(rfd, bgp))) { + return rc; + } + if ((rc = rfapi_open_rfd(rfd, bgp))) { + + h = bgp->rfapi; + + assert(h != NULL && !CHECK_FLAG(h->flags, RFAPI_INCALLBACK)); + + if (CHECK_FLAG(rfd->flags, + RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY) + && h && h->rfp_methods.close_cb) { + + /* + * NB RFAPI_INCALLBACK is tested above, so if we reach + * this point + * we are not already in the context of a callback. + */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.close_cb)((rfapi_handle)rfd, ESTALE); + h->flags &= ~RFAPI_INCALLBACK; + } + return rc; + } + return 0; +} + +/*********************************************************************** + * NVE Routes + ***********************************************************************/ +/* + * Announce reachability to this prefix via the NVE + */ +int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix, + uint32_t lifetime, /* host byte order */ + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + rfapi_register_action action) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + struct bgp *bgp; + struct prefix p; + struct prefix *pfx_ip = NULL; + struct prefix_rd prd; + afi_t afi; + struct prefix pfx_mac_buf; + struct prefix *pfx_mac = NULL; + struct prefix pfx_vn_buf; + const char *action_str = NULL; + uint32_t *label = NULL; + struct rfapi_vn_option *vo; + struct rfapi_l2address_option *l2o = NULL; + struct prefix_rd *prd_override = NULL; + + switch (action) { + case RFAPI_REGISTER_ADD: + action_str = "add"; + break; + case RFAPI_REGISTER_WITHDRAW: + action_str = "withdraw"; + break; + case RFAPI_REGISTER_KILL: + action_str = "kill"; + break; + default: + assert(0); + break; + } + + /* + * Inspect VN options + */ + for (vo = options_vn; vo; vo = vo->next) { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) { + l2o = &vo->v.l2addr; + } + if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type) { + prd_override = &vo->v.internal_rd; + } + } + + /********************************************************************* + * advertise prefix + *********************************************************************/ + + /* + * set <p> based on <prefix> + */ + assert(!rfapiRprefix2Qprefix(prefix, &p)); + + afi = family2afi(prefix->prefix.addr_family); + assert(afi); + + vnc_zlog_debug_verbose( + "%s(rfd=%p, pfx=%pFX, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)", + __func__, rfd, &p, lifetime, options_un, options_vn, + action_str); + + /* + * These tests come after the prefix conversion so that we can + * print the prefix in a debug message before failing + */ + + bgp = rfd->bgp; + if (!bgp) { + vnc_zlog_debug_verbose("%s: no BGP instance: returning ENXIO", + __func__); + return ENXIO; + } + if (!bgp->rfapi) { + vnc_zlog_debug_verbose("%s: no RFAPI instance: returning ENXIO", + __func__); + return ENXIO; + } + if (!rfd->rfg) { + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations_failed; + } + vnc_zlog_debug_verbose( + "%s: rfd=%p, no RF GRP instance: returning ESTALE", + __func__, rfd); + return ESTALE; + } + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) { + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations_failed; + } + vnc_zlog_debug_verbose("%s: in callback: returning EDEADLK", + __func__); + return EDEADLK; + } + + if (!is_valid_rfd(rfd)) { + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations_failed; + } + vnc_zlog_debug_verbose("%s: invalid handle: returning EBADF", + __func__); + return EBADF; + } + + /* + * Is there a MAC address in this registration? + */ + if (l2o && !RFAPI_0_ETHERADDR(&l2o->macaddr)) { + rfapiL2o2Qprefix(l2o, &pfx_mac_buf); + pfx_mac = &pfx_mac_buf; + } + + /* + * Is there an IP prefix in this registration? + */ + if (!(RFAPI_0_PREFIX(&p) && RFAPI_HOST_PREFIX(&p))) { + pfx_ip = &p; + } else { + if (!pfx_mac) { + vnc_zlog_debug_verbose( + "%s: missing mac addr that is required for host 0 pfx", + __func__); + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations_failed; + } + return EINVAL; + } + if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) { + vnc_zlog_debug_verbose( + "%s: handle has bad vn_addr: returning EBADF", + __func__); + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations_failed; + } + return EBADF; + } + } + + if (RFAPI_REGISTER_ADD == action) { + ++bgp->rfapi->stat.count_registrations; + } + + /* + * Figure out if this registration is missing an IP address + * + * MAC-addr based: + * + * In RFAPI, we use prefixes in family AF_LINK to store + * the MAC addresses. These prefixes are used for the + * list of advertised prefixes and in the RFAPI import + * tables. + * + * In BGP proper, we use the prefix matching the NVE's + * VN address with a host prefix-length (i.e., 32 or 128). + * + */ + if (l2o && l2o->logical_net_id && RFAPI_0_PREFIX(&p) + && RFAPI_HOST_PREFIX(&p)) { + + rfapiL2o2Qprefix(l2o, &pfx_mac_buf); + pfx_mac = &pfx_mac_buf; + } + + /* + * Construct route distinguisher + */ + if (prd_override) { + prd = *prd_override; + } else { + memset(&prd, 0, sizeof(prd)); + if (pfx_mac) { + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + encode_rd_type(RD_TYPE_VNC_ETH, prd.val); + if (l2o->local_nve_id + || !(rfd->rfg->flags & RFAPI_RFG_L2RD)) { + /* + * If Local NVE ID is specified in message, use + * it. + * (if no local default configured, also use it + * even if 0) + */ + prd.val[1] = l2o->local_nve_id; + } else { + if (rfd->rfg->l2rd) { + /* + * locally-configured literal value + */ + prd.val[1] = rfd->rfg->l2rd; + } else { + /* + * 0 means auto:vn, which means use LSB + * of VN addr + */ + if (rfd->vn_addr.addr_family + == AF_INET) { + prd.val[1] = + *(((char *)&rfd->vn_addr + .addr.v4 + .s_addr) + + 3); + } else { + prd.val[1] = + *(((char *)&rfd->vn_addr + .addr.v6 + .s6_addr) + + 15); + } + } + } + memcpy(prd.val + 2, pfx_mac->u.prefix_eth.octet, 6); + } else { + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + } + } + + + if (action == RFAPI_REGISTER_WITHDRAW + || action == RFAPI_REGISTER_KILL) { + + int adv_tunnel = 0; + + /* + * withdraw previous advertisement + */ + del_vnc_route( + rfd, rfd->peer, bgp, SAFI_MPLS_VPN, + pfx_ip ? pfx_ip + : &pfx_vn_buf, /* prefix being advertised */ + &prd, /* route distinguisher (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, + action == RFAPI_REGISTER_KILL); + + if (0 == rfapiApDelete(bgp, rfd, &p, pfx_mac, &prd, + &adv_tunnel)) { + if (adv_tunnel) + rfapiTunnelRouteAnnounce( + bgp, rfd, &rfd->max_prefix_lifetime); + } + + } else { + + int adv_tunnel = 0; + uint32_t local_pref; + struct ecommunity *rtlist = NULL; + struct ecommunity_val ecom_value; + + if (!rfapiApCount(rfd)) { + /* + * make sure we advertise tunnel route upon adding the + * first VPN route + */ + adv_tunnel = 1; + } + + if (rfapiApAdd(bgp, rfd, &p, pfx_mac, &prd, lifetime, + prefix->cost, l2o)) { + adv_tunnel = 1; + } + + vnc_zlog_debug_verbose("%s: adv_tunnel = %d", __func__, + adv_tunnel); + if (adv_tunnel) { + vnc_zlog_debug_verbose("%s: announcing tunnel route", + __func__); + rfapiTunnelRouteAnnounce(bgp, rfd, + &rfd->max_prefix_lifetime); + } + + vnc_zlog_debug_verbose("%s: calling add_vnc_route", __func__); + + local_pref = rfp_cost_to_localpref(prefix->cost); + + if (l2o && l2o->label) + label = &l2o->label; + + if (pfx_mac) { + struct ecommunity *l2com = NULL; + + if (label) { + l2com = bgp_rfapi_get_ecommunity_by_lni_label( + bgp, 1, l2o->logical_net_id, *label); + } + if (l2com) { + rtlist = ecommunity_dup(l2com); + } else { + /* + * If mac address is set, add an RT based on the + * registered LNI + */ + memset((char *)&ecom_value, 0, + sizeof(ecom_value)); + ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET; + ecom_value.val[5] = + (l2o->logical_net_id >> 16) & 0xff; + ecom_value.val[6] = + (l2o->logical_net_id >> 8) & 0xff; + ecom_value.val[7] = + (l2o->logical_net_id >> 0) & 0xff; + rtlist = ecommunity_new(); + ecommunity_add_val(rtlist, &ecom_value, + false, false); + } + if (l2o->tag_id) { + as_t as = bgp->as; + uint16_t val = l2o->tag_id; + memset((char *)&ecom_value, 0, + sizeof(ecom_value)); + ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET; + if (as > BGP_AS_MAX) { + ecom_value.val[0] = + ECOMMUNITY_ENCODE_AS4; + ecom_value.val[2] = (as >> 24) & 0xff; + ecom_value.val[3] = (as >> 16) & 0xff; + ecom_value.val[4] = (as >> 8) & 0xff; + ecom_value.val[5] = as & 0xff; + } else { + ecom_value.val[0] = + ECOMMUNITY_ENCODE_AS; + ecom_value.val[2] = (as >> 8) & 0xff; + ecom_value.val[3] = as & 0xff; + } + ecom_value.val[6] = (val >> 8) & 0xff; + ecom_value.val[7] = val & 0xff; + if (rtlist == NULL) + rtlist = ecommunity_new(); + ecommunity_add_val(rtlist, &ecom_value, + false, false); + } + } + + /* + * advertise prefix via tunnel endpoint + */ + add_vnc_route( + rfd, /* rfapi descr, for export list & backref */ + bgp, /* which bgp instance */ + SAFI_MPLS_VPN, /* which SAFI */ + (pfx_ip ? pfx_ip + : &pfx_vn_buf), /* prefix being advertised */ + &prd, /* route distinguisher to use (0 for ENCAP) */ + &rfd->vn_addr, /* nexthop */ + &local_pref, + &lifetime, /* prefix lifetime -> Tunnel Encap attr */ + NULL, options_un, /* rfapi un options */ + options_vn, /* rfapi vn options */ + (rtlist ? rtlist : rfd->rt_export_list), NULL, /* med */ + label, /* label: default */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0); + + if (rtlist) + ecommunity_free(&rtlist); /* sets rtlist = NULL */ + } + + vnc_zlog_debug_verbose("%s: success", __func__); + return 0; +} + +int rfapi_query(void *handle, struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, /* may be NULL */ + struct rfapi_next_hop_entry **ppNextHopEntry) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + struct bgp *bgp = rfd->bgp; + int rc; + + assert(ppNextHopEntry); + *ppNextHopEntry = NULL; + + if (bgp && bgp->rfapi) { + bgp->rfapi->stat.count_queries++; + } + + if (!rfd->rfg) { + if (bgp && bgp->rfapi) + ++bgp->rfapi->stat.count_queries_failed; + return ESTALE; + } + + if ((rc = rfapi_query_inner(handle, target, l2o, ppNextHopEntry))) { + if (bgp && bgp->rfapi) + ++bgp->rfapi->stat.count_queries_failed; + } + return rc; +} + +int rfapi_query_done(rfapi_handle handle, struct rfapi_ip_addr *target) +{ + struct prefix p; + int rc; + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + struct bgp *bgp = rfd->bgp; + + if (!rfd->rfg) + return ESTALE; + + assert(target); + rc = rfapiRaddr2Qprefix(target, &p); + assert(!rc); + + if (!is_valid_rfd(rfd)) + return EBADF; + + /* preemptive */ + if (!bgp || !bgp->rfapi) + return ENXIO; + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + return EDEADLK; + + rfapiMonitorDel(bgp, rfd, &p); + + return 0; +} + +int rfapi_query_done_all(rfapi_handle handle, int *count) +{ + struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle; + struct bgp *bgp = rfd->bgp; + ; + int num; + + if (!rfd->rfg) + return ESTALE; + + if (!is_valid_rfd(rfd)) + return EBADF; + + /* preemptive */ + if (!bgp || !bgp->rfapi) + return ENXIO; + + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + return EDEADLK; + + num = rfapiMonitorDelHd(rfd); + + if (count) + *count = num; + + return 0; +} + +void rfapi_free_next_hop_list(struct rfapi_next_hop_entry *list) +{ + struct rfapi_next_hop_entry *nh; + struct rfapi_next_hop_entry *next; + + for (nh = list; nh; nh = next) { + next = nh->next; + rfapi_un_options_free(nh->un_options); + nh->un_options = NULL; + rfapi_vn_options_free(nh->vn_options); + nh->vn_options = NULL; + XFREE(MTYPE_RFAPI_NEXTHOP, nh); + } +} + +/* + * NULL handle => return total count across all nves + */ +uint32_t rfapi_monitor_count(void *handle) +{ + struct bgp *bgp = bgp_get_default(); + uint32_t count; + + if (handle) { + struct rfapi_descriptor *rfd = + (struct rfapi_descriptor *)handle; + count = rfd->monitor_count; + } else { + + if (!bgp || !bgp->rfapi) + return 0; + + count = bgp->rfapi->monitor_count; + } + + return count; +} + +/*********************************************************************** + * CLI/CONFIG + ***********************************************************************/ + +DEFUN (debug_rfapi_show_nves, + debug_rfapi_show_nves_cmd, + "debug rfapi-dev show nves", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "NVE Information\n") +{ + rfapiPrintMatchingDescriptors(vty, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN ( + debug_rfapi_show_nves_vn_un, + debug_rfapi_show_nves_vn_un_cmd, + "debug rfapi-dev show nves <vn|un> <A.B.C.D|X:X::X:X>", /* prefix also ok */ + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "NVE Information\n" + "Specify virtual network\n" + "Specify underlay network interface\n" + "IPv4 address\n" + "IPv6 address\n") +{ + struct prefix pfx; + + if (!str2prefix(argv[5]->arg, &pfx)) { + vty_out(vty, "Malformed address \"%s\"\n", argv[5]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "Invalid address \"%s\"\n", argv[5]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + if (argv[4]->arg[0] == 'u') { + rfapiPrintMatchingDescriptors(vty, NULL, &pfx); + } else { + rfapiPrintMatchingDescriptors(vty, &pfx, NULL); + } + return CMD_SUCCESS; +} + +/* + * Note: this function does not flush vty output, so if it is called + * with a stream pointing to a vty, the user will have to type something + * before the callback output shows up + */ +static void test_nexthops_callback( + // struct rfapi_ip_addr *target, + struct rfapi_next_hop_entry *next_hops, void *userdata) +{ + void *stream = userdata; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp(out, "Nexthops Callback, Target=("); + // rfapiPrintRfapiIpAddr(stream, target); + fp(out, ")\n"); + + rfapiPrintNhl(stream, next_hops); + + fp(out, "\n"); + + rfapi_free_next_hop_list(next_hops); +} + +DEFUN (debug_rfapi_open, + debug_rfapi_open_cmd, + "debug rfapi-dev open vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_open\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate xt addr follows\n" + "underlay network interface IPv4 address\n" + "underlay network interface IPv6 address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + uint32_t lifetime = 0; + int rc; + rfapi_handle handle; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp_get_default()), &vn, + &un, /*&uo */ NULL, &lifetime, NULL, &handle); + + vty_out(vty, "rfapi_open: status %d, handle %p, lifetime %d\n", rc, + handle, lifetime); + + rc = rfapi_set_response_cb(handle, test_nexthops_callback); + + vty_out(vty, "rfapi_set_response_cb: status %d\n", rc); + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_close_vn_un, + debug_rfapi_close_vn_un_cmd, + "debug rfapi-dev close vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_close\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate xt addr follows\n" + "underlay network interface IPv4 address\n" + "underlay network interface IPv6 address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[4]->arg, argv[6]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = rfapi_close(handle); + + vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_close_rfd, + debug_rfapi_close_rfd_cmd, + "debug rfapi-dev close rfd HANDLE", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_close\n" + "indicate handle follows\n" "rfapi handle in hexadecimal\n") +{ + rfapi_handle handle; + int rc; + char *endptr = NULL; + + handle = (rfapi_handle)(uintptr_t)(strtoull(argv[4]->arg, &endptr, 16)); + + if (*endptr != '\0' || (uintptr_t)handle == UINTPTR_MAX) { + vty_out(vty, "Invalid value: %s\n", argv[4]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + rc = rfapi_close(handle); + + vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_register_vn_un, + debug_rfapi_register_vn_un_cmd, + "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS [cost (0-255)]", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network IPv4 interface address\n" + "virtual network IPv6 interface address\n" + "indicate un addr follows\n" + "underlay network IPv4 interface address\n" + "underlay network IPv6 interface address\n" + "indicate prefix follows\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "indicate lifetime follows\n" + "lifetime\n" + "Cost (localpref = 255-cost)\n" + "0-255\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + uint32_t lifetime; + struct rfapi_ip_prefix hpfx; + int rc; + uint8_t cost = 100; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[4]->arg, argv[6]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix(argv[8]->arg, &pfx)) { + vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + rfapiQprefix2Rprefix(&pfx, &hpfx); + + if (strmatch(argv[10]->text, "infinite")) { + lifetime = RFAPI_INFINITE_LIFETIME; + } else { + lifetime = strtoul(argv[10]->arg, NULL, 10); + } + + if (argc >= 13) + cost = (uint8_t) strtoul(argv[12]->arg, NULL, 10); + hpfx.cost = cost; + + rc = rfapi_register(handle, &hpfx, lifetime, NULL, NULL, + RFAPI_REGISTER_ADD); + if (rc) { + vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc, + strerror(rc)); + } + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_register_vn_un_l2o, + debug_rfapi_register_vn_un_l2o_cmd, + "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS macaddr X:X:X:X:X:X lni (0-16777215)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network IPv4 interface address\n" + "virtual network IPv6 interface address\n" + "indicate un addr follows\n" + "underlay network IPv4 interface address\n" + "underlay network IPv6 interface address\n" + "indicate prefix follows\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "indicate lifetime follows\n" + "Seconds of lifetime\n" + "indicate MAC address follows\n" + "MAC address\n" + "indicate lni follows\n" + "lni value range\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + uint32_t lifetime; + struct rfapi_ip_prefix hpfx; + int rc; + struct rfapi_vn_option optary[10]; /* XXX must be big enough */ + struct rfapi_vn_option *opt = NULL; + int opt_next = 0; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[4]->arg, argv[6]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix(argv[8]->arg, &pfx)) { + vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + rfapiQprefix2Rprefix(&pfx, &hpfx); + + if (strmatch(argv[10]->text, "infinite")) { + lifetime = RFAPI_INFINITE_LIFETIME; + } else { + lifetime = strtoul(argv[10]->arg, NULL, 10); + } + + /* L2 option parsing START */ + memset(optary, 0, sizeof(optary)); + optary[opt_next].v.l2addr.logical_net_id = + strtoul(argv[14]->arg, NULL, 10); + if (rfapiStr2EthAddr(argv[12]->arg, + &optary[opt_next].v.l2addr.macaddr)) { + vty_out(vty, "Bad mac address \"%s\"\n", argv[12]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR; + opt = optary; + + /* L2 option parsing END */ + + /* TBD fixme */ + rc = rfapi_register(handle, &hpfx, lifetime, NULL /* &uo */, opt, + RFAPI_REGISTER_ADD); + if (rc) { + vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc, + strerror(rc)); + } + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_unregister_vn_un, + debug_rfapi_unregister_vn_un_cmd, + "debug rfapi-dev unregister vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> [kill]", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_unregister\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "underlay network interface address\n" + "prefix to remove\n" + "prefix to remove\n" + "prefix to remove\n" + "Remove without holddown\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + struct prefix pfx; + struct rfapi_ip_prefix hpfx; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[4]->arg, argv[6]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix(argv[8]->arg, &pfx)) { + vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + rfapiQprefix2Rprefix(&pfx, &hpfx); + + rfapi_register(handle, &hpfx, 0, NULL, NULL, + (argc == 10 ? + RFAPI_REGISTER_KILL : RFAPI_REGISTER_WITHDRAW)); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_query_vn_un, + debug_rfapi_query_vn_un_cmd, + "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate un addr follows\n" + "IPv4 un address\n" + "IPv6 un address\n" + "indicate target follows\n" + "target IPv4 address\n" + "target IPv6 address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + struct rfapi_next_hop_entry *pNextHopEntry; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[8]->arg, &target))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[4]->arg, argv[6]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query(handle, &target, NULL, &pNextHopEntry); + + if (rc) { + vty_out(vty, "rfapi_query failed with rc=%d (%s)\n", rc, + strerror(rc)); + } else { + /* + * print nexthop list + */ + test_nexthops_callback(/*&target, */ pNextHopEntry, + vty); /* frees nh list! */ + } + + return CMD_SUCCESS; +} + + +DEFUN (debug_rfapi_query_vn_un_l2o, + debug_rfapi_query_vn_un_l2o_cmd, + "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lni LNI target X:X:X:X:X:X", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate xt addr follows\n" + "underlay network interface IPv4 address\n" + "underlay network interface IPv6 address\n" + "logical network ID follows\n" + "logical network ID\n" + "indicate target MAC addr follows\n" + "target MAC addr\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un))) + return rc; + + vty_out(vty, "%% This command is broken.\n"); + return CMD_WARNING_CONFIG_FAILED; +} + + +DEFUN (debug_rfapi_query_done_vn_un, + debug_rfapi_query_vn_un_done_cmd, + "debug rfapi-dev query done vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query_done\n" + "rfapi_query_done\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate xt addr follows\n" + "underlay network interface IPv4 address\n" + "underlay network interface IPv6 address\n" + "indicate target follows\n" + "Target IPv4 address\n" + "Target IPv6 address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[9]->arg, &target))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[5]->arg, argv[7]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query_done(handle, &target); + + vty_out(vty, "rfapi_query_done returned %d\n", rc); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_show_import, + debug_rfapi_show_import_cmd, + "debug rfapi-dev show import", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "import\n") +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + char *s; + int first_l2 = 1; + + /* + * Show all import tables + */ + + bgp = bgp_get_default(); /* assume 1 instance for now */ + if (!bgp) { + vty_out(vty, "No BGP instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + h = bgp->rfapi; + if (!h) { + vty_out(vty, "No RFAPI instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + + for (it = h->imports; it; it = it->next) { + s = ecommunity_ecom2str(it->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, "Import Table %p, RTs: %s\n", it, s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + + rfapiShowImportTable(vty, "IP VPN", it->imported_vpn[AFI_IP], + 1); + rfapiShowImportTable(vty, "IP ENCAP", + it->imported_encap[AFI_IP], 0); + rfapiShowImportTable(vty, "IP6 VPN", it->imported_vpn[AFI_IP6], + 1); + rfapiShowImportTable(vty, "IP6 ENCAP", + it->imported_encap[AFI_IP6], 0); + } + + if (h->import_mac) { + void *cursor = NULL; + uint32_t lni; + uintptr_t lni_as_ptr; + int rc; + char buf[BUFSIZ]; + + for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr, + (void **)&it, &cursor); + !rc; + rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr, + (void **)&it, &cursor)) { + + if (it->imported_vpn[AFI_L2VPN]) { + lni = lni_as_ptr; + if (first_l2) { + vty_out(vty, + "\nLNI-based Ethernet Tables:\n"); + first_l2 = 0; + } + snprintf(buf, sizeof(buf), "L2VPN LNI=%u", lni); + rfapiShowImportTable( + vty, buf, it->imported_vpn[AFI_L2VPN], + 1); + } + } + } + + rfapiShowImportTable(vty, "CE IT - IP VPN", + h->it_ce->imported_vpn[AFI_IP], 1); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_show_import_vn_un, + debug_rfapi_show_import_vn_un_cmd, + "debug rfapi-dev show import vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>", + DEBUG_STR + DEBUG_RFAPI_STR + SHOW_STR + "import\n" + "indicate vn addr follows\n" + "virtual network interface IPv4 address\n" + "virtual network interface IPv6 address\n" + "indicate xt addr follows\n" + "underlay network interface IPv4 address\n" + "underlay network interface IPv6 address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + rfapi_handle handle; + int rc; + struct rfapi_descriptor *rfd; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un))) + return rc; + + + if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) { + vty_out(vty, "can't locate handle matching vn=%s, un=%s\n", + argv[5]->arg, argv[7]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + + rfd = (struct rfapi_descriptor *)handle; + + rfapiShowImportTable(vty, "IP VPN", + rfd->import_table->imported_vpn[AFI_IP], 1); + rfapiShowImportTable(vty, "IP ENCAP", + rfd->import_table->imported_encap[AFI_IP], 0); + rfapiShowImportTable(vty, "IP6 VPN", + rfd->import_table->imported_vpn[AFI_IP6], 1); + rfapiShowImportTable(vty, "IP6 ENCAP", + rfd->import_table->imported_encap[AFI_IP6], 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_rfapi_response_omit_self, + debug_rfapi_response_omit_self_cmd, + "debug rfapi-dev response-omit-self <on|off>", + DEBUG_STR + DEBUG_RFAPI_STR + "Omit self in RFP responses\n" + "filter out self from responses\n" "leave self in responses\n") +{ + struct bgp *bgp = bgp_get_default(); + + if (!bgp) { + vty_out(vty, "No BGP process is configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!bgp->rfapi_cfg) { + vty_out(vty, "VNC not configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (strmatch(argv[3]->text, "on")) + SET_FLAG(bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP); + else + UNSET_FLAG(bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP); + + return CMD_SUCCESS; +} + + +#ifdef RFAPI_DEBUG_SKIPLIST_CLI + +#include "lib/skiplist.h" +DEFUN (skiplist_test_cli, + skiplist_test_cli_cmd, + "skiplist test", + "skiplist command\n" + "test\n") +{ + skiplist_test(vty); + + return CMD_SUCCESS; +} + +DEFUN (skiplist_debug_cli, + skiplist_debug_cli_cmd, + "skiplist debug", + "skiplist command\n" + "debug\n") +{ + skiplist_debug(vty, NULL); + return CMD_SUCCESS; +} + +#endif /* RFAPI_DEBUG_SKIPLIST_CLI */ + +void rfapi_init(void) +{ + bgp_rfapi_cfg_init(); + vnc_debug_init(); + + install_element(ENABLE_NODE, &debug_rfapi_show_import_cmd); + install_element(ENABLE_NODE, &debug_rfapi_show_import_vn_un_cmd); + + install_element(ENABLE_NODE, &debug_rfapi_open_cmd); + install_element(ENABLE_NODE, &debug_rfapi_close_vn_un_cmd); + install_element(ENABLE_NODE, &debug_rfapi_close_rfd_cmd); + install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_cmd); + install_element(ENABLE_NODE, &debug_rfapi_unregister_vn_un_cmd); + install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_cmd); + install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_done_cmd); + install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_l2o_cmd); + + install_element(ENABLE_NODE, &debug_rfapi_response_omit_self_cmd); + + /* Need the following show commands for gpz test scripts */ + install_element(ENABLE_NODE, &debug_rfapi_show_nves_cmd); + install_element(ENABLE_NODE, &debug_rfapi_show_nves_vn_un_cmd); + install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_l2o_cmd); + +#ifdef RFAPI_DEBUG_SKIPLIST_CLI + install_element(ENABLE_NODE, &skiplist_test_cli_cmd); + install_element(ENABLE_NODE, &skiplist_debug_cli_cmd); +#endif + + rfapi_vty_init(); +} + +#ifdef DEBUG_RFAPI +static void rfapi_print_exported(struct bgp *bgp) +{ + struct bgp_dest *destn; + struct bgp_dest *dest; + struct bgp_path_info *bpi; + + if (!bgp) + return; + + for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_MPLS_VPN]); destn; + destn = bgp_route_next(destn)) { + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(destn); + if (!table) + continue; + fprintf(stderr, "%s: vpn destn=%p\n", __func__, destn); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + bpi = bgp_dest_get_bgp_path_info(dest); + + if (!bpi) + continue; + fprintf(stderr, "%s: dest=%p\n", __func__, dest); + for (; bpi; bpi = bpi->next) { + rfapiPrintBi((void *)2, bpi); /* 2 => stderr */ + } + } + } + for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_ENCAP]); destn; + destn = bgp_route_next(destn)) { + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(destn); + if (!table) + continue; + fprintf(stderr, "%s: encap destn=%p\n", __func__, destn); + for (dest = bgp_table_top(table); dest; + dest = bgp_route_next(dest)) { + bpi = bgp_dest_get_bgp_path_info(dest); + if (!bpi) + continue; + fprintf(stderr, "%s: dest=%p\n", __func__, dest); + for (; bpi; bpi = bpi->next) { + rfapiPrintBi((void *)2, bpi); /* 2 => stderr */ + } + } + } +} +#endif /* defined(DEBUG_RFAPI) */ + +/* + * Free all memory to prepare for clean exit as seen by valgrind memcheck + */ +void rfapi_delete(struct bgp *bgp) +{ + extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */ + +#if DEBUG_CLEANUP + zlog_debug("%s: bgp %p", __func__, bgp); +#endif + + /* + * This clears queries and registered routes, and closes nves + */ + if (bgp->rfapi) + rfp_clear_vnc_nve_all(); + + /* + * close any remaining descriptors + */ + struct rfapi *h = bgp->rfapi; + + if (h && h->descriptors.count) { + struct listnode *node, *nnode; + struct rfapi_descriptor *rfd; +#if DEBUG_CLEANUP + zlog_debug("%s: descriptor count %u", __func__, + h->descriptors.count); +#endif + for (ALL_LIST_ELEMENTS(&h->descriptors, node, nnode, rfd)) { +#if DEBUG_CLEANUP + zlog_debug("%s: closing rfd %p", __func__, rfd); +#endif + (void)rfapi_close(rfd); + } + } + + bgp_rfapi_cfg_destroy(bgp, bgp->rfapi_cfg); + bgp->rfapi_cfg = NULL; + bgp_rfapi_destroy(bgp, bgp->rfapi); + bgp->rfapi = NULL; +#ifdef DEBUG_RFAPI + /* + * show what's left in the BGP MPLSVPN RIB + */ + rfapi_print_exported(bgp); +#endif +} + +int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn) +{ + vnc_zlog_debug_verbose("%s: auto-assigning RD", __func__); + if (vn->addr_family != AF_INET && vn->addr_family != AF_INET6) { + vnc_zlog_debug_verbose( + "%s: can't auto-assign RD, VN addr family is not IPv4|v6", + __func__); + return EAFNOSUPPORT; + } + rd->family = AF_UNSPEC; + rd->prefixlen = 64; + rd->val[1] = RD_TYPE_IP; + if (vn->addr_family == AF_INET) { + memcpy(rd->val + 2, &vn->addr.v4.s_addr, 4); + } else { /* is v6 */ + memcpy(rd->val + 2, &vn->addr.v6.s6_addr32[3], + 4); /* low order 4 bytes */ + } + vnc_zlog_debug_verbose("%s: auto-RD is set to %pRDP", __func__, rd); + return 0; +} + +/*------------------------------------------ + * rfapi_bgp_lookup_by_rfp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * rfp_start_val value returned by rfp_startor + * NULL (=get default instance) + * + * output: + * none + * + * return value: + * bgp bgp instance pointer + * NULL = not found + * + --------------------------------------------*/ +struct bgp *rfapi_bgp_lookup_by_rfp(void *rfp_start_val) +{ + struct bgp *bgp = NULL; + struct listnode *node, *nnode; + + if (rfp_start_val == NULL) + bgp = bgp_get_default(); + else + for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) + if (bgp->rfapi != NULL + && bgp->rfapi->rfp == rfp_start_val) + return bgp; + return bgp; +} + +/*------------------------------------------ + * rfapi_get_rfp_start_val_by_bgp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * bgp bgp instance pointer + * + * output: + * none + * + * return value: + * rfp_start_val + * NULL = not found + * + --------------------------------------------*/ +void *rfapi_get_rfp_start_val_by_bgp(struct bgp *bgp) +{ + if (!bgp || !bgp->rfapi) + return NULL; + return bgp->rfapi->rfp; +} + +/*********************************************************************** + * RFP group specific configuration + ***********************************************************************/ +static void *rfapi_rfp_get_or_init_group_config_default(struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + if (rfc->default_rfp_cfg == NULL && size > 0) { + rfc->default_rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size); + vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__, + size); + } + return rfc->default_rfp_cfg; +} + +static void *rfapi_rfp_get_or_init_group_config_nve(struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + struct rfapi_nve_group_cfg *rfg = + VTY_GET_CONTEXT_SUB(rfapi_nve_group_cfg); + + /* make sure group is still in list */ + if (!rfg || !listnode_lookup(rfc->nve_groups_sequential, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current NVE group no longer exists\n"); + return NULL; + } + + if (rfg->rfp_cfg == NULL && size > 0) { + rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size); + vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__, + size); + } + return rfg->rfp_cfg; +} + +static void *rfapi_rfp_get_or_init_group_config_l2(struct rfapi_cfg *rfc, + struct vty *vty, + uint32_t size) +{ + struct rfapi_l2_group_cfg *rfg = + VTY_GET_CONTEXT_SUB(rfapi_l2_group_cfg); + + /* make sure group is still in list */ + if (!rfg || !listnode_lookup(rfc->l2_groups, rfg)) { + /* Not in list anymore */ + vty_out(vty, "Current L2 group no longer exists\n"); + return NULL; + } + if (rfg->rfp_cfg == NULL && size > 0) { + rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size); + vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__, + size); + } + return rfg->rfp_cfg; +} + +/*------------------------------------------ + * rfapi_rfp_init_group_config_ptr_vty + * + * This is used to init or return a previously init'ed group specific + * configuration pointer. Group is identified by vty context. + * NOTE: size is ignored when a previously init'ed value is returned. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * size number of bytes to allocation + * + * output: + * none + * + * return value: + * rfp_cfg_group NULL or Pointer to configuration structure +--------------------------------------------*/ +void *rfapi_rfp_init_group_config_ptr_vty(void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty, uint32_t size) +{ + struct bgp *bgp; + void *ret = NULL; + + if (rfp_start_val == NULL || vty == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (!bgp || !bgp->rfapi_cfg) + return NULL; + + switch (type) { + case RFAPI_RFP_CFG_GROUP_DEFAULT: + ret = rfapi_rfp_get_or_init_group_config_default(bgp->rfapi_cfg, + vty, size); + break; + case RFAPI_RFP_CFG_GROUP_NVE: + ret = rfapi_rfp_get_or_init_group_config_nve(bgp->rfapi_cfg, + vty, size); + break; + case RFAPI_RFP_CFG_GROUP_L2: + ret = rfapi_rfp_get_or_init_group_config_l2(bgp->rfapi_cfg, vty, + size); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d", + __func__, type); + /* should never happen */ + assert("Unknown type" == NULL); + break; + } + return ret; +} + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_vty + * + * This is used to get group specific configuration pointer. + * Group is identified by type and vty context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void *rfapi_rfp_get_group_config_ptr_vty(void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty) +{ + return rfapi_rfp_init_group_config_ptr_vty(rfp_start_val, type, vty, 0); +} + +static void * +rfapi_rfp_get_group_config_name_nve(struct rfapi_cfg *rfc, const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct rfapi_nve_group_cfg *rfg; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(rfc->nve_groups_sequential, node, rfg)) { + if (!strcmp(rfg->name, name) && /* name match */ + (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg))) + return rfg->rfp_cfg; + } + return NULL; +} + +static void * +rfapi_rfp_get_group_config_name_l2(struct rfapi_cfg *rfc, const char *name, + void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(rfc->l2_groups, node, rfg)) { + if (!strcmp(rfg->name, name) && /* name match */ + (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg))) + return rfg->rfp_cfg; + } + return NULL; +} + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_name + * + * This is used to get group specific configuration pointer. + * Group is identified by type and name context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * criteria RFAPI caller provided search criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void *rfapi_rfp_get_group_config_ptr_name( + void *rfp_start_val, rfapi_rfp_cfg_group_type type, const char *name, + void *criteria, rfp_group_config_search_cb_t *search_cb) +{ + struct bgp *bgp; + void *ret = NULL; + + if (rfp_start_val == NULL || name == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (!bgp || !bgp->rfapi_cfg) + return NULL; + + switch (type) { + case RFAPI_RFP_CFG_GROUP_DEFAULT: + ret = bgp->rfapi_cfg->default_rfp_cfg; + break; + case RFAPI_RFP_CFG_GROUP_NVE: + ret = rfapi_rfp_get_group_config_name_nve(bgp->rfapi_cfg, name, + criteria, search_cb); + break; + case RFAPI_RFP_CFG_GROUP_L2: + ret = rfapi_rfp_get_group_config_name_l2(bgp->rfapi_cfg, name, + criteria, search_cb); + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d", + __func__, type); + /* should never happen */ + assert("Unknown type" == NULL); + break; + } + return ret; +} + +/*------------------------------------------ + * rfapi_rfp_get_l2_group_config_ptr_lni + * + * This is used to get group specific configuration pointer. + * Group is identified by type and logical network identifier. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * logical_net_id group logical network identifier + * criteria RFAPI caller provided search criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +void * +rfapi_rfp_get_l2_group_config_ptr_lni(void *rfp_start_val, + uint32_t logical_net_id, void *criteria, + rfp_group_config_search_cb_t *search_cb) +{ + struct bgp *bgp; + struct rfapi_l2_group_cfg *rfg; + struct listnode *node; + + if (rfp_start_val == NULL) + return NULL; + + bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val); + if (!bgp || !bgp->rfapi_cfg) + return NULL; + + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->l2_groups, node, rfg)) { + if (rfg->logical_net_id == logical_net_id + && (search_cb == NULL + || !search_cb(criteria, rfg->rfp_cfg))) { + if (rfg->rfp_cfg == NULL) + vnc_zlog_debug_verbose( + "%s: returning rfp group config for lni=0", + __func__); + return rfg->rfp_cfg; + } + } + return NULL; +} diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h new file mode 100644 index 0000000..3f61d5b --- /dev/null +++ b/bgpd/rfapi/rfapi.h @@ -0,0 +1,900 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_H +#define _QUAGGA_BGP_RFAPI_H + +#ifdef ENABLE_BGP_VNC + +#include <stdint.h> +#include <netinet/in.h> +#include "lib/zebra.h" +#include "lib/vty.h" +#include "lib/prefix.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_encap_types.h" + +/* probably ought to have a field-specific define in config.h */ +#ifndef s6_addr32 /* for solaris/bsd */ +# define s6_addr32 __u6_addr.__u6_addr32 +#endif + +#define RFAPI_V4_ADDR 0x04 +#define RFAPI_V6_ADDR 0x06 +#define RFAPI_SHOW_STR "VNC information\n" + +struct rfapi_ip_addr { + uint8_t addr_family; /* AF_INET | AF_INET6 */ + union { + struct in_addr v4; /* in network order */ + struct in6_addr v6; /* in network order */ + } addr; +}; + +struct rfapi_ip_prefix { + uint8_t length; + uint8_t cost; /* bgp local pref = 255 - cost */ + struct rfapi_ip_addr prefix; +}; + +struct rfapi_nexthop { + struct prefix addr; + uint8_t cost; +}; + +struct rfapi_next_hop_entry { + struct rfapi_next_hop_entry *next; + struct rfapi_ip_prefix prefix; + uint32_t lifetime; + struct rfapi_ip_addr un_address; + struct rfapi_ip_addr vn_address; + struct rfapi_vn_option *vn_options; + struct rfapi_un_option *un_options; +}; + +#define RFAPI_REMOVE_RESPONSE_LIFETIME 0 +#define RFAPI_INFINITE_LIFETIME 0xFFFFFFFF + +struct rfapi_l2address_option { + struct ethaddr macaddr; /* use 0 to assign label to IP prefix */ + uint32_t label; /* 20bit label in low bits, no TC, S, or TTL */ + uint32_t logical_net_id; /* ~= EVPN Ethernet Segment Id, + must not be zero for mac regis. */ + uint8_t local_nve_id; + uint16_t tag_id; /* EVPN Ethernet Tag ID, 0 = none */ +}; + +typedef enum { + RFAPI_UN_OPTION_TYPE_PROVISIONAL, /* internal use only */ + RFAPI_UN_OPTION_TYPE_TUNNELTYPE, +} rfapi_un_option_type; + +struct rfapi_tunneltype_option { + bgp_encap_types type; + union { + struct bgp_encap_type_reserved reserved; + struct bgp_encap_type_l2tpv3_over_ip l2tpv3_ip; + struct bgp_encap_type_gre gre; + struct bgp_encap_type_transmit_tunnel_endpoint + transmit_tunnel_endpoint; + struct bgp_encap_type_ipsec_in_tunnel_mode ipsec_tunnel; + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode + ip_ipsec; + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode + mpls_ipsec; + struct bgp_encap_type_ip_in_ip ip_ip; + struct bgp_encap_type_vxlan vxlan; + struct bgp_encap_type_nvgre nvgre; + struct bgp_encap_type_mpls mpls; + struct bgp_encap_type_mpls_in_gre mpls_gre; + struct bgp_encap_type_vxlan_gpe vxlan_gpe; + struct bgp_encap_type_mpls_in_udp mpls_udp; + struct bgp_encap_type_pbb pbb; + } bgpinfo; +}; + +struct rfapi_un_option { + struct rfapi_un_option *next; + rfapi_un_option_type type; + union { + struct rfapi_tunneltype_option tunnel; + } v; +}; + +typedef enum { + RFAPI_VN_OPTION_TYPE_L2ADDR = + 3, /* Layer 2 address, 3 for legacy compatibility */ + RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP, /* for static routes */ + RFAPI_VN_OPTION_TYPE_INTERNAL_RD, /* internal use only */ +} rfapi_vn_option_type; + +struct rfapi_vn_option { + struct rfapi_vn_option *next; + + rfapi_vn_option_type type; + + union { + struct rfapi_l2address_option l2addr; + + /* + * If this option is present, the next hop is local to the + * client NVE (i.e., not via a tunnel). + */ + struct rfapi_nexthop local_nexthop; + + /* + * For rfapi internal use only + */ + struct prefix_rd internal_rd; + } v; +}; + +struct rfapi_l2address_option_match { + struct rfapi_l2address_option o; + uint32_t flags; + +#define RFAPI_L2O_MACADDR 0x00000001 +#define RFAPI_L2O_LABEL 0x00000002 +#define RFAPI_L2O_LNI 0x00000004 +#define RFAPI_L2O_LHI 0x00000008 +}; + +#define VNC_CONFIG_STR "VNC/RFP related configuration\n" + +typedef void *rfapi_handle; + +/*********************************************************************** + * RFP Callbacks + ***********************************************************************/ +/*------------------------------------------ + * rfapi_response_cb_t (callback typedef) + * + * Callbacks of this type are used to provide asynchronous + * route updates from RFAPI to the RFP client. + * + * response_cb + * called to notify the rfp client that a next hop list + * that has previously been provided in response to an + * rfapi_query call has been updated. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * By default, the routes an NVE receives via this callback include + * its own routes (that it has registered). However, these may be + * filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP + * flag is set. + * + * local_cb + * called to notify the rfp client that a local route + * has been added or deleted. Deleted routes are indicated + * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME. + * + * input: + * next_hops a list of possible next hops. + * This is a linked list allocated within the + * rfapi. The response_cb callback function is responsible + * for freeing this memory via rfapi_free_next_hop_list() + * in order to avoid memory leaks. + * + * userdata value (cookie) originally specified in call to + * rfapi_open() + * + *------------------------------------------*/ +typedef void(rfapi_response_cb_t)(struct rfapi_next_hop_entry *next_hops, + void *userdata); + +/*------------------------------------------ + * rfapi_nve_close_cb_t (callback typedef) + * + * Callbacks of this type are used to provide asynchronous + * notification that an rfapi_handle was invalidated + * + * input: + * pHandle Firmerly valid rfapi_handle returned to + * client via rfapi_open(). + * + * reason EIDRM handle administratively closed (clear nve ...) + * ESTALE handle invalidated by configuration change + * + *------------------------------------------*/ +typedef void(rfapi_nve_close_cb_t)(rfapi_handle pHandle, int reason); + +/*------------------------------------------ + * rfp_cfg_write_cb_t (callback typedef) + * + * This callback is used to generate output for any config parameters + * that may supported by RFP via RFP defined vty commands at the bgp + * level. See loglevel as an example. + * + * input: + * vty -- quagga vty context + * rfp_start_val -- value returned by rfp_start + * + * output: + * to vty, rfp related configuration + * + * return value: + * lines written +--------------------------------------------*/ +typedef int(rfp_cfg_write_cb_t)(struct vty *vty, void *rfp_start_val); + +/*------------------------------------------ + * rfp_cfg_group_write_cb_t (callback typedef) + * + * This callback is used to generate output for any config parameters + * that may supported by RFP via RFP defined vty commands at the + * L2 or NVE level. See loglevel as an example. + * + * input: + * vty quagga vty context + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * rfp_cfg_group Pointer to configuration structure + * + * output: + * to vty, rfp related configuration + * + * return value: + * lines written +--------------------------------------------*/ +typedef enum { + RFAPI_RFP_CFG_GROUP_DEFAULT, + RFAPI_RFP_CFG_GROUP_NVE, + RFAPI_RFP_CFG_GROUP_L2 +} rfapi_rfp_cfg_group_type; + +typedef int(rfp_cfg_group_write_cb_t)(struct vty *vty, void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + const char *name, void *rfp_cfg_group); + +/*********************************************************************** + * Configuration related defines and structures + ***********************************************************************/ + +struct rfapi_rfp_cb_methods { + rfp_cfg_write_cb_t *cfg_cb; /* show top level config */ + rfp_cfg_group_write_cb_t *cfg_group_cb; /* show group level config */ + rfapi_response_cb_t *response_cb; /* unsolicited responses */ + rfapi_response_cb_t *local_cb; /* local route add/delete */ + rfapi_nve_close_cb_t *close_cb; /* handle closed */ +}; + +/* + * If a route with infinite lifetime is withdrawn, this is + * how long (in seconds) to wait before expiring it (because + * RFAPI_LIFETIME_MULTIPLIER_PCT * infinity is too long to wait) + */ +#define RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY (60*120) + +/* + * the factor that should be applied to a prefix's <lifetime> value + * before using it to expire a withdrawn prefix, expressed as a percent. + * Thus, a value of 100 means to use the exact value of <lifetime>, + * a value of 200 means to use twice the value of <lifetime>, etc. + */ +#define RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR 150 + +/* + * This is used by rfapi to determine if RFP is using/supports + * a partial (i.e., cache) or full table download approach for + * mapping information. When full table download approach is + * used all information is passed to RFP after an initial + * rfapi_query. When partial table download is used, only + * information matching a query is passed. + */ +typedef enum { + RFAPI_RFP_DOWNLOAD_PARTIAL = 0, + RFAPI_RFP_DOWNLOAD_FULL +} rfapi_rfp_download_type; + +#define RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL 1 + +struct rfapi_rfp_cfg { + /* partial or full table download */ + rfapi_rfp_download_type download_type; /* default=partial */ + /* + * When full-table-download is enabled, this is the minimum + * number of seconds between times a non-queried prefix will + * be updated to a particular NVE. + * default: RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL + */ + uint32_t ftd_advertisement_interval; + /* + * percentage of registration lifetime to continue to use information + * post soft-state refresh timeout + default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR + */ + uint32_t holddown_factor; + /* Control generation of updated RFP responses */ + uint8_t use_updated_response; /* default=0/no */ + /* when use_updated_response, also generate remove responses */ + uint8_t use_removes; /* default=0/no */ +}; + +/*********************************************************************** + * Process related functions -- MUST be provided by the RFAPI user <<=== + ***********************************************************************/ + +/*------------------------------------------ + * rfp_start + * + * This function will start the RFP code + * + * input: + * master quagga thread_master to tie into bgpd threads + * + * output: + * cfgp Pointer to rfapi_rfp_cfg (null = use defaults), + * copied by caller, updated via rfp_set_configuration + * cbmp Pointer to rfapi_rfp_cb_methods, may be null + * copied by caller, updated via rfapi_rfp_set_cb_methods + * return value: + * rfp_start_val rfp returned value passed on rfp_stop and other rfapi calls +--------------------------------------------*/ +extern void *rfp_start(struct event_loop *master, struct rfapi_rfp_cfg **cfgp, + struct rfapi_rfp_cb_methods **cbmp); + +/*------------------------------------------ + * rfp_stop + * + * This function is called on shutdown to trigger RFP cleanup + * + * input: + * rfp_start_val + * + * output: + * none + * + * return value: +--------------------------------------------*/ +extern void rfp_stop(void *rfp_start_val); + +/*********************************************************************** + * RFP processing behavior configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_set_configuration + * + * This is used to change rfapi's processing behavior based on + * RFP requirements. + * + * input: + * rfp_start_val value returned by rfp_start + * rfapi_rfp_cfg Pointer to configuration structure + * + * output: + * none + * + * return value: + * 0 Success + * ENXIO Unabled to locate configured BGP/VNC +--------------------------------------------*/ +extern int rfapi_rfp_set_configuration(void *rfp_start_val, + struct rfapi_rfp_cfg *rfp_cfg); + +/*------------------------------------------ + * rfapi_rfp_set_cb_methods + * + * Change registered callback functions for asynchronous notifications + * from RFAPI to the RFP client. + * + * input: + * rfp_start_val value by rfp_start + * methods Pointer to struct rfapi_rfp_cb_methods containing + * pointers to callback methods as described above + * + * return value: + * 0 Success + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +extern int rfapi_rfp_set_cb_methods(void *rfp_start_val, + struct rfapi_rfp_cb_methods *methods); + +/*********************************************************************** + * RFP group specific configuration + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_rfp_init_group_config_ptr_vty + * + * This is used to init or return a previously init'ed group specific + * configuration pointer. Group is identified by vty context. + * NOTE: size is ignored when a previously init'ed value is returned. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * size number of bytes to allocation + * + * output: + * none + * + * return value: + * rfp_cfg_group NULL or Pointer to configuration structure +--------------------------------------------*/ +extern void *rfapi_rfp_init_group_config_ptr_vty(void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty, + uint32_t size); + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_vty + * + * This is used to get group specific configuration pointer. + * Group is identified by type and vty context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * vty quagga vty context + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void *rfapi_rfp_get_group_config_ptr_vty(void *rfp_start_val, + rfapi_rfp_cfg_group_type type, + struct vty *vty); + +/*------------------------------------------ + * rfp_group_config_search_cb_t (callback typedef) + * + * This callback is used to called from within a + * rfapi_rfp_get_group_config_ptr to check if the rfp_cfg_group + * matches the search criteria + * + * input: + * criteria RFAPI caller provided serach criteria + * rfp_cfg_group Pointer to configuration structure | NULL + * + * output: + * + * return value: + * 0 Match/Success + * ENOENT No matching +--------------------------------------------*/ +typedef int(rfp_group_config_search_cb_t)(void *criteria, void *rfp_cfg_group); + +/*------------------------------------------ + * rfapi_rfp_get_group_config_ptr_name + * + * This is used to get group specific configuration pointer. + * Group is identified by type and name context. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * type group type + * name group name + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void *rfapi_rfp_get_group_config_ptr_name( + void *rfp_start_val, rfapi_rfp_cfg_group_type type, const char *name, + void *criteria, rfp_group_config_search_cb_t *search_cb); + +/*------------------------------------------ + * rfapi_rfp_get_l2_group_config_ptr_lni + * + * This is used to get group specific configuration pointer. + * Group is identified by type and logical network identifier. + * RFAPI frees rfp_cfg_group when group is deleted during reconfig, + * bgp restart or shutdown. + * + * input: + * rfp_start_val value returned by rfp_start + * logical_net_id group logical network identifier + * criteria RFAPI caller provided serach criteria + * search_cb optional rfp_group_config_search_cb_t + * + * output: + * none + * + * return value: + * rfp_cfg_group Pointer to configuration structure +--------------------------------------------*/ +extern void * +rfapi_rfp_get_l2_group_config_ptr_lni(void *rfp_start_val, + uint32_t logical_net_id, void *criteria, + rfp_group_config_search_cb_t *search_cb); + +/*********************************************************************** + * NVE Sessions + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_open + * + * This function initializes a NVE record and associates it with + * the specified VN and underlay network addresses + * + * input: + * rfp_start_val value returned by rfp_start + * vn NVE virtual network address + * + * un NVE underlay network address + * + * default_options Default options to use on registrations. + * For now only tunnel type is supported. + * May be overridden per-prefix in rfapi_register(). + * Caller owns (rfapi_open() does not free) + * + * response_cb Pointer to next hop list update callback function or + * NULL when no callbacks are desired. + * + * userdata Passed to subsequent response_cb invocations. + * + * output: + * response_lifetime The length of time that responses sent to this + * NVE are valid. + * + * pHandle pointer to location to store rfapi handle. The + * handle must be passed on subsequent rfapi_ calls. + * + * + * return value: + * 0 Success + * EEXIST NVE with this {vn,un} already open + * ENOENT No matching nve group config + * ENOMSG Matched nve group config was incomplete + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD, + * but underlay network address is not IPv4 + * EDEADLK Called from within a callback procedure + *------------------------------------------*/ +extern int rfapi_open(void *rfp_start_val, struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un, + struct rfapi_un_option *default_options, + uint32_t *response_lifetime, void *userdata, + rfapi_handle *pHandle); + + +/*------------------------------------------ + * rfapi_close + * + * Shut down NVE session and release associated data. Calling + * from within a rfapi callback procedure is permitted (the close + * will be completed asynchronously after the callback finishes). + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + *------------------------------------------*/ +extern int rfapi_close(rfapi_handle rfd); + +/*------------------------------------------ + * rfapi_check + * + * Test rfapi descriptor + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * + * return value: + * 0 Success: handle is valid and usable + * EINVAL null argument + * ESTALE formerly valid handle invalidated by config, needs close + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * EAFNOSUPPORT Internal addressing error + *------------------------------------------*/ +extern int rfapi_check(rfapi_handle rfd); + +/*********************************************************************** + * NVE Routes + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_query + * + * This function queries the RIB for a + * particular route. Note that this call may result in subsequent + * callbacks to response_cb. Response callbacks can be cancelled + * by calling rfapi_query_done. A duplicate query using the same target + * will result in only one callback per change in next_hops. (i.e., + * cancel/replace the prior query results.) + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * target: the destination address + * l2o ptr to L2 Options struct, NULL if not present in query + * + * output: + * ppNextHopEntry pointer to a location to store a pointer + * to the returned list of nexthops. It is the + * caller's responsibility to free this list + * via rfapi_free_next_hop_list(). + * + * + * return value: + * 0 Success + * EBADF invalid handle + * ENOENT no valid route + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int rfapi_query(rfapi_handle rfd, struct rfapi_ip_addr *target, + struct rfapi_l2address_option *l2o, + struct rfapi_next_hop_entry **ppNextHopEntry); + +/*------------------------------------------ + * rfapi_query_done + * + * Notifies the rfapi that the user is no longer interested + * in the specified target. + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * target: the destination address + * + * output: + * + * return value: + * 0 Success + * EBADF invalid handle + * ENOENT no match found for target + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int rfapi_query_done(rfapi_handle rfd, struct rfapi_ip_addr *target); + +/*------------------------------------------ + * rfapi_query_done_all + * + * Notifies the rfapi that the user is no longer interested + * in any target. + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * + * output: + * count: number of queries cleared + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure +--------------------------------------------*/ +extern int rfapi_query_done_all(rfapi_handle rfd, int *count); + +/*------------------------------------------ + * rfapi_register + * + * Requests that reachability to the indicated prefix via this NVE + * be advertised by BGP. If <unregister> is non-zero, then the previously- + * advertised prefix should be withdrawn. + * + * (This function should NOT be called if the rfapi_open() function + * returns NULL) + * + * input: + * rfd: rfapi descriptor returned by rfapi_open + * prefix: A prefix to be registered or deregistered + * lifetime Prefix lifetime in seconds, host byte order + * options_un underlay netowrk options, may include tunnel-type + * Caller owns (rfapi_register() does not free). + * options_vn virtual network options, may include layer 2 address + * option and local-nexthop option + * Caller owns (rfapi_register() does not free). + * + * action: RFAPI_REGISTER_ADD add the route + * RFAPI_REGISTER_WITHDRAW withdraw route + * RFAPI_REGISTER_KILL withdraw without holddown + * + * return value: + * 0 Success + * EBADF invalid handle + * ENXIO BGP or VNC not configured + * ESTALE descriptor is no longer usable; should be closed + * EDEADLK Called from within a callback procedure + --------------------------------------------*/ + +typedef enum { + RFAPI_REGISTER_ADD, + RFAPI_REGISTER_WITHDRAW, + RFAPI_REGISTER_KILL +} rfapi_register_action; + +extern int rfapi_register(rfapi_handle rfd, struct rfapi_ip_prefix *prefix, + uint32_t lifetime, struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + rfapi_register_action action); + +/*********************************************************************** + * Helper / Utility functions + ***********************************************************************/ + +/*------------------------------------------ + * rfapi_get_vn_addr + * + * Get the virtual network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * vn NVE virtual network address + *------------------------------------------*/ +extern struct rfapi_ip_addr *rfapi_get_vn_addr(void *); + +/*------------------------------------------ + * rfapi_get_un_addr + * + * Get the underlay network address used by an NVE based on it's RFD + * + * input: + * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * un NVE underlay network address + *------------------------------------------*/ +extern struct rfapi_ip_addr *rfapi_get_un_addr(void *); + +/*------------------------------------------ + * rfapi_error_str + * + * Returns a string describing the rfapi error code. + * + * input: + * + * code Error code returned by rfapi function + * + * returns: + * + * const char * String + *------------------------------------------*/ +extern const char *rfapi_error_str(int code); + +/*------------------------------------------ + * rfapi_get_rfp_start_val + * + * Returns value passed to rfapi on rfp_start + * + * input: + * void * bgp structure + * + * returns: + * void * + *------------------------------------------*/ +extern void *rfapi_get_rfp_start_val(void *bgpv); + +/*------------------------------------------ + * rfapi_compare_rfds + * + * Compare two generic rfapi descriptors. + * + * input: + * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * 0 Mismatch + * 1 Match + *------------------------------------------*/ +extern int rfapi_compare_rfds(void *rfd1, void *rfd2); + +/*------------------------------------------ + * rfapi_free_next_hop_list + * + * Frees a next_hop_list returned by a rfapi_query invocation + * + * input: + * list: a pointer to a response list (as a + * struct rfapi_next_hop_entry) to free. + * + * output: + * + * return value: None + --------------------------------------------*/ +extern void rfapi_free_next_hop_list(struct rfapi_next_hop_entry *list); + +/*------------------------------------------ + * rfapi_get_response_lifetime_default + * + * Returns the default lifetime for a response. + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * input: + * None + * + * output: + * + * return value: The bgp instance default lifetime for a response. + --------------------------------------------*/ +extern int rfapi_get_response_lifetime_default(void *rfp_start_val); + +/*------------------------------------------ + * rfapi_is_vnc_configured + * + * Returns if VNC is configured + * + * input: + * rfp_start_val value returned by rfp_start or + * NULL (=use default instance) + * + * output: + * + * return value: If VNC is configured for the bgpd instance + * 0 Success + * ENXIO VNC not configured + --------------------------------------------*/ +extern int rfapi_is_vnc_configured(void *rfp_start_val); + +/*------------------------------------------ + * rfapi_bgp_lookup_by_rfp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * rfp_start_val value returned by rfp_startor + * NULL (=get default instance) + * + * output: + * none + * + * return value: + * bgp bgp instance pointer + * NULL = not found + * + --------------------------------------------*/ +extern struct bgp *rfapi_bgp_lookup_by_rfp(void *rfp_start_val); + +/*------------------------------------------ + * rfapi_get_rfp_start_val_by_bgp + * + * Find bgp instance pointer based on value returned by rfp_start + * + * input: + * bgp bgp instance pointer + * + * output: + * none + * + * return value: + * rfp_start_val + * NULL = not found + * + --------------------------------------------*/ +extern void *rfapi_get_rfp_start_val_by_bgp(struct bgp *bgp); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_H */ diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c new file mode 100644 index 0000000..fe344a5 --- /dev/null +++ b/bgpd/rfapi/rfapi_ap.c @@ -0,0 +1,542 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" + +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" + +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" + +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" + +#include "bgpd/rfapi/rfapi_ap.h" +#include "bgpd/rfapi/vnc_debug.h" + +/* + * Per-NVE Advertised prefixes + * + * We maintain a list of prefixes advertised by each NVE. + * There are two indices: by prefix and by lifetime. + * + * BY-PREFIX skiplist + * + * key: ptr to struct prefix (when storing, point to prefix that + * is part of rfapi_adb). + * + * value: ptr to struct rfapi_adb + * + * BY-LIFETIME skiplist + * + * key: ptr to struct rfapi_adb + * value: ptr to struct rfapi_adb + * + */ + +/* + * Skiplist sort function that sorts first according to lifetime + * and then according to adb pointer value. The adb pointer + * is used to spread out the sort for adbs with the same lifetime + * and thereby make the skip list operations more efficient. + */ +static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2) +{ + const struct rfapi_adb *a1 = adb1; + const struct rfapi_adb *a2 = adb2; + + if (a1->lifetime < a2->lifetime) + return -1; + if (a1->lifetime > a2->lifetime) + return 1; + + if (a1 < a2) + return -1; + if (a1 > a2) + return 1; + + return 0; +} + +void rfapiApInit(struct rfapi_advertised_prefixes *ap) +{ + ap->ipN_by_prefix = skiplist_new(0, rfapi_rib_key_cmp, NULL); + ap->ip0_by_ether = skiplist_new(0, rfapi_rib_key_cmp, NULL); + ap->by_lifetime = skiplist_new(0, sl_adb_lifetime_cmp, NULL); +} + +void rfapiApRelease(struct rfapi_advertised_prefixes *ap) +{ + struct rfapi_adb *adb; + + /* Free ADBs and lifetime items */ + while (0 == skiplist_first(ap->by_lifetime, NULL, (void **)&adb)) { + rfapiAdbFree(adb); + skiplist_delete_first(ap->by_lifetime); + } + + while (0 == skiplist_delete_first(ap->ipN_by_prefix)) + ; + while (0 == skiplist_delete_first(ap->ip0_by_ether)) + ; + + /* Free lists */ + skiplist_free(ap->ipN_by_prefix); + skiplist_free(ap->ip0_by_ether); + skiplist_free(ap->by_lifetime); + + ap->ipN_by_prefix = NULL; + ap->ip0_by_ether = NULL; + ap->by_lifetime = NULL; +} + +int rfapiApCount(struct rfapi_descriptor *rfd) +{ + if (!rfd->advertised.by_lifetime) + return 0; + + return skiplist_count(rfd->advertised.by_lifetime); +} + +int rfapiApCountAll(struct bgp *bgp) +{ + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + int total = 0; + + h = bgp->rfapi; + if (h) { + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + total += rfapiApCount(rfd); + } + } + return total; +} + + +void rfapiApReadvertiseAll(struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct rfapi_adb *adb; + void *cursor = NULL; + int rc; + + for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL, + (void **)&adb, &cursor); + rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL, + (void **)&adb, &cursor)) { + + struct prefix_rd prd; + uint32_t local_pref = rfp_cost_to_localpref(adb->cost); + + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * TBD this is not quite right. When pfx_ip is 0/32 or 0/128, + * we need to substitute the VN address as the prefix + */ + add_vnc_route(rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip, + &prd, /* RD to use (0 for ENCAP) */ + &rfd->vn_addr, /* nexthop */ + &local_pref, &adb->lifetime, NULL, + NULL, /* struct rfapi_un_option */ + NULL, /* struct rfapi_vn_option */ + rfd->rt_export_list, NULL, /* med */ + NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0); + } +} + +void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct rfapi_adb *adb; + void *cursor; + int rc; + + + cursor = NULL; + for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL, + (void **)&adb, &cursor); + rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL, + (void **)&adb, &cursor)) { + + struct prefix pfx_vn_buf; + struct prefix *pfx_ip; + + if (!(RFAPI_0_PREFIX(&adb->u.s.prefix_ip) + && RFAPI_HOST_PREFIX(&adb->u.s.prefix_ip))) { + + pfx_ip = &adb->u.s.prefix_ip; + + } else { + + pfx_ip = NULL; + + /* + * 0/32 or 0/128 => mac advertisement + */ + if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) { + /* + * Bad: it means we can't delete the route + */ + vnc_zlog_debug_verbose( + "%s: BAD: handle has bad vn_addr: skipping", + __func__); + continue; + } + } + + del_vnc_route(rfd, rfd->peer, bgp, SAFI_MPLS_VPN, + pfx_ip ? pfx_ip : &pfx_vn_buf, + &adb->u.s.prd, /* RD to use (0 for ENCAP) */ + ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); + } +} + +/* + * returns nonzero if tunnel readvertisement is needed, 0 otherwise + */ +static int rfapiApAdjustLifetimeStats( + struct rfapi_descriptor *rfd, + uint32_t *old_lifetime, /* set if removing/replacing */ + uint32_t *new_lifetime) /* set if replacing/adding */ +{ + int advertise = 0; + int find_max = 0; + int find_min = 0; + + vnc_zlog_debug_verbose("%s: rfd=%p, pOldLife=%p, pNewLife=%p", __func__, + rfd, old_lifetime, new_lifetime); + if (old_lifetime) + vnc_zlog_debug_verbose("%s: OldLife=%d", __func__, + *old_lifetime); + if (new_lifetime) + vnc_zlog_debug_verbose("%s: NewLife=%d", __func__, + *new_lifetime); + + if (new_lifetime) { + /* + * Adding new lifetime + */ + if (old_lifetime) { + /* + * replacing existing lifetime + */ + + + /* old and new are same */ + if (*old_lifetime == *new_lifetime) + return 0; + + if (*old_lifetime == rfd->min_prefix_lifetime) { + find_min = 1; + } + if (*old_lifetime == rfd->max_prefix_lifetime) { + find_max = 1; + } + + /* no need to search if new value is at or equals + * min|max */ + if (*new_lifetime <= rfd->min_prefix_lifetime) { + rfd->min_prefix_lifetime = *new_lifetime; + find_min = 0; + } + if (*new_lifetime >= rfd->max_prefix_lifetime) { + rfd->max_prefix_lifetime = *new_lifetime; + advertise = 1; + find_max = 0; + } + + } else { + /* + * Just adding new lifetime + */ + if (*new_lifetime < rfd->min_prefix_lifetime) { + rfd->min_prefix_lifetime = *new_lifetime; + } + if (*new_lifetime > rfd->max_prefix_lifetime) { + advertise = 1; + rfd->max_prefix_lifetime = *new_lifetime; + } + } + } else { + /* + * Deleting + */ + + /* + * See if the max prefix lifetime for this NVE has decreased. + * The easy optimization: track min & max; walk the table only + * if they are different. + * The general optimization: index the advertised_prefixes + * table by lifetime. + * + * Note: for a given nve_descriptor, only one of the + * advertised_prefixes[] tables will be used: viz., the + * address family that matches the VN address. + * + */ + if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) { + + /* + * Common case: all lifetimes are the same. Only + * thing we need to do here is check if there are + * no exported routes left. In that case, reinitialize + * the max and min values. + */ + if (!rfapiApCount(rfd)) { + rfd->max_prefix_lifetime = 0; + rfd->min_prefix_lifetime = UINT32_MAX; + } + + + } else { + if (old_lifetime) { + if (*old_lifetime == rfd->min_prefix_lifetime) { + find_min = 1; + } + if (*old_lifetime == rfd->max_prefix_lifetime) { + find_max = 1; + } + } + } + } + + if (find_min || find_max) { + uint32_t min = UINT32_MAX; + uint32_t max = 0; + + struct rfapi_adb *adb_min; + struct rfapi_adb *adb_max; + + if (!skiplist_first(rfd->advertised.by_lifetime, + (void **)&adb_min, NULL) + && !skiplist_last(rfd->advertised.by_lifetime, + (void **)&adb_max, NULL)) { + + /* + * This should always work + */ + min = adb_min->lifetime; + max = adb_max->lifetime; + + } else { + + void *cursor; + struct rfapi_rib_key rk; + struct rfapi_adb *adb; + int rc; + + vnc_zlog_debug_verbose( + "%s: walking to find new min/max", __func__); + + cursor = NULL; + for (rc = skiplist_next(rfd->advertised.ipN_by_prefix, + (void **)&rk, (void **)&adb, + &cursor); + !rc; + rc = skiplist_next(rfd->advertised.ipN_by_prefix, + (void **)&rk, (void **)&adb, + &cursor)) { + + uint32_t lt = adb->lifetime; + + if (lt > max) + max = lt; + if (lt < min) + min = lt; + } + cursor = NULL; + for (rc = skiplist_next(rfd->advertised.ip0_by_ether, + (void **)&rk, (void **)&adb, + &cursor); + !rc; + rc = skiplist_next(rfd->advertised.ip0_by_ether, + (void **)&rk, (void **)&adb, + &cursor)) { + + uint32_t lt = adb->lifetime; + + if (lt > max) + max = lt; + if (lt < min) + min = lt; + } + } + + /* + * trigger tunnel route update + * but only if we found a VPN route and it had + * a lifetime greater than 0 + */ + if (max && rfd->max_prefix_lifetime != max) + advertise = 1; + rfd->max_prefix_lifetime = max; + rfd->min_prefix_lifetime = min; + } + + vnc_zlog_debug_verbose("%s: returning advertise=%d, min=%d, max=%d", + __func__, advertise, rfd->min_prefix_lifetime, + rfd->max_prefix_lifetime); + + return (advertise != 0); +} + +/* + * Return Value + * + * 0 No need to advertise tunnel route + * non-0 advertise tunnel route + */ +int rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, struct prefix *pfx_eth, + struct prefix_rd *prd, uint32_t lifetime, uint8_t cost, + struct rfapi_l2address_option *l2o) /* other options TBD */ +{ + int rc; + struct rfapi_adb *adb; + uint32_t old_lifetime = 0; + int use_ip0 = 0; + struct rfapi_rib_key rk; + + rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); + if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) { + use_ip0 = 1; + assert(pfx_eth); + rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk, + (void **)&adb); + + } else { + + /* find prefix in advertised prefixes list */ + rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk, + (void **)&adb); + } + + + if (rc) { + /* Not found */ + adb = XCALLOC(MTYPE_RFAPI_ADB, sizeof(struct rfapi_adb)); + adb->lifetime = lifetime; + adb->u.key = rk; + + if (use_ip0) { + assert(pfx_eth); + skiplist_insert(rfd->advertised.ip0_by_ether, + &adb->u.key, adb); + } else { + skiplist_insert(rfd->advertised.ipN_by_prefix, + &adb->u.key, adb); + } + + skiplist_insert(rfd->advertised.by_lifetime, adb, adb); + } else { + old_lifetime = adb->lifetime; + if (old_lifetime != lifetime) { + assert(!skiplist_delete(rfd->advertised.by_lifetime, + adb, NULL)); + adb->lifetime = lifetime; + assert(!skiplist_insert(rfd->advertised.by_lifetime, + adb, adb)); + } + } + adb->cost = cost; + if (l2o) + adb->l2o = *l2o; + else + memset(&adb->l2o, 0, sizeof(struct rfapi_l2address_option)); + + if (rfapiApAdjustLifetimeStats(rfd, (rc ? NULL : &old_lifetime), + &lifetime)) + return 1; + + return 0; +} + +/* + * After this function returns successfully, caller should call + * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce() + */ +int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, struct prefix *pfx_eth, + struct prefix_rd *prd, int *advertise_tunnel) /* out */ +{ + int rc; + struct rfapi_adb *adb; + uint32_t old_lifetime; + int use_ip0 = 0; + struct rfapi_rib_key rk; + + if (advertise_tunnel) + *advertise_tunnel = 0; + + rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk); + /* find prefix in advertised prefixes list */ + if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) { + use_ip0 = 1; + assert(pfx_eth); + + rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk, + (void **)&adb); + + } else { + + /* find prefix in advertised prefixes list */ + rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk, + (void **)&adb); + } + + if (rc) { + return ENOENT; + } + + old_lifetime = adb->lifetime; + + if (use_ip0) { + rc = skiplist_delete(rfd->advertised.ip0_by_ether, &rk, NULL); + } else { + rc = skiplist_delete(rfd->advertised.ipN_by_prefix, &rk, NULL); + } + assert(!rc); + + rc = skiplist_delete(rfd->advertised.by_lifetime, adb, NULL); + assert(!rc); + + rfapiAdbFree(adb); + + if (rfapiApAdjustLifetimeStats(rfd, &old_lifetime, NULL)) { + if (advertise_tunnel) + *advertise_tunnel = 1; + } + + return 0; +} diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h new file mode 100644 index 0000000..7698fba --- /dev/null +++ b/bgpd/rfapi/rfapi_ap.h @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ +#ifndef _QUAGGA_BGP_RFAPI_AP_H +#define _QUAGGA_BGP_RFAPI_AP_H + +/* TBD delete some of these #includes */ + +#include <errno.h> + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" +#include "lib/stream.h" + +#include "bgpd/bgpd.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_advertise.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" + + +extern void rfapiApInit(struct rfapi_advertised_prefixes *ap); + +extern void rfapiApRelease(struct rfapi_advertised_prefixes *ap); + +extern int rfapiApCount(struct rfapi_descriptor *rfd); + + +extern int rfapiApCountAll(struct bgp *bgp); + +extern void rfapiApReadvertiseAll(struct bgp *bgp, + struct rfapi_descriptor *rfd); + +extern void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern int +rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *pfx_ip, + struct prefix *pfx_eth, struct prefix_rd *prd, uint32_t lifetime, + uint8_t cost, + struct rfapi_l2address_option *l2o); /* other options TBD */ + +extern int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *pfx_ip, struct prefix *pfx_eth, + struct prefix_rd *prd, + int *advertise_tunnel); /* out */ + + +#endif /* _QUAGGA_BGP_RFAPI_AP_H */ diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h new file mode 100644 index 0000000..32ea0a2 --- /dev/null +++ b/bgpd/rfapi/rfapi_backend.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_BACKEND_H +#define _QUAGGA_BGP_RFAPI_BACKEND_H + +#ifdef ENABLE_BGP_VNC + +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_nexthop.h" + +extern void rfapi_init(void); +extern void vnc_zebra_init(struct event_loop *master); +extern void vnc_zebra_destroy(void); + +extern void rfapi_delete(struct bgp *); + +struct rfapi *bgp_rfapi_new(struct bgp *bgp); +void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h); + +extern void rfapiProcessUpdate(struct peer *peer, void *rfd, + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, + uint8_t type, uint8_t sub_type, uint32_t *label); + + +extern void rfapiProcessWithdraw(struct peer *peer, void *rfd, + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, + uint8_t type, int kill); + +extern void rfapiProcessPeerDown(struct peer *peer); + +extern void vnc_zebra_announce(struct prefix *p, + struct bgp_path_info *new_select, + struct bgp *bgp); + +extern void vnc_zebra_withdraw(struct prefix *p, + struct bgp_path_info *old_select); + + +extern void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, + struct bgp_path_info *bpi, safi_t safi); + + +extern void vnc_direct_bgp_vpn_enable(struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi); + +extern void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_RFAPI_BACKEND_H */ diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c new file mode 100644 index 0000000..28326ab --- /dev/null +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" + +#include "bgpd/bgpd.h" + +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_descriptor_rfp_utils.h" +#include "bgpd/rfapi/vnc_debug.h" + + +void *rfapi_create_generic(struct rfapi_ip_addr *vn, struct rfapi_ip_addr *un) +{ + struct rfapi_descriptor *rfd; + rfd = XCALLOC(MTYPE_RFAPI_DESC, sizeof(struct rfapi_descriptor)); + vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); + rfd->vn_addr = *vn; + rfd->un_addr = *un; + return (void *)rfd; +} + +/*------------------------------------------ + * rfapi_free_generic + * + * Compare two generic rfapi descriptors. + * + * input: + * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * + *------------------------------------------*/ +void rfapi_free_generic(void *grfd) +{ + struct rfapi_descriptor *rfd; + rfd = (struct rfapi_descriptor *)grfd; + XFREE(MTYPE_RFAPI_DESC, rfd); +} + + +/*------------------------------------------ + * rfapi_compare_rfds + * + * Compare two generic rfapi descriptors. + * + * input: + * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * 0 Mismatch + * 1 Match + *------------------------------------------*/ +int rfapi_compare_rfds(void *rfd1, void *rfd2) +{ + struct rfapi_descriptor *rrfd1, *rrfd2; + int match = 0; + + rrfd1 = (struct rfapi_descriptor *)rfd1; + rrfd2 = (struct rfapi_descriptor *)rfd2; + + if (rrfd1->vn_addr.addr_family == rrfd2->vn_addr.addr_family) { + if (rrfd1->vn_addr.addr_family == AF_INET) + match = IPV4_ADDR_SAME(&(rrfd1->vn_addr.addr.v4), + &(rrfd2->vn_addr.addr.v4)); + else + match = IPV6_ADDR_SAME(&(rrfd1->vn_addr.addr.v6), + &(rrfd2->vn_addr.addr.v6)); + } + + /* + * If the VN addresses don't match in all forms, + * give up. + */ + if (!match) + return 0; + + /* + * do the process again for the UN addresses. + */ + match = 0; + if (rrfd1->un_addr.addr_family == rrfd2->un_addr.addr_family) { + /* VN addresses match + * UN address families match + * now check the actual UN addresses + */ + if (rrfd1->un_addr.addr_family == AF_INET) + match = IPV4_ADDR_SAME(&(rrfd1->un_addr.addr.v4), + &(rrfd2->un_addr.addr.v4)); + else + match = IPV6_ADDR_SAME(&(rrfd1->un_addr.addr.v6), + &(rrfd2->un_addr.addr.v6)); + } + return match; +} diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.h b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h new file mode 100644 index 0000000..9e65a70 --- /dev/null +++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + + +extern void *rfapi_create_generic(struct rfapi_ip_addr *vn, + struct rfapi_ip_addr *un); + +/*------------------------------------------ + * rfapi_free_generic + * + * Compare two generic rfapi descriptors. + * + * input: + * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic + * + * output: + * + * return value: + * + *------------------------------------------*/ +extern void rfapi_free_generic(void *grfd); diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c new file mode 100644 index 0000000..37a7326 --- /dev/null +++ b/bgpd/rfapi/rfapi_encap_tlv.c @@ -0,0 +1,730 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2015-2016, LabN Consulting, L.L.C. + */ + +#include "lib/zebra.h" + +#include "lib/memory.h" +#include "lib/prefix.h" +#include "lib/table.h" +#include "lib/vty.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_attr.h" + +#include "bgpd/bgp_encap_types.h" +#include "bgpd/bgp_encap_tlv.h" + +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/vnc_debug.h" + +static void rfapi_add_endpoint_address_to_subtlv( + struct bgp *bgp, struct rfapi_ip_addr *ea, + struct bgp_tea_subtlv_remote_endpoint *subtlv) +{ + subtlv->family = ea->addr_family; + if (subtlv->family == AF_INET) + subtlv->ip_address.v4 = ea->addr.v4; + else + subtlv->ip_address.v6 = ea->addr.v6; + subtlv->as4 = htonl(bgp->as); +} + +bgp_encap_types +rfapi_tunneltype_option_to_tlv(struct bgp *bgp, struct rfapi_ip_addr *ea, + struct rfapi_tunneltype_option *tto, + struct attr *attr, int always_add) +{ + +#define _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ttype) \ + if ((always_add \ + || (bgp->rfapi_cfg \ + && !CHECK_FLAG(bgp->rfapi_cfg->flags, \ + BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP))) \ + && ea \ + && !CHECK_SUBTLV_FLAG(&tto->bgpinfo.ttype, \ + BGP_TEA_SUBTLV_REMOTE_ENDPOINT)) { \ + rfapi_add_endpoint_address_to_subtlv( \ + bgp, ea, &tto->bgpinfo.ttype.st_endpoint); \ + SET_SUBTLV_FLAG(&tto->bgpinfo.ttype, \ + BGP_TEA_SUBTLV_REMOTE_ENDPOINT); \ + } + + struct rfapi_tunneltype_option dto; + if (tto == NULL) { /* create default type */ + tto = &dto; + memset(tto, 0, sizeof(dto)); + tto->type = RFAPI_BGP_ENCAP_TYPE_DEFAULT; + } + switch (tto->type) { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(l2tpv3_ip); + bgp_encap_type_l2tpv3overip_to_tlv(&tto->bgpinfo.l2tpv3_ip, + attr); + break; + + case BGP_ENCAP_TYPE_GRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(gre); + bgp_encap_type_gre_to_tlv(&tto->bgpinfo.gre, attr); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(transmit_tunnel_endpoint); + bgp_encap_type_transmit_tunnel_endpoint( + &tto->bgpinfo.transmit_tunnel_endpoint, attr); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ipsec_tunnel); + bgp_encap_type_ipsec_in_tunnel_mode_to_tlv( + &tto->bgpinfo.ipsec_tunnel, attr); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ip_ipsec); + bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + &tto->bgpinfo.ip_ipsec, attr); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_ipsec); + bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + &tto->bgpinfo.mpls_ipsec, attr); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ip_ip); + bgp_encap_type_ip_in_ip_to_tlv(&tto->bgpinfo.ip_ip, attr); + break; + + case BGP_ENCAP_TYPE_VXLAN: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(vxlan); + bgp_encap_type_vxlan_to_tlv(&tto->bgpinfo.vxlan, attr); + break; + + case BGP_ENCAP_TYPE_NVGRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(nvgre); + bgp_encap_type_nvgre_to_tlv(&tto->bgpinfo.nvgre, attr); + break; + + case BGP_ENCAP_TYPE_MPLS: + /* nothing to do for MPLS */ + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_gre); + bgp_encap_type_mpls_in_gre_to_tlv(&tto->bgpinfo.mpls_gre, attr); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(vxlan_gpe); + bgp_encap_type_vxlan_gpe_to_tlv(&tto->bgpinfo.vxlan_gpe, attr); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_udp); + bgp_encap_type_mpls_in_udp_to_tlv(&tto->bgpinfo.mpls_udp, attr); + break; + + case BGP_ENCAP_TYPE_PBB: + _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(pbb); + bgp_encap_type_pbb_to_tlv(&tto->bgpinfo.pbb, attr); + break; + + case BGP_ENCAP_TYPE_RESERVED: + assert(!"Cannot process BGP_ENCAP_TYPE_RESERVED"); + } + return tto->type; +} + +struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr) +{ + struct rfapi_un_option *uo = NULL; + struct rfapi_tunneltype_option *tto; + int rc; + struct bgp_attr_encap_subtlv *stlv; + + /* no tunnel encap attr stored */ + if (!attr->encap_tunneltype) + return NULL; + + stlv = attr->encap_subtlvs; + + uo = XCALLOC(MTYPE_RFAPI_UN_OPTION, sizeof(struct rfapi_un_option)); + uo->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; + uo->v.tunnel.type = attr->encap_tunneltype; + tto = &uo->v.tunnel; + + switch (attr->encap_tunneltype) { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + rc = tlv_to_bgp_encap_type_l2tpv3overip( + stlv, &tto->bgpinfo.l2tpv3_ip); + break; + + case BGP_ENCAP_TYPE_GRE: + rc = tlv_to_bgp_encap_type_gre(stlv, &tto->bgpinfo.gre); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + rc = tlv_to_bgp_encap_type_transmit_tunnel_endpoint( + stlv, &tto->bgpinfo.transmit_tunnel_endpoint); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + rc = tlv_to_bgp_encap_type_ipsec_in_tunnel_mode( + stlv, &tto->bgpinfo.ipsec_tunnel); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + rc = tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode( + stlv, &tto->bgpinfo.ip_ipsec); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + rc = tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode( + stlv, &tto->bgpinfo.mpls_ipsec); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + rc = tlv_to_bgp_encap_type_ip_in_ip(stlv, &tto->bgpinfo.ip_ip); + break; + + case BGP_ENCAP_TYPE_VXLAN: + rc = tlv_to_bgp_encap_type_vxlan(stlv, &tto->bgpinfo.vxlan); + break; + + case BGP_ENCAP_TYPE_NVGRE: + rc = tlv_to_bgp_encap_type_nvgre(stlv, &tto->bgpinfo.nvgre); + break; + + case BGP_ENCAP_TYPE_MPLS: + rc = tlv_to_bgp_encap_type_mpls(stlv, &tto->bgpinfo.mpls); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + rc = tlv_to_bgp_encap_type_mpls_in_gre(stlv, + &tto->bgpinfo.mpls_gre); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + rc = tlv_to_bgp_encap_type_vxlan_gpe(stlv, + &tto->bgpinfo.vxlan_gpe); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + rc = tlv_to_bgp_encap_type_mpls_in_udp(stlv, + &tto->bgpinfo.mpls_udp); + break; + + case BGP_ENCAP_TYPE_PBB: + rc = tlv_to_bgp_encap_type_pbb(stlv, &tto->bgpinfo.pbb); + break; + + default: + vnc_zlog_debug_verbose("%s: unknown tunnel type %d", __func__, + attr->encap_tunneltype); + rc = -1; + break; + } + if (rc) { + XFREE(MTYPE_RFAPI_UN_OPTION, uo); + } + return uo; +} + +/*********************************************************************** + * SUBTLV PRINT + ***********************************************************************/ + +static void subtlv_print_encap_l2tpv3_over_ip( + void *stream, int column_offset, + struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(L2TPv3 over IP)", + vty_newline); + fp(out, "%*s SessionID: %d%s", column_offset, "", st->sessionid, + vty_newline); + fp(out, "%*s Cookie: (length %d)%s", column_offset, "", + st->cookie_length, vty_newline); +} + +static void subtlv_print_encap_gre(void *stream, int column_offset, + struct bgp_tea_subtlv_encap_gre_key *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(GRE)", + vty_newline); + fp(out, "%*s GRE key: %d (0x%x)%s", column_offset, "", st->gre_key, + st->gre_key, vty_newline); +} + +static void subtlv_print_encap_pbb(void *stream, int column_offset, + struct bgp_tea_subtlv_encap_pbb *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(PBB)", + vty_newline); + if (st->flag_isid) { + fp(out, "%*s ISID: %d (0x%x)%s", column_offset, "", st->isid, + st->isid, vty_newline); + } + if (st->flag_vid) { + fp(out, "%*s VID: %d (0x%x)%s", column_offset, "", st->vid, + st->vid, vty_newline); + } + fp(out, "%*s MACADDR %02x:%02x:%02x:%02x:%02x:%02x%s", column_offset, + "", st->macaddr[0], st->macaddr[1], st->macaddr[2], st->macaddr[3], + st->macaddr[4], st->macaddr[5], vty_newline); +} + +static void subtlv_print_proto_type(void *stream, int column_offset, + struct bgp_tea_subtlv_proto_type *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(Proto Type)", + vty_newline); + fp(out, "%*s Proto %d (0x%x)%s", column_offset, "", st->proto, + st->proto, vty_newline); +} + +static void subtlv_print_color(void *stream, int column_offset, + struct bgp_tea_subtlv_color *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: Color", vty_newline); + fp(out, "%*s Color: %d (0x%x)", column_offset, "", st->color, + st->color, vty_newline); +} + +static void subtlv_print_ipsec_ta(void *stream, int column_offset, + struct bgp_tea_subtlv_ipsec_ta *st) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!st) + return; + + fp(out, "%*s%s%s", column_offset, "", "SubTLV: IPSEC TA", vty_newline); + fp(out, "%*s Authenticator Type: %d (0x%x)", column_offset, "", + st->authenticator_type, st->authenticator_type, vty_newline); + fp(out, "%*s Authenticator: (length %d)", column_offset, "", + st->authenticator_length, vty_newline); +} + +/*********************************************************************** + * TLV PRINT + ***********************************************************************/ + +static void +print_encap_type_l2tpv3overip(void *stream, int column_offset, + struct bgp_encap_type_l2tpv3_over_ip *bet) +{ + const char *type = "L2TPv3 over IP"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_l2tpv3_over_ip(stream, column_offset + 2, + &bet->st_encap); + subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto); + subtlv_print_color(stream, column_offset + 2, &bet->st_color); +} + +static void print_encap_type_gre(void *stream, int column_offset, + struct bgp_encap_type_gre *bet) +{ + const char *type = "GRE"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_gre(stream, column_offset + 2, &bet->st_encap); + subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto); + subtlv_print_color(stream, column_offset + 2, &bet->st_color); +} + +static void print_encap_type_ip_in_ip(void *stream, int column_offset, + struct bgp_encap_type_ip_in_ip *bet) +{ + const char *type = "IP in IP"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto); + subtlv_print_color(stream, column_offset + 2, &bet->st_color); +} + +static void print_encap_type_transmit_tunnel_endpoint( + void *stream, int column_offset, + struct bgp_encap_type_transmit_tunnel_endpoint *bet) +{ + const char *type = "Transmit Tunnel Endpoint"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void print_encap_type_ipsec_in_tunnel_mode( + void *stream, int column_offset, + struct bgp_encap_type_ipsec_in_tunnel_mode *bet) +{ + const char *type = "IPSEC in Tunnel mode"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta); +} + +static void print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode( + void *stream, int column_offset, + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + const char *type = "IP in IP Tunnel with IPSEC transport mode"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta); +} + +static void print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode( + void *stream, int column_offset, + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + const char *type = "MPLS in IP Tunnel with IPSEC transport mode"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta); +} + + +static void print_encap_type_pbb(void *stream, int column_offset, + struct bgp_encap_type_pbb *bet) +{ + const char *type = "PBB"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + subtlv_print_encap_pbb(stream, column_offset + 2, &bet->st_encap); +} + + +static void print_encap_type_vxlan(void *stream, int column_offset, + struct bgp_encap_type_vxlan *bet) +{ + const char *type = "VXLAN"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + + +static void print_encap_type_nvgre(void *stream, int column_offset, + struct bgp_encap_type_nvgre *bet) +{ + const char *type = "NVGRE"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void print_encap_type_mpls(void *stream, int column_offset, + struct bgp_encap_type_mpls *bet) +{ + const char *type = "MPLS"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void print_encap_type_mpls_in_gre(void *stream, int column_offset, + struct bgp_encap_type_mpls_in_gre *bet) +{ + const char *type = "MPLS in GRE"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void print_encap_type_vxlan_gpe(void *stream, int column_offset, + struct bgp_encap_type_vxlan_gpe *bet) +{ + const char *type = "VXLAN GPE"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +static void print_encap_type_mpls_in_udp(void *stream, int column_offset, + struct bgp_encap_type_mpls_in_udp *bet) +{ + const char *type = "MPLS in UDP"; + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bet) + return; + + fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline); + + /* no subtlvs for this type */ +} + +void rfapi_print_tunneltype_option(void *stream, int column_offset, + struct rfapi_tunneltype_option *tto) +{ + switch (tto->type) { + case BGP_ENCAP_TYPE_L2TPV3_OVER_IP: + print_encap_type_l2tpv3overip(stream, column_offset, + &tto->bgpinfo.l2tpv3_ip); + break; + + case BGP_ENCAP_TYPE_GRE: + print_encap_type_gre(stream, column_offset, &tto->bgpinfo.gre); + break; + + case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT: + print_encap_type_transmit_tunnel_endpoint( + stream, column_offset, + &tto->bgpinfo.transmit_tunnel_endpoint); + break; + + case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE: + print_encap_type_ipsec_in_tunnel_mode( + stream, column_offset, &tto->bgpinfo.ipsec_tunnel); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode( + stream, column_offset, &tto->bgpinfo.ip_ipsec); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE: + print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode( + stream, column_offset, &tto->bgpinfo.mpls_ipsec); + break; + + case BGP_ENCAP_TYPE_IP_IN_IP: + print_encap_type_ip_in_ip(stream, column_offset, + &tto->bgpinfo.ip_ip); + break; + + case BGP_ENCAP_TYPE_VXLAN: + print_encap_type_vxlan(stream, column_offset, + &tto->bgpinfo.vxlan); + break; + + case BGP_ENCAP_TYPE_NVGRE: + print_encap_type_nvgre(stream, column_offset, + &tto->bgpinfo.nvgre); + break; + + case BGP_ENCAP_TYPE_MPLS: + print_encap_type_mpls(stream, column_offset, + &tto->bgpinfo.mpls); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_GRE: + print_encap_type_mpls_in_gre(stream, column_offset, + &tto->bgpinfo.mpls_gre); + break; + + case BGP_ENCAP_TYPE_VXLAN_GPE: + print_encap_type_vxlan_gpe(stream, column_offset, + &tto->bgpinfo.vxlan_gpe); + break; + + case BGP_ENCAP_TYPE_MPLS_IN_UDP: + print_encap_type_mpls_in_udp(stream, column_offset, + &tto->bgpinfo.mpls_udp); + break; + + case BGP_ENCAP_TYPE_PBB: + print_encap_type_pbb(stream, column_offset, &tto->bgpinfo.pbb); + break; + + case BGP_ENCAP_TYPE_RESERVED: + assert(!"Cannot process BGP_ENCAP_TYPE_RESERVED"); + } +} diff --git a/bgpd/rfapi/rfapi_encap_tlv.h b/bgpd/rfapi/rfapi_encap_tlv.h new file mode 100644 index 0000000..56131d8 --- /dev/null +++ b/bgpd/rfapi/rfapi_encap_tlv.h @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2015-2016, LabN Consulting, L.L.C. + */ + +#ifndef _QUAGGA_BGP_RFAPI_ENCAP_TLV_H +#define _QUAGGA_BGP_RFAPI_ENCAP_TLV_H + +#define RFAPI_BGP_ENCAP_TYPE_DEFAULT BGP_ENCAP_TYPE_IP_IN_IP + +extern bgp_encap_types +rfapi_tunneltype_option_to_tlv(struct bgp *bgp, struct rfapi_ip_addr *ea, + struct rfapi_tunneltype_option *tto, + struct attr *attr, int always_add); + +extern struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr); + +extern void rfapi_print_tunneltype_option(void *stream, int column_offset, + struct rfapi_tunneltype_option *tto); + + +#endif /* _QUAGGA_BGP_RFAPI_ENCAP_TLV_H */ diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c new file mode 100644 index 0000000..a93e186 --- /dev/null +++ b/bgpd/rfapi/rfapi_import.c @@ -0,0 +1,4818 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2009-2016, LabN Consulting, L.L.C. + */ + +/* + * File: rfapi_import.c + * Purpose: Handle import of routes from BGP to RFAPI + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/skiplist.h" +#include "frrevent.h" +#include "lib/stream.h" +#include "lib/lib_errors.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */ +#include "bgpd/bgp_vnc_types.h" +#include "bgpd/bgp_rd.h" + +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_nve_addr.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_zebra.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_import_bgp_p.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" + +#ifdef HAVE_GLIBC_BACKTRACE +/* for backtrace and friends */ +#include <execinfo.h> +#endif /* HAVE_GLIBC_BACKTRACE */ + +#undef DEBUG_MONITOR_MOVE_SHORTER +#undef DEBUG_RETURNED_NHL +#undef DEBUG_ROUTE_COUNTERS +#undef DEBUG_ENCAP_MONITOR +#undef DEBUG_L2_EXTRA +#undef DEBUG_IT_NODES +#undef DEBUG_BI_SEARCH + +/* + * Allocated for each withdraw timer instance; freed when the timer + * expires or is canceled + */ +struct rfapi_withdraw { + struct rfapi_import_table *import_table; + struct agg_node *node; + struct bgp_path_info *info; + safi_t safi; /* used only for bulk operations */ + /* + * For import table node reference count checking (i.e., debugging). + * Normally when a timer expires, lockoffset should be 0. However, if + * the timer expiration function is called directly (e.g., + * rfapiExpireVpnNow), the node could be locked by a preceding + * agg_route_top() or agg_route_next() in a loop, so we need to pass + * this value in. + */ + int lockoffset; +}; + +/* + * DEBUG FUNCTION + * It's evil and fiendish. It's compiler-dependent. + * ? Might need LDFLAGS -rdynamic to produce all function names + */ +void rfapiDebugBacktrace(void) +{ +#ifdef HAVE_GLIBC_BACKTRACE +#define RFAPI_DEBUG_BACKTRACE_NENTRIES 200 + void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES]; + char **syms; + size_t i; + size_t size; + + size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES); + syms = backtrace_symbols(buf, size); + + for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i) { + vnc_zlog_debug_verbose("backtrace[%2zu]: %s", i, syms[i]); + } + + free(syms); +#else +#endif +} + +/* + * DEBUG FUNCTION + * Count remote routes and compare with actively-maintained values. + * Abort if they disagree. + */ +void rfapiCheckRouteCount(void) +{ + struct bgp *bgp = bgp_get_default(); + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + assert(bgp); + + h = bgp->rfapi; + assert(h); + + for (it = h->imports; it; it = it->next) { + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_table *rt; + struct agg_node *rn; + + int holddown_count = 0; + int imported_count = 0; + int remote_count = 0; + + rt = it->imported_vpn[afi]; + + for (rn = agg_route_top(rt); rn; + rn = agg_route_next(rn)) { + struct bgp_path_info *bpi; + struct bgp_path_info *next; + + for (bpi = rn->info; bpi; bpi = next) { + next = bpi->next; + + if (CHECK_FLAG(bpi->flags, + BGP_PATH_REMOVED)) { + ++holddown_count; + + } else { + if (!RFAPI_LOCAL_BI(bpi)) { + if (RFAPI_DIRECT_IMPORT_BI( + bpi)) { + ++imported_count; + } else { + ++remote_count; + } + } + } + } + } + + if (it->holddown_count[afi] != holddown_count) { + vnc_zlog_debug_verbose( + "%s: it->holddown_count %d != holddown_count %d", + __func__, it->holddown_count[afi], + holddown_count); + assert(0); + } + if (it->remote_count[afi] != remote_count) { + vnc_zlog_debug_verbose( + "%s: it->remote_count %d != remote_count %d", + __func__, it->remote_count[afi], + remote_count); + assert(0); + } + if (it->imported_count[afi] != imported_count) { + vnc_zlog_debug_verbose( + "%s: it->imported_count %d != imported_count %d", + __func__, it->imported_count[afi], + imported_count); + assert(0); + } + } + } +} + +#ifdef DEBUG_ROUTE_COUNTERS +#define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0) +#else +#define VNC_ITRCCK +#endif + +/* + * Validate reference count for a node in an import table + * + * Normally lockoffset is 0 for nodes in quiescent state. However, + * agg_unlock_node will delete the node if it is called when + * node->lock == 1, and we have to validate the refcount before + * the node is deleted. In this case, we specify lockoffset 1. + */ +void rfapiCheckRefcount(struct agg_node *rn, safi_t safi, int lockoffset) +{ + unsigned int count_bpi = 0; + unsigned int count_monitor = 0; + struct bgp_path_info *bpi; + struct rfapi_monitor_encap *hme; + struct rfapi_monitor_vpn *hmv; + + for (bpi = rn->info; bpi; bpi = bpi->next) + ++count_bpi; + + + if (rn->aggregate) { + ++count_monitor; /* rfapi_it_extra */ + + switch (safi) { + void *cursor; + int rc; + + case SAFI_ENCAP: + for (hme = RFAPI_MONITOR_ENCAP(rn); hme; + hme = hme->next) + ++count_monitor; + break; + + case SAFI_MPLS_VPN: + + for (hmv = RFAPI_MONITOR_VPN(rn); hmv; hmv = hmv->next) + ++count_monitor; + + if (RFAPI_MONITOR_EXTERIOR(rn)->source) { + ++count_monitor; /* sl */ + cursor = NULL; + for (rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn)->source, + NULL, NULL, &cursor); + !rc; + rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn)->source, + NULL, NULL, &cursor)) { + + ++count_monitor; /* sl entry */ + } + } + break; + + case SAFI_UNSPEC: + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_EVPN: + case SAFI_LABELED_UNICAST: + case SAFI_FLOWSPEC: + case SAFI_MAX: + assert(!"Passed in safi should be impossible"); + } + } + + if (count_bpi + count_monitor + lockoffset + != agg_node_get_lock_count(rn)) { + vnc_zlog_debug_verbose( + "%s: count_bpi=%d, count_monitor=%d, lockoffset=%d, rn->lock=%d", + __func__, count_bpi, count_monitor, lockoffset, + agg_node_get_lock_count(rn)); + assert(0); + } +} + +/* + * Perform deferred rfapi_close operations that were queued + * during callbacks. + */ +static wq_item_status rfapi_deferred_close_workfunc(struct work_queue *q, + void *data) +{ + struct rfapi_descriptor *rfd = data; + struct rfapi *h = q->spec.data; + + assert(!(h->flags & RFAPI_INCALLBACK)); + rfapi_close(rfd); + vnc_zlog_debug_verbose("%s: completed deferred close on handle %p", + __func__, rfd); + return WQ_SUCCESS; +} + +/* + * Extract layer 2 option from Encap TLVS in BGP attrs + */ +int rfapiGetL2o(struct attr *attr, struct rfapi_l2address_option *l2o) +{ + if (attr) { + struct bgp_attr_encap_subtlv *pEncap; + + for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap; + pEncap = pEncap->next) { + + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) { + if (pEncap->value[0] + == RFAPI_VN_OPTION_TYPE_L2ADDR) { + + if (pEncap->value[1] == 14) { + memcpy(l2o->macaddr.octet, + pEncap->value + 2, + ETH_ALEN); + l2o->label = + ((pEncap->value[10] + >> 4) + & 0x0f) + + ((pEncap->value[9] + << 4) + & 0xff0) + + ((pEncap->value[8] + << 12) + & 0xff000); + + l2o->local_nve_id = + pEncap->value[12]; + + l2o->logical_net_id = + (pEncap->value[15] + & 0xff) + + ((pEncap->value[14] + << 8) + & 0xff00) + + ((pEncap->value[13] + << 16) + & 0xff0000); + } + + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Extract the lifetime from the Tunnel Encap attribute of a route in + * an import table + */ +int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime) +{ + struct bgp_attr_encap_subtlv *pEncap; + + *lifetime = RFAPI_INFINITE_LIFETIME; /* default to infinite */ + + if (attr) { + + for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap; + pEncap = pEncap->next) { + + if (pEncap->type + == BGP_VNC_SUBTLV_TYPE_LIFETIME) { /* lifetime */ + if (pEncap->length == 4) { + memcpy(lifetime, pEncap->value, 4); + *lifetime = ntohl(*lifetime); + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Look for UN address in Encap attribute + */ +int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p) +{ + struct bgp_attr_encap_subtlv *pEncap; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ + + bgp_attr_extcom_tunnel_type(attr, &tun_type); + if (tun_type == BGP_ENCAP_TYPE_MPLS) { + if (!p) + return 0; + /* MPLS carries UN address in next hop */ + rfapiNexthop2Prefix(attr, p); + if (p->family != AF_UNSPEC) + return 0; + + return ENOENT; + } + if (attr) { + for (pEncap = attr->encap_subtlvs; pEncap; + pEncap = pEncap->next) { + + if (pEncap->type + == BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT) { /* un + addr + */ + switch (pEncap->length) { + case 8: + if (p) { + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + memcpy(p->u.val, pEncap->value, + 4); + } + return 0; + + case 20: + if (p) { + p->family = AF_INET6; + p->prefixlen = IPV6_MAX_BITLEN; + memcpy(p->u.val, pEncap->value, + 16); + } + return 0; + } + } + } + } + + return ENOENT; +} + +/* + * Get UN address wherever it might be + */ +int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p) +{ + /* If it's in this route's VNC attribute, we're done */ + if (!rfapiGetVncTunnelUnAddr(bpi->attr, p)) + return 0; + /* + * Otherwise, see if it's cached from a corresponding ENCAP SAFI + * advertisement + */ + if (bpi->extra) { + switch (bpi->extra->vnc.import.un_family) { + case AF_INET: + if (p) { + p->family = bpi->extra->vnc.import.un_family; + p->u.prefix4 = bpi->extra->vnc.import.un.addr4; + p->prefixlen = IPV4_MAX_BITLEN; + } + return 0; + case AF_INET6: + if (p) { + p->family = bpi->extra->vnc.import.un_family; + p->u.prefix6 = bpi->extra->vnc.import.un.addr6; + p->prefixlen = IPV6_MAX_BITLEN; + } + return 0; + default: + if (p) + p->family = AF_UNSPEC; +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose( + "%s: bpi->extra->vnc.import.un_family is 0, no UN addr", + __func__); +#endif + break; + } + } + + return ENOENT; +} + + +/* + * Make a new bgp_path_info from gathered parameters + */ +static struct bgp_path_info *rfapiBgpInfoCreate(struct attr *attr, + struct peer *peer, void *rfd, + struct prefix_rd *prd, + uint8_t type, uint8_t sub_type, + uint32_t *label) +{ + struct bgp_path_info *new; + + new = info_make(type, sub_type, 0, peer, attr, NULL); + + new->attr = bgp_attr_intern(attr); + + bgp_path_info_extra_get(new); + if (prd) { + new->extra->vnc.import.rd = *prd; + new->extra->vnc.import.create_time = monotime(NULL); + } + if (label) + encode_label(*label, &new->extra->label[0]); + + peer_lock(peer); + + return new; +} + +/* + * Frees bgp_path_info as used in import tables (parts are not + * allocated exactly the way they are in the main RIBs) + */ +static void rfapiBgpInfoFree(struct bgp_path_info *goner) +{ + if (!goner) + return; + + if (goner->peer) { + vnc_zlog_debug_verbose("%s: calling peer_unlock(%p), #%d", + __func__, goner->peer, + goner->peer->lock); + peer_unlock(goner->peer); + } + + bgp_attr_unintern(&goner->attr); + + if (goner->extra) + bgp_path_info_extra_free(&goner->extra); + XFREE(MTYPE_BGP_ROUTE, goner); +} + +struct rfapi_import_table *rfapiMacImportTableGetNoAlloc(struct bgp *bgp, + uint32_t lni) +{ + struct rfapi *h; + struct rfapi_import_table *it = NULL; + uintptr_t lni_as_ptr = lni; + + h = bgp->rfapi; + if (!h) + return NULL; + + if (!h->import_mac) + return NULL; + + if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it)) + return NULL; + + return it; +} + +struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, uint32_t lni) +{ + struct rfapi *h; + struct rfapi_import_table *it = NULL; + uintptr_t lni_as_ptr = lni; + + h = bgp->rfapi; + assert(h); + + if (!h->import_mac) { + /* default cmp is good enough for LNI */ + h->import_mac = skiplist_new(0, NULL, NULL); + } + + if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it)) { + + struct ecommunity *enew; + struct ecommunity_val eval; + afi_t afi; + + it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE, + sizeof(struct rfapi_import_table)); + /* set RT list of new import table based on LNI */ + memset((char *)&eval, 0, sizeof(eval)); + eval.val[0] = 0; /* VNC L2VPN */ + eval.val[1] = 2; /* VNC L2VPN */ + eval.val[5] = (lni >> 16) & 0xff; + eval.val[6] = (lni >> 8) & 0xff; + eval.val[7] = (lni >> 0) & 0xff; + + enew = ecommunity_new(); + ecommunity_add_val(enew, &eval, false, false); + it->rt_import_list = enew; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + it->imported_vpn[afi] = agg_table_init(); + it->imported_encap[afi] = agg_table_init(); + } + + it->l2_logical_net_id = lni; + + skiplist_insert(h->import_mac, (void *)lni_as_ptr, it); + } + + assert(it); + return it; +} + +/* + * Implement MONITOR_MOVE_SHORTER(original_node) from + * RFAPI-Import-Event-Handling.txt + * + * Returns pointer to the list of moved monitors + */ +static struct rfapi_monitor_vpn * +rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset) +{ + struct bgp_path_info *bpi; + struct agg_node *par; + struct rfapi_monitor_vpn *m; + struct rfapi_monitor_vpn *mlast; + struct rfapi_monitor_vpn *moved; + int movecount = 0; + int parent_already_refcounted = 0; + + RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN, lockoffset); + +#ifdef DEBUG_MONITOR_MOVE_SHORTER + { + vnc_zlog_debug_verbose("%s: called with node pfx=%pFX", + __func__, &original_vpn_node->p); + } +#endif + + /* + * 1. If there is at least one bpi (either regular route or + * route marked as withdrawn, with a pending timer) at + * original_node with a valid UN address, we're done. Return. + */ + for (bpi = original_vpn_node->info; bpi; bpi = bpi->next) { + struct prefix pfx; + + if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) { +#ifdef DEBUG_MONITOR_MOVE_SHORTER + vnc_zlog_debug_verbose( + "%s: have valid UN at original node, no change", + __func__); +#endif + return NULL; + } + } + + /* + * 2. Travel up the tree (toward less-specific prefixes) from + * original_node to find the first node that has at least + * one route (even if it is only a withdrawn route) with a + * valid UN address. Call this node "Node P." + */ + for (par = agg_node_parent(original_vpn_node); par; + par = agg_node_parent(par)) { + for (bpi = par->info; bpi; bpi = bpi->next) { + struct prefix pfx; + if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) { + break; + } + } + if (bpi) + break; + } + + if (par) { + RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 0); + } + + /* + * If no less-specific routes, try to use the 0/0 node + */ + if (!par) { + const struct prefix *p; + /* this isn't necessarily 0/0 */ + par = agg_route_table_top(original_vpn_node); + + if (par) + p = agg_node_get_prefix(par); + /* + * If we got the top node but it wasn't 0/0, + * ignore it + */ + if (par && p->prefixlen) { + agg_unlock_node(par); /* maybe free */ + par = NULL; + } + + if (par) { + ++parent_already_refcounted; + } + } + + /* + * Create 0/0 node if it isn't there + */ + if (!par) { + struct prefix pfx_default; + const struct prefix *p = agg_node_get_prefix(original_vpn_node); + + memset(&pfx_default, 0, sizeof(pfx_default)); + pfx_default.family = p->family; + + /* creates default node if none exists */ + par = agg_node_get(agg_get_table(original_vpn_node), + &pfx_default); + ++parent_already_refcounted; + } + + /* + * 3. Move each of the monitors found at original_node to Node P. + * These are "Moved Monitors." + * + */ + + /* + * Attach at end so that the list pointer we return points + * only to the moved routes + */ + for (m = RFAPI_MONITOR_VPN(par), mlast = NULL; m; + mlast = m, m = m->next) + ; + + if (mlast) { + moved = mlast->next = RFAPI_MONITOR_VPN(original_vpn_node); + } else { + moved = RFAPI_MONITOR_VPN_W_ALLOC(par) = + RFAPI_MONITOR_VPN(original_vpn_node); + } + if (RFAPI_MONITOR_VPN( + original_vpn_node)) /* check agg, so not allocated */ + RFAPI_MONITOR_VPN_W_ALLOC(original_vpn_node) = NULL; + + /* + * update the node pointers on the monitors + */ + for (m = moved; m; m = m->next) { + ++movecount; + m->node = par; + } + + RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, + parent_already_refcounted - movecount); + while (movecount > parent_already_refcounted) { + agg_lock_node(par); + ++parent_already_refcounted; + } + while (movecount < parent_already_refcounted) { + /* unlikely, but code defensively */ + agg_unlock_node(par); + --parent_already_refcounted; + } + RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN, + movecount + lockoffset); + while (movecount--) { + agg_unlock_node(original_vpn_node); + } + +#ifdef DEBUG_MONITOR_MOVE_SHORTER + { + vnc_zlog_debug_verbose("%s: moved to node pfx=%pFX", __func__, + &par->p); + } +#endif + + + return moved; +} + +/* + * Implement MONITOR_MOVE_LONGER(new_node) from + * RFAPI-Import-Event-Handling.txt + */ +static void rfapiMonitorMoveLonger(struct agg_node *new_vpn_node) +{ + struct rfapi_monitor_vpn *monitor; + struct rfapi_monitor_vpn *mlast; + struct bgp_path_info *bpi; + struct agg_node *par; + const struct prefix *new_vpn_node_p = agg_node_get_prefix(new_vpn_node); + + RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0); + + /* + * Make sure we have at least one valid route at the new node + */ + for (bpi = new_vpn_node->info; bpi; bpi = bpi->next) { + struct prefix pfx; + if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) + break; + } + + if (!bpi) { + vnc_zlog_debug_verbose( + "%s: no valid routes at node %p, so not attempting moves", + __func__, new_vpn_node); + return; + } + + /* + * Find first parent node that has monitors + */ + for (par = agg_node_parent(new_vpn_node); par; + par = agg_node_parent(par)) { + if (RFAPI_MONITOR_VPN(par)) + break; + } + + if (!par) { + vnc_zlog_debug_verbose( + "%s: no parent nodes with monitors, done", __func__); + return; + } + + /* + * Check each of these monitors to see of their longest-match + * is now the updated node. Move any such monitors to the more- + * specific updated node + */ + for (mlast = NULL, monitor = RFAPI_MONITOR_VPN(par); monitor;) { + /* + * If new longest match for monitor prefix is the new + * route's prefix, move monitor to new route's prefix + */ + if (prefix_match(new_vpn_node_p, &monitor->p)) { + /* detach */ + if (mlast) { + mlast->next = monitor->next; + } else { + RFAPI_MONITOR_VPN_W_ALLOC(par) = monitor->next; + } + + + /* attach */ + monitor->next = RFAPI_MONITOR_VPN(new_vpn_node); + RFAPI_MONITOR_VPN_W_ALLOC(new_vpn_node) = monitor; + monitor->node = new_vpn_node; + + agg_lock_node(new_vpn_node); /* incr refcount */ + + monitor = mlast ? mlast->next : RFAPI_MONITOR_VPN(par); + + RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 1); + /* decr refcount after we're done with par as this might + * free it */ + agg_unlock_node(par); + + continue; + } + mlast = monitor; + monitor = monitor->next; + } + + RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0); +} + + +static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi) +{ + struct bgp_path_info *next; + + while (bpi) { + + /* + * If there is a timer waiting to delete this bpi, cancel + * the timer and delete immediately + */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) + && bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = + EVENT_ARG(bpi->extra->vnc.import.timer); + + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + EVENT_OFF(bpi->extra->vnc.import.timer); + } + + next = bpi->next; + bpi->next = NULL; + rfapiBgpInfoFree(bpi); + bpi = next; + } +} + +static void rfapiImportTableFlush(struct rfapi_import_table *it) +{ + afi_t afi; + + /* + * Free ecommunity + */ + ecommunity_free(&it->rt_import_list); + it->rt_import_list = NULL; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_node *rn; + struct agg_table *at; + + at = it->imported_vpn[afi]; + if (at) { + for (rn = agg_route_top(at); rn; + rn = agg_route_next(rn)) { + /* + * Each route_node has: + * aggregate: points to rfapi_it_extra with + * monitor chain(s) + * info: points to chain of bgp_path_info + */ + /* free bgp_path_info and its children */ + rfapiBgpInfoChainFree(rn->info); + rn->info = NULL; + + rfapiMonitorExtraFlush(SAFI_MPLS_VPN, rn); + } + agg_table_finish(at); + } + + if (at) { + at = it->imported_encap[afi]; + for (rn = agg_route_top(at); rn; + rn = agg_route_next(rn)) { + /* free bgp_path_info and its children */ + rfapiBgpInfoChainFree(rn->info); + rn->info = NULL; + + rfapiMonitorExtraFlush(SAFI_ENCAP, rn); + } + agg_table_finish(at); + } + } + if (it->monitor_exterior_orphans) { + skiplist_free(it->monitor_exterior_orphans); + } +} + +void rfapiImportTableRefDelByIt(struct bgp *bgp, + struct rfapi_import_table *it_target) +{ + struct rfapi *h; + struct rfapi_import_table *it; + struct rfapi_import_table *prev = NULL; + + assert(it_target); + + h = bgp->rfapi; + assert(h); + + for (it = h->imports; it; prev = it, it = it->next) { + if (it == it_target) + break; + } + + assert(it); + assert(it->refcount); + + it->refcount -= 1; + + if (!it->refcount) { + if (prev) { + prev->next = it->next; + } else { + h->imports = it->next; + } + rfapiImportTableFlush(it); + XFREE(MTYPE_RFAPI_IMPORTTABLE, it); + } +} + +#ifdef RFAPI_REQUIRE_ENCAP_BEEC +/* + * Look for magic BGP Encapsulation Extended Community value + * Format in RFC 5512 Sect. 4.5 + */ +static int rfapiEcommunitiesMatchBeec(struct ecommunity *ecom, + bgp_encap_types type) +{ + int i; + + if (!ecom) + return 0; + + for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) { + + uint8_t *ep; + + ep = ecom->val + i; + + if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE + && ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP + && ep[6] == ((type && 0xff00) >> 8) + && ep[7] == (type & 0xff)) { + + return 1; + } + } + return 0; +} +#endif + +int rfapiEcommunitiesIntersect(struct ecommunity *e1, struct ecommunity *e2) +{ + uint32_t i, j; + + if (!e1 || !e2) + return 0; + + { + char *s1, *s2; + s1 = ecommunity_ecom2str(e1, ECOMMUNITY_FORMAT_DISPLAY, 0); + s2 = ecommunity_ecom2str(e2, ECOMMUNITY_FORMAT_DISPLAY, 0); + vnc_zlog_debug_verbose("%s: e1[%s], e2[%s]", __func__, s1, s2); + XFREE(MTYPE_ECOMMUNITY_STR, s1); + XFREE(MTYPE_ECOMMUNITY_STR, s2); + } + + for (i = 0; i < e1->size; ++i) { + for (j = 0; j < e2->size; ++j) { + if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), + e2->val + (j * ECOMMUNITY_SIZE), + ECOMMUNITY_SIZE)) { + + return 1; + } + } + } + return 0; +} + +int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni) +{ + if (ecom) { + uint32_t i; + + for (i = 0; i < ecom->size; ++i) { + uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE); + + if ((*(p + 0) == 0x00) && (*(p + 1) == 0x02)) { + + *lni = (*(p + 5) << 16) | (*(p + 6) << 8) + | (*(p + 7)); + return 0; + } + } + } + return ENOENT; +} + +int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, uint16_t *tag_id) +{ + struct bgp *bgp = bgp_get_default(); + *tag_id = 0; /* default to untagged */ + if (ecom) { + uint32_t i; + + for (i = 0; i < ecom->size; ++i) { + as_t as = 0; + int encode = 0; + const uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE); + + /* High-order octet of type. */ + encode = *p++; + + if (*p++ == ECOMMUNITY_ROUTE_TARGET) { + if (encode == ECOMMUNITY_ENCODE_AS4) { + p = ptr_get_be32(p, &as); + } else if (encode == ECOMMUNITY_ENCODE_AS) { + as = (*p++ << 8); + as |= (*p++); + p += 2; /* skip next two, tag/vid + always in lowest bytes */ + } + if (as == bgp->as) { + *tag_id = *p++ << 8; + *tag_id |= (*p++); + return 0; + } + } + } + } + return ENOENT; +} + +static int rfapiVpnBiNhEqualsPt(struct bgp_path_info *bpi, + struct rfapi_ip_addr *hpt) +{ + uint8_t family; + + if (!hpt || !bpi) + return 0; + + family = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len); + + if (hpt->addr_family != family) + return 0; + + switch (family) { + case AF_INET: + if (bpi->attr->mp_nexthop_global_in.s_addr + != hpt->addr.v4.s_addr) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_CMP(&bpi->attr->mp_nexthop_global, &hpt->addr.v6)) + return 0; + break; + + default: + return 0; + } + + return 1; +} + + +/* + * Compare 2 VPN BIs. Return true if they have the same VN and UN addresses + */ +static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1, + struct bgp_path_info *bpi2) +{ + struct prefix pfx_un1; + struct prefix pfx_un2; + + if (!bpi1 || !bpi2) + return 0; + + /* + * VN address comparisons + */ + + if (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len) + != BGP_MP_NEXTHOP_FAMILY(bpi2->attr->mp_nexthop_len)) { + return 0; + } + + switch (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len)) { + case AF_INET: + if (bpi1->attr->mp_nexthop_global_in.s_addr + != bpi2->attr->mp_nexthop_global_in.s_addr) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_CMP(&bpi1->attr->mp_nexthop_global, + &bpi2->attr->mp_nexthop_global)) + return 0; + break; + + default: + return 0; + } + + memset(&pfx_un1, 0, sizeof(pfx_un1)); + memset(&pfx_un2, 0, sizeof(pfx_un2)); + + /* + * UN address comparisons + */ + if (rfapiGetVncTunnelUnAddr(bpi1->attr, &pfx_un1)) { + if (bpi1->extra) { + pfx_un1.family = bpi1->extra->vnc.import.un_family; + switch (bpi1->extra->vnc.import.un_family) { + case AF_INET: + pfx_un1.u.prefix4 = + bpi1->extra->vnc.import.un.addr4; + break; + case AF_INET6: + pfx_un1.u.prefix6 = + bpi1->extra->vnc.import.un.addr6; + break; + default: + pfx_un1.family = AF_UNSPEC; + break; + } + } + } + + if (rfapiGetVncTunnelUnAddr(bpi2->attr, &pfx_un2)) { + if (bpi2->extra) { + pfx_un2.family = bpi2->extra->vnc.import.un_family; + switch (bpi2->extra->vnc.import.un_family) { + case AF_INET: + pfx_un2.u.prefix4 = + bpi2->extra->vnc.import.un.addr4; + break; + case AF_INET6: + pfx_un2.u.prefix6 = + bpi2->extra->vnc.import.un.addr6; + break; + default: + pfx_un2.family = AF_UNSPEC; + break; + } + } + } + + if (pfx_un1.family == AF_UNSPEC || pfx_un2.family == AF_UNSPEC) + return 0; + + if (pfx_un1.family != pfx_un2.family) + return 0; + + switch (pfx_un1.family) { + case AF_INET: + if (!IPV4_ADDR_SAME(&pfx_un1.u.prefix4, &pfx_un2.u.prefix4)) + return 0; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME(&pfx_un1.u.prefix6, &pfx_un2.u.prefix6)) + return 0; + break; + } + + + return 1; +} + +uint8_t rfapiRfpCost(struct attr *attr) +{ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + if (attr->local_pref > 255) { + return 0; + } + return 255 - attr->local_pref; + } + + return 255; +} + +/*------------------------------------------ + * rfapi_extract_l2o + * + * Find Layer 2 options in an option chain + * + * input: + * pHop option chain + * + * output: + * l2o layer 2 options extracted + * + * return value: + * 0 OK + * 1 no options found + * + --------------------------------------------*/ +int rfapi_extract_l2o( + struct bgp_tea_options *pHop, /* chain of options */ + struct rfapi_l2address_option *l2o) /* return extracted value */ +{ + struct bgp_tea_options *p; + + for (p = pHop; p; p = p->next) { + if ((p->type == RFAPI_VN_OPTION_TYPE_L2ADDR) + && (p->length >= 8)) { + + char *v = p->value; + + memcpy(&l2o->macaddr, v, 6); + + l2o->label = ((v[6] << 12) & 0xff000) + + ((v[7] << 4) & 0xff0) + + ((v[8] >> 4) & 0xf); + + l2o->local_nve_id = (uint8_t)v[10]; + + l2o->logical_net_id = + (v[11] << 16) + (v[12] << 8) + (v[13] << 0); + + return 0; + } + } + return 1; +} + +static struct rfapi_next_hop_entry * +rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix, + struct bgp_path_info *bpi, /* route to encode */ + uint32_t lifetime, /* use this in nhe */ + struct agg_node *rn) /* req for L2 eth addr */ +{ + struct rfapi_next_hop_entry *new; + int have_vnc_tunnel_un = 0; + const struct prefix *p = agg_node_get_prefix(rn); + +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: entry, bpi %p, rn %p", __func__, bpi, rn); +#endif + + new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry)); + + new->prefix = *rprefix; + + if (bpi->extra + && decode_rd_type(bpi->extra->vnc.import.rd.val) + == RD_TYPE_VNC_ETH) { + /* ethernet */ + + struct rfapi_vn_option *vo; + + vo = XCALLOC(MTYPE_RFAPI_VN_OPTION, + sizeof(struct rfapi_vn_option)); + + vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + memcpy(&vo->v.l2addr.macaddr, &p->u.prefix_eth.octet, ETH_ALEN); + /* only low 3 bytes of this are significant */ + (void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr), + &vo->v.l2addr.logical_net_id); + (void)rfapiEcommunityGetEthernetTag( + bgp_attr_get_ecommunity(bpi->attr), + &vo->v.l2addr.tag_id); + + /* local_nve_id comes from lower byte of RD type */ + vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1]; + + /* label comes from MP_REACH_NLRI label */ + vo->v.l2addr.label = decode_label(&bpi->extra->label[0]); + + new->vn_options = vo; + + /* + * If there is an auxiliary prefix (i.e., host IP address), + * use it as the nexthop prefix instead of the query prefix + */ + if (bpi->extra->vnc.import.aux_prefix.family) { + rfapiQprefix2Rprefix(&bpi->extra->vnc.import.aux_prefix, + &new->prefix); + } + } + + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/ + new->prefix.cost = rfapiRfpCost(bpi->attr); + + struct bgp_attr_encap_subtlv *pEncap; + + switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) { + case AF_INET: + new->vn_address.addr_family = AF_INET; + new->vn_address.addr.v4 = bpi->attr->mp_nexthop_global_in; + break; + + case AF_INET6: + new->vn_address.addr_family = AF_INET6; + new->vn_address.addr.v6 = bpi->attr->mp_nexthop_global; + break; + + default: + zlog_warn("%s: invalid vpn nexthop length: %d", __func__, + bpi->attr->mp_nexthop_len); + rfapi_free_next_hop_list(new); + return NULL; + } + + for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap; + pEncap = pEncap->next) { + switch (pEncap->type) { + case BGP_VNC_SUBTLV_TYPE_LIFETIME: + /* use configured lifetime, not attr lifetime */ + break; + + default: + zlog_warn("%s: unknown VNC option type %d", __func__, + pEncap->type); + + break; + } + } + + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); + if (tun_type == BGP_ENCAP_TYPE_MPLS) { + struct prefix p; + /* MPLS carries UN address in next hop */ + rfapiNexthop2Prefix(bpi->attr, &p); + if (p.family != AF_UNSPEC) { + rfapiQprefix2Raddr(&p, &new->un_address); + have_vnc_tunnel_un = 1; + } + } + + for (pEncap = bpi->attr->encap_subtlvs; pEncap; pEncap = pEncap->next) { + switch (pEncap->type) { + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + /* + * Overrides ENCAP UN address, if any + */ + switch (pEncap->length) { + + case 8: + new->un_address.addr_family = AF_INET; + memcpy(&new->un_address.addr.v4, pEncap->value, + 4); + have_vnc_tunnel_un = 1; + break; + + case 20: + new->un_address.addr_family = AF_INET6; + memcpy(&new->un_address.addr.v6, pEncap->value, + 16); + have_vnc_tunnel_un = 1; + break; + + default: + zlog_warn( + "%s: invalid tunnel subtlv UN addr length (%d) for bpi %p", + __func__, pEncap->length, bpi); + } + break; + + default: + zlog_warn("%s: unknown Encap Attribute option type %d", + __func__, pEncap->type); + break; + } + } + + new->un_options = rfapi_encap_tlv_to_un_option(bpi->attr); + +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: line %d: have_vnc_tunnel_un=%d", __func__, + __LINE__, have_vnc_tunnel_un); +#endif + + if (!have_vnc_tunnel_un && bpi->extra) { + /* + * use cached UN address from ENCAP route + */ + new->un_address.addr_family = bpi->extra->vnc.import.un_family; + switch (new->un_address.addr_family) { + case AF_INET: + new->un_address.addr.v4 = + bpi->extra->vnc.import.un.addr4; + break; + case AF_INET6: + new->un_address.addr.v6 = + bpi->extra->vnc.import.un.addr6; + break; + default: + zlog_warn("%s: invalid UN addr family (%d) for bpi %p", + __func__, new->un_address.addr_family, bpi); + rfapi_free_next_hop_list(new); + return NULL; + } + } + + new->lifetime = lifetime; + return new; +} + +int rfapiHasNonRemovedRoutes(struct agg_node *rn) +{ + struct bgp_path_info *bpi; + + for (bpi = rn->info; bpi; bpi = bpi->next) { + struct prefix pfx; + + if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) + && (bpi->extra && !rfapiGetUnAddrOfVpnBi(bpi, &pfx))) { + + return 1; + } + } + return 0; +} + +#ifdef DEBUG_IT_NODES +/* + * DEBUG FUNCTION + */ +void rfapiDumpNode(struct agg_node *rn) +{ + struct bgp_path_info *bpi; + + vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn); + for (bpi = rn->info; bpi; bpi = bpi->next) { + struct prefix pfx; + int ctrc = rfapiGetUnAddrOfVpnBi(bpi, &pfx); + int nr; + + if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) + && (bpi->extra && !ctrc)) { + + nr = 1; + } else { + nr = 0; + } + + vnc_zlog_debug_verbose( + " bpi=%p, nr=%d, flags=0x%x, extra=%p, ctrc=%d", bpi, + nr, bpi->flags, bpi->extra, ctrc); + } +} +#endif + +static int rfapiNhlAddNodeRoutes( + struct agg_node *rn, /* in */ + struct rfapi_ip_prefix *rprefix, /* in */ + uint32_t lifetime, /* in */ + int removed, /* in */ + struct rfapi_next_hop_entry **head, /* in/out */ + struct rfapi_next_hop_entry **tail, /* in/out */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_node *rfd_rib_node, /* preload this NVE rib node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct bgp_path_info *bpi; + struct rfapi_next_hop_entry *new; + struct prefix pfx_un; + struct skiplist *seen_nexthops; + int count = 0; + const struct prefix *p = agg_node_get_prefix(rn); + int is_l2 = (p->family == AF_ETHERNET); + + if (rfd_rib_node) { + struct agg_table *atable = agg_get_table(rfd_rib_node); + struct rfapi_descriptor *rfd; + + if (atable) { + rfd = agg_get_table_info(atable); + + if (rfapiRibFTDFilterRecentPrefix(rfd, rn, + pfx_target_original)) + return 0; + } + } + + seen_nexthops = + skiplist_new(0, vnc_prefix_cmp, prefix_free_lists); + + for (bpi = rn->info; bpi; bpi = bpi->next) { + + struct prefix pfx_vn; + struct prefix *newpfx; + + if (removed && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { +#ifdef DEBUG_RETURNED_NHL + vnc_zlog_debug_verbose( + "%s: want holddown, this route not holddown, skip", + __func__); +#endif + continue; + } + if (!removed && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + continue; + } + + if (!bpi->extra) { + continue; + } + + /* + * Check for excluded VN address + */ + if (rfapiVpnBiNhEqualsPt(bpi, exclude_vnaddr)) + continue; + + /* + * Check for VN address (nexthop) copied already + */ + if (is_l2) { + /* L2 routes: semantic nexthop in aux_prefix; VN addr + * ain't it */ + pfx_vn = bpi->extra->vnc.import.aux_prefix; + } else { + rfapiNexthop2Prefix(bpi->attr, &pfx_vn); + } + if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) { +#ifdef DEBUG_RETURNED_NHL + vnc_zlog_debug_verbose( + "%s: already put VN/nexthop %pFX, skip", + __func__, &pfx_vn); +#endif + continue; + } + + if (rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) { +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose( + "%s: failed to get UN address of this VPN bpi", + __func__); +#endif + continue; + } + + newpfx = prefix_new(); + *newpfx = pfx_vn; + skiplist_insert(seen_nexthops, newpfx, newpfx); + + new = rfapiRouteInfo2NextHopEntry(rprefix, bpi, lifetime, rn); + if (new) { + if (rfapiRibPreloadBi(rfd_rib_node, &pfx_vn, &pfx_un, + lifetime, bpi)) { + /* duplicate filtered by RIB */ + rfapi_free_next_hop_list(new); + new = NULL; + } + } + + if (new) { + if (*tail) { + (*tail)->next = new; + } else { + *head = new; + } + *tail = new; + ++count; + } + } + + skiplist_free(seen_nexthops); + + return count; +} + + +/* + * Breadth-first + * + * omit_node is meant for the situation where we are adding a subtree + * of a parent of some original requested node. The response already + * contains the original requested node, and we don't want to duplicate + * its routes in the list, so we skip it if the right or left node + * matches (of course, we still travel down its child subtrees). + */ +static int rfapiNhlAddSubtree( + struct agg_node *rn, /* in */ + uint32_t lifetime, /* in */ + struct rfapi_next_hop_entry **head, /* in/out */ + struct rfapi_next_hop_entry **tail, /* in/out */ + struct agg_node *omit_node, /* in */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload here */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_ip_prefix rprefix; + int rcount = 0; + + /* FIXME: need to find a better way here to work without sticking our + * hands in node->link */ + if (agg_node_left(rn) && agg_node_left(rn) != omit_node) { + if (agg_node_left(rn)->info) { + const struct prefix *p = + agg_node_get_prefix(agg_node_left(rn)); + int count = 0; + struct agg_node *rib_rn = NULL; + + rfapiQprefix2Rprefix(p, &rprefix); + if (rfd_rib_table) + rib_rn = agg_node_get(rfd_rib_table, p); + + count = rfapiNhlAddNodeRoutes( + agg_node_left(rn), &rprefix, lifetime, 0, head, + tail, exclude_vnaddr, rib_rn, + pfx_target_original); + if (!count) { + count = rfapiNhlAddNodeRoutes( + agg_node_left(rn), &rprefix, lifetime, + 1, head, tail, exclude_vnaddr, rib_rn, + pfx_target_original); + } + rcount += count; + if (rib_rn) + agg_unlock_node(rib_rn); + } + } + + if (agg_node_right(rn) && agg_node_right(rn) != omit_node) { + if (agg_node_right(rn)->info) { + const struct prefix *p = + agg_node_get_prefix(agg_node_right(rn)); + int count = 0; + struct agg_node *rib_rn = NULL; + + rfapiQprefix2Rprefix(p, &rprefix); + if (rfd_rib_table) + rib_rn = agg_node_get(rfd_rib_table, p); + + count = rfapiNhlAddNodeRoutes( + agg_node_right(rn), &rprefix, lifetime, 0, head, + tail, exclude_vnaddr, rib_rn, + pfx_target_original); + if (!count) { + count = rfapiNhlAddNodeRoutes( + agg_node_right(rn), &rprefix, lifetime, + 1, head, tail, exclude_vnaddr, rib_rn, + pfx_target_original); + } + rcount += count; + if (rib_rn) + agg_unlock_node(rib_rn); + } + } + + if (agg_node_left(rn)) { + rcount += rfapiNhlAddSubtree( + agg_node_left(rn), lifetime, head, tail, omit_node, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + } + if (agg_node_right(rn)) { + rcount += rfapiNhlAddSubtree( + agg_node_right(rn), lifetime, head, tail, omit_node, + exclude_vnaddr, rfd_rib_table, pfx_target_original); + } + + return rcount; +} + +/* + * Implementation of ROUTE_LIST(node) from RFAPI-Import-Event-Handling.txt + * + * Construct an rfapi nexthop list based on the routes attached to + * the specified node. + * + * If there are any routes that do NOT have BGP_PATH_REMOVED set, + * return those only. If there are ONLY routes with BGP_PATH_REMOVED, + * then return those, and also include all the non-removed routes from the + * next less-specific node (i.e., this node's parent) at the end. + */ +struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( + struct agg_node *rn, uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload here */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_ip_prefix rprefix; + struct rfapi_next_hop_entry *answer = NULL; + struct rfapi_next_hop_entry *last = NULL; + struct agg_node *parent; + const struct prefix *p = agg_node_get_prefix(rn); + int count = 0; + struct agg_node *rib_rn; + +#ifdef DEBUG_RETURNED_NHL + vnc_zlog_debug_verbose("%s: called with node pfx=%rRN", __func__, rn); + rfapiDebugBacktrace(); +#endif + + rfapiQprefix2Rprefix(p, &rprefix); + + rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL; + + /* + * Add non-withdrawn routes at this node + */ + count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 0, &answer, &last, + exclude_vnaddr, rib_rn, + pfx_target_original); + + /* + * If the list has at least one entry, it's finished + */ + if (count) { + count += rfapiNhlAddSubtree(rn, lifetime, &answer, &last, NULL, + exclude_vnaddr, rfd_rib_table, + pfx_target_original); + vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, + count, answer); +#ifdef DEBUG_RETURNED_NHL + rfapiPrintNhl(NULL, answer); +#endif + if (rib_rn) + agg_unlock_node(rib_rn); + return answer; + } + + /* + * Add withdrawn routes at this node + */ + count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 1, &answer, &last, + exclude_vnaddr, rib_rn, + pfx_target_original); + if (rib_rn) + agg_unlock_node(rib_rn); + + // rfapiPrintNhl(NULL, answer); + + /* + * walk up the tree until we find a node with non-deleted + * routes, then add them + */ + for (parent = agg_node_parent(rn); parent; + parent = agg_node_parent(parent)) { + if (rfapiHasNonRemovedRoutes(parent)) { + break; + } + } + + /* + * Add non-withdrawn routes from less-specific prefix + */ + if (parent) { + const struct prefix *p = agg_node_get_prefix(parent); + + rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL; + rfapiQprefix2Rprefix(p, &rprefix); + count += rfapiNhlAddNodeRoutes(parent, &rprefix, lifetime, 0, + &answer, &last, exclude_vnaddr, + rib_rn, pfx_target_original); + count += rfapiNhlAddSubtree(parent, lifetime, &answer, &last, + rn, exclude_vnaddr, rfd_rib_table, + pfx_target_original); + if (rib_rn) + agg_unlock_node(rib_rn); + } else { + /* + * There is no parent with non-removed routes. Still need to + * add subtree of original node if it contributed routes to the + * answer. + */ + if (count) + count += rfapiNhlAddSubtree(rn, lifetime, &answer, + &last, rn, exclude_vnaddr, + rfd_rib_table, + pfx_target_original); + } + + vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, count, + answer); +#ifdef DEBUG_RETURNED_NHL + rfapiPrintNhl(NULL, answer); +#endif + return answer; +} + +/* + * Construct nexthop list of all routes in table + */ +struct rfapi_next_hop_entry *rfapiRouteTable2NextHopList( + struct agg_table *rt, uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original) /* query target */ +{ + struct agg_node *rn; + struct rfapi_next_hop_entry *biglist = NULL; + struct rfapi_next_hop_entry *nhl; + struct rfapi_next_hop_entry *tail = NULL; + int count = 0; + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + + nhl = rfapiRouteNode2NextHopList(rn, lifetime, exclude_vnaddr, + rfd_rib_table, + pfx_target_original); + if (!tail) { + tail = biglist = nhl; + if (tail) + count = 1; + } else { + tail->next = nhl; + } + if (tail) { + while (tail->next) { + ++count; + tail = tail->next; + } + } + } + + vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count); + return biglist; +} + +struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList( + struct agg_node *rn, struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload NVE rib table */ + struct prefix *pfx_target_original) /* query target */ +{ + int count = 0; + struct rfapi_next_hop_entry *answer = NULL; + struct rfapi_next_hop_entry *last = NULL; + struct agg_node *rib_rn; + + rib_rn = rfd_rib_table + ? agg_node_get(rfd_rib_table, agg_node_get_prefix(rn)) + : NULL; + + count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 0, &answer, &last, + NULL, rib_rn, pfx_target_original); + +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: node %p: %d non-holddown routes", __func__, + rn, count); +#endif + + if (!count) { + count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 1, &answer, + &last, exclude_vnaddr, rib_rn, + pfx_target_original); + vnc_zlog_debug_verbose("%s: node %p: %d holddown routes", + __func__, rn, count); + } + + if (rib_rn) + agg_unlock_node(rib_rn); + +#ifdef DEBUG_RETURNED_NHL + rfapiPrintNhl(NULL, answer); +#endif + + return answer; +} + + +/* + * Construct nexthop list of all routes in table + */ +struct rfapi_next_hop_entry *rfapiEthRouteTable2NextHopList( + uint32_t logical_net_id, struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload NVE rib node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct rfapi_import_table *it; + struct bgp *bgp = bgp_get_default(); + struct agg_table *rt; + struct agg_node *rn; + struct rfapi_next_hop_entry *biglist = NULL; + struct rfapi_next_hop_entry *nhl; + struct rfapi_next_hop_entry *tail = NULL; + int count = 0; + + + it = rfapiMacImportTableGet(bgp, logical_net_id); + rt = it->imported_vpn[AFI_L2VPN]; + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + + nhl = rfapiEthRouteNode2NextHopList( + rn, rprefix, lifetime, exclude_vnaddr, rfd_rib_table, + pfx_target_original); + if (!tail) { + tail = biglist = nhl; + if (tail) + count = 1; + } else { + tail->next = nhl; + } + if (tail) { + while (tail->next) { + ++count; + tail = tail->next; + } + } + } + + vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count); + return biglist; +} + +/* + * Insert a new bpi to the imported route table node, + * keeping the list of BPIs sorted best route first + */ +static void rfapiBgpInfoAttachSorted(struct agg_node *rn, + struct bgp_path_info *info_new, afi_t afi, + safi_t safi) +{ + struct bgp *bgp; + struct bgp_path_info *prev; + struct bgp_path_info *next; + char pfx_buf[PREFIX2STR_BUFFER] = {}; + + + bgp = bgp_get_default(); /* assume 1 instance for now */ + + if (VNC_DEBUG(IMPORT_BI_ATTACH)) { + vnc_zlog_debug_verbose("%s: info_new->peer=%p", __func__, + info_new->peer); + vnc_zlog_debug_verbose("%s: info_new->peer->su_remote=%p", + __func__, info_new->peer->su_remote); + } + + for (prev = NULL, next = rn->info; next; + prev = next, next = next->next) { + enum bgp_path_selection_reason reason; + + if (!bgp + || (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED) + && CHECK_FLAG(next->flags, BGP_PATH_REMOVED)) + || bgp_path_info_cmp_compatible(bgp, info_new, next, + pfx_buf, afi, safi, + &reason) + == -1) { /* -1 if 1st is better */ + break; + } + } + vnc_zlog_debug_verbose("%s: prev=%p, next=%p", __func__, prev, next); + if (prev) { + prev->next = info_new; + } else { + rn->info = info_new; + } + info_new->prev = prev; + info_new->next = next; + if (next) + next->prev = info_new; + bgp_attr_intern(info_new->attr); +} + +static void rfapiBgpInfoDetach(struct agg_node *rn, struct bgp_path_info *bpi) +{ + /* + * Remove the route (doubly-linked) + */ + // bgp_attr_unintern (&bpi->attr); + if (bpi->next) + bpi->next->prev = bpi->prev; + if (bpi->prev) + bpi->prev->next = bpi->next; + else + rn->info = bpi->next; +} + +/* + * For L3-indexed import tables + */ +static int rfapi_bi_peer_rd_cmp(const void *b1, const void *b2) +{ + const struct bgp_path_info *bpi1 = b1; + const struct bgp_path_info *bpi2 = b2; + + /* + * Compare peers + */ + if (bpi1->peer < bpi2->peer) + return -1; + if (bpi1->peer > bpi2->peer) + return 1; + + /* + * compare RDs + */ + return vnc_prefix_cmp( + (const struct prefix *)&bpi1->extra->vnc.import.rd, + (const struct prefix *)&bpi2->extra->vnc.import.rd); +} + +/* + * For L2-indexed import tables + * The BPIs in these tables should ALWAYS have an aux_prefix set because + * they arrive via IPv4 or IPv6 advertisements. + */ +static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2) +{ + const struct bgp_path_info *bpi1 = b1; + const struct bgp_path_info *bpi2 = b2; + int rc; + + /* + * Compare peers + */ + if (bpi1->peer < bpi2->peer) + return -1; + if (bpi1->peer > bpi2->peer) + return 1; + + /* + * compare RDs + */ + rc = vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc.import.rd, + (struct prefix *)&bpi2->extra->vnc.import.rd); + if (rc) { + return rc; + } + + /* + * L2 import tables can have multiple entries with the + * same MAC address, same RD, but different L3 addresses. + * + * Use presence of aux_prefix with AF=ethernet and prefixlen=1 + * as magic value to signify explicit wildcarding of the aux_prefix. + * This magic value will not appear in bona fide bpi entries in + * the import table, but is allowed in the "fake" bpi used to + * probe the table when searching. (We have to test both b1 and b2 + * because there is no guarantee of the order the test key and + * the real key will be passed) + */ + if ((bpi1->extra->vnc.import.aux_prefix.family == AF_ETHERNET + && (bpi1->extra->vnc.import.aux_prefix.prefixlen == 1)) + || (bpi2->extra->vnc.import.aux_prefix.family == AF_ETHERNET + && (bpi2->extra->vnc.import.aux_prefix.prefixlen == 1))) { + + /* + * wildcard aux address specified + */ + return 0; + } + + return vnc_prefix_cmp(&bpi1->extra->vnc.import.aux_prefix, + &bpi2->extra->vnc.import.aux_prefix); +} + + +/* + * Index on RD and Peer + */ +static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */ + struct bgp_path_info *bpi) /* new BPI */ +{ + struct skiplist *sl; + const struct prefix *p; + + assert(rn); + assert(bpi); + assert(bpi->extra); + + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, + bpi->peer, &bpi->extra->vnc.import.rd); + + sl = RFAPI_RDINDEX_W_ALLOC(rn); + if (!sl) { + p = agg_node_get_prefix(rn); + if (AF_ETHERNET == p->family) { + sl = skiplist_new(0, rfapi_bi_peer_rd_aux_cmp, NULL); + } else { + sl = skiplist_new(0, rfapi_bi_peer_rd_cmp, NULL); + } + RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd = sl; + agg_lock_node(rn); /* for skiplist */ + } + assert(!skiplist_insert(sl, (void *)bpi, (void *)bpi)); + agg_lock_node(rn); /* for skiplist entry */ + + /* NB: BPIs in import tables are not refcounted */ +} + +static void rfapiItBiIndexDump(struct agg_node *rn) +{ + struct skiplist *sl; + void *cursor = NULL; + struct bgp_path_info *k; + struct bgp_path_info *v; + int rc; + + sl = RFAPI_RDINDEX(rn); + if (!sl) + return; + + for (rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor); !rc; + rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor)) { + + char buf[RD_ADDRSTRLEN]; + char buf_aux_pfx[PREFIX_STRLEN]; + + prefix_rd2str( + &k->extra->vnc.import.rd, buf, sizeof(buf), + bgp_get_asnotation(k->peer ? k->peer->bgp : NULL)); + if (k->extra->vnc.import.aux_prefix.family) { + prefix2str(&k->extra->vnc.import.aux_prefix, + buf_aux_pfx, sizeof(buf_aux_pfx)); + } else + strlcpy(buf_aux_pfx, "(none)", sizeof(buf_aux_pfx)); + + vnc_zlog_debug_verbose("bpi %p, peer %p, rd %s, aux_prefix %s", + k, k->peer, buf, buf_aux_pfx); + } +} + +static struct bgp_path_info *rfapiItBiIndexSearch( + struct agg_node *rn, /* Import table VPN node */ + struct prefix_rd *prd, struct peer *peer, + const struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */ +{ + struct skiplist *sl; + int rc; + struct bgp_path_info bpi_fake = {0}; + struct bgp_path_info_extra bpi_extra = {0}; + struct bgp_path_info *bpi_result; + + sl = RFAPI_RDINDEX(rn); + if (!sl) + return NULL; + +#ifdef DEBUG_BI_SEARCH + { + char buf_aux_pfx[PREFIX_STRLEN]; + + if (aux_prefix) { + prefix2str(aux_prefix, buf_aux_pfx, + sizeof(buf_aux_pfx)); + } else + strlcpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx)); + + vnc_zlog_debug_verbose( + "%s want prd=%pRDP, peer=%p, aux_prefix=%s", __func__, + prd, peer, buf_aux_pfx); + rfapiItBiIndexDump(rn); + } +#endif + + /* threshold is a WAG */ + if (sl->count < 3) { +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose("%s: short list algorithm", __func__); +#endif + /* if short list, linear search might be faster */ + for (bpi_result = rn->info; bpi_result; + bpi_result = bpi_result->next) { +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose( + "%s: bpi has prd=%pRDP, peer=%p", __func__, + &bpi_result->extra->vnc.import.rd, + bpi_result->peer); +#endif + if (peer == bpi_result->peer + && !prefix_cmp((struct prefix *)&bpi_result->extra + ->vnc.import.rd, + (struct prefix *)prd)) { + +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose( + "%s: peer and RD same, doing aux_prefix check", + __func__); +#endif + if (!aux_prefix + || !prefix_cmp( + aux_prefix, + &bpi_result->extra->vnc.import + .aux_prefix)) { + +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose("%s: match", + __func__); +#endif + break; + } + } + } + return bpi_result; + } + + bpi_fake.peer = peer; + bpi_fake.extra = &bpi_extra; + bpi_fake.extra->vnc.import.rd = *prd; + if (aux_prefix) { + bpi_fake.extra->vnc.import.aux_prefix = *aux_prefix; + } else { + /* wildcard */ + bpi_fake.extra->vnc.import.aux_prefix.family = AF_ETHERNET; + bpi_fake.extra->vnc.import.aux_prefix.prefixlen = 1; + } + + rc = skiplist_search(sl, (void *)&bpi_fake, (void *)&bpi_result); + + if (rc) { +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose("%s: no match", __func__); +#endif + return NULL; + } + +#ifdef DEBUG_BI_SEARCH + vnc_zlog_debug_verbose("%s: matched bpi=%p", __func__, bpi_result); +#endif + + return bpi_result; +} + +static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */ + struct bgp_path_info *bpi) /* old BPI */ +{ + struct skiplist *sl; + int rc; + + vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi, + bpi->peer, &bpi->extra->vnc.import.rd); + + sl = RFAPI_RDINDEX(rn); + assert(sl); + + rc = skiplist_delete(sl, (void *)(bpi), (void *)bpi); + if (rc) { + rfapiItBiIndexDump(rn); + } + assert(!rc); + + agg_unlock_node(rn); /* for skiplist entry */ + + /* NB: BPIs in import tables are not refcounted */ +} + +/* + * Add a backreference at the ENCAP node to the VPN route that + * refers to it + */ +static void +rfapiMonitorEncapAdd(struct rfapi_import_table *import_table, + struct prefix *p, /* VN address */ + struct agg_node *vpn_rn, /* VPN node */ + struct bgp_path_info *vpn_bpi) /* VPN bpi/route */ +{ + afi_t afi = family2afi(p->family); + struct agg_node *rn; + struct rfapi_monitor_encap *m; + + assert(afi); + rn = agg_node_get(import_table->imported_encap[afi], p); /* locks rn */ + assert(rn); + + m = XCALLOC(MTYPE_RFAPI_MONITOR_ENCAP, + sizeof(struct rfapi_monitor_encap)); + + m->node = vpn_rn; + m->bpi = vpn_bpi; + m->rn = rn; + + /* insert to encap node's list */ + m->next = RFAPI_MONITOR_ENCAP(rn); + if (m->next) + m->next->prev = m; + RFAPI_MONITOR_ENCAP_W_ALLOC(rn) = m; + + /* for easy lookup when deleting vpn route */ + vpn_bpi->extra->vnc.import.hme = m; + + vnc_zlog_debug_verbose( + "%s: it=%p, vpn_bpi=%p, afi=%d, encap rn=%p, setting vpn_bpi->extra->vnc.import.hme=%p", + __func__, import_table, vpn_bpi, afi, rn, m); + + RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0); + bgp_attr_intern(vpn_bpi->attr); +} + +static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi) +{ + /* + * Remove encap monitor + */ + vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi); + if (vpn_bpi->extra) { + struct rfapi_monitor_encap *hme = + vpn_bpi->extra->vnc.import.hme; + + if (hme) { + + vnc_zlog_debug_verbose("%s: hme=%p", __func__, hme); + + /* Refcount checking takes too long here */ + // RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 0); + if (hme->next) + hme->next->prev = hme->prev; + if (hme->prev) + hme->prev->next = hme->next; + else + RFAPI_MONITOR_ENCAP_W_ALLOC(hme->rn) = + hme->next; + /* Refcount checking takes too long here */ + // RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 1); + + /* see if the struct rfapi_it_extra is empty and can be + * freed */ + rfapiMonitorExtraPrune(SAFI_ENCAP, hme->rn); + + agg_unlock_node(hme->rn); /* decr ref count */ + XFREE(MTYPE_RFAPI_MONITOR_ENCAP, hme); + vpn_bpi->extra->vnc.import.hme = NULL; + } + } +} + +/* + * Timer callback for withdraw + */ +static void rfapiWithdrawTimerVPN(struct event *t) +{ + struct rfapi_withdraw *wcb = EVENT_ARG(t); + struct bgp_path_info *bpi = wcb->info; + struct bgp *bgp = bgp_get_default(); + const struct prefix *p; + struct rfapi_monitor_vpn *moved; + afi_t afi; + bool early_exit = false; + + if (bgp == NULL) { + vnc_zlog_debug_verbose( + "%s: NULL BGP pointer, assume shutdown race condition!!!", + __func__); + early_exit = true; + } + if (bgp && CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) { + vnc_zlog_debug_verbose( + "%s: BGP delete in progress, assume shutdown race condition!!!", + __func__); + early_exit = true; + } + + /* This callback is responsible for the withdraw object's memory */ + if (early_exit) { + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + return; + } + + assert(wcb->node); + assert(bpi); + assert(wcb->import_table); + assert(bpi->extra); + + RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, wcb->lockoffset); + + vnc_zlog_debug_verbose("%s: removing bpi %p at prefix %pRN", __func__, + bpi, wcb->node); + + /* + * Remove the route (doubly-linked) + */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_VALID) + && VALID_INTERIOR_TYPE(bpi->type)) + RFAPI_MONITOR_EXTERIOR(wcb->node)->valid_interior_count--; + + p = agg_node_get_prefix(wcb->node); + afi = family2afi(p->family); + wcb->import_table->holddown_count[afi] -= 1; /* keep count consistent */ + rfapiItBiIndexDel(wcb->node, bpi); + rfapiBgpInfoDetach(wcb->node, bpi); /* with removed bpi */ + + vnc_import_bgp_exterior_del_route_interior(bgp, wcb->import_table, + wcb->node, bpi); + + + /* + * If VNC is configured to send response remove messages, AND + * if the removed route had a UN address, do response removal + * processing. + */ + if (!(bgp->rfapi_cfg->flags + & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) { + + int has_valid_duplicate = 0; + struct bgp_path_info *bpii; + + /* + * First check if there are any OTHER routes at this node + * that have the same nexthop and a valid UN address. If + * there are (e.g., from other peers), then the route isn't + * really gone, so skip sending a response removal message. + */ + for (bpii = wcb->node->info; bpii; bpii = bpii->next) { + if (rfapiVpnBiSamePtUn(bpi, bpii)) { + has_valid_duplicate = 1; + break; + } + } + + vnc_zlog_debug_verbose("%s: has_valid_duplicate=%d", __func__, + has_valid_duplicate); + + if (!has_valid_duplicate) { + rfapiRibPendingDeleteRoute(bgp, wcb->import_table, afi, + wcb->node); + } + } + + rfapiMonitorEncapDelete(bpi); + + /* + * If there are no VPN monitors at this VPN Node A, + * we are done + */ + if (!RFAPI_MONITOR_VPN(wcb->node)) { + vnc_zlog_debug_verbose("%s: no VPN monitors at this node", + __func__); + goto done; + } + + /* + * rfapiMonitorMoveShorter only moves monitors if there are + * no remaining valid routes at the current node + */ + moved = rfapiMonitorMoveShorter(wcb->node, 1); + + if (moved) { + rfapiMonitorMovedUp(wcb->import_table, wcb->node, moved->node, + moved); + } + +done: + /* + * Free VPN bpi + */ + rfapiBgpInfoFree(bpi); + wcb->info = NULL; + + /* + * If route count at this node has gone to 0, withdraw exported prefix + */ + if (!wcb->node->info) { + /* see if the struct rfapi_it_extra is empty and can be freed */ + rfapiMonitorExtraPrune(SAFI_MPLS_VPN, wcb->node); + vnc_direct_bgp_del_prefix(bgp, wcb->import_table, wcb->node); + vnc_zebra_del_prefix(bgp, wcb->import_table, wcb->node); + } else { + /* + * nexthop change event + * vnc_direct_bgp_add_prefix() will recompute the VN addr + * ecommunity + */ + vnc_direct_bgp_add_prefix(bgp, wcb->import_table, wcb->node); + } + + RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset); + agg_unlock_node(wcb->node); /* decr ref count */ + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); +} + +/* + * This works for multiprotocol extension, but not for plain ol' + * unicast IPv4 because that nexthop is stored in attr->nexthop + */ +void rfapiNexthop2Prefix(struct attr *attr, struct prefix *p) +{ + assert(p); + assert(attr); + + memset(p, 0, sizeof(struct prefix)); + + switch (p->family = BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) { + case AF_INET: + p->u.prefix4 = attr->mp_nexthop_global_in; + p->prefixlen = IPV4_MAX_BITLEN; + break; + + case AF_INET6: + p->u.prefix6 = attr->mp_nexthop_global; + p->prefixlen = IPV6_MAX_BITLEN; + break; + + default: + vnc_zlog_debug_verbose("%s: Family is unknown = %d", __func__, + p->family); + } +} + +void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr, struct prefix *p) +{ + if (afi == AFI_IP) { + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4 = attr->nexthop; + } else { + rfapiNexthop2Prefix(attr, p); + } +} + +static int rfapiAttrNexthopAddrDifferent(struct prefix *p1, struct prefix *p2) +{ + if (!p1 || !p2) { + vnc_zlog_debug_verbose("%s: p1 or p2 is NULL", __func__); + return 1; + } + + /* + * Are address families the same? + */ + if (p1->family != p2->family) { + return 1; + } + + switch (p1->family) { + case AF_INET: + if (IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4)) + return 0; + break; + + case AF_INET6: + if (IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6)) + return 0; + break; + + default: + assert(1); + } + + return 1; +} + +static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi, + struct bgp_path_info *vpn_bpi) +{ + if (!vpn_bpi || !vpn_bpi->extra) { + zlog_warn("%s: no vpn bpi attr/extra, can't copy UN address", + __func__); + return; + } + + switch (BGP_MP_NEXTHOP_FAMILY(encap_bpi->attr->mp_nexthop_len)) { + case AF_INET: + + /* + * instrumentation to debug segfault of 091127 + */ + vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi); + vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__, + vpn_bpi->extra); + + vpn_bpi->extra->vnc.import.un_family = AF_INET; + vpn_bpi->extra->vnc.import.un.addr4 = + encap_bpi->attr->mp_nexthop_global_in; + break; + + case AF_INET6: + vpn_bpi->extra->vnc.import.un_family = AF_INET6; + vpn_bpi->extra->vnc.import.un.addr6 = + encap_bpi->attr->mp_nexthop_global; + break; + + default: + zlog_warn("%s: invalid encap nexthop length: %d", __func__, + encap_bpi->attr->mp_nexthop_len); + vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC; + break; + } +} + +/* + * returns 0 on success, nonzero on error + */ +static int +rfapiWithdrawEncapUpdateCachedUn(struct rfapi_import_table *import_table, + struct bgp_path_info *encap_bpi, + struct agg_node *vpn_rn, + struct bgp_path_info *vpn_bpi) +{ + if (!encap_bpi) { + + /* + * clear cached UN address + */ + if (!vpn_bpi || !vpn_bpi->extra) { + zlog_warn( + "%s: missing VPN bpi/extra, can't clear UN addr", + __func__); + return 1; + } + vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC; + memset(&vpn_bpi->extra->vnc.import.un, 0, + sizeof(vpn_bpi->extra->vnc.import.un)); + if (CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) { + if (rfapiGetVncTunnelUnAddr(vpn_bpi->attr, NULL)) { + UNSET_FLAG(vpn_bpi->flags, BGP_PATH_VALID); + if (VALID_INTERIOR_TYPE(vpn_bpi->type)) + RFAPI_MONITOR_EXTERIOR(vpn_rn) + ->valid_interior_count--; + /* signal interior route withdrawal to + * import-exterior */ + vnc_import_bgp_exterior_del_route_interior( + bgp_get_default(), import_table, vpn_rn, + vpn_bpi); + } + } + + } else { + if (!vpn_bpi) { + zlog_warn("%s: missing VPN bpi, can't clear UN addr", + __func__); + return 1; + } + rfapiCopyUnEncap2VPN(encap_bpi, vpn_bpi); + if (!CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) { + SET_FLAG(vpn_bpi->flags, BGP_PATH_VALID); + if (VALID_INTERIOR_TYPE(vpn_bpi->type)) + RFAPI_MONITOR_EXTERIOR(vpn_rn) + ->valid_interior_count++; + /* signal interior route withdrawal to import-exterior + */ + vnc_import_bgp_exterior_add_route_interior( + bgp_get_default(), import_table, vpn_rn, + vpn_bpi); + } + } + return 0; +} + +static void rfapiWithdrawTimerEncap(struct event *t) +{ + struct rfapi_withdraw *wcb = EVENT_ARG(t); + struct bgp_path_info *bpi = wcb->info; + int was_first_route = 0; + struct rfapi_monitor_encap *em; + struct skiplist *vpn_node_sl = skiplist_new(0, NULL, NULL); + + assert(wcb->node); + assert(bpi); + assert(wcb->import_table); + + RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 0); + + if (wcb->node->info == bpi) + was_first_route = 1; + + /* + * Remove the route/bpi and free it + */ + rfapiBgpInfoDetach(wcb->node, bpi); + rfapiBgpInfoFree(bpi); + + if (!was_first_route) + goto done; + + for (em = RFAPI_MONITOR_ENCAP(wcb->node); em; em = em->next) { + + /* + * Update monitoring VPN BPIs with new encap info at the + * head of the encap bpi chain (which could be NULL after + * removing the expiring bpi above) + */ + if (rfapiWithdrawEncapUpdateCachedUn(wcb->import_table, + wcb->node->info, em->node, + em->bpi)) + continue; + + /* + * Build a list of unique VPN nodes referenced by these + * monitors. + * Use a skiplist for speed. + */ + skiplist_insert(vpn_node_sl, em->node, em->node); + } + + + /* + * for each VPN node referenced in the ENCAP monitors: + */ + struct agg_node *rn; + while (!skiplist_first(vpn_node_sl, (void **)&rn, NULL)) { + if (!wcb->node->info) { + struct rfapi_monitor_vpn *moved; + + moved = rfapiMonitorMoveShorter(rn, 0); + if (moved) { + // rfapiDoRouteCallback(wcb->import_table, + // moved->node, moved); + rfapiMonitorMovedUp(wcb->import_table, rn, + moved->node, moved); + } + } else { + // rfapiDoRouteCallback(wcb->import_table, rn, NULL); + rfapiMonitorItNodeChanged(wcb->import_table, rn, NULL); + } + skiplist_delete_first(vpn_node_sl); + } + +done: + RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 1); + agg_unlock_node(wcb->node); /* decr ref count */ + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + skiplist_free(vpn_node_sl); +} + + +/* + * Works for both VPN and ENCAP routes; timer_service_func is different + * in each case + */ +static void +rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table, + struct agg_node *rn, struct bgp_path_info *bpi, + afi_t afi, safi_t safi, + void (*timer_service_func)(struct event *)) +{ + uint32_t lifetime; + struct rfapi_withdraw *wcb; + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + /* + * Already on the path to being withdrawn, + * should already have a timer set up to + * delete it. + */ + vnc_zlog_debug_verbose( + "%s: already being withdrawn, do nothing", __func__); + return; + } + + rfapiGetVncLifetime(bpi->attr, &lifetime); + vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime); + + /* + * withdrawn routes get to hang around for a while + */ + SET_FLAG(bpi->flags, BGP_PATH_REMOVED); + + /* set timer to remove the route later */ + lifetime = rfapiGetHolddownFromLifetime(lifetime); + vnc_zlog_debug_verbose("%s: using timeout %u", __func__, lifetime); + + /* + * Stash import_table, node, and info for use by timer + * service routine, which is supposed to free the wcb. + */ + wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw)); + wcb->node = rn; + wcb->info = bpi; + wcb->import_table = import_table; + bgp_attr_intern(bpi->attr); + + if (VNC_DEBUG(VERBOSE)) { + vnc_zlog_debug_verbose( + "%s: wcb values: node=%p, info=%p, import_table=%p (bpi follows)", + __func__, wcb->node, wcb->info, wcb->import_table); + rfapiPrintBi(NULL, bpi); + } + + + assert(bpi->extra); + if (lifetime > UINT32_MAX / 1001) { + /* sub-optimal case, but will probably never happen */ + bpi->extra->vnc.import.timer = NULL; + event_add_timer(bm->master, timer_service_func, wcb, lifetime, + &bpi->extra->vnc.import.timer); + } else { + static uint32_t jitter; + uint32_t lifetime_msec; + + /* + * the goal here is to spread out the timers so they are + * sortable in the skip list + */ + if (++jitter >= 1000) + jitter = 0; + + lifetime_msec = (lifetime * 1000) + jitter; + + bpi->extra->vnc.import.timer = NULL; + event_add_timer_msec(bm->master, timer_service_func, wcb, + lifetime_msec, + &bpi->extra->vnc.import.timer); + } + + /* re-sort route list (BGP_PATH_REMOVED routes are last) */ + if (((struct bgp_path_info *)rn->info)->next) { + rfapiBgpInfoDetach(rn, bpi); + rfapiBgpInfoAttachSorted(rn, bpi, afi, safi); + } +} + + +typedef void(rfapi_bi_filtered_import_f)(struct rfapi_import_table *table, + int action, struct peer *peer, + void *rfd, const struct prefix *prefix, + const struct prefix *aux_prefix, + afi_t afi, struct prefix_rd *prd, + struct attr *attr, uint8_t type, + uint8_t sub_type, uint32_t *label); + + +static void rfapiExpireEncapNow(struct rfapi_import_table *it, + struct agg_node *rn, struct bgp_path_info *bpi) +{ + struct rfapi_withdraw *wcb; + struct event t; + + /* + * pretend we're an expiring timer + */ + wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw)); + wcb->info = bpi; + wcb->node = rn; + wcb->import_table = it; + memset(&t, 0, sizeof(t)); + t.arg = wcb; + rfapiWithdrawTimerEncap(&t); /* frees wcb */ +} + +static int rfapiGetNexthop(struct attr *attr, struct prefix *prefix) +{ + switch (BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) { + case AF_INET: + prefix->family = AF_INET; + prefix->prefixlen = IPV4_MAX_BITLEN; + prefix->u.prefix4 = attr->mp_nexthop_global_in; + break; + case AF_INET6: + prefix->family = AF_INET6; + prefix->prefixlen = IPV6_MAX_BITLEN; + prefix->u.prefix6 = attr->mp_nexthop_global; + break; + default: + vnc_zlog_debug_verbose("%s: unknown attr->mp_nexthop_len %d", + __func__, attr->mp_nexthop_len); + return EINVAL; + } + return 0; +} + +/* + * import a bgp_path_info if its route target list intersects with the + * import table's route target list + */ +static void rfapiBgpInfoFilteredImportEncap( + struct rfapi_import_table *import_table, int action, struct peer *peer, + void *rfd, /* set for looped back routes */ + const struct prefix *p, + const struct prefix *aux_prefix, /* Unused for encap routes */ + afi_t afi, struct prefix_rd *prd, + struct attr *attr, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ + uint8_t sub_type, /* part of bgp_path_info */ + uint32_t *label) /* part of bgp_path_info */ +{ + struct agg_table *rt = NULL; + struct agg_node *rn; + struct bgp_path_info *info_new; + struct bgp_path_info *bpi; + struct bgp_path_info *next; + char buf[BUFSIZ]; + + struct prefix p_firstbpi_old; + struct prefix p_firstbpi_new; + int replacing = 0; + const char *action_str = NULL; + struct prefix un_prefix; + + struct bgp *bgp; + bgp = bgp_get_default(); /* assume 1 instance for now */ + + switch (action) { + case FIF_ACTION_UPDATE: + action_str = "update"; + break; + case FIF_ACTION_WITHDRAW: + action_str = "withdraw"; + break; + case FIF_ACTION_KILL: + action_str = "kill"; + break; + default: + assert(0); + break; + } + + vnc_zlog_debug_verbose( + "%s: entry: %s: prefix %s/%d", __func__, action_str, + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)), + p->prefixlen); + + memset(&p_firstbpi_old, 0, sizeof(p_firstbpi_old)); + memset(&p_firstbpi_new, 0, sizeof(p_firstbpi_new)); + + if (action == FIF_ACTION_UPDATE) { + /* + * Compare rt lists. If no intersection, don't import this route + * On a withdraw, peer and RD are sufficient to determine if + * we should act. + */ + if (!attr || !bgp_attr_get_ecommunity(attr)) { + + vnc_zlog_debug_verbose( + "%s: attr, extra, or ecommunity missing, not importing", + __func__); + return; + } +#ifdef RFAPI_REQUIRE_ENCAP_BEEC + if (!rfapiEcommunitiesMatchBeec( + bgp_attr_get_ecommunity(attr))) { + vnc_zlog_debug_verbose( + "%s: it=%p: no match for BGP Encapsulation ecommunity", + __func__, import_table); + return; + } +#endif + if (!rfapiEcommunitiesIntersect( + import_table->rt_import_list, + bgp_attr_get_ecommunity(attr))) { + + vnc_zlog_debug_verbose( + "%s: it=%p: no ecommunity intersection", + __func__, import_table); + return; + } + + /* + * Updates must also have a nexthop address + */ + memset(&un_prefix, 0, + sizeof(un_prefix)); /* keep valgrind happy */ + if (rfapiGetNexthop(attr, &un_prefix)) { + vnc_zlog_debug_verbose("%s: missing nexthop address", + __func__); + return; + } + } + + /* + * Figure out which radix tree the route would go into + */ + switch (afi) { + case AFI_IP: + case AFI_IP6: + rt = import_table->imported_encap[afi]; + break; + + case AFI_UNSPEC: + case AFI_L2VPN: + case AFI_MAX: + flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi); + return; + } + + /* + * agg_node_lookup returns a node only if there is at least + * one route attached. + */ + rn = agg_node_lookup(rt, p); + +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: initial encap lookup(it=%p) rn=%p", + __func__, import_table, rn); +#endif + + if (rn) { + + RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 1); + agg_unlock_node(rn); /* undo lock in agg_node_lookup */ + + + /* + * capture nexthop of first bpi + */ + if (rn->info) { + rfapiNexthop2Prefix( + ((struct bgp_path_info *)(rn->info))->attr, + &p_firstbpi_old); + } + + for (bpi = rn->info; bpi; bpi = bpi->next) { + + /* + * Does this bgp_path_info refer to the same route + * as we are trying to add? + */ + vnc_zlog_debug_verbose("%s: comparing BPI %p", __func__, + bpi); + + + /* + * Compare RDs + * + * RD of import table bpi is in + * bpi->extra->vnc.import.rd RD of info_orig is in prd + */ + if (!bpi->extra) { + vnc_zlog_debug_verbose("%s: no bpi->extra", + __func__); + continue; + } + if (prefix_cmp( + (struct prefix *)&bpi->extra->vnc.import.rd, + (struct prefix *)prd)) { + + vnc_zlog_debug_verbose("%s: prd does not match", + __func__); + continue; + } + + /* + * Compare peers + */ + if (bpi->peer != peer) { + vnc_zlog_debug_verbose( + "%s: peer does not match", __func__); + continue; + } + + vnc_zlog_debug_verbose("%s: found matching bpi", + __func__); + + /* Same route. Delete this bpi, replace with new one */ + + if (action == FIF_ACTION_WITHDRAW) { + + vnc_zlog_debug_verbose( + "%s: withdrawing at prefix %pRN", + __func__, rn); + + rfapiBiStartWithdrawTimer( + import_table, rn, bpi, afi, SAFI_ENCAP, + rfapiWithdrawTimerEncap); + + } else { + vnc_zlog_debug_verbose( + "%s: %s at prefix %pRN", __func__, + ((action == FIF_ACTION_KILL) + ? "killing" + : "replacing"), + rn); + + /* + * If this route is waiting to be deleted + * because of + * a previous withdraw, we must cancel its + * timer. + */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) + && bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = EVENT_ARG( + bpi->extra->vnc.import.timer); + + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + EVENT_OFF(bpi->extra->vnc.import.timer); + } + + if (action == FIF_ACTION_UPDATE) { + rfapiBgpInfoDetach(rn, bpi); + rfapiBgpInfoFree(bpi); + replacing = 1; + } else { + /* + * Kill: do export stuff when removing + * bpi + */ + struct rfapi_withdraw *wcb; + struct event t; + + /* + * pretend we're an expiring timer + */ + wcb = XCALLOC( + MTYPE_RFAPI_WITHDRAW, + sizeof(struct rfapi_withdraw)); + wcb->info = bpi; + wcb->node = rn; + wcb->import_table = import_table; + memset(&t, 0, sizeof(t)); + t.arg = wcb; + rfapiWithdrawTimerEncap( + &t); /* frees wcb */ + } + } + + break; + } + } + + if (rn) + RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, replacing ? 1 : 0); + + if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) + return; + + info_new = + rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, NULL); + + if (rn) { + if (!replacing) + agg_lock_node(rn); /* incr ref count for new BPI */ + } else { + rn = agg_node_get(rt, p); + } + + vnc_zlog_debug_verbose("%s: (afi=%d, rn=%p) inserting at prefix %pRN", + __func__, afi, rn, rn); + + rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_ENCAP); + + /* + * Delete holddown routes from same NVE. See details in + * rfapiBgpInfoFilteredImportVPN() + */ + for (bpi = info_new->next; bpi; bpi = next) { + + struct prefix pfx_un; + int un_match = 0; + + next = bpi->next; + if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + /* + * We already match the VN address (it is the prefix + * of the route node) + */ + + if (!rfapiGetNexthop(bpi->attr, &pfx_un) + && prefix_same(&pfx_un, &un_prefix)) { + + un_match = 1; + } + + if (!un_match) + continue; + + vnc_zlog_debug_verbose( + "%s: removing holddown bpi matching NVE of new route", + __func__); + if (bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = + EVENT_ARG(bpi->extra->vnc.import.timer); + + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + EVENT_OFF(bpi->extra->vnc.import.timer); + } + rfapiExpireEncapNow(import_table, rn, bpi); + } + + rfapiNexthop2Prefix(((struct bgp_path_info *)(rn->info))->attr, + &p_firstbpi_new); + + /* + * If the nexthop address of the selected Encap route (i.e., + * the UN address) has changed, then we must update the VPN + * routes that refer to this Encap route and possibly force + * rfapi callbacks. + */ + if (rfapiAttrNexthopAddrDifferent(&p_firstbpi_old, &p_firstbpi_new)) { + + struct rfapi_monitor_encap *m; + struct rfapi_monitor_encap *mnext; + + struct agg_node *referenced_vpn_prefix; + + /* + * Optimized approach: build radix tree on the fly to + * hold list of VPN nodes referenced by the ENCAP monitors + * + * The nodes in this table correspond to prefixes of VPN routes. + * The "info" pointer of the node points to a chain of + * struct rfapi_monitor_encap, each of which refers to a + * specific VPN node. + */ + struct agg_table *referenced_vpn_table; + + referenced_vpn_table = agg_table_init(); + +/* + * iterate over the set of monitors at this ENCAP node. + */ +#ifdef DEBUG_ENCAP_MONITOR + vnc_zlog_debug_verbose("%s: examining monitors at rn=%p", + __func__, rn); +#endif + for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) { + const struct prefix *p; + + /* + * For each referenced bpi/route, copy the ENCAP route's + * nexthop to the VPN route's cached UN address field + * and set + * the address family of the cached UN address field. + */ + rfapiCopyUnEncap2VPN(info_new, m->bpi); + if (!CHECK_FLAG(m->bpi->flags, BGP_PATH_VALID)) { + SET_FLAG(m->bpi->flags, BGP_PATH_VALID); + if (VALID_INTERIOR_TYPE(m->bpi->type)) + RFAPI_MONITOR_EXTERIOR(m->node) + ->valid_interior_count++; + vnc_import_bgp_exterior_add_route_interior( + bgp, import_table, m->node, m->bpi); + } + + /* + * Build a list of unique VPN nodes referenced by these + * monitors + * + * There could be more than one VPN node here with a + * given + * prefix. Those are currently in an unsorted linear + * list + * per prefix. + */ + p = agg_node_get_prefix(m->node); + referenced_vpn_prefix = + agg_node_get(referenced_vpn_table, p); + assert(referenced_vpn_prefix); + for (mnext = referenced_vpn_prefix->info; mnext; + mnext = mnext->next) { + + if (mnext->node == m->node) + break; + } + + if (mnext) { + /* + * already have an entry for this VPN node + */ + agg_unlock_node(referenced_vpn_prefix); + } else { + mnext = XCALLOC( + MTYPE_RFAPI_MONITOR_ENCAP, + sizeof(struct rfapi_monitor_encap)); + mnext->node = m->node; + mnext->next = referenced_vpn_prefix->info; + referenced_vpn_prefix->info = mnext; + } + } + + /* + * for each VPN node referenced in the ENCAP monitors: + */ + for (referenced_vpn_prefix = + agg_route_top(referenced_vpn_table); + referenced_vpn_prefix; + referenced_vpn_prefix = + agg_route_next(referenced_vpn_prefix)) { + + while ((m = referenced_vpn_prefix->info)) { + + struct agg_node *n; + + rfapiMonitorMoveLonger(m->node); + for (n = m->node; n; n = agg_node_parent(n)) { + // rfapiDoRouteCallback(import_table, n, + // NULL); + } + rfapiMonitorItNodeChanged(import_table, m->node, + NULL); + + referenced_vpn_prefix->info = m->next; + agg_unlock_node(referenced_vpn_prefix); + XFREE(MTYPE_RFAPI_MONITOR_ENCAP, m); + } + } + agg_table_finish(referenced_vpn_table); + } + + RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0); +} + +static void rfapiExpireVpnNow(struct rfapi_import_table *it, + struct agg_node *rn, struct bgp_path_info *bpi, + int lockoffset) +{ + struct rfapi_withdraw *wcb; + struct event t; + + /* + * pretend we're an expiring timer + */ + wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw)); + wcb->info = bpi; + wcb->node = rn; + wcb->import_table = it; + wcb->lockoffset = lockoffset; + memset(&t, 0, sizeof(t)); + t.arg = wcb; + rfapiWithdrawTimerVPN(&t); /* frees wcb */ +} + + +/* + * import a bgp_path_info if its route target list intersects with the + * import table's route target list + */ +void rfapiBgpInfoFilteredImportVPN( + struct rfapi_import_table *import_table, int action, struct peer *peer, + void *rfd, /* set for looped back routes */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ + afi_t afi, struct prefix_rd *prd, + struct attr *attr, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ + uint8_t sub_type, /* part of bgp_path_info */ + uint32_t *label) /* part of bgp_path_info */ +{ + struct agg_table *rt = NULL; + struct agg_node *rn; + struct agg_node *n; + struct bgp_path_info *info_new; + struct bgp_path_info *bpi; + struct bgp_path_info *next; + char buf[BUFSIZ]; + struct prefix vn_prefix; + struct prefix un_prefix; + int un_prefix_valid = 0; + struct agg_node *ern; + int replacing = 0; + int original_had_routes = 0; + struct prefix original_nexthop; + const char *action_str = NULL; + int is_it_ce = 0; + + struct bgp *bgp; + bgp = bgp_get_default(); /* assume 1 instance for now */ + + switch (action) { + case FIF_ACTION_UPDATE: + action_str = "update"; + break; + case FIF_ACTION_WITHDRAW: + action_str = "withdraw"; + break; + case FIF_ACTION_KILL: + action_str = "kill"; + break; + default: + assert(0); + break; + } + + if (import_table == bgp->rfapi->it_ce) + is_it_ce = 1; + + vnc_zlog_debug_verbose("%s: entry: %s%s: prefix %s/%d: it %p, afi %s", + __func__, (is_it_ce ? "CE-IT " : ""), action_str, + rfapi_ntop(p->family, &p->u.prefix, buf, BUFSIZ), + p->prefixlen, import_table, afi2str(afi)); + + VNC_ITRCCK; + + /* + * Compare rt lists. If no intersection, don't import this route + * On a withdraw, peer and RD are sufficient to determine if + * we should act. + */ + if (action == FIF_ACTION_UPDATE) { + if (!attr || !bgp_attr_get_ecommunity(attr)) { + + vnc_zlog_debug_verbose( + "%s: attr, extra, or ecommunity missing, not importing", + __func__); + return; + } + if ((import_table != bgp->rfapi->it_ce) && + !rfapiEcommunitiesIntersect( + import_table->rt_import_list, + bgp_attr_get_ecommunity(attr))) { + + vnc_zlog_debug_verbose( + "%s: it=%p: no ecommunity intersection", + __func__, import_table); + return; + } + + memset(&vn_prefix, 0, + sizeof(vn_prefix)); /* keep valgrind happy */ + if (rfapiGetNexthop(attr, &vn_prefix)) { + /* missing nexthop address would be a bad, bad thing */ + vnc_zlog_debug_verbose("%s: missing nexthop", __func__); + return; + } + } + + /* + * Figure out which radix tree the route would go into + */ + switch (afi) { + case AFI_IP: + case AFI_IP6: + case AFI_L2VPN: + rt = import_table->imported_vpn[afi]; + break; + + case AFI_UNSPEC: + case AFI_MAX: + flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi); + return; + } + + /* clear it */ + memset(&original_nexthop, 0, sizeof(original_nexthop)); + + /* + * agg_node_lookup returns a node only if there is at least + * one route attached. + */ + rn = agg_node_lookup(rt, p); + + vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn); + + if (rn) { + + RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1); + agg_unlock_node(rn); /* undo lock in agg_node_lookup */ + + if (rn->info) + original_had_routes = 1; + + if (VNC_DEBUG(VERBOSE)) { + vnc_zlog_debug_verbose("%s: showing IT node on entry", + __func__); + rfapiShowItNode(NULL, rn); /* debug */ + } + + /* + * Look for same route (will have same RD and peer) + */ + bpi = rfapiItBiIndexSearch(rn, prd, peer, aux_prefix); + + if (bpi) { + + /* + * This was an old test when we iterated over the + * BPIs linearly. Since we're now looking up with + * RD and peer, comparing types should not be + * needed. Changed to assertion. + * + * Compare types. Doing so prevents a RFP-originated + * route from matching an imported route, for example. + */ + if (VNC_DEBUG(VERBOSE) && bpi->type != type) + /* should be handled by RDs, but warn for now */ + zlog_warn("%s: type mismatch! (bpi=%d, arg=%d)", + __func__, bpi->type, type); + + vnc_zlog_debug_verbose("%s: found matching bpi", + __func__); + + /* + * In the special CE table, withdrawals occur without + * holddown + */ + if (import_table == bgp->rfapi->it_ce) { + vnc_direct_bgp_del_route_ce(bgp, rn, bpi); + if (action == FIF_ACTION_WITHDRAW) + action = FIF_ACTION_KILL; + } + + if (action == FIF_ACTION_WITHDRAW) { + + int washolddown = CHECK_FLAG(bpi->flags, + BGP_PATH_REMOVED); + + vnc_zlog_debug_verbose( + "%s: withdrawing at prefix %pRN%s", + __func__, rn, + (washolddown + ? " (already being withdrawn)" + : "")); + + VNC_ITRCCK; + if (!washolddown) { + rfapiBiStartWithdrawTimer( + import_table, rn, bpi, afi, + SAFI_MPLS_VPN, + rfapiWithdrawTimerVPN); + + RFAPI_UPDATE_ITABLE_COUNT( + bpi, import_table, afi, -1); + import_table->holddown_count[afi] += 1; + } + VNC_ITRCCK; + } else { + vnc_zlog_debug_verbose( + "%s: %s at prefix %pRN", __func__, + ((action == FIF_ACTION_KILL) + ? "killing" + : "replacing"), + rn); + + /* + * If this route is waiting to be deleted + * because of + * a previous withdraw, we must cancel its + * timer. + */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) + && bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = EVENT_ARG( + bpi->extra->vnc.import.timer); + + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + EVENT_OFF(bpi->extra->vnc.import.timer); + + import_table->holddown_count[afi] -= 1; + RFAPI_UPDATE_ITABLE_COUNT( + bpi, import_table, afi, 1); + } + /* + * decrement remote count (if route is remote) + * because + * we are going to remove it below + */ + RFAPI_UPDATE_ITABLE_COUNT(bpi, import_table, + afi, -1); + if (action == FIF_ACTION_UPDATE) { + replacing = 1; + + /* + * make copy of original nexthop so we + * can see if it changed + */ + rfapiGetNexthop(bpi->attr, + &original_nexthop); + + /* + * remove bpi without doing any export + * processing + */ + if (CHECK_FLAG(bpi->flags, + BGP_PATH_VALID) + && VALID_INTERIOR_TYPE(bpi->type)) + RFAPI_MONITOR_EXTERIOR(rn) + ->valid_interior_count--; + rfapiItBiIndexDel(rn, bpi); + rfapiBgpInfoDetach(rn, bpi); + rfapiMonitorEncapDelete(bpi); + vnc_import_bgp_exterior_del_route_interior( + bgp, import_table, rn, bpi); + rfapiBgpInfoFree(bpi); + } else { + /* Kill */ + /* + * remove bpi and do export processing + */ + import_table->holddown_count[afi] += 1; + rfapiExpireVpnNow(import_table, rn, bpi, + 0); + } + } + } + } + + if (rn) + RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, replacing ? 1 : 0); + + if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) { + VNC_ITRCCK; + return; + } + + info_new = + rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, label); + + /* + * lookup un address in encap table + */ + ern = agg_node_match(import_table->imported_encap[afi], &vn_prefix); + if (ern) { + rfapiCopyUnEncap2VPN(ern->info, info_new); + agg_unlock_node(ern); /* undo lock in route_note_match */ + } else { + /* Not a big deal, just means VPN route got here first */ + vnc_zlog_debug_verbose("%s: no encap route for vn addr %pFX", + __func__, &vn_prefix); + info_new->extra->vnc.import.un_family = AF_UNSPEC; + } + + if (rn) { + if (!replacing) + agg_lock_node(rn); + } else { + /* + * No need to increment reference count, so only "get" + * if the node is not there already + */ + rn = agg_node_get(rt, p); + } + + /* + * For ethernet routes, if there is an accompanying IP address, + * save it in the bpi + */ + if ((AFI_L2VPN == afi) && aux_prefix) { + + vnc_zlog_debug_verbose("%s: setting BPI's aux_prefix", + __func__); + info_new->extra->vnc.import.aux_prefix = *aux_prefix; + } + + vnc_zlog_debug_verbose("%s: inserting bpi %p at prefix %pRN #%d", + __func__, info_new, rn, + agg_node_get_lock_count(rn)); + + rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_MPLS_VPN); + rfapiItBiIndexAdd(rn, info_new); + if (!rfapiGetUnAddrOfVpnBi(info_new, NULL)) { + if (VALID_INTERIOR_TYPE(info_new->type)) + RFAPI_MONITOR_EXTERIOR(rn)->valid_interior_count++; + SET_FLAG(info_new->flags, BGP_PATH_VALID); + } + RFAPI_UPDATE_ITABLE_COUNT(info_new, import_table, afi, 1); + vnc_import_bgp_exterior_add_route_interior(bgp, import_table, rn, + info_new); + + if (import_table == bgp->rfapi->it_ce) + vnc_direct_bgp_add_route_ce(bgp, rn, info_new); + + if (VNC_DEBUG(VERBOSE)) { + vnc_zlog_debug_verbose("%s: showing IT node", __func__); + rfapiShowItNode(NULL, rn); /* debug */ + } + + rfapiMonitorEncapAdd(import_table, &vn_prefix, rn, info_new); + + if (!rfapiGetUnAddrOfVpnBi(info_new, &un_prefix)) { + + /* + * if we have a valid UN address (either via Encap route + * or via tunnel attribute), then we should attempt + * to move any monitors at less-specific nodes to this node + */ + rfapiMonitorMoveLonger(rn); + + un_prefix_valid = 1; + } + + /* + * 101129 Enhancement: if we add a route (implication: it is not + * in holddown), delete all other routes from this nve at this + * node that are in holddown, regardless of peer. + * + * Reasons it's OK to do that: + * + * - if the holddown route being deleted originally came from BGP VPN, + * it is already gone from BGP (implication of holddown), so there + * won't be any added inconsistency with the BGP RIB. + * + * - once a fresh route is added at a prefix, any routes in holddown + * at that prefix will not show up in RFP responses, so deleting + * the holddown routes won't affect the contents of responses. + * + * - lifetimes are supposed to be consistent, so there should not + * be a case where the fresh route has a shorter lifetime than + * the holddown route, so we don't expect the fresh route to + * disappear and complete its holddown time before the existing + * holddown routes time out. Therefore, we won't have a situation + * where we expect the existing holddown routes to be hidden and + * then to reappear sometime later (as holddown routes) in a + * RFP response. + * + * Among other things, this would enable us to skirt the problem + * of local holddown routes that refer to NVE descriptors that + * have already been closed (if the same NVE triggers a subsequent + * rfapi_open(), the new peer is different and doesn't match the + * peer of the holddown route, so the stale holddown route still + * hangs around until it times out instead of just being replaced + * by the fresh route). + */ + /* + * We know that the new bpi will have been inserted before any routes + * in holddown, so we can skip any that came before it + */ + for (bpi = info_new->next; bpi; bpi = next) { + + struct prefix pfx_vn; + struct prefix pfx_un; + int un_match = 0; + int remote_peer_match = 0; + + next = bpi->next; + + /* + * Must be holddown + */ + if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + /* + * Must match VN address (nexthop of VPN route) + */ + if (rfapiGetNexthop(bpi->attr, &pfx_vn)) + continue; + if (!prefix_same(&pfx_vn, &vn_prefix)) + continue; + + if (un_prefix_valid && /* new route UN addr */ + !rfapiGetUnAddrOfVpnBi(bpi, &pfx_un) + && /* old route UN addr */ + prefix_same(&pfx_un, &un_prefix)) { /* compare */ + un_match = 1; + } + if (!RFAPI_LOCAL_BI(bpi) && !RFAPI_LOCAL_BI(info_new) && + sockunion_same(&bpi->peer->connection->su, + &info_new->peer->connection->su)) { + /* old & new are both remote, same peer */ + remote_peer_match = 1; + } + + if (!un_match && !remote_peer_match) + continue; + + vnc_zlog_debug_verbose( + "%s: removing holddown bpi matching NVE of new route", + __func__); + if (bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = + EVENT_ARG(bpi->extra->vnc.import.timer); + + XFREE(MTYPE_RFAPI_WITHDRAW, wcb); + EVENT_OFF(bpi->extra->vnc.import.timer); + } + rfapiExpireVpnNow(import_table, rn, bpi, 0); + } + + if (!original_had_routes) { + /* + * We went from 0 usable routes to 1 usable route. Perform the + * "Adding a Route" export process. + */ + vnc_direct_bgp_add_prefix(bgp, import_table, rn); + vnc_zebra_add_prefix(bgp, import_table, rn); + } else { + /* + * Check for nexthop change event + * Note: the prefix_same() test below detects two situations: + * 1. route is replaced, new route has different nexthop + * 2. new route is added (original_nexthop is 0) + */ + struct prefix new_nexthop; + + rfapiGetNexthop(attr, &new_nexthop); + if (!prefix_same(&original_nexthop, &new_nexthop)) { + /* + * nexthop change event + * vnc_direct_bgp_add_prefix() will recompute VN addr + * ecommunity + */ + vnc_direct_bgp_add_prefix(bgp, import_table, rn); + } + } + + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + for (n = rn; n; n = agg_node_parent(n)) { + // rfapiDoRouteCallback(import_table, n, NULL); + } + rfapiMonitorItNodeChanged(import_table, rn, NULL); + } + RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 0); + VNC_ITRCCK; +} + +static void rfapiBgpInfoFilteredImportBadSafi( + struct rfapi_import_table *import_table, int action, struct peer *peer, + void *rfd, /* set for looped back routes */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */ + afi_t afi, struct prefix_rd *prd, + struct attr *attr, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ + uint8_t sub_type, /* part of bgp_path_info */ + uint32_t *label) /* part of bgp_path_info */ +{ + vnc_zlog_debug_verbose("%s: Error, bad safi", __func__); +} + +static rfapi_bi_filtered_import_f * +rfapiBgpInfoFilteredImportFunction(safi_t safi) +{ + switch (safi) { + case SAFI_MPLS_VPN: + return rfapiBgpInfoFilteredImportVPN; + + case SAFI_ENCAP: + return rfapiBgpInfoFilteredImportEncap; + + case SAFI_UNSPEC: + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_EVPN: + case SAFI_LABELED_UNICAST: + case SAFI_FLOWSPEC: + case SAFI_MAX: + /* not expected */ + flog_err(EC_LIB_DEVELOPMENT, "%s: bad safi %d", __func__, safi); + return rfapiBgpInfoFilteredImportBadSafi; + } + + assert(!"Reached end of function when we were not expecting to"); +} + +void rfapiProcessUpdate(struct peer *peer, + void *rfd, /* set when looped from RFP/RFAPI */ + const struct prefix *p, struct prefix_rd *prd, + struct attr *attr, afi_t afi, safi_t safi, uint8_t type, + uint8_t sub_type, uint32_t *label) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + int has_ip_route = 1; + uint32_t lni = 0; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + assert(bgp); + + h = bgp->rfapi; + assert(h); + + /* + * look at high-order byte of RD. FF means MAC + * address is present (VNC L2VPN) + */ + if ((safi == SAFI_MPLS_VPN) + && (decode_rd_type(prd->val) == RD_TYPE_VNC_ETH)) { + struct prefix pfx_mac_buf; + struct prefix pfx_nexthop_buf; + int rc; + + /* + * Set flag if prefix and nexthop are the same - don't + * add the route to normal IP-based import tables + */ + if (!rfapiGetNexthop(attr, &pfx_nexthop_buf)) { + if (!prefix_cmp(&pfx_nexthop_buf, p)) { + has_ip_route = 0; + } + } + + memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + memcpy(&pfx_mac_buf.u.prefix_eth.octet, prd->val + 2, 6); + + /* + * Find rt containing LNI (Logical Network ID), which + * _should_ always be present when mac address is present + */ + rc = rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(attr), &lni); + + vnc_zlog_debug_verbose( + "%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p", + __func__, rc, lni, attr); + if (!rc) { + it = rfapiMacImportTableGet(bgp, lni); + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, peer, rfd, + &pfx_mac_buf, /* prefix */ + p, /* aux prefix: IP addr */ + AFI_L2VPN, prd, attr, type, sub_type, label); + } + } + + if (!has_ip_route) + return; + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + for (it = h->imports; it; it = it->next) { + (*rfapiBgpInfoFilteredImportFunction(safi))( + it, FIF_ACTION_UPDATE, peer, rfd, p, /* prefix */ + NULL, afi, prd, attr, type, sub_type, label); + } + + if (safi == SAFI_MPLS_VPN) { + vnc_direct_bgp_rh_add_route(bgp, afi, p, peer, attr); + rfapiBgpInfoFilteredImportVPN( + bgp->rfapi->it_ce, FIF_ACTION_UPDATE, peer, rfd, + p, /* prefix */ + NULL, afi, prd, attr, type, sub_type, label); + } +} + + +void rfapiProcessWithdraw(struct peer *peer, void *rfd, const struct prefix *p, + struct prefix_rd *prd, struct attr *attr, afi_t afi, + safi_t safi, uint8_t type, int kill) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + assert(bgp); + + h = bgp->rfapi; + assert(h); + + /* + * look at high-order byte of RD. FF means MAC + * address is present (VNC L2VPN) + */ + if (h->import_mac != NULL && safi == SAFI_MPLS_VPN + && decode_rd_type(prd->val) == RD_TYPE_VNC_ETH) { + struct prefix pfx_mac_buf; + void *cursor = NULL; + int rc; + + memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + memcpy(&pfx_mac_buf.u.prefix_eth, prd->val + 2, 6); + + /* + * withdraw does not contain attrs, so we don't have + * access to the route's LNI, which would ordinarily + * select the specific mac-based import table. Instead, + * we must iterate over all mac-based tables and rely + * on the RD to match. + * + * If this approach is too slow, add an index where + * key is {RD, peer} and value is the import table + */ + for (rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor); + rc == 0; rc = skiplist_next(h->import_mac, NULL, + (void **)&it, &cursor)) { + +#ifdef DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_L2VPN)", + __func__, it); +#endif + + rfapiBgpInfoFilteredImportVPN( + it, + (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), + peer, rfd, &pfx_mac_buf, /* prefix */ + p, /* aux_prefix: IP */ + AFI_L2VPN, prd, attr, type, 0, + NULL); /* sub_type & label unused for withdraw + */ + } + } + + /* + * XXX For the case where the withdraw involves an L2 + * route with no IP information, we rely on the lack + * of RT-list intersection to filter out the withdraw + * from the IP-based import tables below + */ + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + for (it = h->imports; it; it = it->next) { + (*rfapiBgpInfoFilteredImportFunction(safi))( + it, (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), + peer, rfd, p, /* prefix */ + NULL, afi, prd, attr, type, 0, + NULL); /* sub_type & label unused for withdraw */ + } + + /* TBD the deletion should happen after the lifetime expires */ + if (safi == SAFI_MPLS_VPN) + vnc_direct_bgp_rh_del_route(bgp, afi, p, peer); + + if (safi == SAFI_MPLS_VPN) { + rfapiBgpInfoFilteredImportVPN( + bgp->rfapi->it_ce, + (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), peer, + rfd, p, /* prefix */ + NULL, afi, prd, attr, type, 0, + NULL); /* sub_type & label unused for withdraw */ + } +} + +/* + * TBD optimized withdraw timer algorithm for case of many + * routes expiring at the same time due to peer drop. + */ +/* + * 1. Visit all BPIs in all ENCAP import tables. + * + * a. If a bpi's peer is the failed peer, remove the bpi. + * b. If the removed ENCAP bpi was first in the list of + * BPIs at this ENCAP node, loop over all monitors + * at this node: + * + * (1) for each ENCAP monitor, loop over all its + * VPN node monitors and set their RFAPI_MON_FLAG_NEEDCALLBACK + * flags. + * + * 2. Visit all BPIs in all VPN import tables. + * a. If a bpi's peer is the failed peer, remove the bpi. + * b. loop over all the VPN node monitors and set their + * RFAPI_MON_FLAG_NEEDCALLBACK flags + * c. If there are no BPIs left at this VPN node, + * + */ + + +/* surprise, this gets called from peer_delete(), from rfapi_close() */ +static void rfapiProcessPeerDownRt(struct peer *peer, + struct rfapi_import_table *import_table, + afi_t afi, safi_t safi) +{ + struct agg_node *rn; + struct bgp_path_info *bpi; + struct agg_table *rt = NULL; + void (*timer_service_func)(struct event *) = NULL; + + assert(afi == AFI_IP || afi == AFI_IP6); + + VNC_ITRCCK; + + switch (safi) { + case SAFI_MPLS_VPN: + rt = import_table->imported_vpn[afi]; + timer_service_func = rfapiWithdrawTimerVPN; + break; + case SAFI_ENCAP: + rt = import_table->imported_encap[afi]; + timer_service_func = rfapiWithdrawTimerEncap; + break; + case SAFI_UNSPEC: + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_EVPN: + case SAFI_LABELED_UNICAST: + case SAFI_FLOWSPEC: + case SAFI_MAX: + /* Suppress uninitialized variable warning */ + rt = NULL; + timer_service_func = NULL; + assert(0); + } + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + for (bpi = rn->info; bpi; bpi = bpi->next) { + if (bpi->peer == peer) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + /* already in holddown, skip */ + continue; + } + + if (safi == SAFI_MPLS_VPN) { + RFAPI_UPDATE_ITABLE_COUNT( + bpi, import_table, afi, -1); + import_table->holddown_count[afi] += 1; + } + rfapiBiStartWithdrawTimer(import_table, rn, bpi, + afi, safi, + timer_service_func); + } + } + } + VNC_ITRCCK; +} + +/* + * This gets called when a peer connection drops. We have to remove + * all the routes from this peer. + * + * Current approach is crude. TBD Optimize by setting fewer timers and + * grouping withdrawn routes so we can generate callbacks more + * efficiently. + */ +void rfapiProcessPeerDown(struct peer *peer) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + + /* + * If this peer is a "dummy" peer structure atached to a RFAPI + * nve_descriptor, we don't need to walk the import tables + * because the routes are already withdrawn by rfapi_close() + */ + if (CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) + return; + + /* + * 1. Visit all BPIs in all ENCAP import tables. + * Start withdraw timer on the BPIs that match peer. + * + * 2. Visit All BPIs in all VPN import tables. + * Start withdraw timer on the BPIs that match peer. + */ + + bgp = bgp_get_default(); /* assume 1 instance for now */ + if (!bgp) + return; + + h = bgp->rfapi; + assert(h); + + for (it = h->imports; it; it = it->next) { + rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_ENCAP); + rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_ENCAP); + rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_MPLS_VPN); + rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_MPLS_VPN); + } + + if (h->it_ce) { + rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP, SAFI_MPLS_VPN); + rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP6, SAFI_MPLS_VPN); + } +} + +/* + * Import an entire RIB (for an afi/safi) to an import table RIB, + * filtered according to the import table's RT list + * + * TBD: does this function need additions to match rfapiProcessUpdate() + * for, e.g., L2 handling? + */ +static void rfapiBgpTableFilteredImport(struct bgp *bgp, + struct rfapi_import_table *it, + afi_t afi, safi_t safi) +{ + struct bgp_dest *dest1; + struct bgp_dest *dest2; + + /* Only these SAFIs have 2-level RIBS */ + assert(safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP); + + /* + * Now visit all the rd nodes and the nodes of all the + * route tables attached to them, and import the routes + * if they have matching route targets + */ + for (dest1 = bgp_table_top(bgp->rib[afi][safi]); dest1; + dest1 = bgp_route_next(dest1)) { + + if (bgp_dest_has_bgp_path_info_data(dest1)) { + + for (dest2 = bgp_table_top( + bgp_dest_get_bgp_table_info(dest1)); + dest2; dest2 = bgp_route_next(dest2)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest2); + bpi; bpi = bpi->next) { + uint32_t label = 0; + + if (CHECK_FLAG(bpi->flags, + BGP_PATH_REMOVED)) + continue; + + if (bpi->extra) + label = decode_label( + &bpi->extra->label[0]); + (*rfapiBgpInfoFilteredImportFunction( + safi))( + it, /* which import table */ + FIF_ACTION_UPDATE, bpi->peer, + NULL, + bgp_dest_get_prefix(dest2), + NULL, afi, + (struct prefix_rd *) + bgp_dest_get_prefix( + dest1), + bpi->attr, bpi->type, + bpi->sub_type, &label); + } + } + } + } +} + + +/* per-bgp-instance rfapi data */ +struct rfapi *bgp_rfapi_new(struct bgp *bgp) +{ + struct rfapi *h; + afi_t afi; + struct rfapi_rfp_cfg *cfg = NULL; + struct rfapi_rfp_cb_methods *cbm = NULL; + + assert(bgp->rfapi_cfg == NULL); + + h = XCALLOC(MTYPE_RFAPI, sizeof(struct rfapi)); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + h->un[afi] = agg_table_init(); + } + + /* + * initialize the ce import table + */ + h->it_ce = XCALLOC(MTYPE_RFAPI_IMPORTTABLE, + sizeof(struct rfapi_import_table)); + h->it_ce->imported_vpn[AFI_IP] = agg_table_init(); + h->it_ce->imported_vpn[AFI_IP6] = agg_table_init(); + h->it_ce->imported_encap[AFI_IP] = agg_table_init(); + h->it_ce->imported_encap[AFI_IP6] = agg_table_init(); + rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP, SAFI_MPLS_VPN); + rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP6, SAFI_MPLS_VPN); + + /* + * Set up work queue for deferred rfapi_close operations + */ + h->deferred_close_q = + work_queue_new(bm->master, "rfapi deferred close"); + h->deferred_close_q->spec.workfunc = rfapi_deferred_close_workfunc; + h->deferred_close_q->spec.data = h; + + h->rfp = rfp_start(bm->master, &cfg, &cbm); + bgp->rfapi_cfg = bgp_rfapi_cfg_new(cfg); + if (cbm != NULL) { + h->rfp_methods = *cbm; + } + return h; +} + +void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h) +{ + afi_t afi; + + if (bgp == NULL || h == NULL) + return; + + if (h->resolve_nve_nexthop) { + skiplist_free(h->resolve_nve_nexthop); + h->resolve_nve_nexthop = NULL; + } + + rfapiImportTableFlush(h->it_ce); + + if (h->import_mac) { + struct rfapi_import_table *it; + void *cursor; + int rc; + + for (cursor = NULL, + rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor); + !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor)) { + + rfapiImportTableFlush(it); + XFREE(MTYPE_RFAPI_IMPORTTABLE, it); + } + skiplist_free(h->import_mac); + h->import_mac = NULL; + } + + work_queue_free_and_null(&h->deferred_close_q); + + if (h->rfp != NULL) + rfp_stop(h->rfp); + + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + agg_table_finish(h->un[afi]); + } + + XFREE(MTYPE_RFAPI_IMPORTTABLE, h->it_ce); + XFREE(MTYPE_RFAPI, h); +} + +struct rfapi_import_table * +rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list, + struct rfapi_nve_group_cfg *rfg) +{ + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + h = bgp->rfapi; + assert(h); + + for (it = h->imports; it; it = it->next) { + if (ecommunity_cmp(it->rt_import_list, rt_import_list)) + break; + } + + vnc_zlog_debug_verbose("%s: matched it=%p", __func__, it); + + if (!it) { + it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE, + sizeof(struct rfapi_import_table)); + it->next = h->imports; + h->imports = it; + + it->rt_import_list = ecommunity_dup(rt_import_list); + it->rfg = rfg; + it->monitor_exterior_orphans = + skiplist_new(0, NULL, prefix_free_lists); + + /* + * fill import route tables from RIBs + * + * Potential area for optimization. If this occurs when + * tables are large (e.g., the operator adds a nve group + * with a new RT list to a running system), it could take + * a while. + * + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + it->imported_vpn[afi] = agg_table_init(); + it->imported_encap[afi] = agg_table_init(); + + rfapiBgpTableFilteredImport(bgp, it, afi, + SAFI_MPLS_VPN); + rfapiBgpTableFilteredImport(bgp, it, afi, SAFI_ENCAP); + + vnc_import_bgp_exterior_redist_enable_it(bgp, afi, it); + } + } + + it->refcount += 1; + + return it; +} + +/* + * skiplist element free function + */ +static void delete_rem_pfx_na_free(void *na) +{ + uint32_t *pCounter = ((struct rfapi_nve_addr *)na)->info; + + *pCounter += 1; + XFREE(MTYPE_RFAPI_NVE_ADDR, na); +} + +/* + * Common deleter for IP and MAC import tables + */ +static void rfapiDeleteRemotePrefixesIt( + struct bgp *bgp, struct rfapi_import_table *it, struct prefix *un, + struct prefix *vn, struct prefix *p, int delete_active, + int delete_holddown, uint32_t *pARcount, uint32_t *pAHcount, + uint32_t *pHRcount, uint32_t *pHHcount, + struct skiplist *uniq_active_nves, struct skiplist *uniq_holddown_nves) +{ + afi_t afi; + +#ifdef DEBUG_L2_EXTRA + { + char buf_pfx[PREFIX_STRLEN]; + + if (p) { + prefix2str(p, buf_pfx, sizeof(buf_pfx)); + } else { + buf_pfx[0] = '*'; + buf_pfx[1] = 0; + } + + vnc_zlog_debug_verbose( + "%s: entry, p=%s, delete_active=%d, delete_holddown=%d", + __func__, buf_pfx, delete_active, delete_holddown); + } +#endif + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_table *rt; + struct agg_node *rn; + + if (p && (family2afi(p->family) != afi)) { + continue; + } + + rt = it->imported_vpn[afi]; + if (!rt) + continue; + + vnc_zlog_debug_verbose("%s: scanning rt for afi=%d", __func__, + afi); + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + struct bgp_path_info *bpi; + struct bgp_path_info *next; + const struct prefix *rn_p = agg_node_get_prefix(rn); + + if (p && VNC_DEBUG(IMPORT_DEL_REMOTE)) + vnc_zlog_debug_any("%s: want %pFX, have %pRN", + __func__, p, rn); + + if (p && prefix_cmp(p, rn_p)) + continue; + + vnc_zlog_debug_verbose("%s: rn pfx=%pRN", __func__, rn); + + /* TBD is this valid for afi == AFI_L2VPN? */ + RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1); + + for (bpi = rn->info; bpi; bpi = next) { + next = bpi->next; + + struct prefix qpt; + struct prefix qct; + int qpt_valid = 0; + int qct_valid = 0; + int is_active = 0; + + vnc_zlog_debug_verbose("%s: examining bpi %p", + __func__, bpi); + + if (!rfapiGetNexthop(bpi->attr, &qpt)) + qpt_valid = 1; + + if (vn) { + if (!qpt_valid + || !prefix_match(vn, &qpt)) { +#ifdef DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)", + __func__); +#endif + continue; + } + } + + if (!rfapiGetUnAddrOfVpnBi(bpi, &qct)) + qct_valid = 1; + + if (un) { + if (!qct_valid + || !prefix_match(un, &qct)) { +#ifdef DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: continue at un && !qct_valid || !prefix_match(un, &qct)", + __func__); +#endif + continue; + } + } + + + /* + * Blow bpi away + */ + /* + * If this route is waiting to be deleted + * because of + * a previous withdraw, we must cancel its + * timer. + */ + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + if (!delete_holddown) + continue; + if (bpi->extra->vnc.import.timer) { + struct rfapi_withdraw *wcb = + EVENT_ARG( + bpi->extra->vnc + .import + .timer); + + wcb->import_table + ->holddown_count[afi] -= + 1; + RFAPI_UPDATE_ITABLE_COUNT( + bpi, wcb->import_table, + afi, 1); + XFREE(MTYPE_RFAPI_WITHDRAW, + wcb); + EVENT_OFF(bpi->extra->vnc.import + .timer); + } + } else { + if (!delete_active) + continue; + is_active = 1; + } + + vnc_zlog_debug_verbose( + "%s: deleting bpi %p (qct_valid=%d, qpt_valid=%d, delete_holddown=%d, delete_active=%d)", + __func__, bpi, qct_valid, qpt_valid, + delete_holddown, delete_active); + + + /* + * add nve to list + */ + if (qct_valid && qpt_valid) { + + struct rfapi_nve_addr na; + struct rfapi_nve_addr *nap; + + memset(&na, 0, sizeof(na)); + assert(!rfapiQprefix2Raddr(&qct, + &na.un)); + assert(!rfapiQprefix2Raddr(&qpt, + &na.vn)); + + if (skiplist_search( + (is_active + ? uniq_active_nves + : uniq_holddown_nves), + &na, (void **)&nap)) { + char line[BUFSIZ]; + + nap = XCALLOC( + MTYPE_RFAPI_NVE_ADDR, + sizeof(struct + rfapi_nve_addr)); + *nap = na; + nap->info = is_active + ? pAHcount + : pHHcount; + skiplist_insert( + (is_active + ? uniq_active_nves + : uniq_holddown_nves), + nap, nap); + + rfapiNveAddr2Str(nap, line, + BUFSIZ); + } + } + + vnc_direct_bgp_rh_del_route(bgp, afi, rn_p, + bpi->peer); + + RFAPI_UPDATE_ITABLE_COUNT(bpi, it, afi, -1); + it->holddown_count[afi] += 1; + rfapiExpireVpnNow(it, rn, bpi, 1); + + vnc_zlog_debug_verbose( + "%s: incrementing count (is_active=%d)", + __func__, is_active); + + if (is_active) + ++*pARcount; + else + ++*pHRcount; + } + } + } +} + + +/* + * For use by the "clear vnc prefixes" command + */ +/*------------------------------------------ + * rfapiDeleteRemotePrefixes + * + * UI helper: For use by the "clear vnc prefixes" command + * + * input: + * un if set, tunnel must match this prefix + * vn if set, nexthop prefix must match this prefix + * p if set, prefix must match this prefix + * it if set, only look in this import table + * + * output + * pARcount number of active routes deleted + * pAHcount number of active nves deleted + * pHRcount number of holddown routes deleted + * pHHcount number of holddown nves deleted + * + * return value: + * void + --------------------------------------------*/ +void rfapiDeleteRemotePrefixes(struct prefix *un, struct prefix *vn, + struct prefix *p, + struct rfapi_import_table *arg_it, + int delete_active, int delete_holddown, + uint32_t *pARcount, uint32_t *pAHcount, + uint32_t *pHRcount, uint32_t *pHHcount) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + uint32_t deleted_holddown_route_count = 0; + uint32_t deleted_active_route_count = 0; + uint32_t deleted_holddown_nve_count = 0; + uint32_t deleted_active_nve_count = 0; + struct skiplist *uniq_holddown_nves; + struct skiplist *uniq_active_nves; + + VNC_ITRCCK; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + /* If no bgp instantiated yet, no vnc prefixes exist */ + if (!bgp) + return; + + h = bgp->rfapi; + assert(h); + + uniq_holddown_nves = + skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free); + uniq_active_nves = + skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free); + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + if (arg_it) + it = arg_it; + else + it = h->imports; + for (; it;) { + + vnc_zlog_debug_verbose( + "%s: calling rfapiDeleteRemotePrefixesIt() on (IP) import %p", + __func__, it); + + rfapiDeleteRemotePrefixesIt( + bgp, it, un, vn, p, delete_active, delete_holddown, + &deleted_active_route_count, &deleted_active_nve_count, + &deleted_holddown_route_count, + &deleted_holddown_nve_count, uniq_active_nves, + uniq_holddown_nves); + + if (arg_it) + it = NULL; + else + it = it->next; + } + + /* + * Now iterate over L2 import tables + */ + if (h->import_mac && !(p && (p->family != AF_ETHERNET))) { + + void *cursor = NULL; + int rc; + + for (cursor = NULL, + rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor); + !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor)) { + + vnc_zlog_debug_verbose( + "%s: calling rfapiDeleteRemotePrefixesIt() on import_mac %p", + __func__, it); + + rfapiDeleteRemotePrefixesIt( + bgp, it, un, vn, p, delete_active, + delete_holddown, &deleted_active_route_count, + &deleted_active_nve_count, + &deleted_holddown_route_count, + &deleted_holddown_nve_count, uniq_active_nves, + uniq_holddown_nves); + } + } + + /* + * our custom element freeing function above counts as it deletes + */ + skiplist_free(uniq_holddown_nves); + skiplist_free(uniq_active_nves); + + if (pARcount) + *pARcount = deleted_active_route_count; + if (pAHcount) + *pAHcount = deleted_active_nve_count; + if (pHRcount) + *pHRcount = deleted_holddown_route_count; + if (pHHcount) + *pHHcount = deleted_holddown_nve_count; + + VNC_ITRCCK; +} + +/*------------------------------------------ + * rfapiCountRemoteRoutes + * + * UI helper: count VRF routes from BGP side + * + * input: + * + * output + * pALRcount count of active local routes + * pARRcount count of active remote routes + * pHRcount count of holddown routes + * pIRcount count of direct imported routes + * + * return value: + * void + --------------------------------------------*/ +void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */ + int *pARRcount, /* active remote routes */ + int *pHRcount, /* holddown routes */ + int *pIRcount) /* imported routes */ +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + afi_t afi; + + int total_active_local = 0; + int total_active_remote = 0; + int total_holddown = 0; + int total_imported = 0; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + assert(bgp); + + h = bgp->rfapi; + assert(h); + + /* + * Iterate over all import tables; do a filtered import + * for the afi/safi combination + */ + + for (it = h->imports; it; it = it->next) { + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + total_active_local += it->local_count[afi]; + total_active_remote += it->remote_count[afi]; + total_holddown += it->holddown_count[afi]; + total_imported += it->imported_count[afi]; + } + } + + void *cursor; + int rc; + + if (h->import_mac) { + for (cursor = NULL, + rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor); + !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it, + &cursor)) { + + total_active_local += it->local_count[AFI_L2VPN]; + total_active_remote += it->remote_count[AFI_L2VPN]; + total_holddown += it->holddown_count[AFI_L2VPN]; + total_imported += it->imported_count[AFI_L2VPN]; + } + } + + + if (pALRcount) { + *pALRcount = total_active_local; + } + if (pARRcount) { + *pARRcount = total_active_remote; + } + if (pHRcount) { + *pHRcount = total_holddown; + } + if (pIRcount) { + *pIRcount = total_imported; + } +} + +/*------------------------------------------ + * rfapiGetHolddownFromLifetime + * + * calculate holddown value based on lifetime + * + * input: + * lifetime lifetime + * + * return value: + * Holddown value based on lifetime, holddown_factor, + * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY + * + --------------------------------------------*/ +/* hold down time maxes out at RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY */ +uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime) +{ + uint32_t factor; + struct bgp *bgp; + + bgp = bgp_get_default(); + if (bgp && bgp->rfapi_cfg) + factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor; + else + factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR; + + if (factor < 100 || lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + lifetime = lifetime * factor / 100; + if (lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + return lifetime; + else + return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY; +} diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h new file mode 100644 index 0000000..dd06afe --- /dev/null +++ b/bgpd/rfapi/rfapi_import.h @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: rfapi_import.h + * Purpose: Handle import of routes from BGP to RFAPI + */ + +#ifndef QUAGGA_HGP_RFAPI_IMPORT_H +#define QUAGGA_HGP_RFAPI_IMPORT_H + +#include "frrevent.h" + +/* + * These are per-rt-import-list + * + * routes are not segregated by RD - the RD is stored in bgp_path_info_extra + * and is needed to determine if two prefixes are the same. + */ +struct rfapi_import_table { + struct rfapi_import_table *next; + struct rfapi_nve_group_cfg *rfg; + struct ecommunity *rt_import_list; /* copied from nve grp */ + int refcount; /* nve grps and nves */ + uint32_t l2_logical_net_id; /* L2 only: EVPN Eth Seg Id */ + struct agg_table *imported_vpn[AFI_MAX]; + struct rfapi_monitor_vpn *vpn0_queries[AFI_MAX]; + struct rfapi_monitor_eth *eth0_queries; + struct agg_table *imported_encap[AFI_MAX]; + struct skiplist *monitor_exterior_orphans; + int local_count[AFI_MAX]; + int remote_count[AFI_MAX]; + int holddown_count[AFI_MAX]; + int imported_count[AFI_MAX]; +}; + +#define RFAPI_LOCAL_BI(bpi) \ + (((bpi)->type == ZEBRA_ROUTE_BGP) && ((bpi)->sub_type == BGP_ROUTE_RFP)) + +#define RFAPI_DIRECT_IMPORT_BI(bpi) \ + (((bpi)->type == ZEBRA_ROUTE_BGP_DIRECT) \ + || ((bpi)->type == ZEBRA_ROUTE_BGP_DIRECT_EXT)) + +#define RFAPI_UPDATE_ITABLE_COUNT(bpi, itable, afi, cnt) \ + if (RFAPI_LOCAL_BI(bpi)) { \ + (itable)->local_count[(afi)] += (cnt); \ + } else { \ + if (RFAPI_DIRECT_IMPORT_BI(bpi)) \ + (itable)->imported_count[(afi)] += (cnt); \ + else \ + (itable)->remote_count[(afi)] += (cnt); \ + } + +extern uint8_t rfapiRfpCost(struct attr *attr); + +extern void rfapiDebugBacktrace(void); + +extern void rfapiCheckRouteCount(void); + +/* + * Print BPI in an Import Table + */ +extern void rfapiPrintBi(void *stream, struct bgp_path_info *bpi); + +extern void rfapiShowImportTable(void *stream, const char *label, + struct agg_table *rt, int isvpn); + +extern struct rfapi_import_table * +rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list, + struct rfapi_nve_group_cfg *rfg); + +extern void rfapiImportTableRefDelByIt(struct bgp *bgp, + struct rfapi_import_table *it_target); + + +/* + * Construct an rfapi nexthop list based on the routes attached to + * the specified node. + * + * If there are any routes that do NOT have BGP_PATH_REMOVED set, + * return those only. If there are ONLY routes with BGP_INFO_REMOVED, + * then return those, and also include all the non-removed routes from the + * next less-specific node (i.e., this node's parent) at the end. + */ +extern struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList( + struct agg_node *rn, uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_next_hop_entry *rfapiRouteTable2NextHopList( + struct agg_table *rt, uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rfd_rib_table, /* preload this NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_next_hop_entry *rfapiEthRouteTable2NextHopList( + uint32_t logical_net_id, struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rib_route_table, /* preload NVE rib node */ + struct prefix *pfx_target_original); /* query target */ + +extern int rfapiEcommunitiesIntersect(struct ecommunity *e1, + struct ecommunity *e2); + +extern void rfapiCheckRefcount(struct agg_node *rn, safi_t safi, + int lockoffset); + +extern int rfapiHasNonRemovedRoutes(struct agg_node *rn); + +extern int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p); + +extern void rfapiNexthop2Prefix(struct attr *attr, struct prefix *p); + +extern void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr, + struct prefix *p); + +/* Filtered Import Function actions */ +#define FIF_ACTION_UPDATE 0 +#define FIF_ACTION_WITHDRAW 1 +#define FIF_ACTION_KILL 2 + +extern void rfapiBgpInfoFilteredImportVPN( + struct rfapi_import_table *import_table, int action, struct peer *peer, + void *rfd, /* set for looped back routes */ + const struct prefix *p, + const struct prefix *aux_prefix, /* AFI_ETHER: optional IP */ + afi_t afi, struct prefix_rd *prd, + struct attr *attr, /* part of bgp_path_info */ + uint8_t type, /* part of bgp_path_info */ + uint8_t sub_type, /* part of bgp_path_info */ + uint32_t *label); /* part of bgp_path_info */ + +extern struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList( + struct agg_node *rn, struct rfapi_ip_prefix *rprefix, + uint32_t lifetime, /* put into nexthop entries */ + struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */ + struct agg_table *rib_route_table, /* preload NVE rib table */ + struct prefix *pfx_target_original); /* query target */ + +extern struct rfapi_import_table *rfapiMacImportTableGetNoAlloc(struct bgp *bgp, + uint32_t lni); + +extern struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, + uint32_t lni); + +extern int rfapiGetL2o(struct attr *attr, struct rfapi_l2address_option *l2o); + +extern int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni); + +extern int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, + uint16_t *tag_id); + +/* enable for debugging; disable for performance */ +#if 0 +#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) rfapiCheckRefcount((rn),(safi),(lo)) +#else +#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) {} +#endif + +/*------------------------------------------ + * rfapiDeleteRemotePrefixes + * + * UI helper: For use by the "clear vnc prefixes" command + * + * input: + * un if set, tunnel must match this prefix + * vn if set, nexthop prefix must match this prefix + * p if set, prefix must match this prefix + * it if set, only look in this import table + * + * output + * pARcount number of active routes deleted + * pAHcount number of active nves deleted + * pHRcount number of holddown routes deleted + * pHHcount number of holddown nves deleted + * + * return value: + * void + --------------------------------------------*/ +extern void rfapiDeleteRemotePrefixes(struct prefix *un, struct prefix *vn, + struct prefix *p, + struct rfapi_import_table *it, + int delete_active, int delete_holddown, + uint32_t *pARcount, /* active routes */ + uint32_t *pAHcount, /* active nves */ + uint32_t *pHRcount, /* holddown routes */ + uint32_t *pHHcount); /* holddown nves */ + +/*------------------------------------------ + * rfapiCountAllItRoutes + * + * UI helper: count VRF routes from BGP side + * + * input: + * + * output + * pARcount count of active routes + * pHRcount count of holddown routes + * pIRcount count of holddown routes + * + * return value: + * void + --------------------------------------------*/ +extern void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */ + int *pARRcount, /* active remote routes */ + int *pHRcount, /* holddown routes */ + int *pIRcount); /* direct imported routes */ + +/*------------------------------------------ + * rfapiGetHolddownFromLifetime + * + * calculate holddown value based on lifetime + * + * input: + * lifetime lifetime + * + * return value: + * Holddown value based on lifetime, holddown_factor, + * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY + * + --------------------------------------------*/ +extern uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime); + +#endif /* QUAGGA_HGP_RFAPI_IMPORT_H */ diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c new file mode 100644 index 0000000..3fe957b --- /dev/null +++ b/bgpd/rfapi/rfapi_monitor.c @@ -0,0 +1,1557 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: rfapi_monitor.c + */ + +/* TBD remove unneeded includes */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/table.h" +#include "lib/skiplist.h" + +#include "bgpd/bgpd.h" + +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" + +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define DEBUG_L2_EXTRA 0 +#define DEBUG_DUP_CHECK 0 +#define DEBUG_ETH_SL 0 + +static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m); + +static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m); + +/* + * Forward declarations + */ +static void rfapiMonitorEthDetachImport(struct bgp *bgp, + struct rfapi_monitor_eth *mon); + +#if DEBUG_ETH_SL +/* + * Debug function, special case + */ +void rfapiMonitorEthSlCheck(struct agg_node *rn, const char *tag1, + const char *tag2) +{ + struct agg_node *rn_saved = NULL; + static struct skiplist *sl_saved = NULL; + struct skiplist *sl; + + if (!rn) + return; + + if (rn_saved && (rn != rn_saved)) + return; + + if (!rn_saved) + rn_saved = rn; + + sl = RFAPI_MONITOR_ETH(rn); + if (sl || sl_saved) { + vnc_zlog_debug_verbose( + "%s[%s%s]: rn=%p, rn->lock=%d, old sl=%p, new sl=%p", + __func__, (tag1 ? tag1 : ""), (tag2 ? tag2 : ""), rn, + rn->lock, sl_saved, sl); + sl_saved = sl; + } +} +#endif + +/* + * Debugging function that aborts when it finds monitors whose + * "next" pointer * references themselves + */ +void rfapiMonitorLoopCheck(struct rfapi_monitor_vpn *mchain) +{ + struct rfapi_monitor_vpn *m; + + for (m = mchain; m; m = m->next) + assert(m != m->next); +} + +#if DEBUG_DUP_CHECK +/* + * Debugging code: see if a monitor is mentioned more than once + * in a HD's monitor list + */ +void rfapiMonitorDupCheck(struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) { + struct agg_node *mrn; + + if (!rfd->mon) + continue; + + for (mrn = agg_route_top(rfd->mon); mrn; + mrn = agg_route_next(mrn)) { + struct rfapi_monitor_vpn *m; + for (m = (struct rfapi_monitor_vpn *)(mrn->info); m; + m = m->next) + m->dcount = 0; + } + } + + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) { + struct agg_node *mrn; + + if (!rfd->mon) + continue; + + for (mrn = agg_route_top(rfd->mon); mrn; + mrn = agg_route_next(mrn)) { + struct rfapi_monitor_vpn *m; + + for (m = (struct rfapi_monitor_vpn *)(mrn->info); m; + m = m->next) + assert(++m->dcount == 1); + } + } +} +#endif + +/* debug */ +void rfapiMonitorCleanCheck(struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) { + assert(!rfd->import_table->vpn0_queries[AFI_IP]); + assert(!rfd->import_table->vpn0_queries[AFI_IP6]); + + struct agg_node *rn; + + for (rn = agg_route_top( + rfd->import_table->imported_vpn[AFI_IP]); + rn; rn = agg_route_next(rn)) { + + assert(!RFAPI_MONITOR_VPN(rn)); + } + for (rn = agg_route_top( + rfd->import_table->imported_vpn[AFI_IP6]); + rn; rn = agg_route_next(rn)) { + + assert(!RFAPI_MONITOR_VPN(rn)); + } + } +} + +/* debug */ +void rfapiMonitorCheckAttachAllowed(void) +{ + struct bgp *bgp = bgp_get_default(); + assert(!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)); +} + +void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn) +{ + struct rfapi_it_extra *hie; + struct rfapi_monitor_vpn *v; + struct rfapi_monitor_vpn *v_next; + struct rfapi_monitor_encap *e = NULL; + struct rfapi_monitor_encap *e_next = NULL; + + if (!rn) + return; + + if (!rn->aggregate) + return; + + hie = (struct rfapi_it_extra *)(rn->aggregate); + + switch (safi) { + case SAFI_ENCAP: + for (e = hie->u.encap.e; e; e = e_next) { + e_next = e->next; + e->next = NULL; + XFREE(MTYPE_RFAPI_MONITOR_ENCAP, e); + agg_unlock_node(rn); + } + hie->u.encap.e = NULL; + break; + + case SAFI_MPLS_VPN: + for (v = hie->u.vpn.v; v; v = v_next) { + v_next = v->next; + v->next = NULL; + XFREE(MTYPE_RFAPI_MONITOR, e); + agg_unlock_node(rn); + } + hie->u.vpn.v = NULL; + if (hie->u.vpn.e.source) { + while (!skiplist_delete_first(hie->u.vpn.e.source)) { + agg_unlock_node(rn); + } + skiplist_free(hie->u.vpn.e.source); + hie->u.vpn.e.source = NULL; + agg_unlock_node(rn); + } + if (hie->u.vpn.idx_rd) { + /* looping through bpi->extra->vnc.import.rd is tbd */ + while (!skiplist_delete_first(hie->u.vpn.idx_rd)) { + agg_unlock_node(rn); + } + skiplist_free(hie->u.vpn.idx_rd); + hie->u.vpn.idx_rd = NULL; + agg_unlock_node(rn); + } + if (hie->u.vpn.mon_eth) { + while (!skiplist_delete_first(hie->u.vpn.mon_eth)) { + agg_unlock_node(rn); + } + skiplist_free(hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + agg_unlock_node(rn); + } + break; + + case SAFI_UNSPEC: + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_EVPN: + case SAFI_LABELED_UNICAST: + case SAFI_FLOWSPEC: + case SAFI_MAX: + assert(0); + } + XFREE(MTYPE_RFAPI_IT_EXTRA, hie); + rn->aggregate = NULL; + agg_unlock_node(rn); +} + +/* + * If the child lists are empty, release the rfapi_it_extra struct + */ +void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn) +{ + struct rfapi_it_extra *hie; + + if (!rn) + return; + + if (!rn->aggregate) + return; + + hie = (struct rfapi_it_extra *)(rn->aggregate); + + switch (safi) { + case SAFI_ENCAP: + if (hie->u.encap.e) + return; + break; + + case SAFI_MPLS_VPN: + if (hie->u.vpn.v) + return; + if (hie->u.vpn.mon_eth) { + if (skiplist_count(hie->u.vpn.mon_eth)) + return; + skiplist_free(hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + agg_unlock_node(rn); /* uncount skiplist */ + } + if (hie->u.vpn.e.source) { + if (skiplist_count(hie->u.vpn.e.source)) + return; + skiplist_free(hie->u.vpn.e.source); + hie->u.vpn.e.source = NULL; + agg_unlock_node(rn); + } + if (hie->u.vpn.idx_rd) { + if (skiplist_count(hie->u.vpn.idx_rd)) + return; + skiplist_free(hie->u.vpn.idx_rd); + hie->u.vpn.idx_rd = NULL; + agg_unlock_node(rn); + } + if (hie->u.vpn.mon_eth) { + if (skiplist_count(hie->u.vpn.mon_eth)) + return; + skiplist_free(hie->u.vpn.mon_eth); + hie->u.vpn.mon_eth = NULL; + agg_unlock_node(rn); + } + break; + + case SAFI_UNSPEC: + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_EVPN: + case SAFI_LABELED_UNICAST: + case SAFI_FLOWSPEC: + case SAFI_MAX: + assert(0); + } + XFREE(MTYPE_RFAPI_IT_EXTRA, hie); + rn->aggregate = NULL; + agg_unlock_node(rn); +} + +/* + * returns locked node + */ +struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd, + struct prefix *p) +{ + afi_t afi; + struct agg_node *rn; + + if (RFAPI_0_PREFIX(p)) { + assert(1); + } + + afi = family2afi(p->family); + assert(afi); + + /* + * It's possible that even though there is a route at this node, + * there are no routes with valid UN addresses (i.e,. with no + * valid tunnel routes). Check for that and walk back up the + * tree if necessary. + * + * When the outer loop completes, the matched node, if any, is + * locked (i.e., its reference count has been incremented) to + * account for the VPN monitor we are about to attach. + * + * if a monitor is moved to another node, there must be + * corresponding unlock/locks + */ + for (rn = agg_node_match(rfd->import_table->imported_vpn[afi], p); + rn;) { + + struct bgp_path_info *bpi; + struct prefix pfx_dummy; + + /* TBD update this code to use new valid_interior_count */ + for (bpi = rn->info; bpi; bpi = bpi->next) { + /* + * If there is a cached ENCAP UN address, it's a usable + * VPN route + */ + if (bpi->extra && bpi->extra->vnc.import.un_family) { + break; + } + + /* + * Or if there is a valid Encap Attribute tunnel subtlv + * address, + * it's a usable VPN route. + */ + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_dummy)) { + break; + } + } + if (bpi) + break; + + agg_unlock_node(rn); + if ((rn = agg_node_parent(rn))) { + agg_lock_node(rn); + } + } + + if (!rn) { + struct prefix pfx_default; + + memset(&pfx_default, 0, sizeof(pfx_default)); + pfx_default.family = p->family; + + /* creates default node if none exists, and increments ref count + */ + rn = agg_node_get(rfd->import_table->imported_vpn[afi], + &pfx_default); + } + + return rn; +} + +/* + * If this function happens to attach the monitor to a radix tree + * node (as opposed to the 0-prefix list), the node pointer is + * returned (for the benefit of caller which might like to use it + * to generate an immediate query response). + */ +static struct agg_node *rfapiMonitorAttachImport(struct rfapi_descriptor *rfd, + struct rfapi_monitor_vpn *m) +{ + struct agg_node *rn; + + rfapiMonitorCheckAttachAllowed(); + + if (RFAPI_0_PREFIX(&m->p)) { + /* + * Add new monitor entry to vpn0 list + */ + afi_t afi; + + afi = family2afi(m->p.family); + assert(afi); + + m->next = rfd->import_table->vpn0_queries[afi]; + rfd->import_table->vpn0_queries[afi] = m; + vnc_zlog_debug_verbose("%s: attached monitor %p to vpn0 list", + __func__, m); + return NULL; + } + + /* + * Attach new monitor entry to import table node + */ + rn = rfapiMonitorGetAttachNode(rfd, &m->p); /* returns locked rn */ + m->node = rn; + m->next = RFAPI_MONITOR_VPN(rn); + RFAPI_MONITOR_VPN_W_ALLOC(rn) = m; + RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 0); + vnc_zlog_debug_verbose("%s: attached monitor %p to rn %p", __func__, m, + rn); + return rn; +} + + +/* + * reattach monitors for this HD to import table + */ +void rfapiMonitorAttachImportHd(struct rfapi_descriptor *rfd) +{ + struct agg_node *mrn; + + if (!rfd->mon) { + /* + * No monitors for this HD + */ + return; + } + + for (mrn = agg_route_top(rfd->mon); mrn; mrn = agg_route_next(mrn)) { + + if (!mrn->info) + continue; + + (void)rfapiMonitorAttachImport( + rfd, (struct rfapi_monitor_vpn *)(mrn->info)); + } +} + +/* + * Adds a monitor for a query to the NVE descriptor's list + * and, if callbacks are enabled, attaches it to the import table. + * + * If we happened to locate the import table radix tree attachment + * point, return it so the caller can use it to generate a query + * response without repeating the lookup. Note that when callbacks + * are disabled, this function will not perform a lookup, and the + * caller will have to do its own lookup. + */ +struct agg_node *rfapiMonitorAdd(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *p) +{ + struct rfapi_monitor_vpn *m; + struct agg_node *rn; + + /* + * Initialize nve's monitor list if needed + * NB use the same radix tree for IPv4 and IPv6 targets. + * The prefix will always have full-length mask (/32, /128) + * or be 0/0 so they won't get mixed up. + */ + if (!rfd->mon) { + rfd->mon = agg_table_init(); + } + rn = agg_node_get(rfd->mon, p); + if (rn->info) { + /* + * received this query before, no further action needed + */ + rfapiMonitorTimerRestart((struct rfapi_monitor_vpn *)rn->info); + agg_unlock_node(rn); + return NULL; + } + + /* + * New query for this nve, record it in the HD + */ + rn->info = + XCALLOC(MTYPE_RFAPI_MONITOR, sizeof(struct rfapi_monitor_vpn)); + m = (struct rfapi_monitor_vpn *)(rn->info); + m->rfd = rfd; + prefix_copy(&m->p, p); + + ++rfd->monitor_count; + ++bgp->rfapi->monitor_count; + + rfapiMonitorTimerRestart(m); + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) { + /* + * callbacks turned off, so don't attach monitor to import table + */ + return NULL; + } + + + /* + * attach to import table + */ + return rfapiMonitorAttachImport(rfd, m); +} + +/* + * returns monitor pointer if found, NULL if not + */ +static struct rfapi_monitor_vpn * +rfapiMonitorDetachImport(struct rfapi_monitor_vpn *m) +{ + struct rfapi_monitor_vpn *prev; + struct rfapi_monitor_vpn *this = NULL; + + if (RFAPI_0_PREFIX(&m->p)) { + afi_t afi; + + /* + * 0-prefix monitors are stored in a special list and not + * in the import VPN tree + */ + + afi = family2afi(m->p.family); + assert(afi); + + if (m->rfd->import_table) { + for (prev = NULL, + this = m->rfd->import_table->vpn0_queries[afi]; + this; prev = this, this = this->next) { + + if (this == m) + break; + } + if (this) { + if (!prev) { + m->rfd->import_table + ->vpn0_queries[afi] = + this->next; + } else { + prev->next = this->next; + } + } + } + } else { + + if (m->node) { + for (prev = NULL, this = RFAPI_MONITOR_VPN(m->node); + this; prev = this, this = this->next) { + + if (this == m) + break; + } + if (this) { + if (prev) { + prev->next = this->next; + } else { + RFAPI_MONITOR_VPN_W_ALLOC(m->node) = + this->next; + } + RFAPI_CHECK_REFCOUNT(m->node, SAFI_MPLS_VPN, 1); + agg_unlock_node(m->node); + } + m->node = NULL; + } + } + return this; +} + + +void rfapiMonitorDetachImportHd(struct rfapi_descriptor *rfd) +{ + struct agg_node *rn; + + if (!rfd->mon) + return; + + for (rn = agg_route_top(rfd->mon); rn; rn = agg_route_next(rn)) { + if (rn->info) { + rfapiMonitorDetachImport( + (struct rfapi_monitor_vpn *)(rn->info)); + } + } +} + +void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *p) +{ + struct agg_node *rn; + struct rfapi_monitor_vpn *m; + + assert(rfd->mon); + rn = agg_node_get(rfd->mon, p); /* locks node */ + m = rn->info; + + assert(m); + + /* + * remove from import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + rfapiMonitorDetachImport(m); + } + + EVENT_OFF(m->timer); + + /* + * remove from rfd list + */ + XFREE(MTYPE_RFAPI_MONITOR, m); + rn->info = NULL; + agg_unlock_node(rn); /* undo original lock when created */ + agg_unlock_node(rn); /* undo lock in agg_node_get */ + + --rfd->monitor_count; + --bgp->rfapi->monitor_count; +} + +/* + * returns count of monitors deleted + */ +int rfapiMonitorDelHd(struct rfapi_descriptor *rfd) +{ + struct agg_node *rn; + struct bgp *bgp; + int count = 0; + + vnc_zlog_debug_verbose("%s: entry rfd=%p", __func__, rfd); + + bgp = bgp_get_default(); + + if (rfd->mon) { + for (rn = agg_route_top(rfd->mon); rn; + rn = agg_route_next(rn)) { + struct rfapi_monitor_vpn *m; + if ((m = rn->info)) { + if (!(bgp->rfapi_cfg->flags + & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + rfapiMonitorDetachImport(m); + } + + EVENT_OFF(m->timer); + + XFREE(MTYPE_RFAPI_MONITOR, m); + rn->info = NULL; + agg_unlock_node(rn); /* undo original lock + when created */ + ++count; + --rfd->monitor_count; + --bgp->rfapi->monitor_count; + } + } + agg_table_finish(rfd->mon); + rfd->mon = NULL; + } + + if (rfd->mon_eth) { + + struct rfapi_monitor_eth *mon_eth; + + while (!skiplist_first(rfd->mon_eth, NULL, (void **)&mon_eth)) { + + int rc; + + if (!(bgp->rfapi_cfg->flags + & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + rfapiMonitorEthDetachImport(bgp, mon_eth); + } else { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: callbacks disabled, not attempting to detach mon_eth %p", + __func__, mon_eth); +#endif + } + + EVENT_OFF(mon_eth->timer); + + /* + * remove from rfd list + */ + rc = skiplist_delete(rfd->mon_eth, mon_eth, mon_eth); + assert(!rc); + + vnc_zlog_debug_verbose("%s: freeing mon_eth %p", + __func__, mon_eth); + XFREE(MTYPE_RFAPI_MONITOR_ETH, mon_eth); + + ++count; + --rfd->monitor_count; + --bgp->rfapi->monitor_count; + } + skiplist_free(rfd->mon_eth); + rfd->mon_eth = NULL; + } + + return count; +} + +void rfapiMonitorResponseRemovalOff(struct bgp *bgp) +{ + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE) { + return; + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; +} + +void rfapiMonitorResponseRemovalOn(struct bgp *bgp) +{ + if (!(bgp->rfapi_cfg->flags + & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) { + return; + } + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE; +} + +static void rfapiMonitorTimerExpire(struct event *t) +{ + struct rfapi_monitor_vpn *m = EVENT_ARG(t); + + /* forget reference to thread, it's gone */ + m->timer = NULL; + + /* delete the monitor */ + rfapiMonitorDel(bgp_get_default(), m->rfd, &m->p); +} + +static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m) +{ + unsigned long remain = event_timer_remain_second(m->timer); + + /* unexpected case, but avoid wraparound problems below */ + if (remain > m->rfd->response_lifetime) + return; + + /* don't restart if we just restarted recently */ + if (m->rfd->response_lifetime - remain < 2) + return; + + EVENT_OFF(m->timer); + + { + char buf[BUFSIZ]; + + vnc_zlog_debug_verbose( + "%s: target %s life %u", __func__, + rfapi_ntop(m->p.family, m->p.u.val, buf, BUFSIZ), + m->rfd->response_lifetime); + } + + event_add_timer(bm->master, rfapiMonitorTimerExpire, m, + m->rfd->response_lifetime, &m->timer); +} + +/* + * called when an updated response is sent to the NVE. Per + * ticket 255, restart timers for any monitors that could have + * been responsible for the response, i.e., any monitors for + * the exact prefix or a parent of it. + */ +void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, + const struct prefix *p) +{ + struct agg_node *rn; + + if (AF_ETHERNET == p->family) { + struct rfapi_monitor_eth *mon_eth; + int rc; + void *cursor; + + /* + * XXX match any LNI + */ + for (cursor = NULL, + rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon_eth, + &cursor); + rc == 0; rc = skiplist_next(rfd->mon_eth, NULL, + (void **)&mon_eth, &cursor)) { + + if (!memcmp(mon_eth->macaddr.octet, + p->u.prefix_eth.octet, ETH_ALEN)) { + + rfapiMonitorEthTimerRestart(mon_eth); + } + } + + } else { + for (rn = agg_route_top(rfd->mon); rn; + rn = agg_route_next(rn)) { + struct rfapi_monitor_vpn *m; + const struct prefix *p_node; + + if (!((m = rn->info))) + continue; + + p_node = agg_node_get_prefix(m->node); + /* NB order of test is significant ! */ + if (!m->node || prefix_match(p_node, p)) { + rfapiMonitorTimerRestart(m); + } + } + } +} + +/* + * Find monitors at this node and all its parents. Call + * rfapiRibUpdatePendingNode with this node and all corresponding NVEs. + */ +void rfapiMonitorItNodeChanged( + struct rfapi_import_table *import_table, struct agg_node *it_node, + struct rfapi_monitor_vpn *monitor_list) /* for base it node, NULL=all */ +{ + struct skiplist *nves_seen; + struct agg_node *rn = it_node; + struct bgp *bgp = bgp_get_default(); + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); + + assert(bgp); + assert(import_table); + + nves_seen = skiplist_new(0, NULL, NULL); + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: it=%p, it_node=%p, it_node->prefix=%pFX", + __func__, import_table, it_node, &it_node->p); +#endif + + if (AFI_L2VPN == afi) { + struct rfapi_monitor_eth *m; + struct skiplist *sl; + void *cursor; + int rc; + + if ((sl = RFAPI_MONITOR_ETH(rn))) { + + for (cursor = NULL, + rc = skiplist_next(sl, NULL, (void **)&m, &cursor); + !rc; rc = skiplist_next(sl, NULL, (void **)&m, + &cursor)) { + + if (skiplist_search(nves_seen, m->rfd, NULL)) { + /* + * Haven't done this NVE yet. Add to + * "seen" list. + */ + assert(!skiplist_insert(nves_seen, + m->rfd, NULL)); + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode( + bgp, m->rfd, import_table, + it_node, + m->rfd->response_lifetime); + } + } + } + + } else { + + struct rfapi_monitor_vpn *m; + + if (monitor_list) { + m = monitor_list; + } else { + m = RFAPI_MONITOR_VPN(rn); + } + + do { + /* + * If we have reached the root node (parent==NULL) and + * there + * are no routes here (info==NULL), and the IT node that + * changed was not the root node (it_node->parent != + * NULL), + * then any monitors at this node are here because they + * had + * no match at all. Therefore, do not send route updates + * to them + * because we haven't sent them an initial route. + */ + if (!agg_node_parent(rn) && !rn->info + && it_node->parent) + break; + + for (; m; m = m->next) { + + if (RFAPI_0_PREFIX(&m->p)) { + /* shouldn't happen, but be safe */ + continue; + } + if (skiplist_search(nves_seen, m->rfd, NULL)) { + /* + * Haven't done this NVE yet. Add to + * "seen" list. + */ + assert(!skiplist_insert(nves_seen, + m->rfd, NULL)); + + vnc_zlog_debug_verbose( + "%s: update rfd %p attached to pfx %pRN (targ=%pFX)", + __func__, m->rfd, m->node, + &m->p); + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode( + bgp, m->rfd, import_table, + it_node, + m->rfd->response_lifetime); + } + } + rn = agg_node_parent(rn); + if (rn) + m = RFAPI_MONITOR_VPN(rn); + } while (rn); + } + + /* + * All-routes L2 monitors + */ + if (AFI_L2VPN == afi) { + struct rfapi_monitor_eth *e; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: checking L2 all-routes monitors", + __func__); +#endif + + for (e = import_table->eth0_queries; e; e = e->next) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: checking eth0 mon=%p", + __func__, e); +#endif + if (skiplist_search(nves_seen, e->rfd, NULL)) { + /* + * Haven't done this NVE yet. Add to "seen" + * list. + */ + assert(!skiplist_insert(nves_seen, e->rfd, + NULL)); + +/* + * update its RIB + */ +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: found L2 all-routes monitor %p", + __func__, e); +#endif + rfapiRibUpdatePendingNode( + bgp, e->rfd, import_table, it_node, + e->rfd->response_lifetime); + } + } + } else { + struct rfapi_monitor_vpn *m; + + /* + * All-routes IPv4. IPv6 monitors + */ + for (m = import_table->vpn0_queries[afi]; m; m = m->next) { + if (skiplist_search(nves_seen, m->rfd, NULL)) { + /* + * Haven't done this NVE yet. Add to "seen" + * list. + */ + assert(!skiplist_insert(nves_seen, m->rfd, + NULL)); + + /* + * update its RIB + */ + rfapiRibUpdatePendingNode( + bgp, m->rfd, import_table, it_node, + m->rfd->response_lifetime); + } + } + } + + skiplist_free(nves_seen); +} + +/* + * For the listed monitors, update new node and its subtree, but + * omit old node and its subtree + */ +void rfapiMonitorMovedUp(struct rfapi_import_table *import_table, + struct agg_node *old_node, struct agg_node *new_node, + struct rfapi_monitor_vpn *monitor_list) +{ + struct bgp *bgp = bgp_get_default(); + struct rfapi_monitor_vpn *m; + + assert(new_node); + assert(old_node); + assert(new_node != old_node); + + /* + * If new node is 0/0 and there is no route there, don't + * generate an update because it will not contain any + * routes including the target. + */ + if (!new_node->parent && !new_node->info) { + vnc_zlog_debug_verbose( + "%s: new monitor at 0/0 and no routes, no updates", + __func__); + return; + } + + for (m = monitor_list; m; m = m->next) { + rfapiRibUpdatePendingNode(bgp, m->rfd, import_table, new_node, + m->rfd->response_lifetime); + rfapiRibUpdatePendingNodeSubtree(bgp, m->rfd, import_table, + new_node, old_node, + m->rfd->response_lifetime); + } +} + +static void rfapiMonitorEthTimerExpire(struct event *t) +{ + struct rfapi_monitor_eth *m = EVENT_ARG(t); + + /* forget reference to thread, it's gone */ + m->timer = NULL; + + /* delete the monitor */ + rfapiMonitorEthDel(bgp_get_default(), m->rfd, &m->macaddr, + m->logical_net_id); + +} + +static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m) +{ + unsigned long remain = event_timer_remain_second(m->timer); + + /* unexpected case, but avoid wraparound problems below */ + if (remain > m->rfd->response_lifetime) + return; + + /* don't restart if we just restarted recently */ + if (m->rfd->response_lifetime - remain < 2) + return; + + EVENT_OFF(m->timer); + + { + char buf[BUFSIZ]; + + vnc_zlog_debug_verbose( + "%s: target %s life %u", __func__, + rfapiEthAddr2Str(&m->macaddr, buf, BUFSIZ), + m->rfd->response_lifetime); + } + + event_add_timer(bm->master, rfapiMonitorEthTimerExpire, m, + m->rfd->response_lifetime, &m->timer); +} + +static int mon_eth_cmp(const void *a, const void *b) +{ + const struct rfapi_monitor_eth *m1; + const struct rfapi_monitor_eth *m2; + + int i; + + m1 = (struct rfapi_monitor_eth *)a; + m2 = (struct rfapi_monitor_eth *)b; + + /* + * compare ethernet addresses + */ + for (i = 0; i < ETH_ALEN; ++i) { + if (m1->macaddr.octet[i] != m2->macaddr.octet[i]) + return (m1->macaddr.octet[i] - m2->macaddr.octet[i]); + } + + /* + * compare LNIs + */ + return (m1->logical_net_id - m2->logical_net_id); +} + +static void rfapiMonitorEthAttachImport( + struct rfapi_import_table *it, + struct agg_node *rn, /* it node attach point if non-0 */ + struct rfapi_monitor_eth *mon) /* monitor struct to attach */ +{ + struct skiplist *sl; + int rc; + + vnc_zlog_debug_verbose("%s: it=%p", __func__, it); + + rfapiMonitorCheckAttachAllowed(); + + if (RFAPI_0_ETHERADDR(&mon->macaddr)) { + /* + * These go on a different list + */ + mon->next = it->eth0_queries; + it->eth0_queries = mon; +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: attached monitor %p to eth0 list", + __func__, mon); +#endif + return; + } + + if (rn == NULL) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: rn is null!", __func__); +#endif + return; + } + + /* + * Get sl to attach to + */ + sl = RFAPI_MONITOR_ETH_W_ALLOC(rn); + if (!sl) { + sl = RFAPI_MONITOR_ETH_W_ALLOC(rn) = + skiplist_new(0, NULL, NULL); + agg_lock_node(rn); /* count skiplist mon_eth */ + } + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: rn=%p, rn->lock=%d, sl=%p, attaching eth mon %p", __func__, + rn, rn->lock, sl, mon); +#endif + + rc = skiplist_insert(sl, (void *)mon, (void *)mon); + assert(!rc); + + /* count eth monitor */ + agg_lock_node(rn); +} + +/* + * reattach monitors for this HD to import table + */ +static void rfapiMonitorEthAttachImportHd(struct bgp *bgp, + struct rfapi_descriptor *rfd) +{ + void *cursor; + struct rfapi_monitor_eth *mon; + int rc; + + if (!rfd->mon_eth) { + /* + * No monitors for this HD + */ + return; + } + + for (cursor = NULL, + rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon, &cursor); + rc == 0; + rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon, &cursor)) { + + struct rfapi_import_table *it; + struct prefix pfx_mac_buf; + struct agg_node *rn; + + it = rfapiMacImportTableGet(bgp, mon->logical_net_id); + assert(it); + + memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = mon->macaddr; + + rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf); + assert(rn); + + (void)rfapiMonitorEthAttachImport(it, rn, mon); + } +} + +static void rfapiMonitorEthDetachImport( + struct bgp *bgp, + struct rfapi_monitor_eth *mon) /* monitor struct to detach */ +{ + struct rfapi_import_table *it; + struct prefix pfx_mac_buf; + struct skiplist *sl; + struct agg_node *rn; + int rc; + + it = rfapiMacImportTableGet(bgp, mon->logical_net_id); + assert(it); + + if (RFAPI_0_ETHERADDR(&mon->macaddr)) { + struct rfapi_monitor_eth *prev; + struct rfapi_monitor_eth *this = NULL; + + for (prev = NULL, this = it->eth0_queries; this; + prev = this, this = this->next) { + + if (this == mon) + break; + } + if (this) { + if (!prev) { + it->eth0_queries = this->next; + } else { + prev->next = this->next; + } + } +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: it=%p, LNI=%d, detached eth0 mon %p", __func__, it, + mon->logical_net_id, mon); +#endif + return; + } + + memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = mon->macaddr; + + rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf); + assert(rn); + + /* + * Get sl to detach from + */ + sl = RFAPI_MONITOR_ETH(rn); +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: it=%p, rn=%p, rn->lock=%d, sl=%p, pfx=%pFX, LNI=%d, detaching eth mon %p", + __func__, it, rn, rn->lock, sl, agg_node_get_prefix(rn), + mon->logical_net_id, mon); +#endif + assert(sl); + + + rc = skiplist_delete(sl, (void *)mon, (void *)mon); + assert(!rc); + + /* uncount eth monitor */ + agg_unlock_node(rn); +} + +struct agg_node *rfapiMonitorEthAdd(struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id) +{ + int rc; + struct rfapi_monitor_eth mon_buf; + struct rfapi_monitor_eth *val; + struct rfapi_import_table *it; + struct agg_node *rn = NULL; + struct prefix pfx_mac_buf; + + if (!rfd->mon_eth) { + rfd->mon_eth = skiplist_new(0, mon_eth_cmp, NULL); + } + + it = rfapiMacImportTableGet(bgp, logical_net_id); + assert(it); + + /* + * Get route node in import table. Here is where we attach the + * monitor. + * + * Look it up now because we return it to caller regardless of + * whether we create a new monitor or not. + */ + memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix)); + pfx_mac_buf.family = AF_ETHERNET; + pfx_mac_buf.prefixlen = 48; + pfx_mac_buf.u.prefix_eth = *macaddr; + + if (!RFAPI_0_ETHERADDR(macaddr)) { + rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf); + assert(rn); + } + + memset((void *)&mon_buf, 0, sizeof(mon_buf)); + mon_buf.rfd = rfd; + mon_buf.macaddr = *macaddr; + mon_buf.logical_net_id = logical_net_id; + + { + char buf[BUFSIZ]; + + vnc_zlog_debug_verbose( + "%s: LNI=%d: rfd=%p, pfx=%s", __func__, logical_net_id, + rfd, rfapi_ntop(pfx_mac_buf.family, pfx_mac_buf.u.val, + buf, BUFSIZ)); + } + + + /* + * look up query + */ + rc = skiplist_search(rfd->mon_eth, (void *)&mon_buf, (void **)&val); + if (!rc) { + /* + * Found monitor - we have seen this query before + * restart timer + */ + vnc_zlog_debug_verbose( + "%s: already present in rfd->mon_eth, not adding", + __func__); + rfapiMonitorEthTimerRestart(val); + return rn; + } + + /* + * New query + */ + val = XCALLOC(MTYPE_RFAPI_MONITOR_ETH, + sizeof(struct rfapi_monitor_eth)); + assert(val); + *val = mon_buf; + + ++rfd->monitor_count; + ++bgp->rfapi->monitor_count; + + rc = skiplist_insert(rfd->mon_eth, val, val); + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: inserted rfd=%p mon_eth=%p, rc=%d", + __func__, rfd, val, rc); +#else + (void)rc; +#endif + + /* + * start timer + */ + rfapiMonitorEthTimerRestart(val); + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) { +/* + * callbacks turned off, so don't attach monitor to import table + */ +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: callbacks turned off, not attaching mon_eth %p to import table", + __func__, val); +#endif + return rn; + } + + /* + * attach to import table + */ + rfapiMonitorEthAttachImport(it, rn, val); + + return rn; +} + +void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, uint32_t logical_net_id) +{ + struct rfapi_monitor_eth *val; + struct rfapi_monitor_eth mon_buf; + int rc; + + vnc_zlog_debug_verbose("%s: entry rfd=%p", __func__, rfd); + + assert(rfd->mon_eth); + + memset((void *)&mon_buf, 0, sizeof(mon_buf)); + mon_buf.macaddr = *macaddr; + mon_buf.logical_net_id = logical_net_id; + + rc = skiplist_search(rfd->mon_eth, (void *)&mon_buf, (void **)&val); + assert(!rc); + + /* + * remove from import table + */ + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + rfapiMonitorEthDetachImport(bgp, val); + } + + EVENT_OFF(val->timer); + + /* + * remove from rfd list + */ + rc = skiplist_delete(rfd->mon_eth, val, val); + assert(!rc); + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: freeing mon_eth %p", __func__, val); +#endif + XFREE(MTYPE_RFAPI_MONITOR_ETH, val); + + --rfd->monitor_count; + --bgp->rfapi->monitor_count; +} + + +void rfapiMonitorCallbacksOff(struct bgp *bgp) +{ + struct rfapi_import_table *it; + afi_t afi; + struct agg_table *rt; + struct agg_node *rn; + void *cursor; + int rc; + struct rfapi *h = bgp->rfapi; + + if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) { + /* + * Already off. + */ + return; + } + bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: turned off callbacks", __func__); +#endif + + if (h == NULL) + return; + /* + * detach monitors from import VPN tables. The monitors + * will still be linked in per-nve monitor lists. + */ + for (it = h->imports; it; it = it->next) { + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct rfapi_monitor_vpn *m; + struct rfapi_monitor_vpn *next; + + rt = it->imported_vpn[afi]; + + for (rn = agg_route_top(rt); rn; + rn = agg_route_next(rn)) { + m = RFAPI_MONITOR_VPN(rn); + if (RFAPI_MONITOR_VPN(rn)) + RFAPI_MONITOR_VPN_W_ALLOC(rn) = NULL; + for (; m; m = next) { + next = m->next; + m->next = + NULL; /* gratuitous safeness */ + m->node = NULL; + agg_unlock_node(rn); /* uncount */ + } + } + + for (m = it->vpn0_queries[afi]; m; m = next) { + next = m->next; + m->next = NULL; /* gratuitous safeness */ + m->node = NULL; + } + it->vpn0_queries[afi] = NULL; /* detach first monitor */ + } + } + + /* + * detach monitors from import Eth tables. The monitors + * will still be linked in per-nve monitor lists. + */ + + /* + * Loop over ethernet import tables + */ + for (cursor = NULL, + rc = skiplist_next(h->import_mac, NULL, (void **)&it, &cursor); + !rc; + rc = skiplist_next(h->import_mac, NULL, (void **)&it, &cursor)) { + struct rfapi_monitor_eth *e; + struct rfapi_monitor_eth *enext; + + /* + * The actual route table + */ + rt = it->imported_vpn[AFI_L2VPN]; + + /* + * Find non-0 monitors (i.e., actual addresses, not FTD + * monitors) + */ + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + struct skiplist *sl; + + sl = RFAPI_MONITOR_ETH(rn); + while (!skiplist_delete_first(sl)) { + agg_unlock_node(rn); /* uncount monitor */ + } + } + + /* + * Find 0-monitors (FTD queries) + */ + for (e = it->eth0_queries; e; e = enext) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: detaching eth0 mon %p", + __func__, e); +#endif + enext = e->next; + e->next = NULL; /* gratuitous safeness */ + } + it->eth0_queries = NULL; /* detach first monitor */ + } +} + +void rfapiMonitorCallbacksOn(struct bgp *bgp) +{ + struct listnode *hnode; + struct rfapi_descriptor *rfd; + + if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) { + /* + * Already on. It's important that we don't try to reattach + * monitors that are already attached because, in the interest + * of performance, there is no checking at the lower level + * whether a monitor is already attached. It leads to + * corrupted chains (e.g., looped pointers) + */ + return; + } + bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE; +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: turned on callbacks", __func__); +#endif + if (bgp->rfapi == NULL) + return; + + /* + * reattach monitors + */ + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) { + + rfapiMonitorAttachImportHd(rfd); + rfapiMonitorEthAttachImportHd(bgp, rfd); + } +} diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h new file mode 100644 index 0000000..3200079 --- /dev/null +++ b/bgpd/rfapi/rfapi_monitor.h @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef QUAGGA_HGP_RFAPI_MONITOR_H +#define QUAGGA_HGP_RFAPI_MONITOR_H + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/table.h" + +/* + * These get attached to the nodes in an import table (using "aggregate" ptr) + * to indicate which nves are interested in a prefix/target + */ +struct rfapi_monitor_vpn { + struct rfapi_monitor_vpn *next; /* chain from struct agg_node */ + struct rfapi_descriptor *rfd; /* which NVE requested the route */ + struct prefix p; /* constant: pfx in original request */ + struct agg_node *node; /* node we're currently attached to */ + uint32_t flags; +#define RFAPI_MON_FLAG_NEEDCALLBACK 0x00000001 /* deferred callback */ + + // int dcount; /* debugging counter */ + struct event *timer; +}; + +struct rfapi_monitor_encap { + struct rfapi_monitor_encap *next; + struct rfapi_monitor_encap *prev; + struct agg_node *node; /* VPN node */ + struct bgp_path_info *bpi; /* VPN bpi */ + struct agg_node *rn; /* parent node */ +}; + +struct rfapi_monitor_eth { + struct rfapi_monitor_eth *next; /* for use in vpn0_queries list */ + struct rfapi_descriptor *rfd; /* which NVE requested the route */ + struct ethaddr macaddr; + uint32_t logical_net_id; + struct event *timer; +}; + +/* + * This is referenced by the "aggregate" field of a route node + * in an RFAPI import table. + * + * node lock/unlock: + * - one lock increment for this structure itself + * - one lock per chained struct rfapi_monitor_vpn + * - one lock for the mon_eth skiplist itself + * - one lock per mon_eth skiplist entry + * - one lock for the ext skiplist itself + * - one lock for each ext skiplist entry + * remember to free skiplist when freeing rfapi_it_extra + * - one lock per chained struct rfapi_monitor_encap + * + */ +struct rfapi_it_extra { + union { + struct { + struct rfapi_monitor_vpn *v; + struct skiplist *idx_rd; /* RD index */ + struct skiplist *mon_eth; /* ether queries */ + struct { + /* routes with UN addrs, either cached encap or + * Encap TLV */ + int valid_interior_count; + + /* unicast exterior routes, key=bpi, + * val=allocated prefix */ + struct skiplist *source; + } e; + } vpn; + struct { + struct rfapi_monitor_encap *e; + } encap; + } u; +}; + +#define RFAPI_IT_EXTRA_GET(rn) \ + ((struct rfapi_it_extra \ + *)((rn)->aggregate \ + ? (rn)->aggregate \ + : (agg_lock_node(rn), \ + (rn)->aggregate = XCALLOC( \ + MTYPE_RFAPI_IT_EXTRA, \ + sizeof(struct rfapi_it_extra))))) + +#define RFAPI_RDINDEX(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd : NULL) + +#define RFAPI_RDINDEX_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd) + +#define RFAPI_MONITOR_ETH(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth : NULL) + +#define RFAPI_MONITOR_ETH_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth) + +#define RFAPI_MONITOR_VPN(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.v : NULL) + +#define RFAPI_MONITOR_VPN_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.v) + +#define RFAPI_MONITOR_ENCAP(rn) \ + ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.encap.e : NULL) + +#define RFAPI_MONITOR_ENCAP_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.encap.e) + +#define RFAPI_MONITOR_EXTERIOR(rn) (&(RFAPI_IT_EXTRA_GET(rn)->u.vpn.e)) + +#define RFAPI_HAS_MONITOR_EXTERIOR(rn) \ + (rn && rn->aggregate \ + && ((struct rfapi_it_extra *)(rn->aggregate))->u.vpn.e.source \ + && !skiplist_first(((struct rfapi_it_extra *)(rn->aggregate)) \ + ->u.vpn.e.source, \ + NULL, NULL)) + +extern void rfapiMonitorLoopCheck(struct rfapi_monitor_vpn *mchain); + +extern void rfapiMonitorCleanCheck(struct bgp *bgp); + +extern void rfapiMonitorCheckAttachAllowed(void); + +extern void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn); + +extern struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd, + struct prefix *p); + +extern void rfapiMonitorAttachImportHd(struct rfapi_descriptor *rfd); + +extern struct agg_node *rfapiMonitorAdd(struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct prefix *p); + +extern void rfapiMonitorDetachImportHd(struct rfapi_descriptor *rfd); + +extern void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct prefix *p); + +extern int rfapiMonitorDelHd(struct rfapi_descriptor *rfd); + +extern void rfapiMonitorCallbacksOff(struct bgp *bgp); + +extern void rfapiMonitorCallbacksOn(struct bgp *bgp); + +extern void rfapiMonitorResponseRemovalOff(struct bgp *bgp); + +extern void rfapiMonitorResponseRemovalOn(struct bgp *bgp); + +extern void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn); + +extern void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd, + const struct prefix *p); + +extern void rfapiMonitorItNodeChanged(struct rfapi_import_table *import_table, + struct agg_node *it_node, + struct rfapi_monitor_vpn *monitor_list); + +extern void rfapiMonitorMovedUp(struct rfapi_import_table *import_table, + struct agg_node *old_node, + struct agg_node *new_node, + struct rfapi_monitor_vpn *monitor_list); + +extern struct agg_node *rfapiMonitorEthAdd(struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id); + +extern void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct ethaddr *macaddr, + uint32_t logical_net_id); + +#endif /* QUAGGA_HGP_RFAPI_MONITOR_H */ diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c new file mode 100644 index 0000000..eabec2f --- /dev/null +++ b/bgpd/rfapi/rfapi_nve_addr.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/skiplist.h" + + +#include "bgpd/bgpd.h" + +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" + +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_nve_addr.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define DEBUG_NVE_ADDR 0 + +void rfapiNveAddr2Str(struct rfapi_nve_addr *, char *, int); + + +#if DEBUG_NVE_ADDR +static void logdifferent(const char *tag, struct rfapi_nve_addr *a, + struct rfapi_nve_addr *b) +{ + char a_str[BUFSIZ]; + char b_str[BUFSIZ]; + + rfapiNveAddr2Str(a, a_str, BUFSIZ); + rfapiNveAddr2Str(b, b_str, BUFSIZ); + vnc_zlog_debug_verbose("%s: [%s] [%s]", tag, a_str, b_str); +} +#endif + + +int rfapi_nve_addr_cmp(const void *k1, const void *k2) +{ + const struct rfapi_nve_addr *a = (struct rfapi_nve_addr *)k1; + const struct rfapi_nve_addr *b = (struct rfapi_nve_addr *)k2; + int ret = 0; + + if (!a || !b) { +#if DEBUG_NVE_ADDR + vnc_zlog_debug_verbose("%s: missing address a=%p b=%p", + __func__, a, b); +#endif + return (a - b); + } + if (a->un.addr_family != b->un.addr_family) { +#if DEBUG_NVE_ADDR + vnc_zlog_debug_verbose( + "diff: UN addr fam a->un.af=%d, b->un.af=%d", + a->un.addr_family, b->un.addr_family); +#endif + return (a->un.addr_family - b->un.addr_family); + } + if (a->un.addr_family == AF_INET) { + ret = IPV4_ADDR_CMP(&a->un.addr.v4, &b->un.addr.v4); + if (ret != 0) { +#if DEBUG_NVE_ADDR + logdifferent("diff: UN addr", a, b); +#endif + return ret; + } + } else if (a->un.addr_family == AF_INET6) { + ret = IPV6_ADDR_CMP(&a->un.addr.v6, &b->un.addr.v6); + if (ret == 0) { +#if DEBUG_NVE_ADDR + logdifferent("diff: UN addr", a, b); +#endif + return ret; + } + } else { + assert(0); + } + if (a->vn.addr_family != b->vn.addr_family) { +#if DEBUG_NVE_ADDR + vnc_zlog_debug_verbose( + "diff: pT addr fam a->vn.af=%d, b->vn.af=%d", + a->vn.addr_family, b->vn.addr_family); +#endif + return (a->vn.addr_family - b->vn.addr_family); + } + if (a->vn.addr_family == AF_INET) { + ret = IPV4_ADDR_CMP(&a->vn.addr.v4, &b->vn.addr.v4); + if (ret != 0) { +#if DEBUG_NVE_ADDR + logdifferent("diff: VN addr", a, b); +#endif + return ret; + } + } else if (a->vn.addr_family == AF_INET6) { + ret = IPV6_ADDR_CMP(&a->vn.addr.v6, &b->vn.addr.v6); + if (ret == 0) { +#if DEBUG_NVE_ADDR + logdifferent("diff: VN addr", a, b); +#endif + return ret; + } + } else { + assert(0); + } + return 0; +} + +void rfapiNveAddr2Str(struct rfapi_nve_addr *na, char *buf, int bufsize) +{ + char *p = buf; + int r; + +#define REMAIN (bufsize - (p-buf)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + if (bufsize < 1) + return; + + r = snprintf(p, REMAIN, "VN="); + INCP; + + if (!rfapiRfapiIpAddr2Str(&na->vn, p, REMAIN)) + goto done; + + buf[bufsize - 1] = 0; + p = buf + strlen(buf); + + r = snprintf(p, REMAIN, ", UN="); + INCP; + + rfapiRfapiIpAddr2Str(&na->un, p, REMAIN); + +done: + buf[bufsize - 1] = 0; +} diff --git a/bgpd/rfapi/rfapi_nve_addr.h b/bgpd/rfapi/rfapi_nve_addr.h new file mode 100644 index 0000000..49e9fc5 --- /dev/null +++ b/bgpd/rfapi/rfapi_nve_addr.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_BGP_RFAPI_NVE_ADDR_H +#define _QUAGGA_BGP_RFAPI_NVE_ADDR_H + +#include "rfapi.h" + +struct rfapi_nve_addr { + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + void *info; +}; + + +extern int rfapi_nve_addr_cmp(const void *k1, const void *k2); + +extern void rfapiNveAddr2Str(struct rfapi_nve_addr *na, char *buf, int bufsize); + + +#endif /* _QUAGGA_BGP_RFAPI_NVE_ADDR_H */ diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h new file mode 100644 index 0000000..2161d43 --- /dev/null +++ b/bgpd/rfapi/rfapi_private.h @@ -0,0 +1,400 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * Internal definitions for RFAPI. Not for use by other code + */ + +#ifndef _QUAGGA_BGP_RFAPI_PRIVATE_H +#define _QUAGGA_BGP_RFAPI_PRIVATE_H + +#include "lib/linklist.h" +#include "lib/skiplist.h" +#include "lib/workqueue.h" + +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" + +#include "rfapi.h" + +/* + * Lists of rfapi_adb. Each rfapi_adb is referenced twice: + * + * 1. each is referenced in by_lifetime + * 2. each is referenced by exactly one of: ipN_by_prefix, ip0_by_ether + */ +struct rfapi_advertised_prefixes { + struct skiplist *ipN_by_prefix; /* all except 0/32, 0/128 */ + struct skiplist *ip0_by_ether; /* ip prefix 0/32, 0/128 */ + struct skiplist *by_lifetime; /* all */ +}; + +struct rfapi_descriptor { + struct agg_node *un_node; /* backref to un table */ + + struct rfapi_descriptor *next; /* next vn_addr */ + + /* supplied by client */ + struct bgp *bgp; /* from rfp_start_val */ + struct rfapi_ip_addr vn_addr; + struct rfapi_ip_addr un_addr; + rfapi_response_cb_t *response_cb; /* override per-bgp response_cb */ + void *cookie; /* for callbacks */ + struct rfapi_tunneltype_option default_tunneltype_option; + + /* supplied by matched configuration */ + struct prefix_rd rd; + struct ecommunity *rt_export_list; + uint32_t response_lifetime; + + /* list of prefixes currently being advertised by this nve */ + struct rfapi_advertised_prefixes advertised; + + time_t open_time; + + uint32_t max_prefix_lifetime; + uint32_t min_prefix_lifetime; + + /* reference to this nve's import table */ + struct rfapi_import_table *import_table; + + uint32_t monitor_count; + struct agg_table *mon; /* rfapi_monitors */ + struct skiplist *mon_eth; /* ethernet monitors */ + + /* + * rib RIB as seen by NVE + * rib_pending RIB containing nodes with updated info chains + * rsp_times last time we sent response containing pfx + */ + uint32_t rib_prefix_count; /* pfxes with routes */ + struct agg_table *rib[AFI_MAX]; + struct agg_table *rib_pending[AFI_MAX]; + struct work_queue *updated_responses_queue; + struct agg_table *rsp_times[AFI_MAX]; + + uint32_t rsp_counter; /* dedup initial rsp */ + time_t rsp_time; /* dedup initial rsp */ + time_t ftd_last_allowed_time; /* FTD filter */ + + unsigned int stat_count_nh_reachable; + unsigned int stat_count_nh_removal; + + /* + * points to the original nve group structure that matched + * when this nve_descriptor was created. We use this pointer + * in rfapi_close() to find the nve group structure and + * delete its reference back to us. + * + * If the nve group structure is deleted (via configuration + * change) while this nve_descriptor exists, this rfg pointer + * will be set to NULL. + */ + struct rfapi_nve_group_cfg *rfg; + + /* + * This ~7kB structure is here to permit multiple routes for + * a prefix to be injected to BGP. There are at least two + * situations where such conditions obtain: + * + * When an VNC route is exported to BGP on behalf of the set of + * NVEs that belong to the export NVE group, it is replicated + * so that there is one route per NVE (and the route's nexthop + * is the NVE's VN address). + * + * Each of these routes being injected to BGP must have a distinct + * peer pointer (otherwise, if they have the same peer pointer, each + * route will be considered an implicit waithdraw of the previous + * route injected from that peer, and the new route will replace + * rather than augment the old one(s)). + */ + struct peer *peer; + + uint32_t flags; +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP 0x00000001 +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6 0x00000002 +#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_L2VPN 0x00000004 +#define RFAPI_HD_FLAG_PROVISIONAL 0x00000008 +#define RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY 0x00000010 +#define RFAPI_HD_FLAG_IS_VRF 0x00000012 +}; + +#define RFAPI_QUEUED_FLAG(afi) \ + (((afi) == AFI_IP) \ + ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP \ + : (((afi) == AFI_IP6) \ + ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6 \ + : (((afi) == AFI_L2VPN) \ + ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_L2VPN \ + : (assert(0), 0)))) + + +struct rfapi_global_stats { + time_t last_reset; + unsigned int max_descriptors; + + unsigned int count_unknown_nves; + + unsigned int count_queries; + unsigned int count_queries_failed; + + unsigned int max_responses; /* semantics? */ + + unsigned int count_registrations; + unsigned int count_registrations_failed; + + unsigned int count_updated_response_updates; + unsigned int count_updated_response_deletes; +}; + +/* + * There is one of these per BGP instance. + * + * Radix tree is indexed by un address; follow chain and + * check vn address to get exact match. + */ +struct rfapi { + struct agg_table *un[AFI_MAX]; + struct rfapi_import_table *imports; /* IPv4, IPv6 */ + struct list descriptors; /* debug & resolve-nve imports */ + + struct rfapi_global_stats stat; + + /* + * callbacks into RFP, set at startup time (bgp_rfapi_new() gets + * values from rfp_start()) or via rfapi_rfp_set_cb_methods() + * (otherwise NULL). Note that the response_cb method can also + * be overridden per-rfd (currently used only for debug/test scenarios) + */ + struct rfapi_rfp_cb_methods rfp_methods; + + /* + * Import tables for Ethernet over IPSEC + * + * The skiplist keys are LNIs. Values are pointers + * to struct rfapi_import_table. + */ + struct skiplist *import_mac; /* L2 */ + + /* + * when exporting plain routes ("registered-nve" mode) to + * bgp unicast or zebra, we need to keep track of information + * related to expiring the routes according to the VNC lifetime + */ + struct agg_table *rt_export_bgp[AFI_MAX]; + struct agg_table *rt_export_zebra[AFI_MAX]; + + /* + * For VNC->BGP unicast exports in CE mode, we need a + * routing table that collects all of the VPN routes + * in a single tree. The VPN rib is split up according + * to RD first, so we can't use that. This is an import + * table that matches all RTs. + */ + struct rfapi_import_table *it_ce; + + /* + * when importing bgp-direct routes in resolve-nve mode, + * this list maps unicast route nexthops to their bgp_path_infos + * in the unicast table + */ + struct skiplist *resolve_nve_nexthop; + + /* + * Descriptors for which rfapi_close() was called during a callback. + * They will be closed after the callback finishes. + */ + struct work_queue *deferred_close_q; + + /* + * For "show vnc responses" + */ + uint32_t response_immediate_count; + uint32_t response_updated_count; + uint32_t monitor_count; + + uint32_t rib_prefix_count_total; + uint32_t rib_prefix_count_total_max; + + uint32_t flags; +#define RFAPI_INCALLBACK 0x00000001 + void *rfp; /* from rfp_start */ +}; + +#define RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfapi) \ + do { \ + ++(rfd)->rib_prefix_count; \ + ++(rfapi)->rib_prefix_count_total; \ + if ((rfapi)->rib_prefix_count_total \ + > (rfapi)->rib_prefix_count_total_max) \ + ++(rfapi)->rib_prefix_count_total_max; \ + } while (0) + +#define RFAPI_RIB_PREFIX_COUNT_DECR(rfd, rfapi) \ + do { \ + --(rfd)->rib_prefix_count; \ + --(rfapi)->rib_prefix_count_total; \ + } while (0) + +#define RFAPI_0_PREFIX(prefix) \ + ((((prefix)->family == AF_INET) \ + ? (prefix)->u.prefix4.s_addr == INADDR_ANY \ + : (((prefix)->family == AF_INET6) \ + ? (IN6_IS_ADDR_UNSPECIFIED(&(prefix)->u.prefix6)) \ + : 0))) + +#define RFAPI_0_ETHERADDR(ea) \ + (((ea)->octet[0] | (ea)->octet[1] | (ea)->octet[2] | (ea)->octet[3] \ + | (ea)->octet[4] | (ea)->octet[5]) \ + == 0) + +#define RFAPI_HOST_PREFIX(prefix) \ + (((prefix)->family == AF_INET) \ + ? ((prefix)->prefixlen == IPV4_MAX_BITLEN) \ + : (((prefix)->family == AF_INET6) \ + ? ((prefix)->prefixlen == IPV6_MAX_BITLEN) \ + : 0)) + +extern int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_addr *un_addr, + struct rfapi_descriptor **rfd); + +extern void +add_vnc_route(struct rfapi_descriptor *rfd, /* cookie + UN addr for VPN */ + struct bgp *bgp, int safi, const struct prefix *p, + struct prefix_rd *prd, struct rfapi_ip_addr *nexthop, + uint32_t *local_pref, /* host byte order */ + uint32_t *lifetime, /* host byte order */ + struct bgp_tea_options *rfp_options, + struct rfapi_un_option *options_un, + struct rfapi_vn_option *options_vn, + struct ecommunity *rt_export_list, uint32_t *med, uint32_t *label, + uint8_t type, uint8_t sub_type, int flags); +#define RFAPI_AHR_NO_TUNNEL_SUBTLV 0x00000001 +#define RFAPI_AHR_RFPOPT_IS_VNCTLV 0x00000002 /* hack! */ + +extern void del_vnc_route(struct rfapi_descriptor *rfd, struct peer *peer, + struct bgp *bgp, safi_t safi, const struct prefix *p, + struct prefix_rd *prd, uint8_t type, uint8_t sub_type, + struct rfapi_nexthop *lnh, int kill); + +extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, + struct prefix *p); + +extern int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime); + +extern int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p); + +extern int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp); + +extern void vnc_import_bgp_add_rfp_host_route_mode_resolve_nve( + struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *prefix); + +extern void vnc_import_bgp_del_rfp_host_route_mode_resolve_nve( + struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *prefix); + +extern void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p); + +extern struct rfapi_vn_option *rfapiVnOptionsDup(struct rfapi_vn_option *orig); + +extern struct rfapi_un_option *rfapiUnOptionsDup(struct rfapi_un_option *orig); + +extern struct bgp_tea_options *rfapiOptionsDup(struct bgp_tea_options *orig); + +extern int rfapi_ip_addr_cmp(struct rfapi_ip_addr *a1, + struct rfapi_ip_addr *a2); + +extern uint32_t rfp_cost_to_localpref(uint8_t cost); + +extern int rfapi_set_autord_from_vn(struct prefix_rd *rd, + struct rfapi_ip_addr *vn); + +extern struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme); + +extern void rfapi_nexthop_free(void *goner); + +extern struct rfapi_vn_option * +rfapi_vn_options_dup(struct rfapi_vn_option *existing); + +extern void rfapi_un_options_free(struct rfapi_un_option *goner); + +extern void rfapi_vn_options_free(struct rfapi_vn_option *goner); + +extern void vnc_add_vrf_opener(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg); +extern void clear_vnc_vrf_closer(struct rfapi_nve_group_cfg *rfg); +/*------------------------------------------ + * rfapi_extract_l2o + * + * Find Layer 2 options in an option chain + * + * input: + * pHop option chain + * + * output: + * l2o layer 2 options extracted + * + * return value: + * 0 OK + * 1 no options found + * + --------------------------------------------*/ +extern int rfapi_extract_l2o( + struct bgp_tea_options *pHop, /* chain of options */ + struct rfapi_l2address_option *l2o); /* return extracted value */ + +/* + * compaitibility to old quagga_time call + * time_t value in terms of stabilised absolute time. + * replacement for POSIX time() + * + * Please do not use this. This is kept only for + * Lou's CI in that that CI compiles against some + * private bgp code and it will just fail to compile + * without this. Use monotime() + */ +extern time_t rfapi_time(time_t *t); + +DECLARE_MGROUP(RFAPI); +DECLARE_MTYPE(RFAPI_CFG); +DECLARE_MTYPE(RFAPI_GROUP_CFG); +DECLARE_MTYPE(RFAPI_L2_CFG); +DECLARE_MTYPE(RFAPI_RFP_GROUP_CFG); +DECLARE_MTYPE(RFAPI); +DECLARE_MTYPE(RFAPI_DESC); +DECLARE_MTYPE(RFAPI_IMPORTTABLE); +DECLARE_MTYPE(RFAPI_MONITOR); +DECLARE_MTYPE(RFAPI_MONITOR_ENCAP); +DECLARE_MTYPE(RFAPI_NEXTHOP); +DECLARE_MTYPE(RFAPI_VN_OPTION); +DECLARE_MTYPE(RFAPI_UN_OPTION); +DECLARE_MTYPE(RFAPI_WITHDRAW); +DECLARE_MTYPE(RFAPI_RFG_NAME); +DECLARE_MTYPE(RFAPI_ADB); +DECLARE_MTYPE(RFAPI_ETI); +DECLARE_MTYPE(RFAPI_NVE_ADDR); +DECLARE_MTYPE(RFAPI_PREFIX_BAG); +DECLARE_MTYPE(RFAPI_IT_EXTRA); +DECLARE_MTYPE(RFAPI_INFO); +DECLARE_MTYPE(RFAPI_ADDR); +DECLARE_MTYPE(RFAPI_UPDATED_RESPONSE_QUEUE); +DECLARE_MTYPE(RFAPI_RECENT_DELETE); +DECLARE_MTYPE(RFAPI_L2ADDR_OPT); +DECLARE_MTYPE(RFAPI_AP); +DECLARE_MTYPE(RFAPI_MONITOR_ETH); + + +/* + * Caller must supply an already-allocated rfd with the "caller" + * fields already set (vn_addr, un_addr, callback, cookie) + * The advertised_prefixes[] array elements should be NULL to + * have this function set them to newly-allocated radix trees. + */ +extern int rfapi_init_and_open(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_nve_group_cfg *rfg); + +#endif /* _QUAGGA_BGP_RFAPI_PRIVATE_H */ diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c new file mode 100644 index 0000000..5784f95 --- /dev/null +++ b/bgpd/rfapi/rfapi_rib.c @@ -0,0 +1,2424 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: rfapi_rib.c + * Purpose: maintain per-nve ribs and generate change lists + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/log.h" +#include "lib/skiplist.h" +#include "lib/workqueue.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_vnc_types.h" + +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define DEBUG_PROCESS_PENDING_NODE 0 +#define DEBUG_PENDING_DELETE_ROUTE 0 +#define DEBUG_NHL 0 +#define DEBUG_RIB_SL_RD 0 +#define DEBUG_CLEANUP 0 + +/* forward decl */ +#if DEBUG_NHL +static void rfapiRibShowRibSl(void *stream, struct prefix *pfx, + struct skiplist *sl); +#endif + +/* + * RIB + * --- + * Model of the set of routes currently in the NVE's RIB. + * + * node->info ptr to "struct skiplist". + * MUST be NULL if there are no routes. + * key = ptr to struct prefix {vn} + * val = ptr to struct rfapi_info + * skiplist.del = NULL + * skiplist.cmp = vnc_prefix_cmp + * + * node->aggregate ptr to "struct skiplist". + * key = ptr to struct prefix {vn} + * val = ptr to struct rfapi_info + * skiplist.del = rfapi_info_free + * skiplist.cmp = vnc_prefix_cmp + * + * This skiplist at "aggregate" + * contains the routes recently + * deleted + * + * + * Pending RIB + * ----------- + * Sparse list of prefixes that need to be updated. Each node + * will have the complete set of routes for the prefix. + * + * node->info ptr to "struct list" (lib/linklist.h) + * "Cost List" + * List of routes sorted lowest cost first. + * This list is how the new complete set + * of routes should look. + * Set if there are updates to the prefix; + * MUST be NULL if there are no updates. + * + * .data = ptr to struct rfapi_info + * list.cmp = NULL (sorted manually) + * list.del = rfapi_info_free + * + * Special case: if node->info is 1, it means + * "delete all routes at this prefix". + * + * node->aggregate ptr to struct skiplist + * key = ptr to struct prefix {vn} (part of ri) + * val = struct rfapi_info + * skiplist.cmp = vnc_prefix_cmp + * skiplist.del = NULL + * + * ptlist is rewritten anew each time + * rfapiRibUpdatePendingNode() is called + * + * THE ptlist VALUES ARE REFERENCES TO THE + * rfapi_info STRUCTS IN THE node->info LIST. + */ + +/* + * iterate over RIB to count responses, compare with running counters + */ +void rfapiRibCheckCounts( + int checkstats, /* validate rfd & global counts */ + unsigned int offset) /* number of ri's held separately */ +{ + struct rfapi_descriptor *rfd; + struct listnode *node; + + struct bgp *bgp = bgp_get_default(); + + uint32_t t_pfx_active = 0; + + uint32_t t_ri_active = 0; + uint32_t t_ri_deleted = 0; + uint32_t t_ri_pend = 0; + + unsigned int alloc_count; + + /* + * loop over NVEs + */ + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) { + + afi_t afi; + uint32_t pfx_active = 0; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_node *rn; + + for (rn = agg_route_top(rfd->rib[afi]); rn; + rn = agg_route_next(rn)) { + + struct skiplist *sl = rn->info; + struct skiplist *dsl = rn->aggregate; + uint32_t ri_active = 0; + uint32_t ri_deleted = 0; + + if (sl) { + ri_active = skiplist_count(sl); + assert(ri_active); + t_ri_active += ri_active; + ++pfx_active; + ++t_pfx_active; + } + + if (dsl) { + ri_deleted = skiplist_count(dsl); + t_ri_deleted += ri_deleted; + } + } + for (rn = agg_route_top(rfd->rib_pending[afi]); rn; + rn = agg_route_next(rn)) { + + struct list *l = rn->info; /* sorted by cost */ + struct skiplist *sl = rn->aggregate; + uint32_t ri_pend_cost = 0; + uint32_t ri_pend_uniq = 0; + + if (sl) { + ri_pend_uniq = skiplist_count(sl); + } + + if (l && (l != (void *)1)) { + ri_pend_cost = l->count; + t_ri_pend += l->count; + } + + assert(ri_pend_uniq == ri_pend_cost); + } + } + + if (checkstats) { + if (pfx_active != rfd->rib_prefix_count) { + vnc_zlog_debug_verbose( + "%s: rfd %p actual pfx count %u != running %u", + __func__, rfd, pfx_active, + rfd->rib_prefix_count); + assert(0); + } + } + } + + if (checkstats && bgp->rfapi) { + if (t_pfx_active != bgp->rfapi->rib_prefix_count_total) { + vnc_zlog_debug_verbose( + "%s: actual total pfx count %u != running %u", + __func__, t_pfx_active, + bgp->rfapi->rib_prefix_count_total); + assert(0); + } + } + + /* + * Check against memory allocation count + */ + alloc_count = mtype_stats_alloc(MTYPE_RFAPI_INFO); + assert(t_ri_active + t_ri_deleted + t_ri_pend + offset == alloc_count); +} + +static struct rfapi_info *rfapi_info_new(void) +{ + return XCALLOC(MTYPE_RFAPI_INFO, sizeof(struct rfapi_info)); +} + +void rfapiFreeRfapiUnOptionChain(struct rfapi_un_option *p) +{ + while (p) { + struct rfapi_un_option *next; + + next = p->next; + XFREE(MTYPE_RFAPI_UN_OPTION, p); + p = next; + } +} + +void rfapiFreeRfapiVnOptionChain(struct rfapi_vn_option *p) +{ + while (p) { + struct rfapi_vn_option *next; + + next = p->next; + XFREE(MTYPE_RFAPI_VN_OPTION, p); + p = next; + } +} + + +static void rfapi_info_free(struct rfapi_info *goner) +{ + if (goner) { + if (goner->tea_options) { + rfapiFreeBgpTeaOptionChain(goner->tea_options); + goner->tea_options = NULL; + } + if (goner->un_options) { + rfapiFreeRfapiUnOptionChain(goner->un_options); + goner->un_options = NULL; + } + if (goner->vn_options) { + rfapiFreeRfapiVnOptionChain(goner->vn_options); + goner->vn_options = NULL; + } + if (goner->timer) { + struct rfapi_rib_tcb *tcb; + + tcb = EVENT_ARG(goner->timer); + EVENT_OFF(goner->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); + } + XFREE(MTYPE_RFAPI_INFO, goner); + } +} + +/* + * Timer control block for recently-deleted and expired routes + */ +struct rfapi_rib_tcb { + struct rfapi_descriptor *rfd; + struct skiplist *sl; + struct rfapi_info *ri; + struct agg_node *rn; + int flags; +#define RFAPI_RIB_TCB_FLAG_DELETED 0x00000001 +}; + +/* + * remove route from rib + */ +static void rfapiRibExpireTimer(struct event *t) +{ + struct rfapi_rib_tcb *tcb = EVENT_ARG(t); + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + /* + * Forget reference to thread. Otherwise rfapi_info_free() will + * attempt to free thread pointer as an option chain + */ + tcb->ri->timer = NULL; + + /* "deleted" skiplist frees ri, "active" doesn't */ + assert(!skiplist_delete(tcb->sl, &tcb->ri->rk, NULL)); + if (!tcb->sl->del) { + /* + * XXX in this case, skiplist has no delete function: we must + * therefore delete rfapi_info explicitly. + */ + rfapi_info_free(tcb->ri); + } + + if (skiplist_empty(tcb->sl)) { + if (CHECK_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED)) + tcb->rn->aggregate = NULL; + else { + struct bgp *bgp = bgp_get_default(); + tcb->rn->info = NULL; + RFAPI_RIB_PREFIX_COUNT_DECR(tcb->rfd, bgp->rfapi); + } + skiplist_free(tcb->sl); + agg_unlock_node(tcb->rn); + } + + XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); + + RFAPI_RIB_CHECK_COUNTS(1, 0); +} + +static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, + struct rfapi_info *ri, + struct agg_node *rn, /* route node attached to */ + int deleted) +{ + struct rfapi_rib_tcb *tcb = NULL; + + if (ri->timer) { + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); + } else { + tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE, + sizeof(struct rfapi_rib_tcb)); + } +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p, rn %p, ri %p, tcb %p", __func__, rfd, rn, ri, + tcb); +#endif + + tcb->rfd = rfd; + tcb->ri = ri; + tcb->rn = rn; + if (deleted) { + tcb->sl = (struct skiplist *)rn->aggregate; + SET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); + } else { + tcb->sl = (struct skiplist *)rn->info; + UNSET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED); + } + + vnc_zlog_debug_verbose("%s: rfd %p pfx %pRN life %u", __func__, rfd, rn, + ri->lifetime); + + event_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime, + &ri->timer); +} + +extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ + struct prefix_rd *rd, /* may be NULL */ + struct prefix *aux, /* may be NULL */ + struct rfapi_rib_key *rk) + +{ + memset((void *)rk, 0, sizeof(struct rfapi_rib_key)); + if (prefix) + rk->vn = *prefix; + if (rd) + rk->rd = *rd; + if (aux) + rk->aux_prefix = *aux; +} + +/* + * Compares two <struct rfapi_rib_key>s + */ +int rfapi_rib_key_cmp(const void *k1, const void *k2) +{ + const struct rfapi_rib_key *a = (struct rfapi_rib_key *)k1; + const struct rfapi_rib_key *b = (struct rfapi_rib_key *)k2; + int ret; + + if (!a || !b) + return (a - b); + + ret = vnc_prefix_cmp(&a->vn, &b->vn); + if (ret) + return ret; + + ret = vnc_prefix_cmp(&a->rd, &b->rd); + if (ret) + return ret; + + ret = vnc_prefix_cmp(&a->aux_prefix, &b->aux_prefix); + + return ret; +} + + +/* + * Note: this function will claim that two option chains are + * different unless their option items are in identical order. + * The consequence is that RFP updated responses can be sent + * unnecessarily, or that they might contain nexthop items + * that are not strictly needed. + * + * This function could be modified to compare option chains more + * thoroughly, but it's not clear that the extra compuation would + * be worth it. + */ +static int bgp_tea_options_cmp(struct bgp_tea_options *a, + struct bgp_tea_options *b) +{ + int rc; + + if (!a || !b) { + return (a - b); + } + + if (a->type != b->type) + return (a->type - b->type); + if (a->length != b->length) + return (a->length = b->length); + if ((rc = memcmp(a->value, b->value, a->length))) + return rc; + if (!a->next != !b->next) { /* logical xor */ + return (a->next - b->next); + } + if (a->next) + return bgp_tea_options_cmp(a->next, b->next); + return 0; +} + +static int rfapi_info_cmp(struct rfapi_info *a, struct rfapi_info *b) +{ + int rc; + + if (!a || !b) + return (a - b); + + if ((rc = rfapi_rib_key_cmp(&a->rk, &b->rk))) + return rc; + + if ((rc = vnc_prefix_cmp(&a->un, &b->un))) + return rc; + + if (a->cost != b->cost) + return (a->cost - b->cost); + + if (a->lifetime != b->lifetime) + return (a->lifetime - b->lifetime); + + if ((rc = bgp_tea_options_cmp(a->tea_options, b->tea_options))) + return rc; + + return 0; +} + +void rfapiRibClear(struct rfapi_descriptor *rfd) +{ + struct bgp *bgp; + afi_t afi; + + if (rfd->bgp) + bgp = rfd->bgp; + else + bgp = bgp_get_default(); +#ifdef DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); +#endif + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + struct agg_node *pn; + struct agg_node *rn; + + if (rfd->rib_pending[afi]) { + for (pn = agg_route_top(rfd->rib_pending[afi]); pn; + pn = agg_route_next(pn)) { + if (pn->aggregate) { + /* + * free references into the rfapi_info + * structures before + * freeing the structures themselves + */ + skiplist_free( + (struct skiplist + *)(pn->aggregate)); + pn->aggregate = NULL; + agg_unlock_node( + pn); /* skiplist deleted */ + } + /* + * free the rfapi_info structures + */ + if (pn->info) { + if (pn->info != (void *)1) { + list_delete( + (struct list * + *)(&pn->info)); + } + pn->info = NULL; + /* linklist or 1 deleted */ + agg_unlock_node(pn); + } + } + } + if (rfd->rib[afi]) { + for (rn = agg_route_top(rfd->rib[afi]); rn; + rn = agg_route_next(rn)) { + if (rn->info) { + + struct rfapi_info *ri; + + while (0 == skiplist_first( + (struct skiplist *) + rn->info, + NULL, + (void **)&ri)) { + + if (ri->timer) { + struct rfapi_rib_tcb + *tcb; + + tcb = EVENT_ARG( + ri->timer); + EVENT_OFF(ri->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, + tcb); + } + rfapi_info_free(ri); + skiplist_delete_first( + (struct skiplist *) + rn->info); + } + skiplist_free( + (struct skiplist *)rn->info); + rn->info = NULL; + agg_unlock_node(rn); + RFAPI_RIB_PREFIX_COUNT_DECR(rfd, + bgp->rfapi); + } + if (rn->aggregate) { + + struct rfapi_info *ri_del; + + /* delete skiplist & contents */ + while (!skiplist_first( + (struct skiplist + *)(rn->aggregate), + NULL, (void **)&ri_del)) { + + /* sl->del takes care of ri_del + */ + skiplist_delete_first(( + struct skiplist + *)(rn->aggregate)); + } + skiplist_free( + (struct skiplist + *)(rn->aggregate)); + + rn->aggregate = NULL; + agg_unlock_node(rn); + } + } + } + } + if (rfd->updated_responses_queue) + work_queue_free_and_null(&rfd->updated_responses_queue); +} + +/* + * Release all dynamically-allocated memory that is part of an HD's RIB + */ +void rfapiRibFree(struct rfapi_descriptor *rfd) +{ + afi_t afi; + +#if DEBUG_CLEANUP + zlog_debug("%s: rfd %p", __func__, rfd); +#endif + + /* + * NB rfd is typically detached from master list, so is not included + * in the count performed by RFAPI_RIB_CHECK_COUNTS + */ + + /* + * Free routes attached to radix trees + */ + rfapiRibClear(rfd); + + /* Now the uncounted rfapi_info's are freed, so the check should succeed + */ + RFAPI_RIB_CHECK_COUNTS(1, 0); + + /* + * Free radix trees + */ + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + if (rfd->rib_pending[afi]) + agg_table_finish(rfd->rib_pending[afi]); + rfd->rib_pending[afi] = NULL; + + if (rfd->rib[afi]) + agg_table_finish(rfd->rib[afi]); + rfd->rib[afi] = NULL; + + /* NB agg_table_finish frees only prefix nodes, not chained + * info */ + if (rfd->rsp_times[afi]) + agg_table_finish(rfd->rsp_times[afi]); + rfd->rib[afi] = NULL; + } +} + +/* + * Copies struct bgp_path_info to struct rfapi_info, except for rk fields and un + */ +static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri, + uint32_t lifetime) +{ + struct bgp_attr_encap_subtlv *pEncap; + + ri->cost = rfapiRfpCost(bpi->attr); + ri->lifetime = lifetime; + + /* This loop based on rfapiRouteInfo2NextHopEntry() */ + for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap; + pEncap = pEncap->next) { + struct bgp_tea_options *hop; + + switch (pEncap->type) { + case BGP_VNC_SUBTLV_TYPE_LIFETIME: + /* use configured lifetime, not attr lifetime */ + break; + + case BGP_VNC_SUBTLV_TYPE_RFPOPTION: + hop = XCALLOC(MTYPE_BGP_TEA_OPTIONS, + sizeof(struct bgp_tea_options)); + assert(hop); + hop->type = pEncap->value[0]; + hop->length = pEncap->value[1]; + hop->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE, + pEncap->length - 2); + assert(hop->value); + memcpy(hop->value, pEncap->value + 2, + pEncap->length - 2); + if (hop->length > pEncap->length - 2) { + zlog_warn( + "%s: VNC subtlv length mismatch: RFP option says %d, attr says %d (shrinking)", + __func__, hop->length, + pEncap->length - 2); + hop->length = pEncap->length - 2; + } + hop->next = ri->tea_options; + ri->tea_options = hop; + break; + + default: + break; + } + } + + rfapi_un_options_free(ri->un_options); /* maybe free old version */ + ri->un_options = rfapi_encap_tlv_to_un_option(bpi->attr); + + /* + * VN options + */ + if (bpi->extra + && decode_rd_type(bpi->extra->vnc.import.rd.val) + == RD_TYPE_VNC_ETH) { + /* ethernet route */ + + struct rfapi_vn_option *vo; + + vo = XCALLOC(MTYPE_RFAPI_VN_OPTION, + sizeof(struct rfapi_vn_option)); + assert(vo); + + vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + /* copy from RD already stored in bpi, so we don't need it_node + */ + memcpy(&vo->v.l2addr.macaddr, bpi->extra->vnc.import.rd.val + 2, + ETH_ALEN); + + (void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr), + &vo->v.l2addr.logical_net_id); + (void)rfapiEcommunityGetEthernetTag( + bgp_attr_get_ecommunity(bpi->attr), + &vo->v.l2addr.tag_id); + + /* local_nve_id comes from RD */ + vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1]; + + /* label comes from MP_REACH_NLRI label */ + vo->v.l2addr.label = decode_label(&bpi->extra->label[0]); + + rfapi_vn_options_free( + ri->vn_options); /* maybe free old version */ + ri->vn_options = vo; + } + + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { + ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + } +} + +/* + * rfapiRibPreloadBi + * + * Install route into NVE RIB model so as to be consistent with + * caller's response to rfapi_query(). + * + * Also: return indication to caller whether this specific route + * should be included in the response to the NVE according to + * the following tests: + * + * 1. If there were prior duplicates of this route in this same + * query response, don't include the route. + * + * RETURN VALUE: + * + * 0 OK to include route in response + * !0 do not include route in response + */ +int rfapiRibPreloadBi( + struct agg_node *rfd_rib_node, /* NULL = don't preload or filter */ + struct prefix *pfx_vn, struct prefix *pfx_un, uint32_t lifetime, + struct bgp_path_info *bpi) +{ + struct rfapi_descriptor *rfd; + struct skiplist *slRibPt = NULL; + struct rfapi_info *ori = NULL; + struct rfapi_rib_key rk; + struct agg_node *trn; + afi_t afi; + const struct prefix *p = agg_node_get_prefix(rfd_rib_node); + + if (!rfd_rib_node) + return 0; + + afi = family2afi(p->family); + + rfd = agg_get_table_info(agg_get_table(rfd_rib_node)); + + memset((void *)&rk, 0, sizeof(rk)); + rk.vn = *pfx_vn; + rk.rd = bpi->extra->vnc.import.rd; + + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bpi->extra->vnc.import.aux_prefix.family) { + rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + } + + /* + * is this route already in NVE's RIB? + */ + slRibPt = (struct skiplist *)rfd_rib_node->info; + + if (slRibPt && !skiplist_search(slRibPt, &rk, (void **)&ori)) { + + if ((ori->rsp_counter == rfd->rsp_counter) + && (ori->last_sent_time == rfd->rsp_time)) { + return -1; /* duplicate in this response */ + } + + /* found: update contents of existing route in RIB */ + ori->un = *pfx_un; + rfapiRibBi2Ri(bpi, ori, lifetime); + } else { + /* not found: add new route to RIB */ + ori = rfapi_info_new(); + ori->rk = rk; + ori->un = *pfx_un; + rfapiRibBi2Ri(bpi, ori, lifetime); + + if (!slRibPt) { + slRibPt = skiplist_new(0, rfapi_rib_key_cmp, NULL); + rfd_rib_node->info = slRibPt; + agg_lock_node(rfd_rib_node); + RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfd->bgp->rfapi); + } + skiplist_insert(slRibPt, &ori->rk, ori); + } + + ori->last_sent_time = monotime(NULL); + + /* + * poke timer + */ + RFAPI_RIB_CHECK_COUNTS(0, 0); + rfapiRibStartTimer(rfd, ori, rfd_rib_node, 0); + RFAPI_RIB_CHECK_COUNTS(0, 0); + + /* + * Update last sent time for prefix + */ + trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */ + trn->info = (void *)(uintptr_t)monotime(NULL); + if (agg_node_get_lock_count(trn) > 1) + agg_unlock_node(trn); + + return 0; +} + +/* + * Frees rfapi_info items at node + * + * Adjust 'rib' and 'rib_pending' as follows: + * + * If rib_pending node->info is 1 (magic value): + * callback: NHL = RIB NHL with lifetime = withdraw_lifetime_value + * RIB = remove all routes at the node + * DONE + * + * For each item at rib node: + * if not present in pending node, move RIB item to "delete list" + * + * For each item at pending rib node: + * if present (same vn/un) in rib node with same lifetime & options, drop + * matching item from pending node + * + * For each remaining item at pending rib node, add or replace item + * at rib node. + * + * Construct NHL as concatenation of pending list + delete list + * + * Clear pending node + */ +static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd, + afi_t afi, + struct agg_node *pn, /* pending node */ + struct rfapi_next_hop_entry **head, + struct rfapi_next_hop_entry **tail) +{ + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct rfapi_info *ri = NULL; /* happy valgrind */ + struct rfapi_ip_prefix hp = {0}; /* pfx to put in NHE */ + struct agg_node *rn = NULL; + struct skiplist *slRibPt = NULL; /* rib list */ + struct skiplist *slPendPt = NULL; + struct list *lPendCost = NULL; + struct list *delete_list = NULL; + int printedprefix = 0; + int rib_node_started_nonempty = 0; + int sendingsomeroutes = 0; + const struct prefix *p; +#if DEBUG_PROCESS_PENDING_NODE + unsigned int count_rib_initial = 0; + unsigned int count_pend_vn_initial = 0; + unsigned int count_pend_cost_initial = 0; +#endif + + assert(pn); + p = agg_node_get_prefix(pn); + vnc_zlog_debug_verbose("%s: afi=%d, %pRN pn->info=%p", __func__, afi, + pn, pn->info); + + if (AFI_L2VPN != afi) { + rfapiQprefix2Rprefix(p, &hp); + } + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + /* + * Find corresponding RIB node + */ + rn = agg_node_get(rfd->rib[afi], p); /* locks rn */ + + /* + * RIB skiplist has key=rfapi_addr={vn,un}, val = rfapi_info, + * skiplist.del = NULL + */ + slRibPt = (struct skiplist *)rn->info; + if (slRibPt) + rib_node_started_nonempty = 1; + + slPendPt = (struct skiplist *)(pn->aggregate); + lPendCost = (struct list *)(pn->info); + +#if DEBUG_PROCESS_PENDING_NODE + /* debugging */ + if (slRibPt) + count_rib_initial = skiplist_count(slRibPt); + + if (slPendPt) + count_pend_vn_initial = skiplist_count(slPendPt); + + if (lPendCost && lPendCost != (struct list *)1) + count_pend_cost_initial = lPendCost->count; +#endif + + + /* + * Handle special case: delete all routes at prefix + */ + if (lPendCost == (struct list *)1) { + vnc_zlog_debug_verbose("%s: lPendCost=1 => delete all", + __func__); + if (slRibPt && !skiplist_empty(slRibPt)) { + delete_list = list_new(); + while (0 + == skiplist_first(slRibPt, NULL, (void **)&ri)) { + listnode_add(delete_list, ri); + vnc_zlog_debug_verbose( + "%s: after listnode_add, delete_list->count=%d", + __func__, delete_list->count); + rfapiFreeBgpTeaOptionChain(ri->tea_options); + ri->tea_options = NULL; + + if (ri->timer) { + struct rfapi_rib_tcb *tcb; + + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); + } + + vnc_zlog_debug_verbose( + "%s: put dl pfx=%pRN vn=%pFX un=%pFX cost=%d life=%d vn_options=%p", + __func__, pn, &ri->rk.vn, &ri->un, + ri->cost, ri->lifetime, ri->vn_options); + + skiplist_delete_first(slRibPt); + } + + assert(skiplist_empty(slRibPt)); + + skiplist_free(slRibPt); + rn->info = slRibPt = NULL; + agg_unlock_node(rn); + + lPendCost = pn->info = NULL; + agg_unlock_node(pn); + + goto callback; + } + if (slRibPt) { + skiplist_free(slRibPt); + rn->info = NULL; + agg_unlock_node(rn); + } + + assert(!slPendPt); + if (slPendPt) { /* TBD I think we can toss this block */ + skiplist_free(slPendPt); + pn->aggregate = NULL; + agg_unlock_node(pn); + } + + pn->info = NULL; + agg_unlock_node(pn); + + agg_unlock_node(rn); /* agg_node_get() */ + + if (rib_node_started_nonempty) { + RFAPI_RIB_PREFIX_COUNT_DECR(rfd, bgp->rfapi); + } + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + return; + } + + vnc_zlog_debug_verbose("%s: lPendCost->count=%d, slRibPt->count=%d", + __func__, + (lPendCost ? (int)lPendCost->count : -1), + (slRibPt ? (int)slRibPt->count : -1)); + + /* + * Iterate over routes at RIB Node. + * If not found at Pending Node, delete from RIB Node and add to + * deletelist + * If found at Pending Node + * If identical rfapi_info, delete from Pending Node + */ + if (slRibPt) { + void *cursor = NULL; + struct rfapi_info *ori; + + /* + * Iterate over RIB List + * + */ + while (!skiplist_next(slRibPt, NULL, (void **)&ori, &cursor)) { + + if (skiplist_search(slPendPt, &ori->rk, (void **)&ri)) { + /* + * Not in Pending list, so it should be deleted + */ + if (!delete_list) + delete_list = list_new(); + listnode_add(delete_list, ori); + rfapiFreeBgpTeaOptionChain(ori->tea_options); + ori->tea_options = NULL; + if (ori->timer) { + struct rfapi_rib_tcb *tcb; + + tcb = EVENT_ARG(ori->timer); + EVENT_OFF(ori->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); + } + +#if DEBUG_PROCESS_PENDING_NODE + /* deleted from slRibPt below, after we're done + * iterating */ + vnc_zlog_debug_verbose( + "%s: slRibPt ri %p not matched in pending list, delete", + __func__, ori); +#endif + + } else { + /* + * Found in pending list. If same lifetime, + * cost, options, + * then remove from pending list because the + * route + * hasn't changed. + */ + if (!rfapi_info_cmp(ori, ri)) { + skiplist_delete(slPendPt, &ri->rk, + NULL); + assert(lPendCost); + if (lPendCost) { + /* linear walk: might need + * optimization */ + listnode_delete(lPendCost, + ri); /* XXX + doesn't + free + data! + bug? */ + rfapi_info_free( + ri); /* grr... */ + } + } +#if DEBUG_PROCESS_PENDING_NODE + vnc_zlog_debug_verbose( + "%s: slRibPt ri %p matched in pending list, %s", + __func__, ori, + (same ? "same info" + : "different info")); +#endif + } + } + /* + * Go back and delete items from RIB + */ + if (delete_list) { + for (ALL_LIST_ELEMENTS_RO(delete_list, node, ri)) { + vnc_zlog_debug_verbose( + "%s: deleting ri %p from slRibPt", + __func__, ri); + assert(!skiplist_delete(slRibPt, &ri->rk, + NULL)); + } + if (skiplist_empty(slRibPt)) { + skiplist_free(slRibPt); + slRibPt = rn->info = NULL; + agg_unlock_node(rn); + } + } + } + + RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0)); + + /* + * Iterate over routes at Pending Node + * + * If {vn} found at RIB Node, update RIB Node route contents to match PN + * If {vn} NOT found at RIB Node, add copy to RIB Node + */ + if (lPendCost) { + for (ALL_LIST_ELEMENTS_RO(lPendCost, node, ri)) { + + struct rfapi_info *ori; + + if (slRibPt + && !skiplist_search(slRibPt, &ri->rk, + (void **)&ori)) { + + /* found: update contents of existing route in + * RIB */ + ori->un = ri->un; + ori->cost = ri->cost; + ori->lifetime = ri->lifetime; + rfapiFreeBgpTeaOptionChain(ori->tea_options); + ori->tea_options = + rfapiOptionsDup(ri->tea_options); + ori->last_sent_time = monotime(NULL); + + rfapiFreeRfapiVnOptionChain(ori->vn_options); + ori->vn_options = + rfapiVnOptionsDup(ri->vn_options); + + rfapiFreeRfapiUnOptionChain(ori->un_options); + ori->un_options = + rfapiUnOptionsDup(ri->un_options); + + vnc_zlog_debug_verbose( + "%s: matched lPendCost item %p in slRibPt, rewrote", + __func__, ri); + + } else { + /* not found: add new route to RIB */ + ori = rfapi_info_new(); + ori->rk = ri->rk; + ori->un = ri->un; + ori->cost = ri->cost; + ori->lifetime = ri->lifetime; + ori->tea_options = + rfapiOptionsDup(ri->tea_options); + ori->last_sent_time = monotime(NULL); + ori->vn_options = + rfapiVnOptionsDup(ri->vn_options); + ori->un_options = + rfapiUnOptionsDup(ri->un_options); + + if (!slRibPt) { + slRibPt = skiplist_new( + 0, rfapi_rib_key_cmp, NULL); + rn->info = slRibPt; + agg_lock_node(rn); + } + skiplist_insert(slRibPt, &ori->rk, ori); + + vnc_zlog_debug_verbose( + "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRDP)", + __func__, ri, &ori->rk.rd); + } + + /* + * poke timer + */ + RFAPI_RIB_CHECK_COUNTS( + 0, (delete_list ? delete_list->count : 0)); + rfapiRibStartTimer(rfd, ori, rn, 0); + RFAPI_RIB_CHECK_COUNTS( + 0, (delete_list ? delete_list->count : 0)); + } + } + + +callback: + /* + * Construct NHL as concatenation of pending list + delete list + */ + + + RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0)); + + if (lPendCost) { + + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + + vnc_zlog_debug_verbose("%s: lPendCost->count now %d", __func__, + lPendCost->count); + vnc_zlog_debug_verbose("%s: For prefix %pRN (a)", __func__, pn); + printedprefix = 1; + + for (ALL_LIST_ELEMENTS(lPendCost, node, nnode, ri)) { + + struct rfapi_next_hop_entry *new; + struct agg_node *trn; + + new = XCALLOC(MTYPE_RFAPI_NEXTHOP, + sizeof(struct rfapi_next_hop_entry)); + + if (ri->rk.aux_prefix.family) { + rfapiQprefix2Rprefix(&ri->rk.aux_prefix, + &new->prefix); + } else { + new->prefix = hp; + if (AFI_L2VPN == afi) { + /* hp is 0; need to set length to match + * AF of vn */ + new->prefix.length = + (ri->rk.vn.family == AF_INET) + ? 32 + : 128; + } + } + new->prefix.cost = ri->cost; + new->lifetime = ri->lifetime; + rfapiQprefix2Raddr(&ri->rk.vn, &new->vn_address); + rfapiQprefix2Raddr(&ri->un, &new->un_address); + /* free option chain from ri */ + rfapiFreeBgpTeaOptionChain(ri->tea_options); + + ri->tea_options = + NULL; /* option chain was transferred to NHL */ + + new->vn_options = ri->vn_options; + ri->vn_options = + NULL; /* option chain was transferred to NHL */ + + new->un_options = ri->un_options; + ri->un_options = + NULL; /* option chain was transferred to NHL */ + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) { + *head = new; + } + sendingsomeroutes = 1; + + ++rfd->stat_count_nh_reachable; + ++bgp->rfapi->stat.count_updated_response_updates; + + /* + * update this NVE's timestamp for this prefix + */ + trn = agg_node_get(rfd->rsp_times[afi], + p); /* locks trn */ + trn->info = (void *)(uintptr_t)monotime(NULL); + if (agg_node_get_lock_count(trn) > 1) + agg_unlock_node(trn); + + rfapiRfapiIpAddr2Str(&new->vn_address, buf, BUFSIZ); + rfapiRfapiIpAddr2Str(&new->un_address, buf2, BUFSIZ); + vnc_zlog_debug_verbose( + "%s: add vn=%s un=%s cost=%d life=%d", + __func__, buf, buf2, new->prefix.cost, + new->lifetime); + } + } + + RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0)); + + if (delete_list) { + + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + + if (!printedprefix) { + vnc_zlog_debug_verbose("%s: For prefix %pRN (d)", + __func__, pn); + } + vnc_zlog_debug_verbose("%s: delete_list has %d elements", + __func__, delete_list->count); + + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + if (!CHECK_FLAG(bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) { + + for (ALL_LIST_ELEMENTS(delete_list, node, nnode, ri)) { + + struct rfapi_next_hop_entry *new; + struct rfapi_info *ri_del; + + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + new = XCALLOC( + MTYPE_RFAPI_NEXTHOP, + sizeof(struct rfapi_next_hop_entry)); + + if (ri->rk.aux_prefix.family) { + rfapiQprefix2Rprefix(&ri->rk.aux_prefix, + &new->prefix); + } else { + new->prefix = hp; + if (AFI_L2VPN == afi) { + /* hp is 0; need to set length + * to match AF of vn */ + new->prefix.length = + (ri->rk.vn.family + == AF_INET) + ? 32 + : 128; + } + } + + new->prefix.cost = ri->cost; + new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME; + rfapiQprefix2Raddr(&ri->rk.vn, + &new->vn_address); + rfapiQprefix2Raddr(&ri->un, &new->un_address); + + new->vn_options = ri->vn_options; + ri->vn_options = NULL; /* option chain was + transferred to NHL */ + + new->un_options = ri->un_options; + ri->un_options = NULL; /* option chain was + transferred to NHL */ + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) { + *head = new; + } + ++rfd->stat_count_nh_removal; + ++bgp->rfapi->stat + .count_updated_response_deletes; + + rfapiRfapiIpAddr2Str(&new->vn_address, buf, + BUFSIZ); + rfapiRfapiIpAddr2Str(&new->un_address, buf2, + BUFSIZ); + vnc_zlog_debug_verbose( + "%s: DEL vn=%s un=%s cost=%d life=%d", + __func__, buf, buf2, new->prefix.cost, + new->lifetime); + + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + /* + * Update/add to list of recent deletions at + * this prefix + */ + if (!rn->aggregate) { + rn->aggregate = skiplist_new( + 0, rfapi_rib_key_cmp, + (void (*)(void *)) + rfapi_info_free); + agg_lock_node(rn); + } + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + + /* sanity check lifetime */ + if (ri->lifetime + > RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY) + ri->lifetime = + RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY; + + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + /* cancel normal expire timer */ + if (ri->timer) { + struct rfapi_rib_tcb *tcb; + + tcb = EVENT_ARG(ri->timer); + EVENT_OFF(ri->timer); + XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); + } + RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); + + /* + * Look in "recently-deleted" list + */ + if (skiplist_search( + (struct skiplist *)(rn->aggregate), + &ri->rk, (void **)&ri_del)) { + + int rc; + + RFAPI_RIB_CHECK_COUNTS( + 0, delete_list->count); + /* + * NOT in "recently-deleted" list + */ + list_delete_node( + delete_list, + node); /* does not free ri */ + rc = skiplist_insert( + (struct skiplist + *)(rn->aggregate), + &ri->rk, ri); + assert(!rc); + + RFAPI_RIB_CHECK_COUNTS( + 0, delete_list->count); + rfapiRibStartTimer(rfd, ri, rn, 1); + RFAPI_RIB_CHECK_COUNTS( + 0, delete_list->count); + ri->last_sent_time = monotime(NULL); +#if DEBUG_RIB_SL_RD + vnc_zlog_debug_verbose( + "%s: move route to recently deleted list, rd=%pRDP", + __func__, &ri->rk.rd); +#endif + + } else { + /* + * IN "recently-deleted" list + */ + RFAPI_RIB_CHECK_COUNTS( + 0, delete_list->count); + rfapiRibStartTimer(rfd, ri_del, rn, 1); + RFAPI_RIB_CHECK_COUNTS( + 0, delete_list->count); + ri->last_sent_time = monotime(NULL); + } + } + } else { + vnc_zlog_debug_verbose( + "%s: response removal disabled, omitting removals", + __func__); + } + + delete_list->del = (void (*)(void *))rfapi_info_free; + list_delete(&delete_list); + } + + RFAPI_RIB_CHECK_COUNTS(0, 0); + + /* + * Reset pending lists. The final agg_unlock_node() will probably + * cause the pending node to be released. + */ + if (slPendPt) { + skiplist_free(slPendPt); + pn->aggregate = NULL; + agg_unlock_node(pn); + } + if (lPendCost) { + list_delete(&lPendCost); + pn->info = NULL; + agg_unlock_node(pn); + } + RFAPI_RIB_CHECK_COUNTS(0, 0); + + if (rib_node_started_nonempty) { + if (!rn->info) { + RFAPI_RIB_PREFIX_COUNT_DECR(rfd, bgp->rfapi); + } + } else { + if (rn->info) { + RFAPI_RIB_PREFIX_COUNT_INCR(rfd, bgp->rfapi); + } + } + + if (sendingsomeroutes) + rfapiMonitorTimersRestart(rfd, p); + + agg_unlock_node(rn); /* agg_node_get() */ + + RFAPI_RIB_CHECK_COUNTS(1, 0); +} + +/* + * regardless of targets, construct a single callback by doing + * only one traversal of the pending RIB + * + * + * Do callback + * + */ +static void rib_do_callback_onepass(struct rfapi_descriptor *rfd, afi_t afi) +{ + struct bgp *bgp = bgp_get_default(); + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct agg_node *rn; + +#ifdef DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: rfd=%p, afi=%d", __func__, rfd, afi); +#endif + + if (!rfd->rib_pending[afi]) + return; + + assert(bgp->rfapi); + + for (rn = agg_route_top(rfd->rib_pending[afi]); rn; + rn = agg_route_next(rn)) { + process_pending_node(bgp, rfd, afi, rn, &head, &tail); + } + + if (head) { + rfapi_response_cb_t *f; + +#if DEBUG_NHL + vnc_zlog_debug_verbose("%s: response callback NHL follows:", + __func__); + rfapiPrintNhl(NULL, head); +#endif + + if (rfd->response_cb) + f = rfd->response_cb; + else + f = bgp->rfapi->rfp_methods.response_cb; + + bgp->rfapi->flags |= RFAPI_INCALLBACK; + vnc_zlog_debug_verbose("%s: invoking updated response callback", + __func__); + (*f)(head, rfd->cookie); + bgp->rfapi->flags &= ~RFAPI_INCALLBACK; + ++bgp->rfapi->response_updated_count; + } +} + +static wq_item_status rfapiRibDoQueuedCallback(struct work_queue *wq, + void *data) +{ + struct rfapi_descriptor *rfd; + afi_t afi; + uint32_t queued_flag; + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + rfd = ((struct rfapi_updated_responses_queue *)data)->rfd; + afi = ((struct rfapi_updated_responses_queue *)data)->afi; + + /* Make sure the HD wasn't closed after the work item was scheduled */ + if (rfapi_check(rfd)) + return WQ_SUCCESS; + + rib_do_callback_onepass(rfd, afi); + + queued_flag = RFAPI_QUEUED_FLAG(afi); + + UNSET_FLAG(rfd->flags, queued_flag); + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + return WQ_SUCCESS; +} + +static void rfapiRibQueueItemDelete(struct work_queue *wq, void *data) +{ + XFREE(MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, data); +} + +static void updated_responses_queue_init(struct rfapi_descriptor *rfd) +{ + if (rfd->updated_responses_queue) + return; + + rfd->updated_responses_queue = + work_queue_new(bm->master, "rfapi updated responses"); + assert(rfd->updated_responses_queue); + + rfd->updated_responses_queue->spec.workfunc = rfapiRibDoQueuedCallback; + rfd->updated_responses_queue->spec.del_item_data = + rfapiRibQueueItemDelete; + rfd->updated_responses_queue->spec.max_retries = 0; + rfd->updated_responses_queue->spec.hold = 1; +} + +/* + * Called when an import table node is modified. Construct a + * new complete nexthop list, sorted by cost (lowest first), + * based on the import table node. + * + * Filter out duplicate nexthops (vn address). There should be + * only one UN address per VN address from the point of view of + * a given import table, so we can probably ignore UN addresses + * while filtering. + * + * Based on rfapiNhlAddNodeRoutes() + */ +void rfapiRibUpdatePendingNode( + struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, /* needed for L2 */ + struct agg_node *it_node, uint32_t lifetime) +{ + const struct prefix *prefix; + struct bgp_path_info *bpi; + struct agg_node *pn; + afi_t afi; + uint32_t queued_flag; + int count = 0; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (CHECK_FLAG(bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_CALLBACK_DISABLE)) + return; + + vnc_zlog_debug_verbose("%s: callbacks are not disabled", __func__); + + RFAPI_RIB_CHECK_COUNTS(1, 0); + + prefix = agg_node_get_prefix(it_node); + afi = family2afi(prefix->family); + vnc_zlog_debug_verbose("%s: prefix=%pFX", __func__, prefix); + + pn = agg_node_get(rfd->rib_pending[afi], prefix); + assert(pn); + + vnc_zlog_debug_verbose("%s: pn->info=%p, pn->aggregate=%p", __func__, + pn->info, pn->aggregate); + + if (pn->aggregate) { + /* + * free references into the rfapi_info structures before + * freeing the structures themselves + */ + skiplist_free((struct skiplist *)(pn->aggregate)); + pn->aggregate = NULL; + agg_unlock_node(pn); /* skiplist deleted */ + } + + + /* + * free the rfapi_info structures + */ + if (pn->info) { + if (pn->info != (void *)1) { + list_delete((struct list **)(&pn->info)); + } + pn->info = NULL; + agg_unlock_node(pn); /* linklist or 1 deleted */ + } + + /* + * The BPIs in the import table are already sorted by cost + */ + for (bpi = it_node->info; bpi; bpi = bpi->next) { + + struct rfapi_info *ri; + struct prefix pfx_nh; + + if (!bpi->extra) { + /* shouldn't happen */ + /* TBD increment error stats counter */ + continue; + } + + rfapiNexthop2Prefix(bpi->attr, &pfx_nh); + + /* + * Omit route if nexthop is self + */ + if (CHECK_FLAG(bgp->rfapi_cfg->flags, + BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) { + + struct prefix pfx_vn; + + assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn)); + if (prefix_same(&pfx_vn, &pfx_nh)) + continue; + } + + ri = rfapi_info_new(); + ri->rk.vn = pfx_nh; + ri->rk.rd = bpi->extra->vnc.import.rd; + /* + * If there is an auxiliary IP address (L2 can have it), copy it + */ + if (bpi->extra->vnc.import.aux_prefix.family) { + ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix; + } + + if (rfapiGetUnAddrOfVpnBi(bpi, &ri->un)) { + rfapi_info_free(ri); + continue; + } + + if (!pn->aggregate) { + pn->aggregate = + skiplist_new(0, rfapi_rib_key_cmp, NULL); + agg_lock_node(pn); + } + + /* + * If we have already added this nexthop, the insert will fail. + * Note that the skiplist key is a pointer INTO the rfapi_info + * structure which will be added to the "info" list. + * The skiplist entry VALUE is not used for anything but + * might be useful during debugging. + */ + if (skiplist_insert((struct skiplist *)pn->aggregate, &ri->rk, + ri)) { + + /* + * duplicate + */ + rfapi_info_free(ri); + continue; + } + + rfapiRibBi2Ri(bpi, ri, lifetime); + + if (!pn->info) { + pn->info = list_new(); + ((struct list *)(pn->info))->del = + (void (*)(void *))rfapi_info_free; + agg_lock_node(pn); + } + + listnode_add((struct list *)(pn->info), ri); + } + + if (pn->info) { + count = ((struct list *)(pn->info))->count; + } + + if (!count) { + assert(!pn->info); + assert(!pn->aggregate); + pn->info = (void *)1; /* magic value means this node has no + routes */ + agg_lock_node(pn); + } + + agg_unlock_node(pn); /* agg_node_get */ + + queued_flag = RFAPI_QUEUED_FLAG(afi); + + if (!CHECK_FLAG(rfd->flags, queued_flag)) { + + struct rfapi_updated_responses_queue *urq; + + urq = XCALLOC(MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, + sizeof(struct rfapi_updated_responses_queue)); + if (!rfd->updated_responses_queue) + updated_responses_queue_init(rfd); + + SET_FLAG(rfd->flags, queued_flag); + urq->rfd = rfd; + urq->afi = afi; + work_queue_add(rfd->updated_responses_queue, urq); + } + RFAPI_RIB_CHECK_COUNTS(1, 0); +} + +void rfapiRibUpdatePendingNodeSubtree( + struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, struct agg_node *it_node, + struct agg_node *omit_subtree, /* may be NULL */ + uint32_t lifetime) +{ + /* FIXME: need to find a better way here to work without sticking our + * hands in node->link */ + if (agg_node_left(it_node) + && (agg_node_left(it_node) != omit_subtree)) { + if (agg_node_left(it_node)->info) + rfapiRibUpdatePendingNode( + bgp, rfd, it, agg_node_left(it_node), lifetime); + rfapiRibUpdatePendingNodeSubtree(bgp, rfd, it, + agg_node_left(it_node), + omit_subtree, lifetime); + } + + if (agg_node_right(it_node) + && (agg_node_right(it_node) != omit_subtree)) { + if (agg_node_right(it_node)->info) + rfapiRibUpdatePendingNode(bgp, rfd, it, + agg_node_right(it_node), + lifetime); + rfapiRibUpdatePendingNodeSubtree(bgp, rfd, it, + agg_node_right(it_node), + omit_subtree, lifetime); + } +} + +/* + * RETURN VALUE + * + * 0 allow prefix to be included in response + * !0 don't allow prefix to be included in response + */ +int rfapiRibFTDFilterRecentPrefix( + struct rfapi_descriptor *rfd, + struct agg_node *it_rn, /* import table node */ + struct prefix *pfx_target_original) /* query target */ +{ + struct bgp *bgp = rfd->bgp; + const struct prefix *p = agg_node_get_prefix(it_rn); + afi_t afi = family2afi(p->family); + time_t prefix_time; + struct agg_node *trn; + + /* + * Not in FTD mode, so allow prefix + */ + if (bgp->rfapi_cfg->rfp_cfg.download_type != RFAPI_RFP_DOWNLOAD_FULL) + return 0; + + /* + * TBD + * This matches behavior of now-obsolete rfapiRibFTDFilterRecent(), + * but we need to decide if that is correct. + */ + if (p->family == AF_ETHERNET) + return 0; + +#ifdef DEBUG_FTD_FILTER_RECENT + { + vnc_zlog_debug_verbose("%s: prefix %pFX", __func__, + agg_node_get_prefix(it_rn)); + } +#endif + + /* + * prefix covers target address, so allow prefix + */ + if (prefix_match(p, pfx_target_original)) { +#ifdef DEBUG_FTD_FILTER_RECENT + vnc_zlog_debug_verbose("%s: prefix covers target, allowed", + __func__); +#endif + return 0; + } + + /* + * check this NVE's timestamp for this prefix + */ + trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */ + prefix_time = (time_t)trn->info; + if (agg_node_get_lock_count(trn) > 1) + agg_unlock_node(trn); + +#ifdef DEBUG_FTD_FILTER_RECENT + vnc_zlog_debug_verbose("%s: last sent time %lu, last allowed time %lu", + __func__, prefix_time, + rfd->ftd_last_allowed_time); +#endif + + /* + * haven't sent this prefix, which doesn't cover target address, + * to NVE since ftd_advertisement_interval, so OK to send now. + */ + if (prefix_time <= rfd->ftd_last_allowed_time) + return 0; + + return 1; +} + +/* + * Call when rfapi returns from rfapi_query() so the RIB reflects + * the routes sent to the NVE before the first updated response + * + * Also: remove duplicates from response. Caller should use returned + * value of nexthop chain. + */ +struct rfapi_next_hop_entry * +rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_next_hop_entry *response, int use_eth_resolution) +{ + struct rfapi_next_hop_entry *nhp; + struct rfapi_next_hop_entry *nhp_next; + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + time_t new_last_sent_time; + + vnc_zlog_debug_verbose("%s: loading response=%p, use_eth_resolution=%d", + __func__, response, use_eth_resolution); + + new_last_sent_time = monotime(NULL); + + for (nhp = response; nhp; nhp = nhp_next) { + + struct prefix pfx; + struct rfapi_rib_key rk; + afi_t afi; + struct rfapi_info *ri; + int need_insert; + struct agg_node *rn; + int rib_node_started_nonempty = 0; + struct agg_node *trn; + int allowed = 0; + + /* save in case we delete nhp */ + nhp_next = nhp->next; + + if (nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME) { + /* + * weird, shouldn't happen + */ + vnc_zlog_debug_verbose( + "%s: got nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME", + __func__); + continue; + } + + + if (use_eth_resolution) { + /* get the prefix of the ethernet address in the L2 + * option */ + struct rfapi_l2address_option *pL2o; + struct rfapi_vn_option *vo; + + /* + * Look for VN option of type + * RFAPI_VN_OPTION_TYPE_L2ADDR + */ + for (pL2o = NULL, vo = nhp->vn_options; vo; + vo = vo->next) { + if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) { + pL2o = &vo->v.l2addr; + break; + } + } + + if (!pL2o) { + /* + * not supposed to happen + */ + vnc_zlog_debug_verbose("%s: missing L2 info", + __func__); + continue; + } + + afi = AFI_L2VPN; + rfapiL2o2Qprefix(pL2o, &pfx); + } else { + rfapiRprefix2Qprefix(&nhp->prefix, &pfx); + afi = family2afi(pfx.family); + } + + /* + * TBD for ethernet, rib must know the right way to distinguish + * duplicate routes + * + * Current approach: prefix is key to radix tree; then + * each prefix has a set of routes with unique VN addrs + */ + + /* + * Look up prefix in RIB + */ + rn = agg_node_get(rfd->rib[afi], &pfx); /* locks rn */ + + if (rn->info) { + rib_node_started_nonempty = 1; + } else { + rn->info = skiplist_new(0, rfapi_rib_key_cmp, NULL); + agg_lock_node(rn); + } + + /* + * Look up route at prefix + */ + need_insert = 0; + memset((void *)&rk, 0, sizeof(rk)); + assert(!rfapiRaddr2Qprefix(&nhp->vn_address, &rk.vn)); + + if (use_eth_resolution) { + /* copy what came from aux_prefix to rk.aux_prefix */ + rfapiRprefix2Qprefix(&nhp->prefix, &rk.aux_prefix); + if (RFAPI_0_PREFIX(&rk.aux_prefix) + && RFAPI_HOST_PREFIX(&rk.aux_prefix)) { + /* mark as "none" if nhp->prefix is 0/32 or + * 0/128 */ + rk.aux_prefix.family = AF_UNSPEC; + } + } + +#if DEBUG_NHL + { + char str_aux_prefix[PREFIX_STRLEN]; + + str_aux_prefix[0] = 0; + + prefix2str(&rk.aux_prefix, str_aux_prefix, + sizeof(str_aux_prefix)); + + if (!rk.aux_prefix.family) { + } + vnc_zlog_debug_verbose( + "%s: rk.vn=%pFX rk.aux_prefix=%s", __func__, + &rk.vn, + (rk.aux_prefix.family ? str_aux_prefix : "-")); + } + vnc_zlog_debug_verbose( + "%s: RIB skiplist for this prefix follows", __func__); + rfapiRibShowRibSl(NULL, agg_node_get_prefix(rn), + (struct skiplist *)rn->info); +#endif + + + if (!skiplist_search((struct skiplist *)rn->info, &rk, + (void **)&ri)) { + /* + * Already have this route; make values match + */ + rfapiFreeRfapiUnOptionChain(ri->un_options); + ri->un_options = NULL; + rfapiFreeRfapiVnOptionChain(ri->vn_options); + ri->vn_options = NULL; + +#if DEBUG_NHL + vnc_zlog_debug_verbose("%s: found in RIB", __func__); +#endif + + /* + * Filter duplicate routes from initial response. + * Check timestamps to avoid wraparound problems + */ + if ((ri->rsp_counter != rfd->rsp_counter) + || (ri->last_sent_time != new_last_sent_time)) { + +#if DEBUG_NHL + vnc_zlog_debug_verbose( + "%s: allowed due to counter/timestamp diff", + __func__); +#endif + allowed = 1; + } + + } else { + +#if DEBUG_NHL + vnc_zlog_debug_verbose( + "%s: allowed due to not yet in RIB", __func__); +#endif + /* not found: add new route to RIB */ + ri = rfapi_info_new(); + need_insert = 1; + allowed = 1; + } + + ri->rk = rk; + assert(!rfapiRaddr2Qprefix(&nhp->un_address, &ri->un)); + ri->cost = nhp->prefix.cost; + ri->lifetime = nhp->lifetime; + ri->vn_options = rfapiVnOptionsDup(nhp->vn_options); + ri->rsp_counter = rfd->rsp_counter; + ri->last_sent_time = monotime(NULL); + + if (need_insert) { + int rc; + rc = skiplist_insert((struct skiplist *)rn->info, + &ri->rk, ri); + assert(!rc); + } + + if (!rib_node_started_nonempty) { + RFAPI_RIB_PREFIX_COUNT_INCR(rfd, bgp->rfapi); + } + + RFAPI_RIB_CHECK_COUNTS(0, 0); + rfapiRibStartTimer(rfd, ri, rn, 0); + RFAPI_RIB_CHECK_COUNTS(0, 0); + + agg_unlock_node(rn); + + /* + * update this NVE's timestamp for this prefix + */ + trn = agg_node_get(rfd->rsp_times[afi], &pfx); /* locks trn */ + trn->info = (void *)(uintptr_t)monotime(NULL); + if (agg_node_get_lock_count(trn) > 1) + agg_unlock_node(trn); + + vnc_zlog_debug_verbose( + "%s: added pfx=%pFX nh[vn]=%pFX, cost=%u, lifetime=%u, allowed=%d", + __func__, &pfx, &rk.vn, nhp->prefix.cost, nhp->lifetime, + allowed); + + if (allowed) { + if (tail) + (tail)->next = nhp; + tail = nhp; + if (!head) { + head = nhp; + } + } else { + rfapi_un_options_free(nhp->un_options); + nhp->un_options = NULL; + rfapi_vn_options_free(nhp->vn_options); + nhp->vn_options = NULL; + + XFREE(MTYPE_RFAPI_NEXTHOP, nhp); + } + } + + if (tail) + tail->next = NULL; + return head; +} + +void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it, + afi_t afi, struct agg_node *it_node) +{ + struct rfapi_descriptor *rfd; + struct listnode *node; + const struct prefix *p = agg_node_get_prefix(it_node); + + vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%pRN", + __func__, it, afi, it_node, it_node); + + if (AFI_L2VPN == afi) { + /* + * ethernet import tables are per-LNI and each ethernet monitor + * identifies the rfd that owns it. + */ + struct rfapi_monitor_eth *m; + struct agg_node *rn; + struct skiplist *sl; + void *cursor; + int rc; + + /* + * route-specific monitors + */ + if ((sl = RFAPI_MONITOR_ETH(it_node))) { + + vnc_zlog_debug_verbose( + "%s: route-specific skiplist: %p", __func__, + sl); + + for (cursor = NULL, + rc = skiplist_next(sl, NULL, (void **)&m, &cursor); + !rc; rc = skiplist_next(sl, NULL, (void **)&m, + &cursor)) { + +#if DEBUG_PENDING_DELETE_ROUTE + vnc_zlog_debug_verbose("%s: eth monitor rfd=%p", + __func__, m->rfd); +#endif + /* + * If we have already sent a route with this + * prefix to this + * NVE, it's OK to send an update with the + * delete + */ + if ((rn = agg_node_lookup(m->rfd->rib[afi], + p))) { + rfapiRibUpdatePendingNode( + bgp, m->rfd, it, it_node, + m->rfd->response_lifetime); + agg_unlock_node(rn); + } + } + } + + /* + * all-routes/FTD monitors + */ + for (m = it->eth0_queries; m; m = m->next) { +#if DEBUG_PENDING_DELETE_ROUTE + vnc_zlog_debug_verbose("%s: eth0 monitor rfd=%p", + __func__, m->rfd); +#endif + /* + * If we have already sent a route with this prefix to + * this + * NVE, it's OK to send an update with the delete + */ + if ((rn = agg_node_lookup(m->rfd->rib[afi], p))) { + rfapiRibUpdatePendingNode( + bgp, m->rfd, it, it_node, + m->rfd->response_lifetime); + agg_unlock_node(rn); + } + } + + } else { + /* + * Find RFDs that reference this import table + */ + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, + rfd)) { + + struct agg_node *rn; + + vnc_zlog_debug_verbose( + "%s: comparing rfd(%p)->import_table=%p to it=%p", + __func__, rfd, rfd->import_table, it); + + if (rfd->import_table != it) + continue; + + vnc_zlog_debug_verbose("%s: matched rfd %p", __func__, + rfd); + + /* + * If we have sent a response to this NVE with this + * prefix + * previously, we should send an updated response. + */ + if ((rn = agg_node_lookup(rfd->rib[afi], p))) { + rfapiRibUpdatePendingNode( + bgp, rfd, it, it_node, + rfd->response_lifetime); + agg_unlock_node(rn); + } + } + } +} + +void rfapiRibShowResponsesSummary(void *stream) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + struct bgp *bgp = bgp_get_default(); + + int nves = 0; + int nves_with_nonempty_ribs = 0; + struct rfapi_descriptor *rfd; + struct listnode *node; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bgp) { + fp(out, "Unable to find default BGP instance\n"); + return; + } + + fp(out, "%-24s ", "Responses: (Prefixes)"); + fp(out, "%-8s %-8u ", "Active:", bgp->rfapi->rib_prefix_count_total); + fp(out, "%-8s %-8u", + "Maximum:", bgp->rfapi->rib_prefix_count_total_max); + fp(out, "\n"); + + fp(out, "%-24s ", " (Updated)"); + fp(out, "%-8s %-8u ", + "Update:", bgp->rfapi->stat.count_updated_response_updates); + fp(out, "%-8s %-8u", + "Remove:", bgp->rfapi->stat.count_updated_response_deletes); + fp(out, "%-8s %-8u", "Total:", + bgp->rfapi->stat.count_updated_response_updates + + bgp->rfapi->stat.count_updated_response_deletes); + fp(out, "\n"); + + fp(out, "%-24s ", " (NVEs)"); + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) { + ++nves; + if (rfd->rib_prefix_count) + ++nves_with_nonempty_ribs; + } + fp(out, "%-8s %-8u ", "Active:", nves_with_nonempty_ribs); + fp(out, "%-8s %-8u", "Total:", nves); + fp(out, "\n"); +} + +void rfapiRibShowResponsesSummaryClear(void) +{ + struct bgp *bgp = bgp_get_default(); + + bgp->rfapi->rib_prefix_count_total_max = + bgp->rfapi->rib_prefix_count_total; +} + +static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty, + void *out, struct skiplist *sl, int deleted, + char *str_pfx, int *printedprefix) +{ + struct rfapi_info *ri; + int rc; + void *cursor; + int routes_displayed = 0; + + cursor = NULL; + for (rc = skiplist_next(sl, NULL, (void **)&ri, &cursor); !rc; + rc = skiplist_next(sl, NULL, (void **)&ri, &cursor)) { + + char str_vn[PREFIX_STRLEN]; + char str_un[PREFIX_STRLEN]; + char str_lifetime[BUFSIZ]; + char str_age[BUFSIZ]; + char *p; + + ++routes_displayed; + + prefix2str(&ri->rk.vn, str_vn, sizeof(str_vn)); + p = index(str_vn, '/'); + if (p) + *p = 0; + + prefix2str(&ri->un, str_un, sizeof(str_un)); + p = index(str_un, '/'); + if (p) + *p = 0; + + rfapiFormatSeconds(ri->lifetime, str_lifetime, BUFSIZ); +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE + rfapiFormatAge(ri->last_sent_time, str_age, BUFSIZ); +#else + { + time_t now = monotime(NULL); + time_t expire = + ri->last_sent_time + (time_t)ri->lifetime; + /* allow for delayed/async removal */ + rfapiFormatSeconds((expire > now ? expire - now : 1), + str_age, BUFSIZ); + } +#endif + + fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRDP\n", + deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn, + str_un, ri->cost, str_lifetime, str_age, &ri->rk.rd); + + if (!*printedprefix) + *printedprefix = 1; + } + return routes_displayed; +} + +#if DEBUG_NHL +/* + * This one is for debugging (set stream to NULL to send output to log) + */ +static void rfapiRibShowRibSl(void *stream, struct prefix *pfx, + struct skiplist *sl) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + int nhs_displayed = 0; + char str_pfx[PREFIX_STRLEN]; + int printedprefix = 0; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + prefix2str(pfx, str_pfx, sizeof(str_pfx)); + + nhs_displayed += + print_rib_sl(fp, vty, out, sl, 0, str_pfx, &printedprefix); +} +#endif + +void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, + int show_removed) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + struct rfapi_descriptor *rfd; + struct listnode *node; + + struct bgp *bgp = bgp_get_default(); + int printedheader = 0; + int routes_total = 0; + int nhs_total = 0; + int nves_displayed = 0; + int routes_displayed = 0; + int nhs_displayed = 0; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + if (!bgp) { + fp(out, "Unable to find default BGP instance\n"); + return; + } + + /* + * loop over NVEs + */ + for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) { + + int printednve = 0; + afi_t afi; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_node *rn; + + if (!rfd->rib[afi]) + continue; + + for (rn = agg_route_top(rfd->rib[afi]); rn; + rn = agg_route_next(rn)) { + const struct prefix *p = + agg_node_get_prefix(rn); + struct skiplist *sl; + char str_pfx[PREFIX_STRLEN]; + int printedprefix = 0; + + if (!show_removed) + sl = rn->info; + else + sl = rn->aggregate; + + if (!sl) + continue; + + routes_total++; + nhs_total += skiplist_count(sl); + + if (pfx_match && !prefix_match(pfx_match, p) + && !prefix_match(p, pfx_match)) + continue; + + if (!printedheader) { + ++printedheader; + + fp(out, "\n[%s]\n", + show_removed ? "Removed" : "Active"); + fp(out, "%-15s %-15s\n", "Querying VN", + "Querying UN"); + fp(out, + " %-20s %-15s %-15s %4s %-8s %-8s\n", + "Prefix", "Registered VN", + "Registered UN", "Cost", "Lifetime", +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE + "Age" +#else + "Remaining" +#endif + ); + } + if (!printednve) { + char str_vn[BUFSIZ]; + char str_un[BUFSIZ]; + + ++printednve; + ++nves_displayed; + + fp(out, "%-15s %-15s\n", + rfapiRfapiIpAddr2Str(&rfd->vn_addr, + str_vn, BUFSIZ), + rfapiRfapiIpAddr2Str(&rfd->un_addr, + str_un, + BUFSIZ)); + } + prefix2str(p, str_pfx, sizeof(str_pfx)); + // fp(out, " %s\n", buf); /* prefix */ + + routes_displayed++; + nhs_displayed += print_rib_sl( + fp, vty, out, sl, show_removed, str_pfx, + &printedprefix); + } + } + } + + if (routes_total) { + fp(out, "\n"); + fp(out, "Displayed %u NVEs, and %u out of %u %s prefixes", + nves_displayed, routes_displayed, routes_total, + show_removed ? "removed" : "active"); + if (nhs_displayed != routes_displayed + || nhs_total != routes_total) + fp(out, " with %u out of %u next hops", nhs_displayed, + nhs_total); + fp(out, "\n"); + } +} diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h new file mode 100644 index 0000000..5fa838b --- /dev/null +++ b/bgpd/rfapi/rfapi_rib.h @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: rfapi_rib.h + * Purpose: per-nve rib + */ + +#ifndef QUAGGA_HGP_RFAPI_RIB_H +#define QUAGGA_HGP_RFAPI_RIB_H + +/* + * Key for indexing RIB and Pending RIB skiplists. For L3 RIBs, + * the VN address is sufficient because it represents the actual next hop. + * + * For L2 RIBs, it is possible to have multiple routes to a given L2 + * prefix via a given VN address, but each route having a unique aux_prefix. + */ +struct rfapi_rib_key { + struct prefix vn; + struct prefix_rd rd; + + /* + * for L2 routes: optional IP addr + * .family == 0 means "none" + */ + struct prefix aux_prefix; +}; +#include "rfapi.h" + +/* + * RFAPI Advertisement Data Block + * + * Holds NVE prefix advertisement information + */ +struct rfapi_adb { + union { + struct { + struct prefix prefix_ip; + struct prefix_rd prd; + struct prefix prefix_eth; + } s; /* mainly for legacy use */ + struct rfapi_rib_key key; + } u; + uint32_t lifetime; + uint8_t cost; + struct rfapi_l2address_option l2o; +}; + +struct rfapi_info { + struct rfapi_rib_key rk; /* NVE VN addr + aux addr */ + struct prefix un; + uint8_t cost; + uint32_t lifetime; + time_t last_sent_time; + uint32_t rsp_counter; /* dedup initial responses */ + struct bgp_tea_options *tea_options; + struct rfapi_un_option *un_options; + struct rfapi_vn_option *vn_options; + struct event *timer; +}; + +/* + * Work item for updated responses queue + */ +struct rfapi_updated_responses_queue { + struct rfapi_descriptor *rfd; + afi_t afi; +}; + + +extern void rfapiRibClear(struct rfapi_descriptor *rfd); + +extern void rfapiRibFree(struct rfapi_descriptor *rfd); + +extern void rfapiRibUpdatePendingNode(struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, + struct agg_node *it_node, + uint32_t lifetime); + +extern void rfapiRibUpdatePendingNodeSubtree(struct bgp *bgp, + struct rfapi_descriptor *rfd, + struct rfapi_import_table *it, + struct agg_node *it_node, + struct agg_node *omit_subtree, + uint32_t lifetime); + +extern int rfapiRibPreloadBi(struct agg_node *rfd_rib_node, + struct prefix *pfx_vn, struct prefix *pfx_un, + uint32_t lifetime, struct bgp_path_info *bpi); + +extern struct rfapi_next_hop_entry * +rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd, + struct rfapi_next_hop_entry *response, int use_eth_resolution); + +extern void rfapiRibPendingDeleteRoute(struct bgp *bgp, + struct rfapi_import_table *it, afi_t afi, + struct agg_node *it_node); + +extern void rfapiRibShowResponsesSummary(void *stream); + +extern void rfapiRibShowResponsesSummaryClear(void); + +extern void rfapiRibShowResponses(void *stream, struct prefix *pfx_match, + int show_removed); + +extern int rfapiRibFTDFilterRecentPrefix( + struct rfapi_descriptor *rfd, + struct agg_node *it_rn, /* import table node */ + struct prefix *pfx_target_original); /* query target */ + +extern void rfapiFreeRfapiUnOptionChain(struct rfapi_un_option *p); + +extern void rfapiFreeRfapiVnOptionChain(struct rfapi_vn_option *p); + +extern void +rfapiRibCheckCounts(int checkstats, /* validate rfd & global counts */ + unsigned int offset); /* number of ri's held separately */ + +/* enable for debugging; disable for performance */ +#if 0 +#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) rfapiRibCheckCounts(checkstats, offset) +#else +#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) +#endif + +extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ + struct prefix_rd *rd, /* may be NULL */ + struct prefix *aux, /* may be NULL */ + struct rfapi_rib_key *rk); + +extern int rfapi_rib_key_cmp(const void *k1, const void *k2); + +extern void rfapiAdbFree(struct rfapi_adb *adb); + +#endif /* QUAGGA_HGP_RFAPI_RIB_H */ diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c new file mode 100644 index 0000000..252b6d6 --- /dev/null +++ b/bgpd/rfapi/rfapi_vty.c @@ -0,0 +1,5008 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/memory.h" +#include "lib/routemap.h" +#include "lib/log.h" +#include "lib/linklist.h" +#include "lib/command.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" + +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_backend.h" + +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_vnc_types.h" +#include "bgpd/bgp_label.h" + +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_rib.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/rfapi_ap.h" +#include "bgpd/rfapi/rfapi_encap_tlv.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define DEBUG_L2_EXTRA 0 +#define DEBUG_SHOW_EXTRA 0 + +#define VNC_SHOW_STR "VNC information\n" + +/* format related utilies */ + + +#define FMT_MIN 60 /* seconds */ +#define FMT_HOUR (60 * FMT_MIN) +#define FMT_DAY (24 * FMT_HOUR) +#define FMT_YEAR (365 * FMT_DAY) + +char *rfapiFormatSeconds(uint32_t seconds, char *buf, size_t len) +{ + int year, day, hour, min; + + if (seconds >= FMT_YEAR) { + year = seconds / FMT_YEAR; + seconds -= year * FMT_YEAR; + } else + year = 0; + + if (seconds >= FMT_DAY) { + day = seconds / FMT_DAY; + seconds -= day * FMT_DAY; + } else + day = 0; + + if (seconds >= FMT_HOUR) { + hour = seconds / FMT_HOUR; + seconds -= hour * FMT_HOUR; + } else + hour = 0; + + if (seconds >= FMT_MIN) { + min = seconds / FMT_MIN; + seconds -= min * FMT_MIN; + } else + min = 0; + + if (year > 0) { + snprintf(buf, len, "%dy%dd%dh", year, day, hour); + } else if (day > 0) { + snprintf(buf, len, "%dd%dh%dm", day, hour, min); + } else { + snprintf(buf, len, "%02d:%02d:%02d", hour, min, seconds); + } + + return buf; +} + +char *rfapiFormatAge(time_t age, char *buf, size_t len) +{ + time_t now, age_adjusted; + + now = monotime(NULL); + age_adjusted = now - age; + + return rfapiFormatSeconds(age_adjusted, buf, len); +} + + +/* + * Reimplementation of quagga/lib/prefix.c function, but + * for RFAPI-style prefixes + */ +void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix) +{ + uint8_t *pnt; + int index; + int offset; + + static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + + switch (rprefix->prefix.addr_family) { + case AF_INET: + index = rprefix->length / 8; + if (index < 4) { + pnt = (uint8_t *)&rprefix->prefix.addr.v4; + offset = rprefix->length % 8; + pnt[index] &= maskbit[offset]; + index++; + while (index < 4) + pnt[index++] = 0; + } + break; + + case AF_INET6: + index = rprefix->length / 8; + if (index < 16) { + pnt = (uint8_t *)&rprefix->prefix.addr.v6; + offset = rprefix->length % 8; + pnt[index] &= maskbit[offset]; + index++; + while (index < 16) + pnt[index++] = 0; + } + break; + + default: + assert(0); + } +} + +/* + * translate a quagga prefix into a rfapi IP address. The + * prefix is REQUIRED to be 32 bits for IPv4 and 128 bits for IPv6 + * + * RETURNS: + * + * 0 Success + * <0 Error + */ +int rfapiQprefix2Raddr(struct prefix *qprefix, struct rfapi_ip_addr *raddr) +{ + memset(raddr, 0, sizeof(struct rfapi_ip_addr)); + raddr->addr_family = qprefix->family; + switch (qprefix->family) { + case AF_INET: + if (qprefix->prefixlen != IPV4_MAX_BITLEN) + return -1; + raddr->addr.v4 = qprefix->u.prefix4; + break; + case AF_INET6: + if (qprefix->prefixlen != IPV6_MAX_BITLEN) + return -1; + raddr->addr.v6 = qprefix->u.prefix6; + break; + default: + return -1; + } + return 0; +} + +/* + * Translate Quagga prefix to RFAPI prefix + */ +/* rprefix->cost set to 0 */ +void rfapiQprefix2Rprefix(const struct prefix *qprefix, + struct rfapi_ip_prefix *rprefix) +{ + memset(rprefix, 0, sizeof(struct rfapi_ip_prefix)); + rprefix->length = qprefix->prefixlen; + rprefix->prefix.addr_family = qprefix->family; + switch (qprefix->family) { + case AF_INET: + rprefix->prefix.addr.v4 = qprefix->u.prefix4; + break; + case AF_INET6: + rprefix->prefix.addr.v6 = qprefix->u.prefix6; + break; + default: + assert(0); + } +} + +int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix, + struct prefix *qprefix) +{ + memset(qprefix, 0, sizeof(struct prefix)); + qprefix->prefixlen = rprefix->length; + qprefix->family = rprefix->prefix.addr_family; + + switch (rprefix->prefix.addr_family) { + case AF_INET: + qprefix->u.prefix4 = rprefix->prefix.addr.v4; + break; + case AF_INET6: + qprefix->u.prefix6 = rprefix->prefix.addr.v6; + break; + default: + return EAFNOSUPPORT; + } + return 0; +} + +/* + * returns 1 if prefixes have same addr family, prefix len, and address + * Note that host bits matter in this comparison! + * + * For paralellism with quagga/lib/prefix.c. if we need a comparison + * where host bits are ignored, call that function rfapiRprefixCmp. + */ +int rfapiRprefixSame(struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2) +{ + if (hp1->prefix.addr_family != hp2->prefix.addr_family) + return 0; + if (hp1->length != hp2->length) + return 0; + if (hp1->prefix.addr_family == AF_INET) + if (IPV4_ADDR_SAME(&hp1->prefix.addr.v4, &hp2->prefix.addr.v4)) + return 1; + if (hp1->prefix.addr_family == AF_INET6) + if (IPV6_ADDR_SAME(&hp1->prefix.addr.v6, &hp2->prefix.addr.v6)) + return 1; + return 0; +} + +int rfapiRaddr2Qprefix(struct rfapi_ip_addr *hia, struct prefix *pfx) +{ + memset(pfx, 0, sizeof(struct prefix)); + pfx->family = hia->addr_family; + + switch (hia->addr_family) { + case AF_INET: + pfx->prefixlen = IPV4_MAX_BITLEN; + pfx->u.prefix4 = hia->addr.v4; + break; + case AF_INET6: + pfx->prefixlen = IPV6_MAX_BITLEN; + pfx->u.prefix6 = hia->addr.v6; + break; + default: + return EAFNOSUPPORT; + } + return 0; +} + +void rfapiL2o2Qprefix(struct rfapi_l2address_option *l2o, struct prefix *pfx) +{ + memset(pfx, 0, sizeof(struct prefix)); + pfx->family = AF_ETHERNET; + pfx->prefixlen = 48; + pfx->u.prefix_eth = l2o->macaddr; +} + +char *rfapiEthAddr2Str(const struct ethaddr *ea, char *buf, int bufsize) +{ + return prefix_mac2str(ea, buf, bufsize); +} + +int rfapiStr2EthAddr(const char *str, struct ethaddr *ea) +{ + unsigned int a[6]; + int i; + + if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2, a + 3, + a + 4, a + 5) + != 6) { + + return EINVAL; + } + + for (i = 0; i < 6; ++i) + ea->octet[i] = a[i] & 0xff; + + return 0; +} + +const char *rfapi_ntop(int af, const void *src, char *buf, socklen_t size) +{ + if (af == AF_ETHERNET) { + return rfapiEthAddr2Str((const struct ethaddr *)src, buf, size); + } + + return inet_ntop(af, src, buf, size); +} + +int rfapiDebugPrintf(void *dummy, const char *format, ...) +{ + va_list args; + va_start(args, format); + vzlog(LOG_DEBUG, format, args); + va_end(args); + return 0; +} + +PRINTFRR(2, 3) +static int rfapiStdioPrintf(void *stream, const char *format, ...) +{ + FILE *file = NULL; + + va_list args; + va_start(args, format); + + switch ((uintptr_t)stream) { + case 1: + file = stdout; + break; + case 2: + file = stderr; + break; + default: + assert(0); + } + + vfprintf(file, format, args); + va_end(args); + return 0; +} + +/* Fake out for debug logging */ +static struct vty vty_dummy_zlog; +static struct vty vty_dummy_stdio; +#define HVTYNL ((vty == &vty_dummy_zlog)? "": "\n") + +static const char *str_vty_newline(struct vty *vty) +{ + if (vty == &vty_dummy_zlog) + return ""; + return "\n"; +} + +int rfapiStream2Vty(void *stream, /* input */ + int (**fp)(void *, const char *, ...), /* output */ + struct vty **vty, /* output */ + void **outstream, /* output */ + const char **vty_newline) /* output */ +{ + + if (!stream) { + vty_dummy_zlog.type = VTY_SHELL; /* for VTYNL */ + *vty = &vty_dummy_zlog; + *fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf; + *outstream = NULL; + *vty_newline = str_vty_newline(*vty); + return 1; + } + + if (((uintptr_t)stream == (uintptr_t)1) + || ((uintptr_t)stream == (uintptr_t)2)) { + + vty_dummy_stdio.type = VTY_SHELL; /* for VTYNL */ + *vty = &vty_dummy_stdio; + *fp = (int (*)(void *, const char *, ...))rfapiStdioPrintf; + *outstream = stream; + *vty_newline = str_vty_newline(*vty); + return 1; + } + + *vty = stream; /* VTYNL requires vty to be legit */ + *fp = (int (*)(void *, const char *, ...))vty_out; + *outstream = stream; + *vty_newline = str_vty_newline(*vty); + return 1; +} + +/* called from bgpd/bgp_vty.c'route_vty_out() */ +void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p, + struct bgp_path_info *bpi, safi_t safi) +{ + char *s; + uint32_t lifetime; + + /* + * Print, on an indented line: + * UN address [if VPN route and VNC UN addr subtlv] + * EC list + * VNC lifetime + */ + vty_out(vty, " "); + + if (safi == SAFI_MPLS_VPN) { + struct prefix pfx_un; + + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) { + char buf[BUFSIZ]; + + vty_out(vty, "UN=%s", + inet_ntop(pfx_un.family, pfx_un.u.val, buf, + sizeof(buf))); + } + } + + if (bgp_attr_get_ecommunity(bpi->attr)) { + s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " EC{%s}", s); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + if (bpi->extra != NULL) { + if (bpi->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK) + vty_out(vty, " label=VRF2VRF"); + else + vty_out(vty, " label=%u", + decode_label(&bpi->extra->label[0])); + + if (bpi->attr->srv6_l3vpn || bpi->attr->srv6_vpn) { + struct in6_addr *sid_tmp = + bpi->attr->srv6_l3vpn + ? (&bpi->attr->srv6_l3vpn->sid) + : (&bpi->attr->srv6_vpn->sid); + vty_out(vty, " sid=%pI6", sid_tmp); + + if (bpi->attr->srv6_l3vpn && + bpi->attr->srv6_l3vpn->loc_block_len != 0) { + vty_out(vty, " sid_structure=[%d,%d,%d,%d]", + bpi->attr->srv6_l3vpn->loc_block_len, + bpi->attr->srv6_l3vpn->loc_node_len, + bpi->attr->srv6_l3vpn->func_len, + bpi->attr->srv6_l3vpn->arg_len); + } + } + } + + if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) { + vty_out(vty, " life=%d", lifetime); + } + + vty_out(vty, " type=%s, subtype=%d", zebra_route_string(bpi->type), + bpi->sub_type); + + vty_out(vty, "%s", HVTYNL); +} + +void rfapiPrintAttrPtrs(void *stream, struct attr *attr) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + struct transit *transit; + struct cluster_list *cluster; + struct ecommunity *ecomm; + struct community *comm; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp(out, "Attr[%p]:%s", attr, HVTYNL); + if (!attr) + return; + + /* IPv4 Nexthop */ + fp(out, " nexthop=%pI4%s", &attr->nexthop, HVTYNL); + + fp(out, " aspath=%p, refcnt=%d%s", attr->aspath, + (attr->aspath ? attr->aspath->refcnt : 0), HVTYNL); + + comm = bgp_attr_get_community(attr); + fp(out, " community=%p, refcnt=%d%s", comm, (comm ? comm->refcnt : 0), + HVTYNL); + + ecomm = bgp_attr_get_ecommunity(attr); + fp(out, " ecommunity=%p, refcnt=%d%s", ecomm, + (ecomm ? ecomm->refcnt : 0), HVTYNL); + + cluster = bgp_attr_get_cluster(attr); + fp(out, " cluster=%p, refcnt=%d%s", cluster, + (cluster ? cluster->refcnt : 0), HVTYNL); + + transit = bgp_attr_get_transit(attr); + fp(out, " transit=%p, refcnt=%d%s", transit, + (transit ? transit->refcnt : 0), HVTYNL); +} + +/* + * Print BPI in an Import Table + */ +void rfapiPrintBi(void *stream, struct bgp_path_info *bpi) +{ + char buf[BUFSIZ]; + char *s; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + char line[BUFSIZ]; + char *p = line; + int r; + int has_macaddr = 0; + struct ethaddr macaddr = {{0}}; + struct rfapi_l2address_option l2o_buf; + uint8_t l2hid = 0; /* valid if has_macaddr */ + +#define REMAIN (BUFSIZ - (p-line)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + if (!bpi) + return; + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra + && bpi->extra->vnc.import.timer) { + struct event *t = (struct event *)bpi->extra->vnc.import.timer; + + r = snprintf(p, REMAIN, " [%4lu] ", + event_timer_remain_second(t)); + INCP; + + } else { + r = snprintf(p, REMAIN, " "); + INCP; + } + + if (bpi->extra) { + /* TBD This valid only for SAFI_MPLS_VPN, but not for encap */ + if (decode_rd_type(bpi->extra->vnc.import.rd.val) + == RD_TYPE_VNC_ETH) { + has_macaddr = 1; + memcpy(macaddr.octet, bpi->extra->vnc.import.rd.val + 2, + 6); + l2hid = bpi->extra->vnc.import.rd.val[1]; + } + } + + /* + * Print these items: + * type/subtype + * nexthop address + * lifetime + * RFP option sizes (they are opaque values) + * extended communities (RTs) + */ + uint32_t lifetime; + int printed_1st_gol = 0; + struct bgp_attr_encap_subtlv *pEncap; + struct prefix pfx_un; + int af = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len); + + /* Nexthop */ + if (af == AF_INET) { + r = snprintfrr(p, REMAIN, "%pI4", + &bpi->attr->mp_nexthop_global_in); + INCP; + } else if (af == AF_INET6) { + r = snprintfrr(p, REMAIN, "%pI6", + &bpi->attr->mp_nexthop_global); + INCP; + } else { + r = snprintf(p, REMAIN, "?"); + INCP; + } + + /* + * VNC tunnel subtlv, if present, contains UN address + */ + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) { + r = snprintf(p, REMAIN, " un=%s", + inet_ntop(pfx_un.family, pfx_un.u.val, buf, + sizeof(buf))); + INCP; + } + + /* Lifetime */ + if (rfapiGetVncLifetime(bpi->attr, &lifetime)) { + r = snprintf(p, REMAIN, " nolife"); + INCP; + } else { + if (lifetime == 0xffffffff) + r = snprintf(p, REMAIN, " %6s", "infini"); + else + r = snprintf(p, REMAIN, " %6u", lifetime); + INCP; + } + + /* RFP option lengths */ + for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap; + pEncap = pEncap->next) { + + if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) { + if (printed_1st_gol) { + r = snprintf(p, REMAIN, ","); + INCP; + } else { + r = snprintf(p, REMAIN, + " "); /* leading space */ + INCP; + } + r = snprintf(p, REMAIN, "%d", pEncap->length); + INCP; + printed_1st_gol = 1; + } + } + + /* RT list */ + if (bgp_attr_get_ecommunity(bpi->attr)) { + s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr), + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + r = snprintf(p, REMAIN, " %s", s); + INCP; + XFREE(MTYPE_ECOMMUNITY_STR, s); + } + + r = snprintf(p, REMAIN, " bpi@%p", bpi); + INCP; + + r = snprintf(p, REMAIN, " p@%p", bpi->peer); + INCP; + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) { + r = snprintf(p, REMAIN, " HD=yes"); + INCP; + } else { + r = snprintf(p, REMAIN, " HD=no"); + INCP; + } + + if (bpi->attr->weight) { + r = snprintf(p, REMAIN, " W=%d", bpi->attr->weight); + INCP; + } + + if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + r = snprintf(p, REMAIN, " LP=%d", bpi->attr->local_pref); + INCP; + } else { + r = snprintf(p, REMAIN, " LP=unset"); + INCP; + } + + r = snprintf(p, REMAIN, " %c:%u", zebra_route_char(bpi->type), + bpi->sub_type); + INCP; + + fp(out, "%s%s", line, HVTYNL); + + if (has_macaddr) { + fp(out, " RD HID=%d ETH=%02x:%02x:%02x:%02x:%02x:%02x%s", + l2hid, macaddr.octet[0], macaddr.octet[1], macaddr.octet[2], + macaddr.octet[3], macaddr.octet[4], macaddr.octet[5], + HVTYNL); + } + + if (!rfapiGetL2o(bpi->attr, &l2o_buf)) { + fp(out, + " L2O ETH=%02x:%02x:%02x:%02x:%02x:%02x LBL=%d LNI=%d LHI=%hhu%s", + l2o_buf.macaddr.octet[0], l2o_buf.macaddr.octet[1], + l2o_buf.macaddr.octet[2], l2o_buf.macaddr.octet[3], + l2o_buf.macaddr.octet[4], l2o_buf.macaddr.octet[5], + l2o_buf.label, l2o_buf.logical_net_id, l2o_buf.local_nve_id, + HVTYNL); + } + if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { + const char *sp; + + sp = rfapi_ntop(bpi->extra->vnc.import.aux_prefix.family, + &bpi->extra->vnc.import.aux_prefix.u.prefix, + buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + if (sp) { + fp(out, " IP: %s%s", sp, HVTYNL); + } + } + { + struct rfapi_un_option *uo = + rfapi_encap_tlv_to_un_option(bpi->attr); + if (uo) { + rfapi_print_tunneltype_option(stream, 8, &uo->v.tunnel); + rfapi_un_options_free(uo); + } + } +} + +char *rfapiMonitorVpn2Str(struct rfapi_monitor_vpn *m, char *buf, int size) +{ + char buf_pfx[BUFSIZ]; + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + int rc; + + rfapiRfapiIpAddr2Str(&m->rfd->un_addr, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str(&m->rfd->vn_addr, buf_un, BUFSIZ); + + rc = snprintf(buf, size, + "m=%p, next=%p, rfd=%p(vn=%s un=%s), p=%s/%d, node=%p", m, + m->next, m->rfd, buf_vn, buf_un, + inet_ntop(m->p.family, &m->p.u.prefix, buf_pfx, + sizeof(buf_pfx)), + m->p.prefixlen, m->node); + buf[size - 1] = 0; + if (rc >= size) + return NULL; + return buf; +} + +static void rfapiDebugPrintMonitorVpn(void *stream, struct rfapi_monitor_vpn *m) +{ + char buf[BUFSIZ]; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rfapiMonitorVpn2Str(m, buf, BUFSIZ); + fp(out, " Mon %s%s", buf, HVTYNL); +} + +static void rfapiDebugPrintMonitorEncap(void *stream, + struct rfapi_monitor_encap *m) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp(out, " Mon m=%p, next=%p, node=%p, bpi=%p%s", m, m->next, m->node, + m->bpi, HVTYNL); +} + +void rfapiShowItNode(void *stream, struct agg_node *rn) +{ + struct bgp_path_info *bpi; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp(out, "%pRN @%p #%d%s", rn, rn, agg_node_get_lock_count(rn), HVTYNL); + + for (bpi = rn->info; bpi; bpi = bpi->next) { + rfapiPrintBi(stream, bpi); + } + + /* doesn't show montors */ +} + +void rfapiShowImportTable(void *stream, const char *label, struct agg_table *rt, + int isvpn) +{ + struct agg_node *rn; + char buf[BUFSIZ]; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + fp(out, "Import Table [%s]%s", label, HVTYNL); + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + struct bgp_path_info *bpi; + const struct prefix *p = agg_node_get_prefix(rn); + + if (p->family == AF_ETHERNET) { + rfapiEthAddr2Str(&p->u.prefix_eth, buf, sizeof(buf)); + } else { + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)); + } + + fp(out, "%s/%d @%p #%d%s", buf, p->prefixlen, rn, + agg_node_get_lock_count(rn) + - 1, /* account for loop iterator locking */ + HVTYNL); + + for (bpi = rn->info; bpi; bpi = bpi->next) { + rfapiPrintBi(stream, bpi); + } + + if (isvpn) { + struct rfapi_monitor_vpn *m; + for (m = RFAPI_MONITOR_VPN(rn); m; m = m->next) { + rfapiDebugPrintMonitorVpn(stream, m); + } + } else { + struct rfapi_monitor_encap *m; + for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) { + rfapiDebugPrintMonitorEncap(stream, m); + } + } + } +} + +int rfapiShowVncQueries(void *stream, struct prefix *pfx_match) +{ + struct bgp *bgp; + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + + int printedheader = 0; + int queries_total = 0; + int queries_displayed = 0; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return CMD_WARNING; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + if (!bgp) { + vty_out(vty, "No BGP instance\n"); + return CMD_WARNING; + } + + h = bgp->rfapi; + if (!h) { + vty_out(vty, "No RFAPI instance\n"); + return CMD_WARNING; + } + + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + + struct agg_node *rn; + int printedquerier = 0; + + if (!rfd->mon && + !(rfd->mon_eth && skiplist_count(rfd->mon_eth))) + continue; + + /* + * IP Queries + */ + if (rfd->mon) { + for (rn = agg_route_top(rfd->mon); rn; + rn = agg_route_next(rn)) { + const struct prefix *p = + agg_node_get_prefix(rn); + struct rfapi_monitor_vpn *m; + char buf_remain[BUFSIZ]; + char buf_pfx[BUFSIZ]; + + if (!rn->info) + continue; + + m = rn->info; + + ++queries_total; + + if (pfx_match && !prefix_match(pfx_match, p) + && !prefix_match(p, pfx_match)) + continue; + + ++queries_displayed; + + if (!printedheader) { + ++printedheader; + fp(out, "\n"); + fp(out, "%-15s %-15s %-15s %-10s\n", + "VN Address", "UN Address", "Target", + "Remaining"); + } + + if (!printedquerier) { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str(&rfd->un_addr, + buf_un, BUFSIZ); + rfapiRfapiIpAddr2Str(&rfd->vn_addr, + buf_vn, BUFSIZ); + + fp(out, "%-15s %-15s", buf_vn, buf_un); + printedquerier = 1; + } else + fp(out, "%-15s %-15s", "", ""); + buf_remain[0] = 0; + rfapiFormatSeconds( + event_timer_remain_second(m->timer), + buf_remain, BUFSIZ); + fp(out, " %-15s %-10s\n", + inet_ntop(m->p.family, &m->p.u.prefix, + buf_pfx, sizeof(buf_pfx)), + buf_remain); + } + } + + /* + * Ethernet Queries + */ + if (rfd->mon_eth && skiplist_count(rfd->mon_eth)) { + + int rc; + void *cursor; + struct rfapi_monitor_eth *mon_eth; + + for (cursor = NULL, + rc = skiplist_next(rfd->mon_eth, NULL, + (void **)&mon_eth, &cursor); + rc == 0; + rc = skiplist_next(rfd->mon_eth, NULL, + (void **)&mon_eth, &cursor)) { + + char buf_remain[BUFSIZ]; + char buf_pfx[BUFSIZ]; + struct prefix pfx_mac; + + ++queries_total; + + vnc_zlog_debug_verbose( + "%s: checking rfd=%p mon_eth=%p", + __func__, rfd, mon_eth); + + memset((void *)&pfx_mac, 0, + sizeof(struct prefix)); + pfx_mac.family = AF_ETHERNET; + pfx_mac.prefixlen = 48; + pfx_mac.u.prefix_eth = mon_eth->macaddr; + + if (pfx_match + && !prefix_match(pfx_match, &pfx_mac) + && !prefix_match(&pfx_mac, pfx_match)) + continue; + + ++queries_displayed; + + if (!printedheader) { + ++printedheader; + fp(out, "\n"); + fp(out, + "%-15s %-15s %-17s %10s %-10s\n", + "VN Address", "UN Address", "Target", + "LNI", "Remaining"); + } + + if (!printedquerier) { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str(&rfd->un_addr, + buf_un, BUFSIZ); + rfapiRfapiIpAddr2Str(&rfd->vn_addr, + buf_vn, BUFSIZ); + + fp(out, "%-15s %-15s", buf_vn, buf_un); + printedquerier = 1; + } else + fp(out, "%-15s %-15s", "", ""); + buf_remain[0] = 0; + rfapiFormatSeconds(event_timer_remain_second( + mon_eth->timer), + buf_remain, BUFSIZ); + fp(out, " %-17s %10d %-10s\n", + rfapi_ntop(pfx_mac.family, &pfx_mac.u.prefix, + buf_pfx, BUFSIZ), + mon_eth->logical_net_id, buf_remain); + } + } + } + + if (queries_total) { + fp(out, "\n"); + fp(out, "Displayed %d out of %d total queries\n", + queries_displayed, queries_total); + } + return CMD_SUCCESS; +} + +static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream, + struct agg_node *rn, struct bgp_path_info *bpi) +{ + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + struct prefix pfx_un; + struct prefix pfx_vn; + uint8_t cost; + uint32_t lifetime; + bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/ + + char buf_pfx[BUFSIZ]; + char buf_ntop[BUFSIZ]; + char buf_un[BUFSIZ]; + char buf_vn[BUFSIZ]; + char buf_lifetime[BUFSIZ]; + int nlines = 0; + const struct prefix *p = agg_node_get_prefix(rn); + + if (!stream) + return 0; /* for debug log, print into buf & call output once */ + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return 0; + + /* + * Prefix + */ + buf_pfx[0] = 0; + snprintf( + buf_pfx, sizeof(buf_pfx), "%s/%d", + rfapi_ntop(p->family, &p->u.prefix, buf_ntop, sizeof(buf_ntop)), + p->prefixlen); + buf_pfx[BUFSIZ - 1] = 0; + nlines++; + + /* + * UN addr + */ + buf_un[0] = 0; + if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) { + snprintf(buf_un, sizeof(buf_un), "%s", + inet_ntop(pfx_un.family, &pfx_un.u.prefix, buf_ntop, + sizeof(buf_ntop))); + } + + bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type); + /* + * VN addr + */ + buf_vn[0] = 0; + rfapiNexthop2Prefix(bpi->attr, &pfx_vn); + if (tun_type == BGP_ENCAP_TYPE_MPLS) { + /* MPLS carries un in nrli next hop (same as vn for IP tunnels) + */ + snprintf(buf_un, sizeof(buf_un), "%s", + inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, + sizeof(buf_ntop))); + if (bpi->extra) { + uint32_t l = decode_label(&bpi->extra->label[0]); + snprintf(buf_vn, sizeof(buf_vn), "Label: %d", l); + } else /* should never happen */ + { + snprintf(buf_vn, sizeof(buf_vn), "Label: N/A"); + } + } else { + snprintf(buf_vn, sizeof(buf_vn), "%s", + inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop, + sizeof(buf_ntop))); + } + buf_vn[BUFSIZ - 1] = 0; + buf_un[BUFSIZ - 1] = 0; + + /* + * Cost is encoded in local_pref as (255-cost) + * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion + * back to cost. + */ + uint32_t local_pref; + + if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) + local_pref = bpi->attr->local_pref; + else + local_pref = 0; + cost = (local_pref > 255) ? 0 : 255 - local_pref; + + fp(out, "%-20s ", buf_pfx); + fp(out, "%-15s ", buf_vn); + fp(out, "%-15s ", buf_un); + fp(out, "%-4d ", cost); + + /* Lifetime */ + /* NB rfapiGetVncLifetime sets infinite value when returning !0 */ + if (rfapiGetVncLifetime(bpi->attr, &lifetime) + || (lifetime == RFAPI_INFINITE_LIFETIME)) { + + fp(out, "%-10s ", "infinite"); + } else { + time_t t_lifetime = lifetime; + rfapiFormatSeconds(t_lifetime, buf_lifetime, BUFSIZ); + fp(out, "%-10s ", buf_lifetime); + } + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra + && bpi->extra->vnc.import.timer) { + + uint32_t remaining; + time_t age; + char buf_age[BUFSIZ]; + + struct event *t = (struct event *)bpi->extra->vnc.import.timer; + remaining = event_timer_remain_second(t); + +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE + /* + * Calculate when the timer started. Doing so here saves + * us a timestamp field in "struct bgp_path_info". + * + * See rfapi_import.c'rfapiBiStartWithdrawTimer() for the + * original calculation. + */ + age = rfapiGetHolddownFromLifetime(lifetime, factor) + - remaining; +#else /* report remaining time */ + age = remaining; +#endif + rfapiFormatSeconds(age, buf_age, BUFSIZ); + + fp(out, "%-10s ", buf_age); + + } else if (RFAPI_LOCAL_BI(bpi)) { + + char buf_age[BUFSIZ]; + + if (bpi->extra && bpi->extra->vnc.import.create_time) { + rfapiFormatAge(bpi->extra->vnc.import.create_time, + buf_age, BUFSIZ); + } else { + buf_age[0] = '?'; + buf_age[1] = 0; + } + fp(out, "%-10s ", buf_age); + } + fp(out, "%s", HVTYNL); + + if (p->family == AF_ETHERNET) { + /* + * If there is a corresponding IP address && != VN address, + * print that on the next line + */ + + if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) { + const char *sp; + + sp = rfapi_ntop( + bpi->extra->vnc.import.aux_prefix.family, + &bpi->extra->vnc.import.aux_prefix.u.prefix, + buf_ntop, BUFSIZ); + buf_ntop[BUFSIZ - 1] = 0; + + if (sp && strcmp(buf_vn, sp) != 0) { + fp(out, " IP: %s", sp); + if (nlines == 1) + nlines++; + } + } + } + if (tun_type != BGP_ENCAP_TYPE_MPLS && bpi->extra) { + uint32_t l = decode_label(&bpi->extra->label[0]); + + if (!MPLS_LABEL_IS_NULL(l)) { + fp(out, " Label: %d", l); + if (nlines == 1) + nlines++; + } + } + if (nlines > 1) + fp(out, "%s", HVTYNL); + + return 1; +} + +static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream, + struct rfapi_import_table *it, + struct prefix *prefix_only, + int show_expiring, /* either/or */ + int show_local, int show_remote, + int show_imported, /* either/or */ + uint32_t *pLni) /* AFI_L2VPN only */ +{ + afi_t afi; + int printed_rtlist_hdr = 0; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + int total = 0; + int printed = 0; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return printed; + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + + struct agg_node *rn; + + if (!it->imported_vpn[afi]) + continue; + + for (rn = agg_route_top(it->imported_vpn[afi]); rn; + rn = agg_route_next(rn)) { + const struct prefix *p = agg_node_get_prefix(rn); + struct bgp_path_info *bpi; + int count_only; + + /* allow for wider or more narrow mask from user */ + if (prefix_only && !prefix_match(prefix_only, p) + && !prefix_match(p, prefix_only)) + count_only = 1; + else + count_only = 0; + + for (bpi = rn->info; bpi; bpi = bpi->next) { + + if (!show_local && RFAPI_LOCAL_BI(bpi)) { + + /* local route from RFP */ + continue; + } + + if (!show_remote && !RFAPI_LOCAL_BI(bpi)) { + + /* remote route */ + continue; + } + + if (show_expiring + && !CHECK_FLAG(bpi->flags, + BGP_PATH_REMOVED)) + continue; + + if (!show_expiring + && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT + || bpi->type + == ZEBRA_ROUTE_BGP_DIRECT_EXT) { + if (!show_imported) + continue; + } else { + if (show_imported) + continue; + } + + total++; + if (count_only == 1) + continue; + if (!printed_rtlist_hdr) { + const char *agetype = ""; + char *s; + const char *type = ""; + if (show_imported) { + type = "Imported"; + } else { + if (show_expiring) { + type = "Holddown"; + } else { + if (RFAPI_LOCAL_BI( + bpi)) { + type = "Local"; + } else { + type = "Remote"; + } + } + } + + s = ecommunity_ecom2str( + it->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + + if (pLni) { + fp(out, + "%s[%s] L2VPN Network 0x%x (%u) RT={%s}", + HVTYNL, type, *pLni, + (*pLni & 0xfff), s); + } else { + fp(out, "%s[%s] Prefix RT={%s}", + HVTYNL, type, s); + } + XFREE(MTYPE_ECOMMUNITY_STR, s); + + if (it->rfg && it->rfg->name) { + fp(out, " %s \"%s\"", + (it->rfg->type == RFAPI_GROUP_CFG_VRF + ? "VRF" + : "NVE group"), + it->rfg->name); + } + fp(out, "%s", HVTYNL); + if (show_expiring) { +#ifdef RFAPI_REGISTRATIONS_REPORT_AGE + agetype = "Age"; +#else + agetype = "Remaining"; +#endif + } else if (show_local) { + agetype = "Age"; + } + + printed_rtlist_hdr = 1; + + fp(out, + "%-20s %-15s %-15s %4s %-10s %-10s%s", + (pLni ? "L2 Address/IP" : "Prefix"), + "VN Address", "UN Address", "Cost", + "Lifetime", agetype, HVTYNL); + } + printed += rfapiPrintRemoteRegBi(bgp, stream, + rn, bpi); + } + } + } + + if (printed > 0) { + + const char *type = "prefixes"; + + if (show_imported) { + type = "imported prefixes"; + } else { + if (show_expiring) { + type = "prefixes in holddown"; + } else { + if (show_local && !show_remote) { + type = "locally registered prefixes"; + } else if (!show_local && show_remote) { + type = "remotely registered prefixes"; + } + } + } + + fp(out, "Displayed %d out of %d %s%s", printed, total, type, + HVTYNL); +#if DEBUG_SHOW_EXTRA + fp(out, "IT table above: it=%p%s", it, HVTYNL); +#endif + } + return printed; +} + + +/* + * rfapiShowRemoteRegistrations + * + * Similar to rfapiShowImportTable() above. This function + * is mean to produce the "remote" portion of the output + * of "show vnc registrations". + */ +int rfapiShowRemoteRegistrations(void *stream, struct prefix *prefix_only, + int show_expiring, int show_local, + int show_remote, int show_imported) +{ + struct bgp *bgp; + struct rfapi *h; + struct rfapi_import_table *it; + int printed = 0; + + bgp = bgp_get_default(); + if (!bgp) { + return printed; + } + + h = bgp->rfapi; + if (!h) { + return printed; + } + + for (it = h->imports; it; it = it->next) { + printed += rfapiShowRemoteRegistrationsIt( + bgp, stream, it, prefix_only, show_expiring, show_local, + show_remote, show_imported, NULL); + } + + if (h->import_mac) { + void *cursor = NULL; + int rc; + uintptr_t lni_as_ptr; + uint32_t lni; + uint32_t *pLni; + + for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr, + (void **)&it, &cursor); + !rc; + rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr, + (void **)&it, &cursor)) { + pLni = NULL; + if ((lni_as_ptr & 0xffffffff) == lni_as_ptr) { + lni = (uint32_t)(lni_as_ptr & 0xffffffff); + pLni = &lni; + } + + printed += rfapiShowRemoteRegistrationsIt( + bgp, stream, it, prefix_only, show_expiring, + show_local, show_remote, show_imported, pLni); + } + } + + return printed; +} + +/*------------------------------------------ + * rfapiRfapiIpAddr2Str + * + * UI helper: generate string from rfapi_ip_addr + * + * input: + * a IP v4/v6 address + * + * output + * buf put string here + * bufsize max space to write + * + * return value: + * NULL conversion failed + * non-NULL pointer to buf + --------------------------------------------*/ +const char *rfapiRfapiIpAddr2Str(struct rfapi_ip_addr *a, char *buf, + int bufsize) +{ + const char *rc = NULL; + + switch (a->addr_family) { + case AF_INET: + rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize); + break; + case AF_INET6: + rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize); + break; + } + return rc; +} + +void rfapiPrintRfapiIpAddr(void *stream, struct rfapi_ip_addr *a) +{ + char buf[BUFSIZ]; + const char *rc = NULL; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rc = rfapiRfapiIpAddr2Str(a, buf, BUFSIZ); + + if (rc) + fp(out, "%s", buf); +} + +const char *rfapiRfapiIpPrefix2Str(struct rfapi_ip_prefix *p, char *buf, + int bufsize) +{ + struct rfapi_ip_addr *a = &p->prefix; + const char *rc = NULL; + + switch (a->addr_family) { + case AF_INET: + rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize); + break; + case AF_INET6: + rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize); + break; + } + + if (rc) { + int alen = strlen(buf); + int remaining = bufsize - alen - 1; + int slen; + + if (remaining > 0) { + slen = snprintf(buf + alen, remaining, "/%u", + p->length); + if (slen < remaining) /* see man page for snprintf(3) */ + return rc; + } + } + + return NULL; +} + +void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p) +{ + char buf[BUFSIZ]; + const char *rc; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out = NULL; + const char *vty_newline; + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + rc = rfapiRfapiIpPrefix2Str(p, buf, BUFSIZ); + + if (rc) + fp(out, "%s:%u", buf, p->cost); + else + fp(out, "?/?:?"); +} + +void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd, + safi_t safi, struct prefix *p) +{ + afi_t afi; /* of the VN address */ + struct bgp_dest *bd; + struct bgp_path_info *bpi; + uint8_t type = ZEBRA_ROUTE_BGP; + struct bgp *bgp; + int printed = 0; + struct prefix_rd prd0; + struct prefix_rd *prd; + + /* + * Find the bgp_path in the RIB corresponding to this + * prefix and rfd + */ + + afi = family2afi(p->family); + assert(afi == AFI_IP || afi == AFI_IP6); + + bgp = bgp_get_default(); /* assume 1 instance for now */ + assert(bgp); + + if (safi == SAFI_ENCAP) { + memset(&prd0, 0, sizeof(prd0)); + prd0.family = AF_UNSPEC; + prd0.prefixlen = 64; + prd = &prd0; + } else { + prd = &rfd->rd; + } + bd = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd); + + vty_out(vty, " bd=%p%s", bd, HVTYNL); + + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { + if (bpi->peer == rfd->peer && bpi->type == type + && bpi->sub_type == BGP_ROUTE_RFP && bpi->extra + && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) { + + rfapiPrintBi(vty, bpi); + printed = 1; + } + } + + if (!printed) { + vty_out(vty, " --?--%s", HVTYNL); + return; + } +} + +void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd) +{ + /* pHD un-addr vn-addr pCB cookie rd lifetime */ + /* RT export list */ + /* RT import list */ + /* list of advertised prefixes */ + /* dump import table */ + + char *s; + void *cursor; + int rc; + afi_t afi; + struct rfapi_adb *adb; + + vty_out(vty, "%-10p ", rfd); + rfapiPrintRfapiIpAddr(vty, &rfd->un_addr); + vty_out(vty, " "); + rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr); + vty_out(vty, " %p %p ", rfd->response_cb, rfd->cookie); + vty_out(vty, "%pRDP", &rfd->rd); + vty_out(vty, " %d", rfd->response_lifetime); + vty_out(vty, " %s", (rfd->rfg ? rfd->rfg->name : "<orphaned>")); + vty_out(vty, "%s", HVTYNL); + + vty_out(vty, " Peer %p #%d%s", rfd->peer, rfd->peer->lock, HVTYNL); + + /* export RT list */ + if (rfd->rt_export_list) { + s = ecommunity_ecom2str(rfd->rt_export_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " Export %s%s", s, HVTYNL); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } else { + vty_out(vty, " Export (nil)%s", HVTYNL); + } + + /* import RT list */ + if (rfd->import_table) { + s = ecommunity_ecom2str(rfd->import_table->rt_import_list, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + vty_out(vty, " Import %s%s", s, HVTYNL); + XFREE(MTYPE_ECOMMUNITY_STR, s); + } else { + vty_out(vty, " Import (nil)%s", HVTYNL); + } + + for (afi = AFI_IP; afi < AFI_MAX; ++afi) { + uint8_t family; + + family = afi2family(afi); + if (!family) + continue; + + cursor = NULL; + for (rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL, + (void **)&adb, &cursor); + rc == 0; + rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL, + (void **)&adb, &cursor)) { + + /* group like family prefixes together in output */ + if (family != adb->u.s.prefix_ip.family) + continue; + + vty_out(vty, " Adv Pfx: %pFX%s", &adb->u.s.prefix_ip, + HVTYNL); + rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN, + &adb->u.s.prefix_ip); + } + } + for (rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL, + (void **)&adb, &cursor); + rc == 0; rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL, + (void **)&adb, &cursor)) { + vty_out(vty, " Adv Pfx: %pFX%s", &adb->u.s.prefix_eth, HVTYNL); + + /* TBD update the following function to print ethernet info */ + /* Also need to pass/use rd */ + rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN, + &adb->u.s.prefix_ip); + } + vty_out(vty, "%s", HVTYNL); +} + +/* + * test scripts rely on first line for each nve starting in 1st column, + * leading whitespace for additional detail of that nve + */ +void rfapiPrintMatchingDescriptors(struct vty *vty, struct prefix *vn_prefix, + struct prefix *un_prefix) +{ + struct bgp *bgp; + struct rfapi *h; + struct listnode *ln; + struct rfapi_descriptor *rfd; + int printed = 0; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + if (!bgp) + return; + + h = bgp->rfapi; + assert(h); + + for (ln = listhead(&h->descriptors); ln; ln = listnextnode(ln)) { + rfd = listgetdata(ln); + + struct prefix pfx; + + if (vn_prefix) { + assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx)); + if (!prefix_match(vn_prefix, &pfx)) + continue; + } + + if (un_prefix) { + assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx)); + if (!prefix_match(un_prefix, &pfx)) + continue; + } + + if (!printed) { + /* print column header */ + vty_out(vty, "%s %s %s %s %s %s %s %s%s", "descriptor", + "un-addr", "vn-addr", "callback", "cookie", + "RD", "lifetime", "group", HVTYNL); + } + rfapiPrintDescriptor(vty, rfd); + printed = 1; + } +} + + +/* + * Parse an address and put into a struct prefix + */ +int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, struct prefix *p) +{ + if (!str2prefix(str, p)) { + vty_out(vty, "Malformed address \"%s\"%s", str ? str : "null", + HVTYNL); + return CMD_WARNING; + } + switch (p->family) { + case AF_INET: + if (p->prefixlen != IPV4_MAX_BITLEN) { + vty_out(vty, "Not a host address: \"%s\"%s", str, + HVTYNL); + return CMD_WARNING; + } + break; + case AF_INET6: + if (p->prefixlen != IPV6_MAX_BITLEN) { + vty_out(vty, "Not a host address: \"%s\"%s", str, + HVTYNL); + return CMD_WARNING; + } + break; + default: + vty_out(vty, "Invalid address \"%s\"%s", str, HVTYNL); + return CMD_WARNING; + } + return 0; +} + +int rfapiCliGetRfapiIpAddr(struct vty *vty, const char *str, + struct rfapi_ip_addr *hai) +{ + struct prefix pfx; + int rc; + + rc = rfapiCliGetPrefixAddr(vty, str, &pfx); + if (rc) + return rc; + + hai->addr_family = pfx.family; + if (pfx.family == AF_INET) + hai->addr.v4 = pfx.u.prefix4; + else + hai->addr.v6 = pfx.u.prefix6; + + return 0; +} + +/* + * Note: this function does not flush vty output, so if it is called + * with a stream pointing to a vty, the user will have to type something + * before the callback output shows up + */ +void rfapiPrintNhl(void *stream, struct rfapi_next_hop_entry *next_hops) +{ + struct rfapi_next_hop_entry *nh; + int count; + + int (*fp)(void *, const char *, ...); + struct vty *vty; + void *out; + const char *vty_newline; + +#define REMAIN (BUFSIZ - (p-line)) +#define INCP {p += (r > REMAIN)? REMAIN: r;} + + if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0) + return; + + for (nh = next_hops, count = 1; nh; nh = nh->next, ++count) { + + char line[BUFSIZ]; + char *p = line; + int r; + + r = snprintf(p, REMAIN, "%3d pfx=", count); + INCP; + + if (rfapiRfapiIpPrefix2Str(&nh->prefix, p, REMAIN)) { + /* it fit, so count length */ + r = strlen(p); + } else { + /* didn't fit */ + goto truncate; + } + INCP; + + r = snprintf(p, REMAIN, ", un="); + INCP; + + if (rfapiRfapiIpAddr2Str(&nh->un_address, p, REMAIN)) { + /* it fit, so count length */ + r = strlen(p); + } else { + /* didn't fit */ + goto truncate; + } + INCP; + + r = snprintf(p, REMAIN, ", vn="); + INCP; + + if (rfapiRfapiIpAddr2Str(&nh->vn_address, p, REMAIN)) { + /* it fit, so count length */ + r = strlen(p); + } else { + /* didn't fit */ + goto truncate; + } + INCP; + + truncate: + line[BUFSIZ - 1] = 0; + fp(out, "%s%s", line, HVTYNL); + + /* + * options + */ + if (nh->vn_options) { + struct rfapi_vn_option *vo; + char offset[] = " "; + + for (vo = nh->vn_options; vo; vo = vo->next) { + char pbuf[100]; + + switch (vo->type) { + case RFAPI_VN_OPTION_TYPE_L2ADDR: + rfapiEthAddr2Str(&vo->v.l2addr.macaddr, + pbuf, sizeof(pbuf)); + fp(out, + "%sL2 %s LBL=0x%06x NETID=0x%06x NVEID=%d%s", + offset, pbuf, + (vo->v.l2addr.label & 0x00ffffff), + (vo->v.l2addr.logical_net_id + & 0x00ffffff), + vo->v.l2addr.local_nve_id, HVTYNL); + break; + + case RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP: + fp(out, "%sLNH %pFX cost=%d%s", offset, + &vo->v.local_nexthop.addr, + vo->v.local_nexthop.cost, HVTYNL); + break; + + case RFAPI_VN_OPTION_TYPE_INTERNAL_RD: + fp(out, + "%svn option type %d (unknown)%s", + offset, vo->type, HVTYNL); + break; + } + } + } + if (nh->un_options) { + struct rfapi_un_option *uo; + char offset[] = " "; + + for (uo = nh->un_options; uo; uo = uo->next) { + switch (uo->type) { + case RFAPI_UN_OPTION_TYPE_TUNNELTYPE: + rfapi_print_tunneltype_option( + stream, 8, &uo->v.tunnel); + break; + case RFAPI_UN_OPTION_TYPE_PROVISIONAL: + fp(out, "%sUN Option type %d%s", offset, + uo->type, vty_newline); + break; + } + } + } + } +} + +/*********************************************************************** + * STATIC ROUTES + ***********************************************************************/ + +/* + * Add another nexthop to the NHL + */ +static void rfapiAddDeleteLocalRfpPrefix(struct rfapi_ip_addr *un_addr, + struct rfapi_ip_addr *vn_addr, + struct rfapi_ip_prefix *rprefix, + int is_add, + uint32_t lifetime, /* add only */ + struct rfapi_vn_option *vn_options, + struct rfapi_next_hop_entry **head, + struct rfapi_next_hop_entry **tail) +{ + struct rfapi_next_hop_entry *new; + + /* + * construct NHL + */ + + new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry)); + new->prefix = *rprefix; + new->un_address = *un_addr; + new->vn_address = *vn_addr; + + new->vn_options = vn_options; + if (is_add) { + new->lifetime = lifetime; + } else { + new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME; + } + + if (*tail) + (*tail)->next = new; + *tail = new; + if (!*head) { + *head = new; + } +} + + +static int +register_add(struct vty *vty, struct cmd_token *carg_prefix, + struct cmd_token *carg_vn, struct cmd_token *carg_un, + struct cmd_token *carg_cost, /* optional */ + struct cmd_token *carg_lifetime, /* optional */ + struct cmd_token *carg_macaddr, /* optional */ + struct cmd_token + *carg_vni, /* mac present=>mandatory Virtual Network ID */ + int argc, struct cmd_token **argv) +{ + const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL; + const char *arg_vn = carg_vn ? carg_vn->arg : NULL; + const char *arg_un = carg_un ? carg_un->arg : NULL; + const char *arg_cost = carg_cost ? carg_cost->arg : NULL; + const char *arg_lifetime = carg_lifetime ? carg_lifetime->arg : NULL; + const char *arg_macaddr = carg_macaddr ? carg_macaddr->arg : NULL; + const char *arg_vni = carg_vni ? carg_vni->arg : NULL; + struct rfapi_ip_addr vn_address; + struct rfapi_ip_addr un_address; + struct prefix pfx; + struct rfapi_ip_prefix rpfx; + uint32_t cost; + uint32_t lnh_cost; + uint32_t lifetime; + rfapi_handle rfd; + struct rfapi_vn_option optary[10]; /* XXX must be big enough */ + struct rfapi_vn_option *opt = NULL; + int opt_next = 0; + + int rc = CMD_WARNING_CONFIG_FAILED; + char *endptr; + struct bgp *bgp; + struct rfapi *h; + struct rfapi_cfg *rfapi_cfg; + + const char *arg_lnh = NULL; + const char *arg_lnh_cost = NULL; + + bgp = bgp_get_default(); /* assume 1 instance for now */ + if (!bgp) { + if (vty) + vty_out(vty, "BGP not configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + h = bgp->rfapi; + rfapi_cfg = bgp->rfapi_cfg; + if (!h || !rfapi_cfg) { + if (vty) + vty_out(vty, "RFAPI not configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + for (; argc; --argc, ++argv) { + if (strmatch(argv[0]->text, "local-next-hop")) { + if (arg_lnh) { + vty_out(vty, + "local-next-hop specified more than once\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (argc <= 1) { + vty_out(vty, + "Missing parameter for local-next-hop\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ++argv; + --argc; + arg_lnh = argv[0]->arg; + } + if (strmatch(argv[0]->text, "local-cost")) { + if (arg_lnh_cost) { + vty_out(vty, + "local-cost specified more than once\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (argc <= 1) { + vty_out(vty, + "Missing parameter for local-cost\n"); + return CMD_WARNING_CONFIG_FAILED; + } + ++argv; + --argc; + arg_lnh_cost = argv[0]->arg; + } + } + + if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn, &vn_address))) + goto fail; + if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un, &un_address))) + goto fail; + + /* arg_prefix is optional if mac address is given */ + if (arg_macaddr && !arg_prefix) { + /* + * fake up a 0/32 or 0/128 prefix + */ + switch (vn_address.addr_family) { + case AF_INET: + arg_prefix = "0.0.0.0/32"; + break; + case AF_INET6: + arg_prefix = "0::0/128"; + break; + default: + vty_out(vty, + "Internal error, unknown VN address family\n"); + return CMD_WARNING_CONFIG_FAILED; + } + } + + if (!str2prefix(arg_prefix, &pfx)) { + vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix); + goto fail; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "prefix \"%s\" has invalid address family\n", + arg_prefix); + goto fail; + } + + + memset(optary, 0, sizeof(optary)); + + if (arg_cost) { + endptr = NULL; + cost = strtoul(arg_cost, &endptr, 10); + if (*endptr != '\0' || cost > 255) { + vty_out(vty, "%% Invalid %s value\n", "cost"); + goto fail; + } + } else { + cost = 255; + } + + if (arg_lifetime) { + if (!strcmp(arg_lifetime, "infinite")) { + lifetime = RFAPI_INFINITE_LIFETIME; + } else { + endptr = NULL; + lifetime = strtoul(arg_lifetime, &endptr, 10); + if (*endptr != '\0') { + vty_out(vty, "%% Invalid %s value\n", + "lifetime"); + goto fail; + } + } + } else { + lifetime = RFAPI_INFINITE_LIFETIME; /* default infinite */ + } + + if (arg_lnh_cost) { + if (!arg_lnh) { + vty_out(vty, + "%% %s may only be specified with local-next-hop\n", + "local-cost"); + goto fail; + } + endptr = NULL; + lnh_cost = strtoul(arg_lnh_cost, &endptr, 10); + if (*endptr != '\0' || lnh_cost > 255) { + vty_out(vty, "%% Invalid %s value\n", "local-cost"); + goto fail; + } + } else { + lnh_cost = 255; + } + + if (arg_lnh) { + if (!arg_prefix) { + vty_out(vty, + "%% %s may only be specified with prefix\n", + "local-next-hop"); + goto fail; + } + if ((rc = rfapiCliGetPrefixAddr( + vty, arg_lnh, + &optary[opt_next].v.local_nexthop.addr))) { + + goto fail; + } + + optary[opt_next].v.local_nexthop.cost = lnh_cost; + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP; + + if (opt_next) { + optary[opt_next - 1].next = optary + opt_next; + } else { + opt = optary; + } + ++opt_next; + } + + if (arg_vni && !arg_macaddr) { + vty_out(vty, "%% %s may only be specified with mac address\n", + "virtual-network-identifier"); + goto fail; + } + + if (arg_macaddr) { + if (!arg_vni) { + vty_out(vty, + "Missing \"vni\" parameter (mandatory with mac)\n"); + return CMD_WARNING_CONFIG_FAILED; + } + optary[opt_next].v.l2addr.logical_net_id = + strtoul(arg_vni, NULL, 10); + + if ((rc = rfapiStr2EthAddr( + arg_macaddr, + &optary[opt_next].v.l2addr.macaddr))) { + vty_out(vty, "Invalid %s value\n", "mac address"); + goto fail; + } + /* TBD label, NVE ID */ + + optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR; + + if (opt_next) { + optary[opt_next - 1].next = optary + opt_next; + } else { + opt = optary; + } + ++opt_next; + } + + vnc_zlog_debug_verbose( + "%s: vn=%s, un=%s, prefix=%s, cost=%s, lifetime=%s, lnh=%s", + __func__, arg_vn, arg_un, arg_prefix, + (arg_cost ? arg_cost : "NULL"), + (arg_lifetime ? arg_lifetime : "NULL"), + (arg_lnh ? arg_lnh : "NULL")); + + rfapiQprefix2Rprefix(&pfx, &rpfx); + + rpfx.cost = cost & 255; + + /* look up rf descriptor, call open if it doesn't exist */ + rc = rfapi_find_rfd(bgp, &vn_address, &un_address, + (struct rfapi_descriptor **)&rfd); + if (rc) { + if (ENOENT == rc) { + struct rfapi_un_option uo; + + /* + * flag descriptor as provisionally opened for static + * route + * registration so that we can fix up the other + * parameters + * when the real open comes along + */ + memset(&uo, 0, sizeof(uo)); + uo.type = RFAPI_UN_OPTION_TYPE_PROVISIONAL; + + rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp), + &vn_address, &un_address, + &uo, /* flags */ + NULL, NULL, /* no userdata */ + &rfd); + if (rc) { + vty_out(vty, + "Can't open session for this NVE: %s\n", + rfapi_error_str(rc)); + rc = CMD_WARNING_CONFIG_FAILED; + goto fail; + } + } else { + vty_out(vty, "Can't find session for this NVE: %s\n", + rfapi_error_str(rc)); + goto fail; + } + } + + rc = rfapi_register(rfd, &rpfx, lifetime, NULL, opt, + RFAPI_REGISTER_ADD); + if (!rc) { + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct rfapi_vn_option *vn_opt_new; + + vnc_zlog_debug_verbose( + "%s: rfapi_register succeeded, returning 0", __func__); + + if (h->rfp_methods.local_cb) { + struct rfapi_descriptor *r = + (struct rfapi_descriptor *)rfd; + vn_opt_new = rfapi_vn_options_dup(opt); + + rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr, + &rpfx, 1, lifetime, + vn_opt_new, &head, &tail); + if (head) { + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.local_cb)(head, r->cookie); + h->flags &= ~RFAPI_INCALLBACK; + } + head = tail = NULL; + } + return 0; + } + + vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__); + vty_out(vty, "\n"); + vty_out(vty, "Registration failed.\n"); + vty_out(vty, + "Confirm that either the VN or UN address matches a configured NVE group.\n"); + return CMD_WARNING_CONFIG_FAILED; + +fail: + vnc_zlog_debug_verbose("%s: fail, rc=%d", __func__, rc); + return rc; +} + +/************************************************************************ + * Add prefix With LNH_OPTIONS... + ************************************************************************/ +DEFUN (add_vnc_prefix_cost_life_lnh, + add_vnc_prefix_cost_life_lnh_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295) LNH_OPTIONS...", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11], + /* mac vni */ + NULL, NULL, argc - 12, argv + 12); +} + +DEFUN (add_vnc_prefix_life_cost_lnh, + add_vnc_prefix_life_cost_lnh_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255) LNH_OPTIONS...", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9], + /* mac vni */ + NULL, NULL, argc - 12, argv + 12); +} + +DEFUN (add_vnc_prefix_cost_lnh, + add_vnc_prefix_cost_lnh_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) LNH_OPTIONS...", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL, + /* mac vni */ + NULL, NULL, argc - 10, argv + 10); +} + +DEFUN (add_vnc_prefix_life_lnh, + add_vnc_prefix_life_lnh_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) LNH_OPTIONS...", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9], + /* mac vni */ + NULL, NULL, argc - 10, argv + 10); +} + +DEFUN (add_vnc_prefix_lnh, + add_vnc_prefix_lnh_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> LNH_OPTIONS...", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL, + /* mac vni */ + NULL, NULL, argc - 8, argv + 8); +} + +/************************************************************************ + * Add prefix Without LNH_OPTIONS... + ************************************************************************/ +DEFUN (add_vnc_prefix_cost_life, + add_vnc_prefix_cost_life_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_life_cost, + add_vnc_prefix_life_cost_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255)", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_cost, + add_vnc_prefix_cost_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL, + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix_life, + add_vnc_prefix_life_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9], + /* mac vni */ + NULL, NULL, 0, NULL); +} + +DEFUN (add_vnc_prefix, + add_vnc_prefix_cmd, + "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>", + "Add registration\n" + "VNC Information\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL, + /* mac vni */ + NULL, NULL, 0, NULL); +} + +/************************************************************************ + * Mac address registrations + ************************************************************************/ +DEFUN (add_vnc_mac_vni_prefix_cost_life, + add_vnc_mac_vni_prefix_cost_life_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255) lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[11], argv[7], argv[9], argv[13], argv[15], + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_prefix_life, + add_vnc_mac_vni_prefix_life_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[11], argv[7], argv[9], NULL, argv[13], + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_prefix_cost, + add_vnc_mac_vni_prefix_cost_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Administrative cost [default: 255]\n" "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[11], argv[7], argv[9], argv[13], NULL, + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_prefix, + add_vnc_mac_vni_prefix_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" "IPv6 prefix\n") +{ + /* pfx vn un cost life */ + return register_add(vty, argv[11], argv[7], argv[9], NULL, NULL, + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + +DEFUN (add_vnc_mac_vni_cost_life, + add_vnc_mac_vni_cost_life_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" + "Administrative cost\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, NULL, argv[7], argv[9], argv[11], argv[13], + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_cost, + add_vnc_mac_vni_cost_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Administrative cost [default: 255]\n" "Administrative cost\n") +{ + /* pfx vn un cost life */ + return register_add(vty, NULL, argv[7], argv[9], argv[11], NULL, + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni_life, + add_vnc_mac_vni_life_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Registration lifetime [default: infinite]\n" + "Lifetime value in seconds\n") +{ + /* pfx vn un cost life */ + return register_add(vty, NULL, argv[7], argv[9], NULL, argv[11], + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + + +DEFUN (add_vnc_mac_vni, + add_vnc_mac_vni_cmd, + "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>", + "Add registration\n" + "VNC Information\n" + "Add/modify mac address information\n" + "MAC address\n" + "Virtual Network Identifier follows\n" + "Virtual Network Identifier\n" + "VN address of NVE\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "UN IPv4 interface address\n" "UN IPv6 interface address\n") +{ + /* pfx vn un cost life */ + return register_add(vty, NULL, argv[7], argv[9], NULL, NULL, + /* mac vni */ + argv[3], argv[5], 0, NULL); +} + +/************************************************************************ + * Delete prefix + ************************************************************************/ + +struct rfapi_local_reg_delete_arg { + /* + * match parameters + */ + struct bgp *bgp; + struct rfapi_ip_addr un_address; /* AF==0: wildcard */ + struct rfapi_ip_addr vn_address; /* AF==0: wildcard */ + struct prefix prefix; /* AF==0: wildcard */ + struct prefix_rd rd; /* plen!=64: wildcard */ + struct rfapi_nve_group_cfg *rfg; /* NULL: wildcard */ + + struct rfapi_l2address_option_match l2o; + + /* + * result parameters + */ + struct vty *vty; + uint32_t reg_count; + uint32_t pfx_count; + uint32_t query_count; + + uint32_t failed_pfx_count; + + uint32_t nve_count; + struct skiplist *nves; + + uint32_t remote_active_nve_count; + uint32_t remote_active_pfx_count; + uint32_t remote_holddown_nve_count; + uint32_t remote_holddown_pfx_count; +}; + +struct nve_addr { + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + struct rfapi_descriptor *rfd; + struct rfapi_local_reg_delete_arg *cda; +}; + +static void nve_addr_free(void *hap) +{ + ((struct nve_addr *)hap)->cda->nve_count += 1; + XFREE(MTYPE_RFAPI_NVE_ADDR, hap); +} + +static int nve_addr_cmp(const void *k1, const void *k2) +{ + const struct nve_addr *a = (struct nve_addr *)k1; + const struct nve_addr *b = (struct nve_addr *)k2; + int ret = 0; + + if (!a || !b) { + return (a - b); + } + if (a->un.addr_family != b->un.addr_family) { + return (a->un.addr_family - b->un.addr_family); + } + if (a->vn.addr_family != b->vn.addr_family) { + return (a->vn.addr_family - b->vn.addr_family); + } + if (a->un.addr_family == AF_INET) { + ret = IPV4_ADDR_CMP(&a->un.addr.v4, &b->un.addr.v4); + if (ret != 0) { + return ret; + } + } else if (a->un.addr_family == AF_INET6) { + ret = IPV6_ADDR_CMP(&a->un.addr.v6, &b->un.addr.v6); + if (ret != 0) { + return ret; + } + } else { + assert(0); + } + if (a->vn.addr_family == AF_INET) { + ret = IPV4_ADDR_CMP(&a->vn.addr.v4, &b->vn.addr.v4); + if (ret != 0) + return ret; + } else if (a->vn.addr_family == AF_INET6) { + ret = IPV6_ADDR_CMP(&a->vn.addr.v6, &b->vn.addr.v6); + if (ret == 0) { + return ret; + } + } else { + assert(0); + } + return 0; +} + +static int parse_deleter_args(struct vty *vty, struct bgp *bgp, + const char *arg_prefix, const char *arg_vn, + const char *arg_un, const char *arg_l2addr, + const char *arg_vni, const char *arg_rd, + struct rfapi_nve_group_cfg *arg_rfg, + struct rfapi_local_reg_delete_arg *rcdarg) +{ + int rc = CMD_WARNING; + + memset(rcdarg, 0, sizeof(struct rfapi_local_reg_delete_arg)); + + rcdarg->vty = vty; + if (bgp == NULL) + bgp = bgp_get_default(); + rcdarg->bgp = bgp; + rcdarg->rfg = arg_rfg; /* may be NULL */ + + if (arg_vn && strcmp(arg_vn, "*")) { + if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn, + &rcdarg->vn_address))) + return rc; + } + if (arg_un && strcmp(arg_un, "*")) { + if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un, + &rcdarg->un_address))) + return rc; + } + if (arg_prefix && strcmp(arg_prefix, "*")) { + + if (!str2prefix(arg_prefix, &rcdarg->prefix)) { + vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix); + return rc; + } + } + + if (arg_l2addr) { + if (!arg_vni) { + vty_out(vty, "Missing VNI\n"); + return rc; + } + if (strcmp(arg_l2addr, "*")) { + if ((rc = rfapiStr2EthAddr(arg_l2addr, + &rcdarg->l2o.o.macaddr))) { + vty_out(vty, "Malformed L2 Address \"%s\"\n", + arg_l2addr); + return rc; + } + rcdarg->l2o.flags |= RFAPI_L2O_MACADDR; + } + if (strcmp(arg_vni, "*")) { + rcdarg->l2o.o.logical_net_id = + strtoul(arg_vni, NULL, 10); + rcdarg->l2o.flags |= RFAPI_L2O_LNI; + } + } + if (arg_rd) { + if (!str2prefix_rd(arg_rd, &rcdarg->rd)) { + vty_out(vty, "Malformed RD \"%s\"\n", arg_rd); + return rc; + } + } + + return CMD_SUCCESS; +} + +static int +parse_deleter_tokens(struct vty *vty, struct bgp *bgp, + struct cmd_token *carg_prefix, struct cmd_token *carg_vn, + struct cmd_token *carg_un, struct cmd_token *carg_l2addr, + struct cmd_token *carg_vni, struct cmd_token *carg_rd, + struct rfapi_nve_group_cfg *arg_rfg, + struct rfapi_local_reg_delete_arg *rcdarg) +{ + const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL; + const char *arg_vn = carg_vn ? carg_vn->arg : NULL; + const char *arg_un = carg_un ? carg_un->arg : NULL; + const char *arg_l2addr = carg_l2addr ? carg_l2addr->arg : NULL; + const char *arg_vni = carg_vni ? carg_vni->arg : NULL; + const char *arg_rd = carg_rd ? carg_rd->arg : NULL; + return parse_deleter_args(vty, bgp, arg_prefix, arg_vn, arg_un, + arg_l2addr, arg_vni, arg_rd, arg_rfg, rcdarg); +} + +static void record_nve_in_cda_list(struct rfapi_local_reg_delete_arg *cda, + struct rfapi_ip_addr *un_address, + struct rfapi_ip_addr *vn_address, + struct rfapi_descriptor *rfd) +{ + struct nve_addr ha; + struct nve_addr *hap; + + memset(&ha, 0, sizeof(ha)); + ha.un = *un_address; + ha.vn = *vn_address; + ha.rfd = rfd; + + if (!cda->nves) + cda->nves = skiplist_new(0, nve_addr_cmp, nve_addr_free); + + if (skiplist_search(cda->nves, &ha, (void *)&hap)) { + hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR, sizeof(struct nve_addr)); + assert(hap); + ha.cda = cda; + *hap = ha; + skiplist_insert(cda->nves, hap, hap); + } +} + +static void clear_vnc_responses(struct rfapi_local_reg_delete_arg *cda) +{ + struct rfapi *h; + struct rfapi_descriptor *rfd; + int query_count = 0; + struct listnode *node; + struct bgp *bgp_default = bgp_get_default(); + + if (cda->vn_address.addr_family && cda->un_address.addr_family) { + /* + * Single nve case + */ + if (rfapi_find_rfd(bgp_default, &cda->vn_address, + &cda->un_address, &rfd)) + return; + + rfapiRibClear(rfd); + rfapi_query_done_all(rfd, &query_count); + cda->query_count += query_count; + + /* + * Track unique nves seen + */ + record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd); + return; + } + + /* + * wildcard case + */ + + if (!bgp_default) + return; /* ENXIO */ + + h = bgp_default->rfapi; + + if (!h) + return; /* ENXIO */ + + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + /* + * match un, vn addresses of NVEs + */ + if (cda->un_address.addr_family + && rfapi_ip_addr_cmp(&cda->un_address, &rfd->un_addr)) { + continue; + } + if (cda->vn_address.addr_family + && rfapi_ip_addr_cmp(&cda->vn_address, &rfd->vn_addr)) { + continue; + } + + rfapiRibClear(rfd); + + rfapi_query_done_all(rfd, &query_count); + cda->query_count += query_count; + + /* + * Track unique nves seen + */ + record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd); + } +} + +/* + * TBD need to count deleted prefixes and nves? + * + * ENXIO BGP or VNC not configured + */ +static int rfapiDeleteLocalPrefixesByRFD(struct rfapi_local_reg_delete_arg *cda, + struct rfapi_descriptor *rfd) +{ + struct rfapi_ip_addr *pUn; /* NULL = wildcard */ + struct rfapi_ip_addr *pVn; /* NULL = wildcard */ + struct prefix *pPrefix; /* NULL = wildcard */ + struct prefix_rd *pPrd; /* NULL = wildcard */ + + struct rfapi_ip_prefix rprefix; + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: entry", __func__); +#endif + + pUn = (cda->un_address.addr_family ? &cda->un_address : NULL); + pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL); + pPrefix = (cda->prefix.family ? &cda->prefix : NULL); + pPrd = (cda->rd.prefixlen == 64 ? &cda->rd : NULL); + + if (pPrefix) { + rfapiQprefix2Rprefix(pPrefix, &rprefix); + } + + do /* to preserve old code structure */ + { + struct rfapi *h = cda->bgp->rfapi; + ; + struct rfapi_adb *adb; + int rc; + int deleted_from_this_nve; + struct nve_addr ha; + struct nve_addr *hap; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd); +#endif + + /* + * match un, vn addresses of NVEs + */ + if (pUn && (rfapi_ip_addr_cmp(pUn, &rfd->un_addr))) + break; + if (pVn && (rfapi_ip_addr_cmp(pVn, &rfd->vn_addr))) + break; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose("%s: un, vn match", __func__); +#endif + + /* + * match prefix + */ + + deleted_from_this_nve = 0; + + { + struct skiplist *sl; + struct rfapi_ip_prefix rp; + void *cursor; + struct list *adb_delete_list; + + /* + * The advertisements are stored in a skiplist. + * Withdrawing + * the registration deletes the advertisement from the + * skiplist, which we can't do while iterating over that + * same skiplist using the current skiplist API. + * + * Strategy: iterate over the skiplist and build another + * list containing only the matching ADBs. Then delete + * _everything_ in that second list (which can be done + * using either skiplists or quagga linklists). + */ + adb_delete_list = list_new(); + + /* + * Advertised IP prefixes (not 0/32 or 0/128) + */ + sl = rfd->advertised.ipN_by_prefix; + + for (cursor = NULL, + rc = skiplist_next(sl, NULL, (void **)&adb, + &cursor); + !rc; rc = skiplist_next(sl, NULL, (void **)&adb, + &cursor)) { + + if (pPrefix) { + if (!prefix_same(pPrefix, + &adb->u.s.prefix_ip)) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: adb=%p, prefix doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + if (pPrd) { + if (memcmp(pPrd->val, adb->u.s.prd.val, + 8) + != 0) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: adb=%p, RD doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + if (CHECK_FLAG(cda->l2o.flags, + RFAPI_L2O_MACADDR)) { + if (memcmp(cda->l2o.o.macaddr.octet, + adb->u.s.prefix_eth.u + .prefix_eth.octet, + ETH_ALEN)) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: adb=%p, macaddr doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + + if (CHECK_FLAG(cda->l2o.flags, RFAPI_L2O_LNI)) { + if (cda->l2o.o.logical_net_id + != adb->l2o.logical_net_id) { +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: adb=%p, LNI doesn't match, skipping", + __func__, adb); +#endif + continue; + } + } + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: ipN adding adb %p to delete list", + __func__, adb); +#endif + + listnode_add(adb_delete_list, adb); + } + + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node, adb)) { + int this_advertisement_prefix_count; + struct rfapi_vn_option optary[3]; + struct rfapi_vn_option *opt = NULL; + int cur_opt = 0; + + this_advertisement_prefix_count = 1; + + rfapiQprefix2Rprefix(&adb->u.s.prefix_ip, &rp); + + memset(optary, 0, sizeof(optary)); + + /* if mac addr present in advert, make l2o vn + * option */ + if (adb->u.s.prefix_eth.family == AF_ETHERNET) { + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + opt->v.l2addr.macaddr = + adb->u.s.prefix_eth.u + .prefix_eth; + ++this_advertisement_prefix_count; + } + /* + * use saved RD value instead of trying to + * invert + * complex RD computation in rfapi_register() + */ + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + opt->v.internal_rd = adb->u.s.prd; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: ipN killing reg from adb %p ", + __func__, adb); +#endif + + rc = rfapi_register(rfd, &rp, 0, NULL, + (cur_opt ? optary : NULL), + RFAPI_REGISTER_KILL); + if (!rc) { + cda->pfx_count += + this_advertisement_prefix_count; + cda->reg_count += 1; + deleted_from_this_nve = 1; + } + if (h->rfp_methods.local_cb) { + rfapiAddDeleteLocalRfpPrefix( + &rfd->un_addr, &rfd->vn_addr, + &rp, 0, 0, NULL, &head, &tail); + } + } + list_delete_all_node(adb_delete_list); + + if (!(pPrefix && !RFAPI_0_PREFIX(pPrefix))) { + /* + * Caller didn't specify a prefix, or specified + * (0/32 or 0/128) + */ + + /* + * Advertised 0/32 and 0/128 (indexed by + * ethernet address) + */ + sl = rfd->advertised.ip0_by_ether; + + for (cursor = NULL, + rc = skiplist_next(sl, NULL, (void **)&adb, + &cursor); + !rc; + rc = skiplist_next(sl, NULL, (void **)&adb, + &cursor)) { + + if (CHECK_FLAG(cda->l2o.flags, + RFAPI_L2O_MACADDR)) { + if (memcmp(cda->l2o.o.macaddr + .octet, + adb->u.s.prefix_eth.u + .prefix_eth + .octet, + ETH_ALEN)) { + + continue; + } + } + if (CHECK_FLAG(cda->l2o.flags, + RFAPI_L2O_LNI)) { + if (cda->l2o.o.logical_net_id + != adb->l2o.logical_net_id) { + continue; + } + } +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: ip0 adding adb %p to delete list", + __func__, adb); +#endif + listnode_add(adb_delete_list, adb); + } + + + for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node, + adb)) { + + struct rfapi_vn_option vn; + + rfapiQprefix2Rprefix( + &adb->u.s.prefix_ip, &rp); + + memset(&vn, 0, sizeof(vn)); + vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR; + vn.v.l2addr = adb->l2o; + +#if DEBUG_L2_EXTRA + vnc_zlog_debug_verbose( + "%s: ip0 killing reg from adb %p ", + __func__, adb); +#endif + + rc = rfapi_register( + rfd, &rp, 0, NULL, &vn, + RFAPI_REGISTER_KILL); + if (!rc) { + cda->pfx_count += 1; + cda->reg_count += 1; + deleted_from_this_nve = 1; + } + if (h->rfp_methods.local_cb) { + struct rfapi_vn_option + *vn_opt_new; + + vn_opt_new = + rfapi_vn_options_dup( + &vn); + rfapiAddDeleteLocalRfpPrefix( + &rfd->un_addr, + &rfd->vn_addr, &rp, 0, + 0, vn_opt_new, &head, + &tail); + } + } + list_delete_all_node(adb_delete_list); + } + list_delete(&adb_delete_list); + } + + + if (head) { /* should not be set if (NULL == + rfapi_cfg->local_cb) */ + h->flags |= RFAPI_INCALLBACK; + (*h->rfp_methods.local_cb)(head, rfd->cookie); + h->flags &= ~RFAPI_INCALLBACK; + head = tail = NULL; + } + + if (deleted_from_this_nve) { + /* + * track unique NVEs seen + */ + memset(&ha, 0, sizeof(ha)); + ha.un = rfd->un_addr; + ha.vn = rfd->vn_addr; + + if (!cda->nves) + cda->nves = skiplist_new(0, nve_addr_cmp, + nve_addr_free); + if (skiplist_search(cda->nves, &ha, (void **)&hap)) { + hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR, + sizeof(struct nve_addr)); + assert(hap); + ha.cda = cda; + *hap = ha; + skiplist_insert(cda->nves, hap, hap); + } + } + } while (0); /* to preserve old code structure */ + + return 0; +} + +static int rfapiDeleteLocalPrefixes(struct rfapi_local_reg_delete_arg *cda) +{ + int rc = 0; + + if (cda->rfg) { + if (cda->rfg->rfd) /* if not open, nothing to delete */ + rc = rfapiDeleteLocalPrefixesByRFD(cda, cda->rfg->rfd); + } else { + struct bgp *bgp = cda->bgp; + struct rfapi *h; + struct rfapi_cfg *rfapi_cfg; + + struct listnode *node; + struct rfapi_descriptor *rfd; + if (!bgp) + return ENXIO; + h = bgp->rfapi; + rfapi_cfg = bgp->rfapi_cfg; + if (!h || !rfapi_cfg) + return ENXIO; + vnc_zlog_debug_verbose("%s: starting descriptor loop", + __func__); + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + rc = rfapiDeleteLocalPrefixesByRFD(cda, rfd); + } + } + return rc; +} + +/* + * clear_vnc_prefix + * + * Deletes local and remote prefixes that match + */ +static void clear_vnc_prefix(struct rfapi_local_reg_delete_arg *cda) +{ + struct prefix pfx_un; + struct prefix pfx_vn; + + struct prefix *pUN = NULL; + struct prefix *pVN = NULL; + struct prefix *pPrefix = NULL; + + struct rfapi_import_table *it = NULL; + + /* + * Delete matching remote prefixes in holddown + */ + if (cda->vn_address.addr_family) { + if (!rfapiRaddr2Qprefix(&cda->vn_address, &pfx_vn)) + pVN = &pfx_vn; + } + if (cda->un_address.addr_family) { + if (!rfapiRaddr2Qprefix(&cda->un_address, &pfx_un)) + pUN = &pfx_un; + } + if (cda->prefix.family) { + pPrefix = &cda->prefix; + } + if (cda->rfg) { + it = cda->rfg->rfapi_import_table; + } + rfapiDeleteRemotePrefixes( + pUN, pVN, pPrefix, it, 0, 1, &cda->remote_active_pfx_count, + &cda->remote_active_nve_count, &cda->remote_holddown_pfx_count, + &cda->remote_holddown_nve_count); + + /* + * Now do local prefixes + */ + rfapiDeleteLocalPrefixes(cda); +} + +static void print_cleared_stats(struct rfapi_local_reg_delete_arg *cda) +{ + struct vty *vty = cda->vty; /* for benefit of VTYNL */ + + /* Our special element-deleting function counts nves */ + if (cda->nves) { + skiplist_free(cda->nves); + cda->nves = NULL; + } + if (cda->failed_pfx_count) + vty_out(vty, "Failed to delete %d prefixes\n", + cda->failed_pfx_count); + + /* left as "prefixes" even in single case for ease of machine parsing */ + vty_out(vty, + "[Local] Cleared %u registrations, %u prefixes, %u responses from %d NVEs\n", + cda->reg_count, cda->pfx_count, cda->query_count, + cda->nve_count); + + /* + * We don't currently allow deletion of active remote prefixes from + * the command line + */ + + vty_out(vty, "[Holddown] Cleared %u prefixes from %u NVEs\n", + cda->remote_holddown_pfx_count, cda->remote_holddown_nve_count); +} + +/* + * Caller has already deleted registrations and queries for this/these + * NVEs. Now we just have to close their descriptors. + */ +static void clear_vnc_nve_closer(struct rfapi_local_reg_delete_arg *cda) +{ + struct skiplist *sl = cda->nves; /* contains affected NVEs */ + struct nve_addr *pKey; + struct nve_addr *pValue; + void *cursor = NULL; + int rc; + + if (!sl) + return; + + for (rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue, &cursor); + !rc; rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue, + &cursor)) { + + if (pValue->rfd) { + pValue->rfd->flags |= + RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY; + rfapi_close(pValue->rfd); + } + } +} + +DEFUN (clear_vnc_nve_all, + clear_vnc_nve_all_cmd, + "clear vnc nve *", + "clear\n" + "VNC Information\n" + "Clear per NVE information\n" + "For all NVEs\n") +{ + + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_args(vty, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses(&cda); + clear_vnc_prefix(&cda); + clear_vnc_nve_closer(&cda); + + print_cleared_stats(&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_vn_un, + clear_vnc_nve_vn_un_cmd, + "clear vnc nve vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "VN address of NVE\n" + "For all NVEs\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "For all UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], argv[6], NULL, + NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses(&cda); + clear_vnc_prefix(&cda); + clear_vnc_nve_closer(&cda); + + print_cleared_stats(&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_un_vn, + clear_vnc_nve_un_vn_cmd, + "clear vnc nve un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "UN address of NVE\n" + "For all un NVEs\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "For all vn NVEs\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[6], argv[4], NULL, + NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses(&cda); + clear_vnc_prefix(&cda); + clear_vnc_nve_closer(&cda); + + print_cleared_stats(&cda); + + return 0; +} + +DEFUN (clear_vnc_nve_vn, + clear_vnc_nve_vn_cmd, + "clear vnc nve vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "VN address of NVE\n" + "All addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], NULL, NULL, + NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses(&cda); + clear_vnc_prefix(&cda); + clear_vnc_nve_closer(&cda); + + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_nve_un, + clear_vnc_nve_un_cmd, + "clear vnc nve un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "UN address of NVE\n" + "All un nves\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[4], NULL, + NULL, NULL, NULL, &cda))) + return rc; + + cda.vty = vty; + + clear_vnc_responses(&cda); + clear_vnc_prefix(&cda); + clear_vnc_nve_closer(&cda); + + print_cleared_stats(&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC Prefix + *-------------------------------------------------*/ + +/* + * This function is defined in this file (rather than in rfp_registration.c) + * because here we have access to all the task handles. + */ +DEFUN (clear_vnc_prefix_vn_un, + clear_vnc_prefix_vn_un_cmd, + "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], argv[7], + NULL, NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_un_vn, + clear_vnc_prefix_un_vn_cmd, + "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[7], argv[5], + NULL, NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_un, + clear_vnc_prefix_un_cmd, + "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, argv[5], NULL, + NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_vn, + clear_vnc_prefix_vn_cmd, + "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], NULL, NULL, + NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_prefix_all, + clear_vnc_prefix_all_cmd, + "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> *", + "clear\n" + "VNC Information\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "From any NVE\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, NULL, NULL, + NULL, NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC MAC + *-------------------------------------------------*/ + +/* + * This function is defined in this file (rather than in rfp_registration.c) + * because here we have access to all the task handles. + */ +DEFUN (clear_vnc_mac_vn_un, + clear_vnc_mac_vn_un_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], argv[9], + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_vn, + clear_vnc_mac_un_vn_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[9], argv[7], + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un, + clear_vnc_mac_un_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[7], argv[3], + argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_vn, + clear_vnc_mac_vn_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], NULL, argv[3], + argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_all, + clear_vnc_mac_all_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> *", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "From any NVE\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, NULL, argv[3], + argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +/*------------------------------------------------- + * Clear VNC MAC PREFIX + *-------------------------------------------------*/ + +DEFUN (clear_vnc_mac_vn_un_prefix, + clear_vnc_mac_vn_un_prefix_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[7], argv[9], + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_vn_prefix, + clear_vnc_mac_un_vn_prefix_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M> prefix <*|A.B.C.D/M|X:X::X:X/M>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "VN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[9], argv[7], + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_un_prefix, + clear_vnc_mac_un_prefix_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All UN addresses\n" + "UN IPv4 interface address\n" + "UN IPv6 interface address\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, argv[9], NULL, argv[7], + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_vn_prefix, + clear_vnc_mac_vn_prefix_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n" + "Clear prefix registration information\n" + "All prefixes\n" + "IPv4 Prefix\n" + "IPv6 Prefix\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, argv[9], argv[7], NULL, + argv[3], argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +DEFUN (clear_vnc_mac_all_prefix, + clear_vnc_mac_all_prefix_cmd, + "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> prefix <*|A.B.C.D/M|X:X::X:X/M>", + "clear\n" + "VNC Information\n" + "Clear mac registration information\n" + "All macs\n" + "MAC address\n" + "VNI keyword\n" + "Any virtual network identifier\n" + "Virtual network identifier\n" + "UN address of NVE\n" + "All VN addresses\n" + "VN IPv4 interface address\n" + "VN IPv6 interface address\n") +{ + struct rfapi_local_reg_delete_arg cda; + int rc; + + /* pfx vn un L2 VNI */ + if ((rc = parse_deleter_tokens(vty, NULL, argv[7], NULL, NULL, argv[3], + argv[5], NULL, NULL, &cda))) + return rc; + cda.vty = vty; + clear_vnc_prefix(&cda); + print_cleared_stats(&cda); + return 0; +} + +/************************************************************************ + * Show commands + ************************************************************************/ + + +/* copied from rfp_vty.c */ +static int check_and_display_is_vnc_running(struct vty *vty) +{ + if (bgp_rfapi_is_vnc_configured(NULL) == 0) + return 1; /* is running */ + + if (vty) { + vty_out(vty, "VNC is not configured.\n"); + } + return 0; /* not running */ +} + +static int rfapi_vty_show_nve_summary(struct vty *vty, + show_nve_summary_t show_type) +{ + struct bgp *bgp_default = bgp_get_default(); + struct rfapi *h; + int is_vnc_running = (bgp_rfapi_is_vnc_configured(bgp_default) == 0); + + int active_local_routes; + int active_remote_routes; + int holddown_remote_routes; + int imported_remote_routes; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + /* don't show local info if not running RFP */ + if (is_vnc_running || show_type == SHOW_NVE_SUMMARY_REGISTERED) { + + switch (show_type) { + + case SHOW_NVE_SUMMARY_ACTIVE_NVES: + vty_out(vty, "%-24s ", "NVEs:"); + vty_out(vty, "%-8s %-8u ", + "Active:", h->descriptors.count); + vty_out(vty, "%-8s %-8u ", + "Maximum:", h->stat.max_descriptors); + vty_out(vty, "%-8s %-8u", + "Unknown:", h->stat.count_unknown_nves); + break; + + case SHOW_NVE_SUMMARY_REGISTERED: + /* + * NB: With the introduction of L2 route support, we no + * longer have a one-to-one correspondence between + * locally-originated route advertisements and routes in + * the import tables that have local origin. This + * discrepancy arises because a single advertisement + * may contain both an IP prefix and a MAC address. + * Such an advertisement results in two import table + * entries: one indexed by IP prefix, the other indexed + * by MAC address. + * + * TBD: update computation and display of registration + * statistics to reflect the underlying semantics. + */ + if (is_vnc_running) { + vty_out(vty, "%-24s ", "Registrations:"); + vty_out(vty, "%-8s %-8u ", "Active:", + rfapiApCountAll(bgp_default)); + vty_out(vty, "%-8s %-8u ", "Failed:", + h->stat.count_registrations_failed); + vty_out(vty, "%-8s %-8u", + "Total:", h->stat.count_registrations); + vty_out(vty, "\n"); + } + vty_out(vty, "%-24s ", "Prefixes registered:"); + vty_out(vty, "\n"); + + rfapiCountAllItRoutes(&active_local_routes, + &active_remote_routes, + &holddown_remote_routes, + &imported_remote_routes); + + /* local */ + if (is_vnc_running) { + vty_out(vty, " %-20s ", "Locally:"); + vty_out(vty, "%-8s %-8u ", + "Active:", active_local_routes); + vty_out(vty, "\n"); + } + + + vty_out(vty, " %-20s ", "Remotely:"); + vty_out(vty, "%-8s %-8u", + "Active:", active_remote_routes); + vty_out(vty, "\n"); + vty_out(vty, " %-20s ", "In Holddown:"); + vty_out(vty, "%-8s %-8u", + "Active:", holddown_remote_routes); + vty_out(vty, "\n"); + vty_out(vty, " %-20s ", "Imported:"); + vty_out(vty, "%-8s %-8u", + "Active:", imported_remote_routes); + break; + + case SHOW_NVE_SUMMARY_QUERIES: + vty_out(vty, "%-24s ", "Queries:"); + vty_out(vty, "%-8s %-8u ", + "Active:", rfapi_monitor_count(NULL)); + vty_out(vty, "%-8s %-8u ", + "Failed:", h->stat.count_queries_failed); + vty_out(vty, "%-8s %-8u", + "Total:", h->stat.count_queries); + break; + + case SHOW_NVE_SUMMARY_RESPONSES: + rfapiRibShowResponsesSummary(vty); + + case SHOW_NVE_SUMMARY_UNKNOWN_NVES: + case SHOW_NVE_SUMMARY_MAX: + break; + } + vty_out(vty, "\n"); + } + return 0; + +notcfg: + vty_out(vty, "VNC is not configured.\n"); + return CMD_WARNING; +} + +static int rfapi_show_nves(struct vty *vty, struct prefix *vn_prefix, + struct prefix *un_prefix) +{ + // struct hash *rfds; + // struct rfp_rfapi_descriptor_param param; + + struct bgp *bgp_default = bgp_get_default(); + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + int total = 0; + int printed = 0; + int rc; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + rc = rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES); + if (rc) + return rc; + + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + struct prefix pfx; + char vn_addr_buf[INET6_ADDRSTRLEN] = { + 0, + }; + char un_addr_buf[INET6_ADDRSTRLEN] = { + 0, + }; + char age[10]; + + ++total; + + if (vn_prefix) { + assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx)); + if (!prefix_match(vn_prefix, &pfx)) + continue; + } + + if (un_prefix) { + assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx)); + if (!prefix_match(un_prefix, &pfx)) + continue; + } + + rfapiRfapiIpAddr2Str(&rfd->vn_addr, vn_addr_buf, + INET6_ADDRSTRLEN); + rfapiRfapiIpAddr2Str(&rfd->un_addr, un_addr_buf, + INET6_ADDRSTRLEN); + + if (!printed) { + /* print out a header */ + vty_out(vty, + " Active Next Hops\n"); + vty_out(vty, "%-15s %-15s %-5s %-5s %-6s %-6s %s\n", + "VN Address", "UN Address", "Regis", "Resps", + "Reach", "Remove", "Age"); + } + + ++printed; + + vty_out(vty, "%-15s %-15s %-5u %-5u %-6u %-6u %s\n", + vn_addr_buf, un_addr_buf, rfapiApCount(rfd), + rfapi_monitor_count(rfd), rfd->stat_count_nh_reachable, + rfd->stat_count_nh_removal, + rfapiFormatAge(rfd->open_time, age, 10)); + } + + if (printed > 0 || vn_prefix || un_prefix) + vty_out(vty, "Displayed %d out of %d active NVEs\n", printed, + total); + + return 0; + +notcfg: + vty_out(vty, "VNC is not configured.\n"); + return CMD_WARNING; +} + + +DEFUN (vnc_show_summary, + vnc_show_summary_cmd, + "show vnc summary", + SHOW_STR + VNC_SHOW_STR + "Display VNC status summary\n") +{ + if (!check_and_display_is_vnc_running(vty)) + return CMD_SUCCESS; + bgp_rfapi_show_summary(bgp_get_default(), vty); + vty_out(vty, "\n"); + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES); + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES); + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_RESPONSES); + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_nves, + vnc_show_nves_cmd, + "show vnc nves", + SHOW_STR + VNC_SHOW_STR + "List known NVEs\n") +{ + rfapi_show_nves(vty, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_nves_ptct, + vnc_show_nves_ptct_cmd, + "show vnc nves <vn|un> <A.B.C.D|X:X::X:X>", + SHOW_STR + VNC_SHOW_STR + "List known NVEs\n" + "VN address of NVE\n" + "UN address of NVE\n" + "IPv4 interface address\n" + "IPv6 interface address\n") +{ + struct prefix pfx; + + if (!check_and_display_is_vnc_running(vty)) + return CMD_SUCCESS; + + if (!str2prefix(argv[4]->arg, &pfx)) { + vty_out(vty, "Malformed address \"%s\"\n", argv[4]->arg); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) { + vty_out(vty, "Invalid address \"%s\"\n", argv[4]->arg); + return CMD_WARNING; + } + + if (argv[3]->arg[0] == 'u') { + rfapi_show_nves(vty, NULL, &pfx); + } else { + rfapi_show_nves(vty, &pfx, NULL); + } + + return CMD_SUCCESS; +} + +/* adapted from rfp_registration_cache_log() */ +static void rfapi_show_registrations(struct vty *vty, + struct prefix *restrict_to, int show_local, + int show_remote, int show_holddown, + int show_imported) +{ + int printed = 0; + + if (!vty) + return; + + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED); + + if (show_local) { + /* non-expiring, local */ + printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 1, + 0, 0); + } + if (show_remote) { + /* non-expiring, non-local */ + printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0, + 1, 0); + } + if (show_holddown) { + /* expiring, including local */ + printed += rfapiShowRemoteRegistrations(vty, restrict_to, 1, 1, + 1, 0); + } + if (show_imported) { + /* non-expiring, non-local */ + printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0, + 1, 1); + } + if (!printed) { + vty_out(vty, "\n"); + } +} + +DEFUN (vnc_show_registrations_pfx, + vnc_show_registrations_pfx_cmd, + "show vnc registrations [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n" + "Limit output to a particualr IPV4 address\n" + "Limit output to a particular IPv4 prefix\n" + "Limit output to a particualr IPV6 address\n" + "Limit output to a particular IPv6 prefix\n" + "Limit output to a particular MAC address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + if (argc > 3) { + if (!str2prefix(argv[3]->arg, &p)) { + vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg); + return CMD_SUCCESS; + } else { + p_addr = &p; + } + } + + rfapi_show_registrations(vty, p_addr, 1, 1, 1, 1); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_registrations_some_pfx, + vnc_show_registrations_some_pfx_cmd, + "show vnc registrations <all|holddown|imported|local|remote> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]", + SHOW_STR + VNC_SHOW_STR + "List active prefix registrations\n" + "show all registrations\n" + "show only registrations in holddown\n" + "show only imported prefixes\n" + "show only local registrations\n" + "show only remote registrations\n" + "Limit output to a particualr IPV4 address\n" + "Limit output to a particular IPv4 prefix\n" + "Limit output to a particualr IPV6 address\n" + "Limit output to a particular IPv6 prefix\n" + "Limit output to a particular MAC address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + int show_local = 0; + int show_remote = 0; + int show_holddown = 0; + int show_imported = 0; + + if (argc > 4) { + if (!str2prefix(argv[4]->arg, &p)) { + vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg); + return CMD_SUCCESS; + } else { + p_addr = &p; + } + } + switch (argv[3]->arg[0]) { + case 'a': + show_local = 1; + show_remote = 1; + show_holddown = 1; + show_imported = 1; + break; + + case 'h': + show_holddown = 1; + break; + + case 'i': + show_imported = 1; + break; + + case 'l': + show_local = 1; + break; + + case 'r': + show_remote = 1; + break; + } + + rfapi_show_registrations(vty, p_addr, show_local, show_remote, + show_holddown, show_imported); + return CMD_SUCCESS; +} + +DEFUN (vnc_show_responses_pfx, + vnc_show_responses_pfx_cmd, + "show vnc responses [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n" + "Limit output to a particualr IPV4 address\n" + "Limit output to a particular IPv4 prefix\n" + "Limit output to a particualr IPV6 address\n" + "Limit output to a particular IPv6 prefix\n" + "Limit output to a particular MAC address\n" ) +{ + struct prefix p; + struct prefix *p_addr = NULL; + + if (argc > 3) { + if (!str2prefix(argv[3]->arg, &p)) { + vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg); + return CMD_SUCCESS; + } else { + p_addr = &p; + } + } + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES); + + rfapiRibShowResponsesSummary(vty); + + rfapiRibShowResponses(vty, p_addr, 0); + rfapiRibShowResponses(vty, p_addr, 1); + + return CMD_SUCCESS; +} + +DEFUN (vnc_show_responses_some_pfx, + vnc_show_responses_some_pfx_cmd, + "show vnc responses <active|removed> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]", + SHOW_STR + VNC_SHOW_STR + "List recent query responses\n" + "show only active query responses\n" + "show only removed query responses\n" + "Limit output to a particualr IPV4 address\n" + "Limit output to a particular IPv4 prefix\n" + "Limit output to a particualr IPV6 address\n" + "Limit output to a particular IPv6 prefix\n" + "Limit output to a particular MAC address\n") +{ + struct prefix p; + struct prefix *p_addr = NULL; + + int show_active = 0; + int show_removed = 0; + + if (!check_and_display_is_vnc_running(vty)) + return CMD_SUCCESS; + + if (argc > 4) { + if (!str2prefix(argv[4]->arg, &p)) { + vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg); + return CMD_SUCCESS; + } else { + p_addr = &p; + } + } + + switch (argv[3]->arg[0]) { + case 'a': + show_active = 1; + break; + + case 'r': + show_removed = 1; + break; + } + + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES); + + rfapiRibShowResponsesSummary(vty); + + if (show_active) + rfapiRibShowResponses(vty, p_addr, 0); + if (show_removed) + rfapiRibShowResponses(vty, p_addr, 1); + + return CMD_SUCCESS; +} + +DEFUN (show_vnc_queries_pfx, + show_vnc_queries_pfx_cmd, + "show vnc queries [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]", + SHOW_STR + VNC_SHOW_STR + "List active queries\n" + "Limit output to a particualr IPV4 address\n" + "Limit output to a particular IPv4 prefix\n" + "Limit output to a particualr IPV6 address\n" + "Limit output to a particular IPv6 prefix\n" + "Limit output to a particualr MAC address\n") +{ + struct prefix pfx; + struct prefix *p = NULL; + + if (argc > 3) { + if (!str2prefix(argv[3]->arg, &pfx)) { + vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg); + return CMD_WARNING; + } + p = &pfx; + } + + rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES); + + return rfapiShowVncQueries(vty, p); +} + +DEFUN (vnc_clear_counters, + vnc_clear_counters_cmd, + "clear vnc counters", + CLEAR_STR + VNC_SHOW_STR + "Reset VNC counters\n") +{ + struct bgp *bgp_default = bgp_get_default(); + struct rfapi *h; + struct listnode *node; + struct rfapi_descriptor *rfd; + + if (!bgp_default) + goto notcfg; + + h = bgp_default->rfapi; + + if (!h) + goto notcfg; + + /* per-rfd */ + for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) { + rfd->stat_count_nh_reachable = 0; + rfd->stat_count_nh_removal = 0; + } + + /* global */ + memset(&h->stat, 0, sizeof(h->stat)); + + /* + * 151122 per bug 103, set count_registrations = number active. + * Do same for queries + */ + h->stat.count_registrations = rfapiApCountAll(bgp_default); + h->stat.count_queries = rfapi_monitor_count(NULL); + + rfapiRibShowResponsesSummaryClear(); + + return CMD_SUCCESS; + +notcfg: + vty_out(vty, "VNC is not configured.\n"); + return CMD_WARNING; +} + +/************************************************************************ + * Add prefix with vrf + * + * add [vrf <vrf-name>] prefix <prefix> + * [rd <value>] [label <value>] [local-preference <0-4294967295>] + ************************************************************************/ +void vnc_add_vrf_opener(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + if (rfg->rfd == NULL) { /* need new rfapi_handle */ + /* based on rfapi_open */ + struct rfapi_descriptor *rfd; + + rfd = XCALLOC(MTYPE_RFAPI_DESC, + sizeof(struct rfapi_descriptor)); + rfd->bgp = bgp; + rfg->rfd = rfd; + /* leave most fields empty as will get from (dynamic) config + * when needed */ + rfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS; + rfd->cookie = rfg; + if (rfg->vn_prefix.family + && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) { + rfapiQprefix2Raddr(&rfg->vn_prefix, &rfd->vn_addr); + } else { + memset(&rfd->vn_addr, 0, sizeof(struct rfapi_ip_addr)); + rfd->vn_addr.addr_family = AF_INET; + rfd->vn_addr.addr.v4 = bgp->router_id; + } + rfd->un_addr = rfd->vn_addr; /* sigh, need something in UN for + lookups */ + vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__, + rfg->name); + rfapi_init_and_open(bgp, rfd, rfg); + } +} + +/* NOTE: this functions parallels vnc_direct_add_rn_group_rd */ +static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf, + const char *arg_prefix, + const char *arg_rd, /* optional */ + const char *arg_label, /* optional */ + const char *arg_pref) /* optional */ +{ + struct bgp *bgp; + struct rfapi_nve_group_cfg *rfg; + struct prefix pfx; + struct rfapi_ip_prefix rpfx; + uint32_t pref = 0; + struct rfapi_vn_option optary[3]; + struct rfapi_vn_option *opt = NULL; + int cur_opt = 0; + + bgp = bgp_get_default(); /* assume main instance for now */ + if (!bgp) { + vty_out(vty, "No BGP process is configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!bgp->rfapi || !bgp->rfapi_cfg) { + vty_out(vty, "VRF support not configured\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF); + /* arg checks */ + if (!rfg) { + vty_out(vty, "VRF \"%s\" appears not to be configured.\n", + arg_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + if (!rfg->rt_export_list || !rfg->rfapi_import_table) { + vty_out(vty, + "VRF \"%s\" is missing RT import/export RT configuration.\n", + arg_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + if (!rfg->rd.prefixlen && !arg_rd) { + vty_out(vty, + "VRF \"%s\" isn't configured with an RD, so RD must be provided.\n", + arg_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + if (rfg->label > MPLS_LABEL_MAX && !arg_label) { + vty_out(vty, + "VRF \"%s\" isn't configured with a default labels, so a label must be provided.\n", + arg_vrf); + return CMD_WARNING_CONFIG_FAILED; + } + if (!str2prefix(arg_prefix, &pfx)) { + vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix); + return CMD_WARNING_CONFIG_FAILED; + } + rfapiQprefix2Rprefix(&pfx, &rpfx); + memset(optary, 0, sizeof(optary)); + if (arg_rd) { + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD; + /* TODO: save RD format */ + if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) { + vty_out(vty, "Malformed RD \"%s\"\n", arg_rd); + return CMD_WARNING_CONFIG_FAILED; + } + } + if (rfg->label <= MPLS_LABEL_MAX || arg_label) { + struct rfapi_l2address_option *l2o; + if (opt != NULL) + opt->next = &optary[cur_opt]; + opt = &optary[cur_opt++]; + opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR; + l2o = &opt->v.l2addr; + if (arg_label) { + int32_t label; + label = strtoul(arg_label, NULL, 10); + l2o->label = label; + } else + l2o->label = rfg->label; + } + if (arg_pref) { + char *endptr = NULL; + pref = strtoul(arg_pref, &endptr, 10); + if (*endptr != '\0') { + vty_out(vty, + "%% Invalid local-preference value \"%s\"\n", + arg_pref); + return CMD_WARNING_CONFIG_FAILED; + } + } + rpfx.cost = 255 - (pref & 255); + vnc_add_vrf_opener(bgp, rfg); + + if (!rfapi_register(rfg->rfd, &rpfx, RFAPI_INFINITE_LIFETIME, NULL, + (cur_opt ? optary : NULL), RFAPI_REGISTER_ADD)) { + struct rfapi_next_hop_entry *head = NULL; + struct rfapi_next_hop_entry *tail = NULL; + struct rfapi_vn_option *vn_opt_new; + + vnc_zlog_debug_verbose("%s: rfapi_register succeeded", + __func__); + + if (bgp->rfapi->rfp_methods.local_cb) { + struct rfapi_descriptor *r = + (struct rfapi_descriptor *)rfg->rfd; + vn_opt_new = rfapi_vn_options_dup(opt); + + rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr, + &rpfx, 1, + RFAPI_INFINITE_LIFETIME, + vn_opt_new, &head, &tail); + if (head) { + bgp->rfapi->flags |= RFAPI_INCALLBACK; + (*bgp->rfapi->rfp_methods.local_cb)(head, + r->cookie); + bgp->rfapi->flags &= ~RFAPI_INCALLBACK; + } + head = tail = NULL; + } + vnc_zlog_debug_verbose( + "%s completed, count=%d/%d", __func__, + rfg->rfapi_import_table->local_count[AFI_IP], + rfg->rfapi_import_table->local_count[AFI_IP6]); + return CMD_SUCCESS; + } + + vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__); + vty_out(vty, "Add failed.\n"); + return CMD_WARNING_CONFIG_FAILED; +} + +DEFUN (add_vrf_prefix_rd_label_pref, + add_vrf_prefix_rd_label_pref_cmd, + "add vrf NAME prefix <A.B.C.D/M|X:X::X:X/M> [{rd ASN:NN_OR_IP-ADDRESS|label (0-1048575)|preference (0-4294967295)}]", + "Add\n" + "To a VRF\n" + "VRF name\n" + "Add/modify prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Override configured VRF Route Distinguisher\n" + "<as-number>:<number> or <ip-address>:<number>\n" + "Override configured VRF label\n" + "Label Value <0-1048575>\n" + "Set advertised local preference\n" + "local preference (higher=more preferred)\n") +{ + char *arg_vrf = argv[2]->arg; + char *arg_prefix = argv[4]->arg; + char *arg_rd = NULL; /* optional */ + char *arg_label = NULL; /* optional */ + char *arg_pref = NULL; /* optional */ + int pargc = 5; + argc--; /* don't parse argument */ + while (pargc < argc) { + switch (argv[pargc++]->arg[0]) { + case 'r': + arg_rd = argv[pargc]->arg; + break; + case 'l': + arg_label = argv[pargc]->arg; + break; + case 'p': + arg_pref = argv[pargc]->arg; + break; + default: + break; + } + pargc++; + } + + return vnc_add_vrf_prefix(vty, arg_vrf, arg_prefix, arg_rd, arg_label, + arg_pref); +} + +/************************************************************************ + * del prefix with vrf + * + * clear [vrf <vrf-name>] prefix <prefix> [rd <value>] + ************************************************************************/ +static int rfapi_cfg_group_it_count(struct rfapi_nve_group_cfg *rfg) +{ + int count = 0; + + if (rfg->rfapi_import_table == NULL) + return 0; + + afi_t afi = AFI_MAX; + while (afi-- > 0) { + count += rfg->rfapi_import_table->local_count[afi]; + } + return count; +} + +void clear_vnc_vrf_closer(struct rfapi_nve_group_cfg *rfg) +{ + struct rfapi_descriptor *rfd = rfg->rfd; + afi_t afi; + + if (rfd == NULL) + return; + /* check if IT is empty */ + for (afi = 0; + afi < AFI_MAX && rfg->rfapi_import_table->local_count[afi] == 0; + afi++) + ; + + if (afi == AFI_MAX) { + vnc_zlog_debug_verbose("%s: closing RFD for VRF %s", __func__, + rfg->name); + rfg->rfd = NULL; + rfapi_close(rfd); + } else { + vnc_zlog_debug_verbose( + "%s: VRF %s afi=%d count=%d", __func__, rfg->name, afi, + rfg->rfapi_import_table->local_count[afi]); + } +} + +static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf, + const char *arg_prefix, /* NULL = all */ + const char *arg_rd) /* optional */ +{ + struct rfapi_nve_group_cfg *rfg; + struct rfapi_local_reg_delete_arg cda; + int rc; + int start_count; + + if (bgp == NULL) + bgp = bgp_get_default(); /* assume main instance for now */ + if (!bgp) { + vty_out(vty, "No BGP process is configured\n"); + return CMD_WARNING; + } + if (!bgp->rfapi || !bgp->rfapi_cfg) { + vty_out(vty, "VRF support not configured\n"); + return CMD_WARNING; + } + rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF); + /* arg checks */ + if (!rfg) { + vty_out(vty, "VRF \"%s\" appears not to be configured.\n", + arg_vrf); + return CMD_WARNING; + } + rc = parse_deleter_args(vty, bgp, arg_prefix, NULL, NULL, NULL, NULL, + arg_rd, rfg, &cda); + if (rc != CMD_SUCCESS) /* parse error */ + return rc; + + start_count = rfapi_cfg_group_it_count(rfg); + clear_vnc_prefix(&cda); + vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count, + start_count); + print_cleared_stats(&cda); /* frees lists in cda */ + return CMD_SUCCESS; +} + +DEFUN (clear_vrf_prefix_rd, + clear_vrf_prefix_rd_cmd, + "clear vrf NAME [prefix <A.B.C.D/M|X:X::X:X/M>] [rd ASN:NN_OR_IP-ADDRESS]", + "Clear stored data\n" + "From a VRF\n" + "VRF name\n" + "Prefix related information\n" + "IPv4 prefix\n" + "IPv6 prefix\n" + "Specific VRF Route Distinguisher\n" + "<as-number>:<number> or <ip-address>:<number>\n") +{ + char *arg_vrf = argv[2]->arg; + char *arg_prefix = NULL; /* optional */ + char *arg_rd = NULL; /* optional */ + int pargc = 3; + argc--; /* don't check parameter */ + while (pargc < argc) { + switch (argv[pargc++]->arg[0]) { + case 'r': + arg_rd = argv[pargc]->arg; + break; + case 'p': + arg_prefix = argv[pargc]->arg; + break; + default: + break; + } + pargc++; + } + return vnc_clear_vrf(vty, NULL, arg_vrf, arg_prefix, arg_rd); +} + +DEFUN (clear_vrf_all, + clear_vrf_all_cmd, + "clear vrf NAME all", + "Clear stored data\n" + "From a VRF\n" + "VRF name\n" + "All prefixes\n") +{ + char *arg_vrf = argv[2]->arg; + return vnc_clear_vrf(vty, NULL, arg_vrf, NULL, NULL); +} + +void rfapi_vty_init(void) +{ + install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_lnh_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_cost_lnh_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_life_lnh_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_lnh_cmd); + + install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_cost_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_life_cmd); + install_element(ENABLE_NODE, &add_vnc_prefix_cmd); + + install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_life_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_life_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_life_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_life_cmd); + install_element(ENABLE_NODE, &add_vnc_mac_vni_cmd); + + install_element(ENABLE_NODE, &add_vrf_prefix_rd_label_pref_cmd); + + install_element(ENABLE_NODE, &clear_vnc_nve_all_cmd); + install_element(ENABLE_NODE, &clear_vnc_nve_vn_un_cmd); + install_element(ENABLE_NODE, &clear_vnc_nve_un_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_nve_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_nve_un_cmd); + + install_element(ENABLE_NODE, &clear_vnc_prefix_vn_un_cmd); + install_element(ENABLE_NODE, &clear_vnc_prefix_un_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_prefix_un_cmd); + install_element(ENABLE_NODE, &clear_vnc_prefix_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_prefix_all_cmd); + + install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_un_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_vn_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_all_cmd); + + install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_prefix_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_prefix_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_un_prefix_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd); + install_element(ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd); + + install_element(ENABLE_NODE, &clear_vrf_prefix_rd_cmd); + install_element(ENABLE_NODE, &clear_vrf_all_cmd); + + install_element(ENABLE_NODE, &vnc_clear_counters_cmd); + + install_element(VIEW_NODE, &vnc_show_summary_cmd); + install_element(VIEW_NODE, &vnc_show_nves_cmd); + install_element(VIEW_NODE, &vnc_show_nves_ptct_cmd); + + install_element(VIEW_NODE, &vnc_show_registrations_pfx_cmd); + install_element(VIEW_NODE, &vnc_show_registrations_some_pfx_cmd); + install_element(VIEW_NODE, &vnc_show_responses_pfx_cmd); + install_element(VIEW_NODE, &vnc_show_responses_some_pfx_cmd); + install_element(VIEW_NODE, &show_vnc_queries_pfx_cmd); +} diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h new file mode 100644 index 0000000..a563701 --- /dev/null +++ b/bgpd/rfapi/rfapi_vty.h @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef RFAPI_VTY_H +#define RFAPI_VTY_H + +#include "lib/vty.h" + +typedef enum { + SHOW_NVE_SUMMARY_ACTIVE_NVES, + SHOW_NVE_SUMMARY_UNKNOWN_NVES, /* legacy */ + SHOW_NVE_SUMMARY_REGISTERED, + SHOW_NVE_SUMMARY_QUERIES, + SHOW_NVE_SUMMARY_RESPONSES, + SHOW_NVE_SUMMARY_MAX +} show_nve_summary_t; + +#define VNC_SHOW_STR "VNC information\n" + +extern char *rfapiFormatSeconds(uint32_t seconds, char *buf, size_t len); + +extern char *rfapiFormatAge(time_t age, char *buf, size_t len); + +extern void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix); + +extern int rfapiQprefix2Raddr(struct prefix *qprefix, + struct rfapi_ip_addr *raddr); + +extern void rfapiQprefix2Rprefix(const struct prefix *qprefix, + struct rfapi_ip_prefix *rprefix); + +extern int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix, + struct prefix *qprefix); + +extern int rfapiRaddr2Qprefix(struct rfapi_ip_addr *hia, struct prefix *pfx); + +extern int rfapiRprefixSame(struct rfapi_ip_prefix *hp1, + struct rfapi_ip_prefix *hp2); + +extern void rfapiL2o2Qprefix(struct rfapi_l2address_option *l2o, + struct prefix *pfx); + +extern int rfapiStr2EthAddr(const char *str, struct ethaddr *ea); + +extern const char *rfapi_ntop(int af, const void *src, char *buf, + socklen_t size); + +extern int rfapiDebugPrintf(void *dummy, const char *format, ...) + PRINTFRR(2, 3); + +extern int rfapiStream2Vty(void *stream, /* input */ + int (**fp)(void *, const char *, ...), /* output */ + struct vty **vty, /* output */ + void **outstream, /* output */ + const char **vty_newline); /* output */ + +/*------------------------------------------ + * rfapiRfapiIpAddr2Str + * + * UI helper: generate string from rfapi_ip_addr + * + * input: + * a IP v4/v6 address + * + * output + * buf put string here + * bufsize max space to write + * + * return value: + * NULL conversion failed + * non-NULL pointer to buf + --------------------------------------------*/ +extern const char *rfapiRfapiIpAddr2Str(struct rfapi_ip_addr *a, char *buf, + int bufsize); + +extern void rfapiPrintRfapiIpAddr(void *stream, struct rfapi_ip_addr *a); + +extern void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p); + +extern void rfapiPrintAdvertisedInfo(struct vty *vty, + struct rfapi_descriptor *rfd, safi_t safi, + struct prefix *p); + +extern void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd); + +extern void rfapiPrintMatchingDescriptors(struct vty *vty, + struct prefix *vn_prefix, + struct prefix *un_prefix); + +extern void rfapiPrintAttrPtrs(void *stream, struct attr *attr); + +/* + * Parse an address and put into a struct prefix + */ +extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, + struct prefix *p); + +extern int rfapiCliGetRfapiIpAddr(struct vty *vty, const char *str, + struct rfapi_ip_addr *hai); + +extern void rfapiPrintNhl(void *stream, struct rfapi_next_hop_entry *next_hops); + +extern char *rfapiMonitorVpn2Str(struct rfapi_monitor_vpn *m, char *buf, + int size); + +extern const char *rfapiRfapiIpPrefix2Str(struct rfapi_ip_prefix *p, char *buf, + int bufsize); + +extern void rfapiShowItNode(void *stream, struct agg_node *rn); + +extern char *rfapiEthAddr2Str(const struct ethaddr *ea, char *buf, int bufsize); + +/* install vty commands */ +extern void rfapi_vty_init(void); + +/*------------------------------------------ + * rfapiShowRemoteRegistrations + * + * UI helper: produces the "remote" portion of the output + * of "show vnc registrations". + * + * input: + * stream pointer to output stream + * prefix_only pointer to prefix. If non-NULL, print only registrations + * matching the specified prefix + * show_expiring if non-zero, show expiring registrations + * show_local if non-zero, show local registrations + * show_imported if non-zero, show imported registrations + * + * return value: + * 0 nothing printed + * >0 something printed + --------------------------------------------*/ +extern int rfapiShowRemoteRegistrations(void *stream, + struct prefix *prefix_only, + int show_expiring, int show_local, + int show_remote, int show_imported); + +/*------------------------------------------ + * rfapi_monitor_count + * + * UI helper: count number of active monitors + * + * input: + * handle rfapi handle (NULL to count across + * all open handles) + * + * output + * + * return value: + * count of monitors + --------------------------------------------*/ +extern uint32_t rfapi_monitor_count(rfapi_handle); + +extern int rfapiShowVncQueries(void *stream, struct prefix *pfx_match); + + +#endif diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c new file mode 100644 index 0000000..eb0d861 --- /dev/null +++ b/bgpd/rfapi/vnc_debug.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2016, LabN Consulting, L.L.C. + */ + +#include "lib/zebra.h" + +#include "lib/prefix.h" +#include "lib/linklist.h" +#include "lib/stream.h" +#include "lib/command.h" +#include "lib/log.h" +#include "bgpd/rfapi/vnc_debug.h" + +/* + * debug state storage + */ +unsigned long conf_vnc_debug; +unsigned long term_vnc_debug; + +struct vnc_debug { + unsigned long bit; + const char *name; +}; + +static const struct vnc_debug vncdebug[] = { + {VNC_DEBUG_RFAPI_QUERY, "rfapi-query"}, + {VNC_DEBUG_IMPORT_BI_ATTACH, "import-bi-attach"}, + {VNC_DEBUG_IMPORT_DEL_REMOTE, "import-del-remote"}, + {VNC_DEBUG_EXPORT_BGP_GETCE, "export-bgp-getce"}, + {VNC_DEBUG_EXPORT_BGP_DIRECT_ADD, "export-bgp-direct-add"}, + {VNC_DEBUG_IMPORT_BGP_ADD_ROUTE, "import-bgp-add-route"}, + {VNC_DEBUG_VERBOSE, "verbose"}, +}; + +#define VNC_STR "VNC information\n" + +/*********************************************************************** + * debug bgp vnc <foo> + ***********************************************************************/ +DEFUN (debug_bgp_vnc, + debug_bgp_vnc_cmd, + "debug bgp vnc <rfapi-query|import-bi-attach|import-del-remote|verbose>", + DEBUG_STR + BGP_STR + VNC_STR + "rfapi query handling\n" + "import BI atachment\n" + "import delete remote routes\n" + "verbose logging\n") +{ + size_t i; + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) { + if (strmatch(argv[3]->text, vncdebug[i].name)) { + if (vty->node == CONFIG_NODE) { + conf_vnc_debug |= vncdebug[i].bit; + term_vnc_debug |= vncdebug[i].bit; + } else { + term_vnc_debug |= vncdebug[i].bit; + vty_out(vty, "BGP vnc %s debugging is on\n", + vncdebug[i].name); + } + return CMD_SUCCESS; + } + } + vty_out(vty, "Unknown debug flag: %s\n", argv[3]->arg); + return CMD_WARNING_CONFIG_FAILED; +} + +DEFUN (no_debug_bgp_vnc, + no_debug_bgp_vnc_cmd, + "no debug bgp vnc <rfapi-query|import-bi-attach|import-del-remote|verbose>", + NO_STR + DEBUG_STR + BGP_STR + VNC_STR + "rfapi query handling\n" + "import BI atachment\n" + "import delete remote routes\n" + "verbose logging\n") +{ + size_t i; + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) { + if (strmatch(argv[argc - 1]->text, vncdebug[i].name)) { + if (vty->node == CONFIG_NODE) { + conf_vnc_debug &= ~vncdebug[i].bit; + term_vnc_debug &= ~vncdebug[i].bit; + } else { + term_vnc_debug &= ~vncdebug[i].bit; + vty_out(vty, "BGP vnc %s debugging is off\n", + vncdebug[i].name); + } + return CMD_SUCCESS; + } + } + vty_out(vty, "Unknown debug flag: %s\n", argv[3]->arg); + return CMD_WARNING_CONFIG_FAILED; +} + +/*********************************************************************** + * no debug bgp vnc all + ***********************************************************************/ + +DEFUN (no_debug_bgp_vnc_all, + no_debug_bgp_vnc_all_cmd, + "no debug all bgp vnc", + NO_STR + DEBUG_STR + "Disable all VNC debugging\n" + BGP_STR + VNC_STR) +{ + term_vnc_debug = 0; + vty_out(vty, "All possible VNC debugging has been turned off\n"); + + return CMD_SUCCESS; +} + +/*********************************************************************** + * show/save + ***********************************************************************/ + +DEFUN_NOSH (show_debugging_bgp_vnc, + show_debugging_bgp_vnc_cmd, + "show debugging bgp vnc", + SHOW_STR + DEBUG_STR + BGP_STR + VNC_STR) +{ + size_t i; + + vty_out(vty, "BGP VNC debugging status:\n"); + + for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) { + if (term_vnc_debug & vncdebug[i].bit) { + vty_out(vty, " BGP VNC %s debugging is on\n", + vncdebug[i].name); + } + } + vty_out(vty, "\n"); + return CMD_SUCCESS; +} + +static int bgp_vnc_config_write_debug(struct vty *vty) +{ + int write = 0; + size_t i; + + for (i = 0; i < array_size(vncdebug); ++i) { + if (conf_vnc_debug & vncdebug[i].bit) { + vty_out(vty, "debug bgp vnc %s\n", vncdebug[i].name); + write++; + } + } + return write; +} + +static int bgp_vnc_config_write_debug(struct vty *vty); +static struct cmd_node debug_node = { + .name = "vnc debug", + .node = DEBUG_VNC_NODE, + .prompt = "", + .config_write = bgp_vnc_config_write_debug, +}; + +void vnc_debug_init(void) +{ + install_node(&debug_node); + install_element(ENABLE_NODE, &show_debugging_bgp_vnc_cmd); + + install_element(ENABLE_NODE, &debug_bgp_vnc_cmd); + install_element(CONFIG_NODE, &debug_bgp_vnc_cmd); + install_element(ENABLE_NODE, &no_debug_bgp_vnc_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_vnc_cmd); + + install_element(ENABLE_NODE, &no_debug_bgp_vnc_all_cmd); + install_element(CONFIG_NODE, &no_debug_bgp_vnc_all_cmd); +} diff --git a/bgpd/rfapi/vnc_debug.h b/bgpd/rfapi/vnc_debug.h new file mode 100644 index 0000000..c775127 --- /dev/null +++ b/bgpd/rfapi/vnc_debug.h @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2016, LabN Consulting, L.L.C. + */ + +#ifndef _QUAGGA_BGP_VNC_DEBUG_H +#define _QUAGGA_BGP_VNC_DEBUG_H + +#ifdef ENABLE_BGP_VNC + +/* + * debug state storage + */ +extern unsigned long conf_vnc_debug; +extern unsigned long term_vnc_debug; + +/* + * debug flag bits + */ +#define VNC_DEBUG_RFAPI_QUERY 0x00000001 +#define VNC_DEBUG_IMPORT_BI_ATTACH 0x00000002 +#define VNC_DEBUG_IMPORT_DEL_REMOTE 0x00000004 +#define VNC_DEBUG_EXPORT_BGP_GETCE 0x00000008 +#define VNC_DEBUG_EXPORT_BGP_DIRECT_ADD 0x00000010 +#define VNC_DEBUG_IMPORT_BGP_ADD_ROUTE 0x00000020 +#define VNC_DEBUG_VERBOSE 0x00000040 +#define VNC_DEBUG_ANY 0xFFFFFFFF + +#define VNC_DEBUG(bit) (term_vnc_debug & (VNC_DEBUG_ ## bit)) +#define vnc_zlog_debug_verbose if (VNC_DEBUG(VERBOSE)) zlog_debug +#define vnc_zlog_debug_any if (VNC_DEBUG(ANY)) zlog_debug + +extern void vnc_debug_init(void); + +#endif /* ENABLE_BGP_VNC */ + +#endif /* _QUAGGA_BGP_VNC_DEBUG_H */ diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c new file mode 100644 index 0000000..7decb75 --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -0,0 +1,2091 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: vnc_export_bgp.c + * Purpose: Export routes to BGP directly (not via zebra) + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/log.h" +#include "lib/stream.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/plist.h" +#include "lib/routemap.h" +#include "lib/lib_errors.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_aspath.h" + +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/vnc_export_bgp_p.h" +#include "bgpd/rfapi/vnc_export_table.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/rfapi_backend.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_debug.h" + + +static void vnc_direct_add_rn_group_rd(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + struct agg_node *rn, struct attr *attr, + afi_t afi, + struct rfapi_descriptor *irfd); + +/*********************************************************************** + * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN + ***********************************************************************/ + +/* + * Memory allocation approach: make a ghost attr that + * has non-interned parts for the modifications. ghost attr + * memory is allocated by caller. + * + * - extract ce (=5226) EC and use as new nexthop + * - strip Tunnel Encap attr + * - copy all ECs + */ +static void encap_attr_export_ce(struct attr *new, struct attr *orig, + struct prefix *use_nexthop) +{ + /* + * Make "new" a ghost attr copy of "orig" + */ + memset(new, 0, sizeof(struct attr)); + *new = *orig; + + /* + * Set nexthop + */ + switch (use_nexthop->family) { + case AF_INET: + new->nexthop = use_nexthop->u.prefix4; + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ + new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + break; + + case AF_INET6: + new->mp_nexthop_global = use_nexthop->u.prefix6; + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ + break; + + default: + assert(0); + break; + } + + /* + * Set MED + * + * Note that it will be deleted when BGP sends to any eBGP + * peer unless PEER_FLAG_MED_UNCHANGED is set: + * + * neighbor NEIGHBOR attribute-unchanged med + */ + if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { + if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { + if (new->local_pref > 255) + new->med = 0; + else + new->med = 255 - new->local_pref; + } else { + new->med = 255; /* shouldn't happen */ + } + new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + } + + /* + * "new" is now a ghost attr: + * - it owns an "extra" struct + * - it owns any non-interned parts + * - any references to interned parts are not counted + * + * Caller should, after using the attr, call: + * - bgp_attr_flush() to free non-interned parts + */ +} + +static int getce(struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce) +{ + uint8_t *ecp; + uint32_t i; + uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin; + struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr); + + for (ecp = ecomm->val, i = 0; i < ecomm->size; + ++i, ecp += ECOMMUNITY_SIZE) { + + if (VNC_DEBUG(EXPORT_BGP_GETCE)) { + vnc_zlog_debug_any( + "%s: %02x %02x %02x %02x %02x %02x %02x %02x", + __func__, ecp[0], ecp[1], ecp[2], ecp[3], + ecp[4], ecp[5], ecp[6], ecp[7]); + } + + /* + * is it ROO? + */ + if (ecp[0] != 1 || ecp[1] != 3) { + continue; + } + + /* + * Match local admin value? + */ + if (ecp[6] != ((localadmin & 0xff00) >> 8) + || ecp[7] != (localadmin & 0xff)) + continue; + + memset((uint8_t *)pfx_ce, 0, sizeof(*pfx_ce)); + memcpy(&pfx_ce->u.prefix4, ecp + 2, 4); + pfx_ce->family = AF_INET; + pfx_ce->prefixlen = IPV4_MAX_BITLEN; + + return 0; + } + return -1; +} + + +void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, + struct bgp_path_info *bpi) +{ + struct attr *attr = bpi->attr; + struct peer *peer = bpi->peer; + const struct prefix *prefix = agg_node_get_prefix(rn); + afi_t afi = family2afi(prefix->family); + struct bgp_dest *udest; + struct bgp_path_info *ubpi; + struct attr hattr; + struct attr *iattr; + struct prefix ce_nexthop; + struct prefix post_routemap_nexthop; + + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", + __func__); + return; + } + + if ((bpi->type != ZEBRA_ROUTE_BGP) + || (bpi->sub_type != BGP_ROUTE_NORMAL + && bpi->sub_type != BGP_ROUTE_RFP + && bpi->sub_type != BGP_ROUTE_STATIC)) { + + vnc_zlog_debug_verbose( + "%s: wrong route type/sub_type for export, skipping", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp ce mode not enabled, skipping", + __func__); + return; + } + + /* + * prefix list check + */ + if (bgp->rfapi_cfg->plist_export_bgp[afi]) { + if (prefix_list_apply(bgp->rfapi_cfg->plist_export_bgp[afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list denied, skipping", __func__); + return; + } + } + + + /* + * Extract CE + * This works only for IPv4 because IPv6 addresses are too big + * to fit in an extended community + */ + if (getce(bgp, attr, &ce_nexthop)) { + vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping", + __func__); + return; + } + + /* + * Is this route already represented in the unicast RIB? + * (look up prefix; compare route type, sub_type, peer, nexthop) + */ + udest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST, + prefix, NULL); + for (ubpi = bgp_dest_get_bgp_path_info(udest); ubpi; + ubpi = ubpi->next) { + struct prefix unicast_nexthop; + + if (CHECK_FLAG(ubpi->flags, BGP_PATH_REMOVED)) + continue; + + rfapiUnicastNexthop2Prefix(afi, ubpi->attr, &unicast_nexthop); + + if (ubpi->type == ZEBRA_ROUTE_VNC_DIRECT + && ubpi->sub_type == BGP_ROUTE_REDISTRIBUTE + && ubpi->peer == peer + && prefix_same(&unicast_nexthop, &ce_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: already have matching exported unicast route, skipping", + __func__); + return; + } + } + + /* + * Construct new attribute set with CE addr as + * nexthop and without Tunnel Encap attr + */ + encap_attr_export_ce(&hattr, attr, &ce_nexthop); + if (bgp->rfapi_cfg->routemap_export_bgp) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(bgp->rfapi_cfg->routemap_export_bgp, + prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* + * Rule: disallow route-map alteration of next-hop, because it + * would make it too difficult to keep track of the correspondence + * between VPN routes and unicast routes. + */ + rfapiUnicastNexthop2Prefix(afi, iattr, &post_routemap_nexthop); + + if (!prefix_same(&ce_nexthop, &post_routemap_nexthop)) { + vnc_zlog_debug_verbose( + "%s: route-map modification of nexthop not allowed, skipping", + __func__); + bgp_attr_unintern(&iattr); + return; + } + + bgp_update(peer, prefix, 0, /* addpath_id */ + iattr, /* bgp_update copies this attr */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, 0, /* tag not used for unicast */ + 0, NULL); /* EVPN not used */ + bgp_attr_unintern(&iattr); +} + + +/* + * "Withdrawing a Route" export process + */ +void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, + struct bgp_path_info *bpi) +{ + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); + struct bgp_path_info *vbpi; + struct prefix ce_nexthop; + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi", __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp ce mode not enabled, skipping", + __func__); + return; + } + + /* + * Extract CE + * This works only for IPv4 because IPv6 addresses are too big + * to fit in an extended community + */ + if (getce(bgp, bpi->attr, &ce_nexthop)) { + vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping", + __func__); + return; + } + + /* + * Look for other VPN routes with same prefix, same 5226 CE, + * same peer. If at least one is present, don't remove the + * route from the unicast RIB + */ + + for (vbpi = rn->info; vbpi; vbpi = vbpi->next) { + struct prefix ce; + if (bpi == vbpi) + continue; + if (bpi->peer != vbpi->peer) + continue; + if (getce(bgp, vbpi->attr, &ce)) + continue; + if (prefix_same(&ce, &ce_nexthop)) { + vnc_zlog_debug_verbose( + "%s: still have a route via CE, not deleting unicast", + __func__); + return; + } + } + + /* + * withdraw the route + */ + bgp_withdraw(bpi->peer, p, 0, /* addpath_id */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, 0, NULL); /* tag not used for unicast */ +} + +static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi) +{ + struct agg_node *rn; + struct bgp_path_info *ri; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!(bgp->rfapi_cfg)) + return; + + if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export of CE routes not enabled, skipping", + __func__); + return; + } + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through entire ce import table and export to BGP unicast. + */ + for (rn = agg_route_top(bgp->rfapi->it_ce->imported_vpn[afi]); rn; + rn = agg_route_next(rn)) { + if (!rn->info) + continue; + + vnc_zlog_debug_verbose("%s: checking prefix %pRN", __func__, + rn); + + for (ri = rn->info; ri; ri = ri->next) { + + vnc_zlog_debug_verbose("%s: ri->sub_type: %d", __func__, + ri->sub_type); + + if (ri->sub_type == BGP_ROUTE_NORMAL + || ri->sub_type == BGP_ROUTE_RFP + || ri->sub_type == BGP_ROUTE_STATIC) { + + vnc_direct_bgp_add_route_ce(bgp, rn, ri); + } + } + } +} + +static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi) +{ + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP unicast table and remove routes that + * originated from us + */ + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *ri; + struct bgp_path_info *next; + + for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; + ri = next) { + + next = ri->next; + + if (ri->type == ZEBRA_ROUTE_VNC_DIRECT + && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { + + bgp_withdraw( + ri->peer, bgp_dest_get_prefix(dest), + 0, /* addpath_id */ + AFI_IP, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for unicast */ + NULL, 0, + NULL); /* tag not used for unicast */ + } + } + } +} + +/*********************************************************************** + * Export methods that set nexthop to CE (from 5226 roo EC) END + ***********************************************************************/ + +/*********************************************************************** + * Export methods that proxy nexthop BEGIN + ***********************************************************************/ + +static struct ecommunity *vnc_route_origin_ecom(struct agg_node *rn) +{ + struct ecommunity *new; + struct bgp_path_info *bpi; + + if (!rn->info) + return NULL; + + new = ecommunity_new(); + + for (bpi = rn->info; bpi; bpi = bpi->next) { + + struct ecommunity_val roec; + + switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) { + case AF_INET: + memset(&roec, 0, sizeof(roec)); + roec.val[0] = 0x01; + roec.val[1] = 0x03; + memcpy(roec.val + 2, + &bpi->attr->mp_nexthop_global_in.s_addr, 4); + roec.val[6] = 0; + roec.val[7] = 0; + ecommunity_add_val(new, &roec, false, false); + break; + case AF_INET6: + /* No support for IPv6 addresses in extended communities + */ + break; + } + } + + if (!new->size) { + ecommunity_free(&new); + new = NULL; + } + + return new; +} + +static struct ecommunity *vnc_route_origin_ecom_single(struct in_addr *origin) +{ + struct ecommunity *new; + struct ecommunity_val roec; + + memset(&roec, 0, sizeof(roec)); + roec.val[0] = 0x01; + roec.val[1] = 0x03; + memcpy(roec.val + 2, &origin->s_addr, 4); + roec.val[6] = 0; + roec.val[7] = 0; + + new = ecommunity_new(); + ecommunity_add_val(new, &roec, false, false); + + if (!new->size) { + ecommunity_free(&new); + new = NULL; + } + + return new; +} + + +/* + * New memory allocation approach: make a ghost attr that + * has non-interned parts for the modifications. ghost attr + * memory is allocated by caller. + */ +static int +encap_attr_export(struct attr *new, struct attr *orig, + struct prefix *new_nexthop, + struct agg_node *rn) /* for VN addrs for ecom list */ + /* if rn is 0, use route's nexthop */ +{ + struct prefix orig_nexthop; + struct prefix *use_nexthop; + static struct ecommunity *ecom_ro; + + if (new_nexthop) { + use_nexthop = new_nexthop; + } else { + use_nexthop = &orig_nexthop; + orig_nexthop.family = + BGP_MP_NEXTHOP_FAMILY(orig->mp_nexthop_len); + if (orig_nexthop.family == AF_INET) { + orig_nexthop.prefixlen = IPV4_MAX_BITLEN; + orig_nexthop.u.prefix4 = orig->mp_nexthop_global_in; + } else if (orig_nexthop.family == AF_INET6) { + orig_nexthop.prefixlen = IPV6_MAX_BITLEN; + orig_nexthop.u.prefix6 = orig->mp_nexthop_global; + } else { + return -1; /* FAIL - can't compute nexthop */ + } + } + + + /* + * Make "new" a ghost attr copy of "orig" + */ + memset(new, 0, sizeof(struct attr)); + *new = *orig; + + /* + * Set nexthop + */ + switch (use_nexthop->family) { + case AF_INET: + new->nexthop = use_nexthop->u.prefix4; + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */ + new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + break; + + case AF_INET6: + new->mp_nexthop_global = use_nexthop->u.prefix6; + new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */ + break; + + default: + assert(0); + break; + } + + if (rn) { + ecom_ro = vnc_route_origin_ecom(rn); + } else { + /* TBD use lcom for IPv6 */ + ecom_ro = vnc_route_origin_ecom_single(&use_nexthop->u.prefix4); + } + if (bgp_attr_get_ecommunity(new)) { + if (ecom_ro) + bgp_attr_set_ecommunity( + new, + ecommunity_merge(ecom_ro, + bgp_attr_get_ecommunity(new))); + } else { + bgp_attr_set_ecommunity(new, ecom_ro); + } + + /* + * Set MED + * + * Note that it will be deleted when BGP sends to any eBGP + * peer unless PEER_FLAG_MED_UNCHANGED is set: + * + * neighbor NEIGHBOR attribute-unchanged med + */ + if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) { + if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) { + if (new->local_pref > 255) + new->med = 0; + else + new->med = 255 - new->local_pref; + } else { + new->med = 255; /* shouldn't happen */ + } + new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC); + } + + /* + * "new" is now a ghost attr: + * - it owns an "extra" struct + * - it owns any non-interned parts + * - any references to interned parts are not counted + * + * Caller should, after using the attr, call: + * - bgp_attr_flush() to free non-interned parts + */ + + return 0; +} + +/* + * "Adding a Route" export process + */ +void vnc_direct_bgp_add_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn) +{ + struct attr attr = {0}; + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) { + vnc_zlog_debug_verbose( + "%s: no bgp-direct export nve group, skipping", + __func__); + return; + } + + bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + vnc_zlog_debug_verbose( + "%s: looping over nve-groups in direct-bgp export list", + __func__); + + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + nnode, rfgn)) { + + struct listnode *ln; + + /* + * If nve group is not defined yet, skip it + */ + if (!rfgn->rfg) + continue; + + /* + * If the nve group uses a different import table, skip it + */ + if (import_table != rfgn->rfg->rfapi_import_table) + continue; + + /* + * if no NVEs currently associated with this group, skip it + */ + if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves) + continue; + + /* + * per-nve-group prefix list check + */ + if (rfgn->rfg->plist_export_bgp[afi]) { + if (prefix_list_apply(rfgn->rfg->plist_export_bgp[afi], + p) + == PREFIX_DENY) + + continue; + } + + if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { + vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr, + afi, rfgn->rfg->rfd); + /* + * yuck! + * - but consistent with rest of function + */ + continue; + } + /* + * For each NVE that is assigned to the export nve group, + * generate + * a route with that NVE as its next hop + */ + for (ln = listhead(rfgn->rfg->nves); ln; + ln = listnextnode(ln)) { + vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr, + afi, listgetdata(ln)); + } + } + + aspath_unintern(&attr.aspath); +} + +/* + * "Withdrawing a Route" export process + */ +void vnc_direct_bgp_del_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + const struct prefix *p = agg_node_get_prefix(rn); + afi_t afi = family2afi(p->family); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) { + vnc_zlog_debug_verbose( + "%s: no bgp-direct export nve group, skipping", + __func__); + return; + } + + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + nnode, rfgn)) { + + struct listnode *ln; + + /* + * If nve group is not defined yet, skip it + */ + if (!rfgn->rfg) + continue; + + /* + * if no NVEs currently associated with this group, skip it + */ + if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves) + continue; + + /* + * If the nve group uses a different import table, + * skip it + */ + if (import_table != rfgn->rfg->rfapi_import_table) + continue; + + if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { + struct prefix nhp; + struct rfapi_descriptor *irfd; + + irfd = rfgn->rfg->rfd; + + if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) + continue; + + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for unicast */ + NULL, 0, + NULL); /* tag not used for unicast */ + /* + * yuck! + * - but consistent with rest of function + */ + continue; + } + /* + * For each NVE that is assigned to the export nve group, + * generate + * a route with that NVE as its next hop + */ + for (ln = listhead(rfgn->rfg->nves); ln; + ln = listnextnode(ln)) { + + struct prefix nhp; + struct rfapi_descriptor *irfd; + + irfd = listgetdata(ln); + + if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) + continue; + + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for unicast */ + NULL, 0, + NULL); /* tag not used for unicast */ + } + } +} + +void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + struct rfapi_nve_group_cfg *rfg = rfd->rfg; + afi_t afi = family2afi(rfd->vn_addr.addr_family); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr", + __func__); + return; + } + + if (!bgp) + return; + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp and see if this new NVE's + * group is among them. + */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + nnode, rfgn)) { + + /* + * Yes, this NVE's group is configured for export to direct-bgp + */ + if (rfgn->rfg == rfg) { + + struct agg_table *rt = NULL; + struct agg_node *rn; + struct attr attr = {0}; + struct rfapi_import_table *import_table; + + + import_table = rfg->rfapi_import_table; + + 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; + } + + bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = agg_route_top(rt); rn; + rn = agg_route_next(rn)) { + + if (rn->info) { + + struct prefix nhp; + struct rfapi_descriptor *irfd = rfd; + struct attr hattr; + struct attr *iattr; + struct bgp_path_info info; + const struct prefix *p = + agg_node_get_prefix(rn); + + if (rfapiRaddr2Qprefix(&irfd->vn_addr, + &nhp)) + continue; + + /* + * per-nve-group prefix list check + */ + if (rfgn->rfg->plist_export_bgp[afi]) { + if (prefix_list_apply( + rfgn->rfg->plist_export_bgp + [afi], + p) + == PREFIX_DENY) + + continue; + } + + + /* + * Construct new attribute set with + * NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export(&hattr, &attr, + &nhp, rn)) + continue; + + if (rfgn->rfg->routemap_export_bgp) { + route_map_result_t ret; + info.peer = irfd->peer; + info.attr = &hattr; + ret = route_map_apply( + rfgn->rfg + ->routemap_export_bgp, + p, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + continue; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + bgp_update( + irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies + it */ + afi, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, NULL, + /* RD not used for unicast */ + NULL, + /* tag not used for unicast */ + 0, 0, NULL); /* EVPN not used */ + + bgp_attr_unintern(&iattr); + } + } + + aspath_unintern(&attr.aspath); + } + } +} + + +void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd) +{ + struct listnode *node, *nnode; + struct rfapi_rfg_name *rfgn; + struct rfapi_nve_group_cfg *rfg = rfd->rfg; + afi_t afi = family2afi(rfd->vn_addr.addr_family); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr", + __func__); + return; + } + + if (!bgp) + return; + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + /* + * Loop over the list of NVE-Groups configured for + * exporting to direct-bgp and see if this new NVE's + * group is among them. + */ + for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + nnode, rfgn)) { + + /* + * Yes, this NVE's group is configured for export to direct-bgp + */ + if (rfg && rfgn->rfg == rfg) { + + struct agg_table *rt = NULL; + struct agg_node *rn; + struct rfapi_import_table *import_table; + + import_table = rfg->rfapi_import_table; + + 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; + } + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = agg_route_top(rt); rn; + rn = agg_route_next(rn)) { + + if (rn->info) { + const struct prefix *p = + agg_node_get_prefix(rn); + struct prefix nhp; + struct rfapi_descriptor *irfd = rfd; + + if (rfapiRaddr2Qprefix(&irfd->vn_addr, + &nhp)) + continue; + + bgp_withdraw(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + afi, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for + unicast */ + NULL, 0, NULL); /* tag not + used for + unicast */ + } + } + } + } +} + +static void vnc_direct_add_rn_group_rd(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + struct agg_node *rn, struct attr *attr, + afi_t afi, struct rfapi_descriptor *irfd) +{ + struct prefix nhp; + struct bgp_path_info info; + struct attr hattr; + struct attr *iattr; + const struct prefix *p = agg_node_get_prefix(rn); + + if (irfd == NULL && rfg->type != RFAPI_GROUP_CFG_VRF) { + /* need new rfapi_handle, for peer strcture + * -- based on vnc_add_vrf_prefi */ + assert(rfg->rfd == NULL); + + if (!rfg->rt_export_list || !rfg->rfapi_import_table) { + vnc_zlog_debug_verbose( + "%s: VRF \"%s\" is missing RT import/export configuration.", + __func__, rfg->name); + return; + } + if (!rfg->rd.prefixlen) { + vnc_zlog_debug_verbose( + "%s: VRF \"%s\" is missing RD configuration.", + __func__, rfg->name); + return; + } + if (rfg->label > MPLS_LABEL_MAX) { + vnc_zlog_debug_verbose( + "%s: VRF \"%s\" is missing default label configuration.", + __func__, rfg->name); + return; + } + + irfd = XCALLOC(MTYPE_RFAPI_DESC, + sizeof(struct rfapi_descriptor)); + irfd->bgp = bgp; + rfg->rfd = irfd; + /* + * leave most fields empty as will get from (dynamic) config + * when needed + */ + irfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS; + irfd->cookie = rfg; + if (rfg->vn_prefix.family + && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) { + rfapiQprefix2Raddr(&rfg->vn_prefix, &irfd->vn_addr); + } else { + memset(&irfd->vn_addr, 0, sizeof(struct rfapi_ip_addr)); + irfd->vn_addr.addr_family = AF_INET; + irfd->vn_addr.addr.v4 = bgp->router_id; + } + irfd->un_addr = irfd->vn_addr; /* sigh, need something in UN for + lookups */ + vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__, + rfg->name); + rfapi_init_and_open(bgp, irfd, rfg); + } + + if (irfd == NULL || rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp)) + return; + + /* + * Construct new attribute set with NVE's VN + * addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export(&hattr, attr, &nhp, rn)) + return; + + if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) { + vnc_zlog_debug_any("%s: attr follows", __func__); + rfapiPrintAttrPtrs(NULL, attr); + vnc_zlog_debug_any("%s: hattr follows", __func__); + rfapiPrintAttrPtrs(NULL, &hattr); + } + + if (rfg->routemap_export_bgp) { + route_map_result_t ret; + + info.peer = irfd->peer; + info.attr = &hattr; + ret = route_map_apply(rfg->routemap_export_bgp, p, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map says DENY, so not calling bgp_update", + __func__); + return; + } + } + + if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) { + vnc_zlog_debug_any("%s: hattr after route_map_apply:", + __func__); + rfapiPrintAttrPtrs(NULL, &hattr); + } + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + bgp_update(irfd->peer, p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies it */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast */ + 0, 0, NULL); /* EVPN not used */ + + bgp_attr_unintern(&iattr); + + return; +} + +/* + * Caller is responsible for ensuring that the specified nve-group + * is actually part of the list of exported nve groups. + */ +static void vnc_direct_bgp_add_group_afi(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct agg_table *rt = NULL; + struct agg_node *rn; + struct attr attr = {0}; + struct rfapi_import_table *import_table; + + 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 (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) { + vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__); + return; + } + + bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE); + /* TBD set some configured med, see add_vnc_route() */ + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + + if (rn->info) { + const struct prefix *p = agg_node_get_prefix(rn); + struct listnode *ln; + + /* + * per-nve-group prefix list check + */ + if (rfg->plist_export_bgp[afi]) { + if (prefix_list_apply( + rfg->plist_export_bgp[afi], p) + == PREFIX_DENY) + + continue; + } + if (rfg->type == RFAPI_GROUP_CFG_VRF) { + vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr, + afi, rfg->rfd); + /* + * yuck! + * - but consistent with rest of function + */ + continue; + } + /* + * For each NVE that is assigned to the export nve + * group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead(rfg->nves); ln; + ln = listnextnode(ln)) { + vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr, + afi, + listgetdata(ln)); + } + } + } + + aspath_unintern(&attr.aspath); +} + + +/* + * Caller is responsible for ensuring that the specified nve-group + * is actually part of the list of exported nve groups. + */ +void vnc_direct_bgp_add_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP); + vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP6); +} + +static void vnc_direct_del_rn_group_rd(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + struct agg_node *rn, afi_t afi, + struct rfapi_descriptor *irfd) +{ + if (irfd == NULL) + return; + + bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), /* prefix */ + 0, /* addpath_id */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, 0, NULL); /* tag not used for unicast */ + return; +} + +/* + * Caller is responsible for ensuring that the specified nve-group + * was actually part of the list of exported nve groups. + */ +static void vnc_direct_bgp_del_group_afi(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct agg_table *rt = NULL; + struct agg_node *rn; + struct rfapi_import_table *import_table; + + 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; + } + + rt = import_table->imported_vpn[afi]; + + if (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) { + vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__); + return; + } + + /* + * Walk the NVE-Group's VNC Import table + */ + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) + if (rn->info) { + if (rfg->type == RFAPI_GROUP_CFG_VRF) + vnc_direct_del_rn_group_rd(bgp, rfg, rn, afi, + rfg->rfd); + else { + struct listnode *ln; + + /* + * For each NVE that is assigned to the export + * nve + * group, generate + * a route with that NVE as its next hop + */ + for (ln = listhead(rfg->nves); ln; + ln = listnextnode(ln)) + vnc_direct_del_rn_group_rd( + bgp, rfg, rn, afi, + listgetdata(ln)); + } + } +} + +/* + * Caller is responsible for ensuring that the specified nve-group + * was actually part of the list of exported nve groups. + */ +void vnc_direct_bgp_del_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg) +{ + vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP); + vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP6); +} + +void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi) +{ + struct listnode *node; + struct rfapi_rfg_name *rfgn; + + if (VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + /* + * look in the list of currently-exported groups + */ + for (ALL_LIST_ELEMENTS_RO( + bgp->rfapi_cfg->rfg_export_direct_bgp_l, node, + rfgn)) { + + if (rfgn->rfg == rfg) { + /* + * If it matches, reexport it + */ + vnc_direct_bgp_del_group_afi(bgp, rfg, afi); + vnc_direct_bgp_add_group_afi(bgp, rfg, afi); + break; + } + } + } +} + + +static void vnc_direct_bgp_unexport_table(afi_t afi, struct agg_table *rt, + struct list *nve_list) +{ + if (nve_list) { + + struct agg_node *rn; + + for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) { + + if (rn->info) { + + struct listnode *hln; + struct rfapi_descriptor *irfd; + + for (ALL_LIST_ELEMENTS_RO(nve_list, hln, + irfd)) { + + bgp_withdraw(irfd->peer, + agg_node_get_prefix(rn), + 0, /* addpath_id */ + afi, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for + unicast */ + NULL, 0, NULL); /* tag not + used for + unicast, + EVPN + neither */ + } + } + } + } +} + +static void import_table_to_nve_list_direct_bgp(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_direct_bgp_l, node, + rfgn)) { + + /* + * If this NVE-Group's import table matches the current one + */ + if (rfgn->rfg && rfgn->rfg->rfapi_import_table == it) { + if (rfgn->rfg->nves) + nve_group_to_nve_list(rfgn->rfg, nves, family); + else if (rfgn->rfg->rfd + && rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) { + if (!*nves) + *nves = list_new(); + listnode_add(*nves, rfgn->rfg->rfd); + } + } + } +} + +void vnc_direct_bgp_vpn_enable(struct bgp *bgp, afi_t afi) +{ + struct listnode *rfgn; + struct rfapi_nve_group_cfg *rfg; + + if (!bgp) + return; + + if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Policy is applied per-nve-group, so we need to iterate + * over the groups to add everything. + */ + for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, rfgn, + rfg)) { + + /* + * contains policy management + */ + vnc_direct_bgp_add_group_afi(bgp, rfg, afi); + } +} + + +void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi) +{ + struct rfapi_import_table *it; + uint8_t family = afi2family(afi); + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!bgp->rfapi) { + vnc_zlog_debug_verbose("%s: rfapi not initialized", __func__); + return; + } + + if (!family || (afi != AFI_IP && afi != AFI_IP6)) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + for (it = bgp->rfapi->imports; it; it = it->next) { + + struct list *nve_list = NULL; + + import_table_to_nve_list_direct_bgp(bgp, it, &nve_list, family); + + if (nve_list) { + vnc_direct_bgp_unexport_table( + afi, it->imported_vpn[afi], nve_list); + list_delete(&nve_list); + } + } +} + + +/*********************************************************************** + * Export methods that proxy nexthop END + ***********************************************************************/ + + +/*********************************************************************** + * Export methods that preserve original nexthop BEGIN + * rh = "registering nve" + ***********************************************************************/ + + +/* + * "Adding a Route" export process + * TBD do we need to check bpi->type and bpi->sub_type here, or does + * caller do it? + */ +void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, struct peer *peer, + struct attr *attr) +{ + struct vnc_export_info *eti; + struct attr hattr; + struct rfapi_cfg *hc; + struct attr *iattr; + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp RH mode not enabled, skipping", + __func__); + return; + } + + /* + * prefix list check + */ + if (hc->plist_export_bgp[afi]) { + if (prefix_list_apply(hc->plist_export_bgp[afi], prefix) + == PREFIX_DENY) + return; + } + + /* + * Construct new attribute set with NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export(&hattr, attr, NULL, NULL)) + return; + if (hc->routemap_export_bgp) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(hc->routemap_export_bgp, prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* + * record route information that we will need to expire + * this route + */ + eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); + rfapiGetVncLifetime(attr, &eti->lifetime); + eti->lifetime = rfapiGetHolddownFromLifetime(eti->lifetime); + + /* + * export expiration timer is already running on + * this route: cancel it + */ + EVENT_OFF(eti->timer); + + bgp_update(peer, prefix, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies this attr */ + afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */ + NULL, /* tag not used for unicast, EVPN neither */ + 0, 0, NULL); /* EVPN not used */ + bgp_attr_unintern(&iattr); +} + +static void vncExportWithdrawTimer(struct event *t) +{ + struct vnc_export_info *eti = EVENT_ARG(t); + const struct prefix *p = agg_node_get_prefix(eti->node); + + /* + * withdraw the route + */ + bgp_withdraw(eti->peer, p, 0, /* addpath_id */ + family2afi(p->family), SAFI_UNICAST, eti->type, + eti->subtype, NULL, /* RD not used for unicast */ + NULL, 0, + NULL); /* tag not used for unicast, EVPN neither */ + + /* + * Free the eti + */ + vnc_eti_delete(eti); +} + +/* + * "Withdrawing a Route" export process + * TBD do we need to check bpi->type and bpi->sub_type here, or does + * caller do it? + */ +void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, struct peer *peer) +{ + struct vnc_export_info *eti; + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of VNC direct routes is off", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export-to-bgp group mode not enabled, skipping", + __func__); + return; + } + + eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE); + + if (!eti->timer && eti->lifetime <= INT32_MAX) { + eti->timer = NULL; + event_add_timer(bm->master, vncExportWithdrawTimer, eti, + eti->lifetime, &eti->timer); + vnc_zlog_debug_verbose( + "%s: set expiration timer for %u seconds", __func__, + eti->lifetime); + } +} + + +void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi) +{ + struct prefix_rd prd; + struct bgp_dest *pdest; + struct rfapi_cfg *hc; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (!(hc = bgp->rfapi_cfg)) + return; + + if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose( + "%s: export of RH routes not enabled, skipping", + __func__); + return; + } + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP VPN table and export to BGP unicast. + */ + + vnc_zlog_debug_verbose("%s: starting RD loop", __func__); + + /* Loop over all the RDs */ + for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest; + pdest = bgp_route_next(pdest)) { + + struct bgp_table *table; + struct bgp_dest *dest; + struct bgp_path_info *ri; + 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)) { + const struct prefix *dest_p; + + /* + * skip prefix list check if no routes here + */ + if (!bgp_dest_has_bgp_path_info_data(dest)) + continue; + + vnc_zlog_debug_verbose("%s: checking prefix %pBD", + __func__, dest); + + dest_p = bgp_dest_get_prefix(dest); + + /* + * prefix list check + */ + if (hc->plist_export_bgp[afi]) { + if (prefix_list_apply(hc->plist_export_bgp[afi], + dest_p) + == PREFIX_DENY) { + + vnc_zlog_debug_verbose( + "%s: prefix list says DENY", + __func__); + continue; + } + } + + for (ri = bgp_dest_get_bgp_path_info(dest); ri; + ri = ri->next) { + + vnc_zlog_debug_verbose("%s: ri->sub_type: %d", + __func__, ri->sub_type); + + if (ri->sub_type == BGP_ROUTE_NORMAL + || ri->sub_type == BGP_ROUTE_RFP) { + + struct vnc_export_info *eti; + struct attr hattr; + struct attr *iattr; + + /* + * Construct new attribute set with + * NVE's VN addr as + * nexthop and without Tunnel Encap attr + */ + if (encap_attr_export(&hattr, ri->attr, + NULL, NULL)) { + vnc_zlog_debug_verbose( + "%s: encap_attr_export failed", + __func__); + continue; + } + + if (hc->routemap_export_bgp) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = ri->peer; + info.attr = &hattr; + ret = route_map_apply( + hc->routemap_export_bgp, + dest_p, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map says DENY", + __func__); + continue; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* + * record route information that we will + * need to expire + * this route + */ + eti = vnc_eti_get( + bgp, EXPORT_TYPE_BGP, dest_p, + ri->peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE); + rfapiGetVncLifetime(ri->attr, + &eti->lifetime); + + /* + * export expiration timer is + * already running on + * this route: cancel it + */ + EVENT_OFF(eti->timer); + + vnc_zlog_debug_verbose( + "%s: calling bgp_update", + __func__); + + bgp_update( + ri->peer, dest_p, /* prefix */ + 0, /* addpath_id */ + iattr, /* bgp_update copies + it */ + AFI_IP, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE, NULL, + /* RD not used for unicast */ + NULL, + /* tag not used for unicast, + or EVPN */ + 0, 0, NULL); /* EVPN not used */ + + bgp_attr_unintern(&iattr); + } + } + } + } +} + +void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi) +{ + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (!bgp) + return; + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi); + return; + } + + /* + * Go through the entire BGP unicast table and remove routes that + * originated from us + */ + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + const struct prefix *dest_p = bgp_dest_get_prefix(dest); + struct bgp_path_info *ri; + struct bgp_path_info *next; + + for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri; + ri = next) { + + next = ri->next; + + if (ri->type == ZEBRA_ROUTE_VNC_DIRECT_RH + && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) { + + struct vnc_export_info *eti; + + /* + * Delete routes immediately (no timer) + */ + eti = vnc_eti_checktimer( + bgp, EXPORT_TYPE_BGP, dest_p, ri->peer, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE); + if (eti) { + EVENT_OFF(eti->timer); + vnc_eti_delete(eti); + } + + bgp_withdraw(ri->peer, dest_p, /* prefix */ + 0, /* addpath_id */ + AFI_IP, SAFI_UNICAST, + ZEBRA_ROUTE_VNC_DIRECT_RH, + BGP_ROUTE_REDISTRIBUTE, + NULL, /* RD not used for unicast */ + NULL, 0, NULL); /* tag not used for + unicast, EVPN + neither */ + } + } + } +} + +void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi) +{ + if (VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) { + vnc_direct_bgp_rh_vpn_disable(bgp, afi); + vnc_direct_bgp_rh_vpn_enable(bgp, afi); + } +} + +/*********************************************************************** + * Generic Export methods + ***********************************************************************/ + +/* + * Assumes the correct mode bits are already turned on. Thus it + * is OK to call this function from, e.g., bgp_redistribute_set() + * without caring if export is enabled or not + */ +void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi) +{ + if (!bgp->rfapi_cfg) + return; + + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: + vnc_direct_bgp_vpn_enable(bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: + vnc_direct_bgp_rh_vpn_enable(bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: + vnc_direct_bgp_vpn_enable_ce(bgp, afi); + break; + } +} + +void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi) +{ + if (!bgp->rfapi_cfg) + return; + + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP: + vnc_direct_bgp_vpn_disable(bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH: + vnc_direct_bgp_rh_vpn_disable(bgp, afi); + break; + + case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE: + vnc_direct_bgp_vpn_disable_ce(bgp, afi); + break; + } +} + +void vnc_export_bgp_prechange(struct bgp *bgp) +{ + vnc_export_bgp_disable(bgp, AFI_IP); + vnc_export_bgp_disable(bgp, AFI_IP6); +} + +void vnc_export_bgp_postchange(struct bgp *bgp) +{ + vnc_export_bgp_enable(bgp, AFI_IP); + vnc_export_bgp_enable(bgp, AFI_IP6); +} + +void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi) +{ + vnc_export_bgp_disable(bgp, afi); + vnc_export_bgp_enable(bgp, afi); +} diff --git a/bgpd/rfapi/vnc_export_bgp.h b/bgpd/rfapi/vnc_export_bgp.h new file mode 100644 index 0000000..9ea20b1 --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ +#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ + +#include "lib/zebra.h" +#include "lib/prefix.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" + + +extern void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi); + +extern void vnc_export_bgp_prechange(struct bgp *bgp); + +extern void vnc_export_bgp_postchange(struct bgp *bgp); + +extern void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi); + +extern void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi); + +#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ */ diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h new file mode 100644 index 0000000..8f5b613 --- /dev/null +++ b/bgpd/rfapi/vnc_export_bgp_p.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ +#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ + +#include "lib/zebra.h" +#include "lib/prefix.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" + +#include "rfapi_private.h" + +extern void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn, + struct bgp_path_info *bpi); + +extern void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn, + struct bgp_path_info *bpi); + +extern void vnc_direct_bgp_add_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn); + +extern void vnc_direct_bgp_del_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn); + +extern void vnc_direct_bgp_add_nve(struct bgp *bgp, + struct rfapi_descriptor *rfd); + +extern void vnc_direct_bgp_del_nve(struct bgp *bgp, + struct rfapi_descriptor *rfd); + +extern void vnc_direct_bgp_add_group(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg); + +extern void vnc_direct_bgp_del_group(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg); + +extern void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi); + + +extern void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, + struct peer *peer, struct attr *attr); + + +extern void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, + struct peer *peer); + +extern void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi); + +#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ */ diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c new file mode 100644 index 0000000..4b6baca --- /dev/null +++ b/bgpd/rfapi/vnc_export_table.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/memory.h" +#include "lib/vty.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" + +#include "bgpd/rfapi/vnc_export_table.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/vnc_debug.h" + +struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, + const struct prefix *p) +{ + struct agg_table *t = NULL; + struct agg_node *rn = NULL; + afi_t afi; + + if (!bgp || !bgp->rfapi) + return NULL; + + afi = family2afi(p->family); + assert(afi == AFI_IP || afi == AFI_IP6); + + switch (type) { + case EXPORT_TYPE_BGP: + if (!bgp->rfapi->rt_export_bgp[afi]) + bgp->rfapi->rt_export_bgp[afi] = agg_table_init(); + t = bgp->rfapi->rt_export_bgp[afi]; + break; + + case EXPORT_TYPE_ZEBRA: + if (!bgp->rfapi->rt_export_zebra[afi]) + bgp->rfapi->rt_export_zebra[afi] = agg_table_init(); + t = bgp->rfapi->rt_export_zebra[afi]; + break; + } + + if (t) + rn = agg_node_get(t, p); + return rn; +} + +struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type, + const struct prefix *p) +{ + struct agg_table *t = NULL; + struct agg_node *rn = NULL; + afi_t afi; + + if (!bgp || !bgp->rfapi) + return NULL; + + afi = family2afi(p->family); + assert(afi == AFI_IP || afi == AFI_IP6); + + switch (type) { + case EXPORT_TYPE_BGP: + if (!bgp->rfapi->rt_export_bgp[afi]) + bgp->rfapi->rt_export_bgp[afi] = agg_table_init(); + t = bgp->rfapi->rt_export_bgp[afi]; + break; + + case EXPORT_TYPE_ZEBRA: + if (!bgp->rfapi->rt_export_zebra[afi]) + bgp->rfapi->rt_export_zebra[afi] = agg_table_init(); + t = bgp->rfapi->rt_export_zebra[afi]; + break; + } + + if (t) + rn = agg_node_lookup(t, p); + return rn; +} + +struct vnc_export_info *vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, + const struct prefix *p, struct peer *peer, + uint8_t type, uint8_t subtype) +{ + struct agg_node *etn; + struct vnc_export_info *eti; + + etn = vnc_etn_get(bgp, etype, p); + assert(etn); + + for (eti = etn->info; eti; eti = eti->next) { + if (peer == eti->peer && type == eti->type + && subtype == eti->subtype) { + + break; + } + } + + if (eti) { + agg_unlock_node(etn); + } else { + eti = XCALLOC(MTYPE_RFAPI_ETI, sizeof(struct vnc_export_info)); + eti->node = etn; + eti->peer = peer; + peer_lock(peer); + eti->type = type; + eti->subtype = subtype; + eti->next = etn->info; + etn->info = eti; + } + + return eti; +} + +void vnc_eti_delete(struct vnc_export_info *goner) +{ + struct agg_node *etn; + struct vnc_export_info *eti; + struct vnc_export_info *eti_prev = NULL; + + etn = goner->node; + + for (eti = etn->info; eti; eti_prev = eti, eti = eti->next) { + if (eti == goner) + break; + } + + if (!eti) { + vnc_zlog_debug_verbose("%s: COULDN'T FIND ETI", __func__); + return; + } + + if (eti_prev) { + eti_prev->next = goner->next; + } else { + etn->info = goner->next; + } + + peer_unlock(eti->peer); + goner->node = NULL; + XFREE(MTYPE_RFAPI_ETI, goner); + + agg_unlock_node(etn); +} + +struct vnc_export_info *vnc_eti_checktimer(struct bgp *bgp, + vnc_export_type_t etype, + const struct prefix *p, + struct peer *peer, uint8_t type, + uint8_t subtype) +{ + struct agg_node *etn; + struct vnc_export_info *eti; + + etn = vnc_etn_lookup(bgp, etype, p); + if (!etn) + return NULL; + + for (eti = etn->info; eti; eti = eti->next) { + if (peer == eti->peer && type == eti->type + && subtype == eti->subtype) { + + break; + } + } + + agg_unlock_node(etn); + + if (eti && eti->timer) + return eti; + + return NULL; +} diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h new file mode 100644 index 0000000..f715de0 --- /dev/null +++ b/bgpd/rfapi/vnc_export_table.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ +#define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ + +#include "lib/table.h" +#include "frrevent.h" +#include "lib/vty.h" + +#include "bgpd/bgpd.h" + +#define VNC_EXPORT_TYPE_BGP 1 +#define VNC_EXPORT_TYPE_ZEBRA 2 + +typedef enum vnc_export_type { + EXPORT_TYPE_BGP, + EXPORT_TYPE_ZEBRA +} vnc_export_type_t; + +struct vnc_export_info { + struct vnc_export_info *next; + struct agg_node *node; + struct peer *peer; + uint8_t type; + uint8_t subtype; + uint32_t lifetime; + struct event *timer; +}; + +extern struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type, + const struct prefix *p); + +extern struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type, + const struct prefix *p); + +extern struct vnc_export_info * +vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, const struct prefix *p, + struct peer *peer, uint8_t type, uint8_t subtype); + +extern void vnc_eti_delete(struct vnc_export_info *goner); + +extern struct vnc_export_info * +vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype, + const struct prefix *p, struct peer *peer, uint8_t type, + uint8_t subtype); + + +#endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c new file mode 100644 index 0000000..3fcc0e6 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -0,0 +1,2893 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: vnc_import_bgp.c + * Purpose: Import routes from BGP unicast directly (not via zebra) + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/plist.h" +#include "lib/routemap.h" +#include "lib/lib_errors.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" /* for RD_TYPE_IP */ + +#include "bgpd/rfapi/vnc_export_bgp.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/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_import_bgp_p.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define ENABLE_VNC_RHNCK + +#define DEBUG_RHN_LIST 0 + +static struct rfapi_descriptor vncHDBgpDirect; /* dummy nve descriptor */ +static struct rfapi_descriptor vncHDResolveNve; /* dummy nve descriptor */ + +/* + * For routes from another AS: + * + * If MED is set, + * LOCAL_PREF = 255 - MIN(255, MED) + * else + * LOCAL_PREF = default_local_pref + * + * For routes from the same AS: + * + * LOCAL_PREF unchanged + */ +uint32_t calc_local_pref(struct attr *attr, struct peer *peer) +{ + uint32_t local_pref = 0; + + if (!attr) { + if (peer) { + return peer->bgp->default_local_pref; + } + return bgp_get_default()->default_local_pref; + } + + if (peer && (peer->as != peer->bgp->as)) { + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { + if (attr->med > 255) { + local_pref = 0; + } else { + local_pref = 255 - attr->med; + } + } else { + local_pref = peer->bgp->default_local_pref; + } + } else { + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + local_pref = attr->local_pref; + } else { + if (peer && peer->bgp) { + local_pref = peer->bgp->default_local_pref; + } + } + } + + return local_pref; +} + +static int is_host_prefix(const struct prefix *p) +{ + switch (p->family) { + case AF_INET: + return (p->prefixlen == IPV4_MAX_BITLEN); + case AF_INET6: + return (p->prefixlen == IPV6_MAX_BITLEN); + } + return 0; +} + +/*********************************************************************** + * RHN list + ***********************************************************************/ + +struct prefix_bag { + struct prefix hpfx; /* ce address = unicast nexthop */ + struct prefix upfx; /* unicast prefix */ + struct bgp_path_info *ubpi; /* unicast route */ +}; + +static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + +int vnc_prefix_cmp(const void *pfx1, const void *pfx2) +{ + int offset; + int shift; + uint8_t mask; + + const struct prefix *p1 = pfx1; + const struct prefix *p2 = pfx2; + + if (p1->family < p2->family) + return -1; + if (p1->family > p2->family) + return 1; + + if (p1->prefixlen < p2->prefixlen) + return -1; + if (p1->prefixlen > p2->prefixlen) + return 1; + + offset = p1->prefixlen / 8; + shift = p1->prefixlen % 8; + if (shift == 0 && offset) { /* catch aligned case */ + offset--; + shift = 8; + } + + /* Set both prefix's head pointer. */ + const uint8_t *pp1 = (const uint8_t *)&p1->u.prefix; + const uint8_t *pp2 = (const uint8_t *)&p2->u.prefix; + + while (offset--) { + if (*pp1 < *pp2) + return -1; + if (*pp1 > *pp2) + return 1; + ++pp1; + ++pp2; + } + + mask = maskbit[shift]; + if ((*pp1 & mask) < (*pp2 & mask)) + return -1; + if ((*pp1 & mask) > (*pp2 & mask)) + return 1; + + return 0; +} + +static void prefix_bag_free(void *pb) +{ + XFREE(MTYPE_RFAPI_PREFIX_BAG, pb); +} + +#if DEBUG_RHN_LIST +static void print_rhn_list(const char *tag1, const char *tag2) +{ + struct bgp *bgp; + struct skiplist *sl; + struct skiplistnode *p; + struct prefix_bag *pb; + int count = 0; + + bgp = bgp_get_default(); + if (!bgp) + return; + + sl = bgp->frapi->resolve_nve_nexthop; + if (!sl) { + vnc_zlog_debug_verbose("%s: %s: RHN List is empty", + (tag1 ? tag1 : ""), (tag2 ? tag2 : "")); + return; + } + + vnc_zlog_debug_verbose("%s: %s: RHN list:", (tag1 ? tag1 : ""), + (tag2 ? tag2 : "")); + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) { + pb = p->value; + + vnc_zlog_debug_verbose( + "RHN Entry %d (q=%p): kpfx=%pFX, upfx=%pFX, hpfx=%pFX, ubpi=%p", + ++count, p, p->key, &pb->upfx, &pb->hpfx, pb->ubpi); + } +} +#endif + +#ifdef ENABLE_VNC_RHNCK +static void vnc_rhnck(char *tag) +{ + struct bgp *bgp; + struct skiplist *sl; + struct skiplistnode *p; + + bgp = bgp_get_default(); + if (!bgp) + return; + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) + return; + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) { + struct prefix_bag *pb; + struct prefix *pkey; + afi_t afi; + struct prefix pfx_orig_nexthop; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + pkey = p->key; + pb = p->value; + + afi = family2afi(pb->upfx.family); + + rfapiUnicastNexthop2Prefix(afi, pb->ubpi->attr, + &pfx_orig_nexthop); + + /* pb->hpfx, pb->ubpi nexthop, pkey should all reflect the same + * pfx */ + assert(!vnc_prefix_cmp(&pb->hpfx, pkey)); + if (vnc_prefix_cmp(&pb->hpfx, &pfx_orig_nexthop)) { + vnc_zlog_debug_verbose( + "%s: %s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, tag, &pfx_orig_nexthop, &pb->hpfx); + assert(0); + } + } + vnc_zlog_debug_verbose("%s: vnc_rhnck OK", tag); +} + +#define VNC_RHNCK(n) \ + do { \ + char buf[BUFSIZ]; \ + snprintf(buf, sizeof(buf), "%s: %s", __func__, #n); \ + vnc_rhnck(buf); \ + } while (0) + +#else + +#define VNC_RHNCK(n) + +#endif + +/*********************************************************************** + * Add/Delete Unicast Route + ***********************************************************************/ + +/* + * "Adding a Route" import process + */ + +/* + * extract and package information from the BGP unicast route. + * Return code 0 means OK, non-0 means drop. + * + * If return code is 0, caller MUST release ecom + */ +static int process_unicast_route(struct bgp *bgp, /* in */ + afi_t afi, /* in */ + const struct prefix *prefix, /* in */ + struct bgp_path_info *info, /* in */ + struct ecommunity **ecom, /* OUT */ + struct prefix *unicast_nexthop) /* OUT */ +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct route_map *rmap = NULL; + struct prefix pfx_orig_nexthop; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose("%s: HC prefix list is set, checking", + __func__); + if (prefix_list_apply( + hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return -1; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Extract original nexthop, which we expect to be a NVE connected + * router + * Note that this is the nexthop before any possible application of + * policy + */ + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, attr, &pfx_orig_nexthop); + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(rmap, prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return -1; + } + } + + /* + * Get the (possibly altered by policy) unicast nexthop + * for later lookup in the Import Table by caller + */ + rfapiUnicastNexthop2Prefix(afi, &hattr, unicast_nexthop); + + if (bgp_attr_get_ecommunity(&hattr)) + *ecom = ecommunity_dup(bgp_attr_get_ecommunity(&hattr)); + else + *ecom = ecommunity_new(); + + /* + * Done with hattr, clean up + */ + bgp_attr_flush(&hattr); + + /* + * Add EC that carries original NH of iBGP route (2 bytes = magic + * value indicating it came from an VNC gateway; default 5226, but + * must be user configurable). Note that this is the nexthop before + * any application of policy. + */ + { + struct ecommunity_val vnc_gateway_magic; + uint16_t localadmin; + + /* Using route origin extended community type */ + memset(&vnc_gateway_magic, 0, sizeof(vnc_gateway_magic)); + vnc_gateway_magic.val[0] = 0x01; + vnc_gateway_magic.val[1] = 0x03; + + /* Only works for IPv4 nexthops */ + if (prefix->family == AF_INET) { + memcpy(vnc_gateway_magic.val + 2, + &unicast_nexthop->u.prefix4, 4); + } + localadmin = htons(hc->resolve_nve_roo_local_admin); + memcpy(vnc_gateway_magic.val + 6, (char *)&localadmin, 2); + + ecommunity_add_val(*ecom, &vnc_gateway_magic, false, false); + } + + return 0; +} + + +static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ + struct prefix_rd *prd, /* RD */ + const struct prefix *prefix, /* unicast route prefix */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct ecommunity *ecom) /* generated ecoms */ +{ + struct prefix un; + struct prefix nexthop; + struct rfapi_ip_addr nexthop_h; + uint32_t lifetime; + uint32_t *plifetime; + struct bgp_attr_encap_subtlv *encaptlvs; + uint32_t label = 0; + + struct rfapi_un_option optary[3]; + struct rfapi_un_option *opt = NULL; + int cur_opt = 0; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (bpi->type != ZEBRA_ROUTE_BGP + && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) { + + return; + } + if (bpi->sub_type != BGP_ROUTE_NORMAL + && bpi->sub_type != BGP_ROUTE_STATIC + && bpi->sub_type != BGP_ROUTE_RFP) { + + return; + } + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + return; + + vncHDResolveNve.peer = bpi->peer; + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) { + if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr)) + return; + } else { + memset(&vncHDResolveNve.un_addr, 0, + sizeof(vncHDResolveNve.un_addr)); + } + + /* Use nexthop of VPN route as nexthop of constructed route */ + rfapiNexthop2Prefix(bpi->attr, &nexthop); + rfapiQprefix2Raddr(&nexthop, &nexthop_h); + + if (rfapiGetVncLifetime(bpi->attr, &lifetime)) { + plifetime = NULL; + } else { + plifetime = &lifetime; + } + + encaptlvs = bgp_attr_get_vnc_subtlvs(bpi->attr); + if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED + && bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) { + opt = &optary[cur_opt++]; + memset(opt, 0, sizeof(struct rfapi_un_option)); + opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; + opt->v.tunnel.type = bpi->attr->encap_tunneltype; + /* TBD parse bpi->attr->extra->encap_subtlvs */ + } + + struct ecommunity *new_ecom = ecommunity_dup(ecom); + + if (bgp_attr_get_ecommunity(bpi->attr)) + ecommunity_merge(new_ecom, bgp_attr_get_ecommunity(bpi->attr)); + + if (bpi->extra) + label = decode_label(&bpi->extra->label[0]); + + add_vnc_route(&vncHDResolveNve, bgp, SAFI_MPLS_VPN, + prefix, /* unicast route prefix */ + prd, &nexthop_h, /* new nexthop */ + local_pref, plifetime, + (struct bgp_tea_options *)encaptlvs, /* RFP options */ + opt, NULL, new_ecom, med, /* NULL => don't set med */ + (label ? &label : NULL), /* NULL= default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + RFAPI_AHR_RFPOPT_IS_VNCTLV); /* flags */ + + ecommunity_free(&new_ecom); +} + +static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + struct ecommunity *ecom, /* generated ecoms */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct prefix *ubpi_nexthop) /* unicast nexthop */ +{ + struct bgp_dest *bd; + struct bgp_path_info *bpi; + + if (!table_rd) + return; + + vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop); + + /* exact match */ + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { + vnc_zlog_debug_verbose( + "%s: no match in RD's table for ubpi_nexthop", + __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, prefix, local_pref, med, ecom); + } + + bgp_dest_unlock_node(bd); +} + +static void vnc_import_bgp_add_route_mode_resolve_nve( + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + afi_t afi = family2afi(prefix->family); + + struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */ + + struct ecommunity *ecom = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + struct prefix_bag *pb; + struct bgp_dest *bdp; /* prd table node */ + + /*debugging */ + if (VNC_DEBUG(VERBOSE)) { + char str_nh[PREFIX_STRLEN]; + struct prefix nh; + + nh.prefixlen = 0; + rfapiUnicastNexthop2Prefix(afi, info->attr, &nh); + if (nh.prefixlen) { + prefix2str(&nh, str_nh, sizeof(str_nh)); + } else { + str_nh[0] = '?'; + str_nh[1] = 0; + } + + vnc_zlog_debug_verbose( + "%s(bgp=%p, unicast prefix=%pFX, unicast nh=%s)", + __func__, bgp, prefix, str_nh); + } + + if (info->type != ZEBRA_ROUTE_BGP) { + vnc_zlog_debug_verbose( + "%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string(info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + /* + * Preliminary checks + */ + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + if (process_unicast_route(bgp, afi, prefix, info, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", __func__); + return; + } + + local_pref = calc_local_pref(info->attr, info->peer); + if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + med = &info->attr->med; + + /* + * At this point, we have allocated: + * + * ecom ecommunity ptr, union of unicast and ROO parts (no NVE part) + * + * And we have set: + * + * pfx_unicast_nexthop nexthop of uncast route + */ + + if (!bgp->rfapi->resolve_nve_nexthop) { + bgp->rfapi->resolve_nve_nexthop = + skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, + vnc_prefix_cmp, prefix_bag_free); + } + + pb = XCALLOC(MTYPE_RFAPI_PREFIX_BAG, sizeof(struct prefix_bag)); + pb->hpfx = pfx_unicast_nexthop; + pb->ubpi = info; + pb->upfx = *prefix; + + bgp_path_info_lock(info); /* skiplist refers to it */ + skiplist_insert(bgp->rfapi->resolve_nve_nexthop, &pb->hpfx, pb); + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { + + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(bdp); + + if (!table) + continue; + + vnc_import_bgp_add_route_mode_resolve_nve_one_rd( + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, ecom, &local_pref, med, + &pfx_unicast_nexthop); + } + + + if (ecom) + ecommunity_free(&ecom); + + vnc_zlog_debug_verbose("%s: done", __func__); +} + + +static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + afi_t afi = family2afi(prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!hc) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + /* + * mode "plain" specific code + */ + { + vnc_zlog_debug_verbose("%s: NOT using redist RFG", __func__); + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose( + "%s: HC prefix list is set, checking", + __func__); + if (prefix_list_apply( + hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT] + [afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol + * area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, attr, &vn_pfx_space); + vn_pfx = &vn_pfx_space; + + /* UN address */ + ahr_flags |= RFAPI_AHR_NO_TUNNEL_SUBTLV; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx); + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) { + vnc_zlog_debug_verbose("%s: redist VN invalid, skipping", + __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(rmap, prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * Mode "plain" specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + if (vnaddr.addr_family != AF_INET) { + vnc_zlog_debug_verbose( + "%s: can't auto-assign RD, VN AF (%d) is not IPv4, skipping", + __func__, vnaddr.addr_family); + if (iattr) { + bgp_attr_unintern(&iattr); + } + return; + } + memset(&prd, 0, sizeof(prd)); + rfapi_set_autord_from_vn(&prd, &vnaddr); + + if (iattr && bgp_attr_get_ecommunity(iattr)) + ecom = ecommunity_dup(bgp_attr_get_ecommunity(iattr)); + } + + local_pref = calc_local_pref(iattr, peer); + + if (iattr && (iattr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) { + med = &iattr->med; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { + char buf[PREFIX_STRLEN]; + + rfapiRfapiIpAddr2Str(&vnaddr, buf, sizeof(buf)); + vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, + &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), + NULL, /* RFP options */ + NULL, NULL, ecom, med, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free(&ecom); + if (iattr) + bgp_attr_unintern(&iattr); +} + +static void vnc_import_bgp_add_route_mode_nvegroup( + struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info, struct rfapi_nve_group_cfg *rfg) +{ + afi_t afi = family2afi(prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + + vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix); + + assert(rfg); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + /* + * RFG-specific code + */ + { + + struct rfapi_ip_prefix pfx_un; + + vnc_zlog_debug_verbose("%s: using redist RFG", __func__); + + /* + * RFG prefix list check + */ + if (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose( + "%s: RFG prefix list is set, checking", + __func__); + if (prefix_list_apply( + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT] + [afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + /* + * UN Address + */ + if (!is_host_prefix(&rfg->un_prefix)) { + /* NB prefixlen==0 means it has not been configured */ + vnc_zlog_debug_verbose( + "%s: redist RFG UN pfx not host pfx (plen=%d), skipping", + __func__, rfg->un_prefix.prefixlen); + return; + } + + rfapiQprefix2Rprefix(&rfg->un_prefix, &pfx_un); + + vncHDBgpDirect.un_addr = pfx_un.prefix; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx); + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) { + vnc_zlog_debug_verbose("%s: redist VN invalid, skipping", + __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info path; + route_map_result_t ret; + + memset(&path, 0, sizeof(path)); + path.peer = peer; + path.attr = &hattr; + ret = route_map_apply(rmap, prefix, &path); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * RFG-specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + + memset(&prd, 0, sizeof(prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) { + rfapi_set_autord_from_vn(&prd, &vnaddr); + } + + if (rfg->rt_export_list) + ecom = ecommunity_dup( + bgp->rfapi_cfg->rfg_redist->rt_export_list); + else + ecom = ecommunity_new(); + + if (iattr && bgp_attr_get_ecommunity(iattr)) + ecom = ecommunity_merge(ecom, + bgp_attr_get_ecommunity(iattr)); + } + + local_pref = calc_local_pref(iattr, peer); + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { + char buf[BUFSIZ]; + + buf[0] = 0; + rfapiRfapiIpAddr2Str(&vnaddr, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, + &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), + NULL, /* RFP options */ + NULL, NULL, ecom, NULL, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free(&ecom); + if (iattr) + bgp_attr_unintern(&iattr); +} + +static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi(prefix->family); + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + + + assert(afi); + + /* + * Compute VN address + */ + + if (info) { + rfapiUnicastNexthop2Prefix(afi, info->attr, &vn_pfx_space); + } else { + vnc_zlog_debug_verbose("%s: no attr, can't delete route", + __func__); + return; + } + vn_pfx = &vn_pfx_space; + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) { + case AF_INET: + if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + vnc_zlog_debug_verbose( + "%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + + memset(&prd, 0, sizeof(prd)); + if (rfapi_set_autord_from_vn(&prd, &vnaddr)) { + vnc_zlog_debug_verbose("%s: can't auto-assign RD, skipping", + __func__); + return; + } + + vncHDBgpDirect.peer = info->peer; + vnc_zlog_debug_verbose("%s: setting peer to %p", __func__, + vncHDBgpDirect.peer); + del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix, + &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi(prefix->family); + struct rfapi_nve_group_cfg *rfg = NULL; + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + + + assert(afi); + + rfg = bgp->rfapi_cfg->rfg_redist; + assert(rfg); + + /* + * Compute VN address + */ + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) { + case AF_INET: + if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + vnc_zlog_debug_verbose( + "%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + memset(&prd, 0, sizeof(prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) { + /* means "auto" with VN addr */ + if (rfapi_set_autord_from_vn(&prd, &vnaddr)) { + vnc_zlog_debug_verbose( + "%s: can't auto-assign RD, skipping", __func__); + return; + } + } + + + vncHDBgpDirect.peer = info->peer; + vnc_zlog_debug_verbose("%s: setting peer to %p", __func__, + vncHDBgpDirect.peer); + del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix, + &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ + struct prefix_rd *prd, /* RD */ + const struct prefix *prefix) /* unicast route prefix */ +{ + struct prefix un; + + if (bpi->type != ZEBRA_ROUTE_BGP + && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) { + + return; + } + if (bpi->sub_type != BGP_ROUTE_NORMAL + && bpi->sub_type != BGP_ROUTE_STATIC + && bpi->sub_type != BGP_ROUTE_RFP) { + + return; + } + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + return; + + vncHDResolveNve.peer = bpi->peer; + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) { + if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr)) + return; + } else { + memset(&vncHDResolveNve.un_addr, 0, + sizeof(vncHDResolveNve.un_addr)); + } + + del_vnc_route(&vncHDResolveNve, vncHDResolveNve.peer, bgp, + SAFI_MPLS_VPN, prefix, /* unicast route prefix */ + prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, + 0); /* flags */ +} + +static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( + struct prefix_rd *prd, + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + const struct prefix *ubpi_nexthop) /* unicast bpi's nexthop */ +{ + struct bgp_dest *bd; + struct bgp_path_info *bpi; + + if (!table_rd) + return; + + vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop); + + + /* exact match */ + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { + vnc_zlog_debug_verbose( + "%s: no match in RD's table for ubpi_nexthop", + __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, /* VPN RD */ + prefix); /* unicast route prefix */ + } + + bgp_dest_unlock_node(bd); +} + +static void +vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct ecommunity *ecom = NULL; + struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */ + + // struct listnode *hnode; + // struct rfapi_descriptor *rfd; + struct prefix_bag *pb; + void *cursor; + struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop; + int rc; + struct bgp_dest *bdp; /* prd table node */ + + if (!sl) { + vnc_zlog_debug_verbose("%s: no RHN entries, skipping", + __func__); + return; + } + + if (info->type != ZEBRA_ROUTE_BGP) { + vnc_zlog_debug_verbose( + "%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string(info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + if (process_unicast_route(bgp, afi, prefix, info, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", __func__); + return; + } + + rc = skiplist_first_value(sl, &pfx_unicast_nexthop, (void *)&pb, + &cursor); + while (!rc) { + if (pb->ubpi == info) { + skiplist_delete(sl, &pfx_unicast_nexthop, pb); + bgp_path_info_unlock(info); + break; + } + rc = skiplist_next_value(sl, &pfx_unicast_nexthop, (void *)&pb, + &cursor); + } + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { + + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(bdp); + + if (!table) + continue; + + vnc_import_bgp_del_route_mode_resolve_nve_one_rd( + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, &pfx_unicast_nexthop); + } + + if (ecom) + ecommunity_free(&ecom); +} + + +/*********************************************************************** + * Add/Delete CE->NVE routes + ***********************************************************************/ + +/* + * Should be called whan a bpi is added to VPN RIB. This function + * will check if it is a host route and return immediately if not. + */ +void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi) /* new VPN host route */ +{ + afi_t afi = family2afi(prefix->family); + struct skiplist *sl = NULL; + int rc; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi %d, skipping", __func__, + afi); + return; + } + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) { + vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping", + __func__); + return; + } + + if (bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) { + vnc_zlog_debug_verbose( + "%s: no resolve_nve_nexthop skiplist, skipping", + __func__); + return; + } + + if (!is_host_prefix(prefix)) { + vnc_zlog_debug_verbose("%s: not host prefix, skipping", + __func__); + return; + } + + rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor); + while (!rc) { + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + uint32_t *med = NULL; + uint32_t local_pref; + + memset(&pfx_unicast_nexthop, 0, + sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */ + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any( + "%s: examining RHN Entry (q=%p): upfx=%pFX, hpfx=%pFX, ubpi=%p", + __func__, cursor, &pb->upfx, &pb->hpfx, + pb->ubpi); + + if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", + __func__); + continue; + } + local_pref = calc_local_pref(pb->ubpi->attr, pb->ubpi->peer); + + if (pb->ubpi->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + med = &pb->ubpi->attr->med; + + /* + * Sanity check + */ + if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { + vnc_zlog_debug_verbose( + "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, &pfx_unicast_nexthop, prefix); + assert(0); + } + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, &pb->upfx, /* unicast prefix */ + &local_pref, med, ecom); + + if (ecom) + ecommunity_free(&ecom); + +#if DEBUG_RHN_LIST + /* debug */ + { + vnc_zlog_debug_verbose( + "%s: advancing past RHN Entry (q=%p): with prefix %pFX", + __func__, cursor, prefix); + print_rhn_list(__func__, NULL); /* debug */ + } +#endif + rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor); + } + vnc_zlog_debug_verbose("%s: done", __func__); +} + + +void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi) /* old VPN host route */ +{ + afi_t afi = family2afi(prefix->family); + struct skiplist *sl = NULL; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + int rc; + + vnc_zlog_debug_verbose("%s(bgp=%p, nve prefix=%pFX)", __func__, bgp, + prefix); + + if (afi != AFI_IP && afi != AFI_IP6) + return; + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) { + vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping", + __func__); + return; + } + + if (bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) { + vnc_zlog_debug_verbose("%s: no RHN entries, skipping", + __func__); + return; + } + + if (!is_host_prefix(prefix)) { + vnc_zlog_debug_verbose("%s: not host route, skip", __func__); + return; + } + + /* + * Find all entries with key == CE in the RHN list + */ + rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor); + while (!rc) { + + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + + memset(&pfx_unicast_nexthop, 0, + sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */ + + if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", + __func__); + continue; + } + + /* + * Sanity check + */ + if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { + vnc_zlog_debug_verbose( + "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, &pfx_unicast_nexthop, prefix); + assert(0); + } + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, prd, &pb->upfx); + + if (ecom) + ecommunity_free(&ecom); + + rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor); + } +} + + +/*********************************************************************** + * Exterior Routes + ***********************************************************************/ + +#define DEBUG_IS_USABLE_INTERIOR 1 + +static int is_usable_interior_route(struct bgp_path_info *bpi_interior) +{ + if (!VALID_INTERIOR_TYPE(bpi_interior->type)) { +#if DEBUG_IS_USABLE_INTERIOR + vnc_zlog_debug_verbose( + "%s: NO: type %d is not valid interior type", __func__, + bpi_interior->type); +#endif + return 0; + } + if (!CHECK_FLAG(bpi_interior->flags, BGP_PATH_VALID)) { +#if DEBUG_IS_USABLE_INTERIOR + vnc_zlog_debug_verbose("%s: NO: BGP_PATH_VALID not set", + __func__); +#endif + return 0; + } + return 1; +} + +/* + * There should be only one of these per prefix at a time. + * This should be called as a result of selection operation + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +static void vnc_import_bgp_exterior_add_route_it( + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info, /* unicast info */ + struct rfapi_import_table *it_only) /* NULL, or limit to this IT */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct prefix pfx_orig_nexthop; + struct rfapi_import_table *it; + struct bgp *bgp_default = bgp_get_default(); + afi_t afi = family2afi(prefix->family); + + if (!bgp_default) + return; + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + vnc_zlog_debug_verbose("%s: entry with it=%p", __func__, it_only); + + if (!h || !hc) { + vnc_zlog_debug_verbose( + "%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose("%s: exterior view not set, skipping", + __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose( + "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes not enabled, skipping", + __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) { + struct agg_table *table; + struct agg_node *rn; + struct agg_node *par; + struct bgp_path_info *bpi_interior; + int have_usable_route; + + vnc_zlog_debug_verbose("%s: doing it %p", __func__, it); + + if (it_only && (it_only != it)) { + vnc_zlog_debug_verbose("%s: doesn't match it_only %p", + __func__, it_only); + continue; + } + + table = it->imported_vpn[afi]; + + for (rn = agg_node_match(table, &pfx_orig_nexthop), + have_usable_route = 0; + (!have_usable_route) && rn;) { + + vnc_zlog_debug_verbose("%s: it %p trying rn %p", + __func__, it, rn); + + for (bpi_interior = rn->info; bpi_interior; + bpi_interior = bpi_interior->next) { + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + if (!is_usable_interior_route(bpi_interior)) + continue; + + vnc_zlog_debug_verbose( + "%s: usable: bpi_interior %p", __func__, + bpi_interior); + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(new_attr)); + new_attr = *bpi_interior->attr; + if (info->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + new_attr.local_pref = + info->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, + bpi_interior->peer, NULL, /* rfd */ + prefix, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + if (have_usable_route) { + /* + * Make monitor + * + * TBD factor this out into its own function + */ + struct prefix *pfx_mon = prefix_new(); + if (!RFAPI_MONITOR_EXTERIOR(rn)->source) { + RFAPI_MONITOR_EXTERIOR(rn)->source = + skiplist_new( + 0, NULL, + prefix_free_lists); + agg_lock_node(rn); /* for skiplist */ + } + agg_lock_node(rn); /* for skiplist entry */ + prefix_copy(pfx_mon, prefix); + if (!skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn)->source, + info, pfx_mon)) { + + bgp_path_info_lock(info); + } + } + par = agg_node_parent(rn); + if (par) + agg_lock_node(par); + agg_unlock_node(rn); + rn = par; + } + if (rn) + agg_unlock_node(rn); + + if (!have_usable_route) { + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, prefix); + if (!skiplist_insert(it->monitor_exterior_orphans, info, + pfx_mon)) { + + bgp_path_info_lock(info); + } + } + } +} + +void vnc_import_bgp_exterior_add_route( + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + vnc_import_bgp_exterior_add_route_it(bgp, prefix, info, NULL); +} + +/* + * There should be only one of these per prefix at a time. + * This should probably be called as a result of selection operation. + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +void vnc_import_bgp_exterior_del_route( + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct rfapi_import_table *it; + struct prefix pfx_orig_nexthop; + afi_t afi = family2afi(prefix->family); + struct bgp *bgp_default = bgp_get_default(); + + if (!bgp_default) + return; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + if (!h || !hc) { + vnc_zlog_debug_verbose( + "%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose("%s: exterior view not set, skipping", + __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose( + "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) { + struct agg_table *table; + struct agg_node *rn; + struct agg_node *par; + struct bgp_path_info *bpi_interior; + int have_usable_route; + + table = it->imported_vpn[afi]; + + for (rn = agg_node_match(table, &pfx_orig_nexthop), + have_usable_route = 0; + (!have_usable_route) && rn;) { + + for (bpi_interior = rn->info; bpi_interior; + bpi_interior = bpi_interior->next) { + struct prefix_rd *prd; + uint32_t label = 0; + + if (!is_usable_interior_route(bpi_interior)) + continue; + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi_interior->peer, + NULL, /* rfd */ + prefix, NULL, afi, prd, + bpi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + /* + * Delete monitor + * + * TBD factor this out into its own function + */ + { + if (RFAPI_MONITOR_EXTERIOR(rn) + ->source) { + if (!skiplist_delete( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source, + info, NULL)) { + + bgp_path_info_unlock( + info); + agg_unlock_node( + rn); /* sl entry + */ + } + if (skiplist_empty( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source)) { + skiplist_free( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source); + RFAPI_MONITOR_EXTERIOR( + rn) + ->source = NULL; + agg_unlock_node( + rn); /* skiplist + itself + */ + } + } + } + } + par = agg_node_parent(rn); + if (par) + agg_lock_node(par); + agg_unlock_node(rn); + rn = par; + } + if (rn) + agg_unlock_node(rn); + + if (!have_usable_route) { + if (!skiplist_delete(it->monitor_exterior_orphans, info, + NULL)) { + + bgp_path_info_unlock(info); + } + } + } +} + +/* + * This function should be called after a new interior VPN route + * has been added to an import_table. + * + * NB should also be called whenever an existing vpn interior route + * becomes valid (e.g., valid_interior_count is inremented) + */ +void vnc_import_bgp_exterior_add_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior) /* VPN IT route */ +{ + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); + struct agg_node *par; + struct bgp_path_info *bpi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + struct list *list_adopted; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!is_usable_interior_route(bpi_interior)) { + vnc_zlog_debug_verbose( + "%s: not usable interior route, skipping", __func__); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) { + vnc_zlog_debug_verbose("%s: import table is it_ce, skipping", + __func__); + return; + } + + /*debugging */ + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); + + if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { + + vnc_zlog_debug_verbose( + "%s: has exterior monitor; ext src: %p", __func__, + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + + /* + * There is a monitor here already. Therefore, we do not need + * to do any pulldown. Just construct exterior routes based + * on the new interior route. + */ + cursor = NULL; + for (rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + assert(bpi_exterior); + assert(pfx_exterior); + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi_interior->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + vnc_zlog_debug_verbose( + "%s: finished constructing exteriors based on existing monitors", + __func__); + return; + } + + vnc_zlog_debug_verbose("%s: no exterior monitor", __func__); + + /* + * No monitor at this node. Is this the first valid interior + * route at this node? + */ + if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count > 1) { + vnc_zlog_debug_verbose( + "%s: new interior route not first valid one, skipping pulldown", + __func__); + return; + } + + /* + * Look up the tree for possible pulldown candidates. + * Find nearest parent with an exterior route monitor + */ + for (par = agg_node_parent(rn_interior); par; + par = agg_node_parent(par)) { + if (RFAPI_HAS_MONITOR_EXTERIOR(par)) + break; + } + + if (par) { + + vnc_zlog_debug_verbose( + "%s: checking parent %p for possible pulldowns", + __func__, par); + + /* check monitors at par for possible pulldown */ + cursor = NULL; + for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor); + !rc; + rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix pfx_nexthop; + + memset(&pfx_nexthop, 0, + sizeof(struct prefix)); /* keep valgrind happy */ + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, + &pfx_nexthop); + + if (prefix_match(p, &pfx_nexthop)) { + + struct bgp_path_info *bpi; + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source) { + RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source = skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(rn_interior); + } + skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source, + bpi_exterior, pfx_mon); + agg_lock_node(rn_interior); + + /* + * Delete constructed exterior routes based on + * parent routes. + */ + for (bpi = par->info; bpi; bpi = bpi->next) { + + if (bpi->extra) { + prd = &bpi->extra->vnc.import + .rd; + label = decode_label( + &bpi->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, + bpi->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + + /* + * Add constructed exterior routes based on + * the new interior route at longer prefix. + */ + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, + bpi_interior->peer, NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + } + + /* + * The only monitors at rn_interior are the ones we added just + * above, so we can use the rn_interior list to identify which + * monitors to delete from the parent. + */ + cursor = NULL; + for (rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, NULL, &cursor); + !rc; rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, NULL, &cursor)) { + + + skiplist_delete(RFAPI_MONITOR_EXTERIOR(par)->source, + bpi_exterior, NULL); + agg_unlock_node(par); /* sl entry */ + } + if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(par)->source)) { + skiplist_free(RFAPI_MONITOR_EXTERIOR(par)->source); + RFAPI_MONITOR_EXTERIOR(par)->source = NULL; + agg_unlock_node(par); /* sl itself */ + } + } + + vnc_zlog_debug_verbose("%s: checking orphans", __func__); + + /* + * See if any orphans can be pulled down to the current node + */ + cursor = NULL; + list_adopted = NULL; + for (rc = skiplist_next(it->monitor_exterior_orphans, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; rc = skiplist_next(it->monitor_exterior_orphans, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix pfx_nexthop; + afi_t afi_exterior = family2afi(pfx_exterior->family); + + vnc_zlog_debug_verbose( + "%s: checking exterior orphan at prefix %pFX", __func__, + pfx_exterior); + + if (afi_exterior != afi) { + vnc_zlog_debug_verbose( + "%s: exterior orphan afi %d != interior afi %d, skip", + __func__, afi_exterior, afi); + continue; + } + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, + &pfx_nexthop); + + if (prefix_match(p, &pfx_nexthop)) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR(rn_interior)->source) { + RFAPI_MONITOR_EXTERIOR(rn_interior)->source = + skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(rn_interior); /* sl */ + } + skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + bpi_exterior, pfx_mon); + agg_lock_node(rn_interior); /* sl entry */ + if (!list_adopted) { + list_adopted = list_new(); + } + listnode_add(list_adopted, bpi_exterior); + + /* + * Add constructed exterior routes based on the + * new interior route at the longer prefix. + */ + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi_interior->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + } + if (list_adopted) { + struct listnode *node; + struct agg_node *an_bpi_exterior; + + for (ALL_LIST_ELEMENTS_RO(list_adopted, node, + an_bpi_exterior)) { + skiplist_delete(it->monitor_exterior_orphans, + an_bpi_exterior, NULL); + } + list_delete(&list_adopted); + } +} + +/* + * This function should be called after an interior VPN route + * has been deleted from an import_table. + * bpi_interior must still be valid, but it must already be detached + * from its route node and the route node's valid_interior_count + * must already be decremented. + * + * NB should also be called whenever an existing vpn interior route + * becomes invalid (e.g., valid_interior_count is decremented) + */ +void vnc_import_bgp_exterior_del_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior) /* VPN IT route */ +{ + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); + struct agg_node *par; + struct bgp_path_info *bpi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + + if (!VALID_INTERIOR_TYPE(bpi_interior->type)) { + vnc_zlog_debug_verbose( + "%s: type %d not valid interior type, skipping", + __func__, bpi_interior->type); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) { + vnc_zlog_debug_verbose("%s: it is it_ce, skipping", __func__); + return; + } + + /* If no exterior routes depend on this prefix, nothing to do */ + if (!RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { + vnc_zlog_debug_verbose("%s: no exterior monitor, skipping", + __func__); + return; + } + + /*debugging */ + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); + + /* + * Remove constructed routes based on the deleted interior route + */ + cursor = NULL; + for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; + rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor)) { + + struct prefix_rd *prd; + uint32_t label = 0; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label(&bpi_interior->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi_interior->peer, NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, bpi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, BGP_ROUTE_REDISTRIBUTE, + &label); + } + + /* + * If there are no remaining valid interior routes at this prefix, + * we need to look up the tree for a possible node to move monitors to + */ + if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count) { + vnc_zlog_debug_verbose( + "%s: interior routes still present, skipping", + __func__); + return; + } + + /* + * Find nearest parent with at least one valid interior route + * If none is found, par will end up NULL, and we will move + * the monitors to the orphan list for this import table + */ + for (par = agg_node_parent(rn_interior); par; + par = agg_node_parent(par)) { + if (RFAPI_MONITOR_EXTERIOR(par)->valid_interior_count) + break; + } + + vnc_zlog_debug_verbose("%s: par=%p, ext src: %p", __func__, par, + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + + /* move all monitors */ + /* + * We will use and delete every element of the source skiplist + */ + while (!skiplist_first(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior)) { + + struct prefix *pfx_mon = prefix_new(); + + prefix_copy(pfx_mon, pfx_exterior); + + if (par) { + + struct bgp_path_info *bpi; + + /* + * Add monitor to parent node + */ + if (!RFAPI_MONITOR_EXTERIOR(par)->source) { + RFAPI_MONITOR_EXTERIOR(par)->source = + skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(par); /* sl */ + } + skiplist_insert(RFAPI_MONITOR_EXTERIOR(par)->source, + bpi_exterior, pfx_mon); + agg_lock_node(par); /* sl entry */ + + /* Add constructed exterior routes based on parent */ + for (bpi = par->info; bpi; bpi = bpi->next) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + continue; + + if (bpi->extra) { + prd = &bpi->extra->vnc.import.rd; + label = decode_label( + &bpi->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(new_attr)); + new_attr = *bpi->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + } else { + + /* + * No interior route for exterior's nexthop. Save + * monitor + * in orphan list to await future route. + */ + skiplist_insert(it->monitor_exterior_orphans, + bpi_exterior, pfx_mon); + } + + skiplist_delete_first( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + agg_unlock_node(rn_interior); /* sl entry */ + } + if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(rn_interior)->source)) { + skiplist_free(RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + RFAPI_MONITOR_EXTERIOR(rn_interior)->source = NULL; + agg_unlock_node(rn_interior); /* sl itself */ + } +} + +/*********************************************************************** + * Generic add/delete unicast routes + ***********************************************************************/ + +void vnc_import_bgp_add_route(struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info) +{ + afi_t afi = family2afi(prefix->family); + + if (VNC_DEBUG(VERBOSE)) { + struct prefix pfx_nexthop; + + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); + vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__, + prefix, &pfx_nexthop); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK(enter); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_add_route_mode_plain(bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_add_route_mode_nvegroup( + bgp, prefix, info, bgp->rfapi_cfg->rfg_redist); + else + vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG", + __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_add_route_mode_resolve_nve(bgp, prefix, info); + break; + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK(leave); +} + +/* + * "Withdrawing a Route" import process + */ +void vnc_import_bgp_del_route(struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info) /* unicast info */ +{ + afi_t afi = family2afi(prefix->family); + + assert(afi); + + { + struct prefix pfx_nexthop; + + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); + vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__, + prefix, &pfx_nexthop); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK(enter); + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of afi=%d VNC direct routes is off", + __func__, afi); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_del_route_mode_plain(bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_del_route_mode_nvegroup(bgp, prefix, + info); + else + vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG", + __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_del_route_mode_resolve_nve(bgp, afi, prefix, + info); + break; + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK(leave); +} + + +/*********************************************************************** + * Enable/Disable + ***********************************************************************/ + +void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) +{ + /* iterate over bgp unicast v4 and v6 routes, call + * vnc_import_bgp_add_route */ + + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: already enabled for afi %d, skipping", __func__, + afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1; + + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_add_route(bgp, bgp_dest_get_prefix(dest), + bpi); + } + } + vnc_zlog_debug_verbose( + "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); +} + +void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi) +{ + struct bgp *bgp_exterior; + struct bgp_dest *dest; + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: already enabled for afi %d, skipping", __func__, + afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 1; + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route( + bgp_exterior, bgp_dest_get_prefix(dest), bpi); + } + } + vnc_zlog_debug_verbose( + "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); +} + +/* + * This function is for populating a newly-created Import Table + */ +void vnc_import_bgp_exterior_redist_enable_it( + struct bgp *bgp, afi_t afi, struct rfapi_import_table *it_only) +{ + struct bgp *bgp_exterior; + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose("%s: not enabled for afi %d, skipping", + __func__, afi); + return; + } + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route_it( + bgp_exterior, bgp_dest_get_prefix(dest), bpi, + it_only); + } + } +} + + +void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) +{ + /* + * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT, + * delete (call timer expire immediately) + */ + struct bgp_dest *dest1; + struct bgp_dest *dest2; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: already disabled for afi %d, skipping", __func__, + afi); + return; + } + + /* + * Two-level table for SAFI_MPLS_VPN + * Be careful when changing the things we iterate over + */ + for (dest1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); dest1; + dest1 = bgp_route_next(dest1)) { + const struct prefix *dest1_p; + + if (!bgp_dest_has_bgp_path_info_data(dest1)) + continue; + + dest1_p = bgp_dest_get_prefix(dest1); + for (dest2 = bgp_table_top(bgp_dest_get_bgp_table_info(dest1)); + dest2; dest2 = bgp_route_next(dest2)) { + const struct prefix *dest2_p = + bgp_dest_get_prefix(dest2); + struct bgp_path_info *bpi; + struct bgp_path_info *nextbpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest2); bpi; + bpi = nextbpi) { + + nextbpi = bpi->next; + + if (bpi->type != ZEBRA_ROUTE_BGP_DIRECT) + continue; + + struct rfapi_descriptor *rfd; + vncHDBgpDirect.peer = bpi->peer; + + assert(bpi->extra); + + rfd = bpi->extra->vnc.export.rfapi_handle; + + vnc_zlog_debug_verbose( + "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + __func__, bpi, bpi->peer, bpi->type, + bpi->sub_type, + (bpi->extra ? bpi->extra->vnc.export + .rfapi_handle + : NULL), + rfd); + + del_vnc_route(rfd, bpi->peer, bgp, + SAFI_MPLS_VPN, dest2_p, + (struct prefix_rd *)dest1_p, + bpi->type, bpi->sub_type, NULL, + 1); /* kill */ + + vncHDBgpDirect.peer = NULL; + } + } + } + /* Clear RHN list */ + if (bgp->rfapi->resolve_nve_nexthop) { + struct prefix_bag *pb; + struct bgp_path_info *info; + while (!skiplist_first(bgp->rfapi->resolve_nve_nexthop, NULL, + (void *)&pb)) { + info = pb->ubpi; + skiplist_delete_first(bgp->rfapi->resolve_nve_nexthop); + bgp_path_info_unlock(info); + } + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 0; + vnc_zlog_debug_verbose("%s: return", __func__); +} + + +void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi) +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct bgp *bgp_exterior = hc->redist_bgp_exterior_view; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: already disabled for afi %d, skipping", __func__, + afi); + return; + } + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: bgp exterior view not defined, skipping", + __func__); + return; + } + + + { + struct bgp_dest *dest; + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); + dest; dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_del_route( + bgp_exterior, bgp_dest_get_prefix(dest), + bpi); + } + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, NULL); +#endif + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 0; + vnc_zlog_debug_verbose("%s: return", __func__); +} diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h new file mode 100644 index 0000000..c8d4170 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ +#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ + +#include "lib/zebra.h" +#include "lib/prefix.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" + +#define VALID_INTERIOR_TYPE(type) \ + (((type) == ZEBRA_ROUTE_BGP) || ((type) == ZEBRA_ROUTE_BGP_DIRECT)) + +extern uint32_t calc_local_pref(struct attr *attr, struct peer *peer); + +extern int vnc_prefix_cmp(const void *pfx1, const void *pfx2); + +extern void vnc_import_bgp_add_route(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info); + +extern void vnc_import_bgp_del_route(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info); + +extern void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi); + +extern void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi); + +extern void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi); + +extern void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi); + + +extern void vnc_import_bgp_exterior_add_route( + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info); /* unicast info */ + +extern void vnc_import_bgp_exterior_del_route( + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info); /* unicast info */ + +extern void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi); /* new VPN host route */ + +extern void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi); /* old VPN host route */ + +#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ */ diff --git a/bgpd/rfapi/vnc_import_bgp_p.h b/bgpd/rfapi/vnc_import_bgp_p.h new file mode 100644 index 0000000..7019796 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp_p.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ +#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ + +#include "lib/zebra.h" +#include "lib/prefix.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" + +extern void vnc_import_bgp_exterior_add_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior); /* VPN IT route */ + +extern void vnc_import_bgp_exterior_del_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior); /* VPN IT route */ + +extern void +vnc_import_bgp_exterior_redist_enable_it(struct bgp *bgp, afi_t afi, + struct rfapi_import_table *it_only); + +#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ */ diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c new file mode 100644 index 0000000..c886bee --- /dev/null +++ b/bgpd/rfapi/vnc_zebra.c @@ -0,0 +1,887 @@ +// 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 <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_default, + 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; +} diff --git a/bgpd/rfapi/vnc_zebra.h b/bgpd/rfapi/vnc_zebra.h new file mode 100644 index 0000000..6d7e689 --- /dev/null +++ b/bgpd/rfapi/vnc_zebra.h @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + */ + +/* + * File: vnc_zebra.h + */ + +#ifndef _QUAGGA_BGP_VNC_ZEBRA_H +#define _QUAGGA_BGP_VNC_ZEBRA_H + +#include "lib/zebra.h" + +extern void vnc_zebra_add_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn); + +extern void vnc_zebra_del_prefix(struct bgp *bgp, + struct rfapi_import_table *import_table, + struct agg_node *rn); + +extern void vnc_zebra_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void vnc_zebra_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd); + +extern void vnc_zebra_add_group(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg); + +extern void vnc_zebra_del_group(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg); + +extern void vnc_zebra_reexport_group_afi(struct bgp *bgp, + struct rfapi_nve_group_cfg *rfg, + afi_t afi); + +extern int vnc_redistribute_set(struct bgp *bgp, afi_t afi, int type); + +extern int vnc_redistribute_unset(struct bgp *bgp, afi_t afi, int type); + +#endif /* _QUAGGA_BGP_VNC_ZEBRA_H */ |