From ddb5b4880ba8b6ed79da25f8fddf81f67f376cdd Mon Sep 17 00:00:00 2001 From: "G. Paul Ziemba" Date: Fri, 9 Mar 2018 15:52:55 -0500 Subject: [PATCH] bgpd: vpn-vrf route leaking - add "debug bgp vpn label" CLI - improved debug messages for "debug bgp bestpath" - send vrf label to zebra after zebra informs bgpd of vrf_id - withdraw vrf_label from zebra if zebra informs bgpd that vrf_id is disabled Signed-off-by: G. Paul Ziemba --- bgpd/bgp_debug.c | 139 ++++++ bgpd/bgp_debug.h | 6 + bgpd/bgp_main.c | 6 + bgpd/bgp_mplsvpn.c | 986 ++++++++++++++++++++++++++++++++++++- bgpd/bgp_mplsvpn.h | 119 +++++ bgpd/bgp_route.c | 182 ++++++- bgpd/bgp_route.h | 25 + bgpd/bgp_routemap.c | 11 +- bgpd/bgp_vty.c | 719 +++++++++++++++++++++++++++ bgpd/bgp_vty.h | 1 + bgpd/bgp_zebra.c | 141 +++++- bgpd/bgpd.c | 13 + bgpd/bgpd.h | 23 + 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 ", + 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 ", + 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 ", + "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 (: | :)\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 $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 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 ", + 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 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 ", + 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 []", @@ -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); -- 2.39.5