diff options
-rw-r--r-- | data/configd-include.json | 1 | ||||
-rw-r--r-- | interface-definitions/firewall-options.xml.in | 50 | ||||
-rw-r--r-- | interface-definitions/include/interface/adjust-mss.xml.i | 23 | ||||
-rw-r--r-- | interface-definitions/include/interface/ipv4-options.xml.i | 1 | ||||
-rw-r--r-- | interface-definitions/include/interface/ipv6-options.xml.i | 1 | ||||
-rw-r--r-- | interface-definitions/interfaces-pppoe.xml.in | 2 | ||||
-rwxr-xr-x | python/vyos/ifconfig/interface.py | 66 | ||||
-rw-r--r-- | python/vyos/ifconfig/pppoe.py | 4 | ||||
-rw-r--r-- | smoketest/scripts/cli/base_interfaces_test.py | 22 | ||||
-rwxr-xr-x | src/conf_mode/firewall_options.py | 150 | ||||
-rwxr-xr-x | src/migration-scripts/firewall/5-to-6 | 63 |
11 files changed, 177 insertions, 206 deletions
diff --git a/data/configd-include.json b/data/configd-include.json index 2d7ea149b..6893aaa86 100644 --- a/data/configd-include.json +++ b/data/configd-include.json @@ -6,7 +6,6 @@ "dhcpv6_relay.py", "dns_forwarding.py", "dynamic_dns.py", -"firewall_options.py", "host_name.py", "https.py", "igmp_proxy.py", diff --git a/interface-definitions/firewall-options.xml.in b/interface-definitions/firewall-options.xml.in deleted file mode 100644 index 8d9225a9a..000000000 --- a/interface-definitions/firewall-options.xml.in +++ /dev/null @@ -1,50 +0,0 @@ -<?xml version="1.0"?> -<interfaceDefinition> - <node name="firewall"> - <children> - <node name="options"> - <properties> - <help>Firewall options/Packet manipulation</help> - <priority>990</priority> - </properties> - <children> - <tagNode name="interface" owner="${vyos_conf_scripts_dir}/firewall_options.py"> - <properties> - <help>Interface clamping options</help> - <completionHelp> - <script>${vyos_completion_dir}/list_interfaces.py</script> - </completionHelp> - </properties> - <children> - #include <include/generic-disable-node.xml.i> - <leafNode name="adjust-mss"> - <properties> - <help>Adjust MSS for IPv4 transit packets</help> - <valueHelp> - <format>500-1460</format> - <description>TCP Maximum segment size in bytes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 500-1460"/> - </constraint> - </properties> - </leafNode> - <leafNode name="adjust-mss6"> - <properties> - <help>Adjust MSS for IPv6 transit packets</help> - <valueHelp> - <format>1280-1492</format> - <description>TCP Maximum segment size in bytes</description> - </valueHelp> - <constraint> - <validator name="numeric" argument="--range 1280-1492"/> - </constraint> - </properties> - </leafNode> - </children> - </tagNode> - </children> - </node> - </children> - </node> -</interfaceDefinition> diff --git a/interface-definitions/include/interface/adjust-mss.xml.i b/interface-definitions/include/interface/adjust-mss.xml.i new file mode 100644 index 000000000..57019f02c --- /dev/null +++ b/interface-definitions/include/interface/adjust-mss.xml.i @@ -0,0 +1,23 @@ +<!-- include start from interface/adjust-mss.xml.i --> +<!-- https://datatracker.ietf.org/doc/html/rfc6691 --> +<leafNode name="adjust-mss"> + <properties> + <help>Adjust TCP MSS value</help> + <completionHelp> + <list>clamp-mss-to-pmtu</list> + </completionHelp> + <valueHelp> + <format>clamp-mss-to-pmtu</format> + <description>Automatically sets the MSS to the proper value</description> + </valueHelp> + <valueHelp> + <format>u32:500-65535</format> + <description>TCP Maximum segment size in bytes</description> + </valueHelp> + <constraint> + <validator name="numeric" argument="--range 500-65535"/> + <regex>^(clamp-mss-to-pmtu)$</regex> + </constraint> + </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/interface/ipv4-options.xml.i b/interface-definitions/include/interface/ipv4-options.xml.i index 10884b6eb..bca1229c6 100644 --- a/interface-definitions/include/interface/ipv4-options.xml.i +++ b/interface-definitions/include/interface/ipv4-options.xml.i @@ -4,6 +4,7 @@ <help>IPv4 routing parameters</help> </properties> <children> + #include <include/interface/adjust-mss.xml.i> #include <include/interface/arp-cache-timeout.xml.i> #include <include/interface/disable-arp-filter.xml.i> #include <include/interface/disable-forwarding.xml.i> diff --git a/interface-definitions/include/interface/ipv6-options.xml.i b/interface-definitions/include/interface/ipv6-options.xml.i index e57c242b0..2d2d1d3b2 100644 --- a/interface-definitions/include/interface/ipv6-options.xml.i +++ b/interface-definitions/include/interface/ipv6-options.xml.i @@ -4,6 +4,7 @@ <help>IPv6 routing parameters</help> </properties> <children> + #include <include/interface/adjust-mss.xml.i> #include <include/interface/ipv6-address.xml.i> #include <include/interface/ipv6-disable-forwarding.xml.i> #include <include/interface/ipv6-dup-addr-detect-transmits.xml.i> diff --git a/interface-definitions/interfaces-pppoe.xml.in b/interface-definitions/interfaces-pppoe.xml.in index 1bbfa63af..ac8fa378b 100644 --- a/interface-definitions/interfaces-pppoe.xml.in +++ b/interface-definitions/interfaces-pppoe.xml.in @@ -70,6 +70,7 @@ <help>IPv4 routing parameters</help> </properties> <children> + #include <include/interface/adjust-mss.xml.i> #include <include/interface/source-validation.xml.i> </children> </node> @@ -86,6 +87,7 @@ #include <include/interface/ipv6-address-autoconf.xml.i> </children> </node> + #include <include/interface/adjust-mss.xml.i> </children> </node> <leafNode name="source-interface"> diff --git a/python/vyos/ifconfig/interface.py b/python/vyos/ifconfig/interface.py index a1928ba51..53b57a83f 100755 --- a/python/vyos/ifconfig/interface.py +++ b/python/vyos/ifconfig/interface.py @@ -436,6 +436,62 @@ class Interface(Control): """ return self.set_interface('arp_cache_tmo', tmo) + def set_tcp_ipv4_mss(self, mss): + """ + Set IPv4 TCP MSS value advertised when TCP SYN packets leave this + interface. Value is in bytes. + + A value of 0 will disable the MSS adjustment + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_tcp_ipv4_mss(1340) + """ + iptables_bin = 'iptables' + base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = self._cmd(f'{iptables_bin}-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + # remove OLD MSS mangling configuration + line = line.replace('-A FORWARD', '-D FORWARD') + self._cmd(f'{iptables_bin} -t mangle {line}') + + cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' + if mss == 'clamp-mss-to-pmtu': + self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') + elif int(mss) > 0: + # probably add option to clamp only if bigger: + low_mss = str(int(mss) + 1) + self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') + + def set_tcp_ipv6_mss(self, mss): + """ + Set IPv6 TCP MSS value advertised when TCP SYN packets leave this + interface. Value is in bytes. + + A value of 0 will disable the MSS adjustment + + Example: + >>> from vyos.ifconfig import Interface + >>> Interface('eth0').set_tcp_mss(1320) + """ + iptables_bin = 'ip6tables' + base_options = f'-A FORWARD -o {self.ifname} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = self._cmd(f'{iptables_bin}-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + # remove OLD MSS mangling configuration + line = line.replace('-A FORWARD', '-D FORWARD') + self._cmd(f'{iptables_bin} -t mangle {line}') + + cmd_mss = f'{iptables_bin} -t mangle {base_options} --jump TCPMSS' + if mss == 'clamp-mss-to-pmtu': + self._cmd(f'{cmd_mss} --clamp-mss-to-pmtu') + elif int(mss) > 0: + # probably add option to clamp only if bigger: + low_mss = str(int(mss) + 1) + self._cmd(f'{cmd_mss} -m tcpmss --mss {low_mss}:65535 --set-mss {mss}') + def set_arp_filter(self, arp_filter): """ Filter ARP requests @@ -1202,6 +1258,16 @@ class Interface(Control): # checked before self.set_vrf(config.get('vrf', '')) + # Configure MSS value for IPv4 TCP connections + tmp = dict_search('ip.adjust_mss', config) + value = tmp if (tmp != None) else '0' + self.set_tcp_ipv4_mss(value) + + # Configure MSS value for IPv6 TCP connections + tmp = dict_search('ipv6.adjust_mss', config) + value = tmp if (tmp != None) else '0' + self.set_tcp_ipv6_mss(value) + # Configure ARP cache timeout in milliseconds - has default value tmp = dict_search('ip.arp_cache_timeout', config) value = tmp if (tmp != None) else '30' diff --git a/python/vyos/ifconfig/pppoe.py b/python/vyos/ifconfig/pppoe.py index 65575cf99..6acf7d1c7 100644 --- a/python/vyos/ifconfig/pppoe.py +++ b/python/vyos/ifconfig/pppoe.py @@ -17,9 +17,7 @@ from vyos.ifconfig.interface import Interface @Interface.register class PPPoEIf(Interface): - default = { - 'type': 'pppoe', - } + iftype = 'pppoe' definition = { **Interface.definition, **{ diff --git a/smoketest/scripts/cli/base_interfaces_test.py b/smoketest/scripts/cli/base_interfaces_test.py index 7f69b8444..63f742a8d 100644 --- a/smoketest/scripts/cli/base_interfaces_test.py +++ b/smoketest/scripts/cli/base_interfaces_test.py @@ -556,13 +556,16 @@ class BasicInterfaceTest: if not self._test_ip: self.skipTest('not supported') + arp_tmo = '300' + mss = '1420' + for interface in self._interfaces: - arp_tmo = '300' path = self._base_path + [interface] for option in self._options.get(interface, []): self.cli_set(path + option.split()) # Options + self.cli_set(path + ['ip', 'adjust-mss', mss]) self.cli_set(path + ['ip', 'arp-cache-timeout', arp_tmo]) self.cli_set(path + ['ip', 'disable-arp-filter']) self.cli_set(path + ['ip', 'disable-forwarding']) @@ -576,6 +579,12 @@ class BasicInterfaceTest: self.cli_commit() for interface in self._interfaces: + base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = cmd('sudo iptables-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn(f'--set-mss {mss}', line) + tmp = read_file(f'/proc/sys/net/ipv4/neigh/{interface}/base_reachable_time_ms') self.assertEqual(tmp, str((int(arp_tmo) * 1000))) # tmo value is in milli seconds @@ -607,19 +616,28 @@ class BasicInterfaceTest: if not self._test_ipv6: self.skipTest('not supported') + mss = '1400' + dad_transmits = '10' + for interface in self._interfaces: - dad_transmits = '10' path = self._base_path + [interface] for option in self._options.get(interface, []): self.cli_set(path + option.split()) # Options + self.cli_set(path + ['ipv6', 'adjust-mss', mss]) self.cli_set(path + ['ipv6', 'disable-forwarding']) self.cli_set(path + ['ipv6', 'dup-addr-detect-transmits', dad_transmits]) self.cli_commit() for interface in self._interfaces: + base_options = f'-A FORWARD -o {interface} -p tcp -m tcp --tcp-flags SYN,RST SYN' + out = cmd('sudo ip6tables-save -t mangle') + for line in out.splitlines(): + if line.startswith(base_options): + self.assertIn(f'--set-mss {mss}', line) + tmp = read_file(f'/proc/sys/net/ipv6/conf/{interface}/forwarding') self.assertEqual('0', tmp) diff --git a/src/conf_mode/firewall_options.py b/src/conf_mode/firewall_options.py deleted file mode 100755 index 67bf5d0e2..000000000 --- a/src/conf_mode/firewall_options.py +++ /dev/null @@ -1,150 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) 2018 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 -# published by the Free Software Foundation. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# - -import sys -import os -import copy - -from vyos.config import Config -from vyos import ConfigError -from vyos.util import call - -from vyos import airbag -airbag.enable() - -default_config_data = { - 'intf_opts': [], - 'new_chain4': False, - 'new_chain6': False -} - -def get_config(config=None): - opts = copy.deepcopy(default_config_data) - if config: - conf = config - else: - conf = Config() - if not conf.exists('firewall options'): - # bail out early - return opts - else: - conf.set_level('firewall options') - - # Parse configuration of each individual instance - if conf.exists('interface'): - for intf in conf.list_nodes('interface'): - conf.set_level('firewall options interface {0}'.format(intf)) - config = { - 'intf': intf, - 'disabled': False, - 'mss4': '', - 'mss6': '' - } - - # Check if individual option is disabled - if conf.exists('disable'): - config['disabled'] = True - - # - # Get MSS value IPv4 - # - if conf.exists('adjust-mss'): - config['mss4'] = conf.return_value('adjust-mss') - - # We need a marker that a new iptables chain needs to be generated - if not opts['new_chain4']: - opts['new_chain4'] = True - - # - # Get MSS value IPv6 - # - if conf.exists('adjust-mss6'): - config['mss6'] = conf.return_value('adjust-mss6') - - # We need a marker that a new ip6tables chain needs to be generated - if not opts['new_chain6']: - opts['new_chain6'] = True - - # Append interface options to global list - opts['intf_opts'].append(config) - - return opts - -def verify(tcp): - # syntax verification is done via cli - return None - -def apply(tcp): - target = 'VYOS_FW_OPTIONS' - - # always cleanup iptables - call('iptables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - call('iptables --table mangle --flush {} >&/dev/null'.format(target)) - call('iptables --table mangle --delete-chain {} >&/dev/null'.format(target)) - - # always cleanup ip6tables - call('ip6tables --table mangle --delete FORWARD --jump {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --flush {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --delete-chain {} >&/dev/null'.format(target)) - - # Setup new iptables rules - if tcp['new_chain4']: - call('iptables --table mangle --new-chain {} >&/dev/null'.format(target)) - call('iptables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - - for opts in tcp['intf_opts']: - intf = opts['intf'] - mss = opts['mss4'] - - # Check if this rule iis disabled - if opts['disabled']: - continue - - # adjust TCP MSS per interface - if mss: - call('iptables --table mangle --append {} --out-interface {} --protocol tcp ' - '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - - # Setup new ip6tables rules - if tcp['new_chain6']: - call('ip6tables --table mangle --new-chain {} >&/dev/null'.format(target)) - call('ip6tables --table mangle --append FORWARD --jump {} >&/dev/null'.format(target)) - - for opts in tcp['intf_opts']: - intf = opts['intf'] - mss = opts['mss6'] - - # Check if this rule iis disabled - if opts['disabled']: - continue - - # adjust TCP MSS per interface - if mss: - call('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' - '--tcp-flags SYN,RST SYN --jump TCPMSS --set-mss {} >&/dev/null'.format(target, intf, mss)) - - return None - -if __name__ == '__main__': - - try: - c = get_config() - verify(c) - apply(c) - except ConfigError as e: - print(e) - sys.exit(1) diff --git a/src/migration-scripts/firewall/5-to-6 b/src/migration-scripts/firewall/5-to-6 new file mode 100755 index 000000000..ccb86830a --- /dev/null +++ b/src/migration-scripts/firewall/5-to-6 @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2021 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 +# published by the Free Software Foundation. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# T3090: migrate "firewall options interface <name> adjust-mss" to the +# individual interface. + +from sys import argv +from sys import exit + +from vyos.configtree import ConfigTree +from vyos.ifconfig import Section + +if (len(argv) < 1): + print("Must specify file name!") + exit(1) + +file_name = argv[1] + +with open(file_name, 'r') as f: + config_file = f.read() + +base = ['firewall', 'options', 'interface'] +config = ConfigTree(config_file) + +if not config.exists(base): + # Nothing to do + exit(0) + +for interface in config.list_nodes(base): + if config.exists(base + [interface, 'disable']): + continue + + if config.exists(base + [interface, 'adjust-mss']): + section = Section.section(interface) + tmp = config.return_value(base + [interface, 'adjust-mss']) + config.set(['interfaces', section, interface, 'ip', 'adjust-mss'], value=tmp) + + if config.exists(base + [interface, 'adjust-mss6']): + section = Section.section(interface) + tmp = config.return_value(base + [interface, 'adjust-mss6']) + config.set(['interfaces', section, interface, 'ipv6', 'adjust-mss'], value=tmp) + +config.delete(['firewall', 'options']) + +try: + with open(file_name, 'w') as f: + f.write(config.to_string()) +except OSError as e: + print("Failed to save the modified config: {}".format(e)) + exit(1) |