From 1c6ae6f7e7cf30d9598d2886bb3d2c34685a2c8c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 11 May 2020 18:58:05 +0200 Subject: nat: T2198: automatically determine handler numbers When instantiating NAT it is required to isntall some nftable jump targets. The targets need to be added after a specific other target thus we need to dynamically query the handler number. This is done by get_handler() which could be moved to vyos.util at a later point in time so it can be reused for a firewall rewrite. --- data/templates/firewall/nftables-nat.tmpl | 19 ++++++++++--- debian/control | 2 +- src/conf_mode/nat.py | 45 ++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 8 deletions(-) diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl index 340ab3678..343807e79 100644 --- a/data/templates/firewall/nftables-nat.tmpl +++ b/data/templates/firewall/nftables-nat.tmpl @@ -3,11 +3,22 @@ # Start with a "clean" NAT table flush table nat +{% for rule in init_deinit -%} +# Add or remove conntrack helper rules for NAT operation- +{{ rule }} +{% endfor %} + add chain ip raw NAT_CONNTRACK -add rule ip raw PREROUTING position 25 counter jump VYATTA_CT_HELPER -add rule ip raw PREROUTING position 17 counter jump NAT_CONNTRACK -add rule ip raw OUTPUT position 26 counter jump VYATTA_CT_HELPER -add rule ip raw OUTPUT position 21 counter jump NAT_CONNTRACK + +# insert rule after VYATTA_CT_IGNORE +add rule ip raw PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER +# insert rule after VYATTA_CT_PREROUTING_HOOK +add rule ip raw PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK +# insert rule after VYATTA_CT_IGNORE +add rule ip raw OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER +# insert rule after VYATTA_CT_PREROUTING_HOOK +add rule ip raw OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK + add rule ip raw NAT_CONNTRACK counter accept diff --git a/debian/control b/debian/control index 2aaca13ba..609f46e4d 100644 --- a/debian/control +++ b/debian/control @@ -92,7 +92,7 @@ Depends: python3, pppoe, salt-minion, vyos-utils, - nftables, + nftables (>= 0.9.3), conntrack, ${shlibs:Depends}, ${misc:Depends} diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py index 2e866fdf4..128e2469c 100755 --- a/src/conf_mode/nat.py +++ b/src/conf_mode/nat.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +import json import os from copy import deepcopy @@ -21,24 +22,53 @@ from sys import exit from netifaces import interfaces from vyos.config import Config -from vyos.util import call from vyos.template import render +from vyos.util import call, cmd from vyos import ConfigError default_config_data = { - 'source': [], - 'destination': [] + 'prerouting_ct_helper': '', + 'prerouting_ct_conntrack': '', + 'output_ct_helper': '', + 'output_ct_conntrack': '', + 'destination': [], + 'source': [] } iptables_nat_config = '/tmp/vyos-nat-rules.nft' def _check_kmod(): + """ load required Kernel modules """ modules = ['nft_nat', 'nft_chain_nat_ipv4'] for module in modules: if not os.path.exists(f'/sys/module/{module}'): if call(f'modprobe {module}') != 0: raise ConfigError(f'Loading Kernel module {module} failed') + +def get_handler(chain, target): + """ Get handler number of given chain/target combination. Handler is + required when adding NAT/Conntrack helper targets """ + tmp = json.loads(cmd('nft -j list table raw')) + for rule in tmp.get('nftables'): + # We're only interested in rules - not chains + if not 'rule' in rule.keys(): + continue + + # Search for chain of interest + if rule['rule']['chain'] == chain: + for expr in rule['rule']['expr']: + # We're only interested in jump targets + if not 'jump' in expr.keys(): + continue + + # Search for target of interest + if expr['jump']['target'] == target: + return rule['rule']['handle'] + + return None + + def parse_source_destination(conf, source_dest): """ Common wrapper to read in both NAT source and destination CLI """ tmp = [] @@ -114,6 +144,11 @@ def get_config(): else: conf.set_level(['nat']) + nat['pre_ct_ignore'] = get_handler('PREROUTING', 'VYATTA_CT_IGNORE') + nat['pre_ct_conntrack'] = get_handler('PREROUTING', 'VYATTA_CT_PREROUTING_HOOK') + nat['out_ct_ignore'] = get_handler('OUTPUT', 'VYATTA_CT_IGNORE') + nat['out_ct_conntrack'] = get_handler('OUTPUT', 'VYATTA_CT_OUTPUT_HOOK') + # use a common wrapper function to read in the source / destination # tree from the config - thus we do not need to replicate almost the # same code :-) @@ -126,6 +161,9 @@ def verify(nat): if not nat: return None + if not (nat['pre_ct_ignore'] or nat['pre_ct_conntrack'] or nat['out_ct_ignore'] or nat['out_ct_conntrack']): + raise Exception('could not determine nftable ruleset handlers') + for rule in nat['source']: interface = rule['interface_out'] if interface and interface not in interfaces(): @@ -138,6 +176,7 @@ def generate(nat): return None render(iptables_nat_config, 'firewall/nftables-nat.tmpl', nat, trim_blocks=True, permission=0o755) + return None def apply(nat): -- cgit v1.2.3