diff options
| author | Oleksandr Kuchmystyi <o.kuchmystyi@vyos.io> | 2026-04-02 15:31:32 +0300 |
|---|---|---|
| committer | Oleksandr Kuchmystyi <o.kuchmystyi@vyos.io> | 2026-04-06 15:18:12 +0300 |
| commit | 227699e62146c71c3a0d3923650edd247b20ef16 (patch) | |
| tree | 07b395551d6132d27ff395803b61886a2e2a56f3 /src/migration-scripts | |
| parent | 0792a6b2b7ab32006ff45832a35de4536547cbd3 (diff) | |
| download | vyos-1x-227699e62146c71c3a0d3923650edd247b20ef16.tar.gz vyos-1x-227699e62146c71c3a0d3923650edd247b20ef16.zip | |
firewall: T8281: Normalize network prefixes in rule configuration
Convert non-canonical IPv4 and IPv6 prefixes in 'source/destination address'
fields to proper network addresses (e.g. 10.10.10.1/30 -> 10.10.10.0/30).
This prevents invalid configurations and ensures consistent prefix handling.
Diffstat (limited to 'src/migration-scripts')
| -rw-r--r-- | src/migration-scripts/firewall/10-to-11 | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/migration-scripts/firewall/10-to-11 b/src/migration-scripts/firewall/10-to-11 index 50d3eb89b..f608c200d 100644 --- a/src/migration-scripts/firewall/10-to-11 +++ b/src/migration-scripts/firewall/10-to-11 @@ -36,10 +36,57 @@ # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> action jump # set firewall [ipv4 | ipv6] input filter rule <5,10,15,...> jump-target <name> +# T8281: Normalize firewall rule network prefixes for source and destination address +# +# Fixes invalid/non-canonical prefixes like: +# 10.10.10.1/30 -> 10.10.10.0/30 +# 2001:db8::1/64 -> 2001:db8::/64 +# +# Only "source address" and "destination address" are rewritten. + +from ipaddress import ip_network from vyos.configtree import ConfigTree base = ['firewall'] + +def normalize_address(config: ConfigTree, path: list[str]): + if not config.exists(path): + return + + def normalize(value: str) -> str | None: + if '/' not in value: + return None + + is_except = value.startswith('!') + if is_except: + value = value.lstrip('!') + + try: + new_value = str(ip_network(value, strict=False)) + except ValueError: + return None + + return f'!{new_value}' if is_except else new_value + + value = config.return_value(path) + if value: + normalized = normalize(value) + if normalized and normalized != value: + config.set(path, value=normalized) + + +def migrate_ruleset(config: ConfigTree, ruleset_base: list[str]): + if not config.exists(ruleset_base + ['rule']): + return + + for rule_id in config.list_nodes(ruleset_base + ['rule']): + rule_base = ruleset_base + ['rule', rule_id] + + for direction in ['source', 'destination']: + normalize_address(config, rule_base + [direction, 'address']) + + def migrate(config: ConfigTree) -> None: if not config.exists(base): # Nothing to do @@ -185,3 +232,25 @@ def migrate(config: ConfigTree) -> None: inp_ipv6_rule = inp_ipv6_rule + 5 config.delete(base + ['interface']) + + ### Normalize/fix firewall rule network prefixes for source and destination address (T8281) + for family in ['ipv4', 'ipv6', 'bridge']: + # 1) Named rulesets: + # set firewall ipv4|ipv6|bridge name <ruleset> rule <id> ... + ruleset_base = base + [family, 'name'] + if config.exists(ruleset_base): + for ruleset in config.list_nodes(ruleset_base): + migrate_ruleset(config, ruleset_base + [ruleset]) + + # 2) Hook-based rulesets: + # set firewall ipv4|ipv6|bridge input|output|forward filter|raw rule <id> ... + # set firewall ipv4|ipv6 prerouting raw rule <id> ... + hook_rulesets = { + 'input': ['filter', 'raw'], + 'output': ['filter', 'raw'], + 'forward': ['filter', 'raw'], + 'prerouting': ['raw'], + } + for hook, rulesets in hook_rulesets.items(): + for ruleset in rulesets: + migrate_ruleset(config, base + [family, hook, ruleset]) |
