diff options
-rw-r--r-- | interface-definitions/include/firewall/action-forward.xml.i | 8 | ||||
-rw-r--r-- | interface-definitions/include/firewall/action.xml.i | 8 | ||||
-rw-r--r-- | interface-definitions/include/firewall/common-rule-inet.xml.i | 3 | ||||
-rw-r--r-- | interface-definitions/include/firewall/synproxy.xml.i | 40 | ||||
-rw-r--r-- | python/vyos/firewall.py | 14 | ||||
-rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 27 | ||||
-rwxr-xr-x | src/conf_mode/firewall.py | 10 |
7 files changed, 103 insertions, 7 deletions
diff --git a/interface-definitions/include/firewall/action-forward.xml.i b/interface-definitions/include/firewall/action-forward.xml.i index f61e51887..4e59f3c6f 100644 --- a/interface-definitions/include/firewall/action-forward.xml.i +++ b/interface-definitions/include/firewall/action-forward.xml.i @@ -3,7 +3,7 @@ <properties> <help>Rule action</help> <completionHelp> - <list>accept continue jump reject return drop queue offload</list> + <list>accept continue jump reject return drop queue offload synproxy</list> </completionHelp> <valueHelp> <format>accept</format> @@ -37,8 +37,12 @@ <format>offload</format> <description>Offload packet via flowtable</description> </valueHelp> + <valueHelp> + <format>synproxy</format> + <description>Synproxy connections</description> + </valueHelp> <constraint> - <regex>(accept|continue|jump|reject|return|drop|queue|offload)</regex> + <regex>(accept|continue|jump|reject|return|drop|queue|offload|synproxy)</regex> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/firewall/action.xml.i b/interface-definitions/include/firewall/action.xml.i index 9391a7bee..954e4f23e 100644 --- a/interface-definitions/include/firewall/action.xml.i +++ b/interface-definitions/include/firewall/action.xml.i @@ -3,7 +3,7 @@ <properties> <help>Rule action</help> <completionHelp> - <list>accept continue jump reject return drop queue</list> + <list>accept continue jump reject return drop queue synproxy</list> </completionHelp> <valueHelp> <format>accept</format> @@ -33,8 +33,12 @@ <format>queue</format> <description>Enqueue packet to userspace</description> </valueHelp> + <valueHelp> + <format>synproxy</format> + <description>Synproxy connections</description> + </valueHelp> <constraint> - <regex>(accept|continue|jump|reject|return|drop|queue)</regex> + <regex>(accept|continue|jump|reject|return|drop|queue|synproxy)</regex> </constraint> </properties> </leafNode> diff --git a/interface-definitions/include/firewall/common-rule-inet.xml.i b/interface-definitions/include/firewall/common-rule-inet.xml.i index e51dd0056..b04e40fa0 100644 --- a/interface-definitions/include/firewall/common-rule-inet.xml.i +++ b/interface-definitions/include/firewall/common-rule-inet.xml.i @@ -219,6 +219,7 @@ </leafNode> </children> </node> +#include <include/firewall/synproxy.xml.i> <node name="state"> <properties> <help>Session state</help> @@ -372,4 +373,4 @@ </leafNode> </children> </node> -<!-- include end -->
\ No newline at end of file +<!-- include end --> diff --git a/interface-definitions/include/firewall/synproxy.xml.i b/interface-definitions/include/firewall/synproxy.xml.i new file mode 100644 index 000000000..a65126ea9 --- /dev/null +++ b/interface-definitions/include/firewall/synproxy.xml.i @@ -0,0 +1,40 @@ +<!-- include start from firewall/synproxy.xml.i --> +<node name="synproxy"> + <properties> + <help>Synproxy options</help> + </properties> + <children> + <node name="tcp"> + <properties> + <help>TCP synproxy options</help> + </properties> + <children> + <leafNode name="mss"> + <properties> + <help>TCP Maximum segment size</help> + <valueHelp> + <format>u32:501-65535</format> + <description>Maximum segment size for synproxy connections</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 501-65535"/> + </constraint> + </properties> + </leafNode> + <leafNode name="window-scale"> + <properties> + <help>TCP window scale for synproxy connections</help> + <valueHelp> + <format>u32:1-14</format> + <description>TCP window scale</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 1-14"/> + </constraint> + </properties> + </leafNode> + </children> + </node> + </children> +</node> +<!-- include end --> diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 3ca7a25b9..1ec034937 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -249,6 +249,10 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): output.append(f'{proto} {prefix}port {operator} @P_{group_name}') + if rule_conf['action'] == 'synproxy': + if 'synproxy' in rule_conf: + output.append('ct state invalid,untracked') + if 'hop_limit' in rule_conf: operators = {'eq': '==', 'gt': '>', 'lt': '<'} for op, operator in operators.items(): @@ -419,6 +423,16 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): if 'queue_options' in rule_conf: queue_opts = ','.join(rule_conf['queue_options']) output.append(f'{queue_opts}') + + # Synproxy + if 'synproxy' in rule_conf: + synproxy_mss = dict_search_args(rule_conf, 'synproxy', 'tcp', 'mss') + if synproxy_mss: + output.append(f'mss {synproxy_mss}') + synproxy_ws = dict_search_args(rule_conf, 'synproxy', 'tcp', 'window_scale') + if synproxy_ws: + output.append(f'wscale {synproxy_ws} timestamp sack-perm') + else: output.append('return') diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 676be5305..7fd13d92a 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 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 @@ -338,6 +338,31 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.verify_nftables(nftables_search, 'ip vyos_filter') + def test_ipv4_synproxy(self): + tcp_mss = '1460' + tcp_wscale = '7' + dport = '22' + + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'drop']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'protocol', 'tcp']) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'destination', 'port', dport]) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'mss', tcp_mss]) + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'synproxy', 'tcp', 'window-scale', tcp_wscale]) + + with self.assertRaises(ConfigSessionError): + self.cli_commit() + + self.cli_set(['firewall', 'ipv4', 'input', 'filter', 'rule', '10', 'action', 'synproxy']) + + self.cli_commit() + + nftables_search = [ + [f'tcp dport {dport} ct state invalid,untracked', f'synproxy mss {tcp_mss} wscale {tcp_wscale} timestamp sack-perm'] + ] + + self.verify_nftables(nftables_search, 'ip vyos_filter') + + def test_ipv4_mask(self): name = 'smoketest-mask' interface = 'eth0' diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py index 3d799318e..2ca4bbe2d 100755 --- a/src/conf_mode/firewall.py +++ b/src/conf_mode/firewall.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2021-2022 VyOS maintainers and contributors +# Copyright (C) 2021-2023 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 @@ -173,6 +173,14 @@ def verify_rule(firewall, rule_conf, ipv6): if not dict_search_args(firewall, 'flowtable', offload_target): raise ConfigError(f'Invalid offload-target. Flowtable "{offload_target}" does not exist on the system') + if rule_conf['action'] != 'synproxy' and 'synproxy' in rule_conf: + raise ConfigError('"synproxy" option allowed only for action synproxy') + if rule_conf['action'] == 'synproxy': + if not rule_conf.get('synproxy', {}).get('tcp'): + raise ConfigError('synproxy TCP MSS is not defined') + if rule_conf.get('protocol', {}) != 'tcp': + raise ConfigError('For action "synproxy" the protocol must be set to TCP') + if 'queue_options' in rule_conf: if 'queue' not in rule_conf['action']: raise ConfigError('queue-options defined, but action queue needed and it is not defined') |