Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

firewall: T3509: Add support for IPv6 reverse path filtering #2163

Merged
merged 3 commits into from
Aug 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 0 additions & 78 deletions data/templates/firewall/nftables-zone.j2

This file was deleted.

14 changes: 14 additions & 0 deletions data/templates/firewall/nftables.j2
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

{% import 'firewall/nftables-defines.j2' as group_tmpl %}

{% if first_install is not vyos_defined %}
delete table inet vyos_global_rpfilter
{% endif %}
table inet vyos_global_rpfilter {
chain PREROUTING {
type filter hook prerouting priority -300; policy accept;
{% if global_options.source_validation is vyos_defined('loose') %}
fib saddr oif 0 counter drop
{% elif global_options.source_validation is vyos_defined('strict') %}
fib saddr . iif oif 0 counter drop
{% endif %}
}
}

{% if first_install is not vyos_defined %}
delete table ip vyos_filter
{% endif %}
Expand Down
4 changes: 4 additions & 0 deletions data/vyos-firewall-init.conf
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ table ip6 raw {
type filter hook forward priority -300; policy accept;
}

chain vyos_rpfilter {
type filter hook prerouting priority -300; policy accept;
}

chain PREROUTING {
type filter hook prerouting priority -300; policy accept;
counter jump VYOS_CT_PREROUTING_HOOK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <include/interface/ipv6-accept-dad.xml.i>
#include <include/interface/ipv6-address.xml.i>
#include <include/interface/ipv6-dup-addr-detect-transmits.xml.i>
#include <include/interface/source-validation.xml.i>
</children>
</node>
<!-- include end -->
29 changes: 29 additions & 0 deletions python/vyos/ifconfig/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,30 @@ def set_ipv4_source_validation(self, value):
return None
return self.set_interface('rp_filter', value)

def _cleanup_ipv6_source_validation_rules(self, ifname):
commands = []
results = self._cmd(f'nft -a list chain ip6 raw vyos_rpfilter').split("\n")
for line in results:
if f'iifname "{ifname}"' in line:
handle_search = re.search('handle (\d+)', line)
if handle_search:
self._cmd(f'nft delete rule ip6 raw vyos_rpfilter handle {handle_search[1]}')

def set_ipv6_source_validation(self, mode):
"""
Set IPv6 reverse path validation

Example:
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').set_ipv6_source_validation('strict')
"""
self._cleanup_ipv6_source_validation_rules(self.ifname)
nft_prefix = f'nft add rule ip6 raw vyos_rpfilter iifname "{self.ifname}"'
if mode == 'strict':
self._cmd(f"{nft_prefix} fib saddr . iif oif 0 counter drop")
elif mode == 'loose':
self._cmd(f"{nft_prefix} fib saddr oif 0 counter drop")

def set_ipv6_accept_ra(self, accept_ra):
"""
Accept Router Advertisements; autoconfigure using them.
Expand Down Expand Up @@ -1568,6 +1592,11 @@ def update(self, config):
value = tmp if (tmp != None) else '0'
self.set_ipv4_source_validation(value)

# IPv6 source-validation
tmp = dict_search('ipv6.source_validation', config)
value = tmp if (tmp != None) else '0'
self.set_ipv6_source_validation(value)

# MTU - Maximum Transfer Unit has a default value. It must ALWAYS be set
# before mangling any IPv6 option. If MTU is less then 1280 IPv6 will be
# automatically disabled by the kernel. Also MTU must be increased before
Expand Down
12 changes: 12 additions & 0 deletions smoketest/scripts/cli/base_interfaces_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,7 @@ def test_interface_ipv6_options(self):
mss = '1400'
dad_transmits = '10'
accept_dad = '0'
source_validation = 'strict'

for interface in self._interfaces:
path = self._base_path + [interface]
Expand All @@ -863,6 +864,9 @@ def test_interface_ipv6_options(self):
if cli_defined(self._base_path + ['ipv6'], 'disable-forwarding'):
self.cli_set(path + ['ipv6', 'disable-forwarding'])

if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
self.cli_set(path + ['ipv6', 'source-validation', source_validation])

self.cli_commit()

for interface in self._interfaces:
Expand All @@ -886,6 +890,14 @@ def test_interface_ipv6_options(self):
tmp = read_file(f'{proc_base}/forwarding')
self.assertEqual('0', tmp)

if cli_defined(self._base_path + ['ipv6'], 'source-validation'):
base_options = f'iifname "{interface}"'
out = cmd('sudo nft list chain ip6 raw vyos_rpfilter')
for line in out.splitlines():
if line.startswith(base_options):
self.assertIn('fib saddr . iif oif 0', line)
self.assertIn('drop', line)

def test_dhcpv6_client_options(self):
if not self._test_ipv6_dhcpc6:
self.skipTest('not supported')
Expand Down
21 changes: 21 additions & 0 deletions smoketest/scripts/cli/test_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,27 @@ def test_ipv4_state_and_status_rules(self):

self.verify_nftables(nftables_search, 'ip vyos_filter')

def test_source_validation(self):
# Strict
self.cli_set(['firewall', 'global-options', 'source-validation', 'strict'])
self.cli_commit()

nftables_strict_search = [
['fib saddr . iif oif 0', 'drop']
]

self.verify_nftables(nftables_strict_search, 'inet vyos_global_rpfilter')

# Loose
self.cli_set(['firewall', 'global-options', 'source-validation', 'loose'])
self.cli_commit()

nftables_loose_search = [
['fib saddr oif 0', 'drop']
]

self.verify_nftables(nftables_loose_search, 'inet vyos_global_rpfilter')

def test_sysfs(self):
for name, conf in sysfs_config.items():
paths = glob(conf['sysfs'])
Expand Down
1 change: 0 additions & 1 deletion src/conf_mode/firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
'log_martians': {'sysfs': '/proc/sys/net/ipv4/conf/all/log_martians'},
'receive_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_redirects'},
'send_redirects': {'sysfs': '/proc/sys/net/ipv4/conf/*/send_redirects'},
'source_validation': {'sysfs': '/proc/sys/net/ipv4/conf/*/rp_filter', 'disable': '0', 'strict': '1', 'loose': '2'},
'syn_cookies': {'sysfs': '/proc/sys/net/ipv4/tcp_syncookies'},
'twa_hazards_protection': {'sysfs': '/proc/sys/net/ipv4/tcp_rfc1337'}
}
Expand Down
Loading