summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/firewall.xml.in4
-rw-r--r--interface-definitions/include/firewall/address-mask-ipv6.xml.i14
-rw-r--r--interface-definitions/include/firewall/address-mask.xml.i14
-rw-r--r--python/vyos/firewall.py19
-rwxr-xr-xsmoketest/scripts/cli/test_firewall.py67
5 files changed, 114 insertions, 4 deletions
diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in
index 2ebce79e5..3bce69fc4 100644
--- a/interface-definitions/firewall.xml.in
+++ b/interface-definitions/firewall.xml.in
@@ -412,6 +412,7 @@
#include <include/firewall/geoip.xml.i>
#include <include/firewall/source-destination-group-ipv6.xml.i>
#include <include/firewall/port.xml.i>
+ #include <include/firewall/address-mask-ipv6.xml.i>
</children>
</node>
<node name="source">
@@ -424,6 +425,7 @@
#include <include/firewall/geoip.xml.i>
#include <include/firewall/source-destination-group-ipv6.xml.i>
#include <include/firewall/port.xml.i>
+ #include <include/firewall/address-mask-ipv6.xml.i>
</children>
</node>
#include <include/firewall/common-rule.xml.i>
@@ -578,6 +580,7 @@
#include <include/firewall/geoip.xml.i>
#include <include/firewall/source-destination-group.xml.i>
#include <include/firewall/port.xml.i>
+ #include <include/firewall/address-mask.xml.i>
</children>
</node>
<node name="source">
@@ -590,6 +593,7 @@
#include <include/firewall/geoip.xml.i>
#include <include/firewall/source-destination-group.xml.i>
#include <include/firewall/port.xml.i>
+ #include <include/firewall/address-mask.xml.i>
</children>
</node>
#include <include/firewall/common-rule.xml.i>
diff --git a/interface-definitions/include/firewall/address-mask-ipv6.xml.i b/interface-definitions/include/firewall/address-mask-ipv6.xml.i
new file mode 100644
index 000000000..8c0483209
--- /dev/null
+++ b/interface-definitions/include/firewall/address-mask-ipv6.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from firewall/address-mask-ipv6.xml.i -->
+<leafNode name="address-mask">
+ <properties>
+ <help>IP mask</help>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IP mask to apply</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv6"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/include/firewall/address-mask.xml.i b/interface-definitions/include/firewall/address-mask.xml.i
new file mode 100644
index 000000000..7f6f17d1e
--- /dev/null
+++ b/interface-definitions/include/firewall/address-mask.xml.i
@@ -0,0 +1,14 @@
+<!-- include start from firewall/address-mask.xml.i -->
+<leafNode name="address-mask">
+ <properties>
+ <help>IP mask</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IPv4 mask to apply</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py
index 59ec4948f..48263eef5 100644
--- a/python/vyos/firewall.py
+++ b/python/vyos/firewall.py
@@ -113,12 +113,19 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if side in rule_conf:
prefix = side[0]
side_conf = rule_conf[side]
+ address_mask = side_conf.get('address_mask', None)
if 'address' in side_conf:
suffix = side_conf['address']
- if suffix[0] == '!':
- suffix = f'!= {suffix[1:]}'
- output.append(f'{ip_name} {prefix}addr {suffix}')
+ operator = ''
+ exclude = suffix[0] == '!'
+ if exclude:
+ operator = '!= '
+ suffix = suffix[1:]
+ if address_mask:
+ operator = '!=' if exclude else '=='
+ operator = f'& {address_mask} {operator} '
+ output.append(f'{ip_name} {prefix}addr {operator}{suffix}')
if 'fqdn' in side_conf:
fqdn = side_conf['fqdn']
@@ -168,9 +175,13 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):
if 'address_group' in group:
group_name = group['address_group']
operator = ''
- if group_name[0] == '!':
+ exclude = group_name[0] == "!"
+ if exclude:
operator = '!='
group_name = group_name[1:]
+ if address_mask:
+ operator = '!=' if exclude else '=='
+ operator = f'& {address_mask} {operator}'
output.append(f'{ip_name} {prefix}addr {operator} @A{def_suffix}_{group_name}')
# Generate firewall group domain-group
elif 'domain_group' in group:
diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py
index e172e086d..09b520b72 100755
--- a/smoketest/scripts/cli/test_firewall.py
+++ b/smoketest/scripts/cli/test_firewall.py
@@ -290,6 +290,40 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip vyos_filter')
+ def test_ipv4_mask(self):
+ name = 'smoketest-mask'
+ interface = 'eth0'
+
+ self.cli_set(['firewall', 'group', 'address-group', 'mask_group', 'address', '1.1.1.1'])
+
+ self.cli_set(['firewall', 'name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'name', name, 'enable-default-log'])
+
+ self.cli_set(['firewall', 'name', name, 'rule', '1', 'action', 'drop'])
+ self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address', '0.0.1.2'])
+ self.cli_set(['firewall', 'name', name, 'rule', '1', 'destination', 'address-mask', '0.0.255.255'])
+
+ self.cli_set(['firewall', 'name', name, 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address', '!0.0.3.4'])
+ self.cli_set(['firewall', 'name', name, 'rule', '2', 'source', 'address-mask', '0.0.255.255'])
+
+ self.cli_set(['firewall', 'name', name, 'rule', '3', 'action', 'drop'])
+ self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+ self.cli_set(['firewall', 'name', name, 'rule', '3', 'source', 'address-mask', '0.0.255.255'])
+
+ self.cli_set(['firewall', 'interface', interface, 'in', 'name', name])
+
+ self.cli_commit()
+
+ nftables_search = [
+ [f'daddr & 0.0.255.255 == 0.0.1.2'],
+ [f'saddr & 0.0.255.255 != 0.0.3.4'],
+ [f'saddr & 0.0.255.255 == @A_mask_group']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip vyos_filter')
+
+
def test_ipv6_basic_rules(self):
name = 'v6-smoketest'
interface = 'eth0'
@@ -369,6 +403,39 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):
self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+ def test_ipv6_mask(self):
+ name = 'v6-smoketest-mask'
+ interface = 'eth0'
+
+ self.cli_set(['firewall', 'group', 'ipv6-address-group', 'mask_group', 'address', '::beef'])
+
+ self.cli_set(['firewall', 'ipv6-name', name, 'default-action', 'drop'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'enable-default-log'])
+
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address', '::1111:2222:3333:4444'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '1', 'destination', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'action', 'accept'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address', '!::aaaa:bbbb:cccc:dddd'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '2', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'action', 'drop'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'group', 'address-group', 'mask_group'])
+ self.cli_set(['firewall', 'ipv6-name', name, 'rule', '3', 'source', 'address-mask', '::ffff:ffff:ffff:ffff'])
+
+ self.cli_set(['firewall', 'interface', interface, 'in', 'ipv6-name', name])
+
+ self.cli_commit()
+
+ nftables_search = [
+ ['daddr & ::ffff:ffff:ffff:ffff == ::1111:2222:3333:4444'],
+ ['saddr & ::ffff:ffff:ffff:ffff != ::aaaa:bbbb:cccc:dddd'],
+ ['saddr & ::ffff:ffff:ffff:ffff == @A6_mask_group']
+ ]
+
+ self.verify_nftables(nftables_search, 'ip6 vyos_filter')
+
def test_state_policy(self):
self.cli_set(['firewall', 'state-policy', 'established', 'action', 'accept'])
self.cli_set(['firewall', 'state-policy', 'related', 'action', 'accept'])