Page MenuHomeVyOS Platform

No OneTemporary

Size
24 KB
Referenced Files
None
Subscribers
None
diff --git a/data/config-mode-dependencies/vyos-1x.json b/data/config-mode-dependencies/vyos-1x.json
index ca4ceb58f..9cfbffd96 100644
--- a/data/config-mode-dependencies/vyos-1x.json
+++ b/data/config-mode-dependencies/vyos-1x.json
@@ -1,67 +1,73 @@
{
"system_conntrack": {
"conntrack_sync": ["service_conntrack-sync"],
"vrf": ["vrf"]
},
"firewall": {
"conntrack": ["system_conntrack"],
"group_resync": ["system_conntrack", "nat", "policy_route"]
},
"interfaces_bonding": {
"ethernet": ["interfaces_ethernet"]
},
"interfaces_bridge": {
"vxlan": ["interfaces_vxlan"],
"wlan": ["interfaces_wireless"]
},
"load_balancing_wan": {
"conntrack": ["system_conntrack"]
},
"nat": {
"conntrack": ["system_conntrack"]
},
"nat66": {
"conntrack": ["system_conntrack"]
},
"pki": {
"ethernet": ["interfaces_ethernet"],
"openvpn": ["interfaces_openvpn"],
"https": ["service_https"],
"ipsec": ["vpn_ipsec"],
"openconnect": ["vpn_openconnect"],
"reverse_proxy": ["load-balancing_reverse-proxy"],
"rpki": ["protocols_rpki"],
"sstp": ["vpn_sstp"],
"sstpc": ["interfaces_sstpc"]
},
"vpn_ipsec": {
"nhrp": ["protocols_nhrp"]
},
"vpn_l2tp": {
"ipsec": ["vpn_ipsec"]
},
"qos": {
"bonding": ["interfaces_bonding"],
"bridge": ["interfaces_bridge"],
"dummy": ["interfaces_dummy"],
"ethernet": ["interfaces_ethernet"],
"geneve": ["interfaces_geneve"],
"input": ["interfaces_input"],
"l2tpv3": ["interfaces_l2tpv3"],
"loopback": ["interfaces_loopback"],
"macsec": ["interfaces_macsec"],
"openvpn": ["interfaces_openvpn"],
"pppoe": ["interfaces_pppoe"],
"pseudo-ethernet": ["interfaces_pseudo-ethernet"],
"tunnel": ["interfaces_tunnel"],
"vti": ["interfaces_vti"],
"vxlan": ["interfaces_vxlan"],
"wireguard": ["interfaces_wireguard"],
"wireless": ["interfaces_wireless"],
"wwan": ["interfaces_wwan"]
},
+ "system_ip": {
+ "sysctl": ["system_sysctl"]
+ },
+ "system_ipv6": {
+ "sysctl": ["system_sysctl"]
+ },
"system_option": {
- "ip": ["system_ip"],
- "ipv6": ["system_ipv6"]
+ "ip_ipv6": ["system_ip", "system_ipv6"],
+ "sysctl": ["system_sysctl"]
}
}
diff --git a/smoketest/scripts/cli/test_system_option.py b/smoketest/scripts/cli/test_system_option.py
new file mode 100755
index 000000000..c6f48bfc6
--- /dev/null
+++ b/smoketest/scripts/cli/test_system_option.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2024 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 os
+import unittest
+from base_vyostest_shim import VyOSUnitTestSHIM
+from vyos.utils.file import read_file
+from vyos.utils.process import is_systemd_service_active
+from vyos.utils.system import sysctl_read
+
+base_path = ['system', 'option']
+
+class TestSystemOption(VyOSUnitTestSHIM.TestCase):
+ def tearDown(self):
+ self.cli_delete(base_path)
+ self.cli_commit()
+
+ def test_ctrl_alt_delete(self):
+ self.cli_set(base_path + ['ctrl-alt-delete', 'reboot'])
+ self.cli_commit()
+
+ tmp = os.readlink('/lib/systemd/system/ctrl-alt-del.target')
+ self.assertEqual(tmp, '/lib/systemd/system/reboot.target')
+
+ self.cli_set(base_path + ['ctrl-alt-delete', 'poweroff'])
+ self.cli_commit()
+
+ tmp = os.readlink('/lib/systemd/system/ctrl-alt-del.target')
+ self.assertEqual(tmp, '/lib/systemd/system/poweroff.target')
+
+ self.cli_delete(base_path + ['ctrl-alt-delete', 'poweroff'])
+ self.cli_commit()
+ self.assertFalse(os.path.exists('/lib/systemd/system/ctrl-alt-del.target'))
+
+ def test_reboot_on_panic(self):
+ panic_file = '/proc/sys/kernel/panic'
+
+ tmp = read_file(panic_file)
+ self.assertEqual(tmp, '0')
+
+ self.cli_set(base_path + ['reboot-on-panic'])
+ self.cli_commit()
+
+ tmp = read_file(panic_file)
+ self.assertEqual(tmp, '60')
+
+ def test_performance(self):
+ tuned_service = 'tuned.service'
+
+ self.assertFalse(is_systemd_service_active(tuned_service))
+
+ # T3204 sysctl options must not be overwritten by tuned
+ gc_thresh1 = '131072'
+ gc_thresh2 = '262000'
+ gc_thresh3 = '524000'
+
+ self.cli_set(['system', 'sysctl', 'parameter', 'net.ipv4.neigh.default.gc_thresh1', 'value', gc_thresh1])
+ self.cli_set(['system', 'sysctl', 'parameter', 'net.ipv4.neigh.default.gc_thresh2', 'value', gc_thresh2])
+ self.cli_set(['system', 'sysctl', 'parameter', 'net.ipv4.neigh.default.gc_thresh3', 'value', gc_thresh3])
+
+ self.cli_set(base_path + ['performance', 'throughput'])
+ self.cli_commit()
+
+ self.assertTrue(is_systemd_service_active(tuned_service))
+
+ self.assertEqual(sysctl_read('net.ipv4.neigh.default.gc_thresh1'), gc_thresh1)
+ self.assertEqual(sysctl_read('net.ipv4.neigh.default.gc_thresh2'), gc_thresh2)
+ self.assertEqual(sysctl_read('net.ipv4.neigh.default.gc_thresh3'), gc_thresh3)
+
+if __name__ == '__main__':
+ unittest.main(verbosity=2, failfast=True)
diff --git a/src/conf_mode/system_ip.py b/src/conf_mode/system_ip.py
index 2a0bda91a..c8a91fd2f 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -1,138 +1,146 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2024 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/>.
from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_write
-
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
base = ['system', 'ip']
opt = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True,
with_recursive_defaults=True)
# When working with FRR we need to know the corresponding address-family
opt['afi'] = 'ip'
# We also need the route-map information from the config
#
# XXX: one MUST always call this without the key_mangling() option! See
# vyos.configverify.verify_common_route_maps() for more information.
tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
get_first_key=True)}}
# Merge policy dict into "regular" config dict
opt = dict_merge(tmp, opt)
+
+ # If IPv4 ARP table size is set here and also manually in sysctl, the more
+ # fine grained value from sysctl must win
+ set_dependents('sysctl', conf)
+
return opt
def verify(opt):
if 'protocol' in opt:
for protocol, protocol_options in opt['protocol'].items():
if 'route_map' in protocol_options:
verify_route_map(protocol_options['route_map'], opt)
return
def generate(opt):
opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
return
def apply(opt):
# Apply ARP threshold values
# table_size has a default value - thus the key always exists
size = int(dict_search('arp.table_size', opt))
# Amount upon reaching which the records begin to be cleared immediately
sysctl_write('net.ipv4.neigh.default.gc_thresh3', size)
# Amount after which the records begin to be cleaned after 5 seconds
sysctl_write('net.ipv4.neigh.default.gc_thresh2', size // 2)
# Minimum number of stored records is indicated which is not cleared
sysctl_write('net.ipv4.neigh.default.gc_thresh1', size // 8)
# enable/disable IPv4 forwarding
tmp = dict_search('disable_forwarding', opt)
value = '0' if (tmp != None) else '1'
write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
# configure multipath
tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
value = '1' if (tmp != None) else '0'
sysctl_write('net.ipv4.fib_multipath_use_neigh', value)
tmp = dict_search('multipath.layer4_hashing', opt)
value = '1' if (tmp != None) else '0'
sysctl_write('net.ipv4.fib_multipath_hash_policy', value)
# configure TCP options (defaults as of Linux 6.4)
tmp = dict_search('tcp.mss.probing', opt)
if tmp is None:
value = 0
elif tmp == 'on-icmp-black-hole':
value = 1
elif tmp == 'force':
value = 2
else:
# Shouldn't happen
raise ValueError("TCP MSS probing is neither 'on-icmp-black-hole' nor 'force'!")
sysctl_write('net.ipv4.tcp_mtu_probing', value)
tmp = dict_search('tcp.mss.base', opt)
value = '1024' if (tmp is None) else tmp
sysctl_write('net.ipv4.tcp_base_mss', value)
tmp = dict_search('tcp.mss.floor', opt)
value = '48' if (tmp is None) else tmp
sysctl_write('net.ipv4.tcp_mtu_probe_floor', value)
# During startup of vyos-router that brings up FRR, the service is not yet
# running when this script is called first. Skip this part and wait for initial
# commit of the configuration to trigger this statement
if is_systemd_service_active('frr.service'):
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
# The route-map used for the FIB (zebra) is part of the zebra daemon
frr_cfg.load_configuration(zebra_daemon)
frr_cfg.modify_section(r'no ip nht resolve-via-default')
frr_cfg.modify_section(r'ip protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
if 'frr_zebra_config' in opt:
frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
frr_cfg.commit_configuration(zebra_daemon)
+ call_dependents()
+
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)
diff --git a/src/conf_mode/system_ipv6.py b/src/conf_mode/system_ipv6.py
index 00d440e35..a2442d009 100755
--- a/src/conf_mode/system_ipv6.py
+++ b/src/conf_mode/system_ipv6.py
@@ -1,121 +1,130 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2023 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 os
from sys import exit
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configverify import verify_route_map
from vyos.template import render_to_string
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.process import is_systemd_service_active
from vyos.utils.system import sysctl_write
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos import ConfigError
from vyos import frr
from vyos import airbag
airbag.enable()
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
base = ['system', 'ipv6']
opt = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True,
with_recursive_defaults=True)
# When working with FRR we need to know the corresponding address-family
opt['afi'] = 'ipv6'
# We also need the route-map information from the config
#
# XXX: one MUST always call this without the key_mangling() option! See
# vyos.configverify.verify_common_route_maps() for more information.
tmp = {'policy' : {'route-map' : conf.get_config_dict(['policy', 'route-map'],
get_first_key=True)}}
# Merge policy dict into "regular" config dict
opt = dict_merge(tmp, opt)
+
+ # If IPv6 neighbor table size is set here and also manually in sysctl, the more
+ # fine grained value from sysctl must win
+ set_dependents('sysctl', conf)
+
return opt
def verify(opt):
if 'protocol' in opt:
for protocol, protocol_options in opt['protocol'].items():
if 'route_map' in protocol_options:
verify_route_map(protocol_options['route_map'], opt)
return
def generate(opt):
opt['frr_zebra_config'] = render_to_string('frr/zebra.route-map.frr.j2', opt)
return
def apply(opt):
# configure multipath
tmp = dict_search('multipath.layer4_hashing', opt)
value = '1' if (tmp != None) else '0'
sysctl_write('net.ipv6.fib_multipath_hash_policy', value)
# Apply ND threshold values
# table_size has a default value - thus the key always exists
size = int(dict_search('neighbor.table_size', opt))
# Amount upon reaching which the records begin to be cleared immediately
sysctl_write('net.ipv6.neigh.default.gc_thresh3', size)
# Amount after which the records begin to be cleaned after 5 seconds
sysctl_write('net.ipv6.neigh.default.gc_thresh2', size // 2)
# Minimum number of stored records is indicated which is not cleared
sysctl_write('net.ipv6.neigh.default.gc_thresh1', size // 8)
# enable/disable IPv6 forwarding
tmp = dict_search('disable_forwarding', opt)
value = '0' if (tmp != None) else '1'
write_file('/proc/sys/net/ipv6/conf/all/forwarding', value)
# configure IPv6 strict-dad
tmp = dict_search('strict_dad', opt)
value = '2' if (tmp != None) else '1'
for root, dirs, files in os.walk('/proc/sys/net/ipv6/conf'):
for name in files:
if name == 'accept_dad':
write_file(os.path.join(root, name), value)
# During startup of vyos-router that brings up FRR, the service is not yet
# running when this script is called first. Skip this part and wait for initial
# commit of the configuration to trigger this statement
if is_systemd_service_active('frr.service'):
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
frr_cfg = frr.FRRConfig()
# The route-map used for the FIB (zebra) is part of the zebra daemon
frr_cfg.load_configuration(zebra_daemon)
frr_cfg.modify_section(r'no ipv6 nht resolve-via-default')
frr_cfg.modify_section(r'ipv6 protocol \w+ route-map [-a-zA-Z0-9.]+', stop_pattern='(\s|!)')
if 'frr_zebra_config' in opt:
frr_cfg.add_before(frr.default_add_before, opt['frr_zebra_config'])
frr_cfg.commit_configuration(zebra_daemon)
+ call_dependents()
+
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)
diff --git a/src/conf_mode/system_option.py b/src/conf_mode/system_option.py
index 9fd7a3195..d1647e3a1 100755
--- a/src/conf_mode/system_option.py
+++ b/src/conf_mode/system_option.py
@@ -1,194 +1,195 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2024 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 os
from sys import exit
from time import sleep
from vyos.config import Config
from vyos.configverify import verify_source_interface
from vyos.configverify import verify_interface_exists
from vyos.system import grub_util
from vyos.template import render
from vyos.utils.dict import dict_search
from vyos.utils.file import write_file
from vyos.utils.kernel import check_kmod
from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_running
from vyos.utils.network import is_addr_assigned
from vyos.utils.network import is_intf_addr_assigned
-from vyos.configdep import set_dependents, call_dependents
+from vyos.configdep import set_dependents
+from vyos.configdep import call_dependents
from vyos import ConfigError
from vyos import airbag
airbag.enable()
curlrc_config = r'/etc/curlrc'
ssh_config = r'/etc/ssh/ssh_config.d/91-vyos-ssh-client-options.conf'
systemd_action_file = '/lib/systemd/system/ctrl-alt-del.target'
usb_autosuspend = r'/etc/udev/rules.d/40-usb-autosuspend.rules'
kernel_dynamic_debug = r'/sys/kernel/debug/dynamic_debug/control'
time_format_to_locale = {
'12-hour': 'en_US.UTF-8',
'24-hour': 'en_GB.UTF-8'
}
def get_config(config=None):
if config:
conf = config
else:
conf = Config()
base = ['system', 'option']
options = conf.get_config_dict(base, key_mangling=('-', '_'),
get_first_key=True,
with_recursive_defaults=True)
if 'performance' in options:
- # Update IPv4 and IPv6 options after TuneD reapplies
- # sysctl from config files
- for protocol in ['ip', 'ipv6']:
- set_dependents(protocol, conf)
+ # Update IPv4/IPv6 and sysctl options after tuned applied it's settings
+ set_dependents('ip_ipv6', conf)
+ set_dependents('sysctl', conf)
return options
def verify(options):
if 'http_client' in options:
config = options['http_client']
if 'source_interface' in config:
verify_interface_exists(options, config['source_interface'])
if {'source_address', 'source_interface'} <= set(config):
raise ConfigError('Can not define both HTTP source-interface and source-address')
if 'source_address' in config:
if not is_addr_assigned(config['source_address']):
raise ConfigError('No interface with give address specified!')
if 'ssh_client' in options:
config = options['ssh_client']
if 'source_address' in config:
address = config['source_address']
if not is_addr_assigned(config['source_address']):
raise ConfigError('No interface with address "{address}" configured!')
if 'source_interface' in config:
verify_source_interface(config)
if 'source_address' in config:
address = config['source_address']
interface = config['source_interface']
if not is_intf_addr_assigned(interface, address):
raise ConfigError(f'Address "{address}" not assigned on interface "{interface}"!')
return None
def generate(options):
render(curlrc_config, 'system/curlrc.j2', options)
render(ssh_config, 'system/ssh_config.j2', options)
render(usb_autosuspend, 'system/40_usb_autosuspend.j2', options)
cmdline_options = []
if 'kernel' in options:
if 'disable_mitigations' in options['kernel']:
cmdline_options.append('mitigations=off')
if 'disable_power_saving' in options['kernel']:
cmdline_options.append('intel_idle.max_cstate=0 processor.max_cstate=1')
grub_util.update_kernel_cmdline_options(' '.join(cmdline_options))
return None
def apply(options):
# System bootup beep
+ beep_service = 'vyos-beep.service'
if 'startup_beep' in options:
- cmd('systemctl enable vyos-beep.service')
+ cmd(f'systemctl enable {beep_service}')
else:
- cmd('systemctl disable vyos-beep.service')
+ cmd(f'systemctl disable {beep_service}')
# Ctrl-Alt-Delete action
if os.path.exists(systemd_action_file):
os.unlink(systemd_action_file)
if 'ctrl_alt_delete' in options:
if options['ctrl_alt_delete'] == 'reboot':
os.symlink('/lib/systemd/system/reboot.target', systemd_action_file)
elif options['ctrl_alt_delete'] == 'poweroff':
os.symlink('/lib/systemd/system/poweroff.target', systemd_action_file)
# Configure HTTP client
if 'http_client' not in options:
if os.path.exists(curlrc_config):
os.unlink(curlrc_config)
# Configure SSH client
if 'ssh_client' not in options:
if os.path.exists(ssh_config):
os.unlink(ssh_config)
# Reboot system on kernel panic
timeout = '0'
if 'reboot_on_panic' in options:
timeout = '60'
with open('/proc/sys/kernel/panic', 'w') as f:
f.write(timeout)
# tuned - performance tuning
if 'performance' in options:
cmd('systemctl restart tuned.service')
# wait until daemon has started before sending configuration
while (not is_systemd_service_running('tuned.service')):
sleep(0.250)
cmd('tuned-adm profile network-{performance}'.format(**options))
else:
cmd('systemctl stop tuned.service')
call_dependents()
# Keyboard layout - there will be always the default key inside the dict
# but we check for key existence anyway
if 'keyboard_layout' in options:
cmd('loadkeys {keyboard_layout}'.format(**options))
# Enable/diable root-partition-auto-resize SystemD service
if 'root_partition_auto_resize' in options:
cmd('systemctl enable root-partition-auto-resize.service')
else:
cmd('systemctl disable root-partition-auto-resize.service')
# Time format 12|24-hour
if 'time_format' in options:
time_format = time_format_to_locale.get(options['time_format'])
cmd(f'localectl set-locale LC_TIME={time_format}')
# Reload UDEV, required for USB auto suspend
cmd('udevadm control --reload-rules')
# Enable/disable dynamic debugging for kernel modules
modules = ['wireguard']
modules_enabled = dict_search('kernel.debug', options) or []
for module in modules:
if module in modules_enabled:
check_kmod(module)
write_file(kernel_dynamic_debug, f'module {module} +p')
else:
write_file(kernel_dynamic_debug, f'module {module} -p')
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 6, 9:55 AM (7 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4654092
Default Alt Text
(24 KB)

Event Timeline