diff options
51 files changed, 1399 insertions, 152 deletions
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 5007fafc29..4963ea64d0 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -348,6 +348,7 @@ struct attr { #define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5) #define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6) #define BATTR_RMAP_LINK_BW_SET (1 << 7) +#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8) /* Router Reflector related structure. */ struct cluster_list { diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 1297eb440e..90ae580bab 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -511,6 +511,8 @@ int main(int argc, char **argv) ", bgp@%s:%d", address, bm->port); } + bgp_if_init(); + frr_config_fork(); /* must be called after fork() */ bgp_gr_apply_running_config(); diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 2f1cc4dc82..5a039b25bc 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -978,6 +978,11 @@ leak_update(struct bgp *to_bgp, struct bgp_dest *bn, new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0, to_bgp->peer_self, new_attr, bn); + if (source_bpi->peer) { + extra = bgp_path_info_extra_get(new); + extra->peer_orig = peer_lock(source_bpi->peer); + } + if (nexthop_self_flag) bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF); diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 61f1b295ca..b38e5b7a9a 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -61,22 +61,88 @@ static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc) && bnc->nexthop_num > 0)); } -static int bgp_isvalid_labeled_nexthop(struct bgp_nexthop_cache *bnc) +static int bgp_isvalid_nexthop_for_ebgp(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) +{ + struct interface *ifp = NULL; + struct nexthop *nexthop; + struct bgp_interface *iifp; + struct peer *peer; + + if (!path->extra || !path->extra->peer_orig) + return false; + + peer = path->extra->peer_orig; + + /* only connected ebgp peers are valid */ + if (peer->sort != BGP_PEER_EBGP || peer->ttl != BGP_DEFAULT_TTL || + CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || + CHECK_FLAG(peer->bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) + return false; + + for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop->type == NEXTHOP_TYPE_IFINDEX || + nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX || + nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) { + ifp = if_lookup_by_index( + bnc->ifindex ? bnc->ifindex : nexthop->ifindex, + bnc->bgp->vrf_id); + } + if (!ifp) + continue; + iifp = ifp->info; + if (CHECK_FLAG(iifp->flags, BGP_INTERFACE_MPLS_BGP_FORWARDING)) + return true; + } + return false; +} + +static int bgp_isvalid_nexthop_for_mplsovergre(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) +{ + struct interface *ifp = NULL; + struct nexthop *nexthop; + + for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) { + if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) { + ifp = if_lookup_by_index( + bnc->ifindex ? bnc->ifindex : nexthop->ifindex, + bnc->bgp->vrf_id); + if (ifp && (ifp->ll_type == ZEBRA_LLT_IPGRE || + ifp->ll_type == ZEBRA_LLT_IP6GRE)) + break; + } + } + if (!ifp) + return false; + + if (CHECK_FLAG(path->attr->rmap_change_flags, + BATTR_RMAP_L3VPN_ACCEPT_GRE)) + return true; + + return false; +} + +static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc, + struct bgp_path_info *path) { /* - * In the case of MPLS-VPN, the label is learned from LDP or other + * - In the case of MPLS-VPN, the label is learned from LDP or other * protocols, and nexthop tracking is enabled for the label. * The value is recorded as BGP_NEXTHOP_LABELED_VALID. - * In the case of SRv6-VPN, we need to track the reachability to the + * - In the case of SRv6-VPN, we need to track the reachability to the * SID (in other words, IPv6 address). As in MPLS, we need to record * the value as BGP_NEXTHOP_SID_VALID. However, this function is * currently not implemented, and this function assumes that all * Transit routes for SRv6-VPN are valid. + * - Otherwise check for mpls-gre acceptance */ - return (bgp_zebra_num_connects() == 0 - || (bnc && bnc->nexthop_num > 0 - && (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) - || bnc->bgp->srv6_enabled))); + return (bgp_zebra_num_connects() == 0 || + (bnc && (bnc->nexthop_num > 0 && + (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) || + bnc->bgp->srv6_enabled || + bgp_isvalid_nexthop_for_ebgp(bnc, path) || + bgp_isvalid_nexthop_for_mplsovergre(bnc, path))))); } static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc) @@ -359,11 +425,11 @@ int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop, */ if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) return 1; - else if (safi == SAFI_UNICAST && pi - && pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra - && pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) { - return bgp_isvalid_labeled_nexthop(bnc); - } else + else if (safi == SAFI_UNICAST && pi && + pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra && + pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop) + return bgp_isvalid_nexthop_for_mpls(bnc, pi); + else return (bgp_isvalid_nexthop(bnc)); } @@ -1063,7 +1129,8 @@ void evaluate_paths(struct bgp_nexthop_cache *bnc) && (path->attr->evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP)) { bnc_is_valid_nexthop = - bgp_isvalid_labeled_nexthop(bnc) ? true : false; + bgp_isvalid_nexthop_for_mpls(bnc, path) ? true + : false; } else { if (bgp_update_martian_nexthop( bnc->bgp, afi, safi, path->type, diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index f042d0bd95..e7fac8153c 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -255,6 +255,9 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) if (e->bgp_orig) bgp_unlock(e->bgp_orig); + if (e->peer_orig) + peer_unlock(e->peer_orig); + if (e->aggr_suppressors) list_delete(&e->aggr_suppressors); @@ -8462,6 +8465,17 @@ void bgp_redistribute_add(struct bgp *bgp, struct prefix *p, switch (nhtype) { case NEXTHOP_TYPE_IFINDEX: + switch (p->family) { + case AF_INET: + attr.nexthop.s_addr = INADDR_ANY; + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; + break; + case AF_INET6: + memset(&attr.mp_nexthop_global, 0, + sizeof(attr.mp_nexthop_global)); + attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; + break; + } break; case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 3e46c7043e..ddef4ca1bb 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -236,6 +236,12 @@ struct bgp_path_info_extra { struct bgp *bgp_orig; /* + * Original bgp session to know if the session is a + * connected EBGP session or not + */ + struct peer *peer_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. diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index 33f68c9e88..40bbdccff4 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -1953,6 +1953,57 @@ static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = { route_set_ip_nexthop_free }; +/* `set l3vpn next-hop encapsulation l3vpn gre' */ + +/* Set nexthop to object */ +struct rmap_l3vpn_nexthop_encapsulation_set { + uint8_t protocol; +}; + +static enum route_map_cmd_result_t +route_set_l3vpn_nexthop_encapsulation(void *rule, const struct prefix *prefix, + void *object) +{ + struct rmap_l3vpn_nexthop_encapsulation_set *rins = rule; + struct bgp_path_info *path; + + path = object; + + if (rins->protocol != IPPROTO_GRE) + return RMAP_OKAY; + + SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_L3VPN_ACCEPT_GRE); + return RMAP_OKAY; +} + +/* Route map `l3vpn nexthop encapsulation' compile function. */ +static void *route_set_l3vpn_nexthop_encapsulation_compile(const char *arg) +{ + struct rmap_l3vpn_nexthop_encapsulation_set *rins; + + rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, + sizeof(struct rmap_l3vpn_nexthop_encapsulation_set)); + + /* XXX ALL GRE modes are accepted for now: gre or ip6gre */ + rins->protocol = IPPROTO_GRE; + + return rins; +} + +/* Free route map's compiled `ip nexthop' value. */ +static void route_set_l3vpn_nexthop_encapsulation_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Route map commands for l3vpn next-hop encapsulation set. */ +static const struct route_map_rule_cmd + route_set_l3vpn_nexthop_encapsulation_cmd = { + "l3vpn next-hop encapsulation", + route_set_l3vpn_nexthop_encapsulation, + route_set_l3vpn_nexthop_encapsulation_compile, + route_set_l3vpn_nexthop_encapsulation_free}; + /* `set local-preference LOCAL_PREF' */ /* Set local preference. */ @@ -5290,6 +5341,34 @@ DEFUN_YANG (no_set_distance, return nb_cli_apply_changes(vty, NULL); } +DEFPY_YANG(set_l3vpn_nexthop_encapsulation, set_l3vpn_nexthop_encapsulation_cmd, + "[no] set l3vpn next-hop encapsulation gre", + NO_STR SET_STR + "L3VPN operations\n" + "Next hop Information\n" + "Encapsulation options (for BGP only)\n" + "Accept L3VPN traffic over GRE encapsulation\n") +{ + const char *xpath = + "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']"; + const char *xpath_value = + "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation"; + enum nb_operation operation; + + if (no) + operation = NB_OP_DESTROY; + else + operation = NB_OP_CREATE; + + nb_cli_enqueue_change(vty, xpath, operation, NULL); + if (operation == NB_OP_DESTROY) + return nb_cli_apply_changes(vty, NULL); + + nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "gre"); + + return nb_cli_apply_changes(vty, NULL); +} + DEFUN_YANG (set_local_pref, set_local_pref_cmd, "set local-preference WORD", @@ -6835,6 +6914,7 @@ void bgp_route_map_init(void) route_map_install_set(&route_set_ecommunity_none_cmd); route_map_install_set(&route_set_tag_cmd); route_map_install_set(&route_set_label_index_cmd); + route_map_install_set(&route_set_l3vpn_nexthop_encapsulation_cmd); install_element(RMAP_NODE, &match_peer_cmd); install_element(RMAP_NODE, &match_peer_local_cmd); @@ -6937,6 +7017,7 @@ void bgp_route_map_init(void) install_element(RMAP_NODE, &no_set_ipx_vpn_nexthop_cmd); install_element(RMAP_NODE, &set_originator_id_cmd); install_element(RMAP_NODE, &no_set_originator_id_cmd); + install_element(RMAP_NODE, &set_l3vpn_nexthop_encapsulation_cmd); route_map_install_match(&route_match_ipv6_address_cmd); route_map_install_match(&route_match_ipv6_next_hop_cmd); diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c index 585596e1aa..2117334f7f 100644 --- a/bgpd/bgp_routemap_nb.c +++ b/bgpd/bgp_routemap_nb.c @@ -407,6 +407,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = { } }, { + .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation", + .cbs = { + .modify = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify, + .destroy = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy, + } + }, + { .xpath = NULL, }, } diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h index a01adf7d5d..cd7a70dbcf 100644 --- a/bgpd/bgp_routemap_nb.h +++ b/bgpd/bgp_routemap_nb.h @@ -150,6 +150,10 @@ int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify( struct nb_cb_modify_args *args); int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( struct nb_cb_destroy_args *args); +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify( + struct nb_cb_modify_args *args); +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy( + struct nb_cb_destroy_args *args); #ifdef __cplusplus } diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c index b87877b1e0..585c2a3ff0 100644 --- a/bgpd/bgp_routemap_nb_config.c +++ b/bgpd/bgp_routemap_nb_config.c @@ -2922,3 +2922,56 @@ int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy( return NB_OK; } + +/* + * XPath: + * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/l3vpn-nexthop-encapsulation + */ +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify( + struct nb_cb_modify_args *args) +{ + struct routemap_hook_context *rhc; + const char *type; + int rv; + + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + /* Add configuration. */ + rhc = nb_running_get_entry(args->dnode, NULL, true); + type = yang_dnode_get_string(args->dnode, NULL); + + /* Set destroy information. */ + rhc->rhc_shook = generic_set_delete; + rhc->rhc_rule = "l3vpn next-hop encapsulation"; + rhc->rhc_event = RMAP_EVENT_SET_DELETED; + + rv = generic_set_add(rhc->rhc_rmi, + "l3vpn next-hop encapsulation", type, + args->errmsg, args->errmsg_len); + if (rv != CMD_SUCCESS) { + rhc->rhc_shook = NULL; + return NB_ERR_INCONSISTENCY; + } + } + + return NB_OK; +} + +int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy( + struct nb_cb_destroy_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + break; + case NB_EV_APPLY: + return lib_route_map_entry_set_destroy(args); + } + + return NB_OK; +} diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c index d713192d00..d198aec983 100644 --- a/bgpd/bgp_updgrp.c +++ b/bgpd/bgp_updgrp.c @@ -442,15 +442,16 @@ static unsigned int updgrp_hash_key_make(const void *p) if (bgp_debug_neighbor_events(peer)) { zlog_debug( - "%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %u", + "%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %ju", peer, peer->sort, - (intmax_t)(peer->flags & PEER_UPDGRP_FLAGS), - flags & PEER_UPDGRP_AF_FLAGS); + (intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS), + (intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS)); zlog_debug( "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u", peer, (uint32_t)peer->addpath_type[afi][safi], - peer->cap & PEER_UPDGRP_CAP_FLAGS, - peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS, + CHECK_FLAG(peer->cap, PEER_UPDGRP_CAP_FLAGS), + CHECK_FLAG(peer->af_cap[afi][safi], + PEER_UPDGRP_AF_CAP_FLAGS), peer->v_routeadv, peer->change_local_as); zlog_debug( "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s", @@ -484,14 +485,14 @@ static unsigned int updgrp_hash_key_make(const void *p) peer->shared_network && peer_afi_active_nego(peer, AFI_IP6)); zlog_debug( - "%pBP Update Group Hash: Lonesoul: %d ORF prefix: %u ORF old: %u max prefix out: %u", + "%pBP Update Group Hash: Lonesoul: %d ORF prefix: %u ORF old: %u max prefix out: %ju", peer, !!CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL), CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV), CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_OLD_RCV), - CHECK_FLAG(peer->af_flags[afi][safi], - PEER_FLAG_MAX_PREFIX_OUT)); + (intmax_t)CHECK_FLAG(peer->af_flags[afi][safi], + PEER_FLAG_MAX_PREFIX_OUT)); zlog_debug("%pBP Update Group Hash key: %u", peer, key); } return key; diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 881426d9c4..d84d0919f7 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -53,6 +53,7 @@ #include "bgpd/bgp_debug.h" #include "bgpd/bgp_errors.h" #include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_nht.h" #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_open.h" @@ -16507,7 +16508,7 @@ static bool peergroup_flag_check(struct peer *peer, uint64_t flag) } static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) + uint64_t flag) { if (!peer_group_active(peer)) { if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag)) @@ -18124,6 +18125,84 @@ static void bgp_config_end(void) bgp_post_config_delay, &t_bgp_cfg); } +static int config_write_interface_one(struct vty *vty, struct vrf *vrf) +{ + int write = 0; + struct interface *ifp; + struct bgp_interface *iifp; + + FOR_ALL_INTERFACES (vrf, ifp) { + iifp = ifp->info; + if (!iifp) + continue; + + if_vty_config_start(vty, ifp); + + if (CHECK_FLAG(iifp->flags, + BGP_INTERFACE_MPLS_BGP_FORWARDING)) { + vty_out(vty, " mpls bgp forwarding\n"); + write++; + } + + if_vty_config_end(vty); + } + + return write; +} + +/* Configuration write function for bgpd. */ +static int config_write_interface(struct vty *vty) +{ + int write = 0; + struct vrf *vrf = NULL; + + /* Display all VRF aware OSPF interface configuration */ + RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { + write += config_write_interface_one(vty, vrf); + } + + return write; +} + +DEFPY(mpls_bgp_forwarding, mpls_bgp_forwarding_cmd, + "[no$no] mpls bgp forwarding", + NO_STR MPLS_STR BGP_STR + "Enable MPLS forwarding for eBGP directly connected peers\n") +{ + bool check; + struct bgp_interface *iifp; + + VTY_DECLVAR_CONTEXT(interface, ifp); + iifp = ifp->info; + if (!iifp) { + vty_out(vty, "Interface %s not available\n", ifp->name); + return CMD_WARNING_CONFIG_FAILED; + } + check = CHECK_FLAG(iifp->flags, BGP_INTERFACE_MPLS_BGP_FORWARDING); + if (check != !no) { + if (no) + UNSET_FLAG(iifp->flags, + BGP_INTERFACE_MPLS_BGP_FORWARDING); + else + SET_FLAG(iifp->flags, + BGP_INTERFACE_MPLS_BGP_FORWARDING); + /* trigger a nht update on eBGP sessions */ + if (if_is_operative(ifp)) + bgp_nht_ifp_up(ifp); + } + return CMD_SUCCESS; +} + +/* Initialization of BGP interface. */ +static void bgp_vty_if_init(void) +{ + /* Install interface node. */ + if_cmd_init(config_write_interface); + + /* "mpls bgp forwarding" commands. */ + install_element(INTERFACE_NODE, &mpls_bgp_forwarding_cmd); +} + void bgp_vty_init(void) { cmd_variable_handler_register(bgp_var_neighbor); @@ -19581,6 +19660,8 @@ void bgp_vty_init(void) install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd); install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd); install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd); + + bgp_vty_if_init(); } #include "memory.h" diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 85e25b88a4..7dfb5046dd 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -75,6 +75,8 @@ struct zclient *zclient = NULL; DEFINE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp), (bgp, ifp)); +DEFINE_MTYPE_STATIC(BGPD, BGP_IF_INFO, "BGP interface context"); + /* Can we install into zebra? */ static inline bool bgp_install_info_to_zebra(struct bgp *bgp) { @@ -3335,6 +3337,31 @@ static zclient_handler *const bgp_handlers[] = { bgp_zebra_process_srv6_locator_chunk, }; +static int bgp_if_new_hook(struct interface *ifp) +{ + struct bgp_interface *iifp; + + if (ifp->info) + return 0; + iifp = XCALLOC(MTYPE_BGP_IF_INFO, sizeof(struct bgp_interface)); + ifp->info = iifp; + + return 0; +} + +static int bgp_if_delete_hook(struct interface *ifp) +{ + XFREE(MTYPE_BGP_IF_INFO, ifp->info); + return 0; +} + +void bgp_if_init(void) +{ + /* Initialize Zebra interface data structure. */ + hook_register_prio(if_add, 0, bgp_if_new_hook); + hook_register_prio(if_del, 0, bgp_if_delete_hook); +} + void bgp_zebra_init(struct thread_master *master, unsigned short instance) { zclient_num_connects = 0; diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 17f46e49cc..0a41069411 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -35,6 +35,7 @@ extern void bgp_zebra_init(struct thread_master *master, unsigned short instance); +extern void bgp_if_init(void); extern void bgp_zebra_init_tm_connect(struct bgp *bgp); extern uint32_t bgp_zebra_tm_get_id(void); extern bool bgp_zebra_tm_chunk_obtained(void); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 9a9e287da9..a889a737eb 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -969,7 +969,7 @@ int peer_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag) } void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag) + uint64_t flag) { bool group_val; @@ -2002,8 +2002,8 @@ static void peer_group2peer_config_copy_af(struct peer_group *group, { int in = FILTER_IN; int out = FILTER_OUT; - uint32_t flags_tmp; - uint32_t pflags_ovrd; + uint64_t flags_tmp; + uint64_t pflags_ovrd; uint8_t *pfilter_ovrd; struct peer *conf; @@ -4579,7 +4579,7 @@ int peer_flag_unset(struct peer *peer, uint64_t flag) } static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag, bool set) + uint64_t flag, bool set) { int found; int size; @@ -4762,12 +4762,12 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi, return 0; } -int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag) +int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag) { return peer_af_flag_modify(peer, afi, safi, flag, 1); } -int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag) +int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag) { return peer_af_flag_modify(peer, afi, safi, flag, 0); } diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 1d04ccee42..28883c9e7c 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -778,6 +778,11 @@ struct bgp { }; DECLARE_QOBJ_TYPE(bgp); +struct bgp_interface { +#define BGP_INTERFACE_MPLS_BGP_FORWARDING (1 << 0) + uint32_t flags; +}; + DECLARE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp)); DECLARE_HOOK(bgp_inst_config_write, (struct bgp *bgp, struct vty *vty), @@ -1380,38 +1385,38 @@ struct peer { * specific attributes are being treated the exact same way as global * peer attributes. */ - uint32_t af_flags_override[AFI_MAX][SAFI_MAX]; - uint32_t af_flags_invert[AFI_MAX][SAFI_MAX]; - uint32_t af_flags[AFI_MAX][SAFI_MAX]; -#define PEER_FLAG_SEND_COMMUNITY (1U << 0) /* send-community */ -#define PEER_FLAG_SEND_EXT_COMMUNITY (1U << 1) /* send-community ext. */ -#define PEER_FLAG_NEXTHOP_SELF (1U << 2) /* next-hop-self */ -#define PEER_FLAG_REFLECTOR_CLIENT (1U << 3) /* reflector-client */ -#define PEER_FLAG_RSERVER_CLIENT (1U << 4) /* route-server-client */ -#define PEER_FLAG_SOFT_RECONFIG (1U << 5) /* soft-reconfiguration */ -#define PEER_FLAG_AS_PATH_UNCHANGED (1U << 6) /* transparent-as */ -#define PEER_FLAG_NEXTHOP_UNCHANGED (1U << 7) /* transparent-next-hop */ -#define PEER_FLAG_MED_UNCHANGED (1U << 8) /* transparent-next-hop */ -#define PEER_FLAG_DEFAULT_ORIGINATE (1U << 9) /* default-originate */ -#define PEER_FLAG_REMOVE_PRIVATE_AS (1U << 10) /* remove-private-as */ -#define PEER_FLAG_ALLOWAS_IN (1U << 11) /* set allowas-in */ -#define PEER_FLAG_ORF_PREFIX_SM (1U << 12) /* orf capability send-mode */ -#define PEER_FLAG_ORF_PREFIX_RM (1U << 13) /* orf capability receive-mode */ -#define PEER_FLAG_MAX_PREFIX (1U << 14) /* maximum prefix */ -#define PEER_FLAG_MAX_PREFIX_WARNING (1U << 15) /* maximum prefix warning-only */ -#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1U << 16) /* leave link-local nexthop unchanged */ -#define PEER_FLAG_FORCE_NEXTHOP_SELF (1U << 17) /* next-hop-self force */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1U << 18) /* remove-private-as all */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1U << 19) /* remove-private-as replace-as */ -#define PEER_FLAG_AS_OVERRIDE (1U << 20) /* as-override */ -#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1U << 21) /* remove-private-as all replace-as */ -#define PEER_FLAG_WEIGHT (1U << 24) /* weight */ -#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1U << 25) /* allowas-in origin */ -#define PEER_FLAG_SEND_LARGE_COMMUNITY (1U << 26) /* Send large Communities */ -#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */ -#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */ -#define PEER_FLAG_DISABLE_ADDPATH_RX (1U << 29) /* disable-addpath-rx */ -#define PEER_FLAG_SOO (1U << 30) /* soo */ + uint64_t af_flags_override[AFI_MAX][SAFI_MAX]; + uint64_t af_flags_invert[AFI_MAX][SAFI_MAX]; + uint64_t af_flags[AFI_MAX][SAFI_MAX]; +#define PEER_FLAG_SEND_COMMUNITY (1ULL << 0) +#define PEER_FLAG_SEND_EXT_COMMUNITY (1ULL << 1) +#define PEER_FLAG_NEXTHOP_SELF (1ULL << 2) +#define PEER_FLAG_REFLECTOR_CLIENT (1ULL << 3) +#define PEER_FLAG_RSERVER_CLIENT (1ULL << 4) +#define PEER_FLAG_SOFT_RECONFIG (1ULL << 5) +#define PEER_FLAG_AS_PATH_UNCHANGED (1ULL << 6) +#define PEER_FLAG_NEXTHOP_UNCHANGED (1ULL << 7) +#define PEER_FLAG_MED_UNCHANGED (1ULL << 8) +#define PEER_FLAG_DEFAULT_ORIGINATE (1ULL << 9) +#define PEER_FLAG_REMOVE_PRIVATE_AS (1ULL << 10) +#define PEER_FLAG_ALLOWAS_IN (1ULL << 11) +#define PEER_FLAG_ORF_PREFIX_SM (1ULL << 12) +#define PEER_FLAG_ORF_PREFIX_RM (1ULL << 13) +#define PEER_FLAG_MAX_PREFIX (1ULL << 14) +#define PEER_FLAG_MAX_PREFIX_WARNING (1ULL << 15) +#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1ULL << 16) +#define PEER_FLAG_FORCE_NEXTHOP_SELF (1ULL << 17) +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1ULL << 18) +#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1ULL << 19) +#define PEER_FLAG_AS_OVERRIDE (1ULL << 20) +#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1ULL << 21) +#define PEER_FLAG_WEIGHT (1ULL << 24) +#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1ULL << 25) +#define PEER_FLAG_SEND_LARGE_COMMUNITY (1ULL << 26) +#define PEER_FLAG_MAX_PREFIX_OUT (1ULL << 27) +#define PEER_FLAG_MAX_PREFIX_FORCE (1ULL << 28) +#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29) +#define PEER_FLAG_SOO (1ULL << 30) enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; @@ -2180,11 +2185,13 @@ extern int peer_flag_set(struct peer *peer, uint64_t flag); extern int peer_flag_unset(struct peer *peer, uint64_t flag); extern void peer_flag_inherit(struct peer *peer, uint64_t flag); -extern int peer_af_flag_set(struct peer *, afi_t, safi_t, uint32_t); -extern int peer_af_flag_unset(struct peer *, afi_t, safi_t, uint32_t); +extern int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi, + uint64_t flag); +extern int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, + uint64_t flag); extern int peer_af_flag_check(struct peer *, afi_t, safi_t, uint32_t); extern void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi, - uint32_t flag); + uint64_t flag); extern void peer_change_action(struct peer *peer, afi_t afi, safi_t safi, enum peer_change_type type); diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 033b753639..bd5767beba 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2720,6 +2720,25 @@ are reached using *core* MPLS labels which are distributed using LDP or BGP labeled unicast. *bgpd* also supports inter-VRF route leaking. +L3VPN over GRE interfaces +^^^^^^^^^^^^^^^^^^^^^^^^^ + +In MPLS-VPN or SRv6-VPN, an L3VPN next-hop entry requires that the path +chosen respectively contains a labelled path or a valid SID IPv6 address. +Otherwise the L3VPN entry will not be installed. It is possible to ignore +that check when the path chosen by the next-hop uses a GRE interface, and +there is a route-map configured at inbound side of ipv4-vpn or ipv6-vpn +address family with following syntax: + +.. clicmd:: set l3vpn next-hop encapsulation gre + +The incoming BGP L3VPN entry is accepted, provided that the next hop of the +L3VPN entry uses a path that takes the GRE tunnel as outgoing interface. The +remote endpoint should be configured just behind the GRE tunnel; remote +device configuration may vary depending whether it acts at edge endpoint or +not: in any case, the expectation is that incoming MPLS traffic received at +this endpoint should be considered as a valid path for L3VPN. + .. _bgp-vrf-route-leaking: VRF Route Leaking @@ -2848,6 +2867,13 @@ added for all routes from the CPEs. Routes are validated and prevented from being sent back to the same CPE (e.g.: multi-site). This is especially needed when using ``as-override`` or ``allowas-in`` to prevent routing loops. +.. clicmd:: mpls bgp forwarding + +It is possible to permit BGP install VPN prefixes without transport labels, +by issuing the following command under the interface configuration context. +This configuration will install VPN prefixes originated from an e-bgp session, +and with the next-hop directly connected. + .. _bgp-l3vpn-srv6: L3VPN SRv6 diff --git a/doc/user/routemap.rst b/doc/user/routemap.rst index 05c9eeb755..5e222576ca 100644 --- a/doc/user/routemap.rst +++ b/doc/user/routemap.rst @@ -339,6 +339,9 @@ Route Map Set Command Set the color of a SR-TE Policy to be applied to a learned route. The SR-TE Policy is uniquely determined by the color and the BGP nexthop. +.. clicmd:: set l3vpn next-hop encapsulation gre + + Accept L3VPN traffic over GRE encapsulation. .. _route-map-call-command: diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 178fbe7903..dcc4ed6e42 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -217,6 +217,11 @@ void isis_circuit_del(struct isis_circuit *circuit) list_delete(&circuit->ipv6_link); list_delete(&circuit->ipv6_non_link); + if (circuit->ext) { + isis_del_ext_subtlvs(circuit->ext); + circuit->ext = NULL; + } + XFREE(MTYPE_TMP, circuit->bfd_config.profile); XFREE(MTYPE_ISIS_CIRCUIT, circuit->tag); diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c index 79b167718b..dbe4a017bc 100644 --- a/isisd/isis_nb_config.c +++ b/isisd/isis_nb_config.c @@ -54,7 +54,6 @@ #include "isisd/isis_dr.h" #include "isisd/isis_zebra.h" -DEFINE_MTYPE_STATIC(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters"); DEFINE_MTYPE_STATIC(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name"); /* @@ -86,12 +85,17 @@ int isis_instance_create(struct nb_cb_create_args *args) int isis_instance_destroy(struct nb_cb_destroy_args *args) { struct isis_area *area; + struct isis *isis; if (args->event != NB_EV_APPLY) return NB_OK; area = nb_running_unset_entry(args->dnode); - + isis = area->isis; isis_area_destroy(area); + + if (listcount(isis->area_list) == 0) + isis_finish(isis); + return NB_OK; } @@ -1787,45 +1791,13 @@ int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args) */ int isis_instance_mpls_te_create(struct nb_cb_create_args *args) { - struct listnode *node; struct isis_area *area; - struct isis_circuit *circuit; if (args->event != NB_EV_APPLY) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - if (area->mta == NULL) { - - struct mpls_te_area *new; - - zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering", - area->area_tag); - - new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); - - /* Initialize MPLS_TE structure */ - new->status = enable; - new->level = 0; - new->inter_as = off; - new->interas_areaid.s_addr = 0; - new->router_id.s_addr = 0; - new->ted = ls_ted_new(1, "ISIS", 0); - if (!new->ted) - zlog_warn("Unable to create Link State Data Base"); - - area->mta = new; - } else { - area->mta->status = enable; - } - - /* Initialize Link State Database */ - if (area->mta->ted) - isis_te_init_ted(area); - - /* Update Extended TLVs according to Interface link parameters */ - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) - isis_link_params_update(circuit, circuit->interface); + isis_mpls_te_create(area); /* Reoriginate STD_TE & GMPLS circuits */ lsp_regenerate_schedule(area, area->is_type, 0); @@ -1835,35 +1807,16 @@ int isis_instance_mpls_te_create(struct nb_cb_create_args *args) int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args) { - struct listnode *node; struct isis_area *area; - struct isis_circuit *circuit; if (args->event != NB_EV_APPLY) return NB_OK; area = nb_running_get_entry(args->dnode, NULL, true); - if (IS_MPLS_TE(area->mta)) - area->mta->status = disable; - else - return NB_OK; - - /* Remove Link State Database */ - ls_ted_del_all(&area->mta->ted); - - /* Flush LSP if circuit engage */ - for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { - if (!IS_EXT_TE(circuit->ext)) - continue; - - /* disable MPLS_TE Circuit keeping SR one's */ - if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID)) - circuit->ext->status = EXT_ADJ_SID; - else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID)) - circuit->ext->status = EXT_LAN_ADJ_SID; - else - circuit->ext->status = 0; - } + if (!IS_MPLS_TE(area->mta)) + return NB_OK; + + isis_mpls_te_disable(area); /* Reoriginate STD_TE & GMPLS circuits */ lsp_regenerate_schedule(area, area->is_type, 0); diff --git a/isisd/isis_sr.c b/isisd/isis_sr.c index 259047ff66..f70840a637 100644 --- a/isisd/isis_sr.c +++ b/isisd/isis_sr.c @@ -1253,6 +1253,9 @@ void isis_sr_area_term(struct isis_area *area) if (area->srdb.enabled) isis_sr_stop(area); + /* Free Adjacency SID list */ + list_delete(&srdb->adj_sids); + /* Clear Prefix-SID configuration. */ while (srdb_prefix_cfg_count(&srdb->config.prefix_sids) > 0) { struct sr_prefix_cfg *pcfg; diff --git a/isisd/isis_te.c b/isisd/isis_te.c index 3faff1cc4d..0093279cde 100644 --- a/isisd/isis_te.c +++ b/isisd/isis_te.c @@ -64,10 +64,115 @@ #include "isisd/isis_te.h" #include "isisd/isis_zebra.h" +DEFINE_MTYPE_STATIC(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters"); + /*------------------------------------------------------------------------* * Following are control functions for MPLS-TE parameters management. *------------------------------------------------------------------------*/ +/** + * Create MPLS Traffic Engineering structure which belongs to given area. + * + * @param area IS-IS Area + */ +void isis_mpls_te_create(struct isis_area *area) +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (!area) + return; + + if (area->mta == NULL) { + + struct mpls_te_area *new; + + zlog_debug("ISIS-TE(%s): Initialize MPLS Traffic Engineering", + area->area_tag); + + new = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof(struct mpls_te_area)); + + /* Initialize MPLS_TE structure */ + new->status = enable; + new->level = 0; + new->inter_as = off; + new->interas_areaid.s_addr = 0; + new->router_id.s_addr = 0; + new->ted = ls_ted_new(1, "ISIS", 0); + if (!new->ted) + zlog_warn("Unable to create Link State Data Base"); + + area->mta = new; + } else { + area->mta->status = enable; + } + + /* Initialize Link State Database */ + if (area->mta->ted) + isis_te_init_ted(area); + + /* Update Extended TLVs according to Interface link parameters */ + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + isis_link_params_update(circuit, circuit->interface); +} + +/** + * Disable MPLS Traffic Engineering structure which belongs to given area. + * + * @param area IS-IS Area + */ +void isis_mpls_te_disable(struct isis_area *area) +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (!area->mta) + return; + + area->mta->status = disable; + + /* Remove Link State Database */ + ls_ted_del_all(&area->mta->ted); + + /* Disable Extended SubTLVs on all circuit */ + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + if (!IS_EXT_TE(circuit->ext)) + continue; + + /* disable MPLS_TE Circuit keeping SR one's */ + if (IS_SUBTLV(circuit->ext, EXT_ADJ_SID)) + circuit->ext->status = EXT_ADJ_SID; + else if (IS_SUBTLV(circuit->ext, EXT_LAN_ADJ_SID)) + circuit->ext->status = EXT_LAN_ADJ_SID; + else + circuit->ext->status = 0; + } +} + +void isis_mpls_te_term(struct isis_area *area) +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (!area->mta) + return; + + zlog_info("TE(%s): Terminate MPLS TE", __func__); + /* Remove Link State Database */ + ls_ted_del_all(&area->mta->ted); + + /* Remove Extended SubTLVs */ + zlog_info(" |- Remove Extended SubTLVS for all circuit"); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) { + zlog_info(" |- Call isis_del_ext_subtlvs()"); + isis_del_ext_subtlvs(circuit->ext); + circuit->ext = NULL; + } + + zlog_info(" |- Free MTA structure at %p", area->mta); + XFREE(MTYPE_ISIS_MPLS_TE, area->mta); +} + /* Main initialization / update function of the MPLS TE Circuit context */ /* Call when interface TE Link parameters are modified */ void isis_link_params_update(struct isis_circuit *circuit, diff --git a/isisd/isis_te.h b/isisd/isis_te.h index 56954073dd..03525962f5 100644 --- a/isisd/isis_te.h +++ b/isisd/isis_te.h @@ -123,6 +123,9 @@ enum lsp_event { LSP_UNKNOWN, LSP_ADD, LSP_UPD, LSP_DEL, LSP_INC, LSP_TICK }; /* Prototypes. */ void isis_mpls_te_init(void); +void isis_mpls_te_create(struct isis_area *area); +void isis_mpls_te_disable(struct isis_area *area); +void isis_mpls_te_term(struct isis_area *area); void isis_link_params_update(struct isis_circuit *, struct interface *); int isis_mpls_te_update(struct interface *); void isis_te_lsp_event(struct isis_lsp *lsp, enum lsp_event event); diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index b3c3fd4b0b..8907fa256b 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -139,6 +139,25 @@ struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void) return ext; } +void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext) +{ + struct isis_item *item, *next_item; + + if (!ext) + return; + + /* First, free Adj SID and LAN Adj SID list if needed */ + for (item = ext->adj_sid.head; item; item = next_item) { + next_item = item->next; + XFREE(MTYPE_ISIS_SUBTLV, item); + } + for (item = ext->lan_sid.head; item; item = next_item) { + next_item = item->next; + XFREE(MTYPE_ISIS_SUBTLV, item); + } + XFREE(MTYPE_ISIS_SUBTLV, ext); +} + /* * mtid parameter is used to determine if Adjacency is related to IPv4 or IPv6 * Multi-Topology. Special 4096 value i.e. first R flag set is used to indicate @@ -648,18 +667,7 @@ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, static void free_item_ext_subtlvs(struct isis_ext_subtlvs *exts) { - struct isis_item *item, *next_item; - - /* First, free Adj SID and LAN Adj SID list if needed */ - for (item = exts->adj_sid.head; item; item = next_item) { - next_item = item->next; - XFREE(MTYPE_ISIS_SUBTLV, item); - } - for (item = exts->lan_sid.head; item; item = next_item) { - next_item = item->next; - XFREE(MTYPE_ISIS_SUBTLV, item); - } - XFREE(MTYPE_ISIS_SUBTLV, exts); + isis_del_ext_subtlvs(exts); } static int pack_item_ext_subtlvs(struct isis_ext_subtlvs *exts, @@ -1059,6 +1067,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, log, indent, "TLV size does not match expected size for Adjacency SID!\n"); stream_forward_getp(s, subtlv_len - 2); + XFREE(MTYPE_ISIS_SUBTLV, adj); break; } @@ -1070,6 +1079,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, log, indent, "TLV size does not match expected size for Adjacency SID!\n"); stream_forward_getp(s, subtlv_len - 2); + XFREE(MTYPE_ISIS_SUBTLV, adj); break; } @@ -1114,6 +1124,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); + XFREE(MTYPE_ISIS_SUBTLV, lan); break; } @@ -1127,6 +1138,7 @@ static int unpack_item_ext_subtlvs(uint16_t mtid, uint8_t len, struct stream *s, stream_forward_getp( s, subtlv_len - 2 - ISIS_SYS_ID_LEN); + XFREE(MTYPE_ISIS_SUBTLV, lan); break; } @@ -1892,6 +1904,7 @@ static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, static void free_item_extended_reach(struct isis_item *i) { struct isis_extended_reach *item = (struct isis_extended_reach *)i; + if (item->subtlvs != NULL) free_item_ext_subtlvs(item->subtlvs); XFREE(MTYPE_ISIS_TLV, item); diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 157450dc6c..905032bda1 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -597,6 +597,7 @@ void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, struct prefix_ipv6 *src, uint32_t metric); struct isis_ext_subtlvs *isis_alloc_ext_subtlvs(void); +void isis_del_ext_subtlvs(struct isis_ext_subtlvs *ext); void isis_tlvs_add_adj_sid(struct isis_ext_subtlvs *exts, struct isis_adj_sid *adj); void isis_tlvs_del_adj_sid(struct isis_ext_subtlvs *exts, diff --git a/isisd/isisd.c b/isisd/isisd.c index 3fd2476ad1..bce3dbb77b 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -224,6 +224,12 @@ struct isis *isis_new(const char *vrf_name) void isis_finish(struct isis *isis) { + struct isis_area *area; + struct listnode *node, *nnode; + + for (ALL_LIST_ELEMENTS(isis->area_list, node, nnode, area)) + isis_area_destroy(area); + struct vrf *vrf = NULL; listnode_delete(im->isis, isis); @@ -273,6 +279,13 @@ void isis_area_del_circuit(struct isis_area *area, struct isis_circuit *circuit) isis_csm_state_change(ISIS_DISABLE, circuit, area); } +static void delete_area_addr(void *arg) +{ + struct area_addr *addr = (struct area_addr *)arg; + + XFREE(MTYPE_ISIS_AREA_ADDR, addr); +} + struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) { struct isis_area *area; @@ -318,6 +331,8 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name) area->circuit_list = list_new(); area->adjacency_list = list_new(); area->area_addrs = list_new(); + area->area_addrs->del = delete_area_addr; + if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) thread_add_timer(master, lsp_tick, area, 1, &area->t_tick); flags_initialize(&area->flags); @@ -481,17 +496,12 @@ void isis_area_destroy(struct isis_area *area) { struct listnode *node, *nnode; struct isis_circuit *circuit; - struct area_addr *addr; QOBJ_UNREG(area); if (fabricd) fabricd_finish(area->fabricd); - /* Disable MPLS if necessary before flooding LSP */ - if (IS_MPLS_TE(area->mta)) - area->mta->status = disable; - if (area->circuit_list) { for (ALL_LIST_ELEMENTS(area->circuit_list, node, nnode, circuit)) @@ -499,6 +509,9 @@ void isis_area_destroy(struct isis_area *area) list_delete(&area->circuit_list); } + if (area->flags.free_idcs) + list_delete(&area->flags.free_idcs); + list_delete(&area->adjacency_list); lsp_db_fini(&area->lspdb[0]); @@ -510,6 +523,8 @@ void isis_area_destroy(struct isis_area *area) isis_sr_area_term(area); + isis_mpls_te_term(area); + spftree_area_del(area); if (area->spf_timer[0]) @@ -525,11 +540,7 @@ void isis_area_destroy(struct isis_area *area) if (!CHECK_FLAG(im->options, F_ISIS_UNIT_TEST)) isis_redist_area_finish(area); - for (ALL_LIST_ELEMENTS(area->area_addrs, node, nnode, addr)) { - list_delete_node(area->area_addrs, node); - XFREE(MTYPE_ISIS_AREA_ADDR, addr); - } - area->area_addrs = NULL; + list_delete(&area->area_addrs); for (int i = SPF_PREFIX_PRIO_CRITICAL; i <= SPF_PREFIX_PRIO_MEDIUM; i++) { @@ -554,10 +565,6 @@ void isis_area_destroy(struct isis_area *area) area_mt_finish(area); - if (listcount(area->isis->area_list) == 0) { - isis_finish(area->isis); - } - XFREE(MTYPE_ISIS_AREA, area); } diff --git a/lib/routemap.h b/lib/routemap.h index ad391981e0..0152e820d0 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -387,6 +387,8 @@ DECLARE_QOBJ_TYPE(route_map); (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv4")) #define IS_SET_BGP_EVPN_GATEWAY_IP_IPV6(A) \ (strmatch(A, "frr-bgp-route-map:set-evpn-gateway-ip-ipv6")) +#define IS_SET_BGP_L3VPN_NEXTHOP_ENCAPSULATION(A) \ + (strmatch(A, "frr-bgp-route-map:set-l3vpn-nexthop-encapsulation")) enum ecommunity_lb_type { EXPLICIT_BANDWIDTH, diff --git a/lib/routemap_cli.c b/lib/routemap_cli.c index ff98a14c41..42c7a05d18 100644 --- a/lib/routemap_cli.c +++ b/lib/routemap_cli.c @@ -1258,6 +1258,11 @@ void route_map_action_show(struct vty *vty, const struct lyd_node *dnode, yang_dnode_get_string( dnode, "./rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6")); + } else if (IS_SET_BGP_L3VPN_NEXTHOP_ENCAPSULATION(action)) { + vty_out(vty, " set l3vpn next-hop encapsulation %s\n", + yang_dnode_get_string( + dnode, + "./rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation")); } } diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index 1362554715..0df0072f6d 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -3021,7 +3021,7 @@ int ospf_check_nbr_status(struct ospf *ospf) } -static void ospf_maxage_lsa_remover(struct thread *thread) +void ospf_maxage_lsa_remover(struct thread *thread) { struct ospf *ospf = THREAD_ARG(thread); struct ospf_lsa *lsa, *old; @@ -3899,8 +3899,9 @@ void ospf_refresher_register_lsa(struct ospf *ospf, struct ospf_lsa *lsa) if (lsa->refresh_list < 0) { int delay; int min_delay = - OSPF_LS_REFRESH_TIME - (2 * OSPF_LS_REFRESH_JITTER); - int max_delay = OSPF_LS_REFRESH_TIME - OSPF_LS_REFRESH_JITTER; + ospf->lsa_refresh_timer - (2 * OSPF_LS_REFRESH_JITTER); + int max_delay = + ospf->lsa_refresh_timer - OSPF_LS_REFRESH_JITTER; /* We want to refresh the LSA within OSPF_LS_REFRESH_TIME which * is diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 4b3be15382..97c15d1e3c 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -354,4 +354,5 @@ extern void ospf_check_and_gen_init_seq_lsa(struct ospf_interface *oi, struct ospf_lsa *lsa); extern void ospf_flush_lsa_from_area(struct ospf *ospf, struct in_addr area_id, int type); +extern void ospf_maxage_lsa_remover(struct thread *thread); #endif /* _ZEBRA_OSPF_LSA_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 7d72487686..2a0016ea19 100644 --- a/ospfd/ospf_vty.c +++ b/ospfd/ospf_vty.c @@ -12952,6 +12952,42 @@ DEFUN (clear_ip_ospf_interface, return CMD_SUCCESS; } +DEFPY_HIDDEN(ospf_lsa_refresh_timer, ospf_lsa_refresh_timer_cmd, + "[no$no] ospf lsa-refresh [(120-1800)]$value", + NO_STR OSPF_STR + "OSPF lsa refresh timer\n" + "timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + + if (no) + ospf->lsa_refresh_timer = OSPF_LS_REFRESH_TIME; + else + ospf->lsa_refresh_timer = value; + + return CMD_SUCCESS; +} + +DEFPY_HIDDEN(ospf_maxage_delay_timer, ospf_maxage_delay_timer_cmd, + "[no$no] ospf maxage-delay [(0-60)]$value", + NO_STR OSPF_STR + "OSPF lsa maxage delay timer\n" + "timer value in seconds\n") +{ + VTY_DECLVAR_INSTANCE_CONTEXT(ospf, ospf) + + if (no) + ospf->maxage_delay = OSPF_LSA_MAXAGE_REMOVE_DELAY_DEFAULT; + else + ospf->maxage_delay = value; + + THREAD_OFF(ospf->t_maxage); + OSPF_TIMER_ON(ospf->t_maxage, ospf_maxage_lsa_remover, + ospf->maxage_delay); + + return CMD_SUCCESS; +} + void ospf_vty_clear_init(void) { install_element(ENABLE_NODE, &clear_ip_ospf_interface_cmd); @@ -13109,6 +13145,9 @@ void ospf_vty_init(void) vrf_cmd_init(NULL); + install_element(OSPF_NODE, &ospf_lsa_refresh_timer_cmd); + install_element(OSPF_NODE, &ospf_maxage_delay_timer_cmd); + /* Init interface related vty commands. */ ospf_vty_if_init(); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 8512b6a339..e0c36d86fe 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -392,6 +392,7 @@ struct ospf *ospf_new_alloc(unsigned short instance, const char *name) new->lsa_refresh_queue.index = 0; new->lsa_refresh_interval = OSPF_LSA_REFRESH_INTERVAL_DEFAULT; + new->lsa_refresh_timer = OSPF_LS_REFRESH_TIME; new->t_lsa_refresher = NULL; thread_add_timer(master, ospf_lsa_refresh_walker, new, new->lsa_refresh_interval, &new->t_lsa_refresher); diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 8478c96ddc..3a43010f85 100644 --- a/ospfd/ospfd.h +++ b/ospfd/ospfd.h @@ -313,6 +313,7 @@ struct ospf { time_t lsa_refresher_started; #define OSPF_LSA_REFRESH_INTERVAL_DEFAULT 10 uint16_t lsa_refresh_interval; + uint16_t lsa_refresh_timer; /* Distance parameter. */ uint8_t distance_all; diff --git a/tests/topotests/bgp_vpnv4_ebgp/__init__.py b/tests/topotests/bgp_vpnv4_ebgp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/__init__.py diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf new file mode 100644 index 0000000000..2eebe5e6dd --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65500 + bgp router-id 1.1.1.1 + no bgp ebgp-requires-policy + neighbor 10.125.0.2 remote-as 65501 + address-family ipv4 unicast + no neighbor 10.125.0.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 10.125.0.2 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 1.1.1.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r1-eth0 + mpls bgp forwarding +!
\ No newline at end of file diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json new file mode 100644 index 0000000000..da7d281833 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.200.0.0/24": [ + { + "prefix": "10.200.0.0/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "10.125.0.2", + "afi": "ipv4", + "interfaceName": "r1-eth0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf new file mode 100644 index 0000000000..e9ae4e9831 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r1-eth1 vrf vrf1 + ip address 10.201.0.1/24 +! +interface r1-eth0 + ip address 10.125.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json new file mode 100644 index 0000000000..19797dd561 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65501, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "10.125.0.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf new file mode 100644 index 0000000000..e38c99d69c --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf @@ -0,0 +1,25 @@ +router bgp 65501 + bgp router-id 2.2.2.2 + no bgp ebgp-requires-policy + neighbor 10.125.0.1 remote-as 65500 + address-family ipv4 unicast + no neighbor 10.125.0.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 10.125.0.1 activate + exit-address-family +! +router bgp 65501 vrf vrf1 + bgp router-id 2.2.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +interface r2-eth0 + mpls bgp forwarding +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf new file mode 100644 index 0000000000..6c433aef2b --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf @@ -0,0 +1,7 @@ +log stdout +interface r2-eth1 vrf vrf1 + ip address 10.200.0.2/24 +! +interface r2-eth0 + ip address 10.125.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py new file mode 100644 index 0000000000..cd8a9b6d6f --- /dev/null +++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# +# test_bgp_vpnv4_ebgp.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2022 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_vpnv4_ebgp.py: Test the FRR BGP daemon with EBGP direct connection +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + 'ip link add vrf1 type vrf table 10', + 'echo 100000 > /proc/sys/net/mpls/platform_labels', + 'ip link set dev vrf1 up', + 'ip link set dev {0}-eth1 master vrf1', + 'echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input', + ] + + for cmd in cmds_list: + input = cmd.format('r1', '1', '2') + logger.info('input: ' + cmd) + output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2')) + logger.info('output: ' + output) + + for cmd in cmds_list: + input = cmd.format('r2', '2', '1') + logger.info('input: ' + cmd) + output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1')) + logger.info('output: ' + output) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r1'] + logger.info("Dump some context for r1") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + router = tgen.gears['r2'] + logger.info("Dump some context for r2") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + + # Check IPv4 routing tables on r1 + logger.info("Checking IPv4 routes for convergence on r1") + router = tgen.gears['r1'] + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + assert 0, 'ipv4_routes.json file not found' + return + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf vrf1 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv4 routing tables on r2 not installed + logger.info("Checking BGP IPv4 routes for convergence on r2") + router = tgen.gears['r2'] + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + assert 0, 'bgp_ipv4_routes.json file not found' + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp_vpnv4_gre/__init__.py b/tests/topotests/bgp_vpnv4_gre/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/__init__.py diff --git a/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf new file mode 100644 index 0000000000..0e2d3a8248 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf @@ -0,0 +1,26 @@ +router bgp 65500 + bgp router-id 192.0.2.1 + neighbor 192.0.2.2 remote-as 65500 + neighbor 192.0.2.2 update-source 192.0.2.1 + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.2 activate + neighbor 192.0.2.2 route-map rmap in + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.1 + address-family ipv4 unicast + redistribute connected + label vpn export 101 + rd vpn export 444:1 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! +route-map rmap permit 1 + set l3vpn next-hop encapsulation gre +! diff --git a/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json new file mode 100644 index 0000000000..5f2732aab0 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json @@ -0,0 +1,50 @@ +{ + "10.200.0.0/24": [ + { + "prefix": "10.200.0.0/24", + "prefixLen": 24, + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "nexthops": [ + { + "flags": 3, + "fib": true, + "ip": "192.168.0.2", + "afi": "ipv4", + "interfaceName": "r1-gre0", + "vrf": "default", + "active": true, + "labels":[ + 102 + ] + } + ] + } + ], + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0/24", + "prefixLen": 24, + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops":[ + { + "flags": 3, + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf new file mode 100644 index 0000000000..11780a874c --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.0.2.2/32 192.168.0.2 +interface lo + ip address 192.0.2.1/32 +! +interface r1-gre0 + ip address 192.168.0.1/24 +! +interface r1-eth1 vrf vrf1 + ip address 10.201.0.1/24 +! +interface r1-eth0 + ip address 10.125.0.1/24 +! diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json new file mode 100644 index 0000000000..e50d5dd084 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json @@ -0,0 +1,38 @@ +{ + "vrfName": "vrf1", + "localAS": 65500, + "routes": + { + "10.201.0.0/24": [ + { + "prefix": "10.201.0.0", + "prefixLen": 24, + "network": "10.201.0.0\/24", + "nhVrfName": "default", + "nexthops": [ + { + "ip": "192.0.2.1", + "afi": "ipv4", + "used": true + } + ] + } + ], + "10.200.0.0/24": [ + { + "valid": true, + "bestpath": true, + "prefix": "10.200.0.0", + "prefixLen": 24, + "network": "10.200.0.0\/24", + "nexthops": [ + { + "ip": "0.0.0.0", + "afi": "ipv4", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf new file mode 100644 index 0000000000..bf05866da4 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf @@ -0,0 +1,22 @@ +router bgp 65500 + bgp router-id 192.0.2.2 + neighbor 192.0.2.1 remote-as 65500 + neighbor 192.0.2.1 update-source 192.0.2.2 + address-family ipv4 unicast + no neighbor 192.0.2.1 activate + exit-address-family + address-family ipv4 vpn + neighbor 192.0.2.1 activate + exit-address-family +! +router bgp 65500 vrf vrf1 + bgp router-id 192.0.2.2 + address-family ipv4 unicast + redistribute connected + label vpn export 102 + rd vpn export 444:2 + rt vpn both 52:100 + export vpn + import vpn + exit-address-family +! diff --git a/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf new file mode 100644 index 0000000000..de88a4b64e --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf @@ -0,0 +1,14 @@ +log stdout +ip route 192.0.2.1/32 192.168.0.1 +interface lo + ip address 192.0.2.2/32 +! +interface r2-gre0 + ip address 192.168.0.2/24 +! +interface r2-eth1 vrf vrf1 + ip address 10.200.0.2/24 +! +interface r2-eth0 + ip address 10.125.0.2/24 +! diff --git a/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py new file mode 100644 index 0000000000..f562f44179 --- /dev/null +++ b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +# +# test_bgp_vpnv4_gre.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2021 by 6WIND +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" + test_bgp_vpnv4_gre.py: Test the FRR BGP daemon with BGP IPv6 interface + with route advertisements on a separate netns. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. + + +pytestmark = [pytest.mark.bgpd] + + +def build_topo(tgen): + "Build function" + + # Create 2 routers. + tgen.add_router("r1") + tgen.add_router("r2") + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r1"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r2"]) + +def _populate_iface(): + tgen = get_topogen() + cmds_list = [ + 'ip link add vrf1 type vrf table 10', + 'echo 10 > /proc/sys/net/mpls/platform_labels', + 'ip link set dev vrf1 up', + 'ip link set dev {0}-eth1 master vrf1', + 'echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input', + 'ip tunnel add {0}-gre0 mode gre ttl 64 dev {0}-eth0 local 10.125.0.{1} remote 10.125.0.{2}', + 'ip link set dev {0}-gre0 up', + 'echo 1 > /proc/sys/net/mpls/conf/{0}-gre0/input', + ] + + for cmd in cmds_list: + input = cmd.format('r1', '1', '2') + logger.info('input: ' + cmd) + output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2')) + logger.info('output: ' + output) + + for cmd in cmds_list: + input = cmd.format('r2', '2', '1') + logger.info('input: ' + cmd) + output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1')) + logger.info('output: ' + output) + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + _populate_iface() + + for rname, router in router_list.items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname)) + ) + + # Initialize all routers. + tgen.start_router() + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + tgen.stop_topology() + + +def test_protocols_convergence(): + """ + Assert that all protocols have converged + statuses as they depend on it. + """ + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + router = tgen.gears['r1'] + logger.info("Dump some context for r1") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + router = tgen.gears['r2'] + logger.info("Dump some context for r2") + router.vtysh_cmd("show bgp ipv4 vpn") + router.vtysh_cmd("show bgp summary") + router.vtysh_cmd("show bgp vrf vrf1 ipv4") + router.vtysh_cmd("show running-config") + + # Check IPv4 routing tables on r1 + logger.info("Checking IPv4 routes for convergence on r1") + router = tgen.gears['r1'] + json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + logger.info("skipping file {}".format(json_file)) + assert 0, 'ipv4_routes.json file not found' + return + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show ip route vrf vrf1 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + + # Check BGP IPv4 routing tables on r2 not installed + logger.info("Checking BGP IPv4 routes for convergence on r2") + router = tgen.gears['r2'] + json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name) + if not os.path.isfile(json_file): + assert 0, 'bgp_ipv4_routes.json file not found' + + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv4 json", + expected, + ) + _, result = topotest.run_and_expect(test_func, None, count=40, wait=2) + assertmsg = '"{}" JSON output mismatches'.format(router.name) + assert result is None, assertmsg + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 1f02efee20..68841e2d38 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -60,8 +60,13 @@ extern struct thread_master *master; #define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_LDPD|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_SHARPD|VTYSH_PBRD|VTYSH_STATICD|VTYSH_BFDD|VTYSH_FABRICD|VTYSH_VRRPD|VTYSH_PATHD #define VTYSH_ACL VTYSH_BFDD|VTYSH_BABELD|VTYSH_BGPD|VTYSH_EIGRPD|VTYSH_ISISD|VTYSH_FABRICD|VTYSH_LDPD|VTYSH_NHRPD|VTYSH_OSPF6D|VTYSH_OSPFD|VTYSH_PBRD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_VRRPD|VTYSH_ZEBRA #define VTYSH_RMAP VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_EIGRPD|VTYSH_FABRICD -#define VTYSH_INTERFACE VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD|VTYSH_PIM6D|VTYSH_NHRPD|VTYSH_EIGRPD|VTYSH_BABELD|VTYSH_PBRD|VTYSH_FABRICD|VTYSH_VRRPD -#define VTYSH_VRF VTYSH_INTERFACE|VTYSH_STATICD +#define VTYSH_INTERFACE_SUBSET \ + VTYSH_ZEBRA | VTYSH_RIPD | VTYSH_RIPNGD | VTYSH_OSPFD | VTYSH_OSPF6D | \ + VTYSH_ISISD | VTYSH_PIMD | VTYSH_PIM6D | VTYSH_NHRPD | \ + VTYSH_EIGRPD | VTYSH_BABELD | VTYSH_PBRD | VTYSH_FABRICD | \ + VTYSH_VRRPD +#define VTYSH_INTERFACE VTYSH_INTERFACE_SUBSET | VTYSH_BGPD +#define VTYSH_VRF VTYSH_INTERFACE_SUBSET | VTYSH_STATICD #define VTYSH_KEYS VTYSH_RIPD | VTYSH_EIGRPD | VTYSH_OSPF6D /* Daemons who can process nexthop-group configs */ #define VTYSH_NH_GROUP VTYSH_PBRD|VTYSH_SHARPD diff --git a/yang/frr-bgp-route-map.yang b/yang/frr-bgp-route-map.yang index eaa7891f0c..fcfd14e4fe 100644 --- a/yang/frr-bgp-route-map.yang +++ b/yang/frr-bgp-route-map.yang @@ -330,6 +330,12 @@ module frr-bgp-route-map { "Set EVPN gateway IP overlay index IPv6"; } + identity set-l3vpn-nexthop-encapsulation { + base frr-route-map:rmap-set-type; + description + "Accept L3VPN traffic over other than LSP encapsulation"; + } + grouping extcommunity-non-transitive-types { leaf two-octet-as-specific { type boolean; @@ -902,5 +908,21 @@ module frr-bgp-route-map { type inet:ipv6-address; } } + case l3vpn-nexthop-encapsulation { + when + "derived-from-or-self(/frr-route-map:lib/frr-route-map:route-map/frr-route-map:entry/frr-route-map:set-action/frr-route-map:action, + 'frr-bgp-route-map:set-l3vpn-nexthop-encapsulation')"; + description + "Accept L3VPN traffic over other than LSP encapsulation"; + leaf l3vpn-nexthop-encapsulation { + type enumeration { + enum "gre" { + value 0; + description + "GRE protocol"; + } + } + } + } } } |
