diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 2580d2e5a55a..f8672669569d 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; @@ -1560,6 +1562,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", @@ -1592,6 +1684,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; @@ -1651,6 +1747,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; } @@ -1695,6 +1803,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; } @@ -1771,6 +1888,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; } @@ -1864,6 +1998,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 10a1e3159ffa..fe7ca8c46a85 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 703978601438..bf60f9c11826 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 ec4989de8fc5..d87f78a7830d 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 5c11f7526cea..d0ad8ac8462b 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 fd0c87c8ca7f..032b33229cde 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1379,6 +1379,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) @@ -1867,17 +1877,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; } @@ -1981,6 +2004,13 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp, onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer : NULL); + 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); + } + /* First update is deferred until ORF or ROUTE-REFRESH is received */ if (onlypeer && CHECK_FLAG(onlypeer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) @@ -2072,6 +2102,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) { @@ -2089,6 +2121,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); @@ -2129,6 +2168,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. */ @@ -2143,11 +2190,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); @@ -2194,6 +2246,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); @@ -2229,13 +2283,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); } } @@ -3137,6 +3195,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); @@ -3253,6 +3323,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); @@ -3291,6 +3371,18 @@ int bgp_update(struct peer *peer, struct prefix *p, u_int32_t addpath_id, 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); } @@ -3377,9 +3469,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)); @@ -3863,7 +3965,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); @@ -4264,6 +4368,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; @@ -4311,6 +4424,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); } @@ -4331,6 +4450,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); @@ -4366,6 +4490,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); @@ -4494,6 +4622,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, @@ -4530,6 +4663,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); @@ -5994,6 +6131,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; } } @@ -6006,6 +6151,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. */ @@ -6032,6 +6183,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); @@ -6057,6 +6214,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 dffe2b8ddcfe..748c4f911092 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 b6910a222da0..5a265b6c9c43 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" @@ -3114,13 +3115,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_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c index 705cb152f0c1..e66e5a540b1e 100644 --- a/bgpd/bgp_updgrp_adv.c +++ b/bgpd/bgp_updgrp_adv.c @@ -113,6 +113,14 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg) peer = UPDGRP_PEER(updgrp); addpath_capable = bgp_addpath_encode_tx(peer, afi, safi); + if (BGP_DEBUG(update, UPDATE_OUT)) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(&ctx->rn->p, buf_prefix, sizeof(buf_prefix)); + zlog_debug("%s: afi=%s, safi=%s, p=%s", __func__, afi2str(afi), + safi2str(safi), buf_prefix); + } + + UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) { /* diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index c8e503d72aa4..4a5633c94d07 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 cbb41f0840a5..459c4ffcc3a2 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 23f626e960d2..22284fd28d9b 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 eb68aee9fb0b..2eae2e5e9e33 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 9e1d279091e8..664f8c9da4a8 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) /* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */ #define BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST (1 << 1) @@ -454,6 +461,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 4c7c392ab89d..8c4d5ab04363 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); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index c4d66bbc655c..ae31c3fe9e6b 100644 --- a/bgpd/rfapi/vnc_export_bgp.c +++ b/bgpd/rfapi/vnc_export_bgp.c @@ -2049,6 +2049,9 @@ void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi) */ void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi) { + if (!bgp->rfapi_cfg) + return; + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: break; @@ -2069,6 +2072,9 @@ void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi) void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi) { + if (!bgp->rfapi_cfg) + return; + switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) { case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE: break; diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 198cf35e686a..48fcf449c761 100644 --- a/isisd/isis_redist.c +++ b/isisd/isis_redist.c @@ -254,8 +254,8 @@ void isis_redist_add(int type, struct prefix *p, u_char distance, char debug_buf[BUFSIZ]; prefix2str(p, debug_buf, sizeof(debug_buf)); - zlog_debug("%s: New route %s from %s.", __func__, debug_buf, - zebra_route_string(type)); + zlog_debug("%s: New route %s from %s: distance %d.", __func__, + debug_buf, zebra_route_string(type), distance); if (!ei_table) { zlog_warn("%s: External information table not initialized.", diff --git a/lib/command.c b/lib/command.c index 5697c1d812b0..b289cdd7a3cb 100644 --- a/lib/command.c +++ b/lib/command.c @@ -87,6 +87,8 @@ const char *node_names[] = { "bgp vnc l2", // BGP_VNC_L2_GROUP_NODE, "rfp defaults", // RFP_DEFAULTS_NODE, "bgp evpn", // BGP_EVPN_NODE, + "bgp vpn policy ipv4", // BGP_VPNPOLICY_IPV4_NODE + "bgp vpn policy ipv6", // BGP_VPNPOLICY_IPV6_NODE "ospf", // OSPF_NODE, "ospf6", // OSPF6_NODE, "ldp", // LDP_NODE, @@ -949,6 +951,8 @@ enum node_type node_parent(enum node_type node) case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: case BGP_VRF_POLICY_NODE: + case BGP_VPNPOLICY_IPV4_NODE: + case BGP_VPNPOLICY_IPV6_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1319,6 +1323,8 @@ void cmd_exit(struct vty *vty) case BGP_VPNV4_NODE: case BGP_VPNV6_NODE: case BGP_VRF_POLICY_NODE: + case BGP_VPNPOLICY_IPV4_NODE: + case BGP_VPNPOLICY_IPV6_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: @@ -1389,6 +1395,8 @@ DEFUN (config_end, case BABEL_NODE: case BGP_NODE: case BGP_VRF_POLICY_NODE: + case BGP_VPNPOLICY_IPV4_NODE: + case BGP_VPNPOLICY_IPV6_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: case BGP_VNC_L2_GROUP_NODE: diff --git a/lib/command.h b/lib/command.h index 0febf903a300..1e700aaa8f8c 100644 --- a/lib/command.h +++ b/lib/command.h @@ -73,73 +73,75 @@ struct host { /* List of CLI nodes. Please remember to update the name array in command.c. */ enum node_type { - AUTH_NODE, /* Authentication mode of vty interface. */ - VIEW_NODE, /* View node. Default mode of vty interface. */ - AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ - ENABLE_NODE, /* Enable node. */ - CONFIG_NODE, /* Config node. Default mode of config file. */ - SERVICE_NODE, /* Service node. */ - DEBUG_NODE, /* Debug node. */ - VRF_DEBUG_NODE, /* Vrf Debug node. */ - DEBUG_VNC_NODE, /* Debug VNC node. */ - AAA_NODE, /* AAA node. */ - KEYCHAIN_NODE, /* Key-chain node. */ - KEYCHAIN_KEY_NODE, /* Key-chain key node. */ - LOGICALROUTER_NODE, /* Logical-Router node. */ - VRF_NODE, /* VRF mode node. */ - INTERFACE_NODE, /* Interface mode node. */ - NH_GROUP_NODE, /* Nexthop-Group mode node. */ - ZEBRA_NODE, /* zebra connection node. */ - TABLE_NODE, /* rtm_table selection node. */ - RIP_NODE, /* RIP protocol mode node. */ - RIPNG_NODE, /* RIPng protocol mode node. */ - BABEL_NODE, /* BABEL protocol mode node. */ - EIGRP_NODE, /* EIGRP protocol mode node. */ - BGP_NODE, /* BGP protocol mode which includes BGP4+ */ - BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ - BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ - BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ - BGP_IPV4L_NODE, /* BGP IPv4 labeled unicast address family. */ - BGP_IPV6_NODE, /* BGP IPv6 address family */ - BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ - BGP_IPV6L_NODE, /* BGP IPv6 labeled unicast address family. */ - BGP_VRF_POLICY_NODE, /* BGP VRF policy */ - BGP_VNC_DEFAULTS_NODE, /* BGP VNC nve defaults */ - BGP_VNC_NVE_GROUP_NODE, /* BGP VNC nve group */ - BGP_VNC_L2_GROUP_NODE, /* BGP VNC L2 group */ - RFP_DEFAULTS_NODE, /* RFP defaults node */ - BGP_EVPN_NODE, /* BGP EVPN node. */ - OSPF_NODE, /* OSPF protocol mode */ - OSPF6_NODE, /* OSPF protocol for IPv6 mode */ - LDP_NODE, /* LDP protocol mode */ - LDP_IPV4_NODE, /* LDP IPv4 address family */ - LDP_IPV6_NODE, /* LDP IPv6 address family */ - LDP_IPV4_IFACE_NODE, /* LDP IPv4 Interface */ - LDP_IPV6_IFACE_NODE, /* LDP IPv6 Interface */ - LDP_L2VPN_NODE, /* LDP L2VPN node */ - LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ - ISIS_NODE, /* ISIS protocol mode */ - MASC_NODE, /* MASC for multicast. */ - IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ - IP_NODE, /* Static ip route node. */ - ACCESS_NODE, /* Access list node. */ - PREFIX_NODE, /* Prefix list node. */ - ACCESS_IPV6_NODE, /* Access list node. */ - ACCESS_MAC_NODE, /* MAC access list node*/ - PREFIX_IPV6_NODE, /* Prefix list node. */ - AS_LIST_NODE, /* AS list node. */ - COMMUNITY_LIST_NODE, /* Community list node. */ - RMAP_NODE, /* Route map node. */ - SMUX_NODE, /* SNMP configuration node. */ - DUMP_NODE, /* Packet dump node. */ - FORWARDING_NODE, /* IP forwarding node. */ - PROTOCOL_NODE, /* protocol filtering node */ - MPLS_NODE, /* MPLS config node */ - PW_NODE, /* Pseudowire config node */ - VTY_NODE, /* Vty node. */ - LINK_PARAMS_NODE, /* Link-parameters node */ - BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */ + AUTH_NODE, /* Authentication mode of vty interface. */ + VIEW_NODE, /* View node. Default mode of vty interface. */ + AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ + ENABLE_NODE, /* Enable node. */ + CONFIG_NODE, /* Config node. Default mode of config file. */ + SERVICE_NODE, /* Service node. */ + DEBUG_NODE, /* Debug node. */ + VRF_DEBUG_NODE, /* Vrf Debug node. */ + DEBUG_VNC_NODE, /* Debug VNC node. */ + AAA_NODE, /* AAA node. */ + KEYCHAIN_NODE, /* Key-chain node. */ + KEYCHAIN_KEY_NODE, /* Key-chain key node. */ + LOGICALROUTER_NODE, /* Logical-Router node. */ + VRF_NODE, /* VRF mode node. */ + INTERFACE_NODE, /* Interface mode node. */ + NH_GROUP_NODE, /* Nexthop-Group mode node. */ + ZEBRA_NODE, /* zebra connection node. */ + TABLE_NODE, /* rtm_table selection node. */ + RIP_NODE, /* RIP protocol mode node. */ + RIPNG_NODE, /* RIPng protocol mode node. */ + BABEL_NODE, /* BABEL protocol mode node. */ + EIGRP_NODE, /* EIGRP protocol mode node. */ + BGP_NODE, /* BGP protocol mode which includes BGP4+ */ + BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ + BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ + BGP_IPV4L_NODE, /* BGP IPv4 labeled unicast address family. */ + BGP_IPV6_NODE, /* BGP IPv6 address family */ + BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ + BGP_IPV6L_NODE, /* BGP IPv6 labeled unicast address family. */ + BGP_VRF_POLICY_NODE, /* BGP VRF policy */ + BGP_VNC_DEFAULTS_NODE, /* BGP VNC nve defaults */ + BGP_VNC_NVE_GROUP_NODE, /* BGP VNC nve group */ + BGP_VNC_L2_GROUP_NODE, /* BGP VNC L2 group */ + RFP_DEFAULTS_NODE, /* RFP defaults node */ + BGP_EVPN_NODE, /* BGP EVPN node. */ + BGP_VPNPOLICY_IPV4_NODE, /* BGP VPN IPv6 policy */ + BGP_VPNPOLICY_IPV6_NODE, /* BGP VPN IPv6 policy */ + OSPF_NODE, /* OSPF protocol mode */ + OSPF6_NODE, /* OSPF protocol for IPv6 mode */ + LDP_NODE, /* LDP protocol mode */ + LDP_IPV4_NODE, /* LDP IPv4 address family */ + LDP_IPV6_NODE, /* LDP IPv6 address family */ + LDP_IPV4_IFACE_NODE, /* LDP IPv4 Interface */ + LDP_IPV6_IFACE_NODE, /* LDP IPv6 Interface */ + LDP_L2VPN_NODE, /* LDP L2VPN node */ + LDP_PSEUDOWIRE_NODE, /* LDP Pseudowire node */ + ISIS_NODE, /* ISIS protocol mode */ + MASC_NODE, /* MASC for multicast. */ + IRDP_NODE, /* ICMP Router Discovery Protocol mode. */ + IP_NODE, /* Static ip route node. */ + ACCESS_NODE, /* Access list node. */ + PREFIX_NODE, /* Prefix list node. */ + ACCESS_IPV6_NODE, /* Access list node. */ + ACCESS_MAC_NODE, /* MAC access list node*/ + PREFIX_IPV6_NODE, /* Prefix list node. */ + AS_LIST_NODE, /* AS list node. */ + COMMUNITY_LIST_NODE, /* Community list node. */ + RMAP_NODE, /* Route map node. */ + SMUX_NODE, /* SNMP configuration node. */ + DUMP_NODE, /* Packet dump node. */ + FORWARDING_NODE, /* IP forwarding node. */ + PROTOCOL_NODE, /* protocol filtering node */ + MPLS_NODE, /* MPLS config node */ + PW_NODE, /* Pseudowire config node */ + VTY_NODE, /* Vty node. */ + LINK_PARAMS_NODE, /* Link-parameters node */ + BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */ RPKI_NODE, /* RPKI node for configuration of RPKI cache server connections.*/ NODE_TYPE_MAX, /* maximum */ diff --git a/lib/log.c b/lib/log.c index 74e7be7c7d76..a8e7462baac1 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1055,6 +1055,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BABEL; else if (strmatch(s, "sharp")) return ZEBRA_ROUTE_SHARP; + else if (strmatch(s, "vpn")) + return ZEBRA_ROUTE_BGP_VPN; } if (afi == AFI_IP6) { if (strmatch(s, "kernel")) @@ -1083,6 +1085,8 @@ int proto_redistnum(int afi, const char *s) return ZEBRA_ROUTE_BABEL; else if (strmatch(s, "sharp")) return ZEBRA_ROUTE_SHARP; + else if (strmatch(s, "vpn")) + return ZEBRA_ROUTE_BGP_VPN; } return -1; } diff --git a/lib/route_types.txt b/lib/route_types.txt index 4e764a14c139..98cada8f8988 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -40,7 +40,7 @@ # Long description: Full description, but should try fit on a line. #### # -# If you add a new routing protocol here, make sure you go update +# If you add a new routing protocol here, make sure you also update # meta_queue_map in zebra_rib.c # ## type cname daemon C 4 6 short help @@ -76,6 +76,7 @@ ZEBRA_ROUTE_VNC_DIRECT_RH, vnc-rn, NULL, 'V', 0, 0, "VNC-RN" ZEBRA_ROUTE_BGP_DIRECT, bgp-direct, NULL, 'b', 0, 0, "BGP-Direct" # bgp unicast -> vnc ZEBRA_ROUTE_BGP_DIRECT_EXT, bgp-direct-to-nve-groups, NULL, 'e', 0, 0, "BGP2VNC" +ZEBRA_ROUTE_BGP_VPN, vpn, NULL, 'c', 1, 1, "VPN", bgpd ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" ZEBRA_ROUTE_SHARP, sharp, sharpd, 'D', 1, 1, "SHARP" ZEBRA_ROUTE_ALL, wildcard, none, '-', 0, 0, "-" @@ -101,5 +102,6 @@ ZEBRA_ROUTE_OLSR, "Optimised Link State Routing (OLSR)" ZEBRA_ROUTE_TABLE, "Non-main Kernel Routing Table" ZEBRA_ROUTE_LDP, "Label Distribution Protocol (LDP)" ZEBRA_ROUTE_VNC_DIRECT, "VNC direct (not via zebra) routes" +ZEBRA_ROUTE_BGP_VPN, "BGP VPN routes" ZEBRA_ROUTE_BABEL, "Babel routing protocol (Babel)" ZEBRA_ROUTE_SHARP, "Super Happy Advanced Routing Protocol (sharpd)" diff --git a/lib/zclient.c b/lib/zclient.c index c720e2519bd2..777f6fcf9bf3 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -439,7 +439,7 @@ void zclient_send_reg_requests(struct zclient *zclient, vrf_id_t vrf_id) } } - /* Flush all redistribute request. */ + /* Resend all redistribute request. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) if (i != zclient->redist_default diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 3b257fec96f5..e322f605097c 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -949,6 +949,14 @@ static int ospf_zebra_read_route(int command, struct zclient *zclient, if (IPV4_NET127(ntohl(p.prefix.s_addr))) return 0; + if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + + zlog_debug("%s: from client %s: vrf_id %d, p %s", __func__, + zebra_route_string(api.type), vrf_id, buf_prefix); + } + if (command == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { /* XXX|HACK|TODO|FIXME: * Maybe we should ignore reject/blackhole routes? Testing @@ -1455,8 +1463,6 @@ void ospf_zebra_vrf_register(struct ospf *ospf) __PRETTY_FUNCTION__, ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); - /* Deregister for router-id, interfaces, - * redistributed routes. */ zclient_send_reg_requests(zclient, ospf->vrf_id); } } diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index c8a4dc12cdc6..26bbc5ee11cd 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -2049,6 +2049,23 @@ static int ospf_vrf_delete(struct vrf *vrf) return 0; } +static void ospf_set_redist_vrf_bitmaps(struct ospf *ospf) +{ + int type; + struct list *red_list; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) { + red_list = ospf->redist[type]; + if (!red_list) + continue; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug( + "%s: setting redist vrf %d bitmap for type %d", + __func__, ospf->vrf_id, type); + vrf_bitmap_set(zclient->redist[AFI_IP][type], ospf->vrf_id); + } +} + /* Enable OSPF VRF instance */ static int ospf_vrf_enable(struct vrf *vrf) { @@ -2077,6 +2094,15 @@ static int ospf_vrf_enable(struct vrf *vrf) "ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } + + /* stop zebra redist to us for old vrf */ + zclient_send_dereg_requests(zclient, old_vrf_id); + + ospf_set_redist_vrf_bitmaps(ospf); + + /* start zebra redist to us for new vrf */ + ospf_zebra_vrf_register(ospf); + ret = ospf_sock_init(ospf); if (ospfd_privs.change(ZPRIVS_LOWER)) { zlog_err( @@ -2088,7 +2114,6 @@ static int ospf_vrf_enable(struct vrf *vrf) thread_add_read(master, ospf_read, ospf, ospf->fd, &ospf->t_read); ospf->oi_running = 1; - ospf_zebra_vrf_register(ospf); ospf_router_id_update(ospf); } } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index efef106d978a..556ce2722918 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -360,7 +360,9 @@ static int vtysh_execute_func(const char *line, int pager) } else if ((saved_node == KEYCHAIN_KEY_NODE || saved_node == LDP_PSEUDOWIRE_NODE || saved_node == LDP_IPV4_IFACE_NODE - || saved_node == LDP_IPV6_IFACE_NODE) + || saved_node == LDP_IPV6_IFACE_NODE + || saved_node == BGP_VPNPOLICY_IPV4_NODE + || saved_node == BGP_VPNPOLICY_IPV6_NODE) && (tried == 1)) { vtysh_execute("exit"); } else if (tried) { @@ -632,7 +634,9 @@ int vtysh_mark_file(const char *filename) } else if ((prev_node == BGP_EVPN_VNI_NODE) && (tried == 1)) { fprintf(outputfile, "exit-vni\n"); - } else if ((prev_node == KEYCHAIN_KEY_NODE) + } else if ((prev_node == KEYCHAIN_KEY_NODE + || prev_node == BGP_VPNPOLICY_IPV4_NODE + || prev_node == BGP_VPNPOLICY_IPV6_NODE) && (tried == 1)) { fprintf(outputfile, "exit\n"); } else if (tried) { @@ -1013,6 +1017,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)# "}; +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 struct cmd_node bgp_ipv6l_node = {BGP_IPV6L_NODE, "%s(config-router-af)# "}; @@ -1264,6 +1274,20 @@ DEFUNSH(VTYSH_BGPD, bgp_evpn_vni, bgp_evpn_vni_cmd, "vni (1-16777215)", return CMD_SUCCESS; } +DEFUNSH(VTYSH_BGPD, vpn_policy_afi, vpn_policy_afi_cmd, "vpn-policy ", + "Configure a VPN policy\n" + BGP_AFI_HELP_STR) +{ + int idx = 1; + + if (argv_find(argv, argc, "ipv4", &idx)) + vty->node = BGP_VPNPOLICY_IPV4_NODE; + else + vty->node = BGP_VPNPOLICY_IPV6_NODE; + return CMD_SUCCESS; +} + + #if defined(ENABLE_BGP_VNC) DEFUNSH(VTYSH_BGPD, vnc_defaults, vnc_defaults_cmd, "vnc defaults", "VNC/RFP related configuration\n" @@ -1538,6 +1562,8 @@ static int vtysh_exit(struct vty *vty) case BGP_IPV6M_NODE: case BGP_IPV6L_NODE: case BGP_VRF_POLICY_NODE: + case BGP_VPNPOLICY_IPV4_NODE: + case BGP_VPNPOLICY_IPV6_NODE: case BGP_EVPN_NODE: case BGP_VNC_DEFAULTS_NODE: case BGP_VNC_NVE_GROUP_NODE: @@ -3086,6 +3112,8 @@ void vtysh_init_vty(void) install_node(&bgp_vrf_policy_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_node(&bgp_vnc_defaults_node, NULL); install_node(&bgp_vnc_nve_group_node, NULL); install_node(&bgp_vnc_l2_group_node, NULL); @@ -3178,6 +3206,10 @@ void vtysh_init_vty(void) install_element(BGP_EVPN_VNI_NODE, &vtysh_quit_bgpd_cmd); install_element(BGP_IPV6L_NODE, &vtysh_exit_bgpd_cmd); install_element(BGP_IPV6L_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vtysh_quit_bgpd_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vtysh_exit_bgpd_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vtysh_quit_bgpd_cmd); #if defined(ENABLE_BGP_VNC) install_element(BGP_VRF_POLICY_NODE, &vtysh_exit_bgpd_cmd); install_element(BGP_VRF_POLICY_NODE, &vtysh_quit_bgpd_cmd); @@ -3230,6 +3262,8 @@ void vtysh_init_vty(void) install_element(BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd); install_element(BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd); install_element(BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vtysh_end_all_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vtysh_end_all_cmd); install_element(ISIS_NODE, &vtysh_end_all_cmd); install_element(KEYCHAIN_NODE, &vtysh_end_all_cmd); install_element(KEYCHAIN_KEY_NODE, &vtysh_end_all_cmd); @@ -3279,6 +3313,7 @@ void vtysh_init_vty(void) install_element(CONFIG_NODE, &router_bgp_cmd); install_element(BGP_NODE, &address_family_vpnv4_cmd); install_element(BGP_NODE, &address_family_vpnv6_cmd); + install_element(BGP_NODE, &vpn_policy_afi_cmd); #if defined(ENABLE_BGP_VNC) install_element(BGP_NODE, &vnc_vrf_policy_cmd); install_element(BGP_NODE, &vnc_defaults_cmd); diff --git a/zebra/redistribute.c b/zebra/redistribute.c index 5a239306fbfa..3a66aea45f89 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -115,11 +115,12 @@ static void zebra_redistribute(struct zserv *client, int type, u_short instance, if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( - "%s: checking: selected=%d, type=%d, distance=%d, " + "%s: client %s vrf %d checking: selected=%d, type=%d, distance=%d, " "zebra_check_addr=%d", __func__, - CHECK_FLAG(newre->flags, - ZEBRA_FLAG_SELECTED), + zebra_route_string(client->proto), + vrf_id, CHECK_FLAG(newre->flags, + ZEBRA_FLAG_SELECTED), newre->type, newre->distance, zebra_check_addr(dst_p)); @@ -253,6 +254,12 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) STREAM_GETC(msg, type); STREAM_GETW(msg, instance); + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug( + "%s: client proto %s afi=%d, wants %s, vrf %d, instance=%d", + __func__, zebra_route_string(client->proto), afi, + zebra_route_string(type), zvrf_id(zvrf), instance); + if (afi == 0 || afi > AFI_MAX) { zlog_warn("%s: Specified afi %d does not exist", __PRETTY_FUNCTION__, afi); @@ -276,6 +283,9 @@ void zebra_redistribute_add(ZAPI_HANDLER_ARGS) } else { if (!vrf_bitmap_check(client->redist[afi][type], zvrf_id(zvrf))) { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: setting vrf %d redist bitmap", + __func__, zvrf_id(zvrf)); vrf_bitmap_set(client->redist[afi][type], zvrf_id(zvrf)); zebra_redistribute(client, type, 0, zvrf_id(zvrf), afi); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 72dbfb12fc2e..b979037700f1 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1864,6 +1864,7 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_VNC_DIRECT_RH] = 3, [ZEBRA_ROUTE_BGP_DIRECT] = 3, [ZEBRA_ROUTE_BGP_DIRECT_EXT] = 3, + [ZEBRA_ROUTE_BGP_VPN] = 3, [ZEBRA_ROUTE_BABEL] = 2, [ZEBRA_ROUTE_ALL] = 4, // Shouldn't happen but for safety }; diff --git a/zebra/zserv.c b/zebra/zserv.c index 0def90380312..2dc9e290d384 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -597,6 +597,17 @@ int zsend_redistribute_route(int cmd, struct zserv *client, struct prefix *p, /* Encode route and send. */ if (zapi_route_encode(cmd, s, &api) < 0) return -1; + + if (IS_ZEBRA_DEBUG_SEND) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + + zlog_debug("%s: %s to client %s: type %s, vrf_id %d, p %s", + __func__, zserv_command_string(cmd), + zebra_route_string(client->proto), + zebra_route_string(api.type), api.vrf_id, + buf_prefix); + } return zebra_server_send_message(client, s); } @@ -1149,6 +1160,16 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) s = msg; zapi_route_decode(s, &api); + if (IS_ZEBRA_DEBUG_RECV) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); + zlog_debug("%s: p=%s, ZAPI_MESSAGE_LABEL: %sset, flags=0x%x", + __func__, buf_prefix, + (CHECK_FLAG(api.message, ZAPI_MESSAGE_LABEL) ? "" + : "un"), + api.flags); + } + /* Allocate new route. */ vrf_id = zvrf_id(zvrf); re = XCALLOC(MTYPE_RE, sizeof(struct route_entry)); @@ -1162,17 +1183,34 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) else re->table = zvrf->table_id; + /* + * TBD should _all_ of the nexthop add operations use + * api_nh->vrf_id instead of re->vrf_id ? I only changed + * for cases NEXTHOP_TYPE_IPV4 and NEXTHOP_TYPE_IPV6. + */ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) { for (i = 0; i < api.nexthop_num; i++) { api_nh = &api.nexthops[i]; ifindex_t ifindex = 0; + if (IS_ZEBRA_DEBUG_RECV) { + zlog_debug("nh type %d", api_nh->type); + } + switch (api_nh->type) { case NEXTHOP_TYPE_IFINDEX: nexthop = route_entry_nexthop_ifindex_add( re, api_nh->ifindex, api_nh->vrf_id); break; case NEXTHOP_TYPE_IPV4: + if (IS_ZEBRA_DEBUG_RECV) { + char nhbuf[INET6_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET, &api_nh->gate.ipv4, + nhbuf, INET6_ADDRSTRLEN); + zlog_debug("%s: nh=%s, vrf_id=%d", + __func__, nhbuf, + api_nh->vrf_id); + } nexthop = route_entry_nexthop_ipv4_add( re, &api_nh->gate.ipv4, NULL, api_nh->vrf_id); @@ -1187,6 +1225,15 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) ifindex = api_nh->ifindex; } + if (IS_ZEBRA_DEBUG_RECV) { + char nhbuf[INET6_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET, &api_nh->gate.ipv4, + nhbuf, INET6_ADDRSTRLEN); + zlog_debug( + "%s: nh=%s, vrf_id=%d (re->vrf_id=%d), ifindex=%d", + __func__, nhbuf, api_nh->vrf_id, + re->vrf_id, ifindex); + } nexthop = route_entry_nexthop_ipv4_ifindex_add( re, &api_nh->gate.ipv4, NULL, ifindex, api_nh->vrf_id); @@ -1265,6 +1312,14 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) label_type = lsp_type_from_re_type(client->proto); + + if (IS_ZEBRA_DEBUG_RECV) { + zlog_debug( + "%s: adding %d labels of type %d (1st=%u)", + __func__, api_nh->label_num, + label_type, api_nh->labels[0]); + } + nexthop_add_labels(nexthop, label_type, api_nh->label_num, &api_nh->labels[0]);