diff options
| -rw-r--r-- | bgpd/bgp_debug.c | 139 | ||||
| -rw-r--r-- | bgpd/bgp_debug.h | 6 | ||||
| -rw-r--r-- | bgpd/bgp_main.c | 6 | ||||
| -rw-r--r-- | bgpd/bgp_mplsvpn.c | 986 | ||||
| -rw-r--r-- | bgpd/bgp_mplsvpn.h | 119 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 182 | ||||
| -rw-r--r-- | bgpd/bgp_route.h | 25 | ||||
| -rw-r--r-- | bgpd/bgp_routemap.c | 11 | ||||
| -rw-r--r-- | bgpd/bgp_vty.c | 719 | ||||
| -rw-r--r-- | bgpd/bgp_vty.h | 1 | ||||
| -rw-r--r-- | bgpd/bgp_zebra.c | 141 | ||||
| -rw-r--r-- | bgpd/bgpd.c | 13 | ||||
| -rw-r--r-- | bgpd/bgpd.h | 23 | ||||
| -rw-r--r-- | bgpd/rfapi/bgp_rfapi_cfg.c | 5 | 
14 files changed, 2349 insertions, 27 deletions
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index e89f399e41..691cdd19d7 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -55,6 +55,7 @@ unsigned long conf_bgp_debug_zebra;  unsigned long conf_bgp_debug_allow_martians;  unsigned long conf_bgp_debug_nht;  unsigned long conf_bgp_debug_update_groups; +unsigned long conf_bgp_debug_vpn;  unsigned long term_bgp_debug_as4;  unsigned long term_bgp_debug_neighbor_events; @@ -68,6 +69,7 @@ unsigned long term_bgp_debug_zebra;  unsigned long term_bgp_debug_allow_martians;  unsigned long term_bgp_debug_nht;  unsigned long term_bgp_debug_update_groups; +unsigned long term_bgp_debug_vpn;  struct list *bgp_debug_neighbor_events_peers = NULL;  struct list *bgp_debug_keepalive_peers = NULL; @@ -1557,6 +1559,96 @@ DEFUN (no_debug_bgp_update_groups,  	return CMD_SUCCESS;  } +DEFUN (debug_bgp_vpn, +       debug_bgp_vpn_cmd, +       "debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>", +       DEBUG_STR +       BGP_STR +       "VPN routes\n" +       "leaked from vrf to vpn\n" +       "leaked to vrf from vpn\n" +       "route-map updates\n" +       "labels\n") +{ +	int idx = 3; + +	if (argv_find(argv, argc, "leak-from-vrf", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_ON(vpn, VPN_LEAK_FROM_VRF); +		else +			TERM_DEBUG_ON(vpn, VPN_LEAK_FROM_VRF); +	} else if (argv_find(argv, argc, "leak-to-vrf", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_ON(vpn, VPN_LEAK_TO_VRF); +		else +			TERM_DEBUG_ON(vpn, VPN_LEAK_TO_VRF); +	} else if (argv_find(argv, argc, "rmap-event", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT); +		else +			TERM_DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT); +	} else if (argv_find(argv, argc, "label", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_ON(vpn, VPN_LEAK_LABEL); +		else +			TERM_DEBUG_ON(vpn, VPN_LEAK_LABEL); +	} else { +		vty_out(vty, "%% unknown debug bgp vpn keyword\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	if (vty->node != CONFIG_NODE) +		vty_out(vty, "enabled debug bgp vpn %s\n", argv[idx]->text); + +	return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_vpn, +       no_debug_bgp_vpn_cmd, +       "no debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>", +       NO_STR +       DEBUG_STR +       BGP_STR +       "VPN routes\n" +       "leaked from vrf to vpn\n" +       "leaked to vrf from vpn\n" +       "route-map updates\n" +       "labels\n") +{ +	int idx = 4; + +	if (argv_find(argv, argc, "leak-from-vrf", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF); +		else +			TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF); + +	} else if (argv_find(argv, argc, "leak-to-vrf", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); +		else +			TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); +	} else if (argv_find(argv, argc, "rmap-event", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); +		else +			TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); +	} else if (argv_find(argv, argc, "label", &idx)) { +		if (vty->node == CONFIG_NODE) +			DEBUG_OFF(vpn, VPN_LEAK_LABEL); +		else +			TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL); +	} else { +		vty_out(vty, "%% unknown debug bgp vpn keyword\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	if (vty->node != CONFIG_NODE) +		vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text); + +	return CMD_SUCCESS; +} +  DEFUN (no_debug_bgp,         no_debug_bgp_cmd,         "no debug bgp", @@ -1589,6 +1681,10 @@ DEFUN (no_debug_bgp,  	TERM_DEBUG_OFF(zebra, ZEBRA);  	TERM_DEBUG_OFF(allow_martians, ALLOW_MARTIANS);  	TERM_DEBUG_OFF(nht, NHT); +	TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF); +	TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); +	TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); +	TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);  	vty_out(vty, "All possible debugging has been turned off\n");  	return CMD_SUCCESS; @@ -1648,6 +1744,18 @@ DEFUN_NOSH (show_debugging_bgp,  	if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))  		vty_out(vty, "  BGP allow martian next hop debugging is on\n"); + +	if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) +		vty_out(vty, +			"  BGP route leak from vrf to vpn debugging is on\n"); +	if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) +		vty_out(vty, +			"  BGP route leak to vrf from vpn debugging is on\n"); +	if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) +		vty_out(vty, "  BGP vpn route-map event debugging is on\n"); +	if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) +		vty_out(vty, "  BGP vpn label event debugging is on\n"); +  	vty_out(vty, "\n");  	return CMD_SUCCESS;  } @@ -1692,6 +1800,15 @@ int bgp_debug_count(void)  	if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))  		ret++; +	if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) +		ret++; +	if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) +		ret++; +	if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) +		ret++; +	if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) +		ret++; +  	return ret;  } @@ -1768,6 +1885,23 @@ static int bgp_config_write_debug(struct vty *vty)  		write++;  	} +	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) { +		vty_out(vty, "debug bgp vpn leak-from-vrf\n"); +		write++; +	} +	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) { +		vty_out(vty, "debug bgp vpn leak-to-vrf\n"); +		write++; +	} +	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) { +		vty_out(vty, "debug bgp vpn rmap-event\n"); +		write++; +	} +	if (CONF_BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { +		vty_out(vty, "debug bgp vpn label\n"); +		write++; +	} +  	return write;  } @@ -1861,6 +1995,11 @@ void bgp_debug_init(void)  	install_element(CONFIG_NODE, &no_debug_bgp_bestpath_cmd);  	install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd);  	install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd); + +	install_element(ENABLE_NODE, &debug_bgp_vpn_cmd); +	install_element(CONFIG_NODE, &debug_bgp_vpn_cmd); +	install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd); +	install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);  }  /* Return true if this prefix is on the per_prefix_list of prefixes to debug diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index 765e43f5b4..57f8fef5f2 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -72,6 +72,7 @@ extern unsigned long conf_bgp_debug_zebra;  extern unsigned long conf_bgp_debug_allow_martians;  extern unsigned long conf_bgp_debug_nht;  extern unsigned long conf_bgp_debug_update_groups; +extern unsigned long conf_bgp_debug_vpn;  extern unsigned long term_bgp_debug_as4;  extern unsigned long term_bgp_debug_neighbor_events; @@ -83,6 +84,7 @@ extern unsigned long term_bgp_debug_zebra;  extern unsigned long term_bgp_debug_allow_martians;  extern unsigned long term_bgp_debug_nht;  extern unsigned long term_bgp_debug_update_groups; +extern unsigned long term_bgp_debug_vpn;  extern struct list *bgp_debug_neighbor_events_peers;  extern struct list *bgp_debug_keepalive_peers; @@ -111,6 +113,10 @@ struct bgp_debug_filter {  #define BGP_DEBUG_ALLOW_MARTIANS      0x01  #define BGP_DEBUG_NHT                 0x01  #define BGP_DEBUG_UPDATE_GROUPS       0x01 +#define BGP_DEBUG_VPN_LEAK_FROM_VRF   0x01 +#define BGP_DEBUG_VPN_LEAK_TO_VRF     0x02 +#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04 +#define BGP_DEBUG_VPN_LEAK_LABEL      0x08  #define BGP_DEBUG_PACKET_SEND         0x01  #define BGP_DEBUG_PACKET_SEND_DETAIL  0x02 diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 7039786014..bf60f9c118 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -265,6 +265,8 @@ static int bgp_vrf_enable(struct vrf *vrf)  		if (old_vrf_id != bgp->vrf_id)  			bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id);  		bgp_instance_up(bgp); +		vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); +		vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6);  	}  	return 0; @@ -283,6 +285,10 @@ static int bgp_vrf_disable(struct vrf *vrf)  	bgp = bgp_lookup_by_name(vrf->name);  	if (bgp) { + +		vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP); +		vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6); +  		old_vrf_id = bgp->vrf_id;  		bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);  		/* We have instance configured, unlink from VRF and make it diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index ec4989de8f..d87f78a783 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -27,9 +27,12 @@  #include "stream.h"  #include "queue.h"  #include "filter.h" +#include "mpls.h"  #include "lib/json.h" +#include "lib/zclient.h"  #include "bgpd/bgpd.h" +#include "bgpd/bgp_debug.h"  #include "bgpd/bgp_table.h"  #include "bgpd/bgp_route.h"  #include "bgpd/bgp_attr.h" @@ -38,11 +41,19 @@  #include "bgpd/bgp_packet.h"  #include "bgpd/bgp_vty.h"  #include "bgpd/bgp_vpn.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nexthop.h"  #if ENABLE_BGP_VNC  #include "bgpd/rfapi/rfapi_backend.h"  #endif +/* + * Definitions and external declarations. + */ +extern struct zclient *zclient; +  extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc,  				     int *index, afi_t *afi)  { @@ -92,7 +103,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,  	struct rd_as rd_as;  	struct rd_ip rd_ip;  	struct prefix_rd prd; -	mpls_label_t label; +	mpls_label_t label = {0};  	afi_t afi;  	safi_t safi;  	int addpath_encoded; @@ -233,6 +244,979 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,  #undef VPN_PREFIXLEN_MIN_BYTES  } +/* + * This function informs zebra of the label this vrf sets on routes + * leaked to VPN. Zebra should install this label in the kernel with + * an action of "pop label and then use this vrf's IP FIB to route the PDU." + * + * Sending this vrf-label association is qualified by a) whether vrf->vpn + * exporting is active ("export vpn" is enabled, vpn-policy RD and RT list + * are set) and b) whether vpn-policy label is set. + * + * If any of these conditions do not hold, then we send MPLS_LABEL_NONE + * for this vrf, which zebra interprets to mean "delete this vrf-label + * association." + */ +void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi) +{ +	mpls_label_t label = MPLS_LABEL_NONE; +	const char *name = "default"; +	int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + +	if (debug && (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)) { +		name = bgp->name; +	} + +	if (bgp->vrf_id == VRF_UNKNOWN) { +		if (debug) { +			zlog_debug( +				"%s: vrf %s: afi %s: vrf_id not set, " +				"can't set zebra vrf label", +				__func__, name, afi2str(afi)); +		} +		return; +	} + +	if (vpn_leak_to_vpn_active(bgp, afi, NULL)) { +		label = bgp->vpn_policy[afi].tovpn_label; +	} + +	if (debug) { +		zlog_debug("%s: vrf %s: afi %s: setting label %d for vrf id %d", +			   __func__, name, afi2str(afi), label, bgp->vrf_id); +	} + +	zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); +	bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this label to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) +{ +	mpls_label_t label = MPLS_LABEL_NONE; +	int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + +	if (bgp->vrf_id == VRF_UNKNOWN) { +		if (debug) { +			zlog_debug( +				"%s: vrf_id not set, can't delete zebra vrf label", +				__func__); +		} +		return; +	} + +	if (debug) { +		zlog_debug("%s: deleting label for vrf %s (id=%d)", __func__, +			   (bgp->name ? bgp->name : "default"), bgp->vrf_id); +	} + +	zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); +	bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) +{ +	int i; +	int j; + +	if (!e1 || !e2) +		return 0; + +	for (i = 0; i < e1->size; ++i) { +		for (j = 0; j < e2->size; ++j) { +			if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), +				    e2->val + (j * ECOMMUNITY_SIZE), +				    ECOMMUNITY_SIZE)) { + +				return 1; +			} +		} +	} +	return 0; +} + +/* + * returns pointer to new bgp_info upon success + */ +static struct bgp_info * +leak_update(struct bgp *bgp, /* destination bgp instance */ +	    struct bgp_node *bn, struct attr *new_attr, /* already interned */ +	    afi_t afi, safi_t safi, struct bgp_info *source_bi, u_char type, +	    u_char sub_type, mpls_label_t *label, int num_labels, void *parent, +	    struct bgp *bgp_orig, struct prefix *nexthop_orig, int debug) +{ +	struct prefix *p = &bn->p; +	struct bgp_info *bi; +	struct bgp_info *new; +	char buf_prefix[PREFIX_STRLEN]; +	const char *pDestInstanceName = "default"; + +	if (debug) { +		prefix2str(&bn->p, buf_prefix, sizeof(buf_prefix)); +		if (bgp->name) +			pDestInstanceName = bgp->name; +	} + +	/* +	 * match parent +	 */ +	for (bi = bn->info; bi; bi = bi->next) { +		if (bi->extra && bi->extra->parent == parent) +			break; +	} + +	if (bi) { +		if (attrhash_cmp(bi->attr, new_attr) +		    && !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) { + +			bgp_attr_unintern(&new_attr); +			if (debug) +				zlog_debug( +					"%s: ->%s: %s: Found route, no change", +					__func__, pDestInstanceName, +					buf_prefix); +			return NULL; +		} + +		/* attr is changed */ +		bgp_info_set_flag(bn, bi, BGP_INFO_ATTR_CHANGED); + +		/* Rewrite BGP route information. */ +		if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) +			bgp_info_restore(bn, bi); +		else +			bgp_aggregate_decrement(bgp, p, bi, afi, safi); +		bgp_attr_unintern(&bi->attr); +		bi->attr = new_attr; +		bi->uptime = bgp_clock(); + +		/* Process change. */ +		bgp_aggregate_increment(bgp, p, bi, afi, safi); +		bgp_process(bgp, bn, afi, safi); +		bgp_unlock_node(bn); + +		if (debug) +			zlog_debug("%s: ->%s: %s Found route, changed attr", +				   __func__, pDestInstanceName, buf_prefix); + +		return NULL; +	} + +	new = info_make(type, sub_type, 0, bgp->peer_self, new_attr, bn); +	SET_FLAG(new->flags, BGP_INFO_VALID); + +	bgp_info_extra_get(new); +	if (label) { +		int i; + +		for (i = 0; i < num_labels; ++i) { +			new->extra->label[i] = label[i]; +			if (!bgp_is_valid_label(&label[i])) { +				if (debug) { +					zlog_debug( +						"%s: %s: marking label %d valid", +						__func__, buf_prefix, i); +				} +				bgp_set_valid_label(&new->extra->label[i]); +			} +		} +		new->extra->num_labels = num_labels; +	} +	new->extra->parent = parent; + +	if (bgp_orig) +		new->extra->bgp_orig = bgp_orig; +	if (nexthop_orig) +		new->extra->nexthop_orig = *nexthop_orig; + +	bgp_aggregate_increment(bgp, p, new, afi, safi); +	bgp_info_add(bn, new); + +	bgp_unlock_node(bn); +	bgp_process(bgp, bn, afi, safi); + +	if (debug) +		zlog_debug("%s: ->%s: %s: Added new route", __func__, +			   pDestInstanceName, buf_prefix); + +	return new; +} + +/* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */ +void vpn_leak_from_vrf_update(struct bgp *bgp_vpn,       /* to */ +			      struct bgp *bgp_vrf,       /* from */ +			      struct bgp_info *info_vrf) /* route */ +{ +	int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); +	struct prefix *p = &info_vrf->net->p; +	afi_t afi = family2afi(p->family); +	struct attr static_attr = {0}; +	struct attr *new_attr = NULL; +	safi_t safi = SAFI_MPLS_VPN; +	mpls_label_t label_val; +	mpls_label_t label; +	struct bgp_node *bn; +	const char *debugmsg; + +	if (debug) { +		const char *s = ""; + +		if (info_vrf->attr && info_vrf->attr->ecommunity) { +			s = ecommunity_ecom2str(info_vrf->attr->ecommunity, +						ECOMMUNITY_FORMAT_ROUTE_MAP, 0); +		} + +		zlog_debug("%s: info_vrf->type=%d, EC{%s}", __func__, +			   info_vrf->type, s); +	} + +	if (!bgp_vpn) +		return; + +	if (!afi) { +		if (debug) +			zlog_debug("%s: can't get afi of prefix", __func__); +		return; +	} + +	/* loop check */ +	if (info_vrf->extra && info_vrf->extra->bgp_orig == bgp_vpn) +		return; + + +	if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { +		if (debug) +			zlog_debug("%s: skipping: %s", __func__, debugmsg); +		return; +	} + +	bgp_attr_dup(&static_attr, info_vrf->attr); /* shallow copy */ + +	/* +	 * route map handling +	 */ +	if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) { +		struct bgp_info info; +		route_map_result_t ret; + +		memset(&info, 0, sizeof(info)); +		info.peer = bgp_vpn->peer_self; +		info.attr = &static_attr; +		ret = route_map_apply( +			bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN], +			p, RMAP_BGP, &info); +		if (RMAP_DENYMATCH == ret) { +			bgp_attr_flush(&static_attr); /* free any added parts */ +			if (debug) +				zlog_debug( +					"%s: vrf %s route map \"%s\" says DENY, returning", +					__func__, bgp_vrf->name, +					bgp_vrf->vpn_policy[afi] +						.rmap[BGP_VPN_POLICY_DIR_TOVPN] +						->name); +			return; +		} +	} + +	if (debug) { +		const char *s = ""; + +		if (static_attr.ecommunity) { +			s = ecommunity_ecom2str(static_attr.ecommunity, +						ECOMMUNITY_FORMAT_ROUTE_MAP, 0); +		} +		zlog_debug("%s: post route map static_attr.ecommunity{%s}", +			   __func__, s); +	} + +	/* +	 * Add the vpn-policy rt-list +	 */ +	struct ecommunity *old_ecom; +	struct ecommunity *new_ecom; + +	old_ecom = static_attr.ecommunity; +	if (old_ecom) { +		new_ecom = ecommunity_merge( +			ecommunity_dup(old_ecom), +			bgp_vrf->vpn_policy[afi] +				.rtlist[BGP_VPN_POLICY_DIR_TOVPN]); +		if (!old_ecom->refcnt) +			ecommunity_free(&old_ecom); +	} else { +		new_ecom = ecommunity_dup( +			bgp_vrf->vpn_policy[afi] +				.rtlist[BGP_VPN_POLICY_DIR_TOVPN]); +	} +	static_attr.ecommunity = new_ecom; +	SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + +	if (debug) { +		const char *s = ""; + +		if (static_attr.ecommunity) { +			s = ecommunity_ecom2str(static_attr.ecommunity, +						ECOMMUNITY_FORMAT_ROUTE_MAP, 0); +		} +		zlog_debug("%s: post merge static_attr.ecommunity{%s}", +			   __func__, s); +	} + +	/* Nexthop */ +	/* if policy nexthop not set, use 0 */ +	if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, +		       BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + +		struct prefix *nexthop = +			&bgp_vrf->vpn_policy[afi].tovpn_nexthop; +		switch (nexthop->family) { +		case AF_INET: +			/* prevent mp_nexthop_global_in <- self in bgp_route.c +			 */ +			static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr; + +			static_attr.mp_nexthop_global_in = nexthop->u.prefix4; +			static_attr.mp_nexthop_len = 4; +			break; + +		case AF_INET6: +			static_attr.mp_nexthop_global = nexthop->u.prefix6; +			static_attr.mp_nexthop_len = 16; +			break; + +		default: +			assert(0); +		} +	} else { +		switch (afi) { +		case AFI_IP: +		default: +			/* Clear ipv4 */ +			static_attr.mp_nexthop_global_in.s_addr = 0; +			static_attr.mp_nexthop_len = 4; +			static_attr.nexthop.s_addr = 0; /* self */ +			static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); +			break; + +		case AFI_IP6: +			/* Clear ipv6 */ +			memset(&static_attr.mp_nexthop_global, 0, +			       sizeof(static_attr.mp_nexthop_global)); +			static_attr.mp_nexthop_len = 16; /* bytes */ +			break; +		} +	} + +	label_val = bgp_vrf->vpn_policy[afi].tovpn_label; +	if (label_val == MPLS_LABEL_NONE) { +		/* TBD get from label manager */ +		label = MPLS_LABEL_IMPLICIT_NULL; +	} else { +		encode_label(label_val, &label); +	} + +	/* Set originator ID to "me" */ +	SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)); +	static_attr.originator_id = bgp_vpn->router_id; + + +	new_attr = bgp_attr_intern( +		&static_attr);	/* hashed refcounted everything */ +	bgp_attr_flush(&static_attr); /* free locally-allocated parts */ + +	if (debug) { +		const char *s = ""; + +		if (new_attr->ecommunity) { +			s = ecommunity_ecom2str(new_attr->ecommunity, +						ECOMMUNITY_FORMAT_ROUTE_MAP, 0); +		} +		zlog_debug("%s: new_attr->ecommunity{%s}", __func__, s); +	} + +	/* Now new_attr is an allocated interned attr */ + +	bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p, +			      &(bgp_vrf->vpn_policy[afi].tovpn_rd)); + +	struct bgp_info *new_info; + +	new_info = leak_update(bgp_vpn, bn, new_attr, afi, safi, info_vrf, +			       ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, &label, 1, +			       info_vrf, bgp_vrf, NULL, debug); + +	/* +	 * Routes actually installed in the vpn RIB must also be +	 * offered to all vrfs (because now they originate from +	 * the vpn RIB). +	 * +	 * Acceptance into other vrfs depends on rt-lists. +	 * Originating vrf will not accept the looped back route +	 * because of loop checking. +	 */ +	if (new_info) +		vpn_leak_to_vrf_update(bgp_vrf, new_info); +} + +void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn,       /* to */ +				struct bgp *bgp_vrf,       /* from */ +				struct bgp_info *info_vrf) /* route */ +{ +	int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); +	struct prefix *p = &info_vrf->net->p; +	afi_t afi = family2afi(p->family); +	safi_t safi = SAFI_MPLS_VPN; +	struct bgp_info *bi; +	struct bgp_node *bn; +	const char *debugmsg; + +	if (info_vrf->type != ZEBRA_ROUTE_BGP) { +		if (debug) +			zlog_debug("%s: wrong type %d", __func__, +				   info_vrf->type); +		return; +	} +	if (info_vrf->sub_type != BGP_ROUTE_NORMAL +	    && info_vrf->sub_type != BGP_ROUTE_STATIC) { + +		if (debug) +			zlog_debug("%s: wrong sub_type %d", __func__, +				   info_vrf->sub_type); +		return; +	} +	if (!bgp_vpn) +		return; + +	if (!afi) { +		if (debug) +			zlog_debug("%s: can't get afi of prefix", __func__); +		return; +	} + +	if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { +		if (debug) +			zlog_debug("%s: skipping: %s", __func__, debugmsg); +		return; +	} + +	if (debug) +		zlog_debug("%s: withdrawing (info_vrf=%p)", __func__, info_vrf); + +	bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p, +			      &(bgp_vrf->vpn_policy[afi].tovpn_rd)); + +	/* +	 * vrf -> vpn +	 * match original bi imported from +	 */ +	for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) { +		if (bi->extra && bi->extra->parent == info_vrf) { +			break; +		} +	} + +	if (bi) { +		/* withdraw from looped vrfs as well */ +		vpn_leak_to_vrf_withdraw(bgp_vpn, bi); + +		bgp_aggregate_decrement(bgp_vpn, p, bi, afi, safi); +		bgp_info_delete(bn, bi); +		bgp_process(bgp_vpn, bn, afi, safi); +	} +	bgp_unlock_node(bn); +} + +void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ +				    struct bgp *bgp_vrf, /* from */ +				    afi_t afi) +{ +	int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); +	struct bgp_node *prn; +	safi_t safi = SAFI_MPLS_VPN; + +	/* +	 * Walk vpn table, delete bi with parent == bgp_vrf +	 * Walk vpn table, delete bi with bgp_orig == bgp_vrf +	 */ +	for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; +	     prn = bgp_route_next(prn)) { + +		struct bgp_table *table; +		struct bgp_node *bn; +		struct bgp_info *bi; + +		/* This is the per-RD table of prefixes */ +		table = prn->info; + +		if (!table) +			continue; + +		for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + +			char buf[PREFIX2STR_BUFFER]; + +			if (debug && bn->info) { +				zlog_debug( +					"%s: looking at prefix %s", __func__, +					prefix2str(&bn->p, buf, sizeof(buf))); +			} + +			for (bi = bn->info; bi; bi = bi->next) { +				if (debug) +					zlog_debug("%s: type %d, sub_type %d", +						   __func__, bi->type, +						   bi->sub_type); +				if (bi->sub_type != BGP_ROUTE_IMPORTED) +					continue; +				if (!bi->extra) +					continue; +				if ((struct bgp *)bi->extra->bgp_orig +				    == bgp_vrf) { +					/* delete route */ +					if (debug) +						zlog_debug("%s: deleting it\n", +							   __func__); +					bgp_aggregate_decrement(bgp_vpn, &bn->p, +								bi, afi, safi); +					bgp_info_delete(bn, bi); +					bgp_process(bgp_vpn, bn, afi, safi); +				} +			} +		} +	} +} + +void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, /* to */ +				  struct bgp *bgp_vrf, /* from */ +				  afi_t afi) +{ +	struct bgp_node *bn; +	struct bgp_info *bi; +	int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + +	if (debug) +		zlog_debug("%s: entry, afi=%d, vrf=%s", __func__, afi, +			   bgp_vrf->name); + +	for (bn = bgp_table_top(bgp_vrf->rib[afi][SAFI_UNICAST]); bn; +	     bn = bgp_route_next(bn)) { + +		if (debug) +			zlog_debug("%s: node=%p", __func__, bn); + +		for (bi = bn->info; bi; bi = bi->next) { +			if (debug) +				zlog_debug( +					"%s: calling vpn_leak_from_vrf_update", +					__func__); +			vpn_leak_from_vrf_update(bgp_vpn, bgp_vrf, bi); +		} +	} +} + +static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf,       /* to */ +					  struct bgp *bgp_vpn,       /* from */ +					  struct bgp_info *info_vpn) /* route */ +{ +	struct prefix *p = &info_vpn->net->p; +	afi_t afi = family2afi(p->family); + +	struct bgp_redist *red; +	struct attr static_attr = {0}; +	struct attr *new_attr = NULL; +	struct bgp_node *bn; +	safi_t safi = SAFI_UNICAST; +	const char *debugmsg; +	struct prefix nexthop_orig; +	mpls_label_t *pLabels = NULL; +	int num_labels = 0; + +	int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + +	if (!vpn_leak_from_vpn_active(bgp_vrf, afi, &debugmsg, &red)) { +		if (debug) +			zlog_debug("%s: skipping: %s", __func__, debugmsg); +		return; +	} + +	/* Check for intersection of route targets */ +	if (!ecom_intersect( +		    bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], +		    info_vpn->attr->ecommunity)) { + +		return; +	} + +	if (debug) +		zlog_debug("%s: updating to vrf %s", __func__, bgp_vrf->name); + +	bgp_attr_dup(&static_attr, info_vpn->attr); /* shallow copy */ + +	/* +	 * Nexthop: stash and clear +	 * +	 * Nexthop is valid in context of VPN core, but not in destination vrf. +	 * Stash it for later label resolution by vrf ingress path and then +	 * overwrite with 0, i.e., "me", for the sake of vrf advertisement. +	 */ +	uint8_t nhfamily = NEXTHOP_FAMILY(info_vpn->attr->mp_nexthop_len); + +	memset(&nexthop_orig, 0, sizeof(nexthop_orig)); +	nexthop_orig.family = nhfamily; + +	switch (nhfamily) { + +	case AF_INET: +		/* save */ +		nexthop_orig.u.prefix4 = info_vpn->attr->mp_nexthop_global_in; +		nexthop_orig.prefixlen = 32; + +		static_attr.nexthop.s_addr = 0; /* self */ +		static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + +		break; + +	case AF_INET6: +		/* save */ +		nexthop_orig.u.prefix6 = info_vpn->attr->mp_nexthop_global; +		nexthop_orig.prefixlen = 128; + +		memset(&static_attr.mp_nexthop_global, 0, +		       sizeof(static_attr.mp_nexthop_global)); /* clear */ +		static_attr.mp_nexthop_len = 16;	       /* bytes */ +		break; +	} + + +	/* +	 * route map handling +	 * For now, we apply two route maps: the "redist" route map and the +	 * vpn-policy route map. Once we finalize CLI syntax, one of these +	 * route maps will probably go away. +	 */ +	if (red->rmap.map) { +		struct bgp_info info; +		route_map_result_t ret; + +		memset(&info, 0, sizeof(info)); +		info.peer = bgp_vrf->peer_self; +		info.attr = &static_attr; +		ret = route_map_apply(red->rmap.map, p, RMAP_BGP, &info); +		if (RMAP_DENYMATCH == ret) { +			bgp_attr_flush(&static_attr); /* free any added parts */ +			if (debug) +				zlog_debug( +					"%s: vrf %s redist route map \"%s\" says DENY, skipping", +					__func__, bgp_vrf->name, +					red->rmap.name); +			return; +		} +	} +	if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) { +		struct bgp_info info; +		route_map_result_t ret; + +		memset(&info, 0, sizeof(info)); +		info.peer = bgp_vrf->peer_self; +		info.attr = &static_attr; +		ret = route_map_apply(bgp_vrf->vpn_policy[afi] +					      .rmap[BGP_VPN_POLICY_DIR_FROMVPN], +				      p, RMAP_BGP, &info); +		if (RMAP_DENYMATCH == ret) { +			bgp_attr_flush(&static_attr); /* free any added parts */ +			if (debug) +				zlog_debug( +					"%s: vrf %s vpn-policy route map \"%s\" says DENY, returning", +					__func__, bgp_vrf->name, +					bgp_vrf->vpn_policy[afi] +						.rmap[BGP_VPN_POLICY_DIR_FROMVPN] +						->name); +			return; +		} +	} + +	new_attr = bgp_attr_intern(&static_attr); +	bgp_attr_flush(&static_attr); + +	bn = bgp_afi_node_get(bgp_vrf->rib[afi][safi], afi, safi, p, NULL); + +	/* +	 * ensure labels are copied +	 */ +	if (info_vpn->extra && info_vpn->extra->num_labels) { +		num_labels = info_vpn->extra->num_labels; +		if (num_labels > BGP_MAX_LABELS) +			num_labels = BGP_MAX_LABELS; +		pLabels = info_vpn->extra->label; +	} +	if (debug) { +		char buf_prefix[PREFIX_STRLEN]; +		prefix2str(p, buf_prefix, sizeof(buf_prefix)); +		zlog_debug("%s: pfx %s: num_labels %d", __func__, buf_prefix, +			   num_labels); +	} + +	leak_update(bgp_vrf, bn, new_attr, afi, safi, info_vpn, ZEBRA_ROUTE_BGP, +		    BGP_ROUTE_IMPORTED, pLabels, num_labels, +		    info_vpn, /* parent */ +		    bgp_vpn, &nexthop_orig, debug); +} + +void vpn_leak_to_vrf_update(struct bgp *bgp_vpn,       /* from */ +			    struct bgp_info *info_vpn) /* route */ +{ +	struct listnode *mnode, *mnnode; +	struct bgp *bgp; + +	int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + +	if (debug) +		zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn); + +	/* Loop over VRFs */ +	for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + +		if (!info_vpn->extra +		    || info_vpn->extra->bgp_orig != bgp) { /* no loop */ +			vpn_leak_to_vrf_update_onevrf(bgp, bgp_vpn, info_vpn); +		} +	} +} + +void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn,       /* from */ +			      struct bgp_info *info_vpn) /* route */ +{ +	struct prefix *p = &info_vpn->net->p; +	afi_t afi = family2afi(p->family); +	safi_t safi = SAFI_UNICAST; +	struct bgp *bgp; +	struct listnode *mnode, *mnnode; +	struct bgp_redist *red; +	struct bgp_node *bn; +	struct bgp_info *bi; +	const char *debugmsg; + +	int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + +	if (debug) +		zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn); + + +	/* Loop over VRFs */ +	for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { +		if (!vpn_leak_from_vpn_active(bgp, afi, &debugmsg, &red)) { +			if (debug) +				zlog_debug("%s: skipping: %s", __func__, +					   debugmsg); +			continue; +		} + +		/* Check for intersection of route targets */ +		if (!ecom_intersect(bgp->vpn_policy[afi] +					    .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], +				    info_vpn->attr->ecommunity)) { + +			continue; +		} + +		if (debug) +			zlog_debug("%s: withdrawing from vrf %s", __func__, +				   bgp->name); + +		bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); +		for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) { +			if (bi->extra +			    && (struct bgp_info *)bi->extra->parent +				       == info_vpn) { +				break; +			} +		} + +		if (bi) { +			if (debug) +				zlog_debug("%s: deleting bi %p", __func__, bi); +			bgp_aggregate_decrement(bgp, p, bi, afi, safi); +			bgp_info_delete(bn, bi); +			bgp_process(bgp, bn, afi, safi); +		} +		bgp_unlock_node(bn); +	} +} + +void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ +				  afi_t afi) +{ +	struct bgp_node *bn; +	struct bgp_info *bi; +	safi_t safi = SAFI_UNICAST; +	int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); +	struct bgp *bgp_vpn = bgp_get_default(); + +	if (debug) +		zlog_debug("%s: entry", __func__); +	/* +	 * Walk vrf table, delete bi with bgp_orig == bgp_vpn +	 */ +	for (bn = bgp_table_top(bgp_vrf->rib[afi][safi]); bn; +	     bn = bgp_route_next(bn)) { + +		for (bi = bn->info; bi; bi = bi->next) { +			if (bi->extra && bi->extra->bgp_orig == bgp_vpn) { + +				/* delete route */ +				bgp_aggregate_decrement(bgp_vrf, &bn->p, bi, +							afi, safi); +				bgp_info_delete(bn, bi); +				bgp_process(bgp_vrf, bn, afi, safi); +			} +		} +	} +} + +void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ +				struct bgp *bgp_vpn, /* from */ +				afi_t afi) +{ +	struct prefix_rd prd; +	struct bgp_node *prn; +	safi_t safi = SAFI_MPLS_VPN; + +	/* +	 * Walk vpn table +	 */ +	for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; +	     prn = bgp_route_next(prn)) { + +		struct bgp_table *table; +		struct bgp_node *bn; +		struct bgp_info *bi; + +		memset(&prd, 0, sizeof(prd)); +		prd.family = AF_UNSPEC; +		prd.prefixlen = 64; +		memcpy(prd.val, prn->p.u.val, 8); + +		/* This is the per-RD table of prefixes */ +		table = prn->info; + +		if (!table) +			continue; + +		for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + +			for (bi = bn->info; bi; bi = bi->next) { + +				if (bi->extra && bi->extra->bgp_orig == bgp_vrf) +					continue; + +				vpn_leak_to_vrf_update_onevrf(bgp_vrf, bgp_vpn, +							      bi); +			} +		} +	} +} + +static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name) +{ +	int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); +	afi_t afi; +	struct route_map *rmap; + +	if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT +	    && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) { + +		return; +	} + +	rmap = route_map_lookup_by_name(rmap_name); /* NULL if deleted */ + +	for (afi = 0; afi < AFI_MAX; ++afi) { + +		if (vpn_leak_to_vpn_active(bgp, afi, NULL) +		    && bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN] +		    && !strcmp(rmap_name, +			       bgp->vpn_policy[afi] +				       .rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) { + +			if (debug) +				zlog_debug( +					"%s: rmap \"%s\" matches vrf-policy tovpn for as %d afi %s", +					__func__, rmap_name, bgp->as, +					afi2str(afi)); + +			vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +					   bgp_get_default(), bgp); +			if (debug) +				zlog_debug("%s: after vpn_leak_prechange", +					   __func__); + +			if (!rmap) +				bgp->vpn_policy[afi] +					.rmap[BGP_VPN_POLICY_DIR_TOVPN] = NULL; + +			vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +					    bgp_get_default(), bgp); +			if (debug) +				zlog_debug("%s: after vpn_leak_postchange", +					   __func__); +		} + +		/* +		 * vpn -> vrf leaking currently can have two route-maps: +		 * 1. the vpn-policy tovpn route-map +		 * 2. the (per-afi) redistribute vpn route-map +		 */ +		char *mapname_vpn_policy = +			bgp->vpn_policy[afi] +				.rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]; +		struct bgp_redist *red = NULL; + +		if (vpn_leak_from_vpn_active(bgp, afi, NULL, &red) +		    && ((mapname_vpn_policy +			 && !strcmp(rmap_name, mapname_vpn_policy)) +			|| (red && red->rmap.name +			    && !strcmp(red->rmap.name, rmap_name)))) { + +			if (debug) +				zlog_debug( +					"%s: rmap \"%s\" matches vrf-policy fromvpn" +					" for as %d afi %s", +					__func__, rmap_name, bgp->as, +					afi2str(afi)); + +			vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, +					   bgp_get_default(), bgp); + +			if (!rmap) +				bgp->vpn_policy[afi] +					.rmap[BGP_VPN_POLICY_DIR_FROMVPN] = +					NULL; + +			vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, +					    bgp_get_default(), bgp); +		} +	} +} + +void vpn_policy_routemap_event(const char *rmap_name) +{ +	int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); +	struct listnode *mnode, *mnnode; +	struct bgp *bgp; + +	if (debug) +		zlog_debug("%s: entry", __func__); + +	if (bm->bgp == NULL) /* may be called during cleanup */ +		return; + +	for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) +		vpn_policy_routemap_update(bgp, rmap_name); +} +  /* For testing purpose, static route of MPLS-VPN. */  DEFUN (vpnv4_network,         vpnv4_network_cmd, diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 5c11f7526c..d0ad8ac846 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -23,6 +23,7 @@  #include "bgpd/bgp_route.h"  #include "bgpd/bgp_rd.h" +#include "bgpd/bgp_zebra.h"  #define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)  #define MPLS_LABEL_IS_NULL(label)                                              \ @@ -51,4 +52,122 @@ extern int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd,  			     enum bgp_show_type type, void *output_arg,  			     int tags, u_char use_json); +extern void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, struct bgp *bgp_vrf, +				     struct bgp_info *info_vrf); + +extern void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, struct bgp *bgp_vrf, +				       struct bgp_info *info_vrf); + +extern void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, +					   struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, +					 struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, struct bgp *bgp_vpn, +				       afi_t afi); + +extern void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, +				   struct bgp_info *info_vpn); + +extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, +				     struct bgp_info *info_vpn); + +extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi); + +static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, +					 const char **pmsg) +{ +	/* Is vrf configured to export to vpn? */ +	if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], +			BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { +		if (pmsg) +			*pmsg = "export not set"; +		return 0; +	} + +	/* Is there an RT list set? */ +	if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { +		if (pmsg) +			*pmsg = "rtlist tovpn not defined"; +		return 0; +	} + +	/* Is there an RD set? */ +	if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, +			BGP_VPN_POLICY_TOVPN_RD_SET)) { +		if (pmsg) +			*pmsg = "rd not defined"; +		return 0; +	} +	return 1; +} + +static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi, +					   const char **pmsg, +					   struct bgp_redist **pred) +{ +	struct bgp_redist *red; + +	if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF +	    && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + +		if (pmsg) +			*pmsg = "destination bgp instance neither vrf nor default"; +		return 0; +	} + +	/* Hijack zebra redist bits for this route type */ +	red = bgp_redist_lookup(bgp_vrf, afi, ZEBRA_ROUTE_BGP_VPN, 0); +	if (red) { +		if (pred) +			*pred = red; +	} else { +		if (pmsg) +			*pmsg = "redist not set"; +		return 0; +	} +	if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { +		if (pmsg) +			*pmsg = "rtlist fromvpn not defined"; +		return 0; +	} +	return 1; +} + +static inline void vpn_leak_prechange(vpn_policy_direction_t direction, +				      afi_t afi, struct bgp *bgp_vpn, +				      struct bgp *bgp_vrf) +{ +	if (direction == BGP_VPN_POLICY_DIR_FROMVPN) +		vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi); +	if (direction == BGP_VPN_POLICY_DIR_TOVPN) +		vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi); +} + +static inline void vpn_leak_postchange(vpn_policy_direction_t direction, +				       afi_t afi, struct bgp *bgp_vpn, +				       struct bgp *bgp_vrf) +{ +	if (direction == BGP_VPN_POLICY_DIR_FROMVPN) +		vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi); +	if (direction == BGP_VPN_POLICY_DIR_TOVPN) { + +		if (bgp_vrf->vpn_policy[afi].tovpn_label +		    != bgp_vrf->vpn_policy[afi] +			       .tovpn_zebra_vrf_label_last_sent) { +			vpn_leak_zebra_vrf_label_update(bgp_vrf, afi); +		} + +		vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi); +	} +	if (direction == BGP_VPN_POLICY_DIR_TOVPN) +		vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi); +} + +extern void vpn_policy_routemap_event(const char *rmap_name); +  #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 35e9faf3ff..26f96d684a 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1364,6 +1364,16 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,  	}  #endif +	if (((afi == AFI_IP) || (afi == AFI_IP6)) +	    && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_UNICAST)) +	    && (ri->type == ZEBRA_ROUTE_BGP) +	    && (ri->sub_type == BGP_ROUTE_IMPORTED)) { + +		/* Applies to routes leaked vpn->vrf and vrf->vpn */ + +		samepeer_safe = 1; +	} +  	/* With addpath we may be asked to TX all kinds of paths so make sure  	 * ri is valid */  	if (!CHECK_FLAG(ri->flags, BGP_INFO_VALID) @@ -1852,17 +1862,30 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,  			    && (ri != old_select))  				bgp_info_reap(rn, ri); +			if (debug) +				zlog_debug("%s: ri %p in holddown", __func__, +					   ri); +  			continue;  		}  		if (ri->peer && ri->peer != bgp->peer_self  		    && !CHECK_FLAG(ri->peer->sflags, PEER_STATUS_NSF_WAIT)) -			if (ri->peer->status != Established) +			if (ri->peer->status != Established) { + +				if (debug) +					zlog_debug( +						"%s: ri %p non self peer %s not estab state", +						__func__, ri, ri->peer->host); +  				continue; +			}  		if (bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)  		    && (!CHECK_FLAG(ri->flags, BGP_INFO_DMED_SELECTED))) {  			bgp_info_unset_flag(rn, ri, BGP_INFO_DMED_CHECK); +			if (debug) +				zlog_debug("%s: ri %p dmed", __func__, ri);  			continue;  		} @@ -1969,8 +1992,8 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp,  	if (BGP_DEBUG(update, UPDATE_OUT)) {  		char buf_prefix[PREFIX_STRLEN];  		prefix2str(p, buf_prefix, sizeof(buf_prefix)); -		zlog_debug("%s: p=%s, selected=%p", -		    __func__, buf_prefix, selected); +		zlog_debug("%s: p=%s, selected=%p", __func__, buf_prefix, +			   selected);  	}  	/* First update is deferred until ORF or ROUTE-REFRESH is received */ @@ -2064,6 +2087,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  	struct bgp_info *new_select;  	struct bgp_info *old_select;  	struct bgp_info_pair old_and_new; +	char pfx_buf[PREFIX2STR_BUFFER]; +	int debug = 0;  	/* Is it end of initial update? (after startup) */  	if (!rn) { @@ -2081,6 +2106,13 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  		return;  	} +	debug = bgp_debug_bestpath(&rn->p); +	if (debug) { +		prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); +		zlog_debug("%s: p=%s afi=%s, safi=%s start", __func__, pfx_buf, +			   afi2str(afi), safi2str(safi)); +	} +  	/* Best path selection. */  	bgp_best_selection(bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new,  			   afi, safi); @@ -2121,6 +2153,14 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  		bgp_unregister_for_label(rn);  	} +	if (debug) { +		prefix2str(&rn->p, pfx_buf, sizeof(pfx_buf)); +		zlog_debug( +			"%s: p=%s afi=%s, safi=%s, old_select=%p, new_select=%p", +			__func__, pfx_buf, afi2str(afi), safi2str(safi), +			old_select, new_select); +	} +  	/* If best route remains the same and this is not due to user-initiated  	 * clear, see exactly what needs to be done.  	 */ @@ -2135,11 +2175,16 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  			vnc_import_bgp_exterior_add_route(bgp, p, old_select);  #endif  			if (bgp_fibupd_safi(safi) -			    && !bgp_option_check(BGP_OPT_NO_FIB) -			    && new_select->type == ZEBRA_ROUTE_BGP -			    && new_select->sub_type == BGP_ROUTE_NORMAL) -				bgp_zebra_announce(rn, p, old_select, bgp, afi, -						   safi); +			    && !bgp_option_check(BGP_OPT_NO_FIB)) { + +				if (new_select->type == ZEBRA_ROUTE_BGP +				    && (new_select->sub_type == BGP_ROUTE_NORMAL +					|| new_select->sub_type +						   == BGP_ROUTE_IMPORTED)) + +					bgp_zebra_announce(rn, p, old_select, +							   bgp, afi, safi); +			}  		}  		UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);  		bgp_zebra_clear_route_change_flags(rn); @@ -2186,6 +2231,8 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  	if (old_select)  		bgp_info_unset_flag(rn, old_select, BGP_INFO_SELECTED);  	if (new_select) { +		if (debug) +			zlog_debug("%s: setting SELECTED flag", __func__);  		bgp_info_set_flag(rn, new_select, BGP_INFO_SELECTED);  		bgp_info_unset_flag(rn, new_select, BGP_INFO_ATTR_CHANGED);  		UNSET_FLAG(new_select->flags, BGP_INFO_MULTIPATH_CHG); @@ -2221,13 +2268,17 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,  	    && !bgp_option_check(BGP_OPT_NO_FIB)) {  		if (new_select && new_select->type == ZEBRA_ROUTE_BGP  		    && (new_select->sub_type == BGP_ROUTE_NORMAL -			|| new_select->sub_type == BGP_ROUTE_AGGREGATE)) +			|| new_select->sub_type == BGP_ROUTE_AGGREGATE +			|| new_select->sub_type == BGP_ROUTE_IMPORTED)) +  			bgp_zebra_announce(rn, p, new_select, bgp, afi, safi);  		else {  			/* Withdraw the route from the kernel. */  			if (old_select && old_select->type == ZEBRA_ROUTE_BGP  			    && (old_select->sub_type == BGP_ROUTE_NORMAL -				|| old_select->sub_type == BGP_ROUTE_AGGREGATE)) +				|| old_select->sub_type == BGP_ROUTE_AGGREGATE +				|| old_select->sub_type == BGP_ROUTE_IMPORTED)) +  				bgp_zebra_withdraw(p, old_select, safi);  		}  	} @@ -3128,6 +3179,18 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,  		bgp_process(bgp, rn, afi, safi);  		bgp_unlock_node(rn); +		if (SAFI_UNICAST == safi +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +			|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_from_vrf_update(bgp_get_default(), bgp, ri); +		} +		if ((SAFI_MPLS_VPN == safi) +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_to_vrf_update(bgp, ri); +		} +  #if ENABLE_BGP_VNC  		if (SAFI_MPLS_VPN == safi) {  			mpls_label_t label_decoded = decode_label(label); @@ -3244,6 +3307,16 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id,  	/* Process change. */  	bgp_process(bgp, rn, afi, safi); +	if (SAFI_UNICAST == safi +	    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +		|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { +		vpn_leak_from_vrf_update(bgp_get_default(), bgp, new); +	} +	if ((SAFI_MPLS_VPN == safi) +	    && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +		vpn_leak_to_vrf_update(bgp, new); +	}  #if ENABLE_BGP_VNC  	if (SAFI_MPLS_VPN == safi) {  		mpls_label_t label_decoded = decode_label(label); @@ -3282,6 +3355,18 @@ filtered:  		if (safi == SAFI_EVPN)  			bgp_evpn_unimport_route(bgp, afi, safi, p, ri); +		if (SAFI_UNICAST == safi +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +			|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri); +		} +		if ((SAFI_MPLS_VPN == safi) +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_to_vrf_withdraw(bgp, ri); +		} +  		bgp_rib_remove(rn, ri, peer, afi, safi);  	} @@ -3368,9 +3453,19 @@ int bgp_withdraw(struct peer *peer, struct prefix *p, u_int32_t addpath_id,  	}  	/* Withdraw specified route from routing table. */ -	if (ri && !CHECK_FLAG(ri->flags, BGP_INFO_HISTORY)) +	if (ri && !CHECK_FLAG(ri->flags, BGP_INFO_HISTORY)) {  		bgp_rib_withdraw(rn, ri, peer, afi, safi, prd); -	else if (bgp_debug_update(peer, p, NULL, 1)) { +		if (SAFI_UNICAST == safi +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +			|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { +			vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri); +		} +		if ((SAFI_MPLS_VPN == safi) +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_to_vrf_withdraw(bgp, ri); +		} +	} else if (bgp_debug_update(peer, p, NULL, 1)) {  		bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,  					addpath_id ? 1 : 0, addpath_id, pfx_buf,  					sizeof(pfx_buf)); @@ -3854,7 +3949,9 @@ static void bgp_cleanup_table(struct bgp_table *table, safi_t safi)  			if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)  			    && ri->type == ZEBRA_ROUTE_BGP  			    && (ri->sub_type == BGP_ROUTE_NORMAL -				|| ri->sub_type == BGP_ROUTE_AGGREGATE)) { +				|| ri->sub_type == BGP_ROUTE_AGGREGATE +				|| ri->sub_type == BGP_ROUTE_IMPORTED)) { +  				if (bgp_fibupd_safi(safi))  					bgp_zebra_withdraw(&rn->p, ri, safi);  				bgp_info_reap(rn, ri); @@ -4255,6 +4352,15 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p,  			/* Process change. */  			bgp_aggregate_increment(bgp, p, ri, afi, safi);  			bgp_process(bgp, rn, afi, safi); + +			if (SAFI_UNICAST == safi +			    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +				|| bgp->inst_type +					   == BGP_INSTANCE_TYPE_DEFAULT)) { +				vpn_leak_from_vrf_update(bgp_get_default(), bgp, +							 ri); +			} +  			bgp_unlock_node(rn);  			aspath_unintern(&attr.aspath);  			return; @@ -4302,6 +4408,12 @@ void bgp_static_update(struct bgp *bgp, struct prefix *p,  	/* Process change. */  	bgp_process(bgp, rn, afi, safi); +	if (SAFI_UNICAST == safi +	    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +		|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { +		vpn_leak_from_vrf_update(bgp_get_default(), bgp, new); +	} +  	/* Unintern original. */  	aspath_unintern(&attr.aspath);  } @@ -4322,6 +4434,11 @@ void bgp_static_withdraw(struct bgp *bgp, struct prefix *p, afi_t afi,  	/* Withdraw static BGP route from routing table. */  	if (ri) { +		if (SAFI_UNICAST == safi +		    && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF +			|| bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { +			vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri); +		}  		bgp_aggregate_decrement(bgp, p, ri, afi, safi);  		bgp_unlink_nexthop(ri);  		bgp_info_delete(rn, ri); @@ -4357,6 +4474,10 @@ static void bgp_static_withdraw_safi(struct bgp *bgp, struct prefix *p,  			ri->peer, NULL, p, prd, ri->attr, afi, safi, ri->type,  			1); /* Kill, since it is an administrative change */  #endif +		if (SAFI_MPLS_VPN == safi +		    && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { +			vpn_leak_to_vrf_withdraw(bgp, ri); +		}  		bgp_aggregate_decrement(bgp, p, ri, afi, safi);  		bgp_info_delete(rn, ri);  		bgp_process(bgp, rn, afi, safi); @@ -4485,6 +4606,11 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p,  			/* Process change. */  			bgp_aggregate_increment(bgp, p, ri, afi, safi);  			bgp_process(bgp, rn, afi, safi); + +			if (SAFI_MPLS_VPN == safi +			    && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { +				vpn_leak_to_vrf_update(bgp, ri); +			}  #if ENABLE_BGP_VNC  			rfapiProcessUpdate(ri->peer, NULL, p, &bgp_static->prd,  					   ri->attr, afi, safi, ri->type, @@ -4521,6 +4647,10 @@ static void bgp_static_update_safi(struct bgp *bgp, struct prefix *p,  	/* Process change. */  	bgp_process(bgp, rn, afi, safi); +	if (SAFI_MPLS_VPN == safi +	    && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) { +		vpn_leak_to_vrf_update(bgp, new); +	}  #if ENABLE_BGP_VNC  	rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd, new->attr, afi,  			   safi, new->type, new->sub_type, &label); @@ -5985,6 +6115,14 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,  				bgp_process(bgp, bn, afi, SAFI_UNICAST);  				bgp_unlock_node(bn);  				aspath_unintern(&attr.aspath); + +				if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +				    || (bgp->inst_type +					== BGP_INSTANCE_TYPE_DEFAULT)) { + +					vpn_leak_from_vrf_update( +						bgp_get_default(), bgp, bi); +				}  				return;  			}  		} @@ -5997,6 +6135,12 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,  		bgp_info_add(bn, new);  		bgp_unlock_node(bn);  		bgp_process(bgp, bn, afi, SAFI_UNICAST); + +		if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +		    || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +			vpn_leak_from_vrf_update(bgp_get_default(), bgp, new); +		}  	}  	/* Unintern original. */ @@ -6023,6 +6167,12 @@ void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, u_char type,  				break;  		if (ri) { +			if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +			    || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +				vpn_leak_from_vrf_withdraw(bgp_get_default(), +							   bgp, ri); +			}  			bgp_aggregate_decrement(bgp, p, ri, afi, SAFI_UNICAST);  			bgp_info_delete(rn, ri);  			bgp_process(bgp, rn, afi, SAFI_UNICAST); @@ -6048,6 +6198,12 @@ void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,  				break;  		if (ri) { +			if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +			    || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + +				vpn_leak_from_vrf_withdraw(bgp_get_default(), +							   bgp, ri); +			}  			bgp_aggregate_decrement(bgp, &rn->p, ri, afi,  						SAFI_UNICAST);  			bgp_info_delete(rn, ri); diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index dffe2b8ddc..748c4f9110 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -114,6 +114,30 @@ struct bgp_info_extra {  	/* For imported routes into a VNI (or VRF), this points to the parent.  	 */  	void *parent; + +	/* +	 * Some tunnelish parameters follow. Maybe consolidate into an +	 * internal tunnel structure? +	 */ + +	/* +	 * Original bgp instance for imported routes. Needed for: +	 * 1. Find all routes from a specific vrf for deletion +	 * 2. vrf context of original nexthop +	 * +	 * Store pointer to bgp instance rather than bgp->vrf_id because +	 * bgp->vrf_id is not always valid (or may change?). +	 * +	 * Set to NULL if route is not imported from another bgp instance. +	 */ +	struct bgp *bgp_orig; + +	/* +	 * Nexthop in context of original bgp instance. Needed +	 * for label resolution of core mpls routes exported to a vrf. +	 * Set nexthop_orig.family to 0 if not valid. +	 */ +	struct prefix nexthop_orig;  };  struct bgp_info { @@ -179,6 +203,7 @@ struct bgp_info {  #ifdef ENABLE_BGP_VNC  # define BGP_ROUTE_RFP          4   #endif +#define BGP_ROUTE_IMPORTED     5        /* from another bgp instance/safi */  	u_short instance; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 5371b37239..b79c6a7c3a 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -57,6 +57,7 @@  #include "bgpd/bgp_evpn.h"  #include "bgpd/bgp_evpn_private.h"  #include "bgpd/bgp_evpn_vty.h" +#include "bgpd/bgp_mplsvpn.h"  #if ENABLE_BGP_VNC  #include "bgpd/rfapi/bgp_rfapi_cfg.h" @@ -3096,13 +3097,17 @@ static int bgp_route_map_process_update_cb(char *rmap_name)  	struct listnode *node, *nnode;  	struct bgp *bgp; -	for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) +	for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {  		bgp_route_map_process_update(bgp, rmap_name, 1);  #if ENABLE_BGP_VNC -	zlog_debug("%s: calling vnc_routemap_update", __func__); -	vnc_routemap_update(bgp, __func__); +		/* zlog_debug("%s: calling vnc_routemap_update", __func__); */ +		vnc_routemap_update(bgp, __func__);  #endif +	} + +	vpn_policy_routemap_event(rmap_name); +  	return 0;  } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index c8e503d72a..4a5633c94d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6112,6 +6112,526 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as,  	     NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2  	     "Use addpath to advertise the bestpath per each neighboring AS\n") + +DEFUN_NOSH (vpn_policy_afi, +       vpn_policy_afi_cmd, +       "vpn-policy <ipv4|ipv6>", +       "Enter vpn-policy command mode\n" +       BGP_AFI_HELP_STR) +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF +	    && bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + +		vty_out(vty, +			"vpn-policy supported only in core or vrf instances.\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	afi_t afi; +	int idx = 0; + +	if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { +		if (afi == AFI_IP) +			vty->node = BGP_VPNPOLICY_IPV4_NODE; +		else +			vty->node = BGP_VPNPOLICY_IPV6_NODE; +		return CMD_SUCCESS; +	} +	return CMD_WARNING_CONFIG_FAILED; +} + +static int vpn_policy_afis(struct vty *vty, int *doafi) +{ +	switch (vty->node) { +	case BGP_VPNPOLICY_IPV4_NODE: +		doafi[AFI_IP] = 1; +		break; +	case BGP_VPNPOLICY_IPV6_NODE: +		doafi[AFI_IP6] = 1; +		break; +	default: +		vty_out(vty, +			"%% context error: valid only in vpn-policy block\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} +	return CMD_SUCCESS; +} + +static int argv_find_and_parse_vpn_policy_dirs(struct vty *vty, +					       struct cmd_token **argv, +					       int argc, int *idx, int *dodir) +{ +	if (argv_find(argv, argc, "fromvpn", idx)) { +		dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1; +	} else if (argv_find(argv, argc, "tovpn", idx)) { +		dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1; +	} else if (argv_find(argv, argc, "both", idx)) { +		dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1; +		dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1; +	} else { +		vty_out(vty, "%% direction parse error\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_rd, +       vpn_policy_rd_cmd, +       "rd ASN:NN_OR_IP-ADDRESS:NN", +       "Specify route distinguisher\n" +       "Route Distinguisher (<as-number>:<number> | <ip-address>:<number>)\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	struct prefix_rd prd; +	int ret; +	int doafi[AFI_MAX] = {0}; +	afi_t afi; + +	ret = str2prefix_rd(argv[1]->arg, &prd); +	if (!ret) { +		vty_out(vty, "%% Malformed rd\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		bgp->vpn_policy[afi].tovpn_rd = prd; +		SET_FLAG(bgp->vpn_policy[afi].flags, +			 BGP_VPN_POLICY_TOVPN_RD_SET); + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_rd, +       vpn_policy_no_rd_cmd, +       "no rd", +       NO_STR +       "Specify route distinguisher\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int ret; +	int doafi[AFI_MAX] = {0}; +	afi_t afi; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		UNSET_FLAG(bgp->vpn_policy[afi].flags, +			   BGP_VPN_POLICY_TOVPN_RD_SET); + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_label, +       vpn_policy_label_cmd, +       "label (0-1048575)", +       "label value for VRF\n" +       "Label Value <0-1048575>\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	mpls_label_t label; +	int doafi[AFI_MAX] = {0}; +	afi_t afi; +	int ret; + +	label = strtoul(argv[1]->arg, NULL, 10); + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		bgp->vpn_policy[afi].tovpn_label = label; + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_label, +       vpn_policy_no_label_cmd, +       "no label", +       "Negate a command or set its defaults\n" +       "label value for VRF\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int doafi[AFI_MAX] = {0}; +	afi_t afi; +	int ret; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +DEFPY (vpn_policy_nexthop, +       vpn_policy_nexthop_cmd, +       "nexthop <A.B.C.D|X:X::X:X>$nexthop", +       "Specify next hop to use for VRF advertised prefixes\n" +       "IPv4 prefix\n" +       "IPv6 prefix\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int doafi[AFI_MAX] = {0}; +	afi_t afi; +	int ret; +	struct prefix p; + +	if (!sockunion2hostprefix(nexthop, &p)) +		return CMD_WARNING_CONFIG_FAILED; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* +		 * pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		bgp->vpn_policy[afi].tovpn_nexthop = p; +		SET_FLAG(bgp->vpn_policy[afi].flags, +			 BGP_VPN_POLICY_TOVPN_NEXTHOP_SET); + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_nexthop, +       vpn_policy_no_nexthop_cmd, +       "no nexthop", +       NO_STR +       "Specify next hop to use for VRF advertised prefixes\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int doafi[AFI_MAX] = {0}; +	afi_t afi; +	int ret; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; + +		/* pre-change: un-export vpn routes (vpn->vrf routes unaffected) +		 */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); + +		UNSET_FLAG(bgp->vpn_policy[afi].flags, +			   BGP_VPN_POLICY_TOVPN_NEXTHOP_SET); + +		/* post-change: re-export vpn routes */ +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} + +	return CMD_SUCCESS; +} + +static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, +			 struct ecommunity **list) +{ +	struct ecommunity *ecom = NULL; +	struct ecommunity *ecomadd; + +	for (; argc; --argc, ++argv) { + +		ecomadd = ecommunity_str2com(argv[0]->arg, +					     ECOMMUNITY_ROUTE_TARGET, 0); +		if (!ecomadd) { +			vty_out(vty, "Malformed community-list value\n"); +			if (ecom) +				ecommunity_free(&ecom); +			return CMD_WARNING_CONFIG_FAILED; +		} + +		if (ecom) { +			ecommunity_merge(ecom, ecomadd); +			ecommunity_free(&ecomadd); +		} else { +			ecom = ecomadd; +		} +	} + +	if (*list) { +		ecommunity_free(&*list); +	} +	*list = ecom; + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_rt, +       vpn_policy_rt_cmd, +       "rt <fromvpn|tovpn|both> RTLIST...", +       "Specify route target list\n" +       "fromvpn: match any\n" +       "tovpn: set\n" +       "both fromvpn: match any and tovpn: set\n" +       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int ret; +	struct ecommunity *ecom = NULL; +	int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; +	int doafi[AFI_MAX] = {0}; +	vpn_policy_direction_t dir; +	afi_t afi; +	int idx = 0; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); +	if (ret != CMD_SUCCESS) +		return ret; + +	ret = set_ecom_list(vty, argc - 2, argv + 2, &ecom); +	if (ret != CMD_SUCCESS) { +		return ret; +	} + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; +		for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { +			if (!dodir[dir]) +				continue; + +			vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + +			if (bgp->vpn_policy[afi].rtlist[dir]) +				ecommunity_free( +					&bgp->vpn_policy[afi].rtlist[dir]); +			bgp->vpn_policy[afi].rtlist[dir] = ecommunity_dup(ecom); + +			vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); +		} +	} +	ecommunity_free(&ecom); + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_rt, +       vpn_policy_no_rt_cmd, +       "no rt <fromvpn|tovpn|both>", +       NO_STR +       "Specify route target list\n" +       "fromvpn: match any\n" +       "tovpn: set\n" +       "both fromvpn: match any and tovpn: set\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int ret; +	int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; +	int doafi[AFI_MAX] = {0}; +	vpn_policy_direction_t dir; +	afi_t afi; +	int idx = 0; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; +		for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { +			if (!dodir[dir]) +				continue; + +			vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + +			if (bgp->vpn_policy[afi].rtlist[dir]) +				ecommunity_free( +					&bgp->vpn_policy[afi].rtlist[dir]); +			bgp->vpn_policy[afi].rtlist[dir] = NULL; + +			vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); +		} +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_route_map, +       vpn_policy_route_map_cmd, +       "route-map <fromvpn|tovpn> WORD", +       "Specify route map\n" +       "fromvpn: core vpn -> this vrf\n" +       "tovpn: this vrf -> core vpn\n" +       "name of route-map\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int ret; +	int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; +	int doafi[AFI_MAX] = {0}; +	vpn_policy_direction_t dir; +	afi_t afi; +	int map_name_arg = 2; +	int idx = 0; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; +		for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { +			if (!dodir[dir]) +				continue; + +			vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + +			if (bgp->vpn_policy[afi].rmap_name[dir]) +				XFREE(MTYPE_ROUTE_MAP_NAME, +				      bgp->vpn_policy[afi].rmap_name[dir]); +			bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP( +				MTYPE_ROUTE_MAP_NAME, argv[map_name_arg]->arg); +			bgp->vpn_policy[afi].rmap[dir] = +				route_map_lookup_by_name( +					argv[map_name_arg]->arg); + +			vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); +		} +	} + +	return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_route_map, +       vpn_policy_no_route_map_cmd, +       "no route-map <fromvpn|tovpn>", +       NO_STR +       "Specify route map\n" +       "fromvpn: core vpn -> this vrf\n" +       "tovpn: this vrf -> core vpn\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int ret; +	int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; +	int doafi[AFI_MAX] = {0}; +	vpn_policy_direction_t dir; +	afi_t afi; +	int idx = 0; + +	ret = vpn_policy_afis(vty, doafi); +	if (ret != CMD_SUCCESS) +		return ret; + +	ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); +	if (ret != CMD_SUCCESS) +		return ret; + +	for (afi = 0; afi < AFI_MAX; ++afi) { +		if (!doafi[afi]) +			continue; +		for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { +			if (!dodir[dir]) +				continue; + +			vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + +			if (bgp->vpn_policy[afi].rmap_name[dir]) +				XFREE(MTYPE_ROUTE_MAP_NAME, +				      bgp->vpn_policy[afi].rmap_name[dir]); +			bgp->vpn_policy[afi].rmap_name[dir] = NULL; +			bgp->vpn_policy[afi].rmap[dir] = NULL; + +			vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); +		} +	} + +	return CMD_SUCCESS; +} +  DEFUN_NOSH (address_family_ipv4_safi,         address_family_ipv4_safi_cmd,         "address-family ipv4 [<unicast|multicast|vpn|labeled-unicast>]", @@ -11139,6 +11659,165 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi,  	}  } +/* This command is valid only in a bgp vrf instance or the default instance */ +DEFUN (bgp_export_vpn, +       bgp_export_vpn_cmd, +       "export vpn", +       "Export routes to another routing protocol\n" +       "to VPN RIB per vpn-policy") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int was_off = 0; +	afi_t afi; +	safi_t safi; + +	if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type +	    && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { +		vty_out(vty, +			"%% export vpn valid only for bgp vrf or default instance\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} +	afi = bgp_node_afi(vty); +	safi = bgp_node_safi(vty); +	if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) { +		vty_out(vty, +			"%% export vpn valid only for unicast ipv4|ipv6\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	if (!CHECK_FLAG(bgp->af_flags[afi][safi], +			BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { +		was_off = 1; +	} +	SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT); +	if (was_off) { +		/* trigger export current vrf */ +		zlog_debug("%s: calling postchange", __func__); +		vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				    bgp_get_default(), bgp); +	} +	return CMD_SUCCESS; +} + +DEFUN (bgp_no_export_vpn, +       bgp_no_export_vpn_cmd, +       "no export vpn", +       NO_STR +       "Export routes to another routing protocol\n" +       "to VPN RIB per vpn-policy") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	int was_on = 0; +	afi_t afi; +	safi_t safi; + +	if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type +	    && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { +		vty_out(vty, +			"%% export vpn valid only for bgp vrf or default instance\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} +	afi = bgp_node_afi(vty); +	safi = bgp_node_safi(vty); +	if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) { +		vty_out(vty, +			"%% export vpn valid only for unicast ipv4|ipv6\n"); +		return CMD_WARNING_CONFIG_FAILED; +	} + +	if (CHECK_FLAG(bgp->af_flags[afi][safi], +		       BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { +		was_on = 1; +	} +	if (was_on) { +		/* trigger un-export current vrf */ +		zlog_debug("%s: calling postchange", __func__); +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, +				   bgp_get_default(), bgp); +	} +	UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT); +	return CMD_SUCCESS; +} + +static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, +					    afi_t afi) +{ +	vty_frame(vty, " vpn-policy ipv%d\n", ((afi == AFI_IP) ? 4 : 6)); + +	if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { +		vty_out(vty, "    label %u\n", +			bgp->vpn_policy[afi].tovpn_label); +	} +	if (CHECK_FLAG(bgp->vpn_policy[afi].flags, +		       BGP_VPN_POLICY_TOVPN_RD_SET)) { +		char buf[RD_ADDRSTRLEN]; +		vty_out(vty, "    rd %s\n", +			prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, +				      sizeof(buf))); +	} +	if (CHECK_FLAG(bgp->vpn_policy[afi].flags, +		       BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + +		char buf[PREFIX_STRLEN]; +		if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family, +			      &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf, +			      sizeof(buf))) { + +			vty_out(vty, "    nexthop %s\n", buf); +		} +	} +	if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN] +	    && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] +	    && ecommunity_cmp( +		       bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], +		       bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) { + +		char *b = ecommunity_ecom2str( +			bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN], +			ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); +		vty_out(vty, "    rt both %s\n", b); +		XFREE(MTYPE_ECOMMUNITY_STR, b); +	} else { +		if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { +			char *b = ecommunity_ecom2str( +				bgp->vpn_policy[afi] +					.rtlist[BGP_VPN_POLICY_DIR_FROMVPN], +				ECOMMUNITY_FORMAT_ROUTE_MAP, +				ECOMMUNITY_ROUTE_TARGET); +			vty_out(vty, "    rt fromvpn %s\n", b); +			XFREE(MTYPE_ECOMMUNITY_STR, b); +		} +		if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { +			char *b = ecommunity_ecom2str( +				bgp->vpn_policy[afi] +					.rtlist[BGP_VPN_POLICY_DIR_TOVPN], +				ECOMMUNITY_FORMAT_ROUTE_MAP, +				ECOMMUNITY_ROUTE_TARGET); +			vty_out(vty, "    rt tovpn %s\n", b); +			XFREE(MTYPE_ECOMMUNITY_STR, b); +		} +	} +	if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) { +		vty_out(vty, "    route-map fromvpn %s\n", +			bgp->vpn_policy[afi] +				.rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); +	} +	if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) { +		vty_out(vty, "    route-map tovpn %s\n", +			bgp->vpn_policy[afi] +				.rmap_name[BGP_VPN_POLICY_DIR_TOVPN]); +	} + +	vty_endframe(vty, " exit\n"); +} + +void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp) +{ +	bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP); +	bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP6); +} + +  /* BGP node structure. */  static struct cmd_node bgp_node = {  	BGP_NODE, "%s(config-router)# ", 1, @@ -11180,6 +11859,12 @@ static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE,  static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE,  					    "%s(config-router-af-vni)# ", 1}; +static struct cmd_node bgp_vpn_policy_ipv4_node = { +	BGP_VPNPOLICY_IPV4_NODE, "%s(config-router-vpn-policy-ipv4)# ", 1}; + +static struct cmd_node bgp_vpn_policy_ipv6_node = { +	BGP_VPNPOLICY_IPV6_NODE, "%s(config-router-vpn-policy-ipv6)# ", 1}; +  static void community_list_vty(void);  static void bgp_ac_neighbor(vector comps, struct cmd_token *token) @@ -11240,6 +11925,8 @@ void bgp_vty_init(void)  	install_node(&bgp_vpnv6_node, NULL);  	install_node(&bgp_evpn_node, NULL);  	install_node(&bgp_evpn_vni_node, NULL); +	install_node(&bgp_vpn_policy_ipv4_node, NULL); +	install_node(&bgp_vpn_policy_ipv6_node, NULL);  	/* Install default VTY commands to new nodes.  */  	install_default(BGP_NODE); @@ -11253,6 +11940,8 @@ void bgp_vty_init(void)  	install_default(BGP_VPNV6_NODE);  	install_default(BGP_EVPN_NODE);  	install_default(BGP_EVPN_VNI_NODE); +	install_default(BGP_VPNPOLICY_IPV4_NODE); +	install_default(BGP_VPNPOLICY_IPV6_NODE);  	/* "bgp multiple-instance" commands. */  	install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); @@ -12312,6 +13001,12 @@ void bgp_vty_init(void)  	install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd);  	install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd); +	/* export vpn [route-map WORD] */ +	install_element(BGP_IPV4_NODE, &bgp_export_vpn_cmd); +	install_element(BGP_IPV6_NODE, &bgp_export_vpn_cmd); +	install_element(BGP_IPV4_NODE, &bgp_no_export_vpn_cmd); +	install_element(BGP_IPV6_NODE, &bgp_no_export_vpn_cmd); +  	/* ttl_security commands */  	install_element(BGP_NODE, &neighbor_ttl_security_cmd);  	install_element(BGP_NODE, &no_neighbor_ttl_security_cmd); @@ -12330,6 +13025,30 @@ void bgp_vty_init(void)  	/* Community-list. */  	community_list_vty(); + +	/* vpn-policy commands */ +	install_element(BGP_NODE, &vpn_policy_afi_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rd_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rd_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_label_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_label_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_nexthop_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_nexthop_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rt_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rt_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_route_map_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_route_map_cmd); + +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rd_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rd_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_label_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_label_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_nexthop_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_nexthop_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rt_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rt_cmd); +	install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_route_map_cmd); +	install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_route_map_cmd);  }  #include "memory.h" diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index cbb41f0840..459c4ffcc3 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -71,4 +71,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,  					       safi_t *safi, struct bgp **bgp);  extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,  				safi_t safi, u_char use_json); +extern void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp);  #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 23f626e960..22284fd28d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -54,6 +54,7 @@  #include "bgpd/rfapi/vnc_export_bgp.h"  #endif  #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_mplsvpn.h"  /* All information about zebra. */  struct zclient *zclient = NULL; @@ -987,6 +988,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  	struct bgp_info *mpinfo_cp = &local_info;  	route_tag_t tag;  	mpls_label_t label; +	int nh_othervrf = 0;  	/* Don't try to install if we're not connected to Zebra or Zebra doesn't  	 * know of this instance. @@ -997,6 +999,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  	if (bgp->main_zebra_update_hold)  		return; +	/* +	 * vrf leaking support (will have only one nexthop) +	 */ +	if (info->extra && info->extra->bgp_orig) +		nh_othervrf = 1; +  	/* Make Zebra API structure. */  	memset(&api, 0, sizeof(api));  	memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); @@ -1008,6 +1016,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  	peer = info->peer; +	if (info->type == ZEBRA_ROUTE_BGP +	    && info->sub_type == BGP_ROUTE_IMPORTED) { + +		struct bgp_info *bi; + +		/* +		 * Look at parent chain for peer sort +		 */ +		for (bi = info; bi->extra && bi->extra->parent; +		     bi = bi->extra->parent) { + +			peer = ((struct bgp_info *)(bi->extra->parent))->peer; +		} +	} +  	tag = info->attr->tag;  	/* When we create an aggregate route we must also install a Null0 route @@ -1060,12 +1083,38 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  		if (nh_family == AF_INET) {  			struct in_addr *nexthop; -			if (bgp->table_map[afi][safi].name) { +			if (bgp_debug_zebra(&api.prefix)) { +				char buf_prefix[PREFIX_STRLEN]; +				prefix2str(&api.prefix, buf_prefix, +					   sizeof(buf_prefix)); +				if (mpinfo->extra) { +					zlog_debug( +						"%s: p=%s, bgp_is_valid_label: %d", +						__func__, buf_prefix, +						bgp_is_valid_label( +							&mpinfo->extra +								 ->label[0])); +				} else { +					zlog_debug( +						"%s: p=%s, extra is NULL, no label", +						__func__, buf_prefix); +				} +			} + +			if (bgp->table_map[afi][safi].name || nh_othervrf) {  				/* Copy info and attributes, so the route-map  				   apply doesn't modify the BGP route info. */  				local_attr = *mpinfo->attr;  				mpinfo_cp->attr = &local_attr; +				if (nh_othervrf) { +					/* allow route-map to modify */ +					local_attr.nexthop = +						info->extra->nexthop_orig.u +							.prefix4; +				} +			} +			if (bgp->table_map[afi][safi].name) {  				if (!bgp_table_map_apply(  					    bgp->table_map[afi][safi].map, p,  					    mpinfo_cp)) @@ -1082,6 +1131,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  			nexthop = &mpinfo_cp->attr->nexthop;  			api_nh->gate.ipv4 = *nexthop; +			api_nh->vrf_id = nh_othervrf +						 ? info->extra->bgp_orig->vrf_id +						 : bgp->vrf_id;  			/* EVPN type-2 routes are  			   programmed as onlink on l3-vni SVI  			 */ @@ -1095,6 +1147,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  			ifindex = 0; +			if (bgp->table_map[afi][safi].name || nh_othervrf) { +				/* Copy info and attributes, so the route-map +				   apply doesn't modify the BGP route info. */ +				local_attr = *mpinfo->attr; +				mpinfo_cp->attr = &local_attr; +				if (nh_othervrf) { +					/* allow route-map to modify */ +					local_attr.mp_nexthop_global = +						info->extra->nexthop_orig.u +							.prefix6; +					local_attr.mp_nexthop_len = +						BGP_ATTR_NHLEN_IPV6_GLOBAL; +				} +			} +  			if (bgp->table_map[afi][safi].name) {  				/* Copy info and attributes, so the route-map  				   apply doesn't modify the BGP route info. */ @@ -1139,6 +1206,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,  			api_nh->gate.ipv6 = *nexthop;  			api_nh->ifindex = ifindex;  			api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; +			/* api_nh->vrf_id is not set for normal case? */ +			if (nh_othervrf) +				api_nh->vrf_id = info->extra->bgp_orig->vrf_id;  		}  		if (mpinfo->extra @@ -1229,9 +1299,12 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)  	for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))  		for (ri = rn->info; ri; ri = ri->next) -			if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) -			    && ri->type == ZEBRA_ROUTE_BGP -			    && ri->sub_type == BGP_ROUTE_NORMAL) +			if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) && + +			    (ri->type == ZEBRA_ROUTE_BGP +			     && (ri->sub_type == BGP_ROUTE_NORMAL +				 || ri->sub_type == BGP_ROUTE_IMPORTED))) +  				bgp_zebra_announce(rn, &rn->p, ri, bgp, afi,  						   safi);  } @@ -1244,6 +1317,21 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi)  	peer = info->peer;  	assert(peer); +	if (info->type == ZEBRA_ROUTE_BGP +	    && info->sub_type == BGP_ROUTE_IMPORTED) { + +		struct bgp_info *bi; + +		/* +		 * Look at parent chain for peer sort +		 */ +		for (bi = info; bi->extra && bi->extra->parent; +		     bi = bi->extra->parent) { + +			peer = ((struct bgp_info *)(bi->extra->parent))->peer; +		} +	} +  	/* Don't try to install if we're not connected to Zebra or Zebra doesn't  	 * know of this instance.  	 */ @@ -1363,7 +1451,27 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, u_short instance)  		}  #endif +		/* vpn -> vrf (happens within bgp but we hijack redist bits */ +		if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT +		     || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +		    && type == ZEBRA_ROUTE_BGP_VPN) { + +			/* leak update all */ +			vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, +					   bgp_get_default(), bgp); +		} +  		vrf_bitmap_set(zclient->redist[afi][type], bgp->vrf_id); + +		/* vpn -> vrf (happens within bgp but we hijack redist bits */ +		if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT +		     || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +		    && type == ZEBRA_ROUTE_BGP_VPN) { + +			/* leak update all */ +			vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, +					    bgp_get_default(), bgp); +		}  	}  	/* @@ -1484,11 +1592,6 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,  		vrf_bitmap_unset(zclient->redist[afi][type], bgp->vrf_id);  	} -#if ENABLE_BGP_VNC -	if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) { -		vnc_export_bgp_disable(bgp, afi); -	} -#endif  	if (bgp_install_info_to_zebra(bgp)) {  		/* Send distribute delete message to zebra. */ @@ -1512,6 +1615,26 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type,  {  	struct bgp_redist *red; +/* + * vnc and vpn->vrf checks must be before red check because + * they operate within bgpd irrespective of zebra connection + * status. red lookup fails if there is no zebra connection. + */ +#if ENABLE_BGP_VNC +	if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) { +		vnc_export_bgp_disable(bgp, afi); +	} +#endif +	/* vpn -> vrf (happend within bgp but we hijack redist bits */ +	if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT +	     || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) +	    && type == ZEBRA_ROUTE_BGP_VPN) { + +		/* leak withdraw all */ +		vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, +				   bgp_get_default(), bgp); +	} +  	red = bgp_redist_lookup(bgp, afi, type, instance);  	if (!red)  		return CMD_SUCCESS; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index fde72da4c8..e7d58a021b 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2941,6 +2941,11 @@ static struct bgp *bgp_create(as_t *as, const char *name,  	}  #endif /* ENABLE_BGP_VNC */ +	for (afi = AFI_IP; afi < AFI_MAX; afi++) { +		bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; +		bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = +			MPLS_LABEL_NONE; +	}  	if (name) {  		bgp->name = XSTRDUP(MTYPE_BGP, name);  	} else { @@ -7127,6 +7132,12 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,  	if (safi == SAFI_EVPN)  		bgp_config_write_evpn_info(vty, bgp, afi, safi); +	if (CHECK_FLAG(bgp->af_flags[afi][safi], +		       BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + +		vty_out(vty, "  export vpn\n"); +	} +  	vty_endframe(vty, " exit-address-family\n");  } @@ -7393,6 +7404,8 @@ int bgp_config_write(struct vty *vty)  		if (bgp_option_check(BGP_OPT_CONFIG_CISCO))  			vty_out(vty, " no auto-summary\n"); +		bgp_vpn_policy_config_write(vty, bgp); +  		/* IPv4 unicast configuration.  */  		bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index e265da803f..41ae8e916f 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -159,6 +159,12 @@ struct bgp_redist {  	struct bgp_rmap rmap;  }; +typedef enum { +	BGP_VPN_POLICY_DIR_FROMVPN = 0, +	BGP_VPN_POLICY_DIR_TOVPN = 1, +	BGP_VPN_POLICY_DIR_MAX = 2 +} vpn_policy_direction_t; +  /*   * Type of 'struct bgp'.   * - Default: The default instance @@ -311,6 +317,7 @@ struct bgp {  	/* BGP Per AF flags */  	u_int16_t af_flags[AFI_MAX][SAFI_MAX];  #define BGP_CONFIG_DAMPENING              (1 << 0) +#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT  (1 << 1)  	/* Route table for next-hop lookup cache. */  	struct bgp_table *nexthop_cache_table[AFI_MAX]; @@ -448,6 +455,22 @@ struct bgp {  	/* route map for advertise ipv4/ipv6 unicast (type-5 routes) */  	struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX]; +	/* vpn-policy */ +	struct { +		struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX]; +		char *rmap_name[BGP_VPN_POLICY_DIR_MAX]; +		struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX]; + +		/* should be mpls_label_t? */ +		uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */ +		uint32_t tovpn_zebra_vrf_label_last_sent; +		struct prefix_rd tovpn_rd; +		struct prefix tovpn_nexthop; /* unset => set to router id */ +		uint32_t flags; +#define BGP_VPN_POLICY_TOVPN_RD_SET            0x00000004 +#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET       0x00000008 +	} vpn_policy[AFI_MAX]; +  	QOBJ_FIELDS  };  DECLARE_QOBJ_TYPE(bgp) diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c index 4c7c392ab8..8c4d5ab043 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -2182,6 +2182,7 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused)  	vnc_zlog_debug_verbose("%s done", __func__);  } +#if 0 /* superseded */  static void vnc_routemap_event(route_map_event_t type, /* ignored */  			       const char *rmap_name)  /* ignored */  { @@ -2197,6 +2198,7 @@ static void vnc_routemap_event(route_map_event_t type, /* ignored */  	vnc_zlog_debug_verbose("%s: done", __func__);  } +#endif  /*-------------------------------------------------------------------------   *			nve-group @@ -3673,7 +3675,8 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import,  void bgp_rfapi_cfg_init(void)  {  	/* main bgpd code does not use this hook, but vnc does */ -	route_map_event_hook(vnc_routemap_event); +	/* superseded by bgp_route_map_process_update_cb() */ +	/* bgp_route_map_event_hook_add(vnc_routemap_event); */  	install_node(&bgp_vnc_defaults_node, NULL);  	install_node(&bgp_vnc_nve_group_node, NULL);  | 
