bgpd: add resolution for l3vpn traffic over gre interfaces

When a route imported from l3vpn is analysed, the nexthop from default
VRF is looked up against a valid MPLS path. Generally, this is done on
backbones with a MPLS signalisation transport layer like LDP. Generally,
the BGP connection is multiple hops away. That scenario is already
working.

There is case where it is possible to run L3VPN over GRE interfaces, and
where there is no LSP path over that GRE interface: GRE is just here to
tunnel MPLS traffic. On that case, the nexthop given in the path does not
have MPLS path, but should be authorized to convey MPLS traffic provided
that the user permits it via a configuration command.

That commit introduces a new command that can be activated in route-map:
 > set l3vpn next-hop encapsulation gre

That command authorizes the nexthop tracking engine to accept paths that
o have a GRE interface as output, independently of the presence of an LSP
path or not.

A configuration example is given below. When bgp incoming vpnv4 updates
are received, the nexthop of NLRI is 192.168.0.2. Based on nexthop
tracking service from zebra, BGP knows that the output interface to reach
192.168.0.2 is r1-gre0. Because that interface is not MPLS based, but is
a GRE tunnel, then the update will be using that nexthop to be installed.

    interface r1-gre0
     ip address 192.168.0.1/24
    exit
    router bgp 65500
     bgp router-id 1.1.1.1
     neighbor 192.168.0.2 remote-as 65500
     !
     address-family ipv4 unicast
      no neighbor 192.168.0.2 activate
     exit-address-family
     !
     address-family ipv4 vpn
      neighbor 192.168.0.2 activate
      neighbor 192.168.0.2 route-map rmap in
     exit-address-family
    exit
    !
    router bgp 65500 vrf vrf1
     bgp router-id 1.1.1.1
     no bgp network import-check
     !
     address-family ipv4 unicast
      network 10.201.0.0/24
      redistribute connected
      label vpn export 101
      rd vpn export 444:1
      rt vpn both 52:100
      export vpn
      import vpn
     exit-address-family
    exit
    !
    route-map rmap permit 1
     set l3vpn next-hop encapsulation gre
    exit

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
This commit is contained in:
Philippe Guibert 2021-09-20 11:50:52 +02:00
parent f1f38efd83
commit 1bb550b63c
11 changed files with 240 additions and 13 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

@ -61,22 +61,51 @@ 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_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_mplsovergre(bnc, path)))));
}
static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc)
@ -359,11 +388,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 +1092,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

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

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

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

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