summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/firewall.py37
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py7
-rwxr-xr-xsrc/conf_mode/nat.py15
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf10
-rwxr-xr-xsrc/init/vyos-router2
-rwxr-xr-xsrc/op_mode/firewall.py114
6 files changed, 103 insertions, 82 deletions
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):
diff --git a/src/conf_mode/nat.py b/src/conf_mode/nat.py
index 08e96f10b..e37a7011c 100755
--- a/src/conf_mode/nat.py
+++ b/src/conf_mode/nat.py
@@ -195,11 +195,10 @@ def verify(nat):
if dict_search('source.rule', nat):
for rule, config in dict_search('source.rule', nat).items():
err_msg = f'Source NAT configuration error in rule {rule}:'
- if 'outbound_interface' not in config:
- raise ConfigError(f'{err_msg} outbound-interface not specified')
- if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces():
- Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
+ if 'outbound_interface' in config:
+ if config['outbound_interface'] not in 'any' and config['outbound_interface'] not in interfaces():
+ Warning(f'rule "{rule}" interface "{config["outbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config):
if 'exclude' not in config and 'backend' not in config['load_balance']:
@@ -218,11 +217,9 @@ def verify(nat):
for rule, config in dict_search('destination.rule', nat).items():
err_msg = f'Destination NAT configuration error in rule {rule}:'
- if 'inbound_interface' not in config:
- raise ConfigError(f'{err_msg}\n' \
- 'inbound-interface not specified')
- elif config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
- Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
+ if 'inbound_interface' in config:
+ if config['inbound_interface'] not in 'any' and config['inbound_interface'] not in interfaces():
+ Warning(f'rule "{rule}" interface "{config["inbound_interface"]}" does not exist on this system')
if not dict_search('translation.address', config) and not dict_search('translation.port', config) and 'redirect' not in config['translation']:
if 'exclude' not in config and 'backend' not in config['load_balance']:
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index ad43390bb..fcdc1b21d 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -98,15 +98,6 @@ net.ipv6.route.skip_notify_on_dev_down=1
# Default value of 20 seems to interfere with larger OSPF and VRRP setups
net.ipv4.igmp_max_memberships = 512
-# Increase default garbage collection thresholds
-net.ipv4.neigh.default.gc_thresh1 = 1024
-net.ipv4.neigh.default.gc_thresh2 = 4096
-net.ipv4.neigh.default.gc_thresh3 = 8192
-#
-net.ipv6.neigh.default.gc_thresh1 = 1024
-net.ipv6.neigh.default.gc_thresh2 = 4096
-net.ipv6.neigh.default.gc_thresh3 = 8192
-
# Enable global RFS (Receive Flow Steering) configuration. RFS is inactive
# until explicitly configured at the interface level
net.core.rps_sock_flow_entries = 32768
@@ -114,3 +105,4 @@ net.core.rps_sock_flow_entries = 32768
# Congestion control
net.core.default_qdisc=fq
net.ipv4.tcp_congestion_control=bbr
+
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 1bbb9c869..9ef1fa335 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -346,6 +346,8 @@ start ()
${vyos_conf_scripts_dir}/system-login.py || log_failure_msg "could not reset system login"
${vyos_conf_scripts_dir}/system-login-banner.py || log_failure_msg "could not reset motd and issue files"
${vyos_conf_scripts_dir}/system-option.py || log_failure_msg "could not reset system option files"
+ ${vyos_conf_scripts_dir}/system-ip.py || log_failure_msg "could not reset system IPv4 options"
+ ${vyos_conf_scripts_dir}/system-ipv6.py || log_failure_msg "could not reset system IPv6 options"
${vyos_conf_scripts_dir}/conntrack.py || log_failure_msg "could not reset conntrack subsystem"
${vyos_conf_scripts_dir}/container.py || log_failure_msg "could not reset container subsystem"
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 11cbd977d..3434707ec 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -24,27 +24,39 @@ from vyos.config import Config
from vyos.utils.process import cmd
from vyos.utils.dict import dict_search_args
-def get_config_firewall(conf, hook=None, priority=None, ipv6=False):
+def get_config_firewall(conf, family=None, hook=None, priority=None):
config_path = ['firewall']
- if hook:
- config_path += ['ipv6' if ipv6 else 'ipv4', hook]
- if priority:
- config_path += [priority]
+ if family:
+ config_path += [family]
+ if hook:
+ config_path += [hook]
+ if priority:
+ config_path += [priority]
firewall = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
return firewall
-def get_nftables_details(hook, priority, ipv6=False):
- suffix = '6' if ipv6 else ''
- aux = 'IPV6_' if ipv6 else ''
- name_prefix = 'NAME6_' if ipv6 else 'NAME_'
+def get_nftables_details(family, hook, priority):
+ if family == 'ipv6':
+ suffix = 'ip6'
+ name_prefix = 'NAME6_'
+ aux='IPV6_'
+ elif family == 'ipv4':
+ suffix = 'ip'
+ name_prefix = 'NAME_'
+ aux=''
+ else:
+ suffix = 'bridge'
+ name_prefix = 'NAME_'
+ aux=''
+
if hook == 'name' or hook == 'ipv6-name':
- command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter {name_prefix}{priority}'
else:
up_hook = hook.upper()
- command = f'sudo nft list chain ip{suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
try:
results = cmd(command)
@@ -68,11 +80,10 @@ def get_nftables_details(hook, priority, ipv6=False):
out[rule_id] = rule
return out
-def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {priority}"\n')
+def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {priority}"\n')
- details = get_nftables_details(hook, priority, ipv6)
+ details = get_nftables_details(family, hook, priority)
rows = []
if 'rule' in firewall_conf:
@@ -103,11 +114,10 @@ def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_
header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
print(tabulate.tabulate(rows, header) + '\n')
-def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {prior}"\n')
+def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {prior}"\n')
- details = get_nftables_details(hook, prior, ipv6)
+ details = get_nftables_details(family, hook, prior)
rows = []
if 'rule' in prior_conf:
@@ -210,8 +220,8 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
row.append('0')
row.append('0')
row.append(prior_conf['default_action'])
- row.append('any') # Source
- row.append('any') # Dest
+ row.append('any') # Source
+ row.append('any') # Dest
row.append('any') # inbound-interface
row.append('any') # outbound-interface
rows.append(row)
@@ -229,15 +239,11 @@ def show_firewall():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name(family, hook, prior, prior_conf)
def show_firewall_family(family):
print(f'Rulesets {family} Information')
@@ -245,31 +251,28 @@ def show_firewall_family(family):
conf = Config()
firewall = get_config_firewall(conf)
- if not firewall:
+ if not firewall or family not in firewall:
return
for hook, hook_conf in firewall[family].items():
for prior, prior_conf in firewall[family][hook].items():
- if family == 'ipv6':
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
- else:
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
+ output_firewall_name(family, hook, prior, prior_conf)
-def show_firewall_name(hook, priority, ipv6=False):
+def show_firewall_name(family, hook, priority):
print('Ruleset Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6)
+ output_firewall_name(family, hook, priority, firewall)
-def show_firewall_rule(hook, priority, rule_id, ipv6=False):
+def show_firewall_rule(family, hook, priority, rule_id):
print('Rule Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6, rule_id)
+ output_firewall_name(family, hook, priority, firewall, rule_id)
def show_firewall_group(name=None):
conf = Config()
@@ -369,6 +372,7 @@ def show_summary():
header = ['Ruleset Hook', 'Ruleset Priority', 'Description', 'References']
v4_out = []
v6_out = []
+ br_out = []
if 'ipv4' in firewall:
for hook, hook_conf in firewall['ipv4'].items():
@@ -382,6 +386,12 @@ def show_summary():
description = prior_conf.get('description', '')
v6_out.append([hook, prior, description])
+ if 'bridge' in firewall:
+ for hook, hook_conf in firewall['bridge'].items():
+ for prior, prior_conf in firewall['bridge'][hook].items():
+ description = prior_conf.get('description', '')
+ br_out.append([hook, prior, description])
+
if v6_out:
print('\nIPv6 Ruleset:\n')
print(tabulate.tabulate(v6_out, header) + '\n')
@@ -390,6 +400,10 @@ def show_summary():
print('\nIPv4 Ruleset:\n')
print(tabulate.tabulate(v4_out, header) + '\n')
+ if br_out:
+ print('\nBridge Ruleset:\n')
+ print(tabulate.tabulate(br_out, header) + '\n')
+
show_firewall_group()
def show_statistics():
@@ -401,15 +415,11 @@ def show_statistics():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name_statistics(family, hook,prior, prior_conf)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@@ -425,9 +435,9 @@ if __name__ == '__main__':
if args.action == 'show':
if not args.rule:
- show_firewall_name(args.hook, args.priority, args.ipv6)
+ show_firewall_name(args.family, args.hook, args.priority)
else:
- show_firewall_rule(args.hook, args.priority, args.rule, args.ipv6)
+ show_firewall_rule(args.family, args.hook, args.priority, args.rule)
elif args.action == 'show_all':
show_firewall()
elif args.action == 'show_family':