From 84e578861e3a58ddc3ac7cad337be5abda684bc3 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Wed, 26 Feb 2025 10:26:13 +0100 Subject: [PATCH 1/3] wlb: T7196: Migrate interface wildcards to nftables format --- .../version/wanloadbalance-version.xml.i | 2 +- src/migration-scripts/wanloadbalance/3-to-4 | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/migration-scripts/wanloadbalance/3-to-4 diff --git a/interface-definitions/include/version/wanloadbalance-version.xml.i b/interface-definitions/include/version/wanloadbalance-version.xml.i index 59f8729cc1..34c3c76ff0 100644 --- a/interface-definitions/include/version/wanloadbalance-version.xml.i +++ b/interface-definitions/include/version/wanloadbalance-version.xml.i @@ -1,3 +1,3 @@ - + diff --git a/src/migration-scripts/wanloadbalance/3-to-4 b/src/migration-scripts/wanloadbalance/3-to-4 new file mode 100644 index 0000000000..e49f46a5b0 --- /dev/null +++ b/src/migration-scripts/wanloadbalance/3-to-4 @@ -0,0 +1,33 @@ +# Copyright 2025 VyOS maintainers and contributors +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this library. If not, see . + +from vyos.configtree import ConfigTree + +base = ['load-balancing', 'wan'] + +def migrate(config: ConfigTree) -> None: + if not config.exists(base): + # Nothing to do + return + + if config.exists(base + ['rule']): + for rule in config.list_nodes(base + ['rule']): + rule_base = base + ['rule', rule] + + if config.exists(rule_base + ['inbound-interface']): + ifname = config.return_value(rule_base + ['inbound-interface']) + + if ifname.endswith('+'): + config.set(rule_base + ['inbound-interface'], value=ifname.replace('+', '*')) From b8708570ae25c63960f4aeb08e0ba648d683e308 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:29:18 +0100 Subject: [PATCH 2/3] wlb: T7196: Fix exclude/interface verify check --- smoketest/scripts/cli/test_load-balancing_wan.py | 6 +++++- src/conf_mode/load-balancing_wan.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/smoketest/scripts/cli/test_load-balancing_wan.py b/smoketest/scripts/cli/test_load-balancing_wan.py index f652988b2a..32e5f69157 100755 --- a/smoketest/scripts/cli/test_load-balancing_wan.py +++ b/smoketest/scripts/cli/test_load-balancing_wan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2022-2024 VyOS maintainers and contributors +# Copyright (C) 2022-2025 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -272,6 +272,9 @@ def test_criteria_failover_hook(self): self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'failure-count', '1']) self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'nexthop', '192.0.2.2']) self.cli_set(base_path + ['wan', 'interface-health', isp2_iface, 'success-count', '1']) + self.cli_set(base_path + ['wan', 'rule', '5', 'exclude']) + self.cli_set(base_path + ['wan', 'rule', '5', 'inbound-interface', 'eth*']) + self.cli_set(base_path + ['wan', 'rule', '5', 'destination', 'address', '10.0.0.0/8']) self.cli_set(base_path + ['wan', 'rule', '10', 'failover']) self.cli_set(base_path + ['wan', 'rule', '10', 'inbound-interface', lan_iface]) self.cli_set(base_path + ['wan', 'rule', '10', 'protocol', 'udp']) @@ -291,6 +294,7 @@ def test_criteria_failover_hook(self): # Verify isp1 + criteria nftables_search = [ + [f'iifname "eth*"', 'ip daddr 10.0.0.0/8', 'return'], [f'iifname "{lan_iface}"', 'ip saddr 198.51.100.0/24', 'udp sport 53', 'ip daddr 192.0.2.0/24', 'udp dport 53', f'jump wlb_mangle_isp_{isp1_iface}'] ] diff --git a/src/conf_mode/load-balancing_wan.py b/src/conf_mode/load-balancing_wan.py index b3dd80a9ae..92d9acfba4 100755 --- a/src/conf_mode/load-balancing_wan.py +++ b/src/conf_mode/load-balancing_wan.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2023-2024 VyOS maintainers and contributors +# Copyright (C) 2023-2025 VyOS maintainers and contributors # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 or later as @@ -71,8 +71,8 @@ def verify(lb): if 'rule' in lb: for rule_id, rule_conf in lb['rule'].items(): - if 'interface' not in rule_conf: - raise ConfigError(f'Interface not specified on load-balancing wan rule {rule_id}') + if 'interface' not in rule_conf and 'exclude' not in rule_conf: + raise ConfigError(f'Interface or exclude not specified on load-balancing wan rule {rule_id}') if 'failover' in rule_conf and 'exclude' in rule_conf: raise ConfigError(f'Failover cannot be configured with exclude on load-balancing wan rule {rule_id}') From 51c64a3748c855dadfc2aff1e05916e1085f8da3 Mon Sep 17 00:00:00 2001 From: sarthurdev <965089+sarthurdev@users.noreply.github.com> Date: Wed, 26 Feb 2025 12:30:14 +0100 Subject: [PATCH 3/3] wlb: T7196: Extra sanity check on ipv4 address function --- src/helpers/vyos-load-balancer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/vyos-load-balancer.py b/src/helpers/vyos-load-balancer.py index 2f07160b44..30329fd5c0 100755 --- a/src/helpers/vyos-load-balancer.py +++ b/src/helpers/vyos-load-balancer.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 -# Copyright 2024 VyOS maintainers and contributors +# Copyright 2024-2025 VyOS maintainers and contributors # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -95,7 +95,7 @@ def on_state_change(lb, ifname, state): def get_ipv4_address(ifname): # Get primary ipv4 address on interface (for source nat) addr_json = get_interface_address(ifname) - if 'addr_info' in addr_json and len(addr_json['addr_info']) > 0: + if addr_json and 'addr_info' in addr_json and len(addr_json['addr_info']) > 0: for addr_info in addr_json['addr_info']: if addr_info['family'] == 'inet': if 'local' in addr_info: