-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Pmsi tunnel v6 length #21488
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pmsi tunnel v6 length #21488
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,6 +108,9 @@ enum pta_type { | |
| PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP | ||
| }; | ||
|
|
||
| #define BGP_ATTR_PMSI_TUNNEL_V4_LENGTH 9 | ||
| #define BGP_ATTR_PMSI_TUNNEL_V6_LENGTH 21 | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These lengths look specific to PMSI_TNLTYPE_INGR_REPL. Are they valid for other PMSI tunnel types too? If not, should the constant names include INGR_REPL to make that scope explicit?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From some very superficial reading of the RFC's associated here (rfc6514 and rfc7988) I believe that it applies to a bunch of pmsi tunnel types but since we do not have those programmed yet I did not do anything other than make the generic tunnel lengths. Hopefully someone will program the other types in the near future |
||
| /* | ||
| * Prefix-SID type-4 | ||
| * SRv6-VPN-SID-TLV | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "101": { | ||
| "type": "L2", | ||
| "numRemoteVteps": 1, | ||
| "remoteVteps": [ | ||
| "fd00:100::2" | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| interface r1-eth0 | ||
| ipv6 address fd00:100::1/64 | ||
| ! | ||
| router bgp 65000 | ||
| bgp router-id 10.10.10.1 | ||
| bgp log-neighbor-changes | ||
| no bgp default ipv4-unicast | ||
| neighbor fd00:100::2 remote-as 65000 | ||
| neighbor fd00:100::2 capability extended-nexthop | ||
| ! | ||
| address-family l2vpn evpn | ||
| neighbor fd00:100::2 activate | ||
| advertise-all-vni | ||
| exit-address-family | ||
| ! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| { | ||
| "101": { | ||
| "type": "L2", | ||
| "numRemoteVteps": 1, | ||
| "remoteVteps": [ | ||
| "fd00:100::1" | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| interface r2-eth0 | ||
| ipv6 address fd00:100::2/64 | ||
| ! | ||
| router bgp 65000 | ||
| bgp router-id 10.10.10.2 | ||
| bgp log-neighbor-changes | ||
| no bgp default ipv4-unicast | ||
| neighbor fd00:100::1 remote-as 65000 | ||
| neighbor fd00:100::1 capability extended-nexthop | ||
| ! | ||
| address-family l2vpn evpn | ||
| neighbor fd00:100::1 activate | ||
| advertise-all-vni | ||
| exit-address-family | ||
| ! |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| #!/usr/bin/env python | ||
| # SPDX-License-Identifier: ISC | ||
|
|
||
| # | ||
| # test_bgp_evpn_v6_pmsi_tunnel.py | ||
| # | ||
| # Copyright (c) 2025 Nvidia Inc. | ||
| # Donald Sharp | ||
|
|
||
| """ | ||
| Test that EVPN with an IPv6-only underlay correctly encodes and parses | ||
| the PMSI Tunnel attribute with an IPv6 tunnel identifier (length 21). | ||
|
|
||
| Type-3 IMET routes carry the PMSI Tunnel attribute, so this test verifies | ||
| that two FRR routers with IPv6-only EVPN peering and IPv6 VTEPs can | ||
| exchange type-3 routes and install remote VTEPs. | ||
| """ | ||
|
|
||
| import json | ||
| from functools import partial | ||
| import os | ||
| import sys | ||
| import pytest | ||
| import platform | ||
|
|
||
| CWD = os.path.dirname(os.path.realpath(__file__)) | ||
| sys.path.append(os.path.join(CWD, "../")) | ||
|
|
||
| from lib import topotest | ||
| from lib.topogen import Topogen, get_topogen | ||
| from lib.topolog import logger | ||
|
|
||
| pytestmark = [pytest.mark.bgpd] | ||
|
|
||
|
|
||
| def build_topo(tgen): | ||
| 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"]) | ||
|
|
||
|
|
||
| def setup_module(mod): | ||
| tgen = Topogen(build_topo, mod.__name__) | ||
| tgen.start_topology() | ||
|
|
||
| krel = platform.release() | ||
| if topotest.version_cmp(krel, "4.18") < 0: | ||
| logger.info( | ||
| 'BGP EVPN v6 PMSI tests require kernel >= 4.18 (have "{}")'.format(krel) | ||
| ) | ||
| return pytest.skip("Kernel too old for EVPN NETNS test") | ||
|
|
||
| cmds_vxlan = [ | ||
| "ip link add name bridge-101 up type bridge stp_state 0", | ||
| "ip link set dev bridge-101 up", | ||
| "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev {0}-eth0 local {1}", | ||
| "ip link set dev vxlan-101 master bridge-101", | ||
| "ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off", | ||
| ] | ||
|
|
||
| for rname, vtep_ip in [("r1", "fd00:100::1"), ("r2", "fd00:100::2")]: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add test for iPv4?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are already about 5-10 different topotests that show v4 behavior, I don't think I need to write a v4 specific test. I just wanted to ahve a new test that had absolutely no v4 addresses at all. |
||
| router = tgen.gears[rname] | ||
| for cmd in cmds_vxlan: | ||
| formatted = cmd.format(rname, vtep_ip) | ||
| logger.info("cmd to {}: {}".format(rname, formatted)) | ||
| output = router.cmd_raises(formatted) | ||
| logger.info("result: " + output) | ||
|
|
||
| for rname, router in tgen.routers().items(): | ||
| logger.info("Loading router %s" % rname) | ||
| router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname))) | ||
|
|
||
| tgen.start_router() | ||
|
|
||
|
|
||
| def teardown_module(_mod): | ||
| tgen = get_topogen() | ||
| tgen.stop_topology() | ||
|
|
||
|
|
||
| def test_bgp_convergence(): | ||
| """ | ||
| Assert that BGP EVPN sessions come up between r1 and r2 over IPv6. | ||
| """ | ||
| tgen = get_topogen() | ||
| if tgen.routers_have_failure(): | ||
| pytest.skip(tgen.errors) | ||
|
|
||
| for rname in ("r1", "r2"): | ||
| router = tgen.gears[rname] | ||
|
|
||
| expected = {"peers": {}} | ||
| if rname == "r1": | ||
| expected["peers"]["fd00:100::2"] = {"state": "Established"} | ||
| else: | ||
| expected["peers"]["fd00:100::1"] = {"state": "Established"} | ||
|
|
||
| test_func = partial( | ||
| topotest.router_json_cmp, | ||
| router, | ||
| "show bgp l2vpn evpn summary json", | ||
| expected, | ||
| ) | ||
| _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) | ||
| assert result is None, '"{}" BGP session did not establish'.format(rname) | ||
|
|
||
|
|
||
| def test_evpn_type3_routes(): | ||
| """ | ||
| Verify EVPN type-3 IMET routes are received. These carry the PMSI Tunnel | ||
| attribute. With IPv6-only peering, the PMSI tunnel identifier must be | ||
| encoded as 21 bytes (IPv6). Without the fix, these routes would be | ||
| rejected as malformed. | ||
| """ | ||
| tgen = get_topogen() | ||
| if tgen.routers_have_failure(): | ||
| pytest.skip(tgen.errors) | ||
|
|
||
| for rname in ("r1", "r2"): | ||
| router = tgen.gears[rname] | ||
| json_file = "{}/{}/evpn_type3.json".format(CWD, rname) | ||
| expected = json.loads(open(json_file).read()) | ||
|
|
||
| test_func = partial( | ||
| topotest.router_json_cmp, | ||
| router, | ||
| "show evpn vni json", | ||
| expected, | ||
| ) | ||
| _, result = topotest.run_and_expect(test_func, None, count=30, wait=1) | ||
| assert result is None, '"{}" EVPN type-3 route check failed'.format(rname) | ||
|
|
||
|
|
||
| def test_memory_leak(): | ||
| 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)) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we check for the attribute flag (
bgp_attr_exists()) instead?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so from a quick scan of the source code the attr->mp_nexthop_len is just checked like I do here, the same with attr->nexthop. So I was just following coding conventions. If you think we should switch to a different way of doing it, that is fine but I think it belongs in it's own cleanup work since we would need to touch a bunch of different spots.