Merge pull request #12340 from opensourcerouting/fix/rfc5549_vpn_set_ip_nexthop

bgpd: Allow overriding MPLS VPN next-hops via route-maps
This commit is contained in:
Stephen Worley 2022-11-22 11:41:48 -05:00 committed by GitHub
commit c8b92cd80a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 356 additions and 22 deletions

View File

@ -344,6 +344,8 @@ struct attr {
#define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6) #define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6)
#define BATTR_RMAP_LINK_BW_SET (1 << 7) #define BATTR_RMAP_LINK_BW_SET (1 << 7)
#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8) #define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8)
#define BATTR_RMAP_VPNV4_NHOP_CHANGED (1 << 9)
#define BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED (1 << 10)
/* Router Reflector related structure. */ /* Router Reflector related structure. */
struct cluster_list { struct cluster_list {
@ -473,20 +475,23 @@ extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer, extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
struct attr *attr); struct attr *attr);
static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags, static inline bool bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
uint32_t in_rmap_flags) uint32_t in_rmap_flags)
{ {
return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS) return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS) ||
|| CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED) CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED) ||
|| CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED) CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED) ||
|| CHECK_FLAG(out_rmap_flags, CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED) ||
BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) CHECK_FLAG(out_rmap_flags,
|| CHECK_FLAG(out_rmap_flags, BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED) ||
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) CHECK_FLAG(out_rmap_flags,
|| CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED) BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED) ||
|| CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED)) CHECK_FLAG(out_rmap_flags,
? 1 BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED) ||
: 0); CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED) ||
CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
? true
: false);
} }
static inline uint32_t mac_mobility_seqnum(struct attr *attr) static inline uint32_t mac_mobility_seqnum(struct attr *attr)

View File

@ -2583,8 +2583,8 @@ bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
if (bgp_debug_update(NULL, p, subgrp->update_group, 0)) if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug( zlog_debug(
"%s: BGP_PATH_ANNC_NH_SELF, family=%s", "%s: %pFX BGP_PATH_ANNC_NH_SELF, family=%s",
__func__, family2str(family)); __func__, p, family2str(family));
subgroup_announce_reset_nhop(family, attr); subgroup_announce_reset_nhop(family, attr);
nh_reset = true; nh_reset = true;
} }
@ -11714,6 +11714,9 @@ int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
vty_out(vty, vty_out(vty,
"\nDisplayed %ld routes and %ld total paths\n", "\nDisplayed %ld routes and %ld total paths\n",
output_cum, total_cum); output_cum, total_cum);
} else {
if (use_json && output_cum == 0)
vty_out(vty, "{}\n");
} }
return CMD_SUCCESS; return CMD_SUCCESS;
} }

View File

@ -3725,6 +3725,8 @@ route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix, void *object)
path->attr->mp_nexthop_global_in = *address; path->attr->mp_nexthop_global_in = *address;
path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED);
return RMAP_OKAY; return RMAP_OKAY;
} }
@ -3762,6 +3764,9 @@ route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix, void *object)
sizeof(struct in6_addr)); sizeof(struct in6_addr));
path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL; path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
SET_FLAG(path->attr->rmap_change_flags,
BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED);
return RMAP_OKAY; return RMAP_OKAY;
} }

View File

