aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorVladislav Grishenko <themiron@yandex-team.ru>2024-01-24 16:17:23 +0500
committerNeale Ranns <neale@graphiant.com>2024-04-09 04:47:02 +0000
commit302db471a01b5c21b80e9bc769315cef4da0ed80 (patch)
treebe284a966c720d6dc601128759e42a62553db2a7 /test
parent2a7bc81ae302b02e8e0d4eec7db82c1ca15b7f7a (diff)
mpls: fix default mpls lb hash config
In case of multiple path within tunnel, mpls lookup node computes lb hash with mpls_compute_flow_hash config value 0, so only mpls label and l4 ports gets accounted, not 5-tuple. This leads to flow traffic polarization and disbalance over mpls paths. Use mpls hash config from lb instead, usually it'll be MPLS_FLOw_HASH_DEFAULT with 5-tuple plus flowlabel. As optimization, fix flow hash reuse from the previous lookup node if present, like ip_lookup does. Previously mpls lookup always calcs the hash. Test lb distribution for both cases. Also, use the same flow hash hex format in ip4/ip6 and mpls traces for easier reading, most code changes is due fixstyle formatting. Type: fix Signed-off-by: Vladislav Grishenko <themiron@yandex-team.ru> Change-Id: Ib89e1ab3edec14269866fe825a3e887d6c817b7c
Diffstat (limited to 'test')
-rw-r--r--test/test_mpls.py107
1 files changed, 101 insertions, 6 deletions
diff --git a/test/test_mpls.py b/test/test_mpls.py
index cd44f94d951..9c07251a481 100644
--- a/test/test_mpls.py
+++ b/test/test_mpls.py
@@ -188,16 +188,17 @@ class TestMPLS(VppTestCase):
return pkts
def create_stream_ip4(
- self, src_if, dst_ip, ip_ttl=64, ip_dscp=0, payload_size=None
+ self, src_if, dst_ip, ip_ttl=64, ip_dscp=0, payload_size=None, n=257
):
self.reset_packet_infos()
pkts = []
- for i in range(0, 257):
+ for i in range(0, n):
+ dst = dst_ip[i % len(dst_ip)] if isinstance(dst_ip, list) else dst_ip
info = self.create_packet_info(src_if, src_if)
payload = self.info_to_payload(info)
p = (
Ether(dst=src_if.local_mac, src=src_if.remote_mac)
- / IP(src=src_if.remote_ip4, dst=dst_ip, ttl=ip_ttl, tos=ip_dscp)
+ / IP(src=src_if.remote_ip4, dst=dst, ttl=ip_ttl, tos=ip_dscp)
/ UDP(sport=1234, dport=1234)
/ Raw(payload)
)
@@ -207,15 +208,16 @@ class TestMPLS(VppTestCase):
pkts.append(p)
return pkts
- def create_stream_ip6(self, src_if, dst_ip, ip_ttl=64, ip_dscp=0):
+ def create_stream_ip6(self, src_if, dst_ip, ip_ttl=64, ip_dscp=0, n=257):
self.reset_packet_infos()
pkts = []
- for i in range(0, 257):
+ for i in range(0, n):
+ dst = dst_ip[i % len(dst_ip)] if isinstance(dst_ip, list) else dst_ip
info = self.create_packet_info(src_if, src_if)
payload = self.info_to_payload(info)
p = (
Ether(dst=src_if.local_mac, src=src_if.remote_mac)
- / IPv6(src=src_if.remote_ip6, dst=dst_ip, hlim=ip_ttl, tc=ip_dscp)
+ / IPv6(src=src_if.remote_ip6, dst=dst, hlim=ip_ttl, tc=ip_dscp)
/ UDP(sport=1234, dport=1234)
/ Raw(payload)
)
@@ -1341,6 +1343,99 @@ class TestMPLS(VppTestCase):
],
)
+ def test_tunnel_ecmp(self):
+ """MPLS Tunnel Tests - ECMP"""
+
+ #
+ # Create a tunnel with multiple paths and labels
+ #
+ self.pg0.generate_remote_hosts(2)
+ self.pg0.configure_ipv4_neighbors()
+ mpls_tun = VppMPLSTunnelInterface(
+ self,
+ [
+ VppRoutePath(
+ self.pg0.remote_hosts[0].ip4,
+ self.pg0.sw_if_index,
+ labels=[VppMplsLabel(3)],
+ ),
+ VppRoutePath(
+ self.pg0.remote_hosts[1].ip4,
+ self.pg0.sw_if_index,
+ labels=[VppMplsLabel(44)],
+ ),
+ ],
+ )
+ mpls_tun.add_vpp_config()
+ mpls_tun.admin_up()
+
+ self.vapi.cli("clear trace")
+ pkts = self.create_stream_ip4(
+ self.pg0, ["10.0.0.%d" % i for i in range(NUM_PKTS)], n=NUM_PKTS
+ )
+
+ def send_and_expect_mpls_lb(pkts, path_labels, min_ratio):
+ self.pg0.add_stream(pkts)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ rx = self.pg0.get_capture()
+
+ paths = {}
+ for packet in rx:
+ eth = packet[Ether]
+ self.assertEqual(eth.type, 0x8847)
+
+ mpls = packet[MPLS]
+ labels = []
+ while True:
+ labels.append(mpls.label)
+ if mpls.s == 1:
+ break
+ mpls = mpls[MPLS].payload
+ self.assertIn(labels, path_labels)
+
+ key = "{}-{}".format(eth.dst, "-".join(str(i) for i in labels))
+ paths[key] = paths.get(key, 0) + 1
+
+ #
+ # Check distribution over multiple mpls paths
+ #
+ self.assertEqual(len(paths), len(path_labels))
+ for n in paths.values():
+ self.assertGreaterEqual(n, NUM_PKTS / len(paths) * min_ratio)
+
+ #
+ # Add labelled route through the new tunnel,
+ # traffic should be balanced over all tunnel paths only.
+ #
+ route_10_0_0_0 = VppIpRoute(
+ self,
+ "10.0.0.0",
+ 16,
+ [VppRoutePath("0.0.0.0", mpls_tun._sw_if_index, labels=[33])],
+ )
+ route_10_0_0_0.add_vpp_config()
+ send_and_expect_mpls_lb(pkts, [[33], [44, 33]], 0.85)
+
+ #
+ # Add labelled multipath route through the new tunnel,
+ # traffic should be balanced over both paths first and
+ # then over all tunnel paths.
+ #
+ route_10_0_0_0 = VppIpRoute(
+ self,
+ "10.0.0.0",
+ 16,
+ [
+ VppRoutePath("0.0.0.1", mpls_tun._sw_if_index, labels=[33]),
+ VppRoutePath("0.0.0.2", mpls_tun._sw_if_index, labels=[34]),
+ ],
+ )
+ route_10_0_0_0.add_vpp_config()
+ send_and_expect_mpls_lb(pkts, [[33], [44, 33], [34], [44, 34]], 0.70)
+
def test_mpls_tunnel_many(self):
"""MPLS Multiple Tunnels"""