mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 01:03:15 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			4075 lines
		
	
	
		
			100 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			4075 lines
		
	
	
		
			100 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *
 | |
|  * 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; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include "lib/zebra.h"
 | |
| #include "lib/prefix.h"
 | |
| #include "lib/agg_table.h"
 | |
| #include "lib/vty.h"
 | |
| #include "lib/memory.h"
 | |
| #include "lib/routemap.h"
 | |
| #include "lib/log.h"
 | |
| #include "lib/linklist.h"
 | |
| #include "lib/command.h"
 | |
| #include "lib/stream.h"
 | |
| #include "lib/ringbuf.h"
 | |
| #include "lib/lib_errors.h"
 | |
| 
 | |
| #include "bgpd/bgpd.h"
 | |
| #include "bgpd/bgp_ecommunity.h"
 | |
| #include "bgpd/bgp_attr.h"
 | |
| 
 | |
| #include "bgpd/rfapi/bgp_rfapi_cfg.h"
 | |
| #include "bgpd/rfapi/rfapi.h"
 | |
| #include "bgpd/rfapi/rfapi_backend.h"
 | |
| 
 | |
| #include "bgpd/bgp_route.h"
 | |
| #include "bgpd/bgp_mplsvpn.h"
 | |
| #include "bgpd/bgp_aspath.h"
 | |
| #include "bgpd/bgp_advertise.h"
 | |
| #include "bgpd/bgp_vnc_types.h"
 | |
| #include "bgpd/bgp_zebra.h"
 | |
| 
 | |
| #include "bgpd/rfapi/rfapi_import.h"
 | |
| #include "bgpd/rfapi/rfapi_private.h"
 | |
| #include "bgpd/rfapi/rfapi_monitor.h"
 | |
| #include "bgpd/rfapi/rfapi_vty.h"
 | |
| #include "bgpd/rfapi/vnc_export_bgp.h"
 | |
| #include "bgpd/rfapi/vnc_export_bgp_p.h"
 | |
| #include "bgpd/rfapi/vnc_zebra.h"
 | |
| #include "bgpd/rfapi/vnc_import_bgp.h"
 | |
| #include "bgpd/rfapi/rfapi_rib.h"
 | |
| #include "bgpd/rfapi/rfapi_ap.h"
 | |
| #include "bgpd/rfapi/rfapi_encap_tlv.h"
 | |
| #include "bgpd/rfapi/vnc_debug.h"
 | |
| 
 | |
| #ifdef HAVE_GLIBC_BACKTRACE
 | |
| /* for backtrace and friends */
 | |
| #include <execinfo.h>
 | |
| #endif /* HAVE_GLIBC_BACKTRACE */
 | |
| 
 | |
| struct ethaddr rfapi_ethaddr0 = {{0}};
 | |
| 
 | |
| #define DEBUG_RFAPI_STR "RF API debugging/testing command\n"
 | |
| 
 | |
| const char *rfapi_error_str(int code)
 | |
