summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/firewall/nftables.tmpl10
-rw-r--r--data/templates/high-availability/keepalived.conf.tmpl (renamed from data/templates/vrrp/keepalived.conf.tmpl)57
-rw-r--r--data/templates/zone_policy/nftables.tmpl14
-rw-r--r--interface-definitions/high-availability.xml.in (renamed from interface-definitions/vrrp.xml.in)144
-rw-r--r--interface-definitions/include/firewall/common-rule.xml.i2
-rw-r--r--op-mode-definitions/firewall.xml.in15
-rw-r--r--python/vyos/template.py6
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py4
-rwxr-xr-xsmoketest/scripts/cli/test_ha_virtual_server.py146
-rwxr-xr-xsmoketest/scripts/cli/test_ha_vrrp.py10
-rwxr-xr-xsrc/conf_mode/conntrack_sync.py2
-rwxr-xr-xsrc/conf_mode/firewall-interface.py31
-rwxr-xr-xsrc/conf_mode/firewall.py14
-rwxr-xr-xsrc/conf_mode/high-availability.py (renamed from src/conf_mode/vrrp.py)63
-rwxr-xr-xsrc/conf_mode/zone_policy.py6
15 files changed, 445 insertions, 79 deletions
diff --git a/data/templates/firewall/nftables.tmpl b/data/templates/firewall/nftables.tmpl
index bbb111b1f..68e83de64 100644
--- a/data/templates/firewall/nftables.tmpl
+++ b/data/templates/firewall/nftables.tmpl
@@ -46,11 +46,8 @@ define P_{{ group_name }} = {
table ip filter {
{% if first_install is defined %}
- chain VYOS_FW_IN {
+ chain VYOS_FW_FORWARD {
type filter hook forward priority 0; policy accept;
- }
- chain VYOS_FW_OUT {
- type filter hook forward priority 1; policy accept;
jump VYOS_POST_FW
}
chain VYOS_FW_LOCAL {
@@ -104,11 +101,8 @@ table ip filter {
table ip6 filter {
{% if first_install is defined %}
- chain VYOS_FW6_IN {
+ chain VYOS_FW6_FORWARD {
type filter hook forward priority 0; policy accept;
- }
- chain VYOS_FW6_OUT {
- type filter hook forward priority 1; policy accept;
jump VYOS_POST_FW6
}
chain VYOS_FW6_LOCAL {
diff --git a/data/templates/vrrp/keepalived.conf.tmpl b/data/templates/high-availability/keepalived.conf.tmpl
index 6585fc60b..817c65ff0 100644
--- a/data/templates/vrrp/keepalived.conf.tmpl
+++ b/data/templates/high-availability/keepalived.conf.tmpl
@@ -9,8 +9,8 @@ global_defs {
notify_fifo_script /usr/libexec/vyos/system/keepalived-fifo.py
}
-{% if group is defined and group is not none %}
-{% for name, group_config in group.items() if group_config.disable is not defined %}
+{% if vrrp is defined and vrrp.group is defined and vrrp.group is not none %}
+{% for name, group_config in vrrp.group.items() if group_config.disable is not defined %}
{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none %}
vrrp_script healthcheck_{{ name }} {
script "{{ group_config.health_check.script }}"
@@ -82,8 +82,8 @@ vrrp_instance {{ name }} {
{% endfor %}
{% endif %}
-{% if sync_group is defined and sync_group is not none %}
-{% for name, sync_group_config in sync_group.items() if sync_group_config.disable is not defined %}
+{% if vrrp is defined and vrrp.sync_group is defined and vrrp.sync_group is not none %}
+{% for name, sync_group_config in vrrp.sync_group.items() if sync_group_config.disable is not defined %}
vrrp_sync_group {{ name }} {
group {
{% if sync_group_config.member is defined and sync_group_config.member is not none %}
@@ -94,14 +94,14 @@ vrrp_sync_group {{ name }} {
}
{# Health-check scripts should be in section sync-group if member is part of the sync-group T4081 #}
-{% for name, group_config in group.items() if group_config.disable is not defined %}
+{% for name, group_config in vrrp.group.items() if group_config.disable is not defined %}
{% if group_config.health_check is defined and group_config.health_check.script is defined and group_config.health_check.script is not none and name in sync_group_config.member %}
track_script {
healthcheck_{{ name }}
}
{% endif %}
{% endfor %}
-{% if conntrack_sync_group is defined and conntrack_sync_group == name %}
+{% if vrrp.conntrack_sync_group is defined and vrrp.conntrack_sync_group == name %}
{% set vyos_helper = "/usr/libexec/vyos/vyos-vrrp-conntracksync.sh" %}
notify_master "{{ vyos_helper }} master {{ name }}"
notify_backup "{{ vyos_helper }} backup {{ name }}"
@@ -110,3 +110,48 @@ vrrp_sync_group {{ name }} {
}
{% endfor %}
{% endif %}
+
+# Virtual-server configuration
+{% if virtual_server is defined and virtual_server is not none %}
+{% for vserver, vserver_config in virtual_server.items() %}
+virtual_server {{ vserver }} {{ vserver_config.port }} {
+ delay_loop {{ vserver_config.delay_loop }}
+{% if vserver_config.algorithm == 'round-robin' %}
+ lb_algo rr
+{% elif vserver_config.algorithm == 'weighted-round-robin' %}
+ lb_algo wrr
+{% elif vserver_config.algorithm == 'least-connection' %}
+ lb_algo lc
+{% elif vserver_config.algorithm == 'weighted-least-connection' %}
+ lb_algo wlc
+{% elif vserver_config.algorithm == 'source-hashing' %}
+ lb_algo sh
+{% elif vserver_config.algorithm == 'destination-hashing' %}
+ lb_algo dh
+{% elif vserver_config.algorithm == 'locality-based-least-connection' %}
+ lb_algo lblc
+{% endif %}
+{% if vserver_config.forward_method == "nat" %}
+ lb_kind NAT
+{% elif vserver_config.forward_method == "direct" %}
+ lb_kind DR
+{% elif vserver_config.forward_method == "tunnel" %}
+ lb_kind TUN
+{% endif %}
+ persistence_timeout {{ vserver_config.persistence_timeout }}
+ protocol {{ vserver_config.protocol | upper }}
+{% if vserver_config.real_server is defined and vserver_config.real_server is not none %}
+{% for rserver, rserver_config in vserver_config.real_server.items() %}
+ real_server {{ rserver }} {{ rserver_config.port }} {
+ weight 1
+ {{ vserver_config.protocol | upper }}_CHECK {
+{% if rserver_config.connection_timeout is defined and rserver_config.connection_timeout is not none %}
+ connect_timeout {{ rserver_config.connection_timeout }}
+{% endif %}
+ }
+ }
+{% endfor %}
+{% endif %}
+}
+{% endfor %}
+{% endif %}
diff --git a/data/templates/zone_policy/nftables.tmpl b/data/templates/zone_policy/nftables.tmpl
index 21230c688..fae6e8c4f 100644
--- a/data/templates/zone_policy/nftables.tmpl
+++ b/data/templates/zone_policy/nftables.tmpl
@@ -81,7 +81,7 @@ table ip6 filter {
insert rule ip filter VYOS_FW_LOCAL counter jump VZONE_{{ zone_name }}_IN
insert rule ip filter VYOS_FW_OUTPUT counter jump VZONE_{{ zone_name }}_OUT
{% else %}
-insert rule ip filter VYOS_FW_OUT oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
+insert rule ip filter VYOS_FW_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE_{{ zone_name }}
{% endif %}
{% endif %}
{% if zone_conf.ipv6 %}
@@ -89,9 +89,19 @@ insert rule ip filter VYOS_FW_OUT oifname { {{ zone_conf.interface | join(',') }
insert rule ip6 filter VYOS_FW6_LOCAL counter jump VZONE6_{{ zone_name }}_IN
insert rule ip6 filter VYOS_FW6_OUTPUT counter jump VZONE6_{{ zone_name }}_OUT
{% else %}
-insert rule ip6 filter VYOS_FW6_OUT oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE6_{{ zone_name }}
+insert rule ip6 filter VYOS_FW6_FORWARD oifname { {{ zone_conf.interface | join(',') }} } counter jump VZONE6_{{ zone_name }}
{% endif %}
{% endif %}
{% endfor %}
+{# Ensure that state-policy rule is first in the chain #}
+{% if firewall.state_policy is defined %}
+{% for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'] %}
+insert rule ip filter {{ chain }} jump VYOS_STATE_POLICY
+{% endfor %}
+{% for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL'] %}
+insert rule ip6 filter {{ chain }} jump VYOS_STATE_POLICY6
+{% endfor %}
+{% endif %}
+
{% endif %}
diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/high-availability.xml.in
index 53d79caac..f46343c76 100644
--- a/interface-definitions/vrrp.xml.in
+++ b/interface-definitions/high-availability.xml.in
@@ -1,13 +1,13 @@
<?xml version="1.0"?>
<interfaceDefinition>
- <node name="high-availability">
+ <node name="high-availability" owner="${vyos_conf_scripts_dir}/high-availability.py">
<properties>
+ <priority>800</priority> <!-- after all interfaces and conntrack-sync -->
<help>High availability settings</help>
</properties>
<children>
- <node name="vrrp" owner="${vyos_conf_scripts_dir}/vrrp.py">
+ <node name="vrrp">
<properties>
- <priority>800</priority> <!-- after all interfaces and conntrack-sync -->
<help>Virtual Router Redundancy Protocol settings</help>
</properties>
<children>
@@ -252,6 +252,144 @@
</tagNode>
</children>
</node>
+ <tagNode name="virtual-server">
+ <properties>
+ <help>Load-balancing virtual server address</help>
+ </properties>
+ <children>
+ <leafNode name="algorithm">
+ <properties>
+ <help>Schedule algorithm (default - least-connection)</help>
+ <completionHelp>
+ <list>round-robin weighted-round-robin least-connection weighted-least-connection source-hashing destination-hashing locality-based-least-connection</list>
+ </completionHelp>
+ <valueHelp>
+ <format>round-robin</format>
+ <description>Round robin</description>
+ </valueHelp>
+ <valueHelp>
+ <format>weighted-round-robin</format>
+ <description>Weighted round robin</description>
+ </valueHelp>
+ <valueHelp>
+ <format>least-connection</format>
+ <description>Least connection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>weighted-least-connection</format>
+ <description>Weighted least connection</description>
+ </valueHelp>
+ <valueHelp>
+ <format>source-hashing</format>
+ <description>Source hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>destination-hashing</format>
+ <description>Destination hashing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>locality-based-least-connection</format>
+ <description>Locality-Based least connection</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(round-robin|weighted-round-robin|least-connection|weighted-least-connection|source-hashing|destination-hashing|locality-based-least-connection)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>least-connection</defaultValue>
+ </leafNode>
+ <leafNode name="delay-loop">
+ <properties>
+ <help>Interval between health-checks (in seconds)</help>
+ <valueHelp>
+ <format>u32:1-600</format>
+ <description>Interval in seconds (default: 10)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-3600"/>
+ </constraint>
+ </properties>
+ <defaultValue>10</defaultValue>
+ </leafNode>
+ <leafNode name="forward-method">
+ <properties>
+ <help>Forwarding method (default: NAT)</help>
+ <completionHelp>
+ <list>direct nat tunnel</list>
+ </completionHelp>
+ <valueHelp>
+ <format>direct</format>
+ <description>Direct routing</description>
+ </valueHelp>
+ <valueHelp>
+ <format>nat</format>
+ <description>NAT</description>
+ </valueHelp>
+ <valueHelp>
+ <format>tunnel</format>
+ <description>Tunneling</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(direct|nat|tunnel)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>nat</defaultValue>
+ </leafNode>
+ #include <include/port-number.xml.i>
+ <leafNode name="persistence-timeout">
+ <properties>
+ <help>Timeout for persistent connections</help>
+ <valueHelp>
+ <format>u32:1-86400</format>
+ <description>Timeout for persistent connections (default: 300)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ </properties>
+ <defaultValue>300</defaultValue>
+ </leafNode>
+ <leafNode name="protocol">
+ <properties>
+ <help>Protocol for port checks (default: TCP)</help>
+ <completionHelp>
+ <list>tcp udp</list>
+ </completionHelp>
+ <valueHelp>
+ <format>tcp</format>
+ <description>TCP</description>
+ </valueHelp>
+ <valueHelp>
+ <format>udp</format>
+ <description>UDP</description>
+ </valueHelp>
+ <constraint>
+ <regex>^(tcp|udp)$</regex>
+ </constraint>
+ </properties>
+ <defaultValue>tcp</defaultValue>
+ </leafNode>
+ <tagNode name="real-server">
+ <properties>
+ <help>Real server address</help>
+ </properties>
+ <children>
+ #include <include/port-number.xml.i>
+ <leafNode name="connection-timeout">
+ <properties>
+ <help>Server connection timeout</help>
+ <valueHelp>
+ <format>u32:1-86400</format>
+ <description>Connection timeout to remote server</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
</children>
</node>
</interfaceDefinition>
diff --git a/interface-definitions/include/firewall/common-rule.xml.i b/interface-definitions/include/firewall/common-rule.xml.i
index 415b6bf00..727200ed7 100644
--- a/interface-definitions/include/firewall/common-rule.xml.i
+++ b/interface-definitions/include/firewall/common-rule.xml.i
@@ -99,7 +99,7 @@
<properties>
<help>Protocol to match (protocol name, number, or "all")</help>
<completionHelp>
- <script>cat /etc/protocols | sed -e '/^#.*/d' | awk '{ print $1 }'</script>
+ <script>${vyos_completion_dir}/list_protocols.sh</script>
</completionHelp>
<valueHelp>
<format>all</format>
diff --git a/op-mode-definitions/firewall.xml.in b/op-mode-definitions/firewall.xml.in
index 84df67b3d..b5dee7c9e 100644
--- a/op-mode-definitions/firewall.xml.in
+++ b/op-mode-definitions/firewall.xml.in
@@ -112,11 +112,24 @@
<help>Show firewall information</help>
</properties>
<children>
- <leafNode name="group">
+ <tagNode name="group">
<properties>
<help>Show firewall group</help>
+ <completionHelp>
+ <path>firewall group address-group</path>
+ <path>firewall group network-group</path>
+ <path>firewall group port-group</path>
+ <path>firewall group ipv6-address-group</path>
+ <path>firewall group ipv6-network-group</path>
+ </completionHelp>
</properties>
<command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group --name $4</command>
+ </tagNode>
+ <leafNode name="group">
+ <properties>
+ <help>Show firewall group</help>
+ </properties>
+ <command>sudo ${vyos_op_scripts_dir}/firewall.py --action show_group</command>
</leafNode>
<tagNode name="ipv6-name">
<properties>
diff --git a/python/vyos/template.py b/python/vyos/template.py
index 7671bf377..6f65c6c98 100644
--- a/python/vyos/template.py
+++ b/python/vyos/template.py
@@ -526,11 +526,7 @@ def nft_state_policy(conf, state, ipv6=False):
out.append('counter')
if 'action' in conf:
- if conf['action'] == 'accept':
- jump_target = 'VYOS_POST_FW6' if ipv6 else 'VYOS_POST_FW'
- out.append(f'jump {jump_target}')
- else:
- out.append(conf['action'])
+ out.append(conf['action'])
return " ".join(out)
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index 5f728f0cd..2b3b354ba 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -142,8 +142,8 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.cli_commit()
chains = {
- 'ip filter': ['VYOS_FW_IN', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
- 'ip6 filter': ['VYOS_FW6_IN', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
+ 'ip filter': ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL'],
+ 'ip6 filter': ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']
}
for table in ['ip filter', 'ip6 filter']:
diff --git a/smoketest/scripts/cli/test_ha_virtual_server.py b/smoketest/scripts/cli/test_ha_virtual_server.py
new file mode 100755
index 000000000..e3a91283e
--- /dev/null
+++ b/smoketest/scripts/cli/test_ha_virtual_server.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021-2022 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 <http://www.gnu.org/licenses/>.
+
+import unittest
+
+from base_vyostest_shim import VyOSUnitTestSHIM
+
+from vyos.configsession import ConfigSessionError
+from vyos.ifconfig.vrrp import VRRP
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import read_file
+from vyos.template import inc_ip
+
+PROCESS_NAME = 'keepalived'
+KEEPALIVED_CONF = VRRP.location['config']
+base_path = ['high-availability']
+vrrp_interface = 'eth1'
+
+class TestHAVirtualServer(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ # Check for running process
+ self.assertTrue(process_named_running(PROCESS_NAME))
+
+ self.cli_delete(['interfaces', 'ethernet', vrrp_interface, 'address'])
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ # Process must be terminated after deleting the config
+ self.assertFalse(process_named_running(PROCESS_NAME))
+
+ def test_01_ha_virtual_server(self):
+ algo = 'least-connection'
+ delay = '10'
+ method = 'nat'
+ persistence_timeout = '600'
+ vip = '203.0.113.111'
+ vport = '2222'
+ rservers = ['192.0.2.21', '192.0.2.22', '192.0.2.23']
+ rport = '22'
+ proto = 'tcp'
+ connection_timeout = '30'
+
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+ def test_02_ha_virtual_server_and_vrrp(self):
+ algo = 'least-connection'
+ delay = '15'
+ method = 'nat'
+ persistence_timeout = '300'
+ vip = '203.0.113.222'
+ vport = '22322'
+ rservers = ['192.0.2.11', '192.0.2.12']
+ rport = '222'
+ proto = 'tcp'
+ connection_timeout = '23'
+ group = 'VyOS'
+ vrid = '99'
+
+ vrrp_base = base_path + ['vrrp', 'group']
+ vserver_base = base_path + ['virtual-server']
+
+ self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'address', '203.0.113.10/24'])
+
+ # VRRP config
+ self.cli_set(vrrp_base + [group, 'description', group])
+ self.cli_set(vrrp_base + [group, 'interface', vrrp_interface])
+ self.cli_set(vrrp_base + [group, 'address', vip + '/24'])
+ self.cli_set(vrrp_base + [group, 'vrid', vrid])
+
+ # Virtual-server config
+ self.cli_set(vserver_base + [vip, 'algorithm', algo])
+ self.cli_set(vserver_base + [vip, 'delay-loop', delay])
+ self.cli_set(vserver_base + [vip, 'forward-method', method])
+ self.cli_set(vserver_base + [vip, 'persistence-timeout', persistence_timeout])
+ self.cli_set(vserver_base + [vip, 'port', vport])
+ self.cli_set(vserver_base + [vip, 'protocol', proto])
+ for rs in rservers:
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'connection-timeout', connection_timeout])
+ self.cli_set(vserver_base + [vip, 'real-server', rs, 'port', rport])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(KEEPALIVED_CONF)
+
+ # Keepalived vrrp
+ self.assertIn(f'# {group}', config)
+ self.assertIn(f'interface {vrrp_interface}', config)
+ self.assertIn(f'virtual_router_id {vrid}', config)
+ self.assertIn(f'priority 100', config) # default value
+ self.assertIn(f'advert_int 1', config) # default value
+ self.assertIn(f'preempt_delay 0', config) # default value
+
+ # Keepalived virtual-server
+ self.assertIn(f'delay_loop {delay}', config)
+ self.assertIn(f'lb_algo lc', config)
+ self.assertIn(f'lb_kind {method.upper()}', config)
+ self.assertIn(f'persistence_timeout {persistence_timeout}', config)
+ self.assertIn(f'protocol {proto.upper()}', config)
+ for rs in rservers:
+ self.assertIn(f'real_server {rs} {rport}', config)
+ self.assertIn(f'{proto.upper()}_CHECK', config)
+ self.assertIn(f'connect_timeout {connection_timeout}', config)
+
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2)
diff --git a/smoketest/scripts/cli/test_ha_vrrp.py b/smoketest/scripts/cli/test_ha_vrrp.py
index 23a9f7796..a37ce7e64 100755
--- a/smoketest/scripts/cli/test_ha_vrrp.py
+++ b/smoketest/scripts/cli/test_ha_vrrp.py
@@ -27,7 +27,7 @@ from vyos.template import inc_ip
PROCESS_NAME = 'keepalived'
KEEPALIVED_CONF = VRRP.location['config']
-base_path = ['high-availability', 'vrrp']
+base_path = ['high-availability']
vrrp_interface = 'eth1'
groups = ['VLAN77', 'VLAN78', 'VLAN201']
@@ -56,7 +56,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -91,7 +91,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -138,7 +138,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
for group in groups:
vlan_id = group.lstrip('VLAN')
vip = f'100.64.{vlan_id}.1/24'
- group_base = base_path + ['group', group]
+ group_base = base_path + ['vrrp', 'group', group]
self.cli_set(['interfaces', 'ethernet', vrrp_interface, 'vif', vlan_id, 'address', inc_ip(vip, 1) + '/' + vip.split('/')[-1]])
@@ -146,7 +146,7 @@ class TestVRRP(VyOSUnitTestSHIM.TestCase):
self.cli_set(group_base + ['address', vip])
self.cli_set(group_base + ['vrid', vlan_id])
- self.cli_set(base_path + ['sync-group', sync_group, 'member', group])
+ self.cli_set(base_path + ['vrrp', 'sync-group', sync_group, 'member', group])
# commit changes
self.cli_commit()
diff --git a/src/conf_mode/conntrack_sync.py b/src/conf_mode/conntrack_sync.py
index f82a077e6..8f9837c2b 100755
--- a/src/conf_mode/conntrack_sync.py
+++ b/src/conf_mode/conntrack_sync.py
@@ -36,7 +36,7 @@ airbag.enable()
config_file = '/run/conntrackd/conntrackd.conf'
def resync_vrrp():
- tmp = run('/usr/libexec/vyos/conf_mode/vrrp.py')
+ tmp = run('/usr/libexec/vyos/conf_mode/high-availability.py')
if tmp > 0:
print('ERROR: error restarting VRRP daemon!')
diff --git a/src/conf_mode/firewall-interface.py b/src/conf_mode/firewall-interface.py
index 516fa6c48..b0df9dff4 100755
--- a/src/conf_mode/firewall-interface.py
+++ b/src/conf_mode/firewall-interface.py
@@ -32,13 +32,13 @@ from vyos import airbag
airbag.enable()
NFT_CHAINS = {
- 'in': 'VYOS_FW_IN',
- 'out': 'VYOS_FW_OUT',
+ 'in': 'VYOS_FW_FORWARD',
+ 'out': 'VYOS_FW_FORWARD',
'local': 'VYOS_FW_LOCAL'
}
NFT6_CHAINS = {
- 'in': 'VYOS_FW6_IN',
- 'out': 'VYOS_FW6_OUT',
+ 'in': 'VYOS_FW6_FORWARD',
+ 'out': 'VYOS_FW6_FORWARD',
'local': 'VYOS_FW6_LOCAL'
}
@@ -91,11 +91,11 @@ def verify(if_firewall):
def generate(if_firewall):
return None
-def cleanup_rule(table, chain, ifname, new_name=None):
+def cleanup_rule(table, chain, prefix, ifname, new_name=None):
results = cmd(f'nft -a list chain {table} {chain}').split("\n")
retval = None
for line in results:
- if f'ifname "{ifname}"' in line:
+ if f'{prefix}ifname "{ifname}"' in line:
if new_name and f'jump {new_name}' in line:
# new_name is used to clear rules for any previously referenced chains
# returns true when rule exists and doesn't need to be created
@@ -108,6 +108,7 @@ def cleanup_rule(table, chain, ifname, new_name=None):
return retval
def state_policy_handle(table, chain):
+ # Find any state-policy rule to ensure interface rules are only inserted afterwards
results = cmd(f'nft -a list chain {table} {chain}').split("\n")
for line in results:
if 'jump VYOS_STATE_POLICY' in line:
@@ -126,11 +127,12 @@ def apply(if_firewall):
name = dict_search_args(if_firewall, direction, 'name')
if name:
- rule_exists = cleanup_rule('ip filter', chain, ifname, name)
- rule_action = 'insert'
- rule_prefix = ''
+ rule_exists = cleanup_rule('ip filter', chain, if_prefix, ifname, name)
if not rule_exists:
+ rule_action = 'insert'
+ rule_prefix = ''
+
handle = state_policy_handle('ip filter', chain)
if handle:
rule_action = 'add'
@@ -138,15 +140,16 @@ def apply(if_firewall):
run(f'nft {rule_action} rule ip filter {chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {name}')
else:
- cleanup_rule('ip filter', chain, ifname)
+ cleanup_rule('ip filter', chain, if_prefix, ifname)
ipv6_name = dict_search_args(if_firewall, direction, 'ipv6_name')
if ipv6_name:
- rule_exists = cleanup_rule('ip6 filter', ipv6_chain, ifname, ipv6_name)
- rule_action = 'insert'
- rule_prefix = ''
+ rule_exists = cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname, ipv6_name)
if not rule_exists:
+ rule_action = 'insert'
+ rule_prefix = ''
+
handle = state_policy_handle('ip filter', chain)
if handle:
rule_action = 'add'
@@ -154,7 +157,7 @@ def apply(if_firewall):
run(f'nft {rule_action} rule ip6 filter {ipv6_chain} {rule_prefix} {if_prefix}ifname {ifname} counter jump {ipv6_name}')
else:
- cleanup_rule('ip6 filter', ipv6_chain, ifname)
+ cleanup_rule('ip6 filter', ipv6_chain, if_prefix, ifname)
return None
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 8e037c679..6016d94fa 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -53,14 +53,12 @@ preserve_chains = [
'INPUT',
'FORWARD',
'OUTPUT',
- 'VYOS_FW_IN',
- 'VYOS_FW_OUT',
+ 'VYOS_FW_FORWARD',
'VYOS_FW_LOCAL',
'VYOS_FW_OUTPUT',
'VYOS_POST_FW',
'VYOS_FRAG_MARK',
- 'VYOS_FW6_IN',
- 'VYOS_FW6_OUT',
+ 'VYOS_FW6_FORWARD',
'VYOS_FW6_LOCAL',
'VYOS_FW6_OUTPUT',
'VYOS_POST_FW6',
@@ -228,7 +226,7 @@ def cleanup_commands(firewall):
commands.append(f'delete chain {table} {chain}')
elif 'rule' in item:
rule = item['rule']
- if rule['chain'] in ['VYOS_FW_IN', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL', 'VYOS_FW6_IN', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']:
+ if rule['chain'] in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL', 'VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']:
if 'expr' in rule and any([True for expr in rule['expr'] if dict_search_args(expr, 'jump', 'target') == state_chain]):
if 'state_policy' not in firewall:
chain = rule['chain']
@@ -303,7 +301,7 @@ def post_apply_trap(firewall):
def state_policy_rule_exists():
# Determine if state policy rules already exist in nft
- search_str = cmd(f'nft list chain ip filter VYOS_FW_IN')
+ search_str = cmd(f'nft list chain ip filter VYOS_FW_FORWARD')
return 'VYOS_STATE_POLICY' in search_str
def apply(firewall):
@@ -317,10 +315,10 @@ def apply(firewall):
raise ConfigError('Failed to apply firewall')
if 'state_policy' in firewall and not state_policy_rule_exists():
- for chain in ['VYOS_FW_IN', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL']:
+ for chain in ['VYOS_FW_FORWARD', 'VYOS_FW_OUTPUT', 'VYOS_FW_LOCAL']:
cmd(f'nft insert rule ip filter {chain} jump VYOS_STATE_POLICY')
- for chain in ['VYOS_FW6_IN', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']:
+ for chain in ['VYOS_FW6_FORWARD', 'VYOS_FW6_OUTPUT', 'VYOS_FW6_LOCAL']:
cmd(f'nft insert rule ip6 filter {chain} jump VYOS_STATE_POLICY6')
apply_sysfs(firewall)
diff --git a/src/conf_mode/vrrp.py b/src/conf_mode/high-availability.py
index c72efc61f..7d51bb393 100755
--- a/src/conf_mode/vrrp.py
+++ b/src/conf_mode/high-availability.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -40,33 +40,41 @@ def get_config(config=None):
else:
conf = Config()
- base = ['high-availability', 'vrrp']
+ base = ['high-availability']
+ base_vrrp = ['high-availability', 'vrrp']
if not conf.exists(base):
return None
- vrrp = conf.get_config_dict(base, key_mangling=('-', '_'),
+ ha = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
- if 'group' in vrrp:
- default_values = defaults(base + ['group'])
- for group in vrrp['group']:
- vrrp['group'][group] = dict_merge(default_values, vrrp['group'][group])
+ if 'vrrp' in ha:
+ if 'group' in ha['vrrp']:
+ default_values_vrrp = defaults(base_vrrp + ['group'])
+ for group in ha['vrrp']['group']:
+ ha['vrrp']['group'][group] = dict_merge(default_values_vrrp, ha['vrrp']['group'][group])
+
+ # Merge per virtual-server default values
+ if 'virtual_server' in ha:
+ default_values = defaults(base + ['virtual-server'])
+ for vs in ha['virtual_server']:
+ ha['virtual_server'][vs] = dict_merge(default_values, ha['virtual_server'][vs])
## Get the sync group used for conntrack-sync
conntrack_path = ['service', 'conntrack-sync', 'failover-mechanism', 'vrrp', 'sync-group']
if conf.exists(conntrack_path):
- vrrp['conntrack_sync_group'] = conf.return_value(conntrack_path)
+ ha['conntrack_sync_group'] = conf.return_value(conntrack_path)
- return vrrp
+ return ha
-def verify(vrrp):
- if not vrrp:
+def verify(ha):
+ if not ha:
return None
used_vrid_if = []
- if 'group' in vrrp:
- for group, group_config in vrrp['group'].items():
+ if 'vrrp' in ha and 'group' in ha['vrrp']:
+ for group, group_config in ha['vrrp']['group'].items():
# Check required fields
if 'vrid' not in group_config:
raise ConfigError(f'VRID is required but not set in VRRP group "{group}"')
@@ -119,24 +127,37 @@ def verify(vrrp):
if is_ipv4(group_config['peer_address']):
raise ConfigError(f'VRRP group "{group}" uses IPv6 but peer-address is IPv4!')
# Check sync groups
- if 'sync_group' in vrrp:
- for sync_group, sync_config in vrrp['sync_group'].items():
+ if 'vrrp' in ha and 'sync_group' in ha['vrrp']:
+ for sync_group, sync_config in ha['vrrp']['sync_group'].items():
if 'member' in sync_config:
for member in sync_config['member']:
- if member not in vrrp['group']:
+ if member not in ha['vrrp']['group']:
raise ConfigError(f'VRRP sync-group "{sync_group}" refers to VRRP group "{member}", '\
'but it does not exist!')
-def generate(vrrp):
- if not vrrp:
+ # Virtual-server
+ if 'virtual_server' in ha:
+ for vs, vs_config in ha['virtual_server'].items():
+ if 'port' not in vs_config:
+ raise ConfigError(f'Port is required but not set for virtual-server "{vs}"')
+ if 'real_server' not in vs_config:
+ raise ConfigError(f'Real-server ip is required but not set for virtual-server "{vs}"')
+ # Real-server
+ for rs, rs_config in vs_config['real_server'].items():
+ if 'port' not in rs_config:
+ raise ConfigError(f'Port is required but not set for virtual-server "{vs}" real-server "{rs}"')
+
+
+def generate(ha):
+ if not ha:
return None
- render(VRRP.location['config'], 'vrrp/keepalived.conf.tmpl', vrrp)
+ render(VRRP.location['config'], 'high-availability/keepalived.conf.tmpl', ha)
return None
-def apply(vrrp):
+def apply(ha):
service_name = 'keepalived.service'
- if not vrrp:
+ if not ha:
call(f'systemctl stop {service_name}')
return None
diff --git a/src/conf_mode/zone_policy.py b/src/conf_mode/zone_policy.py
index 2535ea33b..d605e9639 100755
--- a/src/conf_mode/zone_policy.py
+++ b/src/conf_mode/zone_policy.py
@@ -152,7 +152,9 @@ def cleanup_commands():
continue
for expr in item['rule']['expr']:
target = dict_search_args(expr, 'jump', 'target')
- if target and target.startswith("VZONE"):
+ if not target:
+ continue
+ if target.startswith("VZONE") or target.startswith("VYOS_STATE_POLICY"):
commands.append(f'delete rule {table} {chain} handle {handle}')
for item in obj['nftables']:
if 'chain' in item:
@@ -180,7 +182,7 @@ def generate(zone_policy):
def apply(zone_policy):
install_result = run(f'nft -f {nftables_conf}')
- if install_result == 1:
+ if install_result != 0:
raise ConfigError('Failed to apply zone-policy')
return None