summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2020-05-12 17:43:03 +0200
committerChristian Poessinger <christian@poessinger.com>2020-05-16 15:30:26 +0200
commitcc2ad34ce61e205454c4676a5bde77629d463964 (patch)
tree8c40fbd4e28d8e2ceb2c44ff989e36f58ad61e3c
parentfda762065c03d55c05682bf9834354c0edca3e97 (diff)
downloadvyos-1x-cc2ad34ce61e205454c4676a5bde77629d463964.tar.gz
vyos-1x-cc2ad34ce61e205454c4676a5bde77629d463964.zip
nat: T2198: make use of jmespath when walking nftables JSON output
-rw-r--r--data/templates/firewall/nftables-nat.tmpl91
-rw-r--r--debian/control1
-rwxr-xr-xsrc/conf_mode/nat.py71
3 files changed, 83 insertions, 80 deletions
diff --git a/data/templates/firewall/nftables-nat.tmpl b/data/templates/firewall/nftables-nat.tmpl
index 671cd0920..161ef27fb 100644
--- a/data/templates/firewall/nftables-nat.tmpl
+++ b/data/templates/firewall/nftables-nat.tmpl
@@ -1,62 +1,55 @@
#!/usr/sbin/nft -f
-# Start with a "clean" NAT table
+# Start with clean NAT table
flush table nat
-{% for rule in init_deinit -%}
-# Add or remove conntrack helper rules for NAT operation-
-{{ rule }}
-{% endfor %}
-
-
-{% if deleted %}
-# NAT if going to be disabled - remove rules and targets from nftables
-delete rule ip raw PREROUTING handle {{ pre_ct_ignore }}
-delete rule ip raw PREROUTING handle {{ pre_ct_conntrack }}
-delete rule ip raw OUTPUT handle {{ out_ct_ignore }}
-delete rule ip raw OUTPUT handle {{ out_ct_conntrack }}
-
-delete chain ip raw NAT_CONNTRACK
-
-{% else %}
-# NAT if enabled - add targets to nftables
-add chain ip raw NAT_CONNTRACK
-add rule ip raw NAT_CONNTRACK counter accept
-
-add rule ip raw PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
-add rule ip raw PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
-add rule ip raw OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
-add rule ip raw OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
+{% if helper_functions == 'remove' %}
+ # NAT if going to be disabled - remove rules and targets from nftables
+ delete rule ip raw PREROUTING handle {{ pre_ct_ignore }}
+ delete rule ip raw PREROUTING handle {{ pre_ct_conntrack }}
+ delete rule ip raw OUTPUT handle {{ out_ct_ignore }}
+ delete rule ip raw OUTPUT handle {{ out_ct_conntrack }}
+
+ delete chain ip raw NAT_CONNTRACK
+{% elif helper_functions == 'add' %}
+ # NAT if enabled - add targets to nftables
+ add chain ip raw NAT_CONNTRACK
+ add rule ip raw NAT_CONNTRACK counter accept
+
+ add rule ip raw PREROUTING position {{ pre_ct_ignore }} counter jump VYATTA_CT_HELPER
+ add rule ip raw PREROUTING position {{ pre_ct_conntrack }} counter jump NAT_CONNTRACK
+ add rule ip raw OUTPUT position {{ out_ct_ignore }} counter jump VYATTA_CT_HELPER
+ add rule ip raw OUTPUT position {{ out_ct_conntrack }} counter jump NAT_CONNTRACK
{% endif %}
{% for r in destination -%}
-{% if r.protocol == 'tcp_udp' %}
-{# Special handling for protocol tcp_udp which is represented as two individual rules #}
-add rule ip nat PREROUTING iifname "{{ r.interface_in }}" tcp dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }} tcp_udp"
-add rule ip nat PREROUTING iifname "{{ r.interface_in }}" udp dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }} tcp_udp"
-{% else %}
-add rule ip nat PREROUTING iifname "{{ r.interface_in }}" {{ r.protocol }} dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }}"
-{% endif %}
+{% if r.protocol == 'tcp_udp' %}
+ {# Special handling for protocol tcp_udp which is represented as two individual rules #}
+ add rule ip nat PREROUTING iifname "{{ r.interface_in }}" tcp dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }} tcp_udp"
+ add rule ip nat PREROUTING iifname "{{ r.interface_in }}" udp dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }} tcp_udp"
+{% else %}
+ add rule ip nat PREROUTING iifname "{{ r.interface_in }}" {{ r.protocol }} dport { {{ r.dest_port }} } counter dnat to {{ r.translation_address }}{{ ":" + r.translation_port if r.translation_port }} comment "DST-NAT-{{ r.number }}"
+{% endif %}
{% endfor %}
{% for r in source -%}
-{% if r.log %}
-{% if r.exclude %}
-{% set value = 'EXCL' %}
-{% elif r.translation_address == 'masquerade' %}
-{% set value = 'MASQ' %}
-{% endif %}
-add rule ip nat POSTROUTING oifname "{{ r.interface_out }}" ip saddr {{ r.source_address }} counter log prefix "[NAT-SRC-{{ r.number }}-{{ value }}]" comment "SRC-NAT-{{ r.number }}"
-{% endif %}
-
-{% if r.exclude %}
-{% set value = 'return' %}
-{% elif r.translation_address == 'masquerade' %}
-{% set value = 'masquerade' %}
-{% else %}
-{% set value = 'snat to ' + r.translation_address %}
-{% endif %}
-add rule ip nat POSTROUTING oifname "{{ r.interface_out }}" ip saddr {{ r.source_address }} counter {{ value }} comment "SRC-NAT-{{ r.number }}"
+{% if r.log %}
+{% if r.exclude %}
+{% set value = 'EXCL' %}
+{% elif r.translation_address == 'masquerade' %}
+{% set value = 'MASQ' %}
+{% endif %}
+ add rule ip nat POSTROUTING oifname "{{ r.interface_out }}" ip saddr {{ r.source_address }} counter log prefix "[NAT-SRC-{{ r.number }}-{{ value }}]" comment "SRC-NAT-{{ r.number }}"
+{% endif %}
+
+{% if r.exclude %}
+{% set value = 'return' %}
+{% elif r.translation_address == 'masquerade' %}
+{% set value = 'masquerade' %}
+{% else %}
+{% set value = 'snat to ' + r.translation_address %}
+{% endif %}
+ add rule ip nat POSTROUTING oifname "{{ r.interface_out }}" ip saddr {{ r.source_address }} counter {{ value }} comment "SRC-NAT-{{ r.number }}"
{% endfor %}
diff --git a/debian/control b/debian/control
index 609f46e4d..323b8130f 100644
--- a/debian/control
+++ b/debian/control
@@ -32,6 +32,7 @@ Depends: python3,
python3-waitress,
python3-netaddr,
python3-zmq,
+ python3-jmespath,
cron,
easy-rsa,
ipaddrcheck,
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 916f63f09..580a06136 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 jmespath
import json
import os
@@ -29,6 +30,7 @@ from vyos import ConfigError
default_config_data = {
'deleted': False,
'destination': [],
+ 'helper_functions': None,
'pre_ct_helper': '',
'pre_ct_conntrack': '',
'out_ct_helper': '',
@@ -47,25 +49,15 @@ def _check_kmod():
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():
+def get_handler(json, chain, target):
+ """ Get nftable rule handler number of given chain/target combination.
+ Handler is required when adding NAT/Conntrack helper targets """
+ for x in json:
+ if x['chain'] != chain:
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']
+ if x['target'] != target:
+ continue
+ return x['handle']
return None
@@ -141,24 +133,40 @@ def get_config():
nat = deepcopy(default_config_data)
conf = Config()
+ # read in current nftable (once) for further processing
+ tmp = cmd('nft -j list table raw')
+ nftable_json = json.loads(tmp)
+
+ # condense the full JSON table into a list with only relevand informations
+ pattern = 'nftables[?rule].rule[?expr[].jump].{chain: chain, handle: handle, target: expr[].jump.target | [0]}'
+ condensed_json = jmespath.search(pattern, nftable_json)
+
if not conf.exists(['nat']):
+ nat['helper_functions'] = 'remove'
+
# Retrieve current table handler positions
- nat['pre_ct_ignore'] = get_handler('PREROUTING', 'VYATTA_CT_HELPER')
- nat['pre_ct_conntrack'] = get_handler('PREROUTING', 'NAT_CONNTRACK')
- nat['out_ct_ignore'] = get_handler('OUTPUT', 'VYATTA_CT_HELPER')
- nat['out_ct_conntrack'] = get_handler('OUTPUT', 'NAT_CONNTRACK')
+ nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_HELPER')
+ nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK')
+ nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_HELPER')
+ nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'NAT_CONNTRACK')
nat['deleted'] = True
return nat
- else:
- conf.set_level(['nat'])
- # Retrieve current table handler positions
- 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')
+ # check if NAT connection tracking helpers need to be set up - this has to
+ # be done only once
+ if not get_handler(condensed_json, 'PREROUTING', 'NAT_CONNTRACK'):
+ nat['helper_functions'] = 'add'
+
+ # Retrieve current table handler positions
+ nat['pre_ct_ignore'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_IGNORE')
+ nat['pre_ct_conntrack'] = get_handler(condensed_json, 'PREROUTING', 'VYATTA_CT_PREROUTING_HOOK')
+ nat['out_ct_ignore'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_IGNORE')
+ nat['out_ct_conntrack'] = get_handler(condensed_json, 'OUTPUT', 'VYATTA_CT_OUTPUT_HOOK')
+
+ # set config level for parsing in NAT configuration
+ conf.set_level(['nat'])
# use a common wrapper function to read in the source / destination
# tree from the config - thus we do not need to replicate almost the
@@ -173,8 +181,9 @@ def verify(nat):
# no need to verify the CLI as NAT is going to be deactivated
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')
+ if nat['helper_functions']:
+ 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']