From 4d7df91752d7414d9719a361a2fd4cc30943dc96 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 13 Mar 2023 10:47:16 +0100 Subject: [PATCH 1/2] topotests: add an ebgp 6vpe test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test uses the connected ipv4 mapped ipv6 prefix to resolve the received BGP routes. Signed-off-by: Louis Scalbert Signed-off-by: Philippe Guibert Signed-off-by: François Dumontet --- .../topotests/bgp_6vpe_ebgp_topo1/__init__.py | 0 .../bgp_6vpe_ebgp_topo1/h1/zebra.conf | 4 + .../bgp_6vpe_ebgp_topo1/h2/zebra.conf | 8 + .../bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json | 13 ++ .../bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json | 116 ++++++++++++ .../bgp_6vpe_ebgp_topo1/pe1/bgpd.conf | 32 ++++ .../pe1/ipv6_routes_vrf.json | 142 ++++++++++++++ .../bgp_6vpe_ebgp_topo1/pe1/isisd.conf | 23 +++ .../bgp_6vpe_ebgp_topo1/pe1/zebra.conf | 11 ++ .../bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json | 13 ++ .../bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json | 116 ++++++++++++ .../bgp_6vpe_ebgp_topo1/pe2/bgpd.conf | 31 +++ .../bgp_6vpe_ebgp_topo1/pe2/isisd.conf | 22 +++ .../bgp_6vpe_ebgp_topo1/pe2/zebra.conf | 15 ++ .../test_bgp_6vpe_ebgp_topo1.py | 179 ++++++++++++++++++ 15 files changed, 725 insertions(+) create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf create mode 100644 tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py b/tests/topotests/bgp_6vpe_ebgp_topo1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf new file mode 100644 index 000000000000..06a23bb0fd70 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/h1/zebra.conf @@ -0,0 +1,4 @@ +ipv6 route fd00:200::/64 fd00:100::2 +interface eth-pe1 + ipv6 address fd00:100::1/64 +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf new file mode 100644 index 000000000000..2dadfc40074c --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/h2/zebra.conf @@ -0,0 +1,8 @@ +ipv6 route fd00:100::/64 fd00:200::5 +interface eth-pe2 + ipv6 address fd00:200::6/64 + ipv6 address fd00:201::6/64 + ipv6 address fd00:300::6/64 + ipv6 address fd00:400::6/64 + ipv6 address fd01:200::6/64 +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json new file mode 100644 index 000000000000..c2100add8efa --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_summary.json @@ -0,0 +1,13 @@ +{ + "ipv6Vpn": { + "routerId": "198.51.100.2", + "as": 65500, + "peers": { + "192.0.2.5": { + "remoteAs": 65501, + "state": "Established", + "peerState": "OK" + } + } + } +} diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json new file mode 100644 index 000000000000..c6e776d069f3 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgp_vrf_ipv6.json @@ -0,0 +1,116 @@ +{ + "vrfName": "vrf1", + "routerId": "198.51.100.2", + "defaultLocPrf": 100, + "localAS": 65500, + "routes": { + "fd00:100::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:100::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:200::/64", + "metric": 0, + "weight": 0, + "path": "65501", + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:201::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:201::/64", + "metric": 0, + "weight": 0, + "path": "65501", + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:300::/64", + "metric": 0, + "weight": 0, + "path": "65501", + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:400::/64", + "metric": 0, + "weight": 0, + "path": "65501", + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd01:200::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd01:200::/64", + "metric": 0, + "weight": 0, + "path": "65501", + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf new file mode 100644 index 000000000000..26e94d4b971b --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/bgpd.conf @@ -0,0 +1,32 @@ +! +!debug bgp zebra +router bgp 65500 + bgp router-id 198.51.100.2 + no bgp ebgp-requires-policy + neighbor 192.0.2.5 remote-as 65501 + neighbor 192.0.2.5 capability extended-nexthop + address-family ipv4 unicast + no neighbor 192.0.2.5 activate + exit-address-family + address-family ipv6 vpn + neighbor 192.0.2.5 activate + neighbor 192.0.2.5 route-map rmap in + exit-address-family +exit +router bgp 65500 vrf vrf1 + bgp router-id 198.51.100.2 + address-family ipv6 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 eth-pe2 + mpls bgp forwarding +! +route-map rmap permit 1 + set ipv6 next-hop prefer-global +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json new file mode 100644 index 000000000000..154574963b9a --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/ipv6_routes_vrf.json @@ -0,0 +1,142 @@ +{ + "fd00:100::/64": [ + { + "prefix": "fd00:100::/64", + "protocol": "connected", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 0, + "metric": 0, + "installed": true, + "nexthops": [ + { + "fib": true, + "directlyConnected": true, + "interfaceName": "eth-h1", + "active": true + } + ] + } + ], + "fd00:200::/64": [ + { + "prefix": "fd00:200::/64", + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "vrf": "default", + "active": true, + "labels": [ + 102 + ], + "weight": 1 + } + ] + } + ], + "fd00:201::/64": [ + { + "prefix": "fd00:201::/64", + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "vrf": "default", + "active": true, + "labels": [ + 102 + ], + "weight": 1 + } + ] + } + ], + "fd00:300::/64": [ + { + "prefix": "fd00:300::/64", + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "vrf": "default", + "active": true, + "labels": [ + 102 + ], + "weight": 1 + } + ] + } + ], + "fd00:400::/64": [ + { + "prefix": "fd00:400::/64", + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "vrf": "default", + "active": true, + "labels": [ + 102 + ], + "weight": 1 + } + ] + } + ], + "fd01:200::/64": [ + { + "prefix": "fd01:200::/64", + "protocol": "bgp", + "vrfName": "vrf1", + "selected": true, + "destSelected": true, + "distance": 20, + "metric": 0, + "installed": true, + "nexthops": [ + { + "ip": "::ffff:c000:205", + "afi": "ipv6", + "vrf": "default", + "active": true, + "labels": [ + 102 + ], + "weight": 1 + } + ] + } + ] +} diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf new file mode 100644 index 000000000000..61f2fe7defe5 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/isisd.conf @@ -0,0 +1,23 @@ +! +interface lo + ip router isis 1 + isis hello-interval 1 + isis hello-multiplier 3 +! +interface eth-pe2 + ip router isis 1 + isis hello-interval 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0007.e901.2222.00 + is-type level-1 + lsp-gen-interval 1 + mpls-te on + mpls-te router-address 198.51.100.2 + segment-routing on + segment-routing node-msd 8 + segment-routing global-block 1000 10000 local-block 30000 30999 + segment-routing prefix 198.51.100.2/32 index 22 +! + diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf new file mode 100644 index 000000000000..7ddd98f6e7f4 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe1/zebra.conf @@ -0,0 +1,11 @@ +! +interface eth-h1 + ipv6 address fd00:100::2/64 +! +interface eth-pe2 + ip address 192.0.2.2/24 + ipv6 address ::ffff:192.0.2.2/120 +! +interface lo + ip address 198.51.100.2/32 +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json new file mode 100644 index 000000000000..d74079498e95 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_summary.json @@ -0,0 +1,13 @@ +{ + "ipv6Vpn": { + "routerId": "198.51.100.5", + "as": 65501, + "peers": { + "192.0.2.2": { + "remoteAs": 65500, + "state": "Established", + "peerState": "OK" + } + } + } +} diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json new file mode 100644 index 000000000000..ec42999e8a43 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgp_vrf_ipv6.json @@ -0,0 +1,116 @@ +{ + "vrfName": "vrf1", + "routerId": "198.51.100.5", + "defaultLocPrf": 100, + "localAS": 65501, + "routes": { + "fd00:100::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:100::/64", + "metric": 0, + "weight": 0, + "path": "65500", + "nexthops": [ + { + "ip": "::ffff:c000:202", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:200::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:200::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:201::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:201::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:300::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:300::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd00:400::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd00:400::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ], + "fd01:200::/64": [ + { + "valid": true, + "bestpath": true, + "network": "fd01:200::/64", + "metric": 0, + "weight": 32768, + "path": "", + "nexthops": [ + { + "ip": "::", + "afi": "ipv6", + "scope": "global", + "used": true + } + ] + } + ] + } +} diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf new file mode 100644 index 000000000000..03b63af90f54 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/bgpd.conf @@ -0,0 +1,31 @@ +! +router bgp 65501 + bgp router-id 198.51.100.5 + no bgp ebgp-requires-policy + neighbor 192.0.2.2 remote-as 65500 + neighbor 192.0.2.2 capability extended-nexthop + address-family ipv4 unicast + no neighbor 192.0.2.2 activate + exit-address-family + address-family ipv6 vpn + neighbor 192.0.2.2 activate + neighbor 192.0.2.2 route-map rmap in + exit-address-family +exit +router bgp 65501 vrf vrf1 + bgp router-id 198.51.100.5 + address-family ipv6 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 eth-pe1 + mpls bgp forwarding +! +route-map rmap permit 1 + set ipv6 next-hop prefer-global +! \ No newline at end of file diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf new file mode 100644 index 000000000000..f210554ff66c --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/isisd.conf @@ -0,0 +1,22 @@ +! +interface lo + ip router isis 1 + isis hello-interval 1 + isis hello-multiplier 3 +! +interface eth-pe1 + ip router isis 1 + isis hello-interval 1 + isis hello-multiplier 3 +! +router isis 1 + net 49.0000.0007.e901.5555.00 + is-type level-1 + lsp-gen-interval 1 + mpls-te on + mpls-te router-address 198.51.100.5 + segment-routing on + segment-routing node-msd 8 + segment-routing global-block 1000 10000 local-block 33000 33999 + segment-routing prefix 198.51.100.5/32 index 55 +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf new file mode 100644 index 000000000000..bf20638684f9 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/pe2/zebra.conf @@ -0,0 +1,15 @@ +! +interface eth-h2 + ipv6 address fd00:200::5/64 + ipv6 address fd00:201::5/64 + ipv6 address fd00:300::5/64 + ipv6 address fd00:400::5/64 + ipv6 address fd01:200::5/64 +! +interface eth-pe1 + ip address 192.0.2.5/24 + ipv6 address ::ffff:192.0.2.5/120 +! +interface lo + ip address 198.51.100.5/32 +! diff --git a/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py b/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py new file mode 100644 index 000000000000..cbed8f089654 --- /dev/null +++ b/tests/topotests/bgp_6vpe_ebgp_topo1/test_bgp_6vpe_ebgp_topo1.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python +# SPDX-License-Identifier: ISC + +# +# Copyright (c) 2023 by 6WIND +# + +""" +Test the FRR BGP 6VPE functionality +""" + +import os +import sys +import json +import functools +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 +from lib.checkping import check_ping + +pytestmark = [pytest.mark.bgpd, pytest.mark.isisd] + + +def build_topo(tgen): + """ + +---+ +---+ +---+ +---+ + | h1|----|pe1|----|pe2|----| h2| + +---+ +---+ +---+ +---+ + """ + + def connect_routers(tgen, left, right): + pe = None + host = None + for rname in [left, right]: + if rname not in tgen.routers().keys(): + tgen.add_router(rname) + if "pe" in rname: + pe = tgen.gears[rname] + if "h" in rname: + host = tgen.gears[rname] + + switch = tgen.add_switch("s-{}-{}".format(left, right)) + switch.add_link(tgen.gears[left], nodeif="eth-{}".format(right)) + switch.add_link(tgen.gears[right], nodeif="eth-{}".format(left)) + + if pe and host: + pe.cmd("ip link add vrf1 type vrf table 10") + pe.cmd("ip link set vrf1 up") + pe.cmd("ip link set dev eth-{} master vrf1".format(host.name)) + + if "p" in left and "p" in right: + # PE <-> P or P <-> P + tgen.gears[left].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(right)) + tgen.gears[right].run("sysctl -w net.mpls.conf.eth-{}.input=1".format(left)) + + connect_routers(tgen, "h1", "pe1") + connect_routers(tgen, "pe1", "pe2") + connect_routers(tgen, "pe2", "h2") + + +def setup_module(mod): + "Sets up the pytest environment" + + tgen = Topogen(build_topo, mod.__name__) + tgen.start_topology() + logger.info("setup_module") + + for rname, router in tgen.routers().items(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + if "h" in rname: + # hosts + continue + + router.load_config( + TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.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_bgp_convergence(): + "Assert that BGP is converging." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers to go up") + + router_list = ["pe1", "pe2"] + + for name in router_list: + router = tgen.gears[name] + ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, router, "show bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=90, wait=1) + assertmsg = "{}: bgp did not converge".format(router.name) + assert res is None, assertmsg + + +def test_bgp_ipv6_vpn(): + "Assert that BGP is exchanging BGP route." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for bgp peers exchanging UPDATES") + + router_list = ["pe1", "pe2"] + + for name in router_list: + router = tgen.gears[name] + ref_file = "{}/{}/bgp_vrf_ipv6.json".format(CWD, router.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, + router, + "show bgp vrf vrf1 ipv6 unicast json", + expected, + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: BGP UPDATE exchange failure".format(router.name) + assert res is None, assertmsg + + +def test_zebra_ipv6_installed(): + "Assert that routes are installed." + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + pe1 = tgen.gears["pe1"] + logger.info("check ipv6 routes installed on pe1") + + ref_file = "{}/{}/ipv6_routes_vrf.json".format(CWD, pe1.name) + expected = json.loads(open(ref_file).read()) + test_func = partial( + topotest.router_json_cmp, pe1, "show ipv6 route vrf vrf1 json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=30, wait=1) + assertmsg = "{}: Zebra Installation failure on vrf vrf1".format(pe1.name) + assert res is None, assertmsg + + +def test_bgp_ping6_ok(): + "Check that h1 pings h2" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + check_ping("h1", "fd00:200::6", True, 5, 1) + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) From 0325116a27258e1df773a046e8668a029bead60c Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Thu, 15 Feb 2024 13:28:02 +0100 Subject: [PATCH 2/2] bgpd: fix 6vpe nexthop 6vPE enables the announcement of IPv6 VPN prefixes through an IPv4 BGP session. In this scenario, the next hop addresses for these prefixes are represented in an IPv4-mapped IPv6 format, noted as ::ffff:[IPv4]. This format indicates to the peer that it should route these IPv6 addresses using information from the IPv4 nexthop. For example: > Path Attribute - MP_REACH_NLRI > [...] > Address family identifier (AFI): IPv6 (2) > Subsequent address family identifier (SAFI): Labeled VPN Unicast (128) > Next hop: RD=0:0 IPv6=::ffff:192.0.2.5 RD=0:0 Link-local=fe80::501d:42ff:feef:b021 > Number of Subnetwork points of attachment (SNPA): 0 This rule is set out in RFC4798: > The IPv4 address of the egress 6PE router MUST be encoded as an > IPv4-mapped IPv6 address in the BGP Next Hop field. However, in some situations, bgpd sends a standard nexthop IPv6 address instead of an IPv4-mapped IPv6 address because the outgoing interface for the BGP session has a valid IPv6 address. This is problematic because the peer router may not be able to route the nexthop IPv6 address (ie. if the outgoing interface has not IPv6). Fix the issue by always sending a IPv4-mapped IPv6 address as nexthop when the BGP session is on IPv4 and address family IPv6. Link: https://datatracker.ietf.org/doc/html/rfc4798#section-2 Fixes: 92d6f76 ("lib,zebra,bgpd: Fix for nexthop as IPv4 mapped IPv6 address") Signed-off-by: Louis Scalbert --- bgpd/bgp_updgrp_packet.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c index 7502bf2ec6dc..bff52c80d8c7 100644 --- a/bgpd/bgp_updgrp_packet.c +++ b/bgpd/bgp_updgrp_packet.c @@ -523,11 +523,16 @@ struct stream *bpacket_reformat_for_peer(struct bpacket *pkt, gnh_modified = 1; } - if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) { - if (peer->nexthop.v4.s_addr != INADDR_ANY) { - ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, - peer->nexthop.v4); - } + if (peer->nexthop.v4.s_addr != INADDR_ANY && + (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg) || + (peer->connection->su.sa.sa_family == AF_INET && + paf->afi == AFI_IP6))) { + /* set a IPv4 mapped IPv6 address if no global IPv6 + * address is found or if announcing IPv6 prefix + * over an IPv4 BGP session. + */ + ipv4_to_ipv4_mapped_ipv6(mod_v6nhg, peer->nexthop.v4); + gnh_modified = 1; } if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) {