| {
 | |
| 	switch (code) {
 | |
| 	case 0:
 | |
| 		return "Success";
 | |
| 	case ENXIO:
 | |
| 		return "BGP or VNC not configured";
 | |
| 	case ENOENT:
 | |
| 		return "No match";
 | |
| 	case EEXIST:
 | |
| 		return "Handle already open";
 | |
| 	case ENOMSG:
 | |
| 		return "Incomplete configuration";
 | |
| 	case EAFNOSUPPORT:
 | |
| 		return "Invalid address family";
 | |
| 	case EDEADLK:
 | |
| 		return "Called from within a callback procedure";
 | |
| 	case EBADF:
 | |
| 		return "Invalid handle";
 | |
| 	case EINVAL:
 | |
| 		return "Invalid argument";
 | |
| 	case ESTALE:
 | |
| 		return "Stale descriptor";
 | |
| 	default:
 | |
| 		return "Unknown error";
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_get_response_lifetime_default
 | |
|  *
 | |
|  * Returns the default lifetime for a response.
 | |
|  *    rfp_start_val     value returned by rfp_start or
 | |
|  *                      NULL (=use default instance)
 | |
|  *
 | |
|  * input:
 | |
|  *    None
 | |
|  *
 | |
|  * output:
 | |
|  *
 | |
|  * return value: The bgp instance default lifetime for a response.
 | |
|  --------------------------------------------*/
 | |
| int rfapi_get_response_lifetime_default(void *rfp_start_val)
 | |
| {
 | |
| 	struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 	if (bgp)
 | |
| 		return bgp->rfapi_cfg->default_response_lifetime;
 | |
| 	return BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_is_vnc_configured
 | |
|  *
 | |
|  * Returns if VNC is configured
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start or
 | |
|  *                      NULL (=use default instance)
 | |
|  *
 | |
|  * output:
 | |
|  *
 | |
|  * return value: If VNC is configured for the bgpd instance
 | |
|  *	0		Success
 | |
|  *	ENXIO		VNC not configured
 | |
|  --------------------------------------------*/
 | |
| int rfapi_is_vnc_configured(void *rfp_start_val)
 | |
| {
 | |
| 	struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 	if (bgp_rfapi_is_vnc_configured(bgp) == 0)
 | |
| 		return 0;
 | |
| 	return ENXIO;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_get_vn_addr
 | |
|  *
 | |
|  * Get the virtual network address used by an NVE based on it's RFD
 | |
|  *
 | |
|  * input:
 | |
|  *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
 | |
|  *
 | |
|  * output:
 | |
|  *
 | |
|  * return value:
 | |
|  *	vn		NVE virtual network address
 | |
|  *------------------------------------------*/
 | |
| struct rfapi_ip_addr *rfapi_get_vn_addr(void *rfd)
 | |
| {
 | |
| 	struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd;
 | |
| 	return &rrfd->vn_addr;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_get_un_addr
 | |
|  *
 | |
|  * Get the underlay network address used by an NVE based on it's RFD
 | |
|  *
 | |
|  * input:
 | |
|  *    rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
 | |
|  *
 | |
|  * output:
 | |
|  *
 | |
|  * return value:
 | |
|  *	un		NVE underlay network address
 | |
|  *------------------------------------------*/
 | |
| struct rfapi_ip_addr *rfapi_get_un_addr(void *rfd)
 | |
| {
 | |
| 	struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd;
 | |
| 	return &rrfd->un_addr;
 | |
| }
 | |
| 
 | |
| int rfapi_ip_addr_cmp(struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2)
 | |
| {
 | |
| 	if (a1->addr_family != a2->addr_family)
 | |
| 		return a1->addr_family - a2->addr_family;
 | |
| 
 | |
| 	if (a1->addr_family == AF_INET) {
 | |
| 		return IPV4_ADDR_CMP(&a1->addr.v4, &a2->addr.v4);
 | |
| 	}
 | |
| 
 | |
| 	if (a1->addr_family == AF_INET6) {
 | |
| 		return IPV6_ADDR_CMP(&a1->addr.v6, &a2->addr.v6);
 | |
| 	}
 | |
| 
 | |
| 	assert(1);
 | |
| 	/* NOTREACHED */
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| static int rfapi_find_node(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
 | |
| 			   struct rfapi_ip_addr *un_addr,
 | |
| 			   struct agg_node **node)
 | |
| {
 | |
| 	struct rfapi *h;
 | |
| 	struct prefix p;
 | |
| 	struct agg_node *rn;
 | |
| 	int rc;
 | |
| 	afi_t afi;
 | |
| 
 | |
| 	if (!bgp) {
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h) {
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 
 | |
| 	afi = family2afi(un_addr->addr_family);
 | |
| 	if (!afi) {
 | |
| 		return EAFNOSUPPORT;
 | |
| 	}
 | |
| 
 | |
| 	if ((rc = rfapiRaddr2Qprefix(un_addr, &p)))
 | |
| 		return rc;
 | |
| 
 | |
| 	rn = agg_node_lookup(h->un[afi], &p);
 | |
| 
 | |
| 	if (!rn)
 | |
| 		return ENOENT;
 | |
| 
 | |
| 	agg_unlock_node(rn);
 | |
| 
 | |
| 	*node = rn;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
 | |
| 		   struct rfapi_ip_addr *un_addr, struct rfapi_descriptor **rfd)
 | |
| {
 | |
| 	struct agg_node *rn;
 | |
| 	int rc;
 | |
| 
 | |
| 	rc = rfapi_find_node(bgp, vn_addr, un_addr, &rn);
 | |
| 
 | |
| 	if (rc)
 | |
| 		return rc;
 | |
| 
 | |
| 	for (*rfd = (struct rfapi_descriptor *)(rn->info); *rfd;
 | |
| 	     *rfd = (*rfd)->next) {
 | |
| 		if (!rfapi_ip_addr_cmp(&(*rfd)->vn_addr, vn_addr))
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (!*rfd)
 | |
| 		return ENOENT;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_find_handle
 | |
|  *
 | |
|  * input:
 | |
|  *	un		underlay network address
 | |
|  *	vn		virtual network address
 | |
|  *
 | |
|  * output:
 | |
|  *	pHandle		pointer to location to store handle
 | |
|  *
 | |
|  * return value:
 | |
|  *	0		Success
 | |
|  *	ENOENT		no matching handle
 | |
|  *	ENXIO		BGP or VNC not configured
 | |
|  *------------------------------------------*/
 | |
| static int rfapi_find_handle(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
 | |
| 			     struct rfapi_ip_addr *un_addr,
 | |
| 			     rfapi_handle *handle)
 | |
| {
 | |
| 	struct rfapi_descriptor **rfd;
 | |
| 
 | |
| 	rfd = (struct rfapi_descriptor **)handle;
 | |
| 
 | |
| 	return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd);
 | |
| }
 | |
| 
 | |
| static int rfapi_find_handle_vty(struct vty *vty, struct rfapi_ip_addr *vn_addr,
 | |
| 				 struct rfapi_ip_addr *un_addr,
 | |
| 				 rfapi_handle *handle)
 | |
| {
 | |
| 	struct bgp *bgp;
 | |
| 	struct rfapi_descriptor **rfd;
 | |
| 
 | |
| 	bgp = bgp_get_default(); /* assume 1 instance for now */
 | |
| 
 | |
| 	rfd = (struct rfapi_descriptor **)handle;
 | |
| 
 | |
| 	return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd);
 | |
| }
 | |
| 
 | |
| static int is_valid_rfd(struct rfapi_descriptor *rfd)
 | |
| {
 | |
| 	rfapi_handle hh;
 | |
| 
 | |
| 	if (!rfd || rfd->bgp == NULL)
 | |
| 		return 0;
 | |
| 
 | |
| 	if (CHECK_FLAG(
 | |
| 		    rfd->flags,
 | |
| 		    RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */
 | |
| 		return 1;
 | |
| 
 | |
| 	if (rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh))
 | |
| 		return 0;
 | |
| 
 | |
| 	if (rfd != hh)
 | |
| 		return 0;
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * check status of descriptor
 | |
|  */
 | |
| int rfapi_check(void *handle)
 | |
| {
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	rfapi_handle hh;
 | |
| 	int rc;
 | |
| 
 | |
| 	if (!rfd || rfd->bgp == NULL)
 | |
| 		return EINVAL;
 | |
| 
 | |
| 	if (CHECK_FLAG(
 | |
| 		    rfd->flags,
 | |
| 		    RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */
 | |
| 		return 0;
 | |
| 
 | |
| 	if ((rc = rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr,
 | |
| 				    &hh)))
 | |
| 		return rc;
 | |
| 
 | |
| 	if (rfd != hh)
 | |
| 		return ENOENT;
 | |
| 
 | |
| 	if (!rfd->rfg)
 | |
| 		return ESTALE;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void del_vnc_route(struct rfapi_descriptor *rfd,
 | |
| 		   struct peer *peer, /* rfd->peer for RFP regs */
 | |
| 		   struct bgp *bgp, safi_t safi, const struct prefix *p,
 | |
| 		   struct prefix_rd *prd, uint8_t type, uint8_t sub_type,
 | |
| 		   struct rfapi_nexthop *lnh, int kill)
 | |
| {
 | |
| 	afi_t afi; /* of the VN address */
 | |
| 	struct bgp_dest *bn;
 | |
| 	struct bgp_path_info *bpi;
 | |
| 	char buf2[RD_ADDRSTRLEN];
 | |
| 	struct prefix_rd prd0;
 | |
| 
 | |
| 	afi = family2afi(p->family);
 | |
| 	assert(afi == AFI_IP || afi == AFI_IP6);
 | |
| 
 | |
| 	if (safi == SAFI_ENCAP) {
 | |
| 		memset(&prd0, 0, sizeof(prd0));
 | |
| 		prd0.family = AF_UNSPEC;
 | |
| 		prd0.prefixlen = 64;
 | |
| 		prd = &prd0;
 | |
| 	}
 | |
| 	bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
 | |
| 
 | |
| 	vnc_zlog_debug_verbose(
 | |
| 		"%s: peer=%p, prefix=%pFX, prd=%s afi=%d, safi=%d bn=%p, bn->info=%p",
 | |
| 		__func__, peer, p, prefix_rd2str(prd, buf2, sizeof(buf2)), afi,
 | |
| 		safi, bn, (bn ? bgp_dest_get_bgp_path_info(bn) : NULL));
 | |
| 
 | |
| 	for (bpi = (bn ? bgp_dest_get_bgp_path_info(bn) : NULL); bpi;
 | |
| 	     bpi = bpi->next) {
 | |
| 
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%" PRIu64,
 | |
| 			__func__, bpi, bpi->peer, bpi->type, bpi->sub_type,
 | |
| 			(bpi->extra ? bpi->extra->vnc.export.rfapi_handle
 | |
| 				    : NULL),
 | |
| 			CHECK_FLAG(bpi->attr->flag,
 | |
| 				   ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)
 | |
| 				   ? bpi->attr->local_pref : 0));
 | |
| 
 | |
| 		if (bpi->peer == peer && bpi->type == type
 | |
| 		    && bpi->sub_type == sub_type && bpi->extra
 | |
| 		    && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) {
 | |
| 
 | |
| 			vnc_zlog_debug_verbose("%s: matched it", __func__);
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (lnh) {
 | |
| 		/*
 | |
| 		 * lnh set means to JUST delete the local nexthop from this
 | |
| 		 * route. Leave the route itself in place.
 | |
| 		 * TBD add return code reporting of success/failure
 | |
| 		 */
 | |
| 		if (!bpi || !bpi->extra
 | |
| 		    || !bpi->extra->vnc.export.local_nexthops) {
 | |
| 			/*
 | |
| 			 * no local nexthops
 | |
| 			 */
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s: lnh list already empty at prefix %pFX",
 | |
| 				__func__, p);
 | |
| 			goto done;
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * look for it
 | |
| 		 */
 | |
| 		struct listnode *node;
 | |
| 		struct rfapi_nexthop *pLnh = NULL;
 | |
| 
 | |
| 		for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc.export.local_nexthops,
 | |
| 					  node, pLnh)) {
 | |
| 
 | |
| 			if (prefix_same(&pLnh->addr, &lnh->addr)) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (pLnh) {
 | |
| 			listnode_delete(bpi->extra->vnc.export.local_nexthops,
 | |
| 					pLnh);
 | |
| 
 | |
| 			/* silly rabbit, listnode_delete doesn't invoke
 | |
| 			 * list->del on data */
 | |
| 			rfapi_nexthop_free(pLnh);
 | |
| 		} else {
 | |
| 			vnc_zlog_debug_verbose("%s: desired lnh not found %pFX",
 | |
| 					       __func__, p);
 | |
| 		}
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * loop back to import tables
 | |
| 	 * Do this before removing from BGP RIB because rfapiProcessWithdraw
 | |
| 	 * might refer to it
 | |
| 	 */
 | |
| 	rfapiProcessWithdraw(peer, rfd, p, prd, NULL, afi, safi, type, kill);
 | |
| 
 | |
| 	if (bpi) {
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: Found route (safi=%d) to delete at prefix %pFX",
 | |
| 			__func__, safi, p);
 | |
| 
 | |
| 		if (safi == SAFI_MPLS_VPN) {
 | |
| 			struct bgp_dest *pdest = NULL;
 | |
| 			struct bgp_table *table = NULL;
 | |
| 
 | |
| 			pdest = bgp_node_get(bgp->rib[afi][safi],
 | |
| 					     (struct prefix *)prd);
 | |
| 			table = bgp_dest_get_bgp_table_info(pdest);
 | |
| 			if (table)
 | |
| 				vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
 | |
| 					bgp, prd, table, p, bpi);
 | |
| 			bgp_dest_unlock_node(pdest);
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * Delete local_nexthops list
 | |
| 		 */
 | |
| 		if (bpi->extra && bpi->extra->vnc.export.local_nexthops)
 | |
| 			list_delete(&bpi->extra->vnc.export.local_nexthops);
 | |
| 
 | |
| 		bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
 | |
| 		bgp_path_info_delete(bn, bpi);
 | |
| 		bgp_process(bgp, bn, afi, safi);
 | |
| 	} else {
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: Couldn't find route (safi=%d) at prefix %pFX",
 | |
| 			__func__, safi, p);
 | |
| 	}
 | |
| done:
 | |
| 	bgp_dest_unlock_node(bn);
 | |
| }
 | |
| 
 | |
| struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme)
 | |
| {
 | |
| 	struct rfapi_nexthop *new =
 | |
| 		XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_nexthop));
 | |
| 	if (copyme)
 | |
| 		*new = *copyme;
 | |
| 	return new;
 | |
| }
 | |
| 
 | |
| void rfapi_nexthop_free(void *p)
 | |
| {
 | |
| 	struct rfapi_nexthop *goner = p;
 | |
| 	XFREE(MTYPE_RFAPI_NEXTHOP, goner);
 | |
| }
 | |
| 
 | |
| struct rfapi_vn_option *rfapi_vn_options_dup(struct rfapi_vn_option *existing)
 | |
| {
 | |
| 	struct rfapi_vn_option *p;
 | |
| 	struct rfapi_vn_option *head = NULL;
 | |
| 	struct rfapi_vn_option *tail = NULL;
 | |
| 
 | |
| 	for (p = existing; p; p = p->next) {
 | |
| 		struct rfapi_vn_option *new;
 | |
| 
 | |
| 		new = XCALLOC(MTYPE_RFAPI_VN_OPTION,
 | |
| 			      sizeof(struct rfapi_vn_option));
 | |
| 		*new = *p;
 | |
| 		new->next = NULL;
 | |
| 		if (tail)
 | |
| 			(tail)->next = new;
 | |
| 		tail = new;
 | |
| 		if (!head) {
 | |
| 			head = new;
 | |
| 		}
 | |
| 	}
 | |
| 	return head;
 | |
| }
 | |
| 
 | |
| void rfapi_un_options_free(struct rfapi_un_option *p)
 | |
| {
 | |
| 	struct rfapi_un_option *next;
 | |
| 
 | |
| 	while (p) {
 | |
| 		next = p->next;
 | |
| 		XFREE(MTYPE_RFAPI_UN_OPTION, p);
 | |
| 		p = next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void rfapi_vn_options_free(struct rfapi_vn_option *p)
 | |
| {
 | |
| 	struct rfapi_vn_option *next;
 | |
| 
 | |
| 	while (p) {
 | |
| 		next = p->next;
 | |
| 		XFREE(MTYPE_RFAPI_VN_OPTION, p);
 | |
| 		p = next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* Based on bgp_redistribute_add() */
 | |
| void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */
 | |
| 		   struct bgp *bgp, int safi, const struct prefix *p,
 | |
| 		   struct prefix_rd *prd, struct rfapi_ip_addr *nexthop,
 | |
| 		   uint32_t *local_pref,
 | |
| 		   uint32_t *lifetime, /* NULL => dont send lifetime */
 | |
| 		   struct bgp_tea_options *rfp_options,
 | |
| 		   struct rfapi_un_option *options_un,
 | |
| 		   struct rfapi_vn_option *options_vn,
 | |
| 		   struct ecommunity *rt_export_list, /* Copied, not consumed */
 | |
| 		   uint32_t *med,		   /* NULL => don't set med */
 | |
| 		   uint32_t *label,		   /* low order 3 bytes */
 | |
| 		   uint8_t type, uint8_t sub_type, /* RFP, NORMAL or REDIST */
 | |
| 		   int flags)
 | |
| {
 | |
| 	afi_t afi; /* of the VN address */
 | |
| 	struct bgp_path_info *new;
 | |
| 	struct bgp_path_info *bpi;
 | |
| 	struct bgp_dest *bn;
 | |
| 
 | |
| 	struct attr attr = {0};
 | |
| 	struct attr *new_attr;
 | |
| 	uint32_t label_val;
 | |
| 
 | |
| 	struct bgp_attr_encap_subtlv *encaptlv;
 | |
| 	char buf[PREFIX_STRLEN];
 | |
| 	char buf2[RD_ADDRSTRLEN];
 | |
| 
 | |
| 	struct rfapi_nexthop *lnh = NULL; /* local nexthop */
 | |
| 	struct rfapi_vn_option *vo;
 | |
| 	struct rfapi_l2address_option *l2o = NULL;
 | |
| 	struct rfapi_ip_addr *un_addr = &rfd->un_addr;
 | |
| 
 | |
| 	bgp_encap_types TunnelType = BGP_ENCAP_TYPE_RESERVED;
 | |
| 	struct bgp_redist *red;
 | |
| 
 | |
| 	if (safi == SAFI_ENCAP
 | |
| 	    && !(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)) {
 | |
| 
 | |
| 		/*
 | |
| 		 * Encap mode not enabled. UN addresses will be communicated
 | |
| 		 * via VNC Tunnel subtlv instead.
 | |
| 		 */
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: encap mode not enabled, not adding SAFI_ENCAP route",
 | |
| 			__func__);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	for (vo = options_vn; vo; vo = vo->next) {
 | |
| 		if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) {
 | |
| 			l2o = &vo->v.l2addr;
 | |
| 			if (RFAPI_0_ETHERADDR(&l2o->macaddr))
 | |
| 				l2o = NULL; /* not MAC resolution */
 | |
| 		}
 | |
| 		if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) {
 | |
| 			lnh = &vo->v.local_nexthop;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (label)
 | |
| 		label_val = *label;
 | |
| 	else
 | |
| 		label_val = MPLS_LABEL_IMPLICIT_NULL;
 | |
| 
 | |
| 	prefix_rd2str(prd, buf2, sizeof(buf2));
 | |
| 
 | |
| 	afi = family2afi(p->family);
 | |
| 	assert(afi == AFI_IP || afi == AFI_IP6);
 | |
| 
 | |
| 	vnc_zlog_debug_verbose("%s: afi=%s, safi=%s", __func__, afi2str(afi),
 | |
| 			       safi2str(safi));
 | |
| 
 | |
| 	/* Make default attribute. Produces already-interned attr.aspath */
 | |
| 	/* Cripes, the memory management of attributes is byzantine */
 | |
| 
 | |
| 	bgp_attr_default_set(&attr, BGP_ORIGIN_INCOMPLETE);
 | |
| 
 | |
| 	/*
 | |
| 	 * At this point:
 | |
| 	 * attr: static
 | |
| 	 *  extra: dynamically allocated, owned by attr
 | |
| 	 *  aspath: points to interned hash from aspath hash table
 | |
| 	 */
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Route-specific un_options get added to the VPN SAFI
 | |
| 	 * advertisement tunnel encap attribute.  (the per-NVE
 | |
| 	 * "default" un_options are put into the 1-per-NVE ENCAP
 | |
| 	 * SAFI advertisement). The VPN SAFI also gets the
 | |
| 	 * default un_options if there are no route-specific options.
 | |
| 	 */
 | |
| 	if (options_un) {
 | |
| 		struct rfapi_un_option *uo;
 | |
| 
 | |
| 		for (uo = options_un; uo; uo = uo->next) {
 | |
| 			if (RFAPI_UN_OPTION_TYPE_TUNNELTYPE == uo->type) {
 | |
| 				TunnelType = rfapi_tunneltype_option_to_tlv(
 | |
| 					bgp, un_addr, &uo->v.tunnel, &attr,
 | |
| 					l2o != NULL);
 | |
| 			}
 | |
| 		}
 | |
| 	} else {
 | |
| 		/*
 | |
| 		 * Add encap attr
 | |
| 		 * These are the NVE-specific "default" un_options which are
 | |
| 		 * put into the 1-per-NVE ENCAP advertisement.
 | |
| 		 */
 | |
| 		if (rfd->default_tunneltype_option.type) {
 | |
| 			TunnelType = rfapi_tunneltype_option_to_tlv(
 | |
| 				bgp, un_addr, &rfd->default_tunneltype_option,
 | |
| 				&attr, l2o != NULL);
 | |
| 		} else /* create default for local addse  */
 | |
| 			if (type == ZEBRA_ROUTE_BGP
 | |
| 			    && sub_type == BGP_ROUTE_RFP)
 | |
| 			TunnelType = rfapi_tunneltype_option_to_tlv(
 | |
| 				bgp, un_addr, NULL, &attr, l2o != NULL);
 | |
| 	}
 | |
| 
 | |
| 	if (TunnelType == BGP_ENCAP_TYPE_MPLS) {
 | |
| 		if (safi == SAFI_ENCAP) {
 | |
| 			/* Encap SAFI not used with MPLS  */
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s: mpls tunnel type, encap safi omitted",
 | |
| 				__func__);
 | |
| 			aspath_unintern(&attr.aspath); /* Unintern original. */
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (local_pref) {
 | |
| 		attr.local_pref = *local_pref;
 | |
| 		attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
 | |
| 	}
 | |
| 
 | |
| 	if (med) {
 | |
| 		attr.med = *med;
 | |
| 		attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
 | |
| 	}
 | |
| 
 | |
| 	/* override default weight assigned by bgp_attr_default_set() */
 | |
| 	attr.weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * NB: ticket 81: do not reset attr.aspath here because it would
 | |
| 	 * cause iBGP peers to drop route
 | |
| 	 */
 | |
| 
 | |
| 	/*
 | |
| 	 * Set originator ID for routes imported from BGP directly.
 | |
| 	 * These routes could be synthetic, and therefore could
 | |
| 	 * reuse the peer pointers of the routes they are derived
 | |
| 	 * from. Setting the originator ID to "us" prevents the
 | |
| 	 * wrong originator ID from being sent when this route is
 | |
| 	 * sent from a route reflector.
 | |
| 	 */
 | |
| 	if (type == ZEBRA_ROUTE_BGP_DIRECT
 | |
| 	    || type == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
 | |
| 		attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
 | |
| 		attr.originator_id = bgp->router_id;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	/* Set up vnc attribute (sub-tlv for Prefix Lifetime) */
 | |
| 	if (lifetime && *lifetime != RFAPI_INFINITE_LIFETIME) {
 | |
| 		uint32_t lt;
 | |
| 
 | |
| 		encaptlv = XCALLOC(MTYPE_ENCAP_TLV,
 | |
| 				   sizeof(struct bgp_attr_encap_subtlv) + 4);
 | |
| 		assert(encaptlv);
 | |
| 		encaptlv->type =
 | |
| 			BGP_VNC_SUBTLV_TYPE_LIFETIME; /* prefix lifetime */
 | |
| 		encaptlv->length = 4;
 | |
| 		lt = htonl(*lifetime);
 | |
| 		memcpy(encaptlv->value, <, 4);
 | |
| 		bgp_attr_set_vnc_subtlvs(&attr, encaptlv);
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: set Encap Attr Prefix Lifetime to %d", __func__,
 | |
| 			*lifetime);
 | |
| 	}
 | |
| 
 | |
| 	/* add rfp options to vnc attr */
 | |
| 	if (rfp_options) {
 | |
| 
 | |
| 		if (flags & RFAPI_AHR_RFPOPT_IS_VNCTLV) {
 | |
| 			struct bgp_attr_encap_subtlv *vnc_subtlvs =
 | |
| 				bgp_attr_get_vnc_subtlvs(&attr);
 | |
| 			/*
 | |
| 			 * this flag means we're passing a pointer to an
 | |
| 			 * existing encap tlv chain which we should copy.
 | |
| 			 * It's a hack to avoid adding yet another argument
 | |
| 			 * to add_vnc_route()
 | |
| 			 */
 | |
| 			encaptlv = encap_tlv_dup(
 | |
| 				(struct bgp_attr_encap_subtlv *)rfp_options);
 | |
| 			if (vnc_subtlvs)
 | |
| 				vnc_subtlvs->next = encaptlv;
 | |
| 			else
 | |
| 				bgp_attr_set_vnc_subtlvs(&attr, encaptlv);
 | |
| 		} else {
 | |
| 			struct bgp_tea_options *hop;
 | |
| 			/* XXX max of one tlv present so far from above code */
 | |
| 			struct bgp_attr_encap_subtlv *tail =
 | |
| 				bgp_attr_get_vnc_subtlvs(&attr);
 | |
| 
 | |
| 			for (hop = rfp_options; hop; hop = hop->next) {
 | |
| 
 | |
| 				/*
 | |
| 				 * Construct subtlv
 | |
| 				 */
 | |
| 				encaptlv = XCALLOC(
 | |
| 					MTYPE_ENCAP_TLV,
 | |
| 					sizeof(struct bgp_attr_encap_subtlv) + 2
 | |
| 						+ hop->length);
 | |
| 				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
 | |
| 					bgp_attr_set_vnc_subtlvs(&attr,
 | |
| 								 encaptlv);
 | |
| 				tail = encaptlv;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * At this point:
 | |
| 	 * attr: static
 | |
| 	 *  extra: dynamically allocated, owned by attr
 | |
| 	 *      vnc_subtlvs: dynamic chain, length 1
 | |
| 	 *  aspath: points to interned hash from aspath hash table
 | |
| 	 */
 | |
| 
 | |
| 
 | |
| 	attr.ecommunity = ecommunity_new();
 | |
| 	assert(attr.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.ecommunity, &beec, false, false);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Add extended community attributes to match rt export list
 | |
| 	 */
 | |
| 	if (rt_export_list) {
 | |
| 		attr.ecommunity =
 | |
| 			ecommunity_merge(attr.ecommunity, rt_export_list);
 | |
| 	}
 | |
| 
 | |
| 	if (attr.ecommunity->size) {
 | |
| 		attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
 | |
| 	} else {
 | |
| 		ecommunity_free(&attr.ecommunity);
 | |
| 		attr.ecommunity = NULL;
 | |
| 	}
 | |
| 	vnc_zlog_debug_verbose("%s: attr.ecommunity=%p", __func__,
 | |
| 			       attr.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.mp_nexthop_global_in = nexthop->addr.v4;
 | |
| 		attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
 | |
| 		break;
 | |
| 
 | |
| 	case AF_INET6:
 | |
| 		attr.mp_nexthop_global = nexthop->addr.v6;
 | |
| 		attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		assert(0);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	prefix2str(p, buf, sizeof(buf));
 | |
| 
 | |
| 	/*
 | |
| 	 * At this point:
 | |
| 	 *
 | |
| 	 * attr: static
 | |
| 	 *  extra: dynamically allocated, owned by attr
 | |
| 	 *      vnc_subtlvs: dynamic chain, length 1
 | |
| 	 *      ecommunity: dynamic 2-part
 | |
| 	 *  aspath: points to interned hash from aspath hash table
 | |
| 	 */
 | |
| 
 | |
| 	red = bgp_redist_lookup(bgp, afi, type, 0);
 | |
| 
 | |
| 	if (red && red->redist_metric_flag) {
 | |
| 		attr.med = red->redist_metric;
 | |
| 		attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
 | |
| 	}
 | |
| 
 | |
| 	bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
 | |
| 
 | |
| 	/*
 | |
| 	 * bgp_attr_intern creates a new reference to a cached
 | |
| 	 * attribute, but leaves the following bits of trash:
 | |
| 	 * - old attr
 | |
| 	 * - old attr->extra (free via bgp_attr_extra_free(attr))
 | |
| 	 *
 | |
| 	 * Note that it frees the original attr->extra->ecommunity
 | |
| 	 * but leaves the new attribute pointing to the ORIGINAL
 | |
| 	 * vnc options (which therefore we needn't free from the
 | |
| 	 * static attr)
 | |
| 	 */
 | |
| 	new_attr = bgp_attr_intern(&attr);
 | |
| 
 | |
| 	aspath_unintern(&attr.aspath); /* Unintern original. */
 | |
| 
 | |
| 	/*
 | |
| 	 * At this point:
 | |
| 	 *
 | |
| 	 * attr: static
 | |
| 	 *  extra: dynamically allocated, owned by attr
 | |
| 	 *      vnc_subtlvs: dynamic chain, length 1
 | |
| 	 *      ecommunity: POINTS TO INTERNED ecom, THIS REF NOT COUNTED
 | |
| 	 *
 | |
| 	 * new_attr: an attr that is part of the hash table, distinct
 | |
| 	 *           from attr which is static.
 | |
| 	 *  extra: dynamically allocated, owned by new_attr (in hash table)
 | |
| 	 *      vnc_subtlvs: POINTS TO SAME dynamic chain AS attr
 | |
| 	 *      ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr
 | |
| 	 *  aspath: POINTS TO interned/refcounted hashed block
 | |
| 	 */
 | |
| 	for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
 | |
| 		/* probably only need to check
 | |
| 		 * bpi->extra->vnc.export.rfapi_handle */
 | |
| 		if (bpi->peer == rfd->peer && bpi->type == type
 | |
| 		    && bpi->sub_type == sub_type && bpi->extra
 | |
| 		    && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) {
 | |
| 
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (bpi) {
 | |
| 
 | |
| 		/*
 | |
| 		 * Adding new local_nexthop, which does not by itself change
 | |
| 		 * what is advertised via BGP
 | |
| 		 */
 | |
| 		if (lnh) {
 | |
| 			if (!bpi->extra->vnc.export.local_nexthops) {
 | |
| 				/* TBD make arrangements to free when needed */
 | |
| 				bpi->extra->vnc.export.local_nexthops =
 | |
| 					list_new();
 | |
| 				bpi->extra->vnc.export.local_nexthops->del =
 | |
| 					rfapi_nexthop_free;
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * already present?
 | |
| 			 */
 | |
| 			struct listnode *node;
 | |
| 			struct rfapi_nexthop *pLnh = NULL;
 | |
| 
 | |
| 			for (ALL_LIST_ELEMENTS_RO(
 | |
| 				     bpi->extra->vnc.export.local_nexthops,
 | |
| 				     node, pLnh)) {
 | |
| 
 | |
| 				if (prefix_same(&pLnh->addr, &lnh->addr)) {
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/*
 | |
| 			 * Not present, add new one
 | |
| 			 */
 | |
| 			if (!pLnh) {
 | |
| 				pLnh = rfapi_nexthop_new(lnh);
 | |
| 				listnode_add(
 | |
| 					bpi->extra->vnc.export.local_nexthops,
 | |
| 					pLnh);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (attrhash_cmp(bpi->attr, new_attr)
 | |
| 		    && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
 | |
| 			bgp_attr_unintern(&new_attr);
 | |
| 			bgp_dest_unlock_node(bn);
 | |
| 
 | |
| 			vnc_zlog_debug_any(
 | |
| 				"%s: Found route (safi=%d) at prefix %s, no change",
 | |
| 				__func__, safi, buf);
 | |
| 
 | |
| 			goto done;
 | |
| 		} else {
 | |
| 			/* The attribute is changed. */
 | |
| 			bgp_path_info_set_flag(bn, bpi, BGP_PATH_ATTR_CHANGED);
 | |
| 
 | |
| 			if (safi == SAFI_MPLS_VPN) {
 | |
| 				struct bgp_dest *pdest = NULL;
 | |
| 				struct bgp_table *table = NULL;
 | |
| 
 | |
| 				pdest = bgp_node_get(bgp->rib[afi][safi],
 | |
| 						     (struct prefix *)prd);
 | |
| 				table = bgp_dest_get_bgp_table_info(pdest);
 | |
| 				if (table)
 | |
| 					vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
 | |
| 						bgp, prd, table, p, bpi);
 | |
| 				bgp_dest_unlock_node(pdest);
 | |
| 			}
 | |
| 
 | |
| 			/* Rewrite BGP route information. */
 | |
| 			if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
 | |
| 				bgp_path_info_restore(bn, bpi);
 | |
| 			else
 | |
| 				bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
 | |
| 			bgp_attr_unintern(&bpi->attr);
 | |
| 			bpi->attr = new_attr;
 | |
| 			bpi->uptime = bgp_clock();
 | |
| 
 | |
| 
 | |
| 			if (safi == SAFI_MPLS_VPN) {
 | |
| 				struct bgp_dest *pdest = NULL;
 | |
| 				struct bgp_table *table = NULL;
 | |
| 
 | |
| 				pdest = bgp_node_get(bgp->rib[afi][safi],
 | |
| 						     (struct prefix *)prd);
 | |
| 				table = bgp_dest_get_bgp_table_info(pdest);
 | |
| 				if (table)
 | |
| 					vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
 | |
| 						bgp, prd, table, p, bpi);
 | |
| 				bgp_dest_unlock_node(pdest);
 | |
| 			}
 | |
| 
 | |
| 			/* Process change. */
 | |
| 			bgp_aggregate_increment(bgp, p, bpi, afi, safi);
 | |
| 			bgp_process(bgp, bn, afi, safi);
 | |
| 			bgp_dest_unlock_node(bn);
 | |
| 
 | |
| 			vnc_zlog_debug_any(
 | |
| 				"%s: Found route (safi=%d) at prefix %s, changed attr",
 | |
| 				__func__, safi, buf);
 | |
| 
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	new = info_make(type, sub_type, 0, rfd->peer, new_attr, NULL);
 | |
| 	SET_FLAG(new->flags, BGP_PATH_VALID);
 | |
| 
 | |
| 	/* save backref to rfapi handle */
 | |
| 	assert(bgp_path_info_extra_get(new));
 | |
| 	new->extra->vnc.export.rfapi_handle = (void *)rfd;
 | |
| 	encode_label(label_val, &new->extra->label[0]);
 | |
| 
 | |
| 	/* debug */
 | |
| 
 | |
| 	if (VNC_DEBUG(VERBOSE)) {
 | |
| 		vnc_zlog_debug_verbose("%s: printing BPI", __func__);
 | |
| 		rfapiPrintBi(NULL, new);
 | |
| 	}
 | |
| 
 | |
| 	bgp_aggregate_increment(bgp, p, new, afi, safi);
 | |
| 	bgp_path_info_add(bn, new);
 | |
| 
 | |
| 	if (safi == SAFI_MPLS_VPN) {
 | |
| 		struct bgp_dest *pdest = NULL;
 | |
| 		struct bgp_table *table = NULL;
 | |
| 
 | |
| 		pdest = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd);
 | |
| 		table = bgp_dest_get_bgp_table_info(pdest);
 | |
| 		if (table)
 | |
| 			vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
 | |
| 				bgp, prd, table, p, new);
 | |
| 		bgp_dest_unlock_node(pdest);
 | |
| 		encode_label(label_val, &bn->local_label);
 | |
| 	}
 | |
| 
 | |
| 	bgp_dest_unlock_node(bn);
 | |
| 	bgp_process(bgp, bn, afi, safi);
 | |
| 
 | |
| 	vnc_zlog_debug_any(
 | |
| 		"%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%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);
 | |
| 	vnc_zlog_debug_verbose("%s: looped back import route (safi=%d)",
 | |
| 			       __func__, safi);
 | |
| }
 | |
| 
 | |
| uint32_t rfp_cost_to_localpref(uint8_t cost)
 | |
| {
 | |
| 	return 255 - cost;
 | |
| }
 | |
| 
 | |
| static void rfapiTunnelRouteAnnounce(struct bgp *bgp,
 | |
| 				     struct rfapi_descriptor *rfd,
 | |
| 				     uint32_t *pLifetime)
 | |
| {
 | |
| 	struct prefix_rd prd;
 | |
| 	struct prefix pfx_vn;
 | |
| 	int rc;
 | |
| 	uint32_t local_pref = rfp_cost_to_localpref(0);
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(&(rfd->vn_addr), &pfx_vn);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	/*
 | |
| 	 * Construct route distinguisher = 0
 | |
| 	 */
 | |
| 	memset(&prd, 0, sizeof(prd));
 | |
| 	prd.family = AF_UNSPEC;
 | |
| 	prd.prefixlen = 64;
 | |
| 
 | |
| 	add_vnc_route(rfd,	/* rfapi descr, for export list  & backref */
 | |
| 		      bgp,	/* which bgp instance */
 | |
| 		      SAFI_ENCAP, /* which SAFI */
 | |
| 		      &pfx_vn,    /* prefix to advertise */
 | |
| 		      &prd,       /* route distinguisher to use */
 | |
| 		      &rfd->un_addr, /* nexthop */
 | |
| 		      &local_pref,
 | |
| 		      pLifetime, /* max lifetime of child VPN routes */
 | |
| 		      NULL,      /* no rfp options  for ENCAP safi */
 | |
| 		      NULL,      /* rfp un options */
 | |
| 		      NULL,      /* rfp vn options */
 | |
| 		      rfd->rt_export_list, NULL, /* med */
 | |
| 		      NULL,			 /* label: default */
 | |
| 		      ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /***********************************************************************
 | |
|  *			RFP processing behavior configuration
 | |
|  ***********************************************************************/
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_set_configuration
 | |
|  *
 | |
|  * This is used to change rfapi's processing behavior based on
 | |
|  * RFP requirements.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    rfapi_rfp_cfg     Pointer to configuration structure
 | |
|  *
 | |
|  * output:
 | |
|  *    none
 | |
|  *
 | |
|  * return value:
 | |
|  *	0		Success
 | |
|  *	ENXIO		Unabled to locate configured BGP/VNC
 | |
| --------------------------------------------*/
 | |
| int rfapi_rfp_set_configuration(void *rfp_start_val, struct rfapi_rfp_cfg *new)
 | |
| {
 | |
| 	struct rfapi_rfp_cfg *rcfg;
 | |
| 	struct bgp *bgp;
 | |
| 
 | |
| 	bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 
 | |
| 	if (!new || !bgp || !bgp->rfapi_cfg)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	rcfg = &bgp->rfapi_cfg->rfp_cfg;
 | |
| 	rcfg->download_type = new->download_type;
 | |
| 	rcfg->ftd_advertisement_interval = new->ftd_advertisement_interval;
 | |
| 	rcfg->holddown_factor = new->holddown_factor;
 | |
| 
 | |
| 	if (rcfg->use_updated_response != new->use_updated_response) {
 | |
| 		rcfg->use_updated_response = new->use_updated_response;
 | |
| 		if (rcfg->use_updated_response)
 | |
| 			rfapiMonitorCallbacksOn(bgp);
 | |
| 		else
 | |
| 			rfapiMonitorCallbacksOff(bgp);
 | |
| 	}
 | |
| 	if (rcfg->use_removes != new->use_removes) {
 | |
| 		rcfg->use_removes = new->use_removes;
 | |
| 		if (rcfg->use_removes)
 | |
| 			rfapiMonitorResponseRemovalOn(bgp);
 | |
| 		else
 | |
| 			rfapiMonitorResponseRemovalOff(bgp);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_set_cb_methods
 | |
|  *
 | |
|  * Change registered callback functions for asynchronous notifications
 | |
|  * from RFAPI to the RFP client.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    methods		Pointer to struct rfapi_rfp_cb_methods containing
 | |
|  *			pointers to callback methods as described above
 | |
|  *
 | |
|  * return value:
 | |
|  *	0		Success
 | |
|  *	ENXIO		BGP or VNC not configured
 | |
|  *------------------------------------------*/
 | |
| int rfapi_rfp_set_cb_methods(void *rfp_start_val,
 | |
| 			     struct rfapi_rfp_cb_methods *methods)
 | |
| {
 | |
| 	struct rfapi *h;
 | |
| 	struct bgp *bgp;
 | |
| 
 | |
| 	bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 	if (!bgp)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	h->rfp_methods = *methods;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *			NVE Sessions
 | |
|  ***********************************************************************/
 | |
| /*
 | |
|  * Caller must supply an already-allocated rfd with the "caller"
 | |
|  * fields already set (vn_addr, un_addr, callback, cookie)
 | |
|  * The advertised_prefixes[] array elements should be NULL to
 | |
|  * have this function set them to newly-allocated radix trees.
 | |
|  */
 | |
| static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp,
 | |
| 			    struct rfapi *h, struct rfapi_nve_group_cfg *rfg)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	if (h->flags & RFAPI_INCALLBACK)
 | |
| 		return EDEADLK;
 | |
| 
 | |
| 	/*
 | |
| 	 * Fill in configured fields
 | |
| 	 */
 | |
| 
 | |
| 	/*
 | |
| 	 * If group's RD is specified as "auto", then fill in based
 | |
| 	 * on NVE's VN address
 | |
| 	 */
 | |
| 	rfd->rd = rfg->rd;
 | |
| 
 | |
| 	if (rfd->rd.family == AF_UNIX) {
 | |
| 		ret = rfapi_set_autord_from_vn(&rfd->rd, &rfd->vn_addr);
 | |
| 		if (ret != 0)
 | |
| 			return ret;
 | |
| 	}
 | |
| 	rfd->rt_export_list = (rfg->rt_export_list)
 | |
| 				      ? ecommunity_dup(rfg->rt_export_list)
 | |
| 				      : NULL;
 | |
| 	rfd->response_lifetime = rfg->response_lifetime;
 | |
| 	rfd->rfg = rfg;
 | |
| 
 | |
| 	/*
 | |
| 	 * Fill in BGP peer structure
 | |
| 	 */
 | |
| 	rfd->peer = peer_new(bgp);
 | |
| 	rfd->peer->status = Established; /* keep bgp core happy */
 | |
| 	bgp_sync_delete(rfd->peer);      /* don't need these */
 | |
| 
 | |
| 	/*
 | |
| 	 * since this peer is not on the I/O thread, this lock is not strictly
 | |
| 	 * necessary, but serves as a reminder to those who may meddle...
 | |
| 	 */
 | |
| 	frr_with_mutex(&rfd->peer->io_mtx) {
 | |
| 		// we don't need any I/O related facilities
 | |
| 		if (rfd->peer->ibuf)
 | |
| 			stream_fifo_free(rfd->peer->ibuf);
 | |
| 		if (rfd->peer->obuf)
 | |
| 			stream_fifo_free(rfd->peer->obuf);
 | |
| 
 | |
| 		if (rfd->peer->ibuf_work)
 | |
| 			ringbuf_del(rfd->peer->ibuf_work);
 | |
| 		if (rfd->peer->obuf_work)
 | |
| 			stream_free(rfd->peer->obuf_work);
 | |
| 
 | |
| 		rfd->peer->ibuf = NULL;
 | |
| 		rfd->peer->obuf = NULL;
 | |
| 		rfd->peer->obuf_work = NULL;
 | |
| 		rfd->peer->ibuf_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] = agg_table_init();                           \
 | |
| 			agg_set_table_info(ary[afi], rh);                      \
 | |
| 		}                                                              \
 | |
| 	} while (0)
 | |
| 
 | |
| #define RFD_RTINIT(rh, ary)                                                    \
 | |
| 	do {                                                                   \
 | |
| 		RFD_RTINIT_AFI(rh, ary, AFI_IP);                               \
 | |
| 		RFD_RTINIT_AFI(rh, ary, AFI_IP6);                              \
 | |
| 		RFD_RTINIT_AFI(rh, ary, AFI_L2VPN);                            \
 | |
| 	} while (0)
 | |
| 
 | |
| 	RFD_RTINIT(rfd, rfd->rib);
 | |
| 	RFD_RTINIT(rfd, rfd->rib_pending);
 | |
| 	RFD_RTINIT(rfd, rfd->rsp_times);
 | |
| 
 | |
| 	/*
 | |
| 	 * Link to Import Table
 | |
| 	 */
 | |
| 	rfd->import_table = rfg->rfapi_import_table;
 | |
| 	rfd->import_table->refcount += 1;
 | |
| 
 | |
| 	rfapiApInit(&rfd->advertised);
 | |
| 
 | |
| 	/*
 | |
| 	 * add this NVE descriptor to the list of NVEs in the NVE group
 | |
| 	 */
 | |
| 	if (!rfg->nves) {
 | |
| 		rfg->nves = list_new();
 | |
| 	}
 | |
| 	listnode_add(rfg->nves, rfd);
 | |
| 
 | |
| 	vnc_direct_bgp_add_nve(bgp, rfd);
 | |
| 	vnc_zebra_add_nve(bgp, rfd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* moved from rfapi_register */
 | |
| int rfapi_init_and_open(struct bgp *bgp, struct rfapi_descriptor *rfd,
 | |
| 			struct rfapi_nve_group_cfg *rfg)
 | |
| {
 | |
| 	struct rfapi *h = bgp->rfapi;
 | |
| 	char buf_vn[BUFSIZ];
 | |
| 	char buf_un[BUFSIZ];
 | |
| 	afi_t afi_vn, afi_un;
 | |
| 	struct prefix pfx_un;
 | |
| 	struct agg_node *rn;
 | |
| 
 | |
| 
 | |
| 	rfapi_time(&rfd->open_time);
 | |
| 
 | |
| 	if (rfg->type == RFAPI_GROUP_CFG_VRF)
 | |
| 		SET_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF);
 | |
| 
 | |
| 	rfapiRfapiIpAddr2Str(&rfd->vn_addr, buf_vn, BUFSIZ);
 | |
| 	rfapiRfapiIpAddr2Str(&rfd->un_addr, buf_un, BUFSIZ);
 | |
| 
 | |
| 	vnc_zlog_debug_verbose("%s: new RFD with VN=%s UN=%s cookie=%p",
 | |
| 			       __func__, buf_vn, buf_un, rfd->cookie);
 | |
| 
 | |
| 	if (rfg->type != RFAPI_GROUP_CFG_VRF) /* unclear if needed for VRF */
 | |
| 	{
 | |
| 		listnode_add(&h->descriptors, rfd);
 | |
| 		if (h->descriptors.count > h->stat.max_descriptors) {
 | |
| 			h->stat.max_descriptors = h->descriptors.count;
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * attach to UN radix tree
 | |
| 		 */
 | |
| 		afi_vn = family2afi(rfd->vn_addr.addr_family);
 | |
| 		afi_un = family2afi(rfd->un_addr.addr_family);
 | |
| 		assert(afi_vn && afi_un);
 | |
| 		assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un));
 | |
| 
 | |
| 		rn = agg_node_get(h->un[afi_un], &pfx_un);
 | |
| 		assert(rn);
 | |
| 		rfd->next = rn->info;
 | |
| 		rn->info = rfd;
 | |
| 		rfd->un_node = rn;
 | |
| 	}
 | |
| 	return rfapi_open_inner(rfd, bgp, h, rfg);
 | |
| }
 | |
| 
 | |
| struct rfapi_vn_option *rfapiVnOptionsDup(struct rfapi_vn_option *orig)
 | |
| {
 | |
| 	struct rfapi_vn_option *head = NULL;
 | |
| 	struct rfapi_vn_option *tail = NULL;
 | |
| 	struct rfapi_vn_option *vo = NULL;
 | |
| 
 | |
| 	for (vo = orig; vo; vo = vo->next) {
 | |
| 		struct rfapi_vn_option *new;
 | |
| 
 | |
| 		new = XCALLOC(MTYPE_RFAPI_VN_OPTION,
 | |
| 			      sizeof(struct rfapi_vn_option));
 | |
| 		memcpy(new, vo, sizeof(struct rfapi_vn_option));
 | |
| 		new->next = NULL;
 | |
| 
 | |
| 		if (tail) {
 | |
| 			tail->next = new;
 | |
| 		} else {
 | |
| 			head = tail = new;
 | |
| 		}
 | |
| 	}
 | |
| 	return head;
 | |
| }
 | |
| 
 | |
| struct rfapi_un_option *rfapiUnOptionsDup(struct rfapi_un_option *orig)
 | |
| {
 | |
| 	struct rfapi_un_option *head = NULL;
 | |
| 	struct rfapi_un_option *tail = NULL;
 | |
| 	struct rfapi_un_option *uo = NULL;
 | |
| 
 | |
| 	for (uo = orig; uo; uo = uo->next) {
 | |
| 		struct rfapi_un_option *new;
 | |
| 
 | |
| 		new = XCALLOC(MTYPE_RFAPI_UN_OPTION,
 | |
| 			      sizeof(struct rfapi_un_option));
 | |
| 		memcpy(new, uo, sizeof(struct rfapi_un_option));
 | |
| 		new->next = NULL;
 | |
| 
 | |
| 		if (tail) {
 | |
| 			tail->next = new;
 | |
| 		} else {
 | |
| 			head = tail = new;
 | |
| 		}
 | |
| 	}
 | |
| 	return head;
 | |
| }
 | |
| 
 | |
| struct bgp_tea_options *rfapiOptionsDup(struct bgp_tea_options *orig)
 | |
| {
 | |
| 	struct bgp_tea_options *head = NULL;
 | |
| 	struct bgp_tea_options *tail = NULL;
 | |
| 	struct bgp_tea_options *hop = NULL;
 | |
| 
 | |
| 	for (hop = orig; hop; hop = hop->next) {
 | |
| 		struct bgp_tea_options *new;
 | |
| 
 | |
| 		new = XCALLOC(MTYPE_BGP_TEA_OPTIONS,
 | |
| 			      sizeof(struct bgp_tea_options));
 | |
| 		memcpy(new, hop, sizeof(struct bgp_tea_options));
 | |
| 		new->next = NULL;
 | |
| 		if (hop->value) {
 | |
| 			new->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE,
 | |
| 					     hop->length);
 | |
| 			memcpy(new->value, hop->value, hop->length);
 | |
| 		}
 | |
| 		if (tail) {
 | |
| 			tail->next = new;
 | |
| 		} else {
 | |
| 			head = tail = new;
 | |
| 		}
 | |
| 	}
 | |
| 	return head;
 | |
| }
 | |
| 
 | |
| void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p)
 | |
| {
 | |
| 	struct bgp_tea_options *next;
 | |
| 
 | |
| 	while (p) {
 | |
| 		next = p->next;
 | |
| 
 | |
| 		XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value);
 | |
| 		XFREE(MTYPE_BGP_TEA_OPTIONS, p);
 | |
| 
 | |
| 		p = next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void rfapiAdbFree(struct rfapi_adb *adb)
 | |
| {
 | |
| 	XFREE(MTYPE_RFAPI_ADB, adb);
 | |
| }
 | |
| 
 | |
| static int
 | |
| rfapi_query_inner(void *handle, struct rfapi_ip_addr *target,
 | |
| 		  struct rfapi_l2address_option *l2o, /* may be NULL */
 | |
| 		  struct rfapi_next_hop_entry **ppNextHopEntry)
 | |
| {
 | |
| 	afi_t afi;
 | |
| 	struct prefix p;
 | |
| 	struct prefix p_original;
 | |
| 	struct agg_node *rn;
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	struct bgp *bgp = rfd->bgp;
 | |
| 	struct rfapi_next_hop_entry *pNHE = NULL;
 | |
| 	struct rfapi_ip_addr *self_vn_addr = NULL;
 | |
| 	int eth_is_0 = 0;
 | |
| 	int use_eth_resolution = 0;
 | |
| 	struct rfapi_next_hop_entry *i_nhe;
 | |
| 
 | |
| 	/* preemptive */
 | |
| 	if (!bgp) {
 | |
| 		vnc_zlog_debug_verbose("%s: No BGP instance, returning ENXIO",
 | |
| 				       __func__);
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 	if (!bgp->rfapi) {
 | |
| 		vnc_zlog_debug_verbose("%s: No RFAPI instance, returning ENXIO",
 | |
| 				       __func__);
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 	if (bgp->rfapi->flags & RFAPI_INCALLBACK) {
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: Called during calback, returning EDEADLK",
 | |
| 			__func__);
 | |
| 		return EDEADLK;
 | |
| 	}
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd)) {
 | |
| 		vnc_zlog_debug_verbose("%s: invalid handle, returning EBADF",
 | |
| 				       __func__);
 | |
| 		return EBADF;
 | |
| 	}
 | |
| 
 | |
| 	rfd->rsp_counter++;		  /* dedup: identify this generation */
 | |
| 	rfd->rsp_time = 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,
 | |
| 			    ETH_ALEN)) {
 | |
| 			eth_is_0 = 1;
 | |
| 		}
 | |
| 		/* per t/c Paul/Lou 151022 */
 | |
| 		if (!eth_is_0 || l2o->logical_net_id) {
 | |
| 			use_eth_resolution = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ppNextHopEntry)
 | |
| 		*ppNextHopEntry = NULL;
 | |
| 
 | |
| 	/*
 | |
| 	 * Save original target in prefix form. In case of L2-based queries,
 | |
| 	 * p_original will be modified to reflect the L2 target
 | |
| 	 */
 | |
| 	assert(!rfapiRaddr2Qprefix(target, &p_original));
 | |
| 
 | |
| 	if (bgp->rfapi_cfg->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL) {
 | |
| 		/* convert query to 0/0 when full-table download is enabled */
 | |
| 		memset((char *)&p, 0, sizeof(p));
 | |
| 		p.family = target->addr_family;
 | |
| 	} else {
 | |
| 		p = p_original;
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		char *s;
 | |
| 
 | |
| 		vnc_zlog_debug_verbose("%s(rfd=%p, target=%pFX, ppNextHop=%p)",
 | |
| 				       __func__, rfd, &p, ppNextHopEntry);
 | |
| 
 | |
| 		s = ecommunity_ecom2str(rfd->import_table->rt_import_list,
 | |
| 					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s rfd->import_table=%p, rfd->import_table->rt_import_list: %s",
 | |
| 			__func__, rfd->import_table, s);
 | |
| 		XFREE(MTYPE_ECOMMUNITY_STR, s);
 | |
| 	}
 | |
| 
 | |
| 	afi = family2afi(p.family);
 | |
| 	assert(afi);
 | |
| 
 | |
| 	if (CHECK_FLAG(bgp->rfapi_cfg->flags,
 | |
| 		       BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) {
 | |
| 		self_vn_addr = &rfd->vn_addr;
 | |
| 	}
 | |
| 
 | |
| 	if (use_eth_resolution) {
 | |
| 		uint32_t logical_net_id = l2o->logical_net_id;
 | |
| 		struct ecommunity *l2com;
 | |
| 
 | |
| 		/*
 | |
| 		 * fix up p_original to contain L2 address
 | |
| 		 */
 | |
| 		rfapiL2o2Qprefix(l2o, &p_original);
 | |
| 
 | |
| 		l2com = bgp_rfapi_get_ecommunity_by_lni_label(
 | |
| 			bgp, 1, logical_net_id, l2o->label);
 | |
| 		if (l2com) {
 | |
| 			uint8_t *v = l2com->val;
 | |
| 			logical_net_id = (v[5] << 16) + (v[6] << 8) + (v[7]);
 | |
| 		}
 | |
| 		/*
 | |
| 		 * Ethernet/L2-based lookup
 | |
| 		 *
 | |
| 		 * Always returns IT node corresponding to route
 | |
| 		 */
 | |
| 
 | |
| 		if (RFAPI_RFP_DOWNLOAD_FULL
 | |
| 		    == bgp->rfapi_cfg->rfp_cfg.download_type) {
 | |
| 			eth_is_0 = 1;
 | |
| 		}
 | |
| 
 | |
| 		rn = rfapiMonitorEthAdd(
 | |
| 			bgp, rfd, (eth_is_0 ? &rfapi_ethaddr0 : &l2o->macaddr),
 | |
| 			logical_net_id);
 | |
| 
 | |
| 		if (eth_is_0) {
 | |
| 			struct rfapi_ip_prefix rprefix;
 | |
| 
 | |
| 			memset(&rprefix, 0, sizeof(rprefix));
 | |
| 			rprefix.prefix.addr_family = target->addr_family;
 | |
| 			if (target->addr_family == AF_INET) {
 | |
| 				rprefix.length = IPV4_MAX_BITLEN;
 | |
| 			} else {
 | |
| 				rprefix.length = IPV6_MAX_BITLEN;
 | |
| 			}
 | |
| 
 | |
| 			pNHE = rfapiEthRouteTable2NextHopList(
 | |
| 				logical_net_id, &rprefix,
 | |
| 				rfd->response_lifetime, self_vn_addr,
 | |
| 				rfd->rib[afi], &p_original);
 | |
| 			goto done;
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		/*
 | |
| 		 * IP-based lookup
 | |
| 		 */
 | |
| 
 | |
| 		rn = rfapiMonitorAdd(bgp, rfd, &p);
 | |
| 
 | |
| 		/*
 | |
| 		 * If target address is 0, this request is special: means to
 | |
| 		 * return ALL routes in the table
 | |
| 		 *
 | |
| 		 * Monitors for All-Routes queries get put on a special list,
 | |
| 		 * not in the VPN tree
 | |
| 		 */
 | |
| 		if (RFAPI_0_PREFIX(&p)) {
 | |
| 
 | |
| 			vnc_zlog_debug_verbose("%s: 0-prefix", __func__);
 | |
| 
 | |
| 			/*
 | |
| 			 * Generate nexthop list for caller
 | |
| 			 */
 | |
| 			pNHE = rfapiRouteTable2NextHopList(
 | |
| 				rfd->import_table->imported_vpn[afi],
 | |
| 				rfd->response_lifetime, self_vn_addr,
 | |
| 				rfd->rib[afi], &p_original);
 | |
| 			goto done;
 | |
| 		}
 | |
| 
 | |
| 		if (rn) {
 | |
| 			agg_lock_node(rn); /* so we can unlock below */
 | |
| 		} else {
 | |
| 			/*
 | |
| 			 * returns locked node. Don't unlock yet because the
 | |
| 			 * unlock
 | |
| 			 * might free it before we're done with it. This
 | |
| 			 * situation
 | |
| 			 * could occur when rfapiMonitorGetAttachNode() returns
 | |
| 			 * a
 | |
| 			 * newly-created default node.
 | |
| 			 */
 | |
| 			rn = rfapiMonitorGetAttachNode(rfd, &p);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	assert(rn);
 | |
| 	if (!rn->info) {
 | |
| 		agg_unlock_node(rn);
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: VPN route not found, returning ENOENT", __func__);
 | |
| 		return ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	if (VNC_DEBUG(RFAPI_QUERY)) {
 | |
| 		rfapiShowImportTable(NULL, "query",
 | |
| 				     rfd->import_table->imported_vpn[afi], 1);
 | |
| 	}
 | |
| 
 | |
| 	if (use_eth_resolution) {
 | |
| 
 | |
| 		struct rfapi_ip_prefix rprefix;
 | |
| 
 | |
| 		memset(&rprefix, 0, sizeof(rprefix));
 | |
| 		rprefix.prefix.addr_family = target->addr_family;
 | |
| 		if (target->addr_family == AF_INET) {
 | |
| 			rprefix.length = IPV4_MAX_BITLEN;
 | |
| 		} else {
 | |
| 			rprefix.length = IPV6_MAX_BITLEN;
 | |
| 		}
 | |
| 
 | |
| 		pNHE = rfapiEthRouteNode2NextHopList(
 | |
| 			rn, &rprefix, rfd->response_lifetime, self_vn_addr,
 | |
| 			rfd->rib[afi], &p_original);
 | |
| 
 | |
| 
 | |
| 	} else {
 | |
| 		/*
 | |
| 		 * Generate answer to query
 | |
| 		 */
 | |
| 		pNHE = rfapiRouteNode2NextHopList(rn, rfd->response_lifetime,
 | |
| 						  self_vn_addr, rfd->rib[afi],
 | |
| 						  &p_original);
 | |
| 	}
 | |
| 
 | |
| 	agg_unlock_node(rn);
 | |
| 
 | |
| done:
 | |
| 	if (ppNextHopEntry) {
 | |
| 		/* only count if caller gets it */
 | |
| 		++bgp->rfapi->response_immediate_count;
 | |
| 	}
 | |
| 
 | |
| 	if (!pNHE) {
 | |
| 		vnc_zlog_debug_verbose("%s: NO NHEs, returning ENOENT",
 | |
| 				       __func__);
 | |
| 		return ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * count nexthops for statistics
 | |
| 	 */
 | |
| 	for (i_nhe = pNHE; i_nhe; i_nhe = i_nhe->next) {
 | |
| 		++rfd->stat_count_nh_reachable;
 | |
| 	}
 | |
| 
 | |
| 	if (ppNextHopEntry) {
 | |
| 		*ppNextHopEntry = pNHE;
 | |
| 	} else {
 | |
| 		rfapi_free_next_hop_list(pNHE);
 | |
| 	}
 | |
| 
 | |
| 	vnc_zlog_debug_verbose("%s: success", __func__);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * support on-the-fly reassignment of an already-open nve to a new
 | |
|  * nve-group in the event that its original nve-group is
 | |
|  * administratively deleted.
 | |
|  */
 | |
| static int rfapi_open_rfd(struct rfapi_descriptor *rfd, struct bgp *bgp)
 | |
| {
 | |
| 	struct prefix pfx_vn;
 | |
| 	struct prefix pfx_un;
 | |
| 	struct rfapi_nve_group_cfg *rfg;
 | |
| 	struct rfapi *h;
 | |
| 	struct rfapi_cfg *hc;
 | |
| 	int rc;
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	hc = bgp->rfapi_cfg;
 | |
| 	if (!hc)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	/*
 | |
| 	 * Find the matching nve group config block
 | |
| 	 */
 | |
| 	rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un);
 | |
| 	if (!rfg) {
 | |
| 		return ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * check nve group config block for required values
 | |
| 	 */
 | |
| 	if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
 | |
| 
 | |
| 		return ENOMSG;
 | |
| 	}
 | |
| 
 | |
| 	rc = rfapi_open_inner(rfd, bgp, h, rfg);
 | |
| 	if (rc) {
 | |
| 		return rc;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * re-advertise registered routes, this time as part of new NVE-group
 | |
| 	 */
 | |
| 	rfapiApReadvertiseAll(bgp, rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * re-attach callbacks to import table
 | |
| 	 */
 | |
| 	if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
 | |
| 		rfapiMonitorAttachImportHd(rfd);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_open
 | |
|  *
 | |
|  * This function initializes a NVE record and associates it with
 | |
|  * the specified VN and underlay network addresses
 | |
|  *
 | |
|  * input:
 | |
|  *      rfp_start_val   value returned by rfp_start
 | |
|  *	vn		NVE virtual network address
 | |
|  *
 | |
|  *	un		NVE underlay network address
 | |
|  *
 | |
|  *	default_options	Default options to use on registrations.
 | |
|  *			For now only tunnel type is supported.
 | |
|  *			May be overridden per-prefix in rfapi_register().
 | |
|  *			Caller owns (rfapi_open() does not free)
 | |
|  *
 | |
|  *	response_cb	Pointer to next hop list update callback function or
 | |
|  *			NULL when no callbacks are desired.
 | |
|  *
 | |
|  *	userdata	Passed to subsequent response_cb invocations.
 | |
|  *
 | |
|  * output:
 | |
|  *	response_lifetime The length of time that responses sent to this
 | |
|  *			NVE are valid.
 | |
|  *
 | |
|  *	pHandle		pointer to location to store rfapi handle. The
 | |
|  *			handle must be passed on subsequent rfapi_ calls.
 | |
|  *
 | |
|  *
 | |
|  * return value:
 | |
|  *	0		Success
 | |
|  *	EEXIST		NVE with this {vn,un} already open
 | |
|  *	ENOENT		No matching nve group config
 | |
|  *	ENOMSG		Matched nve group config was incomplete
 | |
|  *	ENXIO		BGP or VNC not configured
 | |
|  *	EAFNOSUPPORT	Matched nve group specifies auto-assignment of RD,
 | |
|  *			but underlay network address is not IPv4
 | |
|  *	EDEADLK		Called from within a callback procedure
 | |
|  *------------------------------------------*/
 | |
| int rfapi_open(void *rfp_start_val, struct rfapi_ip_addr *vn,
 | |
| 	       struct rfapi_ip_addr *un,
 | |
| 	       struct rfapi_un_option *default_options,
 | |
| 	       uint32_t *response_lifetime,
 | |
| 	       void *userdata, /* callback cookie */
 | |
| 	       rfapi_handle *pHandle)
 | |
| {
 | |
| 	struct bgp *bgp;
 | |
| 	struct rfapi *h;
 | |
| 	struct rfapi_descriptor *rfd;
 | |
| 	struct rfapi_cfg *hc;
 | |
| 	struct rfapi_nve_group_cfg *rfg;
 | |
| 
 | |
| 	struct prefix pfx_vn;
 | |
| 	struct prefix pfx_un;
 | |
| 
 | |
| 	int rc;
 | |
| 	rfapi_handle hh = NULL;
 | |
| 	int reusing_provisional = 0;
 | |
| 
 | |
| 	{
 | |
| 		char buf[2][INET_ADDRSTRLEN];
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: VN=%s UN=%s", __func__,
 | |
| 			rfapiRfapiIpAddr2Str(vn, buf[0], INET_ADDRSTRLEN),
 | |
| 			rfapiRfapiIpAddr2Str(un, buf[1], INET_ADDRSTRLEN));
 | |
| 	}
 | |
| 
 | |
| 	assert(pHandle);
 | |
| 	*pHandle = NULL;
 | |
| 
 | |
| 	bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 	if (!bgp)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	hc = bgp->rfapi_cfg;
 | |
| 	if (!hc)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	if (h->flags & RFAPI_INCALLBACK)
 | |
| 		return EDEADLK;
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(vn, &pfx_vn);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(un, &pfx_un);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	/*
 | |
| 	 * already have a descriptor with VN and UN?
 | |
| 	 */
 | |
| 	if (!rfapi_find_handle(bgp, vn, un, &hh)) {
 | |
| 		/*
 | |
| 		 * we might have set up a handle for static routes before
 | |
| 		 * this NVE was opened. In that case, reuse the handle
 | |
| 		 */
 | |
| 		rfd = hh;
 | |
| 		if (!CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_PROVISIONAL)) {
 | |
| 			return EEXIST;
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * reuse provisional descriptor
 | |
| 		 * hh is not NULL
 | |
| 		 */
 | |
| 		reusing_provisional = 1;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Find the matching nve group config block
 | |
| 	 */
 | |
| 	rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un);
 | |
| 	if (!rfg) {
 | |
| 		++h->stat.count_unknown_nves;
 | |
| 		{
 | |
| 			char buf[2][INET_ADDRSTRLEN];
 | |
| 			zlog_notice("%s: no matching group VN=%s UN=%s",
 | |
| 				    __func__,
 | |
| 				    rfapiRfapiIpAddr2Str(vn, buf[0],
 | |
| 							 INET_ADDRSTRLEN),
 | |
| 				    rfapiRfapiIpAddr2Str(un, buf[1],
 | |
| 							 INET_ADDRSTRLEN));
 | |
| 		}
 | |
| 		return ENOENT;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * check nve group config block for required values
 | |
| 	 */
 | |
| 	if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
 | |
| 
 | |
| 		++h->stat.count_unknown_nves;
 | |
| 		return ENOMSG;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * If group config specifies auto-rd assignment, check that
 | |
| 	 * VN address is IPv4|v6 so we don't fail in rfapi_open_inner().
 | |
| 	 * Check here so we don't need to unwind memory allocations, &c.
 | |
| 	 */
 | |
| 	if ((rfg->rd.family == AF_UNIX) && (vn->addr_family != AF_INET)
 | |
| 	    && (vn->addr_family != AF_INET6)) {
 | |
| 		return EAFNOSUPPORT;
 | |
| 	}
 | |
| 
 | |
| 	if (hh) {
 | |
| 		/*
 | |
| 		 * reusing provisional rfd
 | |
| 		 */
 | |
| 		rfd = hh;
 | |
| 	} else {
 | |
| 		rfd = XCALLOC(MTYPE_RFAPI_DESC,
 | |
| 			      sizeof(struct rfapi_descriptor));
 | |
| 	}
 | |
| 	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) {
 | |
| 		rc = rfapi_init_and_open(bgp, rfd, rfg);
 | |
| 		/*
 | |
| 		 * This can fail only if the VN address is IPv6 and the group
 | |
| 		 * specified auto-assignment of RDs, which only works for v4,
 | |
| 		 * and the check above should catch it.
 | |
| 		 *
 | |
| 		 * Another failure possibility is that we were called
 | |
| 		 * during an rfapi callback. Also checked above.
 | |
| 		 */
 | |
| 		assert(!rc);
 | |
| 	}
 | |
| 
 | |
| 	if (response_lifetime)
 | |
| 		*response_lifetime = rfd->response_lifetime;
 | |
| 	*pHandle = rfd;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * For use with debug functions
 | |
|  */
 | |
| static int rfapi_set_response_cb(struct rfapi_descriptor *rfd,
 | |
| 				 rfapi_response_cb_t *response_cb)
 | |
| {
 | |
| 	if (!is_valid_rfd(rfd))
 | |
| 		return EBADF;
 | |
| 	rfd->response_cb = response_cb;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * rfapi_close_inner
 | |
|  *
 | |
|  * Does almost all the work of rfapi_close, except:
 | |
|  *	1. preserves the descriptor (doesn't free it)
 | |
|  *	2. preserves the prefix query list (i.e., rfd->mon list)
 | |
|  *	3. preserves the advertised prefix list (rfd->advertised)
 | |
|  *	4. preserves the rib and rib_pending tables
 | |
|  *
 | |
|  * The purpose of organizing it this way is to support on-the-fly
 | |
|  * reassignment of an already-open nve to a new nve-group in the
 | |
|  * event that its original nve-group is administratively deleted.
 | |
|  */
 | |
| static int rfapi_close_inner(struct rfapi_descriptor *rfd, struct bgp *bgp)
 | |
| {
 | |
| 	int rc;
 | |
| 	struct prefix pfx_vn;
 | |
| 	struct prefix_rd prd; /* currently always 0 for VN->UN */
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd))
 | |
| 		return EBADF;
 | |
| 
 | |
| 	rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn);
 | |
| 	assert(!rc); /* should never have bad AF in stored vn address */
 | |
| 
 | |
| 	/*
 | |
| 	 * update exported routes to reflect disappearance of this NVE as
 | |
| 	 * nexthop
 | |
| 	 */
 | |
| 	vnc_direct_bgp_del_nve(bgp, rfd);
 | |
| 	vnc_zebra_del_nve(bgp, rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * unlink this HD's monitors from import table
 | |
| 	 */
 | |
| 	rfapiMonitorDetachImportHd(rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * Unlink from Import Table
 | |
| 	 * NB rfd->import_table will be NULL if we are closing a stale
 | |
| 	 * descriptor
 | |
| 	 */
 | |
| 	if (rfd->import_table)
 | |
| 		rfapiImportTableRefDelByIt(bgp, rfd->import_table);
 | |
| 	rfd->import_table = NULL;
 | |
| 
 | |
| 	/*
 | |
| 	 * Construct route distinguisher
 | |
| 	 */
 | |
| 	memset(&prd, 0, sizeof(prd));
 | |
| 	prd = rfd->rd;
 | |
| 	prd.family = AF_UNSPEC;
 | |
| 	prd.prefixlen = 64;
 | |
| 
 | |
| 	/*
 | |
| 	 * withdraw tunnel
 | |
| 	 */
 | |
| 	del_vnc_route(rfd, rfd->peer, bgp, SAFI_ENCAP,
 | |
| 		      &pfx_vn, /* prefix being advertised */
 | |
| 		      &prd,    /* route distinguisher to use (0 for ENCAP) */
 | |
| 		      ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); /* no kill */
 | |
| 
 | |
| 	/*
 | |
| 	 * Construct route distinguisher for VPN routes
 | |
| 	 */
 | |
| 	prd = rfd->rd;
 | |
| 	prd.family = AF_UNSPEC;
 | |
| 	prd.prefixlen = 64;
 | |
| 
 | |
| 	/*
 | |
| 	 * find all VPN routes associated with this rfd and delete them, too
 | |
| 	 */
 | |
| 	rfapiApWithdrawAll(bgp, rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * remove this nve descriptor from the list of nves
 | |
| 	 * associated with the nve group
 | |
| 	 */
 | |
| 	if (rfd->rfg) {
 | |
| 		listnode_delete(rfd->rfg->nves, rfd);
 | |
| 		rfd->rfg = NULL; /* XXX mark as orphaned/stale */
 | |
| 	}
 | |
| 
 | |
| 	if (rfd->rt_export_list)
 | |
| 		ecommunity_free(&rfd->rt_export_list);
 | |
| 	rfd->rt_export_list = NULL;
 | |
| 
 | |
| 	/*
 | |
| 	 * free peer structure (possibly delayed until its
 | |
| 	 * refcount reaches zero)
 | |
| 	 */
 | |
| 	if (rfd->peer) {
 | |
| 		vnc_zlog_debug_verbose("%s: calling peer_delete(%p), #%d",
 | |
| 				       __func__, rfd->peer, rfd->peer->lock);
 | |
| 		peer_delete(rfd->peer);
 | |
| 	}
 | |
| 	rfd->peer = NULL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rfapi_close(void *handle)
 | |
| {
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	int rc;
 | |
| 	struct agg_node *node;
 | |
| 	struct bgp *bgp;
 | |
| 	struct rfapi *h;
 | |
| 
 | |
| 	vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
 | |
| 
 | |
| #ifdef RFAPI_WHO_IS_CALLING_ME
 | |
| #ifdef HAVE_GLIBC_BACKTRACE
 | |
| #define RFAPI_DEBUG_BACKTRACE_NENTRIES 5
 | |
| 	{
 | |
| 		void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES];
 | |
| 		char **syms;
 | |
| 		int i;
 | |
| 		size_t size;
 | |
| 
 | |
| 		size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES);
 | |
| 		syms = backtrace_symbols(buf, size);
 | |
| 		for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES;
 | |
| 		     ++i) {
 | |
| 			vnc_zlog_debug_verbose("backtrace[%2d]: %s", i,
 | |
| 					       syms[i]);
 | |
| 		}
 | |
| 		free(syms);
 | |
| 	}
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| 	bgp = rfd->bgp;
 | |
| 	if (!bgp)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd))
 | |
| 		return EBADF;
 | |
| 
 | |
| 	if (h->flags & RFAPI_INCALLBACK) {
 | |
| 		/*
 | |
| 		 * Queue these close requests for processing after callback
 | |
| 		 * is finished
 | |
| 		 */
 | |
| 		if (!CHECK_FLAG(rfd->flags,
 | |
| 				RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) {
 | |
| 			work_queue_add(h->deferred_close_q, handle);
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s: added handle %p to deferred close queue",
 | |
| 				__func__, handle);
 | |
| 		}
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) {
 | |
| 
 | |
| 		vnc_zlog_debug_verbose("%s administrative close rfd=%p",
 | |
| 				       __func__, rfd);
 | |
| 
 | |
| 		if (h->rfp_methods.close_cb) {
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s calling close callback rfd=%p", __func__,
 | |
| 				rfd);
 | |
| 
 | |
| 			/*
 | |
| 			 * call the callback fairly early so that it can still
 | |
| 			 * lookup un/vn
 | |
| 			 * from handle, etc.
 | |
| 			 *
 | |
| 			 * NB RFAPI_INCALLBACK is tested above, so if we reach
 | |
| 			 * this point
 | |
| 			 * we are not already in the context of a callback.
 | |
| 			 */
 | |
| 			h->flags |= RFAPI_INCALLBACK;
 | |
| 			(*h->rfp_methods.close_cb)(handle, EIDRM);
 | |
| 			h->flags &= ~RFAPI_INCALLBACK;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (rfd->rfg) {
 | |
| 		/*
 | |
| 		 * Orphaned descriptors have already done this part, so do
 | |
| 		 * only for non-orphaned descriptors.
 | |
| 		 */
 | |
| 		if ((rc = rfapi_close_inner(rfd, bgp)))
 | |
| 			return rc;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Remove descriptor from UN index
 | |
| 	 * (remove from chain at node)
 | |
| 	 */
 | |
| 	rc = rfapi_find_node(bgp, &rfd->vn_addr, &rfd->un_addr, &node);
 | |
| 	if (!rc) {
 | |
| 		struct rfapi_descriptor *hh;
 | |
| 
 | |
| 		if (node->info == rfd) {
 | |
| 			node->info = rfd->next;
 | |
| 		} else {
 | |
| 
 | |
| 			for (hh = node->info; hh; hh = hh->next) {
 | |
| 				if (hh->next == rfd) {
 | |
| 					hh->next = rfd->next;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		agg_unlock_node(node);
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * remove from descriptor list
 | |
| 	 */
 | |
| 	listnode_delete(&h->descriptors, rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * Delete monitor list items and free monitor structures
 | |
| 	 */
 | |
| 	(void)rfapiMonitorDelHd(rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * release advertised prefix data
 | |
| 	 */
 | |
| 	rfapiApRelease(&rfd->advertised);
 | |
| 
 | |
| 	/*
 | |
| 	 * Release RFP callback RIB
 | |
| 	 */
 | |
| 	rfapiRibFree(rfd);
 | |
| 
 | |
| 	/*
 | |
| 	 * free descriptor
 | |
| 	 */
 | |
| 	memset(rfd, 0, sizeof(struct rfapi_descriptor));
 | |
| 	XFREE(MTYPE_RFAPI_DESC, rfd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Reopen a nve descriptor. If the descriptor's NVE-group
 | |
|  * does not exist (e.g., if it has been administratively removed),
 | |
|  * reassignment to a new NVE-group is attempted.
 | |
|  *
 | |
|  * If NVE-group reassignment fails, the descriptor becomes "stale"
 | |
|  * (rfd->rfg == NULL implies "stale:). The only permissible API operation
 | |
|  * on a stale descriptor is rfapi_close(). Any other rfapi_* API operation
 | |
|  * on the descriptor will return ESTALE.
 | |
|  *
 | |
|  * Reopening a descriptor is a potentially expensive operation, because
 | |
|  * it involves withdrawing any routes advertised by the NVE, withdrawing
 | |
|  * the NVE's route queries, and then re-adding them all after a new
 | |
|  * NVE-group is assigned. There are also possible route-export affects
 | |
|  * caused by deleting and then adding the NVE: advertised prefixes
 | |
|  * and nexthop lists for exported routes can turn over.
 | |
|  */
 | |
| int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp)
 | |
| {
 | |
| 	struct rfapi *h;
 | |
| 	int rc;
 | |
| 
 | |
| 	if ((rc = rfapi_close_inner(rfd, bgp))) {
 | |
| 		return rc;
 | |
| 	}
 | |
| 	if ((rc = rfapi_open_rfd(rfd, bgp))) {
 | |
| 
 | |
| 		h = bgp->rfapi;
 | |
| 
 | |
| 		assert(h != NULL && !CHECK_FLAG(h->flags, RFAPI_INCALLBACK));
 | |
| 
 | |
| 		if (CHECK_FLAG(rfd->flags,
 | |
| 			       RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)
 | |
| 		    && h && h->rfp_methods.close_cb) {
 | |
| 
 | |
| 			/*
 | |
| 			 * NB RFAPI_INCALLBACK is tested above, so if we reach
 | |
| 			 * this point
 | |
| 			 * we are not already in the context of a callback.
 | |
| 			 */
 | |
| 			h->flags |= RFAPI_INCALLBACK;
 | |
| 			(*h->rfp_methods.close_cb)((rfapi_handle)rfd, ESTALE);
 | |
| 			h->flags &= ~RFAPI_INCALLBACK;
 | |
| 		}
 | |
| 		return rc;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *			NVE Routes
 | |
|  ***********************************************************************/
 | |
| /*
 | |
|  * Announce reachability to this prefix via the NVE
 | |
|  */
 | |
| int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix,
 | |
| 		   uint32_t lifetime, /* host byte order */
 | |
| 		   struct rfapi_un_option *options_un,
 | |
| 		   struct rfapi_vn_option *options_vn,
 | |
| 		   rfapi_register_action action)
 | |
| {
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	struct bgp *bgp;
 | |
| 	struct prefix p;
 | |
| 	struct prefix *pfx_ip = NULL;
 | |
| 	struct prefix_rd prd;
 | |
| 	afi_t afi;
 | |
| 	struct prefix pfx_mac_buf;
 | |
| 	struct prefix *pfx_mac = NULL;
 | |
| 	struct prefix pfx_vn_buf;
 | |
| 	const char *action_str = NULL;
 | |
| 	uint32_t *label = NULL;
 | |
| 	struct rfapi_vn_option *vo;
 | |
| 	struct rfapi_l2address_option *l2o = NULL;
 | |
| 	struct prefix_rd *prd_override = NULL;
 | |
| 
 | |
| 	switch (action) {
 | |
| 	case RFAPI_REGISTER_ADD:
 | |
| 		action_str = "add";
 | |
| 		break;
 | |
| 	case RFAPI_REGISTER_WITHDRAW:
 | |
| 		action_str = "withdraw";
 | |
| 		break;
 | |
| 	case RFAPI_REGISTER_KILL:
 | |
| 		action_str = "kill";
 | |
| 		break;
 | |
| 	default:
 | |
| 		assert(0);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Inspect VN options
 | |
| 	 */
 | |
| 	for (vo = options_vn; vo; vo = vo->next) {
 | |
| 		if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) {
 | |
| 			l2o = &vo->v.l2addr;
 | |
| 		}
 | |
| 		if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type) {
 | |
| 			prd_override = &vo->v.internal_rd;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/*********************************************************************
 | |
| 	 *			advertise prefix
 | |
| 	 *********************************************************************/
 | |
| 
 | |
| 	/*
 | |
| 	 * set <p> based on <prefix>
 | |
| 	 */
 | |
| 	assert(!rfapiRprefix2Qprefix(prefix, &p));
 | |
| 
 | |
| 	afi = family2afi(prefix->prefix.addr_family);
 | |
| 	assert(afi);
 | |
| 
 | |
| 	vnc_zlog_debug_verbose(
 | |
| 		"%s(rfd=%p, pfx=%pFX, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)",
 | |
| 		__func__, rfd, &p, lifetime, options_un, options_vn,
 | |
| 		action_str);
 | |
| 
 | |
| 	/*
 | |
| 	 * These tests come after the prefix conversion so that we can
 | |
| 	 * print the prefix in a debug message before failing
 | |
| 	 */
 | |
| 
 | |
| 	bgp = rfd->bgp;
 | |
| 	if (!bgp) {
 | |
| 		vnc_zlog_debug_verbose("%s: no BGP instance: returning ENXIO",
 | |
| 				       __func__);
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 	if (!bgp->rfapi) {
 | |
| 		vnc_zlog_debug_verbose("%s: no RFAPI instance: returning ENXIO",
 | |
| 				       __func__);
 | |
| 		return ENXIO;
 | |
| 	}
 | |
| 	if (!rfd->rfg) {
 | |
| 		if (RFAPI_REGISTER_ADD == action) {
 | |
| 			++bgp->rfapi->stat.count_registrations_failed;
 | |
| 		}
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: rfd=%p, no RF GRP instance: returning ESTALE",
 | |
| 			__func__, rfd);
 | |
| 		return ESTALE;
 | |
| 	}
 | |
| 
 | |
| 	if (bgp->rfapi->flags & RFAPI_INCALLBACK) {
 | |
| 		if (RFAPI_REGISTER_ADD == action) {
 | |
| 			++bgp->rfapi->stat.count_registrations_failed;
 | |
| 		}
 | |
| 		vnc_zlog_debug_verbose("%s: in callback: returning EDEADLK",
 | |
| 				       __func__);
 | |
| 		return EDEADLK;
 | |
| 	}
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd)) {
 | |
| 		if (RFAPI_REGISTER_ADD == action) {
 | |
| 			++bgp->rfapi->stat.count_registrations_failed;
 | |
| 		}
 | |
| 		vnc_zlog_debug_verbose("%s: invalid handle: returning EBADF",
 | |
| 				       __func__);
 | |
| 		return EBADF;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Is there a MAC address in this registration?
 | |
| 	 */
 | |
| 	if (l2o && !RFAPI_0_ETHERADDR(&l2o->macaddr)) {
 | |
| 		rfapiL2o2Qprefix(l2o, &pfx_mac_buf);
 | |
| 		pfx_mac = &pfx_mac_buf;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Is there an IP prefix in this registration?
 | |
| 	 */
 | |
| 	if (!(RFAPI_0_PREFIX(&p) && RFAPI_HOST_PREFIX(&p))) {
 | |
| 		pfx_ip = &p;
 | |
| 	} else {
 | |
| 		if (!pfx_mac) {
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s: missing mac addr that is required for host 0 pfx",
 | |
| 				__func__);
 | |
| 			if (RFAPI_REGISTER_ADD == action) {
 | |
| 				++bgp->rfapi->stat.count_registrations_failed;
 | |
| 			}
 | |
| 			return EINVAL;
 | |
| 		}
 | |
| 		if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) {
 | |
| 			vnc_zlog_debug_verbose(
 | |
| 				"%s: handle has bad vn_addr: returning EBADF",
 | |
| 				__func__);
 | |
| 			if (RFAPI_REGISTER_ADD == action) {
 | |
| 				++bgp->rfapi->stat.count_registrations_failed;
 | |
| 			}
 | |
| 			return EBADF;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (RFAPI_REGISTER_ADD == action) {
 | |
| 		++bgp->rfapi->stat.count_registrations;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Figure out if this registration is missing an IP address
 | |
| 	 *
 | |
| 	 * MAC-addr based:
 | |
| 	 *
 | |
| 	 *  In RFAPI, we use prefixes in family AF_LINK to store
 | |
| 	 *  the MAC addresses. These prefixes are used for the
 | |
| 	 *  list of advertised prefixes and in the RFAPI import
 | |
| 	 *  tables.
 | |
| 	 *
 | |
| 	 *  In BGP proper, we use the prefix matching the NVE's
 | |
| 	 *  VN address with a host prefix-length (i.e., 32 or 128).
 | |
| 	 *
 | |
| 	 */
 | |
| 	if (l2o && l2o->logical_net_id && RFAPI_0_PREFIX(&p)
 | |
| 	    && RFAPI_HOST_PREFIX(&p)) {
 | |
| 
 | |
| 		rfapiL2o2Qprefix(l2o, &pfx_mac_buf);
 | |
| 		pfx_mac = &pfx_mac_buf;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Construct route distinguisher
 | |
| 	 */
 | |
| 	if (prd_override) {
 | |
| 		prd = *prd_override;
 | |
| 	} else {
 | |
| 		memset(&prd, 0, sizeof(prd));
 | |
| 		if (pfx_mac) {
 | |
| 			prd.family = AF_UNSPEC;
 | |
| 			prd.prefixlen = 64;
 | |
| 			encode_rd_type(RD_TYPE_VNC_ETH, prd.val);
 | |
| 			if (l2o->local_nve_id
 | |
| 			    || !(rfd->rfg->flags & RFAPI_RFG_L2RD)) {
 | |
| 				/*
 | |
| 				 * If Local NVE ID is specified in message, use
 | |
| 				 * it.
 | |
| 				 * (if no local default configured, also use it
 | |
| 				 * even if 0)
 | |
| 				 */
 | |
| 				prd.val[1] = l2o->local_nve_id;
 | |
| 			} else {
 | |
| 				if (rfd->rfg->l2rd) {
 | |
| 					/*
 | |
| 					 * locally-configured literal value
 | |
| 					 */
 | |
| 					prd.val[1] = rfd->rfg->l2rd;
 | |
| 				} else {
 | |
| 					/*
 | |
| 					 * 0 means auto:vn, which means use LSB
 | |
| 					 * of VN addr
 | |
| 					 */
 | |
| 					if (rfd->vn_addr.addr_family
 | |
| 					    == AF_INET) {
 | |
| 						prd.val[1] =
 | |
| 							*(((char *)&rfd->vn_addr
 | |
| 								   .addr.v4
 | |
| 								   .s_addr)
 | |
| 							  + 3);
 | |
| 					} else {
 | |
| 						prd.val[1] =
 | |
| 							*(((char *)&rfd->vn_addr
 | |
| 								   .addr.v6
 | |
| 								   .s6_addr)
 | |
| 							  + 15);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			memcpy(prd.val + 2, pfx_mac->u.prefix_eth.octet, 6);
 | |
| 		} else {
 | |
| 			prd = rfd->rd;
 | |
| 			prd.family = AF_UNSPEC;
 | |
| 			prd.prefixlen = 64;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	if (action == RFAPI_REGISTER_WITHDRAW
 | |
| 	    || action == RFAPI_REGISTER_KILL) {
 | |
| 
 | |
| 		int adv_tunnel = 0;
 | |
| 
 | |
| 		/*
 | |
| 		 * withdraw previous advertisement
 | |
| 		 */
 | |
| 		del_vnc_route(
 | |
| 			rfd, rfd->peer, bgp, SAFI_MPLS_VPN,
 | |
| 			pfx_ip ? pfx_ip
 | |
| 			       : &pfx_vn_buf, /* prefix being advertised */
 | |
| 			&prd, /* route distinguisher (0 for ENCAP) */
 | |
| 			ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL,
 | |
| 			action == RFAPI_REGISTER_KILL);
 | |
| 
 | |
| 		if (0 == rfapiApDelete(bgp, rfd, &p, pfx_mac, &prd,
 | |
| 				       &adv_tunnel)) {
 | |
| 			if (adv_tunnel)
 | |
| 				rfapiTunnelRouteAnnounce(
 | |
| 					bgp, rfd, &rfd->max_prefix_lifetime);
 | |
| 		}
 | |
| 
 | |
| 	} else {
 | |
| 
 | |
| 		int adv_tunnel = 0;
 | |
| 		uint32_t local_pref;
 | |
| 		struct ecommunity *rtlist = NULL;
 | |
| 		struct ecommunity_val ecom_value;
 | |
| 
 | |
| 		if (!rfapiApCount(rfd)) {
 | |
| 			/*
 | |
| 			 * make sure we advertise tunnel route upon adding the
 | |
| 			 * first VPN route
 | |
| 			 */
 | |
| 			adv_tunnel = 1;
 | |
| 		}
 | |
| 
 | |
| 		if (rfapiApAdd(bgp, rfd, &p, pfx_mac, &prd, lifetime,
 | |
| 			       prefix->cost, l2o)) {
 | |
| 			adv_tunnel = 1;
 | |
| 		}
 | |
| 
 | |
| 		vnc_zlog_debug_verbose("%s: adv_tunnel = %d", __func__,
 | |
| 				       adv_tunnel);
 | |
| 		if (adv_tunnel) {
 | |
| 			vnc_zlog_debug_verbose("%s: announcing tunnel route",
 | |
| 					       __func__);
 | |
| 			rfapiTunnelRouteAnnounce(bgp, rfd,
 | |
| 						 &rfd->max_prefix_lifetime);
 | |
| 		}
 | |
| 
 | |
| 		vnc_zlog_debug_verbose("%s: calling add_vnc_route", __func__);
 | |
| 
 | |
| 		local_pref = rfp_cost_to_localpref(prefix->cost);
 | |
| 
 | |
| 		if (l2o && l2o->label)
 | |
| 			label = &l2o->label;
 | |
| 
 | |
| 		if (pfx_mac) {
 | |
| 			struct ecommunity *l2com = NULL;
 | |
| 
 | |
| 			if (label) {
 | |
| 				l2com = bgp_rfapi_get_ecommunity_by_lni_label(
 | |
| 					bgp, 1, l2o->logical_net_id, *label);
 | |
| 			}
 | |
| 			if (l2com) {
 | |
| 				rtlist = ecommunity_dup(l2com);
 | |
| 			} else {
 | |
| 				/*
 | |
| 				 * If mac address is set, add an RT based on the
 | |
| 				 * registered LNI
 | |
| 				 */
 | |
| 				memset((char *)&ecom_value, 0,
 | |
| 				       sizeof(ecom_value));
 | |
| 				ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET;
 | |
| 				ecom_value.val[5] =
 | |
| 					(l2o->logical_net_id >> 16) & 0xff;
 | |
| 				ecom_value.val[6] =
 | |
| 					(l2o->logical_net_id >> 8) & 0xff;
 | |
| 				ecom_value.val[7] =
 | |
| 					(l2o->logical_net_id >> 0) & 0xff;
 | |
| 				rtlist = ecommunity_new();
 | |
| 				ecommunity_add_val(rtlist, &ecom_value,
 | |
| 						   false, false);
 | |
| 			}
 | |
| 			if (l2o->tag_id) {
 | |
| 				as_t as = bgp->as;
 | |
| 				uint16_t val = l2o->tag_id;
 | |
| 				memset((char *)&ecom_value, 0,
 | |
| 				       sizeof(ecom_value));
 | |
| 				ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET;
 | |
| 				if (as > BGP_AS_MAX) {
 | |
| 					ecom_value.val[0] =
 | |
| 						ECOMMUNITY_ENCODE_AS4;
 | |
| 					ecom_value.val[2] = (as >> 24) & 0xff;
 | |
| 					ecom_value.val[3] = (as >> 16) & 0xff;
 | |
| 					ecom_value.val[4] = (as >> 8) & 0xff;
 | |
| 					ecom_value.val[5] = as & 0xff;
 | |
| 				} else {
 | |
| 					ecom_value.val[0] =
 | |
| 						ECOMMUNITY_ENCODE_AS;
 | |
| 					ecom_value.val[2] = (as >> 8) & 0xff;
 | |
| 					ecom_value.val[3] = as & 0xff;
 | |
| 				}
 | |
| 				ecom_value.val[6] = (val >> 8) & 0xff;
 | |
| 				ecom_value.val[7] = val & 0xff;
 | |
| 				if (rtlist == NULL)
 | |
| 					rtlist = ecommunity_new();
 | |
| 				ecommunity_add_val(rtlist, &ecom_value,
 | |
| 						   false, false);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/*
 | |
| 		 * advertise prefix via tunnel endpoint
 | |
| 		 */
 | |
| 		add_vnc_route(
 | |
| 			rfd, /* rfapi descr, for export list & backref */
 | |
| 			bgp, /* which bgp instance */
 | |
| 			SAFI_MPLS_VPN, /* which SAFI */
 | |
| 			(pfx_ip ? pfx_ip
 | |
| 				: &pfx_vn_buf), /* prefix being advertised */
 | |
| 			&prd, /* route distinguisher to use (0 for ENCAP) */
 | |
| 			&rfd->vn_addr, /* nexthop */
 | |
| 			&local_pref,
 | |
| 			&lifetime, /* prefix lifetime -> Tunnel Encap attr */
 | |
| 			NULL, options_un, /* rfapi un options */
 | |
| 			options_vn,       /* rfapi vn options */
 | |
| 			(rtlist ? rtlist : rfd->rt_export_list), NULL, /* med */
 | |
| 			label, /* label: default */
 | |
| 			ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
 | |
| 
 | |
| 		if (rtlist)
 | |
| 			ecommunity_free(&rtlist); /* sets rtlist = NULL */
 | |
| 	}
 | |
| 
 | |
| 	vnc_zlog_debug_verbose("%s: success", __func__);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rfapi_query(void *handle, struct rfapi_ip_addr *target,
 | |
| 		struct rfapi_l2address_option *l2o, /* may be NULL */
 | |
| 		struct rfapi_next_hop_entry **ppNextHopEntry)
 | |
| {
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	struct bgp *bgp = rfd->bgp;
 | |
| 	int rc;
 | |
| 
 | |
| 	assert(ppNextHopEntry);
 | |
| 	*ppNextHopEntry = NULL;
 | |
| 
 | |
| 	if (bgp && bgp->rfapi) {
 | |
| 		bgp->rfapi->stat.count_queries++;
 | |
| 	}
 | |
| 
 | |
| 	if (!rfd->rfg) {
 | |
| 		if (bgp && bgp->rfapi)
 | |
| 			++bgp->rfapi->stat.count_queries_failed;
 | |
| 		return ESTALE;
 | |
| 	}
 | |
| 
 | |
| 	if ((rc = rfapi_query_inner(handle, target, l2o, ppNextHopEntry))) {
 | |
| 		if (bgp && bgp->rfapi)
 | |
| 			++bgp->rfapi->stat.count_queries_failed;
 | |
| 	}
 | |
| 	return rc;
 | |
| }
 | |
| 
 | |
| int rfapi_query_done(rfapi_handle handle, struct rfapi_ip_addr *target)
 | |
| {
 | |
| 	struct prefix p;
 | |
| 	int rc;
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	struct bgp *bgp = rfd->bgp;
 | |
| 
 | |
| 	if (!rfd->rfg)
 | |
| 		return ESTALE;
 | |
| 
 | |
| 	assert(target);
 | |
| 	rc = rfapiRaddr2Qprefix(target, &p);
 | |
| 	assert(!rc);
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd))
 | |
| 		return EBADF;
 | |
| 
 | |
| 	/* preemptive */
 | |
| 	if (!bgp || !bgp->rfapi)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	if (bgp->rfapi->flags & RFAPI_INCALLBACK)
 | |
| 		return EDEADLK;
 | |
| 
 | |
| 	rfapiMonitorDel(bgp, rfd, &p);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int rfapi_query_done_all(rfapi_handle handle, int *count)
 | |
| {
 | |
| 	struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
 | |
| 	struct bgp *bgp = rfd->bgp;
 | |
| 	;
 | |
| 	int num;
 | |
| 
 | |
| 	if (!rfd->rfg)
 | |
| 		return ESTALE;
 | |
| 
 | |
| 	if (!is_valid_rfd(rfd))
 | |
| 		return EBADF;
 | |
| 
 | |
| 	/* preemptive */
 | |
| 	if (!bgp || !bgp->rfapi)
 | |
| 		return ENXIO;
 | |
| 
 | |
| 	if (bgp->rfapi->flags & RFAPI_INCALLBACK)
 | |
| 		return EDEADLK;
 | |
| 
 | |
| 	num = rfapiMonitorDelHd(rfd);
 | |
| 
 | |
| 	if (count)
 | |
| 		*count = num;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void rfapi_free_next_hop_list(struct rfapi_next_hop_entry *list)
 | |
| {
 | |
| 	struct rfapi_next_hop_entry *nh;
 | |
| 	struct rfapi_next_hop_entry *next;
 | |
| 
 | |
| 	for (nh = list; nh; nh = next) {
 | |
| 		next = nh->next;
 | |
| 		rfapi_un_options_free(nh->un_options);
 | |
| 		nh->un_options = NULL;
 | |
| 		rfapi_vn_options_free(nh->vn_options);
 | |
| 		nh->vn_options = NULL;
 | |
| 		XFREE(MTYPE_RFAPI_NEXTHOP, nh);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * NULL handle => return total count across all nves
 | |
|  */
 | |
| uint32_t rfapi_monitor_count(void *handle)
 | |
| {
 | |
| 	struct bgp *bgp = bgp_get_default();
 | |
| 	uint32_t count;
 | |
| 
 | |
| 	if (handle) {
 | |
| 		struct rfapi_descriptor *rfd =
 | |
| 			(struct rfapi_descriptor *)handle;
 | |
| 		count = rfd->monitor_count;
 | |
| 	} else {
 | |
| 
 | |
| 		if (!bgp || !bgp->rfapi)
 | |
| 			return 0;
 | |
| 
 | |
| 		count = bgp->rfapi->monitor_count;
 | |
| 	}
 | |
| 
 | |
| 	return count;
 | |
| }
 | |
| 
 | |
| /***********************************************************************
 | |
|  *			CLI/CONFIG
 | |
|  ***********************************************************************/
 | |
| 
 | |
| DEFUN (debug_rfapi_show_nves,
 | |
|        debug_rfapi_show_nves_cmd,
 | |
|        "debug rfapi-dev show nves",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        SHOW_STR
 | |
|        "NVE Information\n")
 | |
| {
 | |
| 	rfapiPrintMatchingDescriptors(vty, NULL, NULL);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (
 | |
|   debug_rfapi_show_nves_vn_un,
 | |
|   debug_rfapi_show_nves_vn_un_cmd,
 | |
|   "debug rfapi-dev show nves <vn|un> <A.B.C.D|X:X::X:X>", /* prefix also ok */
 | |
|   DEBUG_STR
 | |
|   DEBUG_RFAPI_STR
 | |
|   SHOW_STR
 | |
|   "NVE Information\n"
 | |
|   "Specify virtual network\n"
 | |
|   "Specify underlay network interface\n"
 | |
|   "IPv4 address\n"
 | |
|   "IPv6 address\n")
 | |
| {
 | |
| 	struct prefix pfx;
 | |
| 
 | |
| 	if (!str2prefix(argv[5]->arg, &pfx)) {
 | |
| 		vty_out(vty, "Malformed address \"%s\"\n", argv[5]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
 | |
| 		vty_out(vty, "Invalid address \"%s\"\n", argv[5]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	if (argv[4]->arg[0] == 'u') {
 | |
| 		rfapiPrintMatchingDescriptors(vty, NULL, &pfx);
 | |
| 	} else {
 | |
| 		rfapiPrintMatchingDescriptors(vty, &pfx, NULL);
 | |
| 	}
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Note: this function does not flush vty output, so if it is called
 | |
|  * with a stream pointing to a vty, the user will have to type something
 | |
|  * before the callback output shows up
 | |
|  */
 | |
| static void test_nexthops_callback(
 | |
| 	//    struct rfapi_ip_addr        *target,
 | |
| 	struct rfapi_next_hop_entry *next_hops, void *userdata)
 | |
| {
 | |
| 	void *stream = userdata;
 | |
| 
 | |
| 	int (*fp)(void *, const char *, ...);
 | |
| 	struct vty *vty;
 | |
| 	void *out;
 | |
| 	const char *vty_newline;
 | |
| 
 | |
| 	if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
 | |
| 		return;
 | |
| 
 | |
| 	fp(out, "Nexthops Callback, Target=(");
 | |
| 	// rfapiPrintRfapiIpAddr(stream, target);
 | |
| 	fp(out, ")\n");
 | |
| 
 | |
| 	rfapiPrintNhl(stream, next_hops);
 | |
| 
 | |
| 	fp(out, "\n");
 | |
| 
 | |
| 	rfapi_free_next_hop_list(next_hops);
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_open,
 | |
|        debug_rfapi_open_cmd,
 | |
|        "debug rfapi-dev open vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_open\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface IPv4 address\n"
 | |
|        "underlay network interface IPv6 address\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	uint32_t lifetime = 0;
 | |
| 	int rc;
 | |
| 	rfapi_handle handle;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 	rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp_get_default()), &vn,
 | |
| 			&un, /*&uo */ NULL, &lifetime, NULL, &handle);
 | |
| 
 | |
| 	vty_out(vty, "rfapi_open: status %d, handle %p, lifetime %d\n", rc,
 | |
| 		handle, lifetime);
 | |
| 
 | |
| 	rc = rfapi_set_response_cb(handle, test_nexthops_callback);
 | |
| 
 | |
| 	vty_out(vty, "rfapi_set_response_cb: status %d\n", rc);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| DEFUN (debug_rfapi_close_vn_un,
 | |
|        debug_rfapi_close_vn_un_cmd,
 | |
|        "debug rfapi-dev close vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_close\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface IPv4 address\n"
 | |
|        "underlay network interface IPv6 address\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	rfapi_handle handle;
 | |
| 	int rc;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[4]->arg, argv[6]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	rc = rfapi_close(handle);
 | |
| 
 | |
| 	vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_close_rfd,
 | |
|        debug_rfapi_close_rfd_cmd,
 | |
|        "debug rfapi-dev close rfd HANDLE",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_close\n"
 | |
|        "indicate handle follows\n" "rfapi handle in hexadecimal\n")
 | |
| {
 | |
| 	rfapi_handle handle;
 | |
| 	int rc;
 | |
| 	char *endptr = NULL;
 | |
| 
 | |
| 	handle = (rfapi_handle)(uintptr_t)(strtoull(argv[4]->arg, &endptr, 16));
 | |
| 
 | |
| 	if (*endptr != '\0' || (uintptr_t)handle == UINTPTR_MAX) {
 | |
| 		vty_out(vty, "Invalid value: %s\n", argv[4]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	rc = rfapi_close(handle);
 | |
| 
 | |
| 	vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_register_vn_un,
 | |
|        debug_rfapi_register_vn_un_cmd,
 | |
|        "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS [cost (0-255)]",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_register\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network IPv4 interface address\n"
 | |
|        "virtual network IPv6 interface address\n"
 | |
|        "indicate un addr follows\n"
 | |
|        "underlay network IPv4 interface address\n"
 | |
|        "underlay network IPv6 interface address\n"
 | |
|        "indicate prefix follows\n"
 | |
|        "IPv4 prefix\n"
 | |
|        "IPv6 prefix\n"
 | |
|        "indicate lifetime follows\n"
 | |
|        "lifetime\n"
 | |
|        "Cost (localpref = 255-cost)\n"
 | |
|        "0-255\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	rfapi_handle handle;
 | |
| 	struct prefix pfx;
 | |
| 	uint32_t lifetime;
 | |
| 	struct rfapi_ip_prefix hpfx;
 | |
| 	int rc;
 | |
| 	uint8_t cost = 100;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[4]->arg, argv[6]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Get prefix to advertise
 | |
| 	 */
 | |
| 	if (!str2prefix(argv[8]->arg, &pfx)) {
 | |
| 		vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
 | |
| 		vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	rfapiQprefix2Rprefix(&pfx, &hpfx);
 | |
| 
 | |
| 	if (strmatch(argv[10]->text, "infinite")) {
 | |
| 		lifetime = RFAPI_INFINITE_LIFETIME;
 | |
| 	} else {
 | |
| 		lifetime = strtoul(argv[10]->arg, NULL, 10);
 | |
| 	}
 | |
| 
 | |
| 	if (argc >= 13)
 | |
| 		cost = (uint8_t) strtoul(argv[12]->arg, NULL, 10);
 | |
| 	hpfx.cost = cost;
 | |
| 
 | |
| 	rc = rfapi_register(handle, &hpfx, lifetime, NULL, NULL,
 | |
| 			    RFAPI_REGISTER_ADD);
 | |
| 	if (rc) {
 | |
| 		vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc,
 | |
| 			strerror(rc));
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_register_vn_un_l2o,
 | |
|        debug_rfapi_register_vn_un_l2o_cmd,
 | |
|        "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS macaddr 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"
 | |
|        "Seconds of lifetime\n"
 | |
|        "indicate MAC address follows\n"
 | |
|        "MAC address\n"
 | |
|        "indicate lni follows\n"
 | |
|        "lni value range\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	rfapi_handle handle;
 | |
| 	struct prefix pfx;
 | |
| 	uint32_t lifetime;
 | |
| 	struct rfapi_ip_prefix hpfx;
 | |
| 	int rc;
 | |
| 	struct rfapi_vn_option optary[10]; /* XXX must be big enough */
 | |
| 	struct rfapi_vn_option *opt = NULL;
 | |
| 	int opt_next = 0;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[4]->arg, argv[6]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Get prefix to advertise
 | |
| 	 */
 | |
| 	if (!str2prefix(argv[8]->arg, &pfx)) {
 | |
| 		vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
 | |
| 		vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	rfapiQprefix2Rprefix(&pfx, &hpfx);
 | |
| 
 | |
| 	if (strmatch(argv[10]->text, "infinite")) {
 | |
| 		lifetime = RFAPI_INFINITE_LIFETIME;
 | |
| 	} else {
 | |
| 		lifetime = strtoul(argv[10]->arg, NULL, 10);
 | |
| 	}
 | |
| 
 | |
| 	/* L2 option parsing START */
 | |
| 	memset(optary, 0, sizeof(optary));
 | |
| 	optary[opt_next].v.l2addr.logical_net_id =
 | |
| 		strtoul(argv[14]->arg, NULL, 10);
 | |
| 	if (rfapiStr2EthAddr(argv[12]->arg,
 | |
| 			     &optary[opt_next].v.l2addr.macaddr)) {
 | |
| 		vty_out(vty, "Bad mac address \"%s\"\n", argv[12]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
 | |
| 	opt = optary;
 | |
| 
 | |
| 	/* L2 option parsing END */
 | |
| 
 | |
| 	/* TBD fixme */
 | |
| 	rc = rfapi_register(handle, &hpfx, lifetime, NULL /* &uo */, opt,
 | |
| 			    RFAPI_REGISTER_ADD);
 | |
| 	if (rc) {
 | |
| 		vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc,
 | |
| 			strerror(rc));
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| DEFUN (debug_rfapi_unregister_vn_un,
 | |
|        debug_rfapi_unregister_vn_un_cmd,
 | |
|        "debug rfapi-dev unregister vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> [kill]",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_register\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface address\n"
 | |
|        "prefix to remove\n"
 | |
|        "Remove without holddown")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	rfapi_handle handle;
 | |
| 	struct prefix pfx;
 | |
| 	struct rfapi_ip_prefix hpfx;
 | |
| 	int rc;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[4]->arg, argv[6]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Get prefix to advertise
 | |
| 	 */
 | |
| 	if (!str2prefix(argv[8]->arg, &pfx)) {
 | |
| 		vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (pfx.family != AF_INET && pfx.family != AF_INET6) {
 | |
| 		vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	rfapiQprefix2Rprefix(&pfx, &hpfx);
 | |
| 
 | |
| 	rfapi_register(handle, &hpfx, 0, NULL, NULL,
 | |
| 		       (argc == 10 ?
 | |
| 			RFAPI_REGISTER_KILL : RFAPI_REGISTER_WITHDRAW));
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_query_vn_un,
 | |
|        debug_rfapi_query_vn_un_cmd,
 | |
|        "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_query\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate un addr follows\n"
 | |
|        "IPv4 un address\n"
 | |
|        "IPv6 un address\n"
 | |
|        "indicate target follows\n"
 | |
|        "target IPv4 address\n"
 | |
|        "target IPv6 address\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	struct rfapi_ip_addr target;
 | |
| 	rfapi_handle handle;
 | |
| 	int rc;
 | |
| 	struct rfapi_next_hop_entry *pNextHopEntry;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get target addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[8]->arg, &target)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[4]->arg, argv[6]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * options parameter not used? Set to NULL for now
 | |
| 	 */
 | |
| 	rc = rfapi_query(handle, &target, NULL, &pNextHopEntry);
 | |
| 
 | |
| 	if (rc) {
 | |
| 		vty_out(vty, "rfapi_query failed with rc=%d (%s)\n", rc,
 | |
| 			strerror(rc));
 | |
| 	} else {
 | |
| 		/*
 | |
| 		 * print nexthop list
 | |
| 		 */
 | |
| 		test_nexthops_callback(/*&target, */ pNextHopEntry,
 | |
| 				       vty); /* frees nh list! */
 | |
| 	}
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| DEFUN (debug_rfapi_query_vn_un_l2o,
 | |
|        debug_rfapi_query_vn_un_l2o_cmd,
 | |
|        "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lni LNI target YY:YY:YY:YY:YY:YY",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_query\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface IPv4 address\n"
 | |
|        "underlay network interface IPv6 address\n"
 | |
|        "logical network ID follows\n"
 | |
|        "logical network ID\n"
 | |
|        "indicate target MAC addr follows\n"
 | |
|        "target MAC addr\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	int rc;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 	vty_out(vty, "%% This command is broken.\n");
 | |
| 	return CMD_WARNING_CONFIG_FAILED;
 | |
| }
 | |
| 
 | |
| 
 | |
| DEFUN (debug_rfapi_query_done_vn_un,
 | |
|        debug_rfapi_query_vn_un_done_cmd,
 | |
|        "debug rfapi-dev query done vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "rfapi_query_done\n"
 | |
|        "rfapi_query_done\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface IPv4 address\n"
 | |
|        "underlay network interface IPv6 address\n"
 | |
|        "indicate target follows\n"
 | |
|        "Target IPv4 address\n"
 | |
|        "Target IPv6 address\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	struct rfapi_ip_addr target;
 | |
| 	rfapi_handle handle;
 | |
| 	int rc;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get target addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[9]->arg, &target)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[5]->arg, argv[7]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * options parameter not used? Set to NULL for now
 | |
| 	 */
 | |
| 	rc = rfapi_query_done(handle, &target);
 | |
| 
 | |
| 	vty_out(vty, "rfapi_query_done returned %d\n", rc);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_show_import,
 | |
|        debug_rfapi_show_import_cmd,
 | |
|        "debug rfapi-dev show import",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        SHOW_STR
 | |
|        "import\n")
 | |
| {
 | |
| 	struct bgp *bgp;
 | |
| 	struct rfapi *h;
 | |
| 	struct rfapi_import_table *it;
 | |
| 	char *s;
 | |
| 	int first_l2 = 1;
 | |
| 
 | |
| 	/*
 | |
| 	 * Show all import tables
 | |
| 	 */
 | |
| 
 | |
| 	bgp = bgp_get_default(); /* assume 1 instance for now */
 | |
| 	if (!bgp) {
 | |
| 		vty_out(vty, "No BGP instance\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	h = bgp->rfapi;
 | |
| 	if (!h) {
 | |
| 		vty_out(vty, "No RFAPI instance\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 * Iterate over all import tables; do a filtered import
 | |
| 	 * for the afi/safi combination
 | |
| 	 */
 | |
| 
 | |
| 
 | |
| 	for (it = h->imports; it; it = it->next) {
 | |
| 		s = ecommunity_ecom2str(it->rt_import_list,
 | |
| 					ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
 | |
| 		vty_out(vty, "Import Table %p, RTs: %s\n", it, s);
 | |
| 		XFREE(MTYPE_ECOMMUNITY_STR, s);
 | |
| 
 | |
| 		rfapiShowImportTable(vty, "IP VPN", it->imported_vpn[AFI_IP],
 | |
| 				     1);
 | |
| 		rfapiShowImportTable(vty, "IP ENCAP",
 | |
| 				     it->imported_encap[AFI_IP], 0);
 | |
| 		rfapiShowImportTable(vty, "IP6 VPN", it->imported_vpn[AFI_IP6],
 | |
| 				     1);
 | |
| 		rfapiShowImportTable(vty, "IP6 ENCAP",
 | |
| 				     it->imported_encap[AFI_IP6], 0);
 | |
| 	}
 | |
| 
 | |
| 	if (h->import_mac) {
 | |
| 		void *cursor = NULL;
 | |
| 		uint32_t lni;
 | |
| 		uintptr_t lni_as_ptr;
 | |
| 		int rc;
 | |
| 		char buf[BUFSIZ];
 | |
| 
 | |
| 		for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
 | |
| 					(void **)&it, &cursor);
 | |
| 		     !rc;
 | |
| 		     rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
 | |
| 					(void **)&it, &cursor)) {
 | |
| 
 | |
| 			if (it->imported_vpn[AFI_L2VPN]) {
 | |
| 				lni = lni_as_ptr;
 | |
| 				if (first_l2) {
 | |
| 					vty_out(vty,
 | |
| 						"\nLNI-based Ethernet Tables:\n");
 | |
| 					first_l2 = 0;
 | |
| 				}
 | |
| 				snprintf(buf, sizeof(buf), "L2VPN LNI=%u", lni);
 | |
| 				rfapiShowImportTable(
 | |
| 					vty, buf, it->imported_vpn[AFI_L2VPN],
 | |
| 					1);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rfapiShowImportTable(vty, "CE IT - IP VPN",
 | |
| 			     h->it_ce->imported_vpn[AFI_IP], 1);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_show_import_vn_un,
 | |
|        debug_rfapi_show_import_vn_un_cmd,
 | |
|        "debug rfapi-dev show import vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        SHOW_STR
 | |
|        "import\n"
 | |
|        "indicate vn addr follows\n"
 | |
|        "virtual network interface IPv4 address\n"
 | |
|        "virtual network interface IPv6 address\n"
 | |
|        "indicate xt addr follows\n"
 | |
|        "underlay network interface IPv4 address\n"
 | |
|        "underlay network interface IPv6 address\n")
 | |
| {
 | |
| 	struct rfapi_ip_addr vn;
 | |
| 	struct rfapi_ip_addr un;
 | |
| 	rfapi_handle handle;
 | |
| 	int rc;
 | |
| 	struct rfapi_descriptor *rfd;
 | |
| 
 | |
| 	/*
 | |
| 	 * Get VN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	/*
 | |
| 	 * Get UN addr
 | |
| 	 */
 | |
| 	if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un)))
 | |
| 		return rc;
 | |
| 
 | |
| 
 | |
| 	if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
 | |
| 		vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
 | |
| 			argv[5]->arg, argv[7]->arg);
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	rfd = (struct rfapi_descriptor *)handle;
 | |
| 
 | |
| 	rfapiShowImportTable(vty, "IP VPN",
 | |
| 			     rfd->import_table->imported_vpn[AFI_IP], 1);
 | |
| 	rfapiShowImportTable(vty, "IP ENCAP",
 | |
| 			     rfd->import_table->imported_encap[AFI_IP], 0);
 | |
| 	rfapiShowImportTable(vty, "IP6 VPN",
 | |
| 			     rfd->import_table->imported_vpn[AFI_IP6], 1);
 | |
| 	rfapiShowImportTable(vty, "IP6 ENCAP",
 | |
| 			     rfd->import_table->imported_encap[AFI_IP6], 0);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (debug_rfapi_response_omit_self,
 | |
|        debug_rfapi_response_omit_self_cmd,
 | |
|        "debug rfapi-dev response-omit-self <on|off>",
 | |
|        DEBUG_STR
 | |
|        DEBUG_RFAPI_STR
 | |
|        "Omit self in RFP responses\n"
 | |
|        "filter out self from responses\n" "leave self in responses\n")
 | |
| {
 | |
| 	struct bgp *bgp = bgp_get_default();
 | |
| 
 | |
| 	if (!bgp) {
 | |
| 		vty_out(vty, "No BGP process is configured\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 	if (!bgp->rfapi_cfg) {
 | |
| 		vty_out(vty, "VNC not configured\n");
 | |
| 		return CMD_WARNING_CONFIG_FAILED;
 | |
| 	}
 | |
| 
 | |
| 	if (strmatch(argv[3]->text, "on"))
 | |
| 		SET_FLAG(bgp->rfapi_cfg->flags,
 | |
| 			 BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
 | |
| 	else
 | |
| 		UNSET_FLAG(bgp->rfapi_cfg->flags,
 | |
| 			   BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| 
 | |
| #ifdef RFAPI_DEBUG_SKIPLIST_CLI
 | |
| 
 | |
| #include "lib/skiplist.h"
 | |
| DEFUN (skiplist_test_cli,
 | |
|        skiplist_test_cli_cmd,
 | |
|        "skiplist test",
 | |
|        "skiplist command\n"
 | |
|        "test\n")
 | |
| {
 | |
| 	skiplist_test(vty);
 | |
| 
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| DEFUN (skiplist_debug_cli,
 | |
|        skiplist_debug_cli_cmd,
 | |
|        "skiplist debug",
 | |
|        "skiplist command\n"
 | |
|        "debug\n")
 | |
| {
 | |
| 	skiplist_debug(vty, NULL);
 | |
| 	return CMD_SUCCESS;
 | |
| }
 | |
| 
 | |
| #endif /* RFAPI_DEBUG_SKIPLIST_CLI */
 | |
| 
 | |
| void rfapi_init(void)
 | |
| {
 | |
| 	bgp_rfapi_cfg_init();
 | |
| 	vnc_debug_init();
 | |
| 
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_show_import_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_show_import_vn_un_cmd);
 | |
| 
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_open_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_close_vn_un_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_close_rfd_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_unregister_vn_un_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_done_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_l2o_cmd);
 | |
| 
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_response_omit_self_cmd);
 | |
| 
 | |
| 	/* Need the following show commands for gpz test scripts */
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_show_nves_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_show_nves_vn_un_cmd);
 | |
| 	install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_l2o_cmd);
 | |
| 
 | |
| #ifdef RFAPI_DEBUG_SKIPLIST_CLI
 | |
| 	install_element(ENABLE_NODE, &skiplist_test_cli_cmd);
 | |
| 	install_element(ENABLE_NODE, &skiplist_debug_cli_cmd);
 | |
| #endif
 | |
| 
 | |
| 	rfapi_vty_init();
 | |
| }
 | |
| 
 | |
| #ifdef DEBUG_RFAPI
 | |
| static void rfapi_print_exported(struct bgp *bgp)
 | |
| {
 | |
| 	struct bgp_dest *destn;
 | |
| 	struct bgp_dest *dest;
 | |
| 	struct bgp_path_info *bpi;
 | |
| 
 | |
| 	if (!bgp)
 | |
| 		return;
 | |
| 
 | |
| 	for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_MPLS_VPN]); destn;
 | |
| 	     destn = bgp_route_next(destn)) {
 | |
| 		struct bgp_table *table;
 | |
| 
 | |
| 		table = bgp_dest_get_bgp_table_info(destn);
 | |
| 		if (!table)
 | |
| 			continue;
 | |
| 		fprintf(stderr, "%s: vpn destn=%p\n", __func__, destn);
 | |
| 		for (dest = bgp_table_top(table); dest;
 | |
| 		     dest = bgp_route_next(dest)) {
 | |
| 			bpi = bgp_dest_get_bgp_path_info(dest);
 | |
| 
 | |
| 			if (!bpi)
 | |
| 				continue;
 | |
| 			fprintf(stderr, "%s: dest=%p\n", __func__, dest);
 | |
| 			for (; bpi; bpi = bpi->next) {
 | |
| 				rfapiPrintBi((void *)2, bpi); /* 2 => stderr */
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_ENCAP]); destn;
 | |
| 	     destn = bgp_route_next(destn)) {
 | |
| 		struct bgp_table *table;
 | |
| 
 | |
| 		table = bgp_dest_get_bgp_table_info(destn);
 | |
| 		if (!table)
 | |
| 			continue;
 | |
| 		fprintf(stderr, "%s: encap destn=%p\n", __func__, destn);
 | |
| 		for (dest = bgp_table_top(table); dest;
 | |
| 		     dest = bgp_route_next(dest)) {
 | |
| 			bpi = bgp_dest_get_bgp_path_info(dest);
 | |
| 			if (!bpi)
 | |
| 				continue;
 | |
| 			fprintf(stderr, "%s: dest=%p\n", __func__, dest);
 | |
| 			for (; bpi; bpi = bpi->next) {
 | |
| 				rfapiPrintBi((void *)2, bpi); /* 2 => stderr */
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| #endif /* defined(DEBUG_RFAPI) */
 | |
| 
 | |
| /*
 | |
|  * Free all memory to prepare for clean exit as seen by valgrind memcheck
 | |
|  */
 | |
| void rfapi_delete(struct bgp *bgp)
 | |
| {
 | |
| 	extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */
 | |
| 
 | |
| 	/*
 | |
| 	 * 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)
 | |
| {
 | |
| 	vnc_zlog_debug_verbose("%s: auto-assigning RD", __func__);
 | |
| 	if (vn->addr_family != AF_INET && vn->addr_family != AF_INET6) {
 | |
| 		vnc_zlog_debug_verbose(
 | |
| 			"%s: can't auto-assign RD, VN addr family is not IPv4|v6",
 | |
| 			__func__);
 | |
| 		return EAFNOSUPPORT;
 | |
| 	}
 | |
| 	rd->family = AF_UNSPEC;
 | |
| 	rd->prefixlen = 64;
 | |
| 	rd->val[1] = RD_TYPE_IP;
 | |
| 	if (vn->addr_family == AF_INET) {
 | |
| 		memcpy(rd->val + 2, &vn->addr.v4.s_addr, 4);
 | |
| 	} else { /* is v6 */
 | |
| 		memcpy(rd->val + 2, &vn->addr.v6.s6_addr32[3],
 | |
| 		       4); /* low order 4 bytes */
 | |
| 	}
 | |
| 	{
 | |
| 		char buf[RD_ADDRSTRLEN];
 | |
| 
 | |
| 		vnc_zlog_debug_verbose("%s: auto-RD is set to %s", __func__,
 | |
| 				       prefix_rd2str(rd, buf, sizeof(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);
 | |
| 		vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
 | |
| 				       size);
 | |
| 	}
 | |
| 	return rfc->default_rfp_cfg;
 | |
| }
 | |
| 
 | |
| static void *rfapi_rfp_get_or_init_group_config_nve(struct rfapi_cfg *rfc,
 | |
| 						    struct vty *vty,
 | |
| 						    uint32_t size)
 | |
| {
 | |
| 	struct rfapi_nve_group_cfg *rfg =
 | |
| 		VTY_GET_CONTEXT_SUB(rfapi_nve_group_cfg);
 | |
| 
 | |
| 	/* make sure group is still in list */
 | |
| 	if (!rfg || !listnode_lookup(rfc->nve_groups_sequential, rfg)) {
 | |
| 		/* Not in list anymore */
 | |
| 		vty_out(vty, "Current NVE group no longer exists\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	if (rfg->rfp_cfg == NULL && size > 0) {
 | |
| 		rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size);
 | |
| 		vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
 | |
| 				       size);
 | |
| 	}
 | |
| 	return rfg->rfp_cfg;
 | |
| }
 | |
| 
 | |
| static void *rfapi_rfp_get_or_init_group_config_l2(struct rfapi_cfg *rfc,
 | |
| 						   struct vty *vty,
 | |
| 						   uint32_t size)
 | |
| {
 | |
| 	struct rfapi_l2_group_cfg *rfg =
 | |
| 		VTY_GET_CONTEXT_SUB(rfapi_l2_group_cfg);
 | |
| 
 | |
| 	/* make sure group is still in list */
 | |
| 	if (!rfg || !listnode_lookup(rfc->l2_groups, rfg)) {
 | |
| 		/* Not in list anymore */
 | |
| 		vty_out(vty, "Current L2 group no longer exists\n");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	if (rfg->rfp_cfg == NULL && size > 0) {
 | |
| 		rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size);
 | |
| 		vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
 | |
| 				       size);
 | |
| 	}
 | |
| 	return rfg->rfp_cfg;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_init_group_config_ptr_vty
 | |
|  *
 | |
|  * This is used to init or return a previously init'ed group specific
 | |
|  * configuration pointer. Group is identified by vty context.
 | |
|  * NOTE: size is ignored when a previously init'ed value is returned.
 | |
|  * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
 | |
|  * bgp restart or shutdown.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    type              group type
 | |
|  *    vty               quagga vty context
 | |
|  *    size              number of bytes to allocation
 | |
|  *
 | |
|  * output:
 | |
|  *    none
 | |
|  *
 | |
|  * return value:
 | |
|  *    rfp_cfg_group     NULL or Pointer to configuration structure
 | |
| --------------------------------------------*/
 | |
| void *rfapi_rfp_init_group_config_ptr_vty(void *rfp_start_val,
 | |
| 					  rfapi_rfp_cfg_group_type type,
 | |
| 					  struct vty *vty, uint32_t size)
 | |
| {
 | |
| 	struct bgp *bgp;
 | |
| 	void *ret = NULL;
 | |
| 
 | |
| 	if (rfp_start_val == NULL || vty == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
 | |
| 	if (!bgp || !bgp->rfapi_cfg)
 | |
| 		return NULL;
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case RFAPI_RFP_CFG_GROUP_DEFAULT:
 | |
| 		ret = rfapi_rfp_get_or_init_group_config_default(bgp->rfapi_cfg,
 | |
| 								 vty, size);
 | |
| 		break;
 | |
| 	case RFAPI_RFP_CFG_GROUP_NVE:
 | |
| 		ret = rfapi_rfp_get_or_init_group_config_nve(bgp->rfapi_cfg,
 | |
| 							     vty, size);
 | |
| 		break;
 | |
| 	case RFAPI_RFP_CFG_GROUP_L2:
 | |
| 		ret = rfapi_rfp_get_or_init_group_config_l2(bgp->rfapi_cfg, vty,
 | |
| 							    size);
 | |
| 		break;
 | |
| 	default:
 | |
| 		flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d",
 | |
| 			 __func__, type);
 | |
| 		/* should never happen */
 | |
| 		assert("Unknown type" == NULL);
 | |
| 		break;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_get_group_config_ptr_vty
 | |
|  *
 | |
|  * This is used to get group specific configuration pointer.
 | |
|  * Group is identified by type and vty context.
 | |
|  * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
 | |
|  * bgp restart or shutdown.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    type              group type
 | |
|  *    vty               quagga vty context
 | |
|  *
 | |
|  * output:
 | |
|  *    none
 | |
|  *
 | |
|  * return value:
 | |
|  *    rfp_cfg_group     Pointer to configuration structure
 | |
| --------------------------------------------*/
 | |
| void *rfapi_rfp_get_group_config_ptr_vty(void *rfp_start_val,
 | |
| 					 rfapi_rfp_cfg_group_type type,
 | |
| 					 struct vty *vty)
 | |
| {
 | |
| 	return rfapi_rfp_init_group_config_ptr_vty(rfp_start_val, type, vty, 0);
 | |
| }
 | |
| 
 | |
| static void *
 | |
| rfapi_rfp_get_group_config_name_nve(struct rfapi_cfg *rfc, const char *name,
 | |
| 				    void *criteria,
 | |
| 				    rfp_group_config_search_cb_t *search_cb)
 | |
| {
 | |
| 	struct rfapi_nve_group_cfg *rfg;
 | |
| 	struct listnode *node;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(rfc->nve_groups_sequential, node, rfg)) {
 | |
| 		if (!strcmp(rfg->name, name) && /* name match */
 | |
| 		    (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg)))
 | |
| 			return rfg->rfp_cfg;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static void *
 | |
| rfapi_rfp_get_group_config_name_l2(struct rfapi_cfg *rfc, const char *name,
 | |
| 				   void *criteria,
 | |
| 				   rfp_group_config_search_cb_t *search_cb)
 | |
| {
 | |
| 	struct rfapi_l2_group_cfg *rfg;
 | |
| 	struct listnode *node;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(rfc->l2_groups, node, rfg)) {
 | |
| 		if (!strcmp(rfg->name, name) && /* name match */
 | |
| 		    (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg)))
 | |
| 			return rfg->rfp_cfg;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_get_group_config_ptr_name
 | |
|  *
 | |
|  * This is used to get group specific configuration pointer.
 | |
|  * Group is identified by type and name context.
 | |
|  * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
 | |
|  * bgp restart or shutdown.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    type              group type
 | |
|  *    name              group name
 | |
|  *    criteria          RFAPI caller provided 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:
 | |
| 		flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d",
 | |
| 			 __func__, type);
 | |
| 		/* should never happen */
 | |
| 		assert("Unknown type" == NULL);
 | |
| 		break;
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| /*------------------------------------------
 | |
|  * rfapi_rfp_get_l2_group_config_ptr_lni
 | |
|  *
 | |
|  * This is used to get group specific configuration pointer.
 | |
|  * Group is identified by type and logical network identifier.
 | |
|  * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
 | |
|  * bgp restart or shutdown.
 | |
|  *
 | |
|  * input:
 | |
|  *    rfp_start_val     value returned by rfp_start
 | |
|  *    type              group type
 | |
|  *    logical_net_id    group logical network identifier
 | |
|  *    criteria          RFAPI caller provided 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)
 | |
| 				vnc_zlog_debug_verbose(
 | |
| 					"%s: returning rfp group config for lni=0",
 | |
| 					__func__);
 | |
| 			return rfg->rfp_cfg;
 | |
| 		}
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | 
