mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-02 20:27:14 +00:00
Merge pull request #11823 from pguibert6WIND/bgp_vpnv4_gre_ebgp
Bgp vpnv4 convey without transport label
This commit is contained in:
commit
340ed5f9e2
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
0
tests/topotests/bgp_vpnv4_ebgp/__init__.py
Normal file
0
tests/topotests/bgp_vpnv4_ebgp/__init__.py
Normal file
25
tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf
Normal file
25
tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf
Normal 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
|
||||
!
|
50
tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json
Normal file
50
tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
7
tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf
Normal file
7
tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf
Normal 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
|
||||
!
|
38
tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json
Normal file
38
tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
25
tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf
Normal file
25
tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf
Normal 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
|
||||
!
|
7
tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf
Normal file
7
tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf
Normal 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
|
||||
!
|
187
tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py
Normal file
187
tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py
Normal 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))
|
0
tests/topotests/bgp_vpnv4_gre/__init__.py
Normal file
0
tests/topotests/bgp_vpnv4_gre/__init__.py
Normal file
26
tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf
Normal file
26
tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf
Normal 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
|
||||
!
|
50
tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json
Normal file
50
tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
14
tests/topotests/bgp_vpnv4_gre/r1/zebra.conf
Normal file
14
tests/topotests/bgp_vpnv4_gre/r1/zebra.conf
Normal 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
|
||||
!
|
38
tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json
Normal file
38
tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
22
tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf
Normal file
22
tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf
Normal 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
|
||||
!
|
14
tests/topotests/bgp_vpnv4_gre/r2/zebra.conf
Normal file
14
tests/topotests/bgp_vpnv4_gre/r2/zebra.conf
Normal 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
|
||||
!
|
191
tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py
Normal file
191
tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py
Normal 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))
|
@ -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
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user