diff options
45 files changed, 3735 insertions, 217 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index c23950799f..c3a1105995 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -2107,6 +2107,51 @@ bgp_attr_prefix_sid(struct bgp_attr_parser_args *args, return BGP_ATTR_PARSE_PROCEED; } +/* PMSI tunnel attribute (RFC 6514) + * Basic validation checks done here. + */ +static bgp_attr_parse_ret_t +bgp_attr_pmsi_tunnel(struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + u_int8_t tnl_type; + + /* Verify that the receiver is expecting "ingress replication" as we + * can only support that. + */ + if (length < 2) { + zlog_err("Bad PMSI tunnel attribute length %d", length); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + stream_getc(peer->curr); /* Flags */ + tnl_type = stream_getc(peer->curr); + if (tnl_type > PMSI_TNLTYPE_MAX) { + zlog_err("Invalid PMSI tunnel attribute type %d", tnl_type); + return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + } + if (tnl_type == PMSI_TNLTYPE_INGR_REPL) { + if (length != 9) { + zlog_err("Bad PMSI tunnel attribute length %d for IR", + length); + return bgp_attr_malformed( + args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + args->total); + } + } + + attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL); + attr->pmsi_tnl_type = tnl_type; + + /* Forward read pointer of input stream. */ + stream_forward_getp(peer->curr, length - 2); + + return BGP_ATTR_PARSE_PROCEED; +} + /* BGP unknown attribute treatment. */ static bgp_attr_parse_ret_t bgp_attr_unknown(struct bgp_attr_parser_args *args) { @@ -2440,6 +2485,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr, case BGP_ATTR_PREFIX_SID: ret = bgp_attr_prefix_sid(&attr_args, mp_update); break; + case BGP_ATTR_PMSI_TUNNEL: + ret = bgp_attr_pmsi_tunnel(&attr_args); + break; default: ret = bgp_attr_unknown(&attr_args); break; @@ -3263,7 +3311,7 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer, stream_putc(s, BGP_ATTR_PMSI_TUNNEL); stream_putc(s, 9); // Length stream_putc(s, 0); // Flags - stream_putc(s, 6); // Tunnel type: Ingress Replication (6) + stream_putc(s, PMSI_TNLTYPE_INGR_REPL); // IR (6) stream_put(s, &(attr->label), BGP_LABEL_BYTES); // MPLS Label / VXLAN VNI stream_put_ipv4(s, attr->nexthop.s_addr); diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 1a49e4ecf2..5403f32543 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -66,6 +66,8 @@ #define BGP_PREFIX_SID_IPV6_LENGTH 19 #define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6 +/* PMSI tunnel types (RFC 6514) */ + struct bgp_attr_encap_subtlv { struct bgp_attr_encap_subtlv *next; /* for chaining */ /* Reference count of this attribute. */ @@ -96,6 +98,18 @@ struct overlay_index { union gw_addr gw_ip; }; +enum pta_type { + PMSI_TNLTYPE_NO_INFO = 0, + PMSI_TNLTYPE_RSVP_TE_P2MP, + PMSI_TNLTYPE_MLDP_P2MP, + PMSI_TNLTYPE_PIM_SSM, + PMSI_TNLTYPE_PIM_SM, + PMSI_TNLTYPE_PIM_BIDIR, + PMSI_TNLTYPE_INGR_REPL, + PMSI_TNLTYPE_MLDP_MP2MP, + PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP +}; + /* BGP core attribute structure. */ struct attr { /* AS Path structure */ @@ -119,6 +133,9 @@ struct attr { /* Path origin attribute */ u_char origin; + /* PMSI tunnel type (RFC 6514). */ + enum pta_type pmsi_tnl_type; + /* has the route-map changed any attribute? Used on the peer outbound side. */ u_int32_t rmap_change_flags; diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index e89f399e41..f867266956 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; @@ -160,7 +162,6 @@ static const struct message bgp_notify_capability_msg[] = { const char *bgp_origin_str[] = {"i", "e", "?"}; const char *bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"}; - /* Given a string return a pointer the corresponding peer structure */ static struct peer *bgp_find_peer(struct vty *vty, const char *peer_str) { @@ -415,6 +416,10 @@ int bgp_dump_attr(struct attr *attr, char *buf, size_t size) inet_ntoa(attr->cluster->list[i])); } + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL))) + snprintf(buf + strlen(buf), size - strlen(buf), + ", pmsi tnltype %u", attr->pmsi_tnl_type); + if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) snprintf(buf + strlen(buf), size - strlen(buf), ", path %s", aspath_print(attr->aspath)); @@ -1557,6 +1562,96 @@ DEFUN (no_debug_bgp_update_groups, return CMD_SUCCESS; } +DEFUN (debug_bgp_vpn, + debug_bgp_vpn_cmd, + "debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>", + DEBUG_STR + BGP_STR + "VPN routes\n" + "leaked from vrf to vpn\n" + "leaked to vrf from vpn\n" + "route-map updates\n" + "labels\n") +{ + int idx = 3; + + if (argv_find(argv, argc, "leak-from-vrf", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_ON(vpn, VPN_LEAK_FROM_VRF); + else + TERM_DEBUG_ON(vpn, VPN_LEAK_FROM_VRF); + } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_ON(vpn, VPN_LEAK_TO_VRF); + else + TERM_DEBUG_ON(vpn, VPN_LEAK_TO_VRF); + } else if (argv_find(argv, argc, "rmap-event", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT); + else + TERM_DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT); + } else if (argv_find(argv, argc, "label", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_ON(vpn, VPN_LEAK_LABEL); + else + TERM_DEBUG_ON(vpn, VPN_LEAK_LABEL); + } else { + vty_out(vty, "%% unknown debug bgp vpn keyword\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (vty->node != CONFIG_NODE) + vty_out(vty, "enabled debug bgp vpn %s\n", argv[idx]->text); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_vpn, + no_debug_bgp_vpn_cmd, + "no debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>", + NO_STR + DEBUG_STR + BGP_STR + "VPN routes\n" + "leaked from vrf to vpn\n" + "leaked to vrf from vpn\n" + "route-map updates\n" + "labels\n") +{ + int idx = 4; + + if (argv_find(argv, argc, "leak-from-vrf", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF); + else + TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF); + + } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); + else + TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF); + } else if (argv_find(argv, argc, "rmap-event", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); + else + TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT); + } else if (argv_find(argv, argc, "label", &idx)) { + if (vty->node == CONFIG_NODE) + DEBUG_OFF(vpn, VPN_LEAK_LABEL); + else + TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL); + } else { + vty_out(vty, "%% unknown debug bgp vpn keyword\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (vty->node != CONFIG_NODE) + vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text); + + return CMD_SUCCESS; +} + DEFUN (no_debug_bgp, no_debug_bgp_cmd, "no debug bgp", @@ -1589,6 +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; @@ -1648,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; } @@ -1692,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; } @@ -1768,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; } @@ -1861,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 765e43f5b4..fe7ca8c46a 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 @@ -136,6 +142,7 @@ struct bgp_debug_filter { #define CONF_BGP_DEBUG(a, b) (conf_bgp_debug_ ## a & BGP_DEBUG_ ## b) extern const char *bgp_type_str[]; +extern const char *pmsi_tnltype_str[]; extern int bgp_dump_attr(struct attr *, char *, size_t); extern int bgp_debug_peer_updout_enabled(char *host); diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 2e37cce7e2..94d9cb465b 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -2935,6 +2935,19 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi, return -1; } + /* If PMSI is present, log if it is anything other than IR. + * Note: We just simply ignore the values as it is not clear if + * doing anything else is better. + */ + if (attr && + (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL))) { + if (attr->pmsi_tnl_type != PMSI_TNLTYPE_INGR_REPL) { + zlog_warn("%u:%s - Rx EVPN Type-3 NLRI with unsupported PTA %d", + peer->bgp->vrf_id, peer->host, + attr->pmsi_tnl_type); + } + } + /* Make prefix_rd */ prd.family = AF_UNSPEC; prd.prefixlen = 64; diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 7039786014..bf60f9c118 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -265,6 +265,8 @@ static int bgp_vrf_enable(struct vrf *vrf) if (old_vrf_id != bgp->vrf_id) bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id); bgp_instance_up(bgp); + vpn_leak_zebra_vrf_label_update(bgp, AFI_IP); + vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6); } return 0; @@ -283,6 +285,10 @@ static int bgp_vrf_disable(struct vrf *vrf) bgp = bgp_lookup_by_name(vrf->name); if (bgp) { + + vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP); + vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6); + old_vrf_id = bgp->vrf_id; bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); /* We have instance configured, unlink from VRF and make it diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index ec4989de8f..d87f78a783 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -27,9 +27,12 @@ #include "stream.h" #include "queue.h" #include "filter.h" +#include "mpls.h" #include "lib/json.h" +#include "lib/zclient.h" #include "bgpd/bgpd.h" +#include "bgpd/bgp_debug.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" @@ -38,11 +41,19 @@ #include "bgpd/bgp_packet.h" #include "bgpd/bgp_vty.h" #include "bgpd/bgp_vpn.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_zebra.h" +#include "bgpd/bgp_nexthop.h" #if ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" #endif +/* + * Definitions and external declarations. + */ +extern struct zclient *zclient; + extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *afi) { @@ -92,7 +103,7 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, struct rd_as rd_as; struct rd_ip rd_ip; struct prefix_rd prd; - mpls_label_t label; + mpls_label_t label = {0}; afi_t afi; safi_t safi; int addpath_encoded; @@ -233,6 +244,979 @@ int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr, #undef VPN_PREFIXLEN_MIN_BYTES } +/* + * This function informs zebra of the label this vrf sets on routes + * leaked to VPN. Zebra should install this label in the kernel with + * an action of "pop label and then use this vrf's IP FIB to route the PDU." + * + * Sending this vrf-label association is qualified by a) whether vrf->vpn + * exporting is active ("export vpn" is enabled, vpn-policy RD and RT list + * are set) and b) whether vpn-policy label is set. + * + * If any of these conditions do not hold, then we send MPLS_LABEL_NONE + * for this vrf, which zebra interprets to mean "delete this vrf-label + * association." + */ +void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi) +{ + mpls_label_t label = MPLS_LABEL_NONE; + const char *name = "default"; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (debug && (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)) { + name = bgp->name; + } + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) { + zlog_debug( + "%s: vrf %s: afi %s: vrf_id not set, " + "can't set zebra vrf label", + __func__, name, afi2str(afi)); + } + return; + } + + if (vpn_leak_to_vpn_active(bgp, afi, NULL)) { + label = bgp->vpn_policy[afi].tovpn_label; + } + + if (debug) { + zlog_debug("%s: vrf %s: afi %s: setting label %d for vrf id %d", + __func__, name, afi2str(afi), label, bgp->vrf_id); + } + + zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); + bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +/* + * If zebra tells us vrf has become unconfigured, tell zebra not to + * use this label to forward to the vrf anymore + */ +void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi) +{ + mpls_label_t label = MPLS_LABEL_NONE; + int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL); + + if (bgp->vrf_id == VRF_UNKNOWN) { + if (debug) { + zlog_debug( + "%s: vrf_id not set, can't delete zebra vrf label", + __func__); + } + return; + } + + if (debug) { + zlog_debug("%s: deleting label for vrf %s (id=%d)", __func__, + (bgp->name ? bgp->name : "default"), bgp->vrf_id); + } + + zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP); + bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label; +} + +static int ecom_intersect(struct ecommunity *e1, struct ecommunity *e2) +{ + int i; + int j; + + if (!e1 || !e2) + return 0; + + for (i = 0; i < e1->size; ++i) { + for (j = 0; j < e2->size; ++j) { + if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE), + e2->val + (j * ECOMMUNITY_SIZE), + ECOMMUNITY_SIZE)) { + + return 1; + } + } + } + return 0; +} + +/* + * returns pointer to new bgp_info upon success + */ +static struct bgp_info * +leak_update(struct bgp *bgp, /* destination bgp instance */ + struct bgp_node *bn, struct attr *new_attr, /* already interned */ + afi_t afi, safi_t safi, struct bgp_info *source_bi, u_char type, + u_char sub_type, mpls_label_t *label, int num_labels, void *parent, + struct bgp *bgp_orig, struct prefix *nexthop_orig, int debug) +{ + struct prefix *p = &bn->p; + struct bgp_info *bi; + struct bgp_info *new; + char buf_prefix[PREFIX_STRLEN]; + const char *pDestInstanceName = "default"; + + if (debug) { + prefix2str(&bn->p, buf_prefix, sizeof(buf_prefix)); + if (bgp->name) + pDestInstanceName = bgp->name; + } + + /* + * match parent + */ + for (bi = bn->info; bi; bi = bi->next) { + if (bi->extra && bi->extra->parent == parent) + break; + } + + if (bi) { + if (attrhash_cmp(bi->attr, new_attr) + && !CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) { + + bgp_attr_unintern(&new_attr); + if (debug) + zlog_debug( + "%s: ->%s: %s: Found route, no change", + __func__, pDestInstanceName, + buf_prefix); + return NULL; + } + + /* attr is changed */ + bgp_info_set_flag(bn, bi, BGP_INFO_ATTR_CHANGED); + + /* Rewrite BGP route information. */ + if (CHECK_FLAG(bi->flags, BGP_INFO_REMOVED)) + bgp_info_restore(bn, bi); + else + bgp_aggregate_decrement(bgp, p, bi, afi, safi); + bgp_attr_unintern(&bi->attr); + bi->attr = new_attr; + bi->uptime = bgp_clock(); + + /* Process change. */ + bgp_aggregate_increment(bgp, p, bi, afi, safi); + bgp_process(bgp, bn, afi, safi); + bgp_unlock_node(bn); + + if (debug) + zlog_debug("%s: ->%s: %s Found route, changed attr", + __func__, pDestInstanceName, buf_prefix); + + return NULL; + } + + new = info_make(type, sub_type, 0, bgp->peer_self, new_attr, bn); + SET_FLAG(new->flags, BGP_INFO_VALID); + + bgp_info_extra_get(new); + if (label) { + int i; + + for (i = 0; i < num_labels; ++i) { + new->extra->label[i] = label[i]; + if (!bgp_is_valid_label(&label[i])) { + if (debug) { + zlog_debug( + "%s: %s: marking label %d valid", + __func__, buf_prefix, i); + } + bgp_set_valid_label(&new->extra->label[i]); + } + } + new->extra->num_labels = num_labels; + } + new->extra->parent = parent; + + if (bgp_orig) + new->extra->bgp_orig = bgp_orig; + if (nexthop_orig) + new->extra->nexthop_orig = *nexthop_orig; + + bgp_aggregate_increment(bgp, p, new, afi, safi); + bgp_info_add(bn, new); + + bgp_unlock_node(bn); + bgp_process(bgp, bn, afi, safi); + + if (debug) + zlog_debug("%s: ->%s: %s: Added new route", __func__, + pDestInstanceName, buf_prefix); + + return new; +} + +/* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */ +void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, /* to */ + struct bgp *bgp_vrf, /* from */ + struct bgp_info *info_vrf) /* route */ +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct prefix *p = &info_vrf->net->p; + afi_t afi = family2afi(p->family); + struct attr static_attr = {0}; + struct attr *new_attr = NULL; + safi_t safi = SAFI_MPLS_VPN; + mpls_label_t label_val; + mpls_label_t label; + struct bgp_node *bn; + const char *debugmsg; + + if (debug) { + const char *s = ""; + + if (info_vrf->attr && info_vrf->attr->ecommunity) { + s = ecommunity_ecom2str(info_vrf->attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + } + + zlog_debug("%s: info_vrf->type=%d, EC{%s}", __func__, + info_vrf->type, s); + } + + if (!bgp_vpn) + return; + + if (!afi) { + if (debug) + zlog_debug("%s: can't get afi of prefix", __func__); + return; + } + + /* loop check */ + if (info_vrf->extra && info_vrf->extra->bgp_orig == bgp_vpn) + return; + + + if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { + if (debug) + zlog_debug("%s: skipping: %s", __func__, debugmsg); + return; + } + + bgp_attr_dup(&static_attr, info_vrf->attr); /* shallow copy */ + + /* + * route map handling + */ + if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) { + struct bgp_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = bgp_vpn->peer_self; + info.attr = &static_attr; + ret = route_map_apply( + bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN], + p, RMAP_BGP, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug( + "%s: vrf %s route map \"%s\" says DENY, returning", + __func__, bgp_vrf->name, + bgp_vrf->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN] + ->name); + return; + } + } + + if (debug) { + const char *s = ""; + + if (static_attr.ecommunity) { + s = ecommunity_ecom2str(static_attr.ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + } + zlog_debug("%s: post route map static_attr.ecommunity{%s}", + __func__, s); + } + + /* + * Add the vpn-policy rt-list + */ + struct ecommunity *old_ecom; + struct ecommunity *new_ecom; + + old_ecom = static_attr.ecommunity; + if (old_ecom) { + new_ecom = ecommunity_merge( + ecommunity_dup(old_ecom), + bgp_vrf->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + if (!old_ecom->refcnt) + ecommunity_free(&old_ecom); + } else { + new_ecom = ecommunity_dup( + bgp_vrf->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN]); + } + static_attr.ecommunity = new_ecom; + SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)); + + if (debug) { + const char *s = ""; + + if (static_attr.ecommunity) { + s = ecommunity_ecom2str(static_attr.ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + } + zlog_debug("%s: post merge static_attr.ecommunity{%s}", + __func__, s); + } + + /* Nexthop */ + /* if policy nexthop not set, use 0 */ + if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + + struct prefix *nexthop = + &bgp_vrf->vpn_policy[afi].tovpn_nexthop; + switch (nexthop->family) { + case AF_INET: + /* prevent mp_nexthop_global_in <- self in bgp_route.c + */ + static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr; + + static_attr.mp_nexthop_global_in = nexthop->u.prefix4; + static_attr.mp_nexthop_len = 4; + break; + + case AF_INET6: + static_attr.mp_nexthop_global = nexthop->u.prefix6; + static_attr.mp_nexthop_len = 16; + break; + + default: + assert(0); + } + } else { + switch (afi) { + case AFI_IP: + default: + /* Clear ipv4 */ + static_attr.mp_nexthop_global_in.s_addr = 0; + static_attr.mp_nexthop_len = 4; + static_attr.nexthop.s_addr = 0; /* self */ + static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + break; + + case AFI_IP6: + /* Clear ipv6 */ + memset(&static_attr.mp_nexthop_global, 0, + sizeof(static_attr.mp_nexthop_global)); + static_attr.mp_nexthop_len = 16; /* bytes */ + break; + } + } + + label_val = bgp_vrf->vpn_policy[afi].tovpn_label; + if (label_val == MPLS_LABEL_NONE) { + /* TBD get from label manager */ + label = MPLS_LABEL_IMPLICIT_NULL; + } else { + encode_label(label_val, &label); + } + + /* Set originator ID to "me" */ + SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)); + static_attr.originator_id = bgp_vpn->router_id; + + + new_attr = bgp_attr_intern( + &static_attr); /* hashed refcounted everything */ + bgp_attr_flush(&static_attr); /* free locally-allocated parts */ + + if (debug) { + const char *s = ""; + + if (new_attr->ecommunity) { + s = ecommunity_ecom2str(new_attr->ecommunity, + ECOMMUNITY_FORMAT_ROUTE_MAP, 0); + } + zlog_debug("%s: new_attr->ecommunity{%s}", __func__, s); + } + + /* Now new_attr is an allocated interned attr */ + + bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p, + &(bgp_vrf->vpn_policy[afi].tovpn_rd)); + + struct bgp_info *new_info; + + new_info = leak_update(bgp_vpn, bn, new_attr, afi, safi, info_vrf, + ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, &label, 1, + info_vrf, bgp_vrf, NULL, debug); + + /* + * Routes actually installed in the vpn RIB must also be + * offered to all vrfs (because now they originate from + * the vpn RIB). + * + * Acceptance into other vrfs depends on rt-lists. + * Originating vrf will not accept the looped back route + * because of loop checking. + */ + if (new_info) + vpn_leak_to_vrf_update(bgp_vrf, new_info); +} + +void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, /* to */ + struct bgp *bgp_vrf, /* from */ + struct bgp_info *info_vrf) /* route */ +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct prefix *p = &info_vrf->net->p; + afi_t afi = family2afi(p->family); + safi_t safi = SAFI_MPLS_VPN; + struct bgp_info *bi; + struct bgp_node *bn; + const char *debugmsg; + + if (info_vrf->type != ZEBRA_ROUTE_BGP) { + if (debug) + zlog_debug("%s: wrong type %d", __func__, + info_vrf->type); + return; + } + if (info_vrf->sub_type != BGP_ROUTE_NORMAL + && info_vrf->sub_type != BGP_ROUTE_STATIC) { + + if (debug) + zlog_debug("%s: wrong sub_type %d", __func__, + info_vrf->sub_type); + return; + } + if (!bgp_vpn) + return; + + if (!afi) { + if (debug) + zlog_debug("%s: can't get afi of prefix", __func__); + return; + } + + if (!vpn_leak_to_vpn_active(bgp_vrf, afi, &debugmsg)) { + if (debug) + zlog_debug("%s: skipping: %s", __func__, debugmsg); + return; + } + + if (debug) + zlog_debug("%s: withdrawing (info_vrf=%p)", __func__, info_vrf); + + bn = bgp_afi_node_get(bgp_vpn->rib[afi][safi], afi, safi, p, + &(bgp_vrf->vpn_policy[afi].tovpn_rd)); + + /* + * vrf -> vpn + * match original bi imported from + */ + for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) { + if (bi->extra && bi->extra->parent == info_vrf) { + break; + } + } + + if (bi) { + /* withdraw from looped vrfs as well */ + vpn_leak_to_vrf_withdraw(bgp_vpn, bi); + + bgp_aggregate_decrement(bgp_vpn, p, bi, afi, safi); + bgp_info_delete(bn, bi); + bgp_process(bgp_vpn, bn, afi, safi); + } + bgp_unlock_node(bn); +} + +void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, /* to */ + struct bgp *bgp_vrf, /* from */ + afi_t afi) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + struct bgp_node *prn; + safi_t safi = SAFI_MPLS_VPN; + + /* + * Walk vpn table, delete bi with parent == bgp_vrf + * Walk vpn table, delete bi with bgp_orig == bgp_vrf + */ + for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; + prn = bgp_route_next(prn)) { + + struct bgp_table *table; + struct bgp_node *bn; + struct bgp_info *bi; + + /* This is the per-RD table of prefixes */ + table = prn->info; + + if (!table) + continue; + + for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + + char buf[PREFIX2STR_BUFFER]; + + if (debug && bn->info) { + zlog_debug( + "%s: looking at prefix %s", __func__, + prefix2str(&bn->p, buf, sizeof(buf))); + } + + for (bi = bn->info; bi; bi = bi->next) { + if (debug) + zlog_debug("%s: type %d, sub_type %d", + __func__, bi->type, + bi->sub_type); + if (bi->sub_type != BGP_ROUTE_IMPORTED) + continue; + if (!bi->extra) + continue; + if ((struct bgp *)bi->extra->bgp_orig + == bgp_vrf) { + /* delete route */ + if (debug) + zlog_debug("%s: deleting it\n", + __func__); + bgp_aggregate_decrement(bgp_vpn, &bn->p, + bi, afi, safi); + bgp_info_delete(bn, bi); + bgp_process(bgp_vpn, bn, afi, safi); + } + } + } + } +} + +void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, /* to */ + struct bgp *bgp_vrf, /* from */ + afi_t afi) +{ + struct bgp_node *bn; + struct bgp_info *bi; + int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF); + + if (debug) + zlog_debug("%s: entry, afi=%d, vrf=%s", __func__, afi, + bgp_vrf->name); + + for (bn = bgp_table_top(bgp_vrf->rib[afi][SAFI_UNICAST]); bn; + bn = bgp_route_next(bn)) { + + if (debug) + zlog_debug("%s: node=%p", __func__, bn); + + for (bi = bn->info; bi; bi = bi->next) { + if (debug) + zlog_debug( + "%s: calling vpn_leak_from_vrf_update", + __func__); + vpn_leak_from_vrf_update(bgp_vpn, bgp_vrf, bi); + } + } +} + +static void vpn_leak_to_vrf_update_onevrf(struct bgp *bgp_vrf, /* to */ + struct bgp *bgp_vpn, /* from */ + struct bgp_info *info_vpn) /* route */ +{ + struct prefix *p = &info_vpn->net->p; + afi_t afi = family2afi(p->family); + + struct bgp_redist *red; + struct attr static_attr = {0}; + struct attr *new_attr = NULL; + struct bgp_node *bn; + safi_t safi = SAFI_UNICAST; + const char *debugmsg; + struct prefix nexthop_orig; + mpls_label_t *pLabels = NULL; + int num_labels = 0; + + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (!vpn_leak_from_vpn_active(bgp_vrf, afi, &debugmsg, &red)) { + if (debug) + zlog_debug("%s: skipping: %s", __func__, debugmsg); + return; + } + + /* Check for intersection of route targets */ + if (!ecom_intersect( + bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + info_vpn->attr->ecommunity)) { + + return; + } + + if (debug) + zlog_debug("%s: updating to vrf %s", __func__, bgp_vrf->name); + + bgp_attr_dup(&static_attr, info_vpn->attr); /* shallow copy */ + + /* + * Nexthop: stash and clear + * + * Nexthop is valid in context of VPN core, but not in destination vrf. + * Stash it for later label resolution by vrf ingress path and then + * overwrite with 0, i.e., "me", for the sake of vrf advertisement. + */ + uint8_t nhfamily = NEXTHOP_FAMILY(info_vpn->attr->mp_nexthop_len); + + memset(&nexthop_orig, 0, sizeof(nexthop_orig)); + nexthop_orig.family = nhfamily; + + switch (nhfamily) { + + case AF_INET: + /* save */ + nexthop_orig.u.prefix4 = info_vpn->attr->mp_nexthop_global_in; + nexthop_orig.prefixlen = 32; + + static_attr.nexthop.s_addr = 0; /* self */ + static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP); + + break; + + case AF_INET6: + /* save */ + nexthop_orig.u.prefix6 = info_vpn->attr->mp_nexthop_global; + nexthop_orig.prefixlen = 128; + + memset(&static_attr.mp_nexthop_global, 0, + sizeof(static_attr.mp_nexthop_global)); /* clear */ + static_attr.mp_nexthop_len = 16; /* bytes */ + break; + } + + + /* + * route map handling + * For now, we apply two route maps: the "redist" route map and the + * vpn-policy route map. Once we finalize CLI syntax, one of these + * route maps will probably go away. + */ + if (red->rmap.map) { + struct bgp_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = bgp_vrf->peer_self; + info.attr = &static_attr; + ret = route_map_apply(red->rmap.map, p, RMAP_BGP, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug( + "%s: vrf %s redist route map \"%s\" says DENY, skipping", + __func__, bgp_vrf->name, + red->rmap.name); + return; + } + } + if (bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) { + struct bgp_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = bgp_vrf->peer_self; + info.attr = &static_attr; + ret = route_map_apply(bgp_vrf->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_FROMVPN], + p, RMAP_BGP, &info); + if (RMAP_DENYMATCH == ret) { + bgp_attr_flush(&static_attr); /* free any added parts */ + if (debug) + zlog_debug( + "%s: vrf %s vpn-policy route map \"%s\" says DENY, returning", + __func__, bgp_vrf->name, + bgp_vrf->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_FROMVPN] + ->name); + return; + } + } + + new_attr = bgp_attr_intern(&static_attr); + bgp_attr_flush(&static_attr); + + bn = bgp_afi_node_get(bgp_vrf->rib[afi][safi], afi, safi, p, NULL); + + /* + * ensure labels are copied + */ + if (info_vpn->extra && info_vpn->extra->num_labels) { + num_labels = info_vpn->extra->num_labels; + if (num_labels > BGP_MAX_LABELS) + num_labels = BGP_MAX_LABELS; + pLabels = info_vpn->extra->label; + } + if (debug) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(p, buf_prefix, sizeof(buf_prefix)); + zlog_debug("%s: pfx %s: num_labels %d", __func__, buf_prefix, + num_labels); + } + + leak_update(bgp_vrf, bn, new_attr, afi, safi, info_vpn, ZEBRA_ROUTE_BGP, + BGP_ROUTE_IMPORTED, pLabels, num_labels, + info_vpn, /* parent */ + bgp_vpn, &nexthop_orig, debug); +} + +void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, /* from */ + struct bgp_info *info_vpn) /* route */ +{ + struct listnode *mnode, *mnnode; + struct bgp *bgp; + + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (debug) + zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn); + + /* Loop over VRFs */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + + if (!info_vpn->extra + || info_vpn->extra->bgp_orig != bgp) { /* no loop */ + vpn_leak_to_vrf_update_onevrf(bgp, bgp_vpn, info_vpn); + } + } +} + +void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, /* from */ + struct bgp_info *info_vpn) /* route */ +{ + struct prefix *p = &info_vpn->net->p; + afi_t afi = family2afi(p->family); + safi_t safi = SAFI_UNICAST; + struct bgp *bgp; + struct listnode *mnode, *mnnode; + struct bgp_redist *red; + struct bgp_node *bn; + struct bgp_info *bi; + const char *debugmsg; + + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + + if (debug) + zlog_debug("%s: start (info_vpn=%p)", __func__, info_vpn); + + + /* Loop over VRFs */ + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) { + if (!vpn_leak_from_vpn_active(bgp, afi, &debugmsg, &red)) { + if (debug) + zlog_debug("%s: skipping: %s", __func__, + debugmsg); + continue; + } + + /* Check for intersection of route targets */ + if (!ecom_intersect(bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + info_vpn->attr->ecommunity)) { + + continue; + } + + if (debug) + zlog_debug("%s: withdrawing from vrf %s", __func__, + bgp->name); + + bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL); + for (bi = (bn ? bn->info : NULL); bi; bi = bi->next) { + if (bi->extra + && (struct bgp_info *)bi->extra->parent + == info_vpn) { + break; + } + } + + if (bi) { + if (debug) + zlog_debug("%s: deleting bi %p", __func__, bi); + bgp_aggregate_decrement(bgp, p, bi, afi, safi); + bgp_info_delete(bn, bi); + bgp_process(bgp, bn, afi, safi); + } + bgp_unlock_node(bn); + } +} + +void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, /* to */ + afi_t afi) +{ + struct bgp_node *bn; + struct bgp_info *bi; + safi_t safi = SAFI_UNICAST; + int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF); + struct bgp *bgp_vpn = bgp_get_default(); + + if (debug) + zlog_debug("%s: entry", __func__); + /* + * Walk vrf table, delete bi with bgp_orig == bgp_vpn + */ + for (bn = bgp_table_top(bgp_vrf->rib[afi][safi]); bn; + bn = bgp_route_next(bn)) { + + for (bi = bn->info; bi; bi = bi->next) { + if (bi->extra && bi->extra->bgp_orig == bgp_vpn) { + + /* delete route */ + bgp_aggregate_decrement(bgp_vrf, &bn->p, bi, + afi, safi); + bgp_info_delete(bn, bi); + bgp_process(bgp_vrf, bn, afi, safi); + } + } + } +} + +void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, /* to */ + struct bgp *bgp_vpn, /* from */ + afi_t afi) +{ + struct prefix_rd prd; + struct bgp_node *prn; + safi_t safi = SAFI_MPLS_VPN; + + /* + * Walk vpn table + */ + for (prn = bgp_table_top(bgp_vpn->rib[afi][safi]); prn; + prn = bgp_route_next(prn)) { + + struct bgp_table *table; + struct bgp_node *bn; + struct bgp_info *bi; + + memset(&prd, 0, sizeof(prd)); + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(prd.val, prn->p.u.val, 8); + + /* This is the per-RD table of prefixes */ + table = prn->info; + + if (!table) + continue; + + for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) { + + for (bi = bn->info; bi; bi = bi->next) { + + if (bi->extra && bi->extra->bgp_orig == bgp_vrf) + continue; + + vpn_leak_to_vrf_update_onevrf(bgp_vrf, bgp_vpn, + bi); + } + } + } +} + +static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); + afi_t afi; + struct route_map *rmap; + + if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT + && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) { + + return; + } + + rmap = route_map_lookup_by_name(rmap_name); /* NULL if deleted */ + + for (afi = 0; afi < AFI_MAX; ++afi) { + + if (vpn_leak_to_vpn_active(bgp, afi, NULL) + && bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN] + && !strcmp(rmap_name, + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) { + + if (debug) + zlog_debug( + "%s: rmap \"%s\" matches vrf-policy tovpn for as %d afi %s", + __func__, rmap_name, bgp->as, + afi2str(afi)); + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + if (debug) + zlog_debug("%s: after vpn_leak_prechange", + __func__); + + if (!rmap) + bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_TOVPN] = NULL; + + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + if (debug) + zlog_debug("%s: after vpn_leak_postchange", + __func__); + } + + /* + * vpn -> vrf leaking currently can have two route-maps: + * 1. the vpn-policy tovpn route-map + * 2. the (per-afi) redistribute vpn route-map + */ + char *mapname_vpn_policy = + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]; + struct bgp_redist *red = NULL; + + if (vpn_leak_from_vpn_active(bgp, afi, NULL, &red) + && ((mapname_vpn_policy + && !strcmp(rmap_name, mapname_vpn_policy)) + || (red && red->rmap.name + && !strcmp(red->rmap.name, rmap_name)))) { + + if (debug) + zlog_debug( + "%s: rmap \"%s\" matches vrf-policy fromvpn" + " for as %d afi %s", + __func__, rmap_name, bgp->as, + afi2str(afi)); + + vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + + if (!rmap) + bgp->vpn_policy[afi] + .rmap[BGP_VPN_POLICY_DIR_FROMVPN] = + NULL; + + vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + } + } +} + +void vpn_policy_routemap_event(const char *rmap_name) +{ + int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT); + struct listnode *mnode, *mnnode; + struct bgp *bgp; + + if (debug) + zlog_debug("%s: entry", __func__); + + if (bm->bgp == NULL) /* may be called during cleanup */ + return; + + for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) + vpn_policy_routemap_update(bgp, rmap_name); +} + /* For testing purpose, static route of MPLS-VPN. */ DEFUN (vpnv4_network, vpnv4_network_cmd, diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 5c11f7526c..d0ad8ac846 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -23,6 +23,7 @@ #include "bgpd/bgp_route.h" #include "bgpd/bgp_rd.h" +#include "bgpd/bgp_zebra.h" #define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION) #define MPLS_LABEL_IS_NULL(label) \ @@ -51,4 +52,122 @@ extern int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd, enum bgp_show_type type, void *output_arg, int tags, u_char use_json); +extern void vpn_leak_from_vrf_update(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + struct bgp_info *info_vrf); + +extern void vpn_leak_from_vrf_withdraw(struct bgp *bgp_vpn, struct bgp *bgp_vrf, + struct bgp_info *info_vrf); + +extern void vpn_leak_from_vrf_withdraw_all(struct bgp *bgp_vpn, + struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_from_vrf_update_all(struct bgp *bgp_vpn, + struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_to_vrf_withdraw_all(struct bgp *bgp_vrf, afi_t afi); + +extern void vpn_leak_to_vrf_update_all(struct bgp *bgp_vrf, struct bgp *bgp_vpn, + afi_t afi); + +extern void vpn_leak_to_vrf_update(struct bgp *bgp_vpn, + struct bgp_info *info_vpn); + +extern void vpn_leak_to_vrf_withdraw(struct bgp *bgp_vpn, + struct bgp_info *info_vpn); + +extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi); +extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi); + +static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi, + const char **pmsg) +{ + /* Is vrf configured to export to vpn? */ + if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + if (pmsg) + *pmsg = "export not set"; + return 0; + } + + /* Is there an RT list set? */ + if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { + if (pmsg) + *pmsg = "rtlist tovpn not defined"; + return 0; + } + + /* Is there an RD set? */ + if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) { + if (pmsg) + *pmsg = "rd not defined"; + return 0; + } + return 1; +} + +static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi, + const char **pmsg, + struct bgp_redist **pred) +{ + struct bgp_redist *red; + + if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF + && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + + if (pmsg) + *pmsg = "destination bgp instance neither vrf nor default"; + return 0; + } + + /* Hijack zebra redist bits for this route type */ + red = bgp_redist_lookup(bgp_vrf, afi, ZEBRA_ROUTE_BGP_VPN, 0); + if (red) { + if (pred) + *pred = red; + } else { + if (pmsg) + *pmsg = "redist not set"; + return 0; + } + if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { + if (pmsg) + *pmsg = "rtlist fromvpn not defined"; + return 0; + } + return 1; +} + +static inline void vpn_leak_prechange(vpn_policy_direction_t direction, + afi_t afi, struct bgp *bgp_vpn, + struct bgp *bgp_vrf) +{ + if (direction == BGP_VPN_POLICY_DIR_FROMVPN) + vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi); + if (direction == BGP_VPN_POLICY_DIR_TOVPN) + vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi); +} + +static inline void vpn_leak_postchange(vpn_policy_direction_t direction, + afi_t afi, struct bgp *bgp_vpn, + struct bgp *bgp_vrf) +{ + if (direction == BGP_VPN_POLICY_DIR_FROMVPN) + vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi); + if (direction == BGP_VPN_POLICY_DIR_TOVPN) { + + if (bgp_vrf->vpn_policy[afi].tovpn_label + != bgp_vrf->vpn_policy[afi] + .tovpn_zebra_vrf_label_last_sent) { + vpn_leak_zebra_vrf_label_update(bgp_vrf, afi); + } + + vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi); + } + if (direction == BGP_VPN_POLICY_DIR_TOVPN) + vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi); +} + +extern void vpn_policy_routemap_event(const char *rmap_name); + #endif /* _QUAGGA_BGP_MPLSVPN_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 6a7a822ab7..032b33229c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -82,6 +82,21 @@ extern const char *bgp_origin_str[]; extern const char *bgp_origin_long_str[]; +/* PMSI strings. */ +#define PMSI_TNLTYPE_STR_NO_INFO "No info" +#define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO +static const struct message bgp_pmsi_tnltype_str[] = { + {PMSI_TNLTYPE_NO_INFO, PMSI_TNLTYPE_STR_NO_INFO}, + {PMSI_TNLTYPE_RSVP_TE_P2MP, "RSVP-TE P2MP"}, + {PMSI_TNLTYPE_MLDP_P2MP, "mLDP P2MP"}, + {PMSI_TNLTYPE_PIM_SSM, "PIM-SSM"}, + {PMSI_TNLTYPE_PIM_SM, "PIM-SM"}, + {PMSI_TNLTYPE_PIM_BIDIR, "PIM-BIDIR"}, + {PMSI_TNLTYPE_INGR_REPL, "Ingress Replication"}, + {PMSI_TNLTYPE_MLDP_MP2MP, "mLDP MP2MP"}, + {0} +}; + struct bgp_node *bgp_afi_node_get(struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p, struct prefix_rd *prd) @@ -1364,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) @@ -1852,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; } @@ -1966,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)) @@ -2057,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) { @@ -2074,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); @@ -2114,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. */ @@ -2128,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); @@ -2179,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); @@ -2214,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); } } @@ -3122,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); @@ -3238,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); @@ -3276,6 +3371,18 @@ filtered: if (safi == SAFI_EVPN) bgp_evpn_unimport_route(bgp, afi, safi, p, ri); + if (SAFI_UNICAST == safi + && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF + || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + + vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, ri); + } + if ((SAFI_MPLS_VPN == safi) + && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) { + + vpn_leak_to_vrf_withdraw(bgp, ri); + } + bgp_rib_remove(rn, ri, peer, afi, safi); } @@ -3362,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)); @@ -3848,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); @@ -4249,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; @@ -4296,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); } @@ -4316,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); @@ -4351,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); @@ -4479,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, @@ -4515,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); @@ -5979,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; } } @@ -5991,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. */ @@ -6017,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); @@ -6042,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); @@ -7035,6 +7213,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_object *json_ext_community = NULL; json_object *json_lcommunity = NULL; json_object *json_last_update = NULL; + json_object *json_pmsi = NULL; json_object *json_nexthop_global = NULL; json_object *json_nexthop_ll = NULL; json_object *json_nexthops = NULL; @@ -7787,6 +7966,24 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p, json_last_update); } else vty_out(vty, " Last update: %s", ctime(&tbuf)); + + /* Line 10 display PMSI tunnel attribute, if present */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) { + const char *str = lookup_msg(bgp_pmsi_tnltype_str, + attr->pmsi_tnl_type, + PMSI_TNLTYPE_STR_DEFAULT); + + if (json_paths) { + json_pmsi = json_object_new_object(); + json_object_string_add(json_pmsi, + "tunnelType", str); + json_object_object_add(json_path, "pmsi", + json_pmsi); + } else + vty_out(vty, " PMSI Tunnel Type: %s\n", + str); + } + } /* We've constructed the json object for this path, add it to the json diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index dffe2b8ddc..748c4f9110 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -114,6 +114,30 @@ struct bgp_info_extra { /* For imported routes into a VNI (or VRF), this points to the parent. */ void *parent; + + /* + * Some tunnelish parameters follow. Maybe consolidate into an + * internal tunnel structure? + */ + + /* + * Original bgp instance for imported routes. Needed for: + * 1. Find all routes from a specific vrf for deletion + * 2. vrf context of original nexthop + * + * Store pointer to bgp instance rather than bgp->vrf_id because + * bgp->vrf_id is not always valid (or may change?). + * + * Set to NULL if route is not imported from another bgp instance. + */ + struct bgp *bgp_orig; + + /* + * Nexthop in context of original bgp instance. Needed + * for label resolution of core mpls routes exported to a vrf. + * Set nexthop_orig.family to 0 if not valid. + */ + struct prefix nexthop_orig; }; struct bgp_info { @@ -179,6 +203,7 @@ struct bgp_info { #ifdef ENABLE_BGP_VNC # define BGP_ROUTE_RFP 4 #endif +#define BGP_ROUTE_IMPORTED 5 /* from another bgp instance/safi */ u_short instance; diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index b6910a222d..5a265b6c9c 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 705cb152f0..e66e5a540b 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 c8e503d72a..4a5633c94d 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -6112,6 +6112,526 @@ ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as, NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Use addpath to advertise the bestpath per each neighboring AS\n") + +DEFUN_NOSH (vpn_policy_afi, + vpn_policy_afi_cmd, + "vpn-policy <ipv4|ipv6>", + "Enter vpn-policy command mode\n" + BGP_AFI_HELP_STR) +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF + && bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) { + + vty_out(vty, + "vpn-policy supported only in core or vrf instances.\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + afi_t afi; + int idx = 0; + + if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) { + if (afi == AFI_IP) + vty->node = BGP_VPNPOLICY_IPV4_NODE; + else + vty->node = BGP_VPNPOLICY_IPV6_NODE; + return CMD_SUCCESS; + } + return CMD_WARNING_CONFIG_FAILED; +} + +static int vpn_policy_afis(struct vty *vty, int *doafi) +{ + switch (vty->node) { + case BGP_VPNPOLICY_IPV4_NODE: + doafi[AFI_IP] = 1; + break; + case BGP_VPNPOLICY_IPV6_NODE: + doafi[AFI_IP6] = 1; + break; + default: + vty_out(vty, + "%% context error: valid only in vpn-policy block\n"); + return CMD_WARNING_CONFIG_FAILED; + } + return CMD_SUCCESS; +} + +static int argv_find_and_parse_vpn_policy_dirs(struct vty *vty, + struct cmd_token **argv, + int argc, int *idx, int *dodir) +{ + if (argv_find(argv, argc, "fromvpn", idx)) { + dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1; + } else if (argv_find(argv, argc, "tovpn", idx)) { + dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1; + } else if (argv_find(argv, argc, "both", idx)) { + dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1; + dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1; + } else { + vty_out(vty, "%% direction parse error\n"); + return CMD_WARNING_CONFIG_FAILED; + } + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_rd, + vpn_policy_rd_cmd, + "rd ASN:NN_OR_IP-ADDRESS:NN", + "Specify route distinguisher\n" + "Route Distinguisher (<as-number>:<number> | <ip-address>:<number>)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + struct prefix_rd prd; + int ret; + int doafi[AFI_MAX] = {0}; + afi_t afi; + + ret = str2prefix_rd(argv[1]->arg, &prd); + if (!ret) { + vty_out(vty, "%% Malformed rd\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + bgp->vpn_policy[afi].tovpn_rd = prd; + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_rd, + vpn_policy_no_rd_cmd, + "no rd", + NO_STR + "Specify route distinguisher\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + int doafi[AFI_MAX] = {0}; + afi_t afi; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET); + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_label, + vpn_policy_label_cmd, + "label (0-1048575)", + "label value for VRF\n" + "Label Value <0-1048575>\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + mpls_label_t label; + int doafi[AFI_MAX] = {0}; + afi_t afi; + int ret; + + label = strtoul(argv[1]->arg, NULL, 10); + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + bgp->vpn_policy[afi].tovpn_label = label; + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_label, + vpn_policy_no_label_cmd, + "no label", + "Negate a command or set its defaults\n" + "label value for VRF\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int doafi[AFI_MAX] = {0}; + afi_t afi; + int ret; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE; + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +DEFPY (vpn_policy_nexthop, + vpn_policy_nexthop_cmd, + "nexthop <A.B.C.D|X:X::X:X>$nexthop", + "Specify next hop to use for VRF advertised prefixes\n" + "IPv4 prefix\n" + "IPv6 prefix\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int doafi[AFI_MAX] = {0}; + afi_t afi; + int ret; + struct prefix p; + + if (!sockunion2hostprefix(nexthop, &p)) + return CMD_WARNING_CONFIG_FAILED; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* + * pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + bgp->vpn_policy[afi].tovpn_nexthop = p; + SET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET); + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_nexthop, + vpn_policy_no_nexthop_cmd, + "no nexthop", + NO_STR + "Specify next hop to use for VRF advertised prefixes\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int doafi[AFI_MAX] = {0}; + afi_t afi; + int ret; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + + /* pre-change: un-export vpn routes (vpn->vrf routes unaffected) + */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + + UNSET_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET); + + /* post-change: re-export vpn routes */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + + return CMD_SUCCESS; +} + +static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, + struct ecommunity **list) +{ + struct ecommunity *ecom = NULL; + struct ecommunity *ecomadd; + + for (; argc; --argc, ++argv) { + + ecomadd = ecommunity_str2com(argv[0]->arg, + ECOMMUNITY_ROUTE_TARGET, 0); + if (!ecomadd) { + vty_out(vty, "Malformed community-list value\n"); + if (ecom) + ecommunity_free(&ecom); + return CMD_WARNING_CONFIG_FAILED; + } + + if (ecom) { + ecommunity_merge(ecom, ecomadd); + ecommunity_free(&ecomadd); + } else { + ecom = ecomadd; + } + } + + if (*list) { + ecommunity_free(&*list); + } + *list = ecom; + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_rt, + vpn_policy_rt_cmd, + "rt <fromvpn|tovpn|both> RTLIST...", + "Specify route target list\n" + "fromvpn: match any\n" + "tovpn: set\n" + "both fromvpn: match any and tovpn: set\n" + "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + struct ecommunity *ecom = NULL; + int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; + int doafi[AFI_MAX] = {0}; + vpn_policy_direction_t dir; + afi_t afi; + int idx = 0; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); + if (ret != CMD_SUCCESS) + return ret; + + ret = set_ecom_list(vty, argc - 2, argv + 2, &ecom); + if (ret != CMD_SUCCESS) { + return ret; + } + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { + if (!dodir[dir]) + continue; + + vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + + if (bgp->vpn_policy[afi].rtlist[dir]) + ecommunity_free( + &bgp->vpn_policy[afi].rtlist[dir]); + bgp->vpn_policy[afi].rtlist[dir] = ecommunity_dup(ecom); + + vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + } + } + ecommunity_free(&ecom); + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_rt, + vpn_policy_no_rt_cmd, + "no rt <fromvpn|tovpn|both>", + NO_STR + "Specify route target list\n" + "fromvpn: match any\n" + "tovpn: set\n" + "both fromvpn: match any and tovpn: set\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; + int doafi[AFI_MAX] = {0}; + vpn_policy_direction_t dir; + afi_t afi; + int idx = 0; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { + if (!dodir[dir]) + continue; + + vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + + if (bgp->vpn_policy[afi].rtlist[dir]) + ecommunity_free( + &bgp->vpn_policy[afi].rtlist[dir]); + bgp->vpn_policy[afi].rtlist[dir] = NULL; + + vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + } + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_route_map, + vpn_policy_route_map_cmd, + "route-map <fromvpn|tovpn> WORD", + "Specify route map\n" + "fromvpn: core vpn -> this vrf\n" + "tovpn: this vrf -> core vpn\n" + "name of route-map\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; + int doafi[AFI_MAX] = {0}; + vpn_policy_direction_t dir; + afi_t afi; + int map_name_arg = 2; + int idx = 0; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { + if (!dodir[dir]) + continue; + + vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + + if (bgp->vpn_policy[afi].rmap_name[dir]) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp->vpn_policy[afi].rmap_name[dir]); + bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP( + MTYPE_ROUTE_MAP_NAME, argv[map_name_arg]->arg); + bgp->vpn_policy[afi].rmap[dir] = + route_map_lookup_by_name( + argv[map_name_arg]->arg); + + vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + } + } + + return CMD_SUCCESS; +} + +DEFUN (vpn_policy_no_route_map, + vpn_policy_no_route_map_cmd, + "no route-map <fromvpn|tovpn>", + NO_STR + "Specify route map\n" + "fromvpn: core vpn -> this vrf\n" + "tovpn: this vrf -> core vpn\n") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int ret; + int dodir[BGP_VPN_POLICY_DIR_MAX] = {0}; + int doafi[AFI_MAX] = {0}; + vpn_policy_direction_t dir; + afi_t afi; + int idx = 0; + + ret = vpn_policy_afis(vty, doafi); + if (ret != CMD_SUCCESS) + return ret; + + ret = argv_find_and_parse_vpn_policy_dirs(vty, argv, argc, &idx, dodir); + if (ret != CMD_SUCCESS) + return ret; + + for (afi = 0; afi < AFI_MAX; ++afi) { + if (!doafi[afi]) + continue; + for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) { + if (!dodir[dir]) + continue; + + vpn_leak_prechange(dir, afi, bgp_get_default(), bgp); + + if (bgp->vpn_policy[afi].rmap_name[dir]) + XFREE(MTYPE_ROUTE_MAP_NAME, + bgp->vpn_policy[afi].rmap_name[dir]); + bgp->vpn_policy[afi].rmap_name[dir] = NULL; + bgp->vpn_policy[afi].rmap[dir] = NULL; + + vpn_leak_postchange(dir, afi, bgp_get_default(), bgp); + } + } + + return CMD_SUCCESS; +} + DEFUN_NOSH (address_family_ipv4_safi, address_family_ipv4_safi_cmd, "address-family ipv4 [<unicast|multicast|vpn|labeled-unicast>]", @@ -11139,6 +11659,165 @@ void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp, afi_t afi, } } +/* This command is valid only in a bgp vrf instance or the default instance */ +DEFUN (bgp_export_vpn, + bgp_export_vpn_cmd, + "export vpn", + "Export routes to another routing protocol\n" + "to VPN RIB per vpn-policy") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int was_off = 0; + afi_t afi; + safi_t safi; + + if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type + && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { + vty_out(vty, + "%% export vpn valid only for bgp vrf or default instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + afi = bgp_node_afi(vty); + safi = bgp_node_safi(vty); + if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) { + vty_out(vty, + "%% export vpn valid only for unicast ipv4|ipv6\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (!CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + was_off = 1; + } + SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT); + if (was_off) { + /* trigger export current vrf */ + zlog_debug("%s: calling postchange", __func__); + vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + return CMD_SUCCESS; +} + +DEFUN (bgp_no_export_vpn, + bgp_no_export_vpn_cmd, + "no export vpn", + NO_STR + "Export routes to another routing protocol\n" + "to VPN RIB per vpn-policy") +{ + VTY_DECLVAR_CONTEXT(bgp, bgp); + int was_on = 0; + afi_t afi; + safi_t safi; + + if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type + && BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) { + vty_out(vty, + "%% export vpn valid only for bgp vrf or default instance\n"); + return CMD_WARNING_CONFIG_FAILED; + } + afi = bgp_node_afi(vty); + safi = bgp_node_safi(vty); + if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) { + vty_out(vty, + "%% export vpn valid only for unicast ipv4|ipv6\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (CHECK_FLAG(bgp->af_flags[afi][safi], + BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) { + was_on = 1; + } + if (was_on) { + /* trigger un-export current vrf */ + zlog_debug("%s: calling postchange", __func__); + vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi, + bgp_get_default(), bgp); + } + UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT); + return CMD_SUCCESS; +} + +static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp, + afi_t afi) +{ + vty_frame(vty, " vpn-policy ipv%d\n", ((afi == AFI_IP) ? 4 : 6)); + + if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) { + vty_out(vty, " label %u\n", + bgp->vpn_policy[afi].tovpn_label); + } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_RD_SET)) { + char buf[RD_ADDRSTRLEN]; + vty_out(vty, " rd %s\n", + prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf, + sizeof(buf))); + } + if (CHECK_FLAG(bgp->vpn_policy[afi].flags, + BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) { + + char buf[PREFIX_STRLEN]; + if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family, + &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf, + sizeof(buf))) { + + vty_out(vty, " nexthop %s\n", buf); + } + } + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN] + && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN] + && ecommunity_cmp( + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) { + + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt both %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } else { + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) { + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_FROMVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt fromvpn %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) { + char *b = ecommunity_ecom2str( + bgp->vpn_policy[afi] + .rtlist[BGP_VPN_POLICY_DIR_TOVPN], + ECOMMUNITY_FORMAT_ROUTE_MAP, + ECOMMUNITY_ROUTE_TARGET); + vty_out(vty, " rt tovpn %s\n", b); + XFREE(MTYPE_ECOMMUNITY_STR, b); + } + } + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) { + vty_out(vty, " route-map fromvpn %s\n", + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]); + } + if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]) { + vty_out(vty, " route-map tovpn %s\n", + bgp->vpn_policy[afi] + .rmap_name[BGP_VPN_POLICY_DIR_TOVPN]); + } + + vty_endframe(vty, " exit\n"); +} + +void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp) +{ + bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP); + bgp_vpn_policy_config_write_afi(vty, bgp, AFI_IP6); +} + + /* BGP node structure. */ static struct cmd_node bgp_node = { BGP_NODE, "%s(config-router)# ", 1, @@ -11180,6 +11859,12 @@ static struct cmd_node bgp_evpn_node = {BGP_EVPN_NODE, static struct cmd_node bgp_evpn_vni_node = {BGP_EVPN_VNI_NODE, "%s(config-router-af-vni)# ", 1}; +static struct cmd_node bgp_vpn_policy_ipv4_node = { + BGP_VPNPOLICY_IPV4_NODE, "%s(config-router-vpn-policy-ipv4)# ", 1}; + +static struct cmd_node bgp_vpn_policy_ipv6_node = { + BGP_VPNPOLICY_IPV6_NODE, "%s(config-router-vpn-policy-ipv6)# ", 1}; + static void community_list_vty(void); static void bgp_ac_neighbor(vector comps, struct cmd_token *token) @@ -11240,6 +11925,8 @@ void bgp_vty_init(void) install_node(&bgp_vpnv6_node, NULL); install_node(&bgp_evpn_node, NULL); install_node(&bgp_evpn_vni_node, NULL); + install_node(&bgp_vpn_policy_ipv4_node, NULL); + install_node(&bgp_vpn_policy_ipv6_node, NULL); /* Install default VTY commands to new nodes. */ install_default(BGP_NODE); @@ -11253,6 +11940,8 @@ void bgp_vty_init(void) install_default(BGP_VPNV6_NODE); install_default(BGP_EVPN_NODE); install_default(BGP_EVPN_VNI_NODE); + install_default(BGP_VPNPOLICY_IPV4_NODE); + install_default(BGP_VPNPOLICY_IPV6_NODE); /* "bgp multiple-instance" commands. */ install_element(CONFIG_NODE, &bgp_multiple_instance_cmd); @@ -12312,6 +13001,12 @@ void bgp_vty_init(void) install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd); install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd); + /* export vpn [route-map WORD] */ + install_element(BGP_IPV4_NODE, &bgp_export_vpn_cmd); + install_element(BGP_IPV6_NODE, &bgp_export_vpn_cmd); + install_element(BGP_IPV4_NODE, &bgp_no_export_vpn_cmd); + install_element(BGP_IPV6_NODE, &bgp_no_export_vpn_cmd); + /* ttl_security commands */ install_element(BGP_NODE, &neighbor_ttl_security_cmd); install_element(BGP_NODE, &no_neighbor_ttl_security_cmd); @@ -12330,6 +13025,30 @@ void bgp_vty_init(void) /* Community-list. */ community_list_vty(); + + /* vpn-policy commands */ + install_element(BGP_NODE, &vpn_policy_afi_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rd_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rd_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_label_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_label_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_nexthop_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_nexthop_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_rt_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_rt_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_route_map_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_route_map_cmd); + + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rd_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rd_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_label_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_label_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_nexthop_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_nexthop_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_rt_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_rt_cmd); + install_element(BGP_VPNPOLICY_IPV4_NODE, &vpn_policy_no_route_map_cmd); + install_element(BGP_VPNPOLICY_IPV6_NODE, &vpn_policy_no_route_map_cmd); } #include "memory.h" diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index cbb41f0840..459c4ffcc3 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -71,4 +71,5 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty, safi_t *safi, struct bgp **bgp); extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, safi_t safi, u_char use_json); +extern void bgp_vpn_policy_config_write(struct vty *vty, struct bgp *bgp); #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 23f626e960..22284fd28d 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -54,6 +54,7 @@ #include "bgpd/rfapi/vnc_export_bgp.h" #endif #include "bgpd/bgp_evpn.h" +#include "bgpd/bgp_mplsvpn.h" /* All information about zebra. */ struct zclient *zclient = NULL; @@ -987,6 +988,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, struct bgp_info *mpinfo_cp = &local_info; route_tag_t tag; mpls_label_t label; + int nh_othervrf = 0; /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. @@ -997,6 +999,12 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (bgp->main_zebra_update_hold) return; + /* + * vrf leaking support (will have only one nexthop) + */ + if (info->extra && info->extra->bgp_orig) + nh_othervrf = 1; + /* Make Zebra API structure. */ memset(&api, 0, sizeof(api)); memcpy(&api.rmac, &(info->attr->rmac), sizeof(struct ethaddr)); @@ -1008,6 +1016,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, peer = info->peer; + if (info->type == ZEBRA_ROUTE_BGP + && info->sub_type == BGP_ROUTE_IMPORTED) { + + struct bgp_info *bi; + + /* + * Look at parent chain for peer sort + */ + for (bi = info; bi->extra && bi->extra->parent; + bi = bi->extra->parent) { + + peer = ((struct bgp_info *)(bi->extra->parent))->peer; + } + } + tag = info->attr->tag; /* When we create an aggregate route we must also install a Null0 route @@ -1060,12 +1083,38 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (nh_family == AF_INET) { struct in_addr *nexthop; - if (bgp->table_map[afi][safi].name) { + if (bgp_debug_zebra(&api.prefix)) { + char buf_prefix[PREFIX_STRLEN]; + prefix2str(&api.prefix, buf_prefix, + sizeof(buf_prefix)); + if (mpinfo->extra) { + zlog_debug( + "%s: p=%s, bgp_is_valid_label: %d", + __func__, buf_prefix, + bgp_is_valid_label( + &mpinfo->extra + ->label[0])); + } else { + zlog_debug( + "%s: p=%s, extra is NULL, no label", + __func__, buf_prefix); + } + } + + if (bgp->table_map[afi][safi].name || nh_othervrf) { /* Copy info and attributes, so the route-map apply doesn't modify the BGP route info. */ local_attr = *mpinfo->attr; mpinfo_cp->attr = &local_attr; + if (nh_othervrf) { + /* allow route-map to modify */ + local_attr.nexthop = + info->extra->nexthop_orig.u + .prefix4; + } + } + if (bgp->table_map[afi][safi].name) { if (!bgp_table_map_apply( bgp->table_map[afi][safi].map, p, mpinfo_cp)) @@ -1082,6 +1131,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, nexthop = &mpinfo_cp->attr->nexthop; api_nh->gate.ipv4 = *nexthop; + api_nh->vrf_id = nh_othervrf + ? info->extra->bgp_orig->vrf_id + : bgp->vrf_id; /* EVPN type-2 routes are programmed as onlink on l3-vni SVI */ @@ -1095,6 +1147,21 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, ifindex = 0; + if (bgp->table_map[afi][safi].name || nh_othervrf) { + /* Copy info and attributes, so the route-map + apply doesn't modify the BGP route info. */ + local_attr = *mpinfo->attr; + mpinfo_cp->attr = &local_attr; + if (nh_othervrf) { + /* allow route-map to modify */ + local_attr.mp_nexthop_global = + info->extra->nexthop_orig.u + .prefix6; + local_attr.mp_nexthop_len = + BGP_ATTR_NHLEN_IPV6_GLOBAL; + } + } + if (bgp->table_map[afi][safi].name) { /* Copy info and attributes, so the route-map apply doesn't modify the BGP route info. */ @@ -1139,6 +1206,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, api_nh->gate.ipv6 = *nexthop; api_nh->ifindex = ifindex; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; + /* api_nh->vrf_id is not set for normal case? */ + if (nh_othervrf) + api_nh->vrf_id = info->extra->bgp_orig->vrf_id; } if (mpinfo->extra @@ -1229,9 +1299,12 @@ void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi) for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) for (ri = rn->info; ri; ri = ri->next) - if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) - && ri->type == ZEBRA_ROUTE_BGP - && ri->sub_type == BGP_ROUTE_NORMAL) + if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) && + + (ri->type == ZEBRA_ROUTE_BGP + && (ri->sub_type == BGP_ROUTE_NORMAL + || ri->sub_type == BGP_ROUTE_IMPORTED))) + bgp_zebra_announce(rn, &rn->p, ri, bgp, afi, safi); } @@ -1244,6 +1317,21 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi) peer = info->peer; assert(peer); + if (info->type == ZEBRA_ROUTE_BGP + && info->sub_type == BGP_ROUTE_IMPORTED) { + + struct bgp_info *bi; + + /* + * Look at parent chain for peer sort + */ + for (bi = info; bi->extra && bi->extra->parent; + bi = bi->extra->parent) { + + peer = ((struct bgp_info *)(bi->extra->parent))->peer; + } + } + /* Don't try to install if we're not connected to Zebra or Zebra doesn't * know of this instance. */ @@ -1363,7 +1451,27 @@ int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type, u_short instance) } #endif + /* vpn -> vrf (happens within bgp but we hijack redist bits */ + if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT + || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + && type == ZEBRA_ROUTE_BGP_VPN) { + + /* leak update all */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + } + vrf_bitmap_set(zclient->redist[afi][type], bgp->vrf_id); + + /* vpn -> vrf (happens within bgp but we hijack redist bits */ + if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT + || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + && type == ZEBRA_ROUTE_BGP_VPN) { + + /* leak update all */ + vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + } } /* @@ -1484,11 +1592,6 @@ int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type, vrf_bitmap_unset(zclient->redist[afi][type], bgp->vrf_id); } -#if ENABLE_BGP_VNC - if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) { - vnc_export_bgp_disable(bgp, afi); - } -#endif if (bgp_install_info_to_zebra(bgp)) { /* Send distribute delete message to zebra. */ @@ -1512,6 +1615,26 @@ int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type, { struct bgp_redist *red; +/* + * vnc and vpn->vrf checks must be before red check because + * they operate within bgpd irrespective of zebra connection + * status. red lookup fails if there is no zebra connection. + */ +#if ENABLE_BGP_VNC + if (bgp->vrf_id == VRF_DEFAULT && type == ZEBRA_ROUTE_VNC_DIRECT) { + vnc_export_bgp_disable(bgp, afi); + } +#endif + /* vpn -> vrf (happend within bgp but we hijack redist bits */ + if ((bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT + || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + && type == ZEBRA_ROUTE_BGP_VPN) { + + /* leak withdraw all */ + vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi, + bgp_get_default(), bgp); + } + red = bgp_redist_lookup(bgp, afi, type, instance); if (!red) return CMD_SUCCESS; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index eb68aee9fb..2eae2e5e9e 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 9e1d279091..664f8c9da4 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 4c7c392ab8..8c4d5ab043 100644 --- a/bgpd/rfapi/bgp_rfapi_cfg.c +++ b/bgpd/rfapi/bgp_rfapi_cfg.c @@ -2182,6 +2182,7 @@ void vnc_routemap_update(struct bgp *bgp, const char *unused) vnc_zlog_debug_verbose("%s done", __func__); } +#if 0 /* superseded */ static void vnc_routemap_event(route_map_event_t type, /* ignored */ const char *rmap_name) /* ignored */ { @@ -2197,6 +2198,7 @@ static void vnc_routemap_event(route_map_event_t type, /* ignored */ vnc_zlog_debug_verbose("%s: done", __func__); } +#endif /*------------------------------------------------------------------------- * nve-group @@ -3673,7 +3675,8 @@ bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import, void bgp_rfapi_cfg_init(void) { /* main bgpd code does not use this hook, but vnc does */ - route_map_event_hook(vnc_routemap_event); + /* superseded by bgp_route_map_process_update_cb() */ + /* bgp_route_map_event_hook_add(vnc_routemap_event); */ install_node(&bgp_vnc_defaults_node, NULL); install_node(&bgp_vnc_nve_group_node, NULL); diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c index c4d66bbc65..ae31c3fe9e 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/doc/manpages/vtysh.rst b/doc/manpages/vtysh.rst index 38cb668e82..2db746020d 100644 --- a/doc/manpages/vtysh.rst +++ b/doc/manpages/vtysh.rst @@ -3,7 +3,7 @@ VTYSH ***** .. include:: defines.rst -.. |DAEMON| replace:: eigrpd +.. |DAEMON| replace:: vtysh SYNOPSIS ======== @@ -41,6 +41,18 @@ OPTIONS available for the vtysh command: When the -c option is being used, this flag will cause the standard vtysh prompt and command to be echoed prior to displaying the results. This is particularly useful to separate the results when executing multiple commands. +.. option:: -C, --dryrun + + When the -C option is being used, this flag will check the config for syntatic validity. + +.. option:: -m, --markfile + + Mark the input file with context ends, useful for cleanup of a config file that has a lot of extraneous space and end markers + +.. option:: -n, --noerror + + When executing cli that does not invoke a vtysh shell, if an error ocurrs ignore it for purposes of return codes from vtysh. + .. option:: -h, --help Display a usage message on standard output and exit. diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index 139d024e4f..b6989809a7 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -337,6 +337,15 @@ ISIS interface Set PSNP interval in seconds globally, for an area (level-1) or a domain (level-2). +.. index:: isis three-way-handshake +.. clicmd:: isis three-way-handshake + +.. index:: no isis three-way-handshake +.. clicmd:: no isis three-way-handshake + + Enable or disable :rfc:`5303` Three-Way Handshake for P2P adjacencies. + Three-Way Handshake is enabled by default. + .. _showing-isis-information: Showing ISIS information diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c index 198cf35e68..48fcf449c7 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 5697c1d812..b289cdd7a3 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 0febf903a3..1e700aaa8f 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 */ @@ -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/nexthop.c b/lib/nexthop.c index cee34e85c7..fb7ccc169e 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -163,6 +163,57 @@ void nexthops_free(struct nexthop *nexthop) } } +bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2) +{ + if (nh1 && !nh2) + return false; + + if (!nh1 && nh2) + return false; + + if (nh1 == nh2) + return true; + + if (nh1->vrf_id != nh2->vrf_id) + return false; + + if (nh1->type != nh2->type) + return false; + + switch (nh1->type) { + case NEXTHOP_TYPE_IFINDEX: + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV4: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (nh1->gate.ipv4.s_addr != nh2->gate.ipv4.s_addr) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_IPV6: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + if (memcmp(&nh1->gate.ipv6, &nh2->gate.ipv6, 16)) + return false; + if (nh1->ifindex != nh2->ifindex) + return false; + break; + case NEXTHOP_TYPE_BLACKHOLE: + if (nh1->bh_type != nh2->bh_type) + return false; + break; + } + + return true; +} + /* Update nexthop with label information. */ void nexthop_add_labels(struct nexthop *nexthop, enum lsp_types_t type, u_int8_t num_labels, mpls_label_t *label) diff --git a/lib/nexthop.h b/lib/nexthop.h index 0ca8a0063a..568243d3a9 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -118,6 +118,8 @@ void nexthop_add_labels(struct nexthop *, enum lsp_types_t, u_int8_t, mpls_label_t *); void nexthop_del_labels(struct nexthop *); +extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2); + extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type); extern int nexthop_same_no_recurse(const struct nexthop *next1, const struct nexthop *next2); diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index e7f10487d1..e486247244 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -19,6 +19,7 @@ */ #include <zebra.h> +#include <vrf.h> #include <nexthop.h> #include <nexthop_group.h> #include <vty.h> @@ -28,6 +29,56 @@ #include "lib/nexthop_group_clippy.c" #endif +DEFINE_MTYPE_STATIC(LIB, NEXTHOP_GROUP, "Nexthop Group") + +struct nexthop_group_hooks { + void (*new)(const char *name); + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop); + void (*delete)(const char *name); +}; + +static struct nexthop_group_hooks nhg_hooks; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2); +RB_GENERATE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) + +struct nhgc_entry_head nhgc_entries; + +static inline int +nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1, + const struct nexthop_group_cmd *nhgc2) +{ + return strcmp(nhgc1->name, nhgc2->name); +} + +struct nexthop *nexthop_exists(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + return nexthop; + } + + return NULL; +} + +struct nexthop_group *nexthop_group_new(void) +{ + return XCALLOC(MTYPE_NEXTHOP_GROUP, sizeof(struct nexthop_group)); +} + +void nexthop_group_delete(struct nexthop_group **nhg) +{ + XFREE(MTYPE_NEXTHOP_GROUP, *nhg); +} + /* Add nexthop to the end of a nexthop list. */ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) { @@ -42,6 +93,27 @@ void nexthop_add(struct nexthop **target, struct nexthop *nexthop) nexthop->prev = last; } +/* Delete nexthop from a nexthop list. */ +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nh) +{ + struct nexthop *nexthop; + + for (nexthop = nhg->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop_same(nh, nexthop)) + break; + } + + assert(nexthop); + + if (nexthop->prev) + nexthop->prev->next = nexthop->next; + else + nhg->nexthop = nexthop->next; + + if (nexthop->next) + nexthop->next->prev = nexthop->prev; +} + void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent) { @@ -71,12 +143,169 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, } } -DEFPY (nexthop_group, - nexthop_group_cmd, - "nexthop-group NAME", - "Enter into the nexthop-group submode\n" - "Specify the NAME of the nexthop-group\n") +static void nhgc_delete_nexthops(struct nexthop_group_cmd *nhgc) +{ + struct nexthop *nexthop; + + nexthop = nhgc->nhg.nexthop; + while (nexthop) { + struct nexthop *next = nexthop_next(nexthop); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nexthop); + + nexthop_free(nexthop); + + nexthop = next; + } +} + +struct nexthop_group_cmd *nhgc_find(const char *name) +{ + struct nexthop_group_cmd find; + + strlcpy(find.name, name, sizeof(find.name)); + + return RB_FIND(nhgc_entry_head, &nhgc_entries, &find); +} + +static struct nexthop_group_cmd *nhgc_get(const char *name) { + struct nexthop_group_cmd *nhgc; + + nhgc = nhgc_find(name); + if (!nhgc) { + nhgc = XCALLOC(MTYPE_TMP, sizeof(*nhgc)); + strlcpy(nhgc->name, name, sizeof(nhgc->name)); + + QOBJ_REG(nhgc, nexthop_group_cmd); + RB_INSERT(nhgc_entry_head, &nhgc_entries, nhgc); + + if (nhg_hooks.new) + nhg_hooks.new(name); + } + + return nhgc; +} + +static void nhgc_delete(struct nexthop_group_cmd *nhgc) +{ + nhgc_delete_nexthops(nhgc); + + if (nhg_hooks.delete) + nhg_hooks.delete(nhgc->name); + + RB_REMOVE(nhgc_entry_head, &nhgc_entries, nhgc); +} + +DEFINE_QOBJ_TYPE(nexthop_group_cmd) + +DEFUN_NOSH(nexthop_group, nexthop_group_cmd, "nexthop-group NAME", + "Enter into the nexthop-group submode\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[1]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_get(nhg_name); + VTY_PUSH_CONTEXT(NH_GROUP_NODE, nhgc); + + return CMD_SUCCESS; +} + +DEFUN_NOSH(no_nexthop_group, no_nexthop_group_cmd, "no nexthop-group NAME", + NO_STR + "Delete the nexthop-group\n" + "Specify the NAME of the nexthop-group\n") +{ + const char *nhg_name = argv[2]->arg; + struct nexthop_group_cmd *nhgc = NULL; + + nhgc = nhgc_find(nhg_name); + if (nhgc) + nhgc_delete(nhgc); + + return CMD_SUCCESS; +} + +DEFPY(ecmp_nexthops, ecmp_nexthops_cmd, + "[no] nexthop <A.B.C.D|X:X::X:X>$addr [INTERFACE]$intf [nexthop-vrf NAME$name]", + NO_STR + "Specify one of the nexthops in this ECMP group\n" + "v4 Address\n" + "v6 Address\n" + "Interface to use\n" + "If the nexthop is in a different vrf tell us\n" + "The nexthop-vrf Name\n") +{ + VTY_DECLVAR_CONTEXT(nexthop_group_cmd, nhgc); + struct vrf *vrf; + struct nexthop nhop; + struct nexthop *nh; + + if (name) + vrf = vrf_lookup_by_name(name); + else + vrf = vrf_lookup_by_id(VRF_DEFAULT); + + if (!vrf) { + vty_out(vty, "Specified: %s is non-existent\n", name); + return CMD_WARNING; + } + + memset(&nhop, 0, sizeof(nhop)); + nhop.vrf_id = vrf->vrf_id; + + if (addr->sa.sa_family == AF_INET) { + nhop.gate.ipv4.s_addr = addr->sin.sin_addr.s_addr; + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV4_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV4; + } else { + memcpy(&nhop.gate.ipv6, &addr->sin6.sin6_addr, 16); + if (intf) { + nhop.type = NEXTHOP_TYPE_IPV6_IFINDEX; + nhop.ifindex = ifname2ifindex(intf, vrf->vrf_id); + if (nhop.ifindex == IFINDEX_INTERNAL) { + vty_out(vty, + "Specified Intf %s does not exist in vrf: %s\n", + intf, vrf->name); + return CMD_WARNING; + } + } else + nhop.type = NEXTHOP_TYPE_IPV6; + } + + nh = nexthop_exists(&nhgc->nhg, &nhop); + + if (no) { + if (nh) { + nexthop_del(&nhgc->nhg, nh); + + if (nhg_hooks.del_nexthop) + nhg_hooks.del_nexthop(nhgc, nh); + + nexthop_free(nh); + } + } else if (!nh) { + /* must be adding new nexthop since !no and !nexthop_exists */ + nh = nexthop_new(); + + memcpy(nh, &nhop, sizeof(nhop)); + nexthop_add(&nhgc->nhg.nexthop, nh); + + if (nhg_hooks.add_nexthop) + nhg_hooks.add_nexthop(nhgc, nh); + } + return CMD_SUCCESS; } @@ -86,15 +315,85 @@ struct cmd_node nexthop_group_node = { 1 }; +void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh) +{ + char buf[100]; + struct vrf *vrf; + + vty_out(vty, " nexthop "); + + switch (nh->type) { + case NEXTHOP_TYPE_IFINDEX: + vty_out(vty, "%s", ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV4: + vty_out(vty, "%s", inet_ntoa(nh->gate.ipv4)); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out(vty, "%s %s", inet_ntoa(nh->gate.ipv4), + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + vty_out(vty, "%s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf))); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, "%s %s", + inet_ntop(AF_INET6, &nh->gate.ipv6, buf, sizeof(buf)), + ifindex2ifname(nh->ifindex, nh->vrf_id)); + break; + case NEXTHOP_TYPE_BLACKHOLE: + break; + } + + if (nh->vrf_id != VRF_DEFAULT) { + vrf = vrf_lookup_by_id(nh->vrf_id); + vty_out(vty, " nexthop-vrf %s", vrf->name); + } + vty_out(vty, "\n"); +} + static int nexthop_group_write(struct vty *vty) { - vty_out(vty, "!\n"); + struct nexthop_group_cmd *nhgc; + struct nexthop *nh; + + RB_FOREACH (nhgc, nhgc_entry_head, &nhgc_entries) { + vty_out(vty, "nexthop-group %s\n", nhgc->name); + + for (nh = nhgc->nhg.nexthop; nh; nh = nh->next) + nexthop_group_write_nexthop(vty, nh); + + vty_out(vty, "!\n"); + } return 1; } -void nexthop_group_init(void) +void nexthop_group_init(void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhg, + const struct nexthop *nhop), + void (*delete)(const char *name)) { + RB_INIT(nhgc_entry_head, &nhgc_entries); + install_node(&nexthop_group_node, nexthop_group_write); install_element(CONFIG_NODE, &nexthop_group_cmd); + install_element(CONFIG_NODE, &no_nexthop_group_cmd); + + install_default(NH_GROUP_NODE); + install_element(NH_GROUP_NODE, &ecmp_nexthops_cmd); + + memset(&nhg_hooks, 0, sizeof(nhg_hooks)); + + if (new) + nhg_hooks.new = new; + if (add_nexthop) + nhg_hooks.add_nexthop = add_nexthop; + if (del_nexthop) + nhg_hooks.del_nexthop = del_nexthop; + if (delete) + nhg_hooks.delete = delete; } diff --git a/lib/nexthop_group.h b/lib/nexthop_group.h index 561fe96425..c2e4c4d757 100644 --- a/lib/nexthop_group.h +++ b/lib/nexthop_group.h @@ -21,6 +21,8 @@ #ifndef __NEXTHOP_GROUP__ #define __NEXTHOP_GROUP__ +#include <vty.h> + /* * What is a nexthop group? * @@ -33,9 +35,11 @@ struct nexthop_group { struct nexthop *nexthop; }; -void nexthop_group_init(void); +struct nexthop_group *nexthop_group_new(void); +void nexthop_group_delete(struct nexthop_group **nhg); void nexthop_add(struct nexthop **target, struct nexthop *nexthop); +void nexthop_del(struct nexthop_group *nhg, struct nexthop *nexthop); void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, struct nexthop *rparent); @@ -51,4 +55,40 @@ void copy_nexthops(struct nexthop **tnh, struct nexthop *nh, (nhop) = (head.nexthop); \ (nhop); \ (nhop) = nexthop_next(nhop) + +struct nexthop_group_cmd { + + RB_ENTRY(nexthop_group_cmd) nhgc_entry; + + char name[80]; + + struct nexthop_group nhg; + + QOBJ_FIELDS +}; +RB_HEAD(nhgc_entry_head, nexthp_group_cmd); +RB_PROTOTYPE(nhgc_entry_head, nexthop_group_cmd, nhgc_entry, + nexthop_group_cmd_compare) +DECLARE_QOBJ_TYPE(nexthop_group_cmd) + +/* + * Initialize nexthop_groups. If you are interested in when + * a nexthop_group is added/deleted/modified, then set the + * appropriate callback functions to handle it in your + * code + */ +void nexthop_group_init( + void (*new)(const char *name), + void (*add_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*del_nexthop)(const struct nexthop_group_cmd *nhgc, + const struct nexthop *nhop), + void (*delete)(const char *name)); + +extern struct nexthop *nexthop_exists(struct nexthop_group *nhg, + struct nexthop *nh); + +extern struct nexthop_group_cmd *nhgc_find(const char *name); + +extern void nexthop_group_write_nexthop(struct vty *vty, struct nexthop *nh); #endif diff --git a/lib/route_types.txt b/lib/route_types.txt index 4e764a14c1..98cada8f89 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/strlcat.c b/lib/strlcat.c index 8186304437..be211f82a8 100644 --- a/lib/strlcat.c +++ b/lib/strlcat.c @@ -28,23 +28,25 @@ #ifndef HAVE_STRLCAT #undef strlcat -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize); -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size) +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize) { size_t src_length = strlen(src); /* Our implementation strlcat supports dest == NULL if size == 0 (for consistency with snprintf and strlcpy), but strnlen does not, so we have to cover this case explicitly. */ - if (size == 0) + if (destsize == 0) return src_length; - size_t dest_length = strnlen(dest, size); - if (dest_length != size) { + size_t dest_length = strnlen(dest, destsize); + if (dest_length != destsize) { /* Copy at most the remaining number of characters in the destination buffer. Leave for the NUL terminator. */ - size_t to_copy = size - dest_length - 1; + size_t to_copy = destsize - dest_length - 1; /* But not more than what is available in the source string. */ if (to_copy > src_length) to_copy = src_length; diff --git a/lib/strlcpy.c b/lib/strlcpy.c index b7681754aa..b0c33ca7f4 100644 --- a/lib/strlcpy.c +++ b/lib/strlcpy.c @@ -27,23 +27,26 @@ #ifndef HAVE_STRLCPY #undef strlcpy -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize); -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size) +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize) { size_t src_length = strlen(src); - if (__builtin_expect(src_length >= size, 0)) { - if (size > 0) { - /* Copy the leading portion of the string. The last - character is subsequently overwritten with the NUL - terminator, but the destination size is usually a - multiple of a small power of two, so writing it twice - should be more efficient than copying an odd number - of - bytes. */ - memcpy(dest, src, size); - dest[size - 1] = '\0'; + if (__builtin_expect(src_length >= destsize, 0)) { + if (destsize > 0) { + /* + * Copy the leading portion of the string. The last + * character is subsequently overwritten with the NUL + * terminator, but the destination destsize is usually + * a multiple of a small power of two, so writing it + * twice should be more efficient than copying an odd + * number of bytes. + */ + memcpy(dest, src, destsize); + dest[destsize - 1] = '\0'; } } else /* Copy the string and its terminating NUL character. */ diff --git a/lib/zclient.c b/lib/zclient.c index c720e2519b..777f6fcf9b 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/lib/zebra.h b/lib/zebra.h index 262ad2e43d..923f6f77c6 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -232,10 +232,12 @@ typedef unsigned char u_int8_t; #include "zassert.h" #ifndef HAVE_STRLCAT -size_t strlcat(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcat(char *__restrict dest, + const char *__restrict src, size_t destsize); #endif #ifndef HAVE_STRLCPY -size_t strlcpy(char *__restrict dest, const char *__restrict src, size_t size); +size_t strlcpy(char *__restrict dest, + const char *__restrict src, size_t destsize); #endif #ifdef HAVE_BROKEN_CMSG_FIRSTHDR diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index 3b257fec96..e322f60509 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 c8a4dc12cd..26bbc5ee11 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/pimd/mtracebis.c b/pimd/mtracebis.c index 337d420d34..5c8229175a 100644 --- a/pimd/mtracebis.c +++ b/pimd/mtracebis.c @@ -37,6 +37,7 @@ #include <net/if.h> #include <unistd.h> #include <getopt.h> +#include <netdb.h> #define MTRACEBIS_VERSION "0.1" #define MTRACE_TIMEOUT (5) @@ -56,6 +57,160 @@ static void version(void) fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION); } +static void print_host(struct in_addr addr) +{ + struct hostent *h; + + h = gethostbyaddr(&addr, sizeof(addr), AF_INET); + if (h == NULL) + printf("?"); + else + printf("%s", h->h_name); + printf(" (%s) ", inet_ntoa(addr)); +} + +static void print_line_no(int i) +{ + printf("%3d ", -i); +} + +static const char *rtg_proto_str(enum mtrace_rtg_proto proto) +{ + static char buf[80]; + + buf[0] = '\0'; + + switch (proto) { + case MTRACE_RTG_PROTO_DVMRP: + return "DVMRP"; + case MTRACE_RTG_PROTO_MOSPF: + return "MOSPF"; + case MTRACE_RTG_PROTO_PIM: + return "PIM"; + case MTRACE_RTG_PROTO_CBT: + return "CBT"; + case MTRACE_RTG_PROTO_PIM_SPECIAL: + return "PIM special"; + case MTRACE_RTG_PROTO_PIM_STATIC: + return "PIM static"; + case MTRACE_RTG_PROTO_DVMRP_STATIC: + return "DVMRP static"; + case MTRACE_RTG_PROTO_PIM_MBGP: + return "PIM MBGP"; + case MTRACE_RTG_PROTO_CBT_SPECIAL: + return "CBT special"; + case MTRACE_RTG_PROTO_CBT_STATIC: + return "CBT static"; + case MTRACE_RTG_PROTO_PIM_ASSERT: + return "PIM assert"; + default: + sprintf(buf, "unknown protocol (%d)", proto); + return buf; + } +} + +static void print_rtg_proto(uint32_t rtg_proto) +{ + printf("%s", rtg_proto_str(rtg_proto)); +} + +static void print_fwd_ttl(uint32_t fwd_ttl) +{ + printf("thresh^ %d", fwd_ttl); +} + +static const char *fwd_code_str(enum mtrace_fwd_code code) +{ + static char buf[80]; + + buf[0] = '\0'; + + switch (code) { + case MTRACE_FWD_CODE_NO_ERROR: + return "no error"; + case MTRACE_FWD_CODE_WRONG_IF: + return "wrong interface"; + case MTRACE_FWD_CODE_PRUNE_SENT: + return "prune sent"; + case MTRACE_FWD_CODE_PRUNE_RCVD: + return "prune received"; + case MTRACE_FWD_CODE_SCOPED: + return "scoped"; + case MTRACE_FWD_CODE_NO_ROUTE: + return "no route"; + case MTRACE_FWD_CODE_WRONG_LAST_HOP: + return "wrong last hop"; + case MTRACE_FWD_CODE_NOT_FORWARDING: + return "not forwarding"; + case MTRACE_FWD_CODE_REACHED_RP: + return "reached RP"; + case MTRACE_FWD_CODE_RPF_IF: + return "RPF interface"; + case MTRACE_FWD_CODE_NO_MULTICAST: + return "no multicast"; + case MTRACE_FWD_CODE_INFO_HIDDEN: + return "info hidden"; + case MTRACE_FWD_CODE_NO_SPACE: + return "no space"; + case MTRACE_FWD_CODE_OLD_ROUTER: + return "old router"; + case MTRACE_FWD_CODE_ADMIN_PROHIB: + return "admin. prohib."; + default: + sprintf(buf, "unknown fwd. code (%d)", code); + return buf; + } +} + +static void print_fwd_code(uint32_t fwd_code) +{ + printf("%s", fwd_code_str(fwd_code)); +} + +static void print_rsp(struct igmp_mtrace_rsp *rsp) +{ + print_host(rsp->outgoing); + if (rsp->fwd_code == 0) { + print_rtg_proto(rsp->rtg_proto); + printf(" "); + print_fwd_ttl(rsp->fwd_ttl); + } else { + print_fwd_code(rsp->fwd_code); + } + printf("\n"); +} + +static void print_dest(struct igmp_mtrace *mtrace) +{ + print_line_no(0); + print_host(mtrace->dst_addr); + printf("\n"); +} + +static void print_summary(struct igmp_mtrace *mtrace, int hops, long msec) +{ + int i; + int t = 0; + + for (i = 0; i < hops; i++) + t += mtrace->rsp[i].fwd_ttl; + + printf("Round trip time %ld ms; total ttl of %d required.\n", msec, t); +} + +static void print_responses(struct igmp_mtrace *mtrace, int hops, long msec) +{ + int i; + + print_dest(mtrace); + + for (i = 0; i < hops; i++) { + print_line_no(i + 1); + print_rsp(&mtrace->rsp[i]); + } + print_summary(mtrace, hops, msec); +} + static int send_query(int fd, struct in_addr to_addr, struct igmp_mtrace *mtrace) { @@ -88,7 +243,7 @@ static void print_query(struct igmp_mtrace *mtrace) inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str))); } -static int recv_response(int fd, long msec, int *hops) +static int recv_response(int fd, int *hops, struct igmp_mtrace *mtracer) { int recvd; char mtrace_buf[IP_AND_MTRACE_BUF_LEN]; @@ -96,7 +251,6 @@ static int recv_response(int fd, long msec, int *hops) struct igmp_mtrace *mtrace; int mtrace_len; int responses; - int i; u_short sum; recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0); @@ -145,22 +299,22 @@ static int recv_response(int fd, long msec, int *hops) responses = mtrace_len - sizeof(struct igmp_mtrace); responses /= sizeof(struct igmp_mtrace_rsp); - printf("%ld ms received responses from %d hops.\n", msec, responses); + if (responses > MTRACE_MAX_HOPS) { + fprintf(stderr, "mtrace too large\n"); + return -1; + } if (hops) *hops = responses; - for (i = 0; i < responses; i++) { - struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i]; - - if (rsp->fwd_code != 0) - printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code); - } + if (mtracer) + memcpy(mtracer, mtrace, mtrace_len); return 0; } -static int wait_for_response(int fd, int *hops) +static int wait_for_response(int fd, int *hops, struct igmp_mtrace *mtrace, + long *ret_msec) { fd_set readfds; struct timeval timeout; @@ -181,11 +335,19 @@ static int wait_for_response(int fd, int *hops) return ret; rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000; msec = tmsec - rmsec; - } while (recv_response(fd, msec, hops) != 0); + } while (recv_response(fd, hops, mtrace) != 0); + + if (ret_msec) + *ret_msec = msec; return ret; } +static bool check_end(struct igmp_mtrace *mtrace, int hops) +{ + return mtrace->src_addr.s_addr == mtrace->rsp[hops - 1].prev_hop.s_addr; +} + int main(int argc, char *const argv[]) { struct in_addr mc_source; @@ -193,6 +355,7 @@ int main(int argc, char *const argv[]) struct in_addr gw_addr; struct in_addr mtrace_addr; struct igmp_mtrace mtrace; + struct igmp_mtrace *mtracep; int hops = 255; int rhops; int maxhops = 255; @@ -203,9 +366,10 @@ int main(int argc, char *const argv[]) int fd = -1; int ret = -1; int c; + long msec; int i, j; char ifname[IF_NAMESIZE]; - char ip_str[INET_ADDRSTRLEN]; + char mbuf[MTRACE_BUF_LEN]; mtrace_addr.s_addr = inet_addr("224.0.1.32"); @@ -312,8 +476,10 @@ int main(int argc, char *const argv[]) goto close_fd; } printf("Querying full reverse path...\n"); - ret = wait_for_response(fd, NULL); + mtracep = (struct igmp_mtrace *)mbuf; + ret = wait_for_response(fd, &rhops, mtracep, &msec); if (ret > 0) { + print_responses(mtracep, rhops, msec); ret = 0; goto close_fd; } @@ -325,10 +491,9 @@ int main(int argc, char *const argv[]) } printf(" * "); printf("switching to hop-by-hop:\n"); - printf("%3d ? (%s)\n", 0, - inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str))); + print_dest(&mtrace); for (i = 1; i < maxhops; i++) { - printf("%3d ", -i); + print_line_no(i); mtrace.hops = i; for (j = 0; j < perhop; j++) { mtrace.qry_id++; @@ -340,12 +505,20 @@ int main(int argc, char *const argv[]) ret = EXIT_FAILURE; goto close_fd; } - ret = wait_for_response(fd, &rhops); + ret = wait_for_response(fd, &rhops, mtracep, &msec); if (ret > 0) { + if (check_end(mtracep, rhops)) { + print_rsp(&mtracep->rsp[rhops - 1]); + print_summary(mtracep, rhops, msec); + ret = 0; + goto close_fd; + } if (i > rhops) { + printf(" * ...giving up.\n"); ret = 0; goto close_fd; } + print_rsp(&mtracep->rsp[rhops - 1]); break; } printf(" *"); diff --git a/tools/frr-reload.py b/tools/frr-reload.py index 0b7e80962c..208fb116e6 100755 --- a/tools/frr-reload.py +++ b/tools/frr-reload.py @@ -413,7 +413,7 @@ end ctx_keys = [] current_context_lines = [] - elif line == "exit-address-family" or line == "exit" or line == "exit-vni": + elif line in ["exit-address-family", "exit", "exit-vnc", "exit-vni"]: # if this exit is for address-family ipv4 unicast, ignore the pop if main_ctx_key: self.save_contexts(ctx_keys, current_context_lines) @@ -433,26 +433,14 @@ end current_context_lines = [] new_ctx = False log.debug('LINE %-50s: entering new context, %-50s', line, ctx_keys) - - # The 'vni' keyword under 'router bgp X/address-family l2vpn evpn' creates - # a sub-context but the 'vni' keyword in other places (such as 'vrf BLUE') - # does not. - elif ("vni " in line and - len(ctx_keys) == 2 and - ctx_keys[0].startswith('router bgp') and - ctx_keys[1] == 'address-family l2vpn evpn'): - - main_ctx_key = [] - - # Save old context first - self.save_contexts(ctx_keys, current_context_lines) - current_context_lines = [] - main_ctx_key = copy.deepcopy(ctx_keys) - log.debug('LINE %-50s: entering sub-context, append to ctx_keys', line) - - ctx_keys.append(line) - - elif "address-family " in line: + elif (line.startswith("address-family ") or + line.startswith("vnc defaults") or + line.startswith("vnc l2-group") or + line.startswith("vnc nve-group") or + (line.startswith("vni ") and + len(ctx_keys) == 2 and + ctx_keys[0].startswith('router bgp') and + ctx_keys[1] == 'address-family l2vpn evpn')): main_ctx_key = [] # Save old context first diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index efef106d97..556ce27229 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 <ipv4|ipv6>", + "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 5a239306fb..3a66aea45f 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/rib.h b/zebra/rib.h index 5dd444dce0..0ee89e015a 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -447,6 +447,8 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), extern void zebra_vty_init(void); extern int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi, safi_t safi, const char *cmd); +extern void static_config_install_delayed_routes(struct zebra_vrf *zvrf); + extern pid_t pid; #endif /*_ZEBRA_RIB_H */ diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 72dbfb12fc..26dd48733e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -685,7 +685,8 @@ struct route_entry *rib_match_ipv4_multicast(vrf_id_t vrf_id, char buf[BUFSIZ]; inet_ntop(AF_INET, &addr, buf, BUFSIZ); - zlog_debug("%s: %s: found %s, using %s", __func__, buf, + zlog_debug("%s: %s: vrf: %u found %s, using %s", + __func__, buf, vrf_id, mre ? (ure ? "MRIB+URIB" : "MRIB") : ure ? "URIB" : "nothing", re == ure ? "URIB" : re == mre ? "MRIB" : "none"); @@ -1608,9 +1609,9 @@ static void rib_process(struct route_node *rn) if (re != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s: %s: imported via import-table but denied " + "%s: %u:%s: imported via import-table but denied " "by the ip protocol table route-map", - __func__, buf); + __func__, vrf_id, buf); rib_unlink(rn, re); } else SET_FLAG(re->status, @@ -1864,6 +1865,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 }; @@ -2191,9 +2193,9 @@ void _route_entry_dump(const char *func, union prefixconstptr pp, for (ALL_NEXTHOPS(re->ng, nexthop)) { inet_ntop(p->family, &nexthop->gate, straddr, INET6_ADDRSTRLEN); - zlog_debug("%s: %s %s[%u] with flags %s%s%s", func, + zlog_debug("%s: %s %s[%u] vrf %u with flags %s%s%s", func, (nexthop->rparent ? " NH" : "NH"), straddr, - nexthop->ifindex, + nexthop->ifindex, nexthop->vrf_id, (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? "ACTIVE " : ""), @@ -2221,7 +2223,8 @@ void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* Lookup table. */ table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id); if (!table) { - zlog_err("%s: zebra_vrf_table() returned NULL", __func__); + zlog_err("%s:%u zebra_vrf_table() returned NULL", + __func__, vrf_id); return; } @@ -2230,7 +2233,7 @@ void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* No route for this prefix. */ if (!rn) { - zlog_debug("%s: lookup failed for %s", __func__, + zlog_debug("%s:%u lookup failed for %s", __func__, vrf_id, prefix2str((struct prefix *)p, prefix_buf, sizeof(prefix_buf))); return; @@ -2241,8 +2244,9 @@ void rib_lookup_and_dump(struct prefix_ipv4 *p, vrf_id_t vrf_id) /* let's go */ RNODE_FOREACH_RE (rn, re) { - zlog_debug("%s: rn %p, re %p: %s, %s", __func__, (void *)rn, - (void *)re, + zlog_debug("%s:%u rn %p, re %p: %s, %s", + __func__, vrf_id, + (void *)rn, (void *)re, (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) ? "removed" : "NOT removed"), @@ -2266,7 +2270,8 @@ void rib_lookup_and_pushup(struct prefix_ipv4 *p, vrf_id_t vrf_id) rib_dest_t *dest; if (NULL == (table = zebra_vrf_table(AFI_IP, SAFI_UNICAST, vrf_id))) { - zlog_err("%s: zebra_vrf_table() returned NULL", __func__); + zlog_err("%s:%u zebra_vrf_table() returned NULL", + __func__, vrf_id); return; } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 64585c4c1a..f7877f71b6 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -162,6 +162,12 @@ static int zebra_vrf_enable(struct vrf *vrf) } } + /* + * We may have static routes that are now possible to + * insert into the appropriate tables + */ + static_config_install_delayed_routes(zvrf); + /* Kick off any VxLAN-EVPN processing. */ zebra_vxlan_vrf_enable(zvrf); @@ -569,7 +575,6 @@ static int vrf_config_write(struct vty *vty) ? " prefix-routes-only" : ""); zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); - vty_out(vty, "!\n"); } static_config(vty, zvrf, AFI_IP, SAFI_UNICAST, "ip route"); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9fe3c707bb..32d5286d5e 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -29,6 +29,7 @@ #include "rib.h" #include "nexthop.h" #include "vrf.h" +#include "linklist.h" #include "mpls.h" #include "routemap.h" #include "srcdest_table.h" @@ -76,7 +77,186 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, /* VNI range as per RFC 7432 */ #define CMD_VNI_RANGE "(1-16777215)" +struct static_hold_route { + char *vrf_name; + char *nhvrf_name; + afi_t afi; + safi_t safi; + char *dest_str; + char *mask_str; + char *src_str; + char *gate_str; + char *ifname; + char *flag_str; + char *tag_str; + char *distance_str; + char *label_str; +}; + +static struct list *static_list; + +static int static_list_compare_helper(const char *s1, const char *s2) +{ + /* Are Both NULL */ + if (s1 == s2) + return 0; + + if (!s1 && s2) + return -1; + + if (s1 && !s2) + return 1; + + return strcmp(s1, s2); +} + +static void static_list_delete(struct static_hold_route *shr) +{ + if (shr->vrf_name) + XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name); + if (shr->nhvrf_name) + XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name); + if (shr->dest_str) + XFREE(MTYPE_STATIC_ROUTE, shr->dest_str); + if (shr->mask_str) + XFREE(MTYPE_STATIC_ROUTE, shr->mask_str); + if (shr->src_str) + XFREE(MTYPE_STATIC_ROUTE, shr->src_str); + if (shr->gate_str) + XFREE(MTYPE_STATIC_ROUTE, shr->gate_str); + if (shr->ifname) + XFREE(MTYPE_STATIC_ROUTE, shr->ifname); + if (shr->flag_str) + XFREE(MTYPE_STATIC_ROUTE, shr->flag_str); + if (shr->tag_str) + XFREE(MTYPE_STATIC_ROUTE, shr->tag_str); + if (shr->distance_str) + XFREE(MTYPE_STATIC_ROUTE, shr->distance_str); + if (shr->label_str) + XFREE(MTYPE_STATIC_ROUTE, shr->label_str); + + XFREE(MTYPE_STATIC_ROUTE, shr); +} + +static int static_list_compare(void *arg1, void *arg2) +{ + struct static_hold_route *shr1 = arg1; + struct static_hold_route *shr2 = arg2; + int ret; + + ret = strcmp(shr1->vrf_name, shr2->vrf_name); + if (ret) + return ret; + + ret = strcmp(shr2->nhvrf_name, shr2->nhvrf_name); + if (ret) + return ret; + + ret = shr1->afi - shr2->afi; + if (ret) + return ret; + + ret = shr1->safi - shr2->afi; + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->dest_str, shr2->dest_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->mask_str, shr2->mask_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->src_str, shr2->src_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->gate_str, shr2->gate_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->ifname, shr2->ifname); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->flag_str, shr2->flag_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->tag_str, shr2->tag_str); + if (ret) + return ret; + + ret = static_list_compare_helper(shr1->distance_str, + shr2->distance_str); + if (ret) + return ret; + + return static_list_compare_helper(shr1->label_str, shr2->label_str); +} + + /* General function for static route. */ +static int zebra_static_route_holdem(struct zebra_vrf *zvrf, + struct zebra_vrf *nh_zvrf, + afi_t afi, safi_t safi, + const char *negate, const char *dest_str, + const char *mask_str, const char *src_str, + const char *gate_str, const char *ifname, + const char *flag_str, const char *tag_str, + const char *distance_str, + const char *label_str) +{ + struct static_hold_route *shr, *lookup; + struct listnode *node; + + shr = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(*shr)); + shr->vrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, zvrf->vrf->name); + shr->nhvrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, nh_zvrf->vrf->name); + shr->afi = afi; + shr->safi = safi; + if (dest_str) + shr->dest_str = XSTRDUP(MTYPE_STATIC_ROUTE, dest_str); + if (mask_str) + shr->mask_str = XSTRDUP(MTYPE_STATIC_ROUTE, mask_str); + if (src_str) + shr->src_str = XSTRDUP(MTYPE_STATIC_ROUTE, src_str); + if (gate_str) + shr->gate_str = XSTRDUP(MTYPE_STATIC_ROUTE, gate_str); + if (ifname) + shr->ifname = XSTRDUP(MTYPE_STATIC_ROUTE, ifname); + if (flag_str) + shr->flag_str = XSTRDUP(MTYPE_STATIC_ROUTE, flag_str); + if (tag_str) + shr->tag_str = XSTRDUP(MTYPE_STATIC_ROUTE, tag_str); + if (distance_str) + shr->distance_str = XSTRDUP(MTYPE_STATIC_ROUTE, distance_str); + if (label_str) + shr->label_str = XSTRDUP(MTYPE_STATIC_ROUTE, label_str); + + for (ALL_LIST_ELEMENTS_RO(static_list, node, lookup)) { + if (static_list_compare(shr, lookup) == 0) + break; + } + + if (lookup) { + if (negate) { + listnode_delete(static_list, lookup); + static_list_delete(shr); + static_list_delete(lookup); + + return CMD_SUCCESS; + } + + assert(!"We should not have found a duplicate and not remove it"); + } + + listnode_add_sort(static_list, shr); + + return CMD_SUCCESS; +} + static int zebra_static_route_leak( struct vty *vty, struct zebra_vrf *zvrf, struct zebra_vrf *nh_zvrf, afi_t afi, safi_t safi, const char *negate, const char *dest_str, @@ -98,17 +278,34 @@ static int zebra_static_route_leak( ret = str2prefix(dest_str, &p); if (ret <= 0) { - vty_out(vty, "%% Malformed address\n"); + if (vty) + vty_out(vty, "%% Malformed address\n"); + else + zlog_warn("%s: Malformed address: %s", + __PRETTY_FUNCTION__, dest_str); return CMD_WARNING_CONFIG_FAILED; } + if (zvrf->vrf->vrf_id == VRF_UNKNOWN + || nh_zvrf->vrf->vrf_id == VRF_UNKNOWN) { + vrf_set_user_cfged(zvrf->vrf); + return zebra_static_route_holdem( + zvrf, nh_zvrf, afi, safi, negate, dest_str, mask_str, + src_str, gate_str, ifname, flag_str, tag_str, + distance_str, label_str); + } switch (afi) { case AFI_IP: /* Cisco like mask notation. */ if (mask_str) { ret = inet_aton(mask_str, &mask); if (ret == 0) { - vty_out(vty, "%% Malformed address\n"); + if (vty) + vty_out(vty, "%% Malformed address\n"); + else + zlog_warn("%s: Malformed address: %s", + __PRETTY_FUNCTION__, + mask_str); return CMD_WARNING_CONFIG_FAILED; } p.prefixlen = ip_masklen(mask); @@ -119,7 +316,13 @@ static int zebra_static_route_leak( if (src_str) { ret = str2prefix(src_str, &src); if (ret <= 0 || src.family != AF_INET6) { - vty_out(vty, "%% Malformed source address\n"); + if (vty) + vty_out(vty, + "%% Malformed source address\n"); + else + zlog_warn( + "%s: Malformed Source address: %s", + __PRETTY_FUNCTION__, src_str); return CMD_WARNING_CONFIG_FAILED; } src_p = (struct prefix_ipv6 *)&src; @@ -146,8 +349,13 @@ static int zebra_static_route_leak( memset(&snh_label, 0, sizeof(struct static_nh_label)); if (label_str) { if (!mpls_enabled) { - vty_out(vty, - "%% MPLS not turned on in kernel, ignoring command\n"); + if (vty) + vty_out(vty, + "%% MPLS not turned on in kernel, ignoring command\n"); + else + zlog_warn( + "%s: MPLS not turned on in kernel ignoring static route to %s", + __PRETTY_FUNCTION__, dest_str); return CMD_WARNING_CONFIG_FAILED; } int rc = mpls_str2label(label_str, &snh_label.num_labels, @@ -155,18 +363,37 @@ static int zebra_static_route_leak( if (rc < 0) { switch (rc) { case -1: - vty_out(vty, "%% Malformed label(s)\n"); + if (vty) + vty_out(vty, "%% Malformed label(s)\n"); + else + zlog_warn( + "%s: Malformed labels specified for route %s", + __PRETTY_FUNCTION__, dest_str); break; case -2: - vty_out(vty, - "%% Cannot use reserved label(s) (%d-%d)\n", - MPLS_LABEL_RESERVED_MIN, - MPLS_LABEL_RESERVED_MAX); + if (vty) + vty_out(vty, + "%% Cannot use reserved label(s) (%d-%d)\n", + MPLS_LABEL_RESERVED_MIN, + MPLS_LABEL_RESERVED_MAX); + else + zlog_warn( + "%s: Cannot use reserved labels (%d-%d) for %s", + __PRETTY_FUNCTION__, + MPLS_LABEL_RESERVED_MIN, + MPLS_LABEL_RESERVED_MAX, + dest_str); break; case -3: - vty_out(vty, - "%% Too many labels. Enter %d or fewer\n", - MPLS_MAX_LABELS); + if (vty) + vty_out(vty, + "%% Too many labels. Enter %d or fewer\n", + MPLS_MAX_LABELS); + else + zlog_warn( + "%s: Too many labels, Enter %d or fewer for %s", + __PRETTY_FUNCTION__, + MPLS_MAX_LABELS, dest_str); break; } return CMD_WARNING_CONFIG_FAILED; @@ -178,8 +405,13 @@ static int zebra_static_route_leak( if (strncasecmp(ifname, "Null0", strlen(ifname)) == 0 || strncasecmp(ifname, "reject", strlen(ifname)) == 0 || strncasecmp(ifname, "blackhole", strlen(ifname)) == 0) { - vty_out(vty, - "%% Nexthop interface cannot be Null0, reject or blackhole\n"); + if (vty) + vty_out(vty, + "%% Nexthop interface cannot be Null0, reject or blackhole\n"); + else + zlog_warn( + "%s: Nexthop interface cannot be Null0, reject or blackhole for %s", + __PRETTY_FUNCTION__, dest_str); return CMD_WARNING_CONFIG_FAILED; } } @@ -197,15 +429,28 @@ static int zebra_static_route_leak( bh_type = STATIC_BLACKHOLE_NULL; break; default: - vty_out(vty, "%% Malformed flag %s \n", flag_str); + if (vty) + vty_out(vty, "%% Malformed flag %s \n", + flag_str); + else + zlog_warn("%s: Malformed flag %s for %s", + __PRETTY_FUNCTION__, flag_str, + dest_str); return CMD_WARNING_CONFIG_FAILED; } } if (gate_str) { if (inet_pton(afi2family(afi), gate_str, &gate) != 1) { - vty_out(vty, "%% Malformed nexthop address %s\n", - gate_str); + if (vty) + vty_out(vty, + "%% Malformed nexthop address %s\n", + gate_str); + else + zlog_warn( + "%s: Malformed nexthop address %s for %s", + __PRETTY_FUNCTION__, gate_str, + dest_str); return CMD_WARNING_CONFIG_FAILED; } gatep = &gate; @@ -287,7 +532,38 @@ static int zebra_static_route(struct vty *vty, afi_t afi, safi_t safi, gate_str, ifname, flag_str, tag_str, distance_str, label_str); } +void static_config_install_delayed_routes(struct zebra_vrf *zvrf) +{ + struct listnode *node, *nnode; + struct static_hold_route *shr; + struct zebra_vrf *ozvrf, *nh_zvrf; + int installed; + + for (ALL_LIST_ELEMENTS(static_list, node, nnode, shr)) { + ozvrf = zebra_vrf_lookup_by_name(shr->vrf_name); + nh_zvrf = zebra_vrf_lookup_by_name(shr->nhvrf_name); + + if (ozvrf != zvrf && nh_zvrf != zvrf) + continue; + + if (ozvrf->vrf->vrf_id == VRF_UNKNOWN + || nh_zvrf->vrf->vrf_id == VRF_UNKNOWN) + continue; + installed = zebra_static_route_leak( + NULL, ozvrf, nh_zvrf, shr->afi, shr->safi, NULL, + shr->dest_str, shr->mask_str, shr->src_str, + shr->gate_str, shr->ifname, shr->flag_str, shr->tag_str, + shr->distance_str, shr->label_str); + + if (installed != CMD_SUCCESS) + zlog_debug( + "%s: Attempt to install %s as a route and it was rejected", + __PRETTY_FUNCTION__, shr->dest_str); + listnode_delete(static_list, shr); + static_list_delete(shr); + } +} /* Static unicast routes for multicast RPF lookup. */ DEFPY (ip_mroute_dist, ip_mroute_dist_cmd, @@ -1894,6 +2170,8 @@ static void vty_show_ip_route_summary_prefix(struct vty *vty, int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi, safi_t safi, const char *cmd) { + struct static_hold_route *shr; + struct listnode *node; char spacing[100]; struct route_node *rn; struct static_route *si; @@ -1907,6 +2185,40 @@ int static_config(struct vty *vty, struct zebra_vrf *zvrf, afi_t afi, sprintf(spacing, "%s%s", (zvrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); + /* + * Static routes for vrfs not fully inited + */ + for (ALL_LIST_ELEMENTS_RO(static_list, node, shr)) { + if (shr->afi != afi || shr->safi != safi) + continue; + + if (strcmp(zvrf->vrf->name, shr->vrf_name) != 0) + continue; + + vty_out(vty, "%s ", spacing); + if (shr->dest_str) + vty_out(vty, "%s ", shr->dest_str); + if (shr->mask_str) + vty_out(vty, "%s ", shr->mask_str); + if (shr->src_str) + vty_out(vty, "from %s ", shr->src_str); + if (shr->gate_str) + vty_out(vty, "%s ", shr->gate_str); + if (shr->ifname) + vty_out(vty, "%s ", shr->ifname); + if (shr->flag_str) + vty_out(vty, "%s ", shr->flag_str); + if (shr->tag_str) + vty_out(vty, "tag %s", shr->tag_str); + if (shr->distance_str) + vty_out(vty, "%s ", shr->distance_str); + if (shr->label_str) + vty_out(vty, "label %s ", shr->label_str); + if (strcmp(shr->vrf_name, shr->nhvrf_name) != 0) + vty_out(vty, "nexthop-vrf %s", shr->nhvrf_name); + vty_out(vty, "\n"); + } + for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) for (si = rn->info; si; si = si->next) { vty_out(vty, "%s %s", spacing, @@ -3409,4 +3721,8 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &no_default_vrf_vni_mapping_cmd); install_element(VRF_NODE, &vrf_vni_mapping_cmd); install_element(VRF_NODE, &no_vrf_vni_mapping_cmd); + + static_list = list_new(); + static_list->cmp = (int (*)(void *, void *))static_list_compare; + static_list->del = (void (*)(void *))static_list_delete; } diff --git a/zebra/zserv.c b/zebra/zserv.c index 0def903803..57b1be4b8b 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); } @@ -663,9 +674,9 @@ int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, char buff[PREFIX_STRLEN]; zlog_debug( - "Not Notifying Owner: %u about prefix %s(%u) %d", + "Not Notifying Owner: %u about prefix %s(%u) %d vrf: %u", re->type, prefix2str(p, buff, sizeof(buff)), - re->table, note); + re->table, note, re->vrf_id); } return 0; } @@ -673,9 +684,9 @@ int zsend_route_notify_owner(struct route_entry *re, struct prefix *p, if (IS_ZEBRA_DEBUG_PACKET) { char buff[PREFIX_STRLEN]; - zlog_debug("Notifying Owner: %u about prefix %s(%u) %d", + zlog_debug("Notifying Owner: %u about prefix %s(%u) %d vrf: %u", re->type, prefix2str(p, buff, sizeof(buff)), - re->table, note); + re->table, note, re->vrf_id); } s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -849,9 +860,10 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_NHT) zlog_debug( - "rnh_register msg from client %s: hdr->length=%d, type=%s\n", + "rnh_register msg from client %s: hdr->length=%d, type=%s vrf=%u\n", zebra_route_string(client->proto), hdr->length, - (type == RNH_NEXTHOP_TYPE) ? "nexthop" : "route"); + (type == RNH_NEXTHOP_TYPE) ? "nexthop" : "route", + zvrf->vrf->vrf_id); s = msg; @@ -924,8 +936,9 @@ static void zread_rnh_unregister(ZAPI_HANDLER_ARGS) if (IS_ZEBRA_DEBUG_NHT) zlog_debug( - "rnh_unregister msg from client %s: hdr->length=%d\n", - zebra_route_string(client->proto), hdr->length); + "rnh_unregister msg from client %s: hdr->length=%d vrf: %u\n", + zebra_route_string(client->proto), hdr->length, + zvrf->vrf->vrf_id); s = msg; @@ -1149,6 +1162,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 +1185,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 +1227,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 +1314,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]); @@ -1997,8 +2054,9 @@ static void zread_hello(ZAPI_HANDLER_ARGS) /* accept only dynamic routing protocols */ if ((proto < ZEBRA_ROUTE_MAX) && (proto > ZEBRA_ROUTE_STATIC)) { zlog_notice( - "client %d says hello and bids fair to announce only %s routes", - client->sock, zebra_route_string(proto)); + "client %d says hello and bids fair to announce only %s routes vrf=%u", + client->sock, zebra_route_string(proto), + zvrf->vrf->vrf_id); if (instance) zlog_notice("client protocol instance %d", instance); @@ -2130,8 +2188,8 @@ static void zread_label_manager_connect(struct zserv *client, zsend_label_manager_connect_response(client, vrf_id, 1); return; } - zlog_notice("client %d with instance %u connected as %s", client->sock, - instance, zebra_route_string(proto)); + zlog_notice("client %d with vrf %u instance %u connected as %s", + client->sock, vrf_id, instance, zebra_route_string(proto)); client->proto = proto; client->instance = instance; @@ -2142,8 +2200,8 @@ static void zread_label_manager_connect(struct zserv *client, release_daemon_chunks(proto, instance); zlog_debug( - " Label Manager client connected: sock %d, proto %s, instance %u", - client->sock, zebra_route_string(proto), instance); + " Label Manager client connected: sock %d, proto %s, vrf %u instance %u", + client->sock, zebra_route_string(proto), vrf_id, instance); /* send response back */ zsend_label_manager_connect_response(client, vrf_id, 0); |
