summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/dhcp_server.py4
-rwxr-xr-xsrc/conf_mode/dhcpv6_server.py4
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py25
-rwxr-xr-xsrc/conf_mode/interfaces-geneve.py22
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py6
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py93
-rwxr-xr-xsrc/conf_mode/salt-minion.py2
-rwxr-xr-xsrc/conf_mode/service_monitoring_telegraf.py22
-rwxr-xr-xsrc/conf_mode/system-ipv6.py4
-rwxr-xr-xsrc/conf_mode/system-login.py21
-rwxr-xr-xsrc/conf_mode/vrf.py8
-rw-r--r--src/etc/cron.d/check-wwan1
-rw-r--r--src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper2
-rwxr-xr-xsrc/op_mode/restart_frr.py2
-rwxr-xr-xsrc/op_mode/vpn_ipsec.py52
-rwxr-xr-xsrc/services/vyos-http-api-server37
-rw-r--r--src/tests/test_util.py16
17 files changed, 246 insertions, 75 deletions
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index a8cef5ebf..d27f8d995 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2021 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -109,7 +109,7 @@ def get_config(config=None):
if not conf.exists(base):
return None
- dhcp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ dhcp = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
# T2665: defaults include lease time per TAG node which need to be added to
# individual subnet definitions
default_values = defaults(base + ['shared-network-name', 'subnet'])
diff --git a/src/conf_mode/dhcpv6_server.py b/src/conf_mode/dhcpv6_server.py
index e6a2e4486..be1e6db1e 100755
--- a/src/conf_mode/dhcpv6_server.py
+++ b/src/conf_mode/dhcpv6_server.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -41,7 +41,7 @@ def get_config(config=None):
if not conf.exists(base):
return None
- dhcpv6 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ dhcpv6 = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
return dhcpv6
def verify(dhcpv6):
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 06366362a..bc3821f61 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-2022 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
@@ -16,6 +16,7 @@
import os
+from netifaces import interfaces
from sys import exit
from vyos.config import Config
@@ -62,10 +63,6 @@ def get_config(config=None):
if conf.exists(base_nameservers):
dns.update({'system_name_server': conf.return_values(base_nameservers)})
- base_nameservers_dhcp = ['system', 'name-servers-dhcp']
- if conf.exists(base_nameservers_dhcp):
- dns.update({'system_name_server_dhcp': conf.return_values(base_nameservers_dhcp)})
-
return dns
def verify(dns):
@@ -87,9 +84,8 @@ def verify(dns):
raise ConfigError(f'No server configured for domain {domain}!')
if 'system' in dns:
- if not ('system_name_server' in dns or 'system_name_server_dhcp' in dns):
- print("Warning: No 'system name-server' or 'system " \
- "name-servers-dhcp' configured")
+ if not 'system_name_server' in dns:
+ print('Warning: No "system name-server" configured')
return None
@@ -142,10 +138,15 @@ def apply(dns):
hc.delete_name_server_tags_recursor(['system'])
# add dhcp nameserver tags for configured interfaces
- if 'system_name_server_dhcp' in dns:
- for interface in dns['system_name_server_dhcp']:
- hc.add_name_server_tags_recursor(['dhcp-' + interface,
- 'dhcpv6-' + interface ])
+ if 'system_name_server' in dns:
+ for interface in dns['system_name_server']:
+ # system_name_server key contains both IP addresses and interface
+ # names (DHCP) to use DNS servers. We need to check if the
+ # value is an interface name - only if this is the case, add the
+ # interface based DNS forwarder.
+ if interface in interfaces():
+ hc.add_name_server_tags_recursor(['dhcp-' + interface,
+ 'dhcpv6-' + interface ])
# hostsd will generate the forward-zones file
# the list and keys() are required as get returns a dict, not list
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py
index 979a5612e..f49d5b304 100755
--- a/src/conf_mode/interfaces-geneve.py
+++ b/src/conf_mode/interfaces-geneve.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019-2020 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -21,6 +21,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configdict import leaf_node_changed
from vyos.configverify import verify_address
from vyos.configverify import verify_mtu_ipv6
from vyos.configverify import verify_bridge_delete
@@ -41,6 +42,14 @@ def get_config(config=None):
conf = Config()
base = ['interfaces', 'geneve']
geneve = get_interface_dict(conf, base)
+
+ # GENEVE interfaces are picky and require recreation if certain parameters
+ # change. But a GENEVE interface should - of course - not be re-created if
+ # it's description or IP address is adjusted. Feels somehow logic doesn't it?
+ for cli_option in ['remote', 'vni']:
+ if leaf_node_changed(conf, cli_option):
+ geneve.update({'rebuild_required': {}})
+
return geneve
def verify(geneve):
@@ -65,11 +74,12 @@ def generate(geneve):
def apply(geneve):
# Check if GENEVE interface already exists
- if geneve['ifname'] in interfaces():
- g = GeneveIf(geneve['ifname'])
- # GENEVE is super picky and the tunnel always needs to be recreated,
- # thus we can simply always delete it first.
- g.remove()
+ if 'rebuild_required' in geneve or 'delete' in geneve:
+ if geneve['ifname'] in interfaces():
+ g = GeneveIf(geneve['ifname'])
+ # GENEVE is super picky and the tunnel always needs to be recreated,
+ # thus we can simply always delete it first.
+ g.remove()
if 'deleted' not in geneve:
# This is a special type of interface which needs additional parameters
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 38ed127ff..f7edddcbf 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -29,7 +29,7 @@ from shutil import rmtree
from vyos.config import Config
from vyos.configdict import get_interface_dict
-from vyos.configdict import leaf_node_changed
+from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
from vyos.configverify import verify_bridge_delete
from vyos.configverify import verify_diffie_hellman_length
@@ -83,8 +83,8 @@ def get_config(config=None):
openvpn = get_interface_dict(conf, base)
if 'deleted' not in openvpn:
- tmp = leaf_node_changed(conf, ['openvpn-option'])
- if tmp: openvpn['restart_required'] = ''
+ if is_node_changed(conf, ['openvpn-option']):
+ openvpn.update({'restart_required': {}})
openvpn['auth_user_pass_file'] = '/run/openvpn/{ifname}.pw'.format(**openvpn)
return openvpn
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index a4b033374..179d1efb4 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -21,6 +21,7 @@ from time import sleep
from vyos.config import Config
from vyos.configdict import get_interface_dict
+from vyos.configdict import is_node_changed
from vyos.configverify import verify_authentication
from vyos.configverify import verify_interface_exists
from vyos.configverify import verify_vrf
@@ -36,7 +37,7 @@ from vyos import airbag
airbag.enable()
service_name = 'ModemManager.service'
-cron_script = '/etc/cron.d/wwan'
+cron_script = '/etc/cron.d/vyos-wwan'
def get_config(config=None):
"""
@@ -50,6 +51,30 @@ def get_config(config=None):
base = ['interfaces', 'wwan']
wwan = get_interface_dict(conf, base)
+ # We should only terminate the WWAN session if critical parameters change.
+ # All parameters that can be changed on-the-fly (like interface description)
+ # should not lead to a reconnect!
+ tmp = is_node_changed(conf, ['address'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = is_node_changed(conf, ['apn'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = is_node_changed(conf, ['disable'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = is_node_changed(conf, ['vrf'])
+ if tmp: wwan.update({'vrf_old': {}})
+
+ tmp = is_node_changed(conf, ['authentication', 'user'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = is_node_changed(conf, ['authentication', 'password'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
+ tmp = is_node_changed(conf, ['ipv6', 'address', 'autoconf'])
+ if tmp: wwan.update({'shutdown_required': {}})
+
# We need to know the amount of other WWAN interfaces as ModemManager needs
# to be started or stopped.
conf.set_level(base)
@@ -57,8 +82,8 @@ def get_config(config=None):
get_first_key=True,
no_tag_node_value_mangle=True)
- # This if-clause is just to be sure - it will always evaluate to true
ifname = wwan['ifname']
+ # This if-clause is just to be sure - it will always evaluate to true
if ifname in wwan['other_interfaces']:
del wwan['other_interfaces'][ifname]
if len(wwan['other_interfaces']) == 0:
@@ -82,13 +107,25 @@ def verify(wwan):
def generate(wwan):
if 'deleted' in wwan:
+ # We are the last WWAN interface - there are no other ones remaining
+ # thus the cronjob needs to go away, too
+ if 'other_interfaces' not in wwan:
+ if os.path.exists(cron_script):
+ os.unlink(cron_script)
return None
+ # Install cron triggered helper script to re-dial WWAN interfaces on
+ # disconnect - e.g. happens during RF signal loss. The script watches every
+ # WWAN interface - so there is only one instance.
if not os.path.exists(cron_script):
write_file(cron_script, '*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py')
+
return None
def apply(wwan):
+ # ModemManager is required to dial WWAN connections - one instance is
+ # required to serve all modems. Activate ModemManager on first invocation
+ # of any WWAN interface.
if not is_systemd_service_active(service_name):
cmd(f'systemctl start {service_name}')
@@ -101,17 +138,19 @@ def apply(wwan):
break
sleep(0.250)
- # we only need the modem number. wwan0 -> 0, wwan1 -> 1
- modem = wwan['ifname'].lstrip('wwan')
- base_cmd = f'mmcli --modem {modem}'
- # Number of bearers is limited - always disconnect first
- cmd(f'{base_cmd} --simple-disconnect')
+ if 'shutdown_required' in wwan:
+ # we only need the modem number. wwan0 -> 0, wwan1 -> 1
+ modem = wwan['ifname'].lstrip('wwan')
+ base_cmd = f'mmcli --modem {modem}'
+ # Number of bearers is limited - always disconnect first
+ cmd(f'{base_cmd} --simple-disconnect')
w = WWANIf(wwan['ifname'])
if 'deleted' in wwan or 'disable' in wwan:
w.remove()
- # There are no other WWAN interfaces - stop the daemon
+ # We are the last WWAN interface - there are no other WWAN interfaces
+ # remaining, thus we can stop ModemManager and free resources.
if 'other_interfaces' not in wwan:
cmd(f'systemctl stop {service_name}')
# Clean CRON helper script which is used for to re-connect when
@@ -121,27 +160,25 @@ def apply(wwan):
return None
- ip_type = 'ipv4'
- slaac = dict_search('ipv6.address.autoconf', wwan) != None
- if 'address' in wwan:
- if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac):
- ip_type = 'ipv4v6'
- elif 'dhcpv6' in wwan['address'] or slaac:
- ip_type = 'ipv6'
- elif 'dhcp' in wwan['address']:
- ip_type = 'ipv4'
-
- options = f'ip-type={ip_type},apn=' + wwan['apn']
- if 'authentication' in wwan:
- options += ',user={user},password={password}'.format(**wwan['authentication'])
-
- command = f'{base_cmd} --simple-connect="{options}"'
- call(command, stdout=DEVNULL)
- w.update(wwan)
+ if 'shutdown_required' in wwan:
+ ip_type = 'ipv4'
+ slaac = dict_search('ipv6.address.autoconf', wwan) != None
+ if 'address' in wwan:
+ if 'dhcp' in wwan['address'] and ('dhcpv6' in wwan['address'] or slaac):
+ ip_type = 'ipv4v6'
+ elif 'dhcpv6' in wwan['address'] or slaac:
+ ip_type = 'ipv6'
+ elif 'dhcp' in wwan['address']:
+ ip_type = 'ipv4'
- if 'other_interfaces' not in wwan and 'deleted' in wwan:
- cmd(f'systemctl start {service_name}')
+ options = f'ip-type={ip_type},apn=' + wwan['apn']
+ if 'authentication' in wwan:
+ options += ',user={user},password={password}'.format(**wwan['authentication'])
+ command = f'{base_cmd} --simple-connect="{options}"'
+ call(command, stdout=DEVNULL)
+
+ w.update(wwan)
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/salt-minion.py b/src/conf_mode/salt-minion.py
index 841bf6a39..5a8528741 100755
--- a/src/conf_mode/salt-minion.py
+++ b/src/conf_mode/salt-minion.py
@@ -39,7 +39,7 @@ default_config_data = {
'user': 'minion',
'group': 'vyattacfg',
'salt_id': gethostname(),
- 'mine_interval': '60',
+ 'interval': '60',
'verify_master_pubkey_sign': 'false',
'master_key': ''
}
diff --git a/src/conf_mode/service_monitoring_telegraf.py b/src/conf_mode/service_monitoring_telegraf.py
index 8a972b9fe..a71565df4 100755
--- a/src/conf_mode/service_monitoring_telegraf.py
+++ b/src/conf_mode/service_monitoring_telegraf.py
@@ -99,6 +99,15 @@ def get_config(config=None):
monitoring['interfaces_ethernet'] = get_interfaces('ethernet', vlan=False)
monitoring['nft_chains'] = get_nft_filter_chains()
+ if 'authentication' in monitoring or \
+ 'url' in monitoring:
+ monitoring['influxdb_configured'] = True
+
+ # Ignore default XML values if config doesn't exists
+ # Delete key from dict
+ if not conf.exists(base + ['prometheus-client']):
+ del monitoring['prometheus_client']
+
return monitoring
def verify(monitoring):
@@ -106,13 +115,14 @@ def verify(monitoring):
if not monitoring:
return None
- if 'authentication' not in monitoring or \
- 'organization' not in monitoring['authentication'] or \
- 'token' not in monitoring['authentication']:
- raise ConfigError(f'Authentication "organization and token" are mandatory!')
+ if 'influxdb_configured' in monitoring:
+ if 'authentication' not in monitoring or \
+ 'organization' not in monitoring['authentication'] or \
+ 'token' not in monitoring['authentication']:
+ raise ConfigError(f'Authentication "organization and token" are mandatory!')
- if 'url' not in monitoring:
- raise ConfigError(f'Monitoring "url" is mandatory!')
+ if 'url' not in monitoring:
+ raise ConfigError(f'Monitoring "url" is mandatory!')
return None
diff --git a/src/conf_mode/system-ipv6.py b/src/conf_mode/system-ipv6.py
index 7fb2dd1cf..e6bcc12ad 100755
--- a/src/conf_mode/system-ipv6.py
+++ b/src/conf_mode/system-ipv6.py
@@ -17,6 +17,7 @@
import os
from sys import exit
+from vyos.base import DeprecationWarning
from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.configdict import leaf_node_changed
@@ -49,6 +50,9 @@ def get_config(config=None):
return opt
def verify(opt):
+ if 'disable' in opt:
+ DeprecationWarning('VyOS 1.4 (sagitta) will remove the CLI command to '\
+ 'disable IPv6 address family in the Linux Kernel!')
pass
def generate(opt):
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 8aa43dd32..aba10689d 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020-2021 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -23,6 +23,7 @@ from pwd import getpwall
from pwd import getpwnam
from spwd import getspnam
from sys import exit
+from time import sleep
from vyos.config import Config
from vyos.configdict import dict_merge
@@ -31,6 +32,7 @@ from vyos.template import render
from vyos.template import is_ipv4
from vyos.util import cmd
from vyos.util import call
+from vyos.util import run
from vyos.util import DEVNULL
from vyos.util import dict_search
from vyos.xml import defaults
@@ -256,13 +258,22 @@ def apply(login):
if 'rm_users' in login:
for user in login['rm_users']:
try:
+ # Disable user to prevent re-login
+ call(f'usermod -s /sbin/nologin {user}')
+
# Logout user if he is still logged in
if user in list(set([tmp[0] for tmp in users()])):
print(f'{user} is logged in, forcing logout!')
- call(f'pkill -HUP -u {user}')
-
- # Remove user account but leave home directory to be safe
- call(f'userdel -r {user}', stderr=DEVNULL)
+ # re-run command until user is logged out
+ while run(f'pkill -HUP -u {user}'):
+ sleep(0.250)
+
+ # Remove user account but leave home directory in place. Re-run
+ # command until user is removed - userdel might return 8 as
+ # SSH sessions are not all yet properly cleaned away, thus we
+ # simply re-run the command until the account wen't away
+ while run(f'userdel --remove {user}', stderr=DEVNULL):
+ sleep(0.250)
except Exception as e:
raise ConfigError(f'Deleting user "{user}" raised exception: {e}')
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index dd1739087..fb2182fff 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -27,6 +27,7 @@ from vyos.util import call
from vyos.util import cmd
from vyos.util import dict_search
from vyos.util import sysctl_write
+from vyos.util import is_ipv6_enabled
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -194,10 +195,11 @@ def apply(vrf):
# set VRF description for e.g. SNMP monitoring
vrf_if = Interface(name)
- # We also should add proper loopback IP addresses to the newly
- # created VRFs for services bound to the loopback address (SNMP, NTP)
+ # We also should add proper loopback IP addresses to the newly added
+ # VRF for services bound to the loopback address (SNMP, NTP)
vrf_if.add_addr('127.0.0.1/8')
- vrf_if.add_addr('::1/128')
+ if is_ipv6_enabled():
+ vrf_if.add_addr('::1/128')
# add VRF description if available
vrf_if.set_alias(config.get('description', ''))
diff --git a/src/etc/cron.d/check-wwan b/src/etc/cron.d/check-wwan
deleted file mode 100644
index 28190776f..000000000
--- a/src/etc/cron.d/check-wwan
+++ /dev/null
@@ -1 +0,0 @@
-*/5 * * * * root /usr/libexec/vyos/vyos-check-wwan.py
diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper
index fc035766b..2bd2d1d22 100644
--- a/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/03-vyos-ipwrapper
@@ -23,7 +23,7 @@ function iptovtysh () {
local VTYSH_GATEWAY=""
local VTYSH_DEV=""
local VTYSH_TAG="210"
- local VTYSH_DISTANCE=""
+ local VTYSH_DISTANCE=$IF_METRIC
# convert default route to 0.0.0.0/0
if [ "$4" == "default" ] ; then
VTYSH_NETADDR="0.0.0.0/0"
diff --git a/src/op_mode/restart_frr.py b/src/op_mode/restart_frr.py
index 109c8dd7b..e5014452f 100755
--- a/src/op_mode/restart_frr.py
+++ b/src/op_mode/restart_frr.py
@@ -138,7 +138,7 @@ def _reload_config(daemon):
# define program arguments
cmd_args_parser = argparse.ArgumentParser(description='restart frr daemons')
cmd_args_parser.add_argument('--action', choices=['restart'], required=True, help='action to frr daemons')
-cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
+cmd_args_parser.add_argument('--daemon', choices=['bfdd', 'bgpd', 'ldpd', 'ospfd', 'ospf6d', 'isisd', 'ripd', 'ripngd', 'staticd', 'zebra'], required=False, nargs='*', help='select single or multiple daemons')
# parse arguments
cmd_args = cmd_args_parser.parse_args()
diff --git a/src/op_mode/vpn_ipsec.py b/src/op_mode/vpn_ipsec.py
new file mode 100755
index 000000000..0c9e83112
--- /dev/null
+++ b/src/op_mode/vpn_ipsec.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022 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 argparse
+
+from vyos.util import call
+
+
+def debug_peer(peer, tunnel):
+ if not peer or peer == "all":
+ debug_commands = [
+ "sudo ipsec statusall",
+ "sudo swanctl -L",
+ "sudo swanctl -l",
+ "sudo swanctl -P",
+ "sudo ip x sa show",
+ "sudo ip x policy show",
+ "sudo ip tunnel show",
+ "sudo ip address",
+ "sudo ip rule show",
+ "sudo ip route | head -100",
+ "sudo ip route show table 220"
+ ]
+ for debug_cmd in debug_commands:
+ print(f'\n### {debug_cmd} ###')
+ call(debug_cmd)
+ return
+
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--action', help='Control action', required=True)
+ parser.add_argument('--name', help='Name for peer reset', required=False)
+ parser.add_argument('--tunnel', help='Specific tunnel of peer', required=False)
+
+ args = parser.parse_args()
+
+ if args.action == "vpn-debug":
+ debug_peer(args.name, args.tunnel)
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index 1000d8b72..ed8cf6a44 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -1,6 +1,6 @@
#!/usr/share/vyos-http-api-tools/bin/python3
#
-# Copyright (C) 2019-2021 VyOS maintainers and contributors
+# Copyright (C) 2019-2022 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
@@ -201,6 +201,19 @@ class ShowModel(ApiModel):
}
}
+class ResetModel(ApiModel):
+ op: StrictStr
+ path: List[StrictStr]
+
+ class Config:
+ schema_extra = {
+ "example": {
+ "key": "id_key",
+ "op": "reset",
+ "path": ["op", "mode", "path"],
+ }
+ }
+
class Success(BaseModel):
success: bool
data: Union[str, bool, Dict]
@@ -372,7 +385,7 @@ class MultipartRoute(APIRoute):
return error(400, "Malformed command \"{0}\": \"value\" field must be a string".format(json.dumps(request.offending_command)))
if request.ERR_PATH_NOT_LIST_OF_STR:
return error(400, "Malformed command \"{0}\": \"path\" field must be a list of strings".format(json.dumps(request.offending_command)))
- if endpoint in ('/retrieve','/generate','/show'):
+ if endpoint in ('/retrieve','/generate','/show','reset'):
if request.ERR_NO_OP or request.ERR_NO_PATH:
return error(400, "Missing required field. \"op\" and \"path\" fields are required")
if endpoint in ('/config-file', '/image'):
@@ -607,6 +620,26 @@ def show_op(data: ShowModel):
return success(res)
+@app.post('/reset')
+def reset_op(data: ResetModel):
+ session = app.state.vyos_session
+
+ op = data.op
+ path = data.path
+
+ try:
+ if op == 'reset':
+ res = session.reset(path)
+ else:
+ return error(400, "\"{0}\" is not a valid operation".format(op))
+ except ConfigSessionError as e:
+ return error(400, str(e))
+ except Exception as e:
+ logger.critical(traceback.format_exc())
+ return error(500, "An internal error occured. Check the logs for details.")
+
+ return success(res)
+
###
# GraphQL integration
###
diff --git a/src/tests/test_util.py b/src/tests/test_util.py
index 22bc085c5..91890262c 100644
--- a/src/tests/test_util.py
+++ b/src/tests/test_util.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2020 VyOS maintainers and contributors
+# Copyright (C) 2020-2022 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
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import TestCase
-from vyos.util import mangle_dict_keys
+from vyos.util import *
class TestVyOSUtil(TestCase):
def test_key_mangline(self):
@@ -24,3 +24,15 @@ class TestVyOSUtil(TestCase):
new_data = mangle_dict_keys(data, '-', '_')
self.assertEqual(new_data, expected_data)
+ def test_sysctl_read(self):
+ self.assertEqual(sysctl_read('net.ipv4.conf.lo.forwarding'), '1')
+
+ def test_ipv6_enabled(self):
+ tmp = sysctl_read('net.ipv6.conf.all.disable_ipv6')
+ # We need to test for both variants as this depends on how the
+ # Docker container is started (with or without IPv6 support) - so we
+ # will simply check both cases to not make the users life miserable.
+ if tmp == '0':
+ self.assertTrue(is_ipv6_enabled())
+ else:
+ self.assertFalse(is_ipv6_enabled())