summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-10-17 20:55:01 +0200
committerChristian Breunig <christian@breunig.cc>2026-10-17 20:56:32 +0200
commite71bb87517cec1818714f4dd7199a524494c1949 (patch)
tree4800ae4cdced47758d63268dae7eb2bc21b6838b
parent272b6e646d64651af0b5e8e726021e4fb09cd872 (diff)
downloadvyos-1x-e71bb87517cec1818714f4dd7199a524494c1949.tar.gz
vyos-1x-e71bb87517cec1818714f4dd7199a524494c1949.zip
wlb: T9472: honour per-packet-balancing on rules
The renderer checked a key no CLI node produces, so "per-packet-balancing" had no effect and every rule balanced per flow only. With the option set, the rule no longer matches new connections only and every packet is balanced on its own.
-rw-r--r--python/vyos/wanloadbalance.py3
-rwxr-xr-xsmoketest/scripts/cli/test_load-balancing_wan.py41
2 files changed, 43 insertions, 1 deletions
diff --git a/python/vyos/wanloadbalance.py b/python/vyos/wanloadbalance.py
index 1dc9a8081..fc9fb1ade 100644
--- a/python/vyos/wanloadbalance.py
+++ b/python/vyos/wanloadbalance.py
@@ -112,7 +112,8 @@ def nft_rule(rule_conf, rule_id, local=False, exclude=False, limit=False, weight
output.append(f'{proto} {prefix}port {operator} @P_{group_name}')
- if 'source_based_routing' not in rule_conf and not restore_mark:
+ # per flow by default - only the first packet of a connection is balanced
+ if 'per_packet_balancing' not in rule_conf and not restore_mark:
output.append('ct state new')
if limit and 'limit' in rule_conf and 'rate' in rule_conf['limit']:
diff --git a/smoketest/scripts/cli/test_load-balancing_wan.py b/smoketest/scripts/cli/test_load-balancing_wan.py
index 686c9e04e..c46c445bb 100755
--- a/smoketest/scripts/cli/test_load-balancing_wan.py
+++ b/smoketest/scripts/cli/test_load-balancing_wan.py
@@ -117,6 +117,24 @@ class TestLoadBalancingWan(VyOSUnitTestSHIM.TestCase):
f'TCP:{lab_target}:{port},connect-timeout=2{bind} </dev/null')
return out.splitlines()[-1] if rc == 0 and out else None
+ @staticmethod
+ def lab_count_echo_requests(isp_count):
+ """ Return how many ICMP echo requests every ISP received since the
+ counters were added """
+ counts = []
+ for index in range(1, isp_count + 1):
+ rc, out = rc_cmd(f'sudo ip netns exec wlb-isp{index} nft list table ip lab')
+ counts.append(int(out.split('packets ')[1].split()[0]) if rc == 0 else 0)
+ return counts
+
+ @staticmethod
+ def lab_add_echo_counters(isp_count):
+ for index in range(1, isp_count + 1):
+ ns = f'wlb-isp{index}'
+ cmd_in_netns(ns, 'nft add table ip lab')
+ cmd_in_netns(ns, "nft add chain ip lab c '{ type filter hook input priority 0; }'")
+ cmd_in_netns(ns, 'nft add rule ip lab c icmp type echo-request counter')
+
def lab_wait_status(self, expected, timeout=30):
found, last = self.wait_for_result(wlb_status, expected, timeout=timeout)
self.assertTrue(found, msg=f'health state {last}, expected {expected}')
@@ -746,6 +764,29 @@ printf 'WLB_INTERFACE_NAME=%s WLB_SCRIPT_IFACE=%s\\n' \\
self.assertNotIn('"any"', out)
answers = {self.lab_connect() for _ in range(20)}
self.assertEqual(answers, {'isp1 peer=10.250.1.1', 'isp2 peer=10.250.2.1'})
+ def test_per_packet_balancing(self):
+ # the option was never read, every rule balanced per flow
+ isps = ['eth231', 'eth232']
+ self.lab_setup('eth230', isps)
+ for index, ifname in enumerate(isps, start=1):
+ self.lab_health(ifname, index)
+ self.cli_set(base_path + ['wan', 'rule', '10', 'interface', ifname])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', 'eth230'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'protocol', 'icmp'])
+ self.cli_set(base_path + ['wan', 'rule', '10', 'per-packet-balancing'])
+ self.cli_commit()
+
+ self.lab_wait_status({'eth231': 'ACTIVE', 'eth232': 'ACTIVE'})
+ found, out = self.wait_for_result(lambda: wlb_chain('wlb_mangle_prerouting'),
+ lambda out: 'meta l4proto icmp counter' in out, timeout=15)
+ self.assertTrue(found, msg=out)
+ self.assertNotIn('ct state new', out)
+
+ # one ICMP flow - per packet, both ISPs see some of its requests
+ self.lab_add_echo_counters(len(isps))
+ cmd_in_netns('wlb-lan', f'ping -c 20 -i 0.05 -W 1 {lab_target}')
+ counts = self.lab_count_echo_requests(len(isps))
+ self.assertTrue(all(counts), msg=f'echo requests per ISP: {counts}')
if __name__ == '__main__':
unittest.main(verbosity=2, failfast=VyOSUnitTestSHIM.TestCase.debug_on())