From 8e0e1a99e5510c7575ab8a09145d6b4354692d55 Mon Sep 17 00:00:00 2001 From: Nicolas Fort Date: Mon, 26 Aug 2024 18:10:01 +0000 Subject: T6647: firewall. Introduce patch for accepting ARP and DHCP replies on stateful bridge firewall. This patch is needed because ARP and DHCP are marked as invalid connections. Also, add ehternet-type matcher in bridge firewall. --- data/templates/firewall/nftables.j2 | 8 +++++- .../include/firewall/common-rule-bridge.xml.i | 1 + .../include/firewall/global-options.xml.i | 6 +++++ .../include/firewall/match-ether-type.xml.i | 30 ++++++++++++++++++++++ python/vyos/firewall.py | 14 ++++++++++ smoketest/scripts/cli/test_firewall.py | 11 +++++++- 6 files changed, 68 insertions(+), 2 deletions(-) mode change 100644 => 100755 interface-definitions/include/firewall/common-rule-bridge.xml.i mode change 100644 => 100755 interface-definitions/include/firewall/global-options.xml.i create mode 100755 interface-definitions/include/firewall/match-ether-type.xml.i diff --git a/data/templates/firewall/nftables.j2 b/data/templates/firewall/nftables.j2 index 155b7f4d0..034328400 100755 --- a/data/templates/firewall/nftables.j2 +++ b/data/templates/firewall/nftables.j2 @@ -376,8 +376,14 @@ table bridge vyos_filter { {% if bridge.output is vyos_defined %} {% for prior, conf in bridge.output.items() %} - chain VYOS_OUTUT_{{ prior }} { + chain VYOS_OUTPUT_{{ prior }} { type filter hook output priority {{ prior }}; policy accept; +{% if global_options.apply_to_bridged_traffic is vyos_defined %} +{% if 'invalid_connections' in global_options.apply_to_bridged_traffic %} + ct state invalid udp sport 67 udp dport 68 counter accept + ct state invalid ether type arp counter accept +{% endif %} +{% endif %} {% if global_options.state_policy is vyos_defined %} jump VYOS_STATE_POLICY {% endif %} diff --git a/interface-definitions/include/firewall/common-rule-bridge.xml.i b/interface-definitions/include/firewall/common-rule-bridge.xml.i old mode 100644 new mode 100755 index 9ae28f7be..80088bbec --- a/interface-definitions/include/firewall/common-rule-bridge.xml.i +++ b/interface-definitions/include/firewall/common-rule-bridge.xml.i @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/interface-definitions/include/firewall/global-options.xml.i b/interface-definitions/include/firewall/global-options.xml.i old mode 100644 new mode 100755 index cee8f1854..05fdd75cb --- a/interface-definitions/include/firewall/global-options.xml.i +++ b/interface-definitions/include/firewall/global-options.xml.i @@ -49,6 +49,12 @@ Apply configured firewall rules to traffic switched by bridges + + + Accept ARP and DHCP despite they are marked as invalid connection + + + Apply configured IPv4 firewall rules diff --git a/interface-definitions/include/firewall/match-ether-type.xml.i b/interface-definitions/include/firewall/match-ether-type.xml.i new file mode 100755 index 000000000..abfa9034d --- /dev/null +++ b/interface-definitions/include/firewall/match-ether-type.xml.i @@ -0,0 +1,30 @@ + + + + Ethernet type + + 802.1q 802.1ad arp ipv4 ipv6 + + + 802.1q + Customer VLAN tag type + + + 802.1ad + Service VLAN tag type + + + arp + Adress Resolution Protocol + + + _ipv4 + Internet Protocol version 4 + + + _ipv6 + Internet Protocol version 6 + + + + diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index f0cf3c924..b1978c1fa 100755 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -151,6 +151,20 @@ def parse_rule(rule_conf, hook, fw_name, rule_id, ip_name): proto = '{tcp, udp}' output.append(f'meta l4proto {operator} {proto}') + if 'ethernet_type' in rule_conf: + ether_type_mapping = { + '802.1q': '8021q', + '802.1ad': '8021ad', + 'ipv6': 'ip6', + 'ipv4': 'ip', + 'arp': 'arp' + } + ether_type = rule_conf['ethernet_type'] + operator = '!=' if ether_type.startswith('!') else '' + ether_type = ether_type.lstrip('!') + ether_type = ether_type_mapping.get(ether_type, ether_type) + output.append(f'ether type {operator} {ether_type}') + for side in ['destination', 'source']: if side in rule_conf: prefix = side[0] diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index b8031eed0..e4f9b14be 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -707,6 +707,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'group', 'ipv6-address-group', 'AGV6', 'address', '2001:db1::1']) self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept']) self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'ipv4']) + self.cli_set(['firewall', 'global-options', 'apply-to-bridged-traffic', 'invalid-connections']) self.cli_set(['firewall', 'bridge', 'name', name, 'default-action', 'accept']) self.cli_set(['firewall', 'bridge', 'name', name, 'default-log']) @@ -731,6 +732,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): self.cli_set(['firewall', 'bridge', 'prerouting', 'filter', 'rule', '1', 'action', 'notrack']) self.cli_set(['firewall', 'bridge', 'prerouting', 'filter', 'rule', '1', 'destination', 'group', 'ipv6-address-group', 'AGV6']) + self.cli_set(['firewall', 'bridge', 'prerouting', 'filter', 'rule', '2', 'ethernet-type', 'arp']) + self.cli_set(['firewall', 'bridge', 'prerouting', 'filter', 'rule', '2', 'action', 'accept']) + self.cli_commit() @@ -750,9 +754,14 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase): ['chain VYOS_INPUT_filter'], ['type filter hook input priority filter; policy accept;'], ['ct state new', 'ip saddr 192.0.2.2', f'iifname "{interface_in}"', 'accept'], + ['chain VYOS_OUTPUT_filter'], + ['type filter hook output priority filter; policy accept;'], + ['ct state invalid', 'udp sport 67', 'udp dport 68', 'accept'], + ['ct state invalid', 'ether type arp', 'accept'], ['chain VYOS_PREROUTING_filter'], ['type filter hook prerouting priority filter; policy accept;'], - ['ip6 daddr @A6_AGV6', 'notrack'] + ['ip6 daddr @A6_AGV6', 'notrack'], + ['ether type arp', 'accept'] ] self.verify_nftables(nftables_search, 'bridge vyos_filter') -- cgit v1.2.3