Merge pull request #11823 from pguibert6WIND/bgp_vpnv4_gre_ebgp

Bgp vpnv4 convey without transport label
This commit is contained in:
Rafael Zalamena 2022-09-06 13:37:19 -03:00 committed by GitHub
commit 340ed5f9e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1126 additions and 15 deletions

View File

@ -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 {

View File

@ -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();

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -235,6 +235,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.

View File

@ -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);

View File

@ -406,6 +406,13 @@ const struct frr_yang_module_info frr_bgp_route_map_info = {
.destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy,
}
},
{
.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,
},

View File

@ -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
}

View File

@ -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;
}

View File

@ -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"

View File

@ -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;

View File

@ -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);

View File

@ -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),

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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"));
}
}

View File

@ -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
!

View File

@ -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
}
]
}
]
}

View File

@ -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
!

View File

@ -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
}
]
}
]
}
}

View File

@ -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
!

View File

@ -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
!

View File

@ -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))

View File

@ -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
!

View File

@ -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
}
]
}
]
}

View File

@ -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
!

View File

@ -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
}
]
}
]
}
}

View File

@ -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
!

View File

@ -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
!

View File

@ -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))

View File

@ -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

View File

@ -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";
}
}
}
}
}
}