diff options
Diffstat (limited to 'bgpd/rfapi/rfapi.c')
| -rw-r--r-- | bgpd/rfapi/rfapi.c | 4412 |
1 files changed, 4412 insertions, 0 deletions
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c new file mode 100644 index 0000000000..3360ad89ce --- /dev/null +++ b/bgpd/rfapi/rfapi.c @@ -0,0 +1,4412 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +#include <errno.h> + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" +#include "stream.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_advertise.h" +#include "bgp_vnc_types.h" +#include "bgp_zebra.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" +#include "rfapi_ap.h" +#include "rfapi_encap_tlv.h" +#include "vnc_debug.h" + +#ifdef HAVE_GLIBC_BACKTRACE +/* for backtrace and friends */ +#include <execinfo.h> +#endif /* HAVE_GLIBC_BACKTRACE */ + +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 (BGP VPN messaging /VPN & encap SAFIs) are 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); + return bgp_rfapi_is_vnc_configured (bgp); +} + + +/*------------------------------------------ + * 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 route_node **node) +{ + struct rfapi *h; + struct prefix p; + struct route_node *rn; + int rc; + int 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 = route_node_lookup (&h->un[afi], &p); + + if (!rn) + return ENOENT; + + route_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 route_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 (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 ((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, + 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_node *bn; + struct bgp_info *bi; + char buf[BUFSIZ]; + char buf2[BUFSIZ]; + struct prefix_rd prd0; + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + prefix_rd2str (prd, buf2, BUFSIZ); + buf2[BUFSIZ - 1] = 0; + + 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); + + zlog_debug + ("%s: peer=%p, prefix=%s, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p", + __func__, peer, buf, buf2, afi, safi, bn, (bn ? bn->info : NULL)); + + for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) + { + + zlog_debug + ("%s: trying bi=%p, bi->peer=%p, bi->type=%d, bi->sub_type=%d, bi->extra->vnc.export.rfapi_handle=%p", + __func__, bi, bi->peer, bi->type, bi->sub_type, + (bi->extra ? bi->extra->vnc.export.rfapi_handle : NULL)); + + if (bi->peer == peer && + bi->type == type && + bi->sub_type == sub_type && + bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd) + { + + zlog_debug ("%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 (!bi || !bi->extra || !bi->extra->vnc.export.local_nexthops) + { + /* + * no local nexthops + */ + zlog_debug ("%s: lnh list already empty at prefix %s", + __func__, buf); + goto done; + } + + /* + * look for it + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO (bi->extra->vnc.export.local_nexthops, + node, pLnh)) + { + + if (prefix_same (&pLnh->addr, &lnh->addr)) + { + break; + } + } + + if (pLnh) + { + listnode_delete (bi->extra->vnc.export.local_nexthops, pLnh); + + /* silly rabbit, listnode_delete doesn't invoke list->del on data */ + rfapi_nexthop_free (pLnh); + } + else + { + zlog_debug ("%s: desired lnh not found %s", __func__, buf); + } + 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 (bi) + { + char buf[BUFSIZ]; + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + zlog_debug ("%s: Found route (safi=%d) to delete at prefix %s", + __func__, safi, buf); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve (bgp, + prd, + table, + p, bi); + } + bgp_unlock_node (prn); + } + + /* + * Delete local_nexthops list + */ + if (bi->extra && bi->extra->vnc.export.local_nexthops) + { + list_delete (bi->extra->vnc.export.local_nexthops); + } + + bgp_aggregate_decrement (bgp, p, bi, afi, safi); + bgp_info_delete (bn, bi); + bgp_process (bgp, bn, afi, safi); + } + else + { + zlog_debug ("%s: Couldn't find route (safi=%d) at prefix %s", + __func__, safi, buf); + } +done: + bgp_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, + 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_info *new; + struct bgp_info *bi; + struct bgp_node *bn; + + struct attr attr = { 0 }; + struct attr *new_attr; + uint32_t label_val; + + struct bgp_attr_encap_subtlv *encaptlv; + char buf[BUFSIZ]; + char buf2[BUFSIZ]; +#if 0 /* unused? */ + struct prefix pfx_buf; +#endif + + 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. + */ + zlog_debug ("%s: encap mode not enabled, not adding SAFI_ENCAP route", + __func__); + return; + } + +#if 0 /* unused? */ + if ((safi == SAFI_MPLS_VPN) && (flags & RFAPI_AHR_SET_PFX_TO_NEXTHOP)) + { + + if (rfapiRaddr2Qprefix (nexthop, &pfx_buf)) + { + zlog_debug + ("%s: can't set pfx to vn addr, not adding SAFI_MPLS_VPN route", + __func__); + return; + } + p = &pfx_buf; + } +#endif + 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; + + prefix_rd2str (prd, buf2, BUFSIZ); + buf2[BUFSIZ - 1] = 0; + + + afi = family2afi (p->family); + assert (afi == AFI_IP || afi == AFI_IP6); + + zlog_debug ("%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_ORIGIN_INCOMPLETE); + assert (attr.extra); + + /* + * 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 + TunnelType = rfapi_tunneltype_option_to_tlv ( + bgp, un_addr, NULL, + /* create one to carry un_addr */ &attr, l2o != NULL); + } + + if (TunnelType == BGP_ENCAP_TYPE_MPLS) + { + if (safi == SAFI_ENCAP) + { + /* Encap SAFI not used with MPLS */ + zlog_debug ("%s: mpls tunnel type, encap safi omitted", __func__); + aspath_unintern (&attr.aspath); /* Unintern original. */ + bgp_attr_extra_free (&attr); + return; + } + nexthop = un_addr; /* UN used as MPLS NLRI nexthop */ + } + + 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.extra->weight = (rfd->peer ? rfd->peer->weight : 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.extra->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) - 1 + 4); + assert (encaptlv); + encaptlv->type = BGP_VNC_SUBTLV_TYPE_LIFETIME; /* prefix lifetime */ + encaptlv->length = 4; + lt = htonl (*lifetime); + memcpy (encaptlv->value, <, 4); + attr.extra->vnc_subtlvs = encaptlv; + zlog_debug ("%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) + { + + /* + * 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 (attr.extra->vnc_subtlvs) + { + attr.extra->vnc_subtlvs->next = encaptlv; + } + else + { + attr.extra->vnc_subtlvs = encaptlv; + } + + } + else + { + struct bgp_tea_options *hop; + /* XXX max of one tlv present so far from above code */ + struct bgp_attr_encap_subtlv *tail = attr.extra->vnc_subtlvs; + + for (hop = rfp_options; hop; hop = hop->next) + { + + /* + * Construct subtlv + */ + encaptlv = XCALLOC (MTYPE_ENCAP_TLV, + sizeof (struct bgp_attr_encap_subtlv) - 1 + + 2 + hop->length); + assert (encaptlv); + 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 + { + attr.extra->vnc_subtlvs = 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 + */ + + + attr.extra->ecommunity = ecommunity_new (); + assert (attr.extra->ecommunity); + + 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 (attr.extra->ecommunity, &beec); + } + + /* + * Add extended community attributes to match rt export list + */ + if (rt_export_list) + { + attr.extra->ecommunity = + ecommunity_merge (attr.extra->ecommunity, rt_export_list); + } + + if (attr.extra->ecommunity->size) + { + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); + } + else + { + ecommunity_free (&attr.extra->ecommunity); + attr.extra->ecommunity = NULL; + } + zlog_debug ("%s: attr.extra->ecommunity=%p", __func__, + attr.extra->ecommunity); + + + /* + * 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.extra->mp_nexthop_global_in = nexthop->addr.v4; + attr.extra->mp_nexthop_len = 4; + break; + + case AF_INET6: + attr.extra->mp_nexthop_global = nexthop->addr.v6; + attr.extra->mp_nexthop_len = 16; + break; + + default: + assert (0); + } + + + prefix2str (p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + + /* + * 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, VRF_DEFAULT); + + 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. */ + bgp_attr_extra_free (&attr); + + /* + * 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 (bi = bn->info; bi; bi = bi->next) + { + /* probably only need to check bi->extra->vnc.export.rfapi_handle */ + if (bi->peer == rfd->peer && + bi->type == type && + bi->sub_type == sub_type && + bi->extra && bi->extra->vnc.export.rfapi_handle == (void *) rfd) + { + + break; + } + } + + if (bi) + { + + /* + * Adding new local_nexthop, which does not by itself change + * what is advertised via BGP + */ + if (lnh) + { + if (!bi->extra->vnc.export.local_nexthops) + { + /* TBD make arrangements to free when needed */ + bi->extra->vnc.export.local_nexthops = list_new (); + bi->extra->vnc.export.local_nexthops->del = rfapi_nexthop_free; + } + + /* + * already present? + */ + struct listnode *node; + struct rfapi_nexthop *pLnh = NULL; + + for (ALL_LIST_ELEMENTS_RO (bi->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 (bi->extra->vnc.export.local_nexthops, pLnh); + } + } + + if (attrhash_cmp (bi->attr, new_attr) && + !CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + { + bgp_attr_unintern (&new_attr); + bgp_unlock_node (bn); + + zlog_info ("%s: Found route (safi=%d) at prefix %s, no change", + __func__, safi, buf); + + goto done; + } + else + { + /* The attribute is changed. */ + bgp_info_set_flag (bn, bi, BGP_INFO_ATTR_CHANGED); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_del_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, bi); + } + bgp_unlock_node (prn); + } + + /* Rewrite BGP route information. */ + if (CHECK_FLAG (bi->flags, BGP_INFO_REMOVED)) + bgp_info_restore (bn, bi); + else + bgp_aggregate_decrement (bgp, p, bi, afi, safi); + bgp_attr_unintern (&bi->attr); + bi->attr = new_attr; + bi->uptime = bgp_clock (); + + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, bi); + } + bgp_unlock_node (prn); + } + + /* Process change. */ + bgp_aggregate_increment (bgp, p, bi, afi, safi); + bgp_process (bgp, bn, afi, safi); + bgp_unlock_node (bn); + + zlog_info ("%s: Found route (safi=%d) at prefix %s, changed attr", + __func__, safi, buf); + + goto done; + } + } + + + new = bgp_info_new (); + new->type = type; + new->sub_type = sub_type; + new->peer = rfd->peer; + SET_FLAG (new->flags, BGP_INFO_VALID); + new->attr = new_attr; + new->uptime = bgp_clock (); + + /* save backref to rfapi handle */ + assert (bgp_info_extra_get (new)); + new->extra->vnc.export.rfapi_handle = (void *) rfd; + encode_label (label_val, new->extra->tag); + + /* debug */ + zlog_debug ("%s: printing BI", __func__); + rfapiPrintBi (NULL, new); + + bgp_aggregate_increment (bgp, p, new, afi, safi); + bgp_info_add (bn, new); + + if (safi == SAFI_MPLS_VPN) + { + struct bgp_node *prn = NULL; + struct bgp_table *table = NULL; + + prn = bgp_node_get (bgp->rib[afi][safi], (struct prefix *) prd); + if (prn->info) + { + table = (struct bgp_table *) (prn->info); + + vnc_import_bgp_add_vnc_host_route_mode_resolve_nve ( + bgp, prd, table, p, new); + } + bgp_unlock_node (prn); + } + + bgp_unlock_node (bn); + bgp_process (bgp, bn, afi, safi); + + zlog_info ("%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%s)", + __func__, safi2str (safi), buf, bn, buf2); + +done: + /* Loop back to import tables */ + rfapiProcessUpdate (rfd->peer, + rfd, + p, prd, new_attr, afi, safi, type, sub_type, &label_val); + zlog_debug ("%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->status = Established; /* keep bgp core happy */ + bgp_sync_delete (rfd->peer); /* don't need these */ + if (rfd->peer->ibuf) + { + stream_free (rfd->peer->ibuf); /* don't need it */ + rfd->peer->ibuf = NULL; + } + if (rfd->peer->obuf) + { + stream_fifo_free (rfd->peer->obuf); /* don't need it */ + rfd->peer->obuf = NULL; + } + if (rfd->peer->work) + { + stream_free (rfd->peer->work); /* don't need it */ + rfd->peer->work = NULL; + } + { /* base code assumes have valid host pointer */ + char buf[BUFSIZ]; + buf[0] = 0; + + if (rfd->vn_addr.addr_family == AF_INET) + { + inet_ntop (AF_INET, &rfd->vn_addr.addr.v4, buf, BUFSIZ); + } + else if (rfd->vn_addr.addr_family == AF_INET6) + { + inet_ntop (AF_INET6, &rfd->vn_addr.addr.v6, buf, BUFSIZ); + } + 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] = route_table_init ();\ + ary[afi]->info = 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_ETHER);\ +} 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; +} + +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; + + if (p->value) + { + XFREE (MTYPE_BGP_TEA_OPTIONS_VALUE, p->value); + p->value = NULL; + } + 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 route_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) + { + zlog_debug ("%s: No BGP instance, returning ENXIO", __func__); + return ENXIO; + } + if (!bgp->rfapi) + { + zlog_debug ("%s: No RFAPI instance, returning ENXIO", __func__); + return ENXIO; + } + if (bgp->rfapi->flags & RFAPI_INCALLBACK) + { + zlog_debug ("%s: Called during calback, returning EDEADLK", __func__); + return EDEADLK; + } + + if (!is_valid_rfd (rfd)) + { + zlog_debug ("%s: invalid handle, returning EBADF", __func__); + return EBADF; + } + + rfd->rsp_counter++; /* dedup: identify this generation */ + rfd->rsp_time = rfapi_time (NULL); /* response content dedup */ + rfd->ftd_last_allowed_time = + bgp_clock() - bgp->rfapi_cfg->rfp_cfg.ftd_advertisement_interval; + + if (l2o) + { + if (!memcmp (l2o->macaddr.octet, rfapi_ethaddr0.octet, ETHER_ADDR_LEN)) + { + 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 buf[BUFSIZ]; + + prefix2str (&p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + zlog_debug ("%s(rfd=%p, target=%s, ppNextHop=%p)", + __func__, rfd, buf, ppNextHopEntry); + } + + 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 = 32; + } + else + { + rprefix.length = 128; + } + + 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)) + { + + zlog_debug ("%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) + { + route_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) + { + route_unlock_node (rn); + zlog_debug ("%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 = 32; + } + else + { + rprefix.length = 128; + } + + 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); + } + + route_unlock_node (rn); + +done: + if (ppNextHopEntry) + { + /* only count if caller gets it */ + ++bgp->rfapi->response_immediate_count; + } + + if (!pNHE) + { + zlog_debug ("%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); + } + + zlog_debug ("%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; + struct prefix_rd prd; + 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; + } + + + /* + * Construct route distinguisher for VPN routes + */ + prd = rfd->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + /* + * 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; + + struct route_node *rn; + int rc; + rfapi_handle hh = NULL; + int reusing_provisional = 0; + + afi_t afi_vn; + afi_t afi_un; + + { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug ("%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)); + } + assert (rfd); + + 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) + { + rfapi_time (&rfd->open_time); + + { + char buf_vn[BUFSIZ]; + char buf_un[BUFSIZ]; + + rfapiRfapiIpAddr2Str (vn, buf_vn, BUFSIZ); + rfapiRfapiIpAddr2Str (un, buf_un, BUFSIZ); + + zlog_debug ("%s: new HD with VN=%s UN=%s cookie=%p", + __func__, buf_vn, buf_un, userdata); + } + + 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 = route_node_get (&(h->un[afi_un]), &pfx_un); + assert (rn); + rfd->next = rn->info; + rn->info = rfd; + rfd->un_node = rn; + + rc = rfapi_open_inner (rfd, bgp, h, 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) + { + zlog_debug ("%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 route_node *node; + struct bgp *bgp; + struct rfapi *h; + struct rfapi_cfg *hc; + + zlog_debug ("%s: rfd=%p", __func__, rfd); + +#if 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) + { + zlog_debug ("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); + zlog_debug ("%s: added handle %p to deferred close queue", + __func__, handle); + } + return 0; + } + + hc = bgp->rfapi_cfg; + + if (CHECK_FLAG (rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) + { + + zlog_debug ("%s administrative close rfd=%p", __func__, rfd); + + if (h && h->rfp_methods.close_cb) + { + zlog_debug ("%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; + } + } + } + route_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; + struct rfapi_cfg *hc; + int rc; + + if ((rc = rfapi_close_inner (rfd, bgp))) + { + return rc; + } + if ((rc = rfapi_open_rfd (rfd, bgp))) + { + + h = bgp->rfapi; + hc = bgp->rfapi_cfg; + + assert (!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; + int 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 rfapi_nexthop *lnh = 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_LOCAL_NEXTHOP == vo->type) + { + lnh = &vo->v.local_nexthop; + } + 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); + + + { + char buf[BUFSIZ]; + + prefix2str (&p, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; /* guarantee NUL-terminated */ + zlog_debug + ("%s(rfd=%p, pfx=%s, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)", + __func__, rfd, buf, 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) + { + zlog_debug ("%s: no BGP instance: returning ENXIO", __func__); + return ENXIO; + } + if (!bgp->rfapi) + { + zlog_debug ("%s: no RFAPI instance: returning ENXIO", __func__); + return ENXIO; + } + if (!rfd->rfg) + { + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + zlog_debug ("%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; + } + zlog_debug ("%s: in callback: returning EDEADLK", __func__); + return EDEADLK; + } + + if (!is_valid_rfd (rfd)) + { + if (RFAPI_REGISTER_ADD == action) + { + ++bgp->rfapi->stat.count_registrations_failed; + } + zlog_debug ("%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) + { + zlog_debug ("%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)) + { + zlog_debug ("%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, &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; + } + + zlog_debug ("%s: adv_tunnel = %d", __func__, adv_tunnel); + if (adv_tunnel) + { + zlog_debug ("%s: announcing tunnel route", __func__); + rfapiTunnelRouteAnnounce (bgp, rfd, &rfd->max_prefix_lifetime); + } + + zlog_debug ("%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] = 0x02; + 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); + } + } + + /* + * 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 */ + } + + zlog_debug ("%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 or underlay network interface\n" + "IPv4 or IPv6 address\n") +{ + struct prefix pfx; + + if (!str2prefix (argv[1], &pfx)) + { + vty_out (vty, "Malformed address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Invalid address \"%s\"%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (*(argv[0]) == 'c') + { + 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, ")%s", VTY_NEWLINE); + + rfapiPrintNhl (stream, next_hops); + + 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 address\n" + "indicate xt addr follows\n" "underlay network interface address\n") +{ + struct rfapi_ip_addr vn; + struct rfapi_ip_addr un; + uint32_t lifetime; + int rc; + rfapi_handle handle; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &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%s", + rc, handle, lifetime, VTY_NEWLINE); + + rc = rfapi_set_response_cb (handle, test_nexthops_callback); + + vty_out (vty, "rfapi_set_response_cb: status %d%s", rc, VTY_NEWLINE); + + 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 address\n" + "indicate xt addr follows\n" "underlay network interface 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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = rfapi_close (handle); + + vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc, + VTY_NEWLINE); + + 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; + +#if (UINTPTR_MAX == ULONG_MAX) + handle = (void *) (uintptr_t) (strtoul (argv[0], &endptr, 16)); +#elif (UINTPTR_MAX == ULLONG_MAX) + handle = (rfapi_handle) (uintptr_t) (strtoull (argv[0], &endptr, 16)); +#else + /* give up */ + assert (0); +#endif + + if (*endptr != '\0' || (uintptr_t) handle == UINTPTR_MAX) + { + vty_out (vty, "Invalid value: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + rc = rfapi_close (handle); + + vty_out (vty, "rfapi_close(handle=%p): status %d%s", handle, rc, + VTY_NEWLINE); + + 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", + 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") +{ + 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; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + if (!strcmp (argv[3], "infinite")) + { + lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]); + } + + + rc = rfapi_register (handle, &hpfx, lifetime, NULL, NULL, 0); + if (rc) + { + vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + + 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 YY:YY:YY:YY:YY:YY" + " 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" "lifetime\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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + if (!strcmp (argv[3], "infinite")) + { + lifetime = RFAPI_INFINITE_LIFETIME; + } + else + { + VTY_GET_INTEGER ("Lifetime", lifetime, argv[3]); + } + + /* L2 option parsing START */ + memset (optary, 0, sizeof (optary)); + VTY_GET_INTEGER ("Logical Network ID", + optary[opt_next].v.l2addr.logical_net_id, argv[5]); + if ((rc = rfapiStr2EthAddr (argv[4], &optary[opt_next].v.l2addr.macaddr))) + { + vty_out (vty, "Bad mac address \"%s\"%s", argv[4], VTY_NEWLINE); + return CMD_WARNING; + } + 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; + /* L2 option parsing END */ + + /* TBD fixme */ + rc = rfapi_register (handle, &hpfx, lifetime, NULL /* &uo */ , opt, 0); + if (rc) + { + vty_out (vty, "rfapi_register failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + + 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)", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_register\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate prefix follows\n" "prefix") +{ + 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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Get prefix to advertise + */ + if (!str2prefix (argv[2], &pfx)) + { + vty_out (vty, "Malformed prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + if (pfx.family != AF_INET && pfx.family != AF_INET6) + { + vty_out (vty, "Bad family for prefix \"%s\"%s", argv[2], VTY_NEWLINE); + return CMD_WARNING; + } + rfapiQprefix2Rprefix (&pfx, &hpfx); + + rfapi_register (handle, &hpfx, 0, NULL, NULL, 1); + + 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 address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate target follows\n" "target\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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * 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)%s", rc, + strerror (rc), VTY_NEWLINE); + } + 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 YY:YY:YY:YY:YY:YY", + DEBUG_STR + DEBUG_RFAPI_STR + "rfapi_query\n" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface 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; + struct rfapi_ip_addr target; + rfapi_handle handle; + int rc; + struct rfapi_next_hop_entry *pNextHopEntry; + struct rfapi_l2address_option l2o_buf; + struct bgp_tea_options hopt; + uint8_t valbuf[14]; + + /* + * Get VN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * Set up L2 parameters + */ + memset (&l2o_buf, 0, sizeof (l2o_buf)); + if (rfapiStr2EthAddr (argv[3], &l2o_buf.macaddr)) + { + vty_out (vty, "Bad mac address \"%s\"%s", argv[3], VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("Logical Network ID", l2o_buf.logical_net_id, argv[2]); + + /* construct option chain */ + + memset (valbuf, 0, sizeof (valbuf)); + memcpy (valbuf, &l2o_buf.macaddr.octet, ETHER_ADDR_LEN); + valbuf[11] = (l2o_buf.logical_net_id >> 16) & 0xff; + valbuf[12] = (l2o_buf.logical_net_id >> 8) & 0xff; + valbuf[13] = l2o_buf.logical_net_id & 0xff; + + memset (&hopt, 0, sizeof (hopt)); + hopt.options_count = 1; + hopt.options_length = sizeof (valbuf); /* is this right? */ + hopt.type = RFAPI_VN_OPTION_TYPE_L2ADDR; + hopt.length = sizeof (valbuf); + hopt.value = valbuf; + + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query (handle, &target, &l2o_buf, &pNextHopEntry); + + if (rc) + { + vty_out (vty, "rfapi_query failed with rc=%d (%s)%s", rc, + strerror (rc), VTY_NEWLINE); + } + else + { + /* + * print nexthop list + */ + /* TBD enhance to print L2 information */ + test_nexthops_callback ( /*&target, */ pNextHopEntry, vty); /* frees nh list! */ + } + + return CMD_SUCCESS; +} + + +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" + "indicate vn addr follows\n" + "virtual network interface address\n" + "indicate xt addr follows\n" + "underlay network interface address\n" + "indicate prefix follows\n" "prefix\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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + /* + * Get target addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[2], &target))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * options parameter not used? Set to NULL for now + */ + rc = rfapi_query_done (handle, &target); + + vty_out (vty, "rfapi_query_done returned %d%s", rc, VTY_NEWLINE); + + 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%s", VTY_NEWLINE); + return CMD_WARNING; + } + + h = bgp->rfapi; + if (!h) + { + vty_out (vty, "No RFAPI instance%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* + * 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); + vty_out (vty, "Import Table %p, RTs: %s%s", it, s, VTY_NEWLINE); + 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_ETHER]) + { + lni = lni_as_ptr; + if (first_l2) + { + vty_out (vty, "%sLNI-based Ethernet Tables:%s", + VTY_NEWLINE, VTY_NEWLINE); + first_l2 = 0; + } + snprintf (buf, BUFSIZ, "L2VPN LNI=%u", lni); + rfapiShowImportTable (vty, buf, it->imported_vpn[AFI_ETHER], 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 address\n" + "indicate xt addr follows\n" "underlay network interface 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[0], &vn))) + return rc; + + + /* + * Get UN addr + */ + if ((rc = rfapiCliGetRfapiIpAddr (vty, argv[1], &un))) + return rc; + + + if (rfapi_find_handle_vty (vty, &vn, &un, &handle)) + { + vty_out (vty, "can't locate handle matching vn=%s, un=%s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + 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%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (!bgp->rfapi_cfg) + { + vty_out (vty, "VNC not configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp (argv[0], "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 "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_node *rdn; + struct bgp_node *rn; + struct bgp_info *bi; + + if (!bgp) + return; + + for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rdn; + rdn = bgp_route_next (rdn)) + { + if (!rdn->info) + continue; + fprintf (stderr, "%s: vpn rdn=%p\n", __func__, rdn); + for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn)) + { + if (!rn->info) + continue; + fprintf (stderr, "%s: rn=%p\n", __func__, rn); + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi ((void *) 2, bi); /* 2 => stderr */ + } + } + } + for (rdn = bgp_table_top (bgp->rib[AFI_IP][SAFI_ENCAP]); rdn; + rdn = bgp_route_next (rdn)) + { + if (!rdn->info) + continue; + fprintf (stderr, "%s: encap rdn=%p\n", __func__, rdn); + for (rn = bgp_table_top (rdn->info); rn; rn = bgp_route_next (rn)) + { + if (!rn->info) + continue; + fprintf (stderr, "%s: rn=%p\n", __func__, rn); + for (bi = rn->info; bi; bi = bi->next) + { + rfapiPrintBi ((void *) 2, bi); /* 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 */ + + /* + * This clears queries and registered routes, and closes nves + */ + if (bgp->rfapi) + rfp_clear_vnc_nve_all (); + 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) +{ + zlog_debug ("%s: auto-assigning RD", __func__); + if (vn->addr_family != AF_INET + && vn->addr_family != AF_INET6) + { + zlog_debug ("%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 */ + } + { + char buf[BUFSIZ]; + buf[0] = 0; + prefix_rd2str (rd, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + zlog_debug ("%s: auto-RD is set to %s", __func__, buf); + } + 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); + zlog_debug ("%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->index_sub; + + /* make sure group is still in list */ + if (!listnode_lookup (rfc->nve_groups_sequential, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current NVE group no longer exists%s", VTY_NEWLINE); + return NULL; + } + + if (rfg->rfp_cfg == NULL && size > 0) + { + rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size); + zlog_debug ("%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->index_sub; + + /* make sure group is still in list */ + if (!listnode_lookup (rfc->l2_groups, rfg)) + { + /* Not in list anymore */ + vty_out (vty, "Current L2 group no longer exists%s", VTY_NEWLINE); + return NULL; + } + if (rfg->rfp_cfg == NULL && size > 0) + { + rfg->rfp_cfg = XCALLOC (MTYPE_RFAPI_RFP_GROUP_CFG, size); + zlog_debug ("%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 || !vty->index_sub) + 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: + zlog_err ("%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 serach 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: + zlog_err ("%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 serach 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) + zlog_debug ("%s: returning rfp group config for lni=0", __func__); + return rfg->rfp_cfg; + } + } + return NULL; +} |
