Summary
load-balancing wan rule <N> inbound-interface silently fails to match any traffic when set to any or to a wildcard using the + suffix (e.g. eth+), even though any is offered as a CLI completion and eth+ is documented as a valid wildcard in the official configuration examples. The rule commits without error, but the resulting nftables rule contains the value as a literal interface name (iifname "any" / iifname "eth+"), which never matches any real interface and results in packets 0 bytes 0 forever. This is a silent failure — there is no commit-time warning, verify() does not validate the value, and show wan-load-balance gives no indication that the rule is dead. In our case this caused a full internet outage during a WAN failover test, because the failover rule appeared configured correctly but never actually matched traffic.
Steps to reproduce
set load-balancing wan interface-health eth0 nexthop dhcp
set load-balancing wan interface-health eth1 nexthop dhcp
set load-balancing wan rule 10 inbound-interface 'any'
set load-balancing wan rule 10 interface eth0 weight 10
set load-balancing wan rule 10 interface eth1 weight 1
set load-balancing wan rule 10 failover
commit
Then generate some traffic through any LAN-facing interface and inspect the live nftables ruleset with: sudo nft list chain ip vyos_wanloadbalance wlb_mangle_prerouting
Expected behavior
Per the official documentation (both 1.5 LTS and rolling, configexamples/wan-load-balancing.html, Example 5), eth+ is described as "an alias that refers to all ethernet interfaces". The CLI also offers any as a completion for inbound-interface (interface-definitions/load-balancing_wan.xml.in, completionHelp list any, and the compiled node.def: allowed: echo "any" && echo " " && sh -c "${vyos_completion_dir}/list_interfaces"). Either value should therefore match the intended set of interfaces.
Actual behavior
The generated nftables rule contains the raw string as a literal iifname match, which never matches any real interface: iifname "any" ct state new ... or iifname "eth+" ct state new ... Traffic counters for this rule stay at packets 0 bytes 0 indefinitely, regardless of actual traffic volume.
Root cause
In python/vyos/wanloadbalance.py, function nft_rule(), the relevant code is:
if 'inbound_interface' in rule_conf: ifname = rule_conf['inbound_interface'] if local and not exclude: output.append(f'oifname != "{ifname}"') elif not local: output.append(f'iifname "{ifname}"')
The value of inbound_interface is inserted into the nftables expression verbatim, with no handling for any as a special "match everything" value (referenced only in the CLI completion, never implemented in the rendering logic), and no handling for + as a legacy wildcard suffix (eth+), which was the convention under the old C++ daemon (vyatta-wanloadbalance) but is meaningless to nftables. Separately, src/conf_mode/load-balancing_wan.py's verify() performs no validation at all on inbound_interface — no interface-existence check, no wildcard-syntax check — so an invalid value like this commits cleanly with no warning.
Note that nftables' own wildcard syntax uses *, not + (iifname "eth*"), and this does work correctly, since nftables itself expands the * — the Python code plays no part in that expansion. This appears to be referenced in the February 2025 VyOS Networks Blog post ("Rewritten WAN load balancing daemon (T4470)"): "if you want a rule to apply to all interfaces of a type, you should write eth* rather than eth+, as per nftables conventions (T7196)". However: (1) any is not mentioned anywhere in that migration note, and has no working equivalent in the new implementation; (2) the configuration examples page (docs.vyos.io/en/rolling/configexamples/wan-load-balancing.html, also present in the 1.5 LTS docs) still documents eth+ as the recommended wildcard and has not been updated to eth*; (3) the CLI still completes any as a suggested value (interface-definitions/load-balancing_wan.xml.in), implying it is supported when it is not.
Suggested fix
Option (a): implement any and +-suffix handling in nft_rule() (e.g. emit no iifname/oifname clause at all for any, and expand eth+ to eth* for backward compatibility with pre-migration configs), plus add commit-time validation in verify() so an unrecognized interface name/pattern raises a ConfigError instead of committing silently. Option (b): if any and +-wildcards are intentionally unsupported in the new implementation, remove any from the CLI completion list, add a constraint to inbound-interface in interface-definitions/load-balancing_wan.xml.in that accepts a valid interface name or an nftables-style *-suffixed pattern (similar to interface-definitions/include/constraint/interface-name-with-wildcard.xml.i and interface-definitions/include/firewall/inbound-interface-no-group.xml.i, which already support this correctly for firewall rules), and update the configuration examples documentation from eth+ to eth*. Given that this is a routing/failover feature where a silently non-matching rule can cause a full traffic blackhole (as in our case), option (a) combined with commit-time validation is the safer choice, but option (b) alone would already prevent the silent failure.
Additional notes
T7196 and the referenced nftables convention change appear to only be documented in the monthly blog recap, not reflected in the CLI help text, completion list, or configuration examples page — all of which still suggest eth+/any are valid.