summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@users.noreply.github.com>2022-09-06 13:37:19 -0300
committerGitHub <noreply@github.com>2022-09-06 13:37:19 -0300
commit340ed5f9e20871bd8ae391cb06432138e1fa11b7 (patch)
tree2226f4b66ba9a3048e8439322fc84611ca14d0b4
parentcbe4fd18fc9840540cf817ababe7837deff335c3 (diff)
parent6fc4929e095fb3119f05e544a6b623104ef1c509 (diff)
Merge pull request #11823 from pguibert6WIND/bgp_vpnv4_gre_ebgp
Bgp vpnv4 convey without transport label
-rw-r--r--bgpd/bgp_attr.h1
-rw-r--r--bgpd/bgp_main.c2
-rw-r--r--bgpd/bgp_mplsvpn.c5
-rw-r--r--bgpd/bgp_nht.c93
-rw-r--r--bgpd/bgp_route.c14
-rw-r--r--bgpd/bgp_route.h6
-rw-r--r--bgpd/bgp_routemap.c81
-rw-r--r--bgpd/bgp_routemap_nb.c7
-rw-r--r--bgpd/bgp_routemap_nb.h4
-rw-r--r--bgpd/bgp_routemap_nb_config.c53
-rw-r--r--bgpd/bgp_vty.c81
-rw-r--r--bgpd/bgp_zebra.c27
-rw-r--r--bgpd/bgp_zebra.h1
-rw-r--r--bgpd/bgpd.h5
-rw-r--r--doc/user/bgp.rst26
-rw-r--r--doc/user/routemap.rst3
-rw-r--r--lib/routemap.h2
-rw-r--r--lib/routemap_cli.c5
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json50
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py187
-rw-r--r--tests/topotests/bgp_vpnv4_gre/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf26
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json50
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf22
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py191
-rw-r--r--vtysh/vtysh.h9
-rw-r--r--yang/frr-bgp-route-map.yang22
36 files changed, 1126 insertions, 15 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_vty.c b/bgpd/bgp_vty.c
index c4dd9157a7..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"
@@ -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.h b/bgpd/bgpd.h
index 14c0f121c2..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),
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/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/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";
+ }
+ }
+ }
+ }
}
}