#!/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 . # 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 == 'clamp-mss-to-pmtu': call('iptables --table mangle --append {} --out-interface {} --protocol tcp ' '--tcp-flags SYN,RST SYN --jump TCPMSS --clamp-mss-to-pmtu >&/dev/null'.format(target, intf)) elif 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 == 'clamp-mss-to-pmtu': call('ip6tables --table mangle --append {} --out-interface {} --protocol tcp ' '--tcp-flags SYN,RST SYN --jump TCPMSS --clamp-mss-to-pmtu >&/dev/null'.format(target, intf)) elif 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)