diff options
author | Christian Poessinger <christian@poessinger.com> | 2020-05-11 18:58:05 +0200 |
---|---|---|
committer | Christian Poessinger <christian@poessinger.com> | 2020-05-16 15:30:26 +0200 |
commit | 1c6ae6f7e7cf30d9598d2886bb3d2c34685a2c8c (patch) | |
tree | caa41f12fb793bb6fa0b7e6a8c45e2318484d911 | |
parent | a927192af24079e6d392e5cae0340441490c0091 (diff) | |
download | vyos-1x-1c6ae6f7e7cf30d9598d2886bb3d2c34685a2c8c.tar.gz vyos-1x-1c6ae6f7e7cf30d9598d2886bb3d2c34685a2c8c.zip |
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.
-rw-r--r-- | data/templates/firewall/nftables-nat.tmpl | 19 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rwxr-xr-x | 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 <http://www.gnu.org/licenses/>. +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): |