summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--data/templates/conntrack/sysctl.conf.j21
-rw-r--r--data/templates/firewall/nftables-offload.j211
-rw-r--r--data/templates/firewall/nftables.j224
-rw-r--r--data/templates/frr/daemons.frr.tmpl31
-rw-r--r--data/templates/openvpn/server.conf.j211
-rw-r--r--interface-definitions/include/firewall/flow-offload.xml.i47
-rw-r--r--interface-definitions/include/firewall/global-options.xml.i1
-rw-r--r--interface-definitions/system-conntrack.xml.in6
-rw-r--r--op-mode-definitions/show-techsupport_report.xml.in3
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py12
-rwxr-xr-xsrc/conf_mode/firewall.py37
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py7
13 files changed, 154 insertions, 38 deletions
diff --git a/Makefile b/Makefile
index b75a78784..97f4de6c7 100644
--- a/Makefile
+++ b/Makefile
@@ -62,7 +62,6 @@ op_mode_definitions: $(op_xml_obj)
rm -f $(OP_TMPL_DIR)/delete/node.def
rm -f $(OP_TMPL_DIR)/generate/node.def
rm -f $(OP_TMPL_DIR)/set/node.def
- rm -f $(OP_TMPL_DIR)/show/tech-support/node.def
# XXX: ping and traceroute must be able to recursivly call itself as the
# options are provided from the script itself
diff --git a/data/templates/conntrack/sysctl.conf.j2 b/data/templates/conntrack/sysctl.conf.j2
index 075402c04..3d6fc43f2 100644
--- a/data/templates/conntrack/sysctl.conf.j2
+++ b/data/templates/conntrack/sysctl.conf.j2
@@ -24,3 +24,4 @@ net.netfilter.nf_conntrack_tcp_timeout_time_wait = {{ timeout.tcp.time_wait }}
net.netfilter.nf_conntrack_udp_timeout = {{ timeout.udp.other }}
net.netfilter.nf_conntrack_udp_timeout_stream = {{ timeout.udp.stream }}
+net.netfilter.nf_conntrack_acct = {{ '1' if flow_accounting is vyos_defined else '0' }}
diff --git a/data/templates/firewall/nftables-offload.j2 b/data/templates/firewall/nftables-offload.j2
new file mode 100644
index 000000000..6afcd79f7
--- /dev/null
+++ b/data/templates/firewall/nftables-offload.j2
@@ -0,0 +1,11 @@
+{% macro render_flowtable(name, devices, priority='filter', hardware_offload=false, with_counter=true) %}
+flowtable {{ name }} {
+ hook ingress priority {{ priority }}; devices = { {{ devices | join(', ') }} };
+{% if hardware_offload %}
+ flags offload;
+{% endif %}
+{% if with_counter %}
+ counter
+{% endif %}
+}
+{% endmacro %}
diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2
index 87630940b..1b764c9da 100644
--- a/data/templates/firewall/nftables.j2
+++ b/data/templates/firewall/nftables.j2
@@ -2,6 +2,7 @@
{% import 'firewall/nftables-defines.j2' as group_tmpl %}
{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %}
+{% import 'firewall/nftables-offload.j2' as offload %}
flush chain raw FW_CONNTRACK
flush chain ip6 raw FW_CONNTRACK
@@ -271,3 +272,26 @@ table bridge vyos_filter {
{{ group_tmpl.groups(group, False, False) }}
}
{% endif %}
+{{ group_tmpl.groups(group, True) }}
+}
+
+table inet vyos_offload
+delete table inet vyos_offload
+table inet vyos_offload {
+{% if flowtable_enabled %}
+{% if global_options.flow_offload.hardware.interface is vyos_defined %}
+ {{- offload.render_flowtable('VYOS_FLOWTABLE_hardware', global_options.flow_offload.hardware.interface | list, priority='filter - 2', hardware_offload=true) }}
+ chain VYOS_OFFLOAD_hardware {
+ type filter hook forward priority filter - 2; policy accept;
+ ct state { established, related } meta l4proto { tcp, udp } flow add @VYOS_FLOWTABLE_hardware
+ }
+{% endif %}
+{% if global_options.flow_offload.software.interface is vyos_defined %}
+ {{- offload.render_flowtable('VYOS_FLOWTABLE_software', global_options.flow_offload.software.interface | list, priority='filter - 1') }}
+ chain VYOS_OFFLOAD_software {
+ type filter hook forward priority filter - 1; policy accept;
+ ct state { established, related } meta l4proto { tcp, udp } flow add @VYOS_FLOWTABLE_software
+ }
+{% endif %}
+{% endif %}
+}
diff --git a/data/templates/frr/daemons.frr.tmpl b/data/templates/frr/daemons.frr.tmpl
index e09c7d1d2..fe2610724 100644
--- a/data/templates/frr/daemons.frr.tmpl
+++ b/data/templates/frr/daemons.frr.tmpl
@@ -17,40 +17,41 @@ bfdd=yes
staticd=yes
vtysh_enable=yes
-zebra_options=" -s 90000000 --daemon -A 127.0.0.1
+zebra_options=" --daemon -A 127.0.0.1 -s 90000000
{%- if irdp is defined %} -M irdp{% endif -%}
{%- if snmp is defined and snmp.zebra is defined %} -M snmp{% endif -%}
"
-bgpd_options=" --daemon -A 127.0.0.1
+bgpd_options=" --daemon -A 127.0.0.1 -M rpki
{%- if bmp is defined %} -M bmp{% endif -%}
{%- if snmp is defined and snmp.bgpd is defined %} -M snmp{% endif -%}
"
-ospfd_options=" --daemon -A 127.0.0.1
+ospfd_options=" --daemon -A 127.0.0.1
{%- if snmp is defined and snmp.ospfd is defined %} -M snmp{% endif -%}
"
-ospf6d_options=" --daemon -A ::1
+ospf6d_options=" --daemon -A ::1
{%- if snmp is defined and snmp.ospf6d is defined %} -M snmp{% endif -%}
"
-ripd_options=" --daemon -A 127.0.0.1
+ripd_options=" --daemon -A 127.0.0.1
{%- if snmp is defined and snmp.ripd is defined %} -M snmp{% endif -%}
"
-ripngd_options=" --daemon -A ::1"
-isisd_options=" --daemon -A 127.0.0.1
+ripngd_options=" --daemon -A ::1"
+isisd_options=" --daemon -A 127.0.0.1
{%- if snmp is defined and snmp.isisd is defined %} -M snmp{% endif -%}
"
-pimd_options=" --daemon -A 127.0.0.1"
-pim6d_options=" --daemon -A ::1"
-ldpd_options=" --daemon -A 127.0.0.1
+pimd_options=" --daemon -A 127.0.0.1"
+pim6d_options=" --daemon -A ::1"
+ldpd_options=" --daemon -A 127.0.0.1
{%- if snmp is defined and snmp.ldpd is defined %} -M snmp{% endif -%}
"
-mgmtd_options=" --daemon -A 127.0.0.1"
-nhrpd_options=" --daemon -A 127.0.0.1"
+mgmtd_options=" --daemon -A 127.0.0.1"
+nhrpd_options=" --daemon -A 127.0.0.1"
eigrpd_options=" --daemon -A 127.0.0.1"
babeld_options=" --daemon -A 127.0.0.1"
sharpd_options=" --daemon -A 127.0.0.1"
-pbrd_options=" --daemon -A 127.0.0.1"
-staticd_options=" --daemon -A 127.0.0.1"
-bfdd_options=" --daemon -A 127.0.0.1"
+pbrd_options=" --daemon -A 127.0.0.1"
+staticd_options=" --daemon -A 127.0.0.1"
+bfdd_options=" --daemon -A 127.0.0.1"
watchfrr_enable=no
valgrind_enable=no
+
diff --git a/data/templates/openvpn/server.conf.j2 b/data/templates/openvpn/server.conf.j2
index f76fbbe79..2eb9416fe 100644
--- a/data/templates/openvpn/server.conf.j2
+++ b/data/templates/openvpn/server.conf.j2
@@ -74,7 +74,7 @@ topology {{ server.topology }}
{% endif %}
{% for subnet in server.subnet %}
{% if subnet | is_ipv4 %}
-server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} nopool
+server {{ subnet | address_from_cidr }} {{ subnet | netmask_from_cidr }} {{ 'nopool' if server.client_ip_pool is vyos_defined and server.client_ip_pool.disable is not vyos_defined else '' }}
{# First ip address is used as gateway. It's allows to use metrics #}
{% if server.push_route is vyos_defined %}
{% for route, route_config in server.push_route.items() %}
@@ -85,15 +85,6 @@ push "route-ipv6 {{ route }}"
{% endif %}
{% endfor %}
{% endif %}
-{# OpenVPN assigns the first IP address to its local interface so the pool used #}
-{# in net30 topology - where each client receives a /30 must start from the second subnet #}
-{% if server.topology is vyos_defined('net30') %}
-ifconfig-pool {{ subnet | inc_ip('4') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tap' else '' }}
-{% else %}
-{# OpenVPN assigns the first IP address to its local interface so the pool must #}
-{# start from the second address and end on the last address #}
-ifconfig-pool {{ subnet | first_host_address | inc_ip('1') }} {{ subnet | last_host_address | dec_ip('1') }} {{ subnet | netmask_from_cidr if device_type == 'tun' else '' }}
-{% endif %}
{% elif subnet | is_ipv6 %}
server-ipv6 {{ subnet }}
{% endif %}
diff --git a/interface-definitions/include/firewall/flow-offload.xml.i b/interface-definitions/include/firewall/flow-offload.xml.i
new file mode 100644
index 000000000..706836362
--- /dev/null
+++ b/interface-definitions/include/firewall/flow-offload.xml.i
@@ -0,0 +1,47 @@
+<!-- include start from firewall/flow-offload.xml.i -->
+<node name="flow-offload">
+ <properties>
+ <help>Configurable flow offload options</help>
+ </properties>
+ <children>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable flow offload</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="software">
+ <properties>
+ <help>Software offload</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>Interfaces to enable</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="hardware">
+ <properties>
+ <help>Hardware offload</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>Interfaces to enable</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces</script>
+ </completionHelp>
+ <multi/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+</node>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i
index e655cd6ac..03c07e657 100644
--- a/interface-definitions/include/firewall/global-options.xml.i
+++ b/interface-definitions/include/firewall/global-options.xml.i
@@ -271,6 +271,7 @@
</properties>
<defaultValue>disable</defaultValue>
</leafNode>
+ #include <include/firewall/flow-offload.xml.i>
</children>
</node>
<!-- include end -->
diff --git a/interface-definitions/system-conntrack.xml.in b/interface-definitions/system-conntrack.xml.in
index 3abf9bbf0..78d19090c 100644
--- a/interface-definitions/system-conntrack.xml.in
+++ b/interface-definitions/system-conntrack.xml.in
@@ -9,6 +9,12 @@
<priority>218</priority>
</properties>
<children>
+ <leafNode name="flow-accounting">
+ <properties>
+ <help>Enable connection tracking flow accounting</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="expect-table-size">
<properties>
<help>Size of connection tracking expect table</help>
diff --git a/op-mode-definitions/show-techsupport_report.xml.in b/op-mode-definitions/show-techsupport_report.xml.in
index aa51eacd9..ef051e940 100644
--- a/op-mode-definitions/show-techsupport_report.xml.in
+++ b/op-mode-definitions/show-techsupport_report.xml.in
@@ -3,6 +3,9 @@
<node name="show">
<children>
<node name="tech-support">
+ <properties>
+ <help>Show tech-support report</help>
+ </properties>
<children>
<node name="report">
<properties>
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index f74ce4b72..391ef03ff 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -603,5 +603,17 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
with open(path, 'r') as f:
self.assertNotEqual(f.read().strip(), conf['default'], msg=path)
+ def test_flow_offload_software(self):
+ self.cli_set(['firewall', 'global-options', 'flow-offload', 'software', 'interface', 'eth0'])
+ self.cli_commit()
+ nftables_search = [
+ ['flowtable VYOS_FLOWTABLE_software'],
+ ['hook ingress priority filter - 1'],
+ ['devices = { eth0 }'],
+ ['flow add @VYOS_FLOWTABLE_software'],
+ ]
+ self.verify_nftables(nftables_search, 'inet vyos_offload')
+
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index c3b1ee015..769cc598f 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -26,7 +26,7 @@ from vyos.config import Config
from vyos.configdict import node_changed
from vyos.configdiff import get_config_diff, Diff
from vyos.configdep import set_dependents, call_dependents
-# from vyos.configverify import verify_interface_exists
+from vyos.configverify import verify_interface_exists
from vyos.firewall import fqdn_config_parse
from vyos.firewall import geoip_update
from vyos.template import render
@@ -38,6 +38,7 @@ from vyos.utils.process import process_named_running
from vyos.utils.process import rc_cmd
from vyos import ConfigError
from vyos import airbag
+
airbag.enable()
nat_conf_script = 'nat.py'
@@ -100,7 +101,7 @@ def geoip_updated(conf, firewall):
elif (path[0] == 'ipv6'):
set_name = f'GEOIP_CC6_{path[1]}_{path[2]}_{path[4]}'
out['ipv6_name'].append(set_name)
-
+
updated = True
if 'delete' in node_diff:
@@ -140,6 +141,14 @@ def get_config(config=None):
fqdn_config_parse(firewall)
+ firewall['flowtable_enabled'] = False
+ flow_offload = dict_search_args(firewall, 'global_options', 'flow_offload')
+ if flow_offload and 'disable' not in flow_offload:
+ for offload_type in ('software', 'hardware'):
+ if dict_search_args(flow_offload, offload_type, 'interface'):
+ firewall['flowtable_enabled'] = True
+ break
+
return firewall
def verify_rule(firewall, rule_conf, ipv6):
@@ -327,6 +336,14 @@ def verify(firewall):
for rule_id, rule_conf in name_conf['rule'].items():
verify_rule(firewall, rule_conf, True)
+ # Verify flow offload options
+ flow_offload = dict_search_args(firewall, 'global_options', 'flow_offload')
+ for offload_type in ('software', 'hardware'):
+ interfaces = dict_search_args(flow_offload, offload_type, 'interface') or []
+ for interface in interfaces:
+ # nft will raise an error when adding a non-existent interface to a flowtable
+ verify_interface_exists(interface)
+
return None
def generate(firewall):
@@ -336,13 +353,15 @@ def generate(firewall):
# Determine if conntrack is needed
firewall['ipv4_conntrack_action'] = 'return'
firewall['ipv6_conntrack_action'] = 'return'
-
- for rules, path in dict_search_recursive(firewall, 'rule'):
- if any(('state' in rule_conf or 'connection_status' in rule_conf) for rule_conf in rules.values()):
- if path[0] == 'ipv4':
- firewall['ipv4_conntrack_action'] = 'accept'
- elif path[0] == 'ipv6':
- firewall['ipv6_conntrack_action'] = 'accept'
+ if firewall['flowtable_enabled']: # Netfilter's flowtable offload requires conntrack
+ firewall['ipv4_conntrack_action'] = 'accept'
+ firewall['ipv6_conntrack_action'] = 'accept'
+ else: # Check if conntrack is needed by firewall rules
+ for proto in ('ipv4', 'ipv6'):
+ for rules, _ in dict_search_recursive(firewall.get(proto, {}), 'rule'):
+ if any(('state' in rule_conf or 'connection_status' in rule_conf) for rule_conf in rules.values()):
+ firewall[f'{proto}_conntrack_action'] = 'accept'
+ break
render(nftables_conf, 'firewall/nftables.j2', firewall)
return None
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 1d0feb56f..9f4de990c 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -344,9 +344,6 @@ def verify(openvpn):
if v6_subnets > 1:
raise ConfigError('Cannot specify more than 1 IPv6 server subnet')
- if v6_subnets > 0 and v4_subnets == 0:
- raise ConfigError('IPv6 server requires an IPv4 server subnet')
-
for subnet in tmp:
if is_ipv4(subnet):
subnet = IPv4Network(subnet)
@@ -388,6 +385,10 @@ def verify(openvpn):
for v4PoolNet in v4PoolNets:
if IPv4Address(client['ip'][0]) in v4PoolNet:
print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.')
+ # configuring a client_ip_pool will set 'server ... nopool' which is currently incompatible with 'server-ipv6' (probably to be fixed upstream)
+ for subnet in (dict_search('server.subnet', openvpn) or []):
+ if is_ipv6(subnet):
+ raise ConfigError(f'Setting client-ip-pool is incompatible having an IPv6 server subnet.')
for subnet in (dict_search('server.subnet', openvpn) or []):
if is_ipv6(subnet):