@ -88,6 +88,8 @@ typedef struct {
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED (1 << 4) #define BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED (1 << 4)
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED (1 << 5) #define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED (1 << 5)
#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_LNH_CHANGED (1 << 6) #define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_LNH_CHANGED (1 << 6)
#define BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED (1 << 7)
#define BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED (1 << 8)
typedef struct bpacket_attr_vec_arr { typedef struct bpacket_attr_vec_arr {
bpacket_attr_vec entries[BGP_ATTR_VEC_MAX]; bpacket_attr_vec entries[BGP_ATTR_VEC_MAX];

View File

@ -379,9 +379,10 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
route_map_sets_nh = route_map_sets_nh =
(CHECK_FLAG(vec->flags, (CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED) BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED) ||
|| CHECK_FLAG( CHECK_FLAG(vec->flags,
vec->flags, BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED) ||
CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
switch (nhlen) { switch (nhlen) {
@ -468,9 +469,11 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
route_map_sets_nh = route_map_sets_nh =
(CHECK_FLAG(vec->flags, (CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED) BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED) ||
|| CHECK_FLAG( CHECK_FLAG(
vec->flags, vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED) ||
CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)); BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
/* /*
@ -1276,6 +1279,15 @@ bpacket_vec_arr_inherit_attr_flags(struct bpacket_attr_vec_arr *vecarr,
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags, SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED); BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED);
if (CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_VPNV4_NHOP_CHANGED))
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
BPKT_ATTRVEC_FLAGS_RMAP_VPNV4_NH_CHANGED);
if (CHECK_FLAG(attr->rmap_change_flags,
BATTR_RMAP_VPNV6_GLOBAL_NHOP_CHANGED))
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
BPKT_ATTRVEC_FLAGS_RMAP_VPNV6_GNH_CHANGED);
if (CHECK_FLAG(attr->rmap_change_flags, if (CHECK_FLAG(attr->rmap_change_flags,
BATTR_RMAP_IPV6_LL_NHOP_CHANGED)) BATTR_RMAP_IPV6_LL_NHOP_CHANGED))
SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags, SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,

View File

@ -0,0 +1,9 @@
router bgp 65000
no bgp ebgp-requires-policy
neighbor 192.168.1.2 remote-as external
neighbor 192.168.1.2 timers 1 3
neighbor 192.168.1.2 timers connect 1
address-family ipv4 unicast
redistribute connected
exit-address-family
!

View File

@ -0,0 +1,9 @@
!
interface lo
ip address 172.16.255.1/32
!
interface cpe1-eth0
ip address 192.168.1.1/24
!
ip forwarding
!

View File

@ -0,0 +1,6 @@
router bgp 65000
no bgp ebgp-requires-policy
neighbor 192.168.2.2 remote-as external
neighbor 192.168.2.2 timers 1 3
neighbor 192.168.2.2 timers connect 1
!

View File

@ -0,0 +1,6 @@
!
interface cpe2-eth0
ip address 192.168.2.1/24
!
ip forwarding
!

View File

@ -0,0 +1,38 @@
router bgp 65001
bgp router-id 10.10.10.10
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
neighbor 2001:db8:1::2 remote-as internal
neighbor 2001:db8:1::2 update-source 2001:db8:1::1
neighbor 2001:db8:1::2 timers 1 3
neighbor 2001:db8:1::2 timers connect 1
neighbor 2001:db8:1::2 capability extended-nexthop
address-family ipv4 vpn
neighbor 2001:db8:1::2 activate
neighbor 2001:db8:1::2 route-map pe2 out
exit-address-family
!
router bgp 65001 vrf RED
bgp router-id 192.168.1.2
no bgp ebgp-requires-policy
neighbor 192.168.1.1 remote-as external
neighbor 192.168.1.1 timers 1 3
neighbor 192.168.1.1 timers connect 1
address-family ipv4 unicast
label vpn export 1111
rd vpn export 192.168.1.2:2
rt vpn import 192.168.2.2:2 192.168.1.2:2
rt vpn export 192.168.1.2:2
export vpn
import vpn
exit-address-family
!
ip prefix-list cpe1 seq 5 permit 172.16.255.1/32
!
route-map pe2 permit 10
match ip address prefix-list cpe1
set ipv6 vpn next-hop 2001:db8::1
!
route-map pe2 permit 20
exit
!

View File

@ -0,0 +1,10 @@
mpls ldp
router-id 10.10.10.10
!
address-family ipv4
discovery transport-address 10.10.10.10
!
interface pe1-eth1
!
!
!

View File

@ -0,0 +1,12 @@
!
interface lo
ipv6 ospf6 area 0
!
interface pe1-eth1
ipv6 ospf6 area 0
ipv6 ospf6 hello-interval 1
ipv6 ospf6 dead-interval 3
!
router ospf6
ospf6 router-id 10.10.10.10
!

View File

@ -0,0 +1,14 @@
!
interface lo
ip address 10.10.10.10/32
ipv6 address 2001:db8:1::1/128
!
interface pe1-eth0 vrf RED
ip address 192.168.1.2/24
!
interface pe1-eth1
ip address 10.0.1.1/24
ipv6 address 2001:db8::1/64
!
ip forwarding
!

View File

@ -0,0 +1,29 @@
router bgp 65001
bgp router-id 10.10.10.20
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
neighbor 2001:db8:1::1 remote-as internal
neighbor 2001:db8:1::1 update-source 2001:db8:1::2
neighbor 2001:db8:1::1 timers 1 3
neighbor 2001:db8:1::1 timers connect 1
neighbor 2001:db8:1::1 capability extended-nexthop
address-family ipv4 vpn
neighbor 2001:db8:1::1 activate
exit-address-family
!
router bgp 65001 vrf RED
bgp router-id 192.168.2.2
no bgp ebgp-requires-policy
neighbor 192.168.2.1 remote-as external
neighbor 192.168.2.1 timers 1 3
neighbor 192.168.2.1 timers connect 1
address-family ipv4 unicast
label vpn export 2222
rd vpn export 192.168.2.2:2
rt vpn import 192.168.2.2:2 192.168.1.2:2
rt vpn export 192.168.2.2:2
export vpn
import vpn
exit-address-family
!

View File

@ -0,0 +1,10 @@
mpls ldp
router-id 10.10.10.20
!
address-family ipv4
discovery transport-address 10.10.10.20
!
interface pe2-eth0
!
!
!

View File

@ -0,0 +1,12 @@
!
interface lo
ipv6 ospf6 area 0
!
interface pe2-eth0
ipv6 ospf6 area 0
ipv6 ospf6 hello-interval 1
ipv6 ospf6 dead-interval 3
!
router ospf6
ospf6 router-id 10.10.10.20
!

View File

@ -0,0 +1,14 @@
!
interface lo
ip address 10.10.10.20/32
ipv6 address 2001:db8:1::2/128
!
interface pe2-eth1 vrf RED
ip address 192.168.2.2/24
!
interface pe2-eth0
ip address 10.0.1.2/24
ipv6 address 2001:db8::2/64
!
ip forwarding
!

View File

@ -0,0 +1,138 @@
#!/usr/bin/env python
#
# Copyright (c) 2022 by
# Donatas Abraitis <donatas@opensourcerouting.org>
#
# 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.
#
"""
Check if we can override VPN underlay next-hop from PE1 to PE2.
"""
import os
import sys
import json
import pytest
import functools
CWD = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(CWD, "../"))
# pylint: disable=C0413
from lib import topotest
from lib.topogen import Topogen, TopoRouter, get_topogen
from lib.common_config import step
pytestmark = [pytest.mark.bgpd]
def build_topo(tgen):
tgen.add_router("cpe1")
tgen.add_router("cpe2")
tgen.add_router("pe1")
tgen.add_router("pe2")
switch = tgen.add_switch("s1")
switch.add_link(tgen.gears["cpe1"])
switch.add_link(tgen.gears["pe1"])
switch = tgen.add_switch("s2")
switch.add_link(tgen.gears["pe1"])
switch.add_link(tgen.gears["pe2"])
switch = tgen.add_switch("s3")
switch.add_link(tgen.gears["pe2"])
switch.add_link(tgen.gears["cpe2"])
def setup_module(mod):
tgen = Topogen(build_topo, mod.__name__)
tgen.start_topology()
pe1 = tgen.gears["pe1"]
pe2 = tgen.gears["pe2"]
pe1.run("ip link add RED type vrf table 1001")
pe1.run("ip link set up dev RED")
pe2.run("ip link add RED type vrf table 1001")
pe2.run("ip link set up dev RED")
pe1.run("ip link set pe1-eth0 master RED")
pe2.run("ip link set pe2-eth1 master RED")
pe1.run("sysctl -w net.ipv4.ip_forward=1")
pe2.run("sysctl -w net.ipv4.ip_forward=1")
pe1.run("sysctl -w net.mpls.conf.pe1-eth0.input=1")
pe2.run("sysctl -w net.mpls.conf.pe2-eth1.input=1")
router_list = tgen.routers()
for i, (rname, router) in enumerate(router_list.items(), 1):
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))
)
router.load_config(
TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
)
router.load_config(
TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
)
tgen.start_router()
def teardown_module(mod):
tgen = get_topogen()
tgen.stop_topology()
def test_bgp_vpn_5549():
tgen = get_topogen()
pe2 = tgen.gears["pe2"]
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
def _bgp_vpn_nexthop_changed():
output = json.loads(pe2.vtysh_cmd("show bgp ipv4 vpn json"))
expected = {
"routes": {
"routeDistinguishers": {
"192.168.1.2:2": {
"172.16.255.1/32": [
{"valid": True, "nexthops": [{"ip": "2001:db8::1"}]}
],
"192.168.1.0/24": [
{"valid": True, "nexthops": [{"ip": "2001:db8:1::1"}]}
],
}
}
}
}
return topotest.json_cmp(output, expected)
test_func = functools.partial(_bgp_vpn_nexthop_changed)
_, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
assert result is None, "Failed overriding IPv6 next-hop for VPN underlay"
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))