summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/container.py31
-rwxr-xr-xsrc/conf_mode/dhcp_server.py2
-rwxr-xr-xsrc/conf_mode/http-api.py7
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py2
-rwxr-xr-xsrc/conf_mode/interfaces-wwan.py2
-rwxr-xr-xsrc/conf_mode/snmp.py5
-rwxr-xr-xsrc/conf_mode/system-login.py40
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py22
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py23
-rwxr-xr-xsrc/etc/commit/post-hooks.d/00vyos-sync2
-rw-r--r--src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks5
-rwxr-xr-xsrc/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks5
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf2
-rw-r--r--src/etc/systemd/system/ddclient.service.d/override.conf2
-rwxr-xr-xsrc/migration-scripts/interfaces/0-to-12
-rwxr-xr-xsrc/migration-scripts/interfaces/1-to-24
-rwxr-xr-xsrc/migration-scripts/interfaces/16-to-172
-rwxr-xr-xsrc/migration-scripts/interfaces/2-to-32
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-212
-rwxr-xr-xsrc/migration-scripts/interfaces/26-to-2749
-rwxr-xr-xsrc/migration-scripts/interfaces/27-to-2855
-rwxr-xr-xsrc/migration-scripts/interfaces/4-to-52
-rwxr-xr-xsrc/migration-scripts/ipsec/11-to-1253
-rwxr-xr-xsrc/migration-scripts/qos/1-to-222
-rwxr-xr-xsrc/migration-scripts/snmp/0-to-16
-rwxr-xr-xsrc/op_mode/accelppp.py38
-rwxr-xr-xsrc/op_mode/generate_public_key_command.py59
-rwxr-xr-xsrc/op_mode/ipsec.py125
-rwxr-xr-xsrc/op_mode/nhrp.py101
-rwxr-xr-xsrc/op_mode/openvpn.py10
-rwxr-xr-xsrc/op_mode/show_openconnect_otp.py2
-rw-r--r--src/services/api/graphql/generate/config_session_function.py6
-rw-r--r--src/services/api/graphql/graphql/auth_token_mutation.py14
-rw-r--r--src/services/api/graphql/libs/token_auth.py7
-rw-r--r--src/services/api/graphql/session/session.py35
-rwxr-xr-xsrc/services/vyos-http-api-server4
-rw-r--r--src/tests/test_configverify.py5
-rwxr-xr-xsrc/validators/timezone4
-rw-r--r--src/xdp/common/common_libbpf.c15
-rw-r--r--src/xdp/common/common_user_bpf_xdp.c47
-rw-r--r--src/xdp/common/xdp_stats_kern.h12
-rw-r--r--src/xdp/xdp_prog_kern.c30
42 files changed, 634 insertions, 229 deletions
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 08861053d..4f93c93a1 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2021-2022 VyOS maintainers and contributors
+# Copyright (C) 2021-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
@@ -84,16 +84,16 @@ def get_config(config=None):
# tagNodes in place, it is better to blend in the defaults manually.
if 'port' in container['name'][name]:
for port in container['name'][name]['port']:
- default_values = defaults(base + ['name', 'port'])
+ default_values_port = defaults(base + ['name', 'port'])
container['name'][name]['port'][port] = dict_merge(
- default_values, container['name'][name]['port'][port])
+ default_values_port, container['name'][name]['port'][port])
# XXX: T2665: we can not safely rely on the defaults() when there are
# tagNodes in place, it is better to blend in the defaults manually.
if 'volume' in container['name'][name]:
for volume in container['name'][name]['volume']:
- default_values = defaults(base + ['name', 'volume'])
+ default_values_volume = defaults(base + ['name', 'volume'])
container['name'][name]['volume'][volume] = dict_merge(
- default_values, container['name'][name]['volume'][volume])
+ default_values_volume, container['name'][name]['volume'][volume])
# Delete container network, delete containers
tmp = node_changed(conf, base + ['network'])
@@ -279,8 +279,22 @@ def generate_run_arguments(name, container_config):
f'--memory {memory}m --shm-size {shared_memory}m --memory-swap 0 --restart {restart} ' \
f'--name {name} {device} {port} {volume} {env_opt}'
+ entrypoint = ''
+ if 'entrypoint' in container_config:
+ # it needs to be json-formatted with single quote on the outside
+ entrypoint = json_write(container_config['entrypoint'].split()).replace('"', """)
+ entrypoint = f'--entrypoint '{entrypoint}''
+
+ command = ''
+ if 'command' in container_config:
+ command = container_config['command'].strip()
+
+ command_arguments = ''
+ if 'arguments' in container_config:
+ command_arguments = container_config['arguments'].strip()
+
if 'allow_host_networks' in container_config:
- return f'{container_base_cmd} --net host {image}'
+ return f'{container_base_cmd} --net host {entrypoint} {image} {command} {command_arguments}'.strip()
ip_param = ''
networks = ",".join(container_config['network'])
@@ -289,7 +303,7 @@ def generate_run_arguments(name, container_config):
address = container_config['network'][network]['address']
ip_param = f'--ip {address}'
- return f'{container_base_cmd} --net {networks} {ip_param} {image}'
+ return f'{container_base_cmd} --net {networks} {ip_param} {entrypoint} {image} {command} {command_arguments}'.strip()
def generate(container):
# bail out early - looks like removal from running config
@@ -341,7 +355,8 @@ def generate(container):
file_path = os.path.join(systemd_unit_path, f'vyos-container-{name}.service')
run_args = generate_run_arguments(name, container_config)
- render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args})
+ render(file_path, 'container/systemd-unit.j2', {'name': name, 'run_args': run_args,},
+ formater=lambda _: _.replace(""", '"').replace("'", "'"))
return None
diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py
index 52b682d6d..39c87478f 100755
--- a/src/conf_mode/dhcp_server.py
+++ b/src/conf_mode/dhcp_server.py
@@ -283,7 +283,7 @@ def generate(dhcp):
if not dhcp or 'disable' in dhcp:
return None
- # Please see: https://phabricator.vyos.net/T1129 for quoting of the raw
+ # Please see: https://vyos.dev/T1129 for quoting of the raw
# parameters we can pass to ISC DHCPd
tmp_file = '/tmp/dhcpd.conf'
render(tmp_file, 'dhcp-server/dhcpd.conf.j2', dhcp,
diff --git a/src/conf_mode/http-api.py b/src/conf_mode/http-api.py
index 6328294c1..7e801eb26 100755
--- a/src/conf_mode/http-api.py
+++ b/src/conf_mode/http-api.py
@@ -79,9 +79,10 @@ def get_config(config=None):
# http-api.conf format for api_keys:
if 'keys' in api_dict:
api_dict['api_keys'] = []
- for el in list(api_dict['keys']['id']):
- key = api_dict['keys']['id'][el]['key']
- api_dict['api_keys'].append({'id': el, 'key': key})
+ for el in list(api_dict['keys'].get('id', {})):
+ key = api_dict['keys']['id'][el].get('key', '')
+ if key:
+ api_dict['api_keys'].append({'id': el, 'key': key})
del api_dict['keys']
# Do we run inside a VRF context?
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 8155f36c2..13d84a6fe 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -645,7 +645,7 @@ def generate(openvpn):
user=user, group=group)
# we need to support quoting of raw parameters from OpenVPN CLI
- # see https://phabricator.vyos.net/T1632
+ # see https://vyos.dev/T1632
render(cfg_file.format(**openvpn), 'openvpn/server.conf.j2', openvpn,
formater=lambda _: _.replace(""", '"'), user=user, group=group)
diff --git a/src/conf_mode/interfaces-wwan.py b/src/conf_mode/interfaces-wwan.py
index a14a992ae..9ca495476 100755
--- a/src/conf_mode/interfaces-wwan.py
+++ b/src/conf_mode/interfaces-wwan.py
@@ -171,7 +171,7 @@ def apply(wwan):
options = f'ip-type={ip_type},apn=' + wwan['apn']
if 'authentication' in wwan:
- options += ',user={user},password={password}'.format(**wwan['authentication'])
+ options += ',user={username},password={password}'.format(**wwan['authentication'])
command = f'{base_cmd} --simple-connect="{options}"'
call(command, stdout=DEVNULL)
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index ab2ccf99e..9b7c04eb0 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -92,7 +92,7 @@ def get_config(config=None):
# Always listen on localhost if an explicit address has been configured
# This is a safety measure to not end up with invalid listen addresses
- # that are not configured on this system. See https://phabricator.vyos.net/T850
+ # that are not configured on this system. See https://vyos.dev/T850
if '127.0.0.1' not in snmp['listen_address']:
tmp = {'127.0.0.1': {'port': '161'}}
snmp['listen_address'] = dict_merge(tmp, snmp['listen_address'])
@@ -103,6 +103,9 @@ def get_config(config=None):
if 'community' in snmp:
default_values = defaults(base + ['community'])
+ if 'network' in default_values:
+ # convert multiple default networks to list
+ default_values['network'] = default_values['network'].split()
for community in snmp['community']:
snmp['community'][community] = dict_merge(
default_values, snmp['community'][community])
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index e26b81e3d..0a4a88bf8 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -16,18 +16,17 @@
import os
-from crypt import crypt
-from crypt import METHOD_SHA512
+from passlib.hosts import linux_context
from psutil import users
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
from vyos.configverify import verify_vrf
+from vyos.defaults import directories
from vyos.template import render
from vyos.template import is_ipv4
from vyos.util import cmd
@@ -43,6 +42,11 @@ airbag.enable()
autologout_file = "/etc/profile.d/autologout.sh"
radius_config_file = "/etc/pam_radius_auth.conf"
+# LOGIN_TIMEOUT from /etc/loign.defs minus 10 sec
+MAX_RADIUS_TIMEOUT: int = 50
+# MAX_RADIUS_TIMEOUT divided by 2 sec (minimum recomended timeout)
+MAX_RADIUS_COUNT: int = 25
+
def get_local_users():
"""Return list of dynamically allocated users (see Debian Policy Manual)"""
local_users = []
@@ -54,6 +58,13 @@ def get_local_users():
return local_users
+def get_shadow_password(username):
+ with open('/etc/shadow') as f:
+ for user in f.readlines():
+ items = user.split(":")
+ if username == items[0]:
+ return items[1]
+ return None
def get_config(config=None):
if config:
@@ -118,18 +129,27 @@ def verify(login):
if 'radius' in login:
if 'server' not in login['radius']:
raise ConfigError('No RADIUS server defined!')
-
+ sum_timeout: int = 0
+ radius_servers_count: int = 0
fail = True
for server, server_config in dict_search('radius.server', login).items():
if 'key' not in server_config:
raise ConfigError(f'RADIUS server "{server}" requires key!')
-
- if 'disabled' not in server_config:
+ if 'disable' not in server_config:
+ sum_timeout += int(server_config['timeout'])
+ radius_servers_count += 1
fail = False
- continue
+
if fail:
raise ConfigError('All RADIUS servers are disabled')
+ if radius_servers_count > MAX_RADIUS_COUNT:
+ raise ConfigError('Number of RADIUS servers more than 25 ')
+
+ if sum_timeout > MAX_RADIUS_TIMEOUT:
+ raise ConfigError('Sum of RADIUS servers timeouts '
+ 'has to be less or eq 50 sec')
+
verify_vrf(login['radius'])
if 'source_address' in login['radius']:
@@ -153,13 +173,13 @@ def generate(login):
for user, user_config in login['user'].items():
tmp = dict_search('authentication.plaintext_password', user_config)
if tmp:
- encrypted_password = crypt(tmp, METHOD_SHA512)
+ encrypted_password = linux_context.hash(tmp)
login['user'][user]['authentication']['encrypted_password'] = encrypted_password
del login['user'][user]['authentication']['plaintext_password']
# remove old plaintext password and set new encrypted password
env = os.environ.copy()
- env['vyos_libexec_dir'] = '/usr/libexec/vyos'
+ env['vyos_libexec_dir'] = directories['base']
# Set default commands for re-adding user with encrypted password
del_user_plain = f"system login user '{user}' authentication plaintext-password"
@@ -186,7 +206,7 @@ def generate(login):
call(f"/opt/vyatta/sbin/my_set {add_user_encrypt}", env=env)
else:
try:
- if getspnam(user).sp_pwdp == dict_search('authentication.encrypted_password', user_config):
+ if get_shadow_password(user) == dict_search('authentication.encrypted_password', user_config):
# If the current encrypted bassword matches the encrypted password
# from the config - do not update it. This will remove the encrypted
# value from the system logs.
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index ce4f13d27..d207c63df 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -53,8 +53,6 @@ dhcp_wait_attempts = 2
dhcp_wait_sleep = 1
swanctl_dir = '/etc/swanctl'
-ipsec_conf = '/etc/ipsec.conf'
-ipsec_secrets = '/etc/ipsec.secrets'
charon_conf = '/etc/strongswan.d/charon.conf'
charon_dhcp_conf = '/etc/strongswan.d/charon/dhcp.conf'
charon_radius_conf = '/etc/strongswan.d/charon/eap-radius.conf'
@@ -542,8 +540,7 @@ def generate(ipsec):
cleanup_pki_files()
if not ipsec:
- for config_file in [ipsec_conf, ipsec_secrets, charon_dhcp_conf,
- charon_radius_conf, interface_conf, swanctl_conf]:
+ for config_file in [charon_dhcp_conf, charon_radius_conf, interface_conf, swanctl_conf]:
if os.path.isfile(config_file):
os.unlink(config_file)
render(charon_conf, 'ipsec/charon.j2', {'install_routes': default_install_routes})
@@ -618,8 +615,6 @@ def generate(ipsec):
if id:
ipsec['authentication']['psk'][psk]['id'].append(id)
- render(ipsec_conf, 'ipsec/ipsec.conf.j2', ipsec)
- render(ipsec_secrets, 'ipsec/ipsec.secrets.j2', ipsec)
render(charon_conf, 'ipsec/charon.j2', ipsec)
render(charon_dhcp_conf, 'ipsec/charon/dhcp.conf.j2', ipsec)
render(charon_radius_conf, 'ipsec/charon/eap-radius.conf.j2', ipsec)
@@ -634,25 +629,12 @@ def resync_nhrp(ipsec):
if tmp > 0:
print('ERROR: failed to reapply NHRP settings!')
-def wait_for_vici_socket(timeout=5, sleep_interval=0.1):
- start_time = time()
- test_command = f'sudo socat -u OPEN:/dev/null UNIX-CONNECT:{vici_socket}'
- while True:
- if (start_time + timeout) < time():
- return None
- result = run(test_command)
- if result == 0:
- return True
- sleep(sleep_interval)
-
def apply(ipsec):
- systemd_service = 'strongswan-starter.service'
+ systemd_service = 'strongswan.service'
if not ipsec:
call(f'systemctl stop {systemd_service}')
else:
call(f'systemctl reload-or-restart {systemd_service}')
- if wait_for_vici_socket():
- call('sudo swanctl -q')
resync_nhrp(ipsec)
diff --git a/src/conf_mode/vpn_openconnect.py b/src/conf_mode/vpn_openconnect.py
index 63ffe2a41..68da70d7d 100755
--- a/src/conf_mode/vpn_openconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2022 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -47,9 +47,9 @@ def get_hash(password):
return crypt(password, mksalt(METHOD_SHA512))
-def T2665_default_dict_cleanup(origin: dict, default_values: dict) -> dict:
+def _default_dict_cleanup(origin: dict, default_values: dict) -> dict:
"""
- https://phabricator.vyos.net/T2665
+ https://vyos.dev/T2665
Clear unnecessary key values in merged config by dict_merge function
:param origin: config
:type origin: dict
@@ -63,7 +63,7 @@ def T2665_default_dict_cleanup(origin: dict, default_values: dict) -> dict:
del origin['authentication']['local_users']['username']['otp']
if not origin["authentication"]["local_users"]["username"]:
raise ConfigError(
- 'Openconnect mode local required at least one user')
+ 'Openconnect authentication mode local requires at least one user')
default_ocserv_usr_values = \
default_values['authentication']['local_users']['username']['otp']
for user, params in origin['authentication']['local_users'][
@@ -82,7 +82,7 @@ def T2665_default_dict_cleanup(origin: dict, default_values: dict) -> dict:
del origin['authentication']['radius']['server']['port']
if not origin["authentication"]['radius']['server']:
raise ConfigError(
- 'Openconnect authentication mode radius required at least one radius server')
+ 'Openconnect authentication mode radius requires at least one RADIUS server')
default_values_radius_port = \
default_values['authentication']['radius']['server']['port']
for server, params in origin['authentication']['radius'][
@@ -95,7 +95,7 @@ def T2665_default_dict_cleanup(origin: dict, default_values: dict) -> dict:
del origin['accounting']['radius']['server']['port']
if not origin["accounting"]['radius']['server']:
raise ConfigError(
- 'Openconnect accounting mode radius required at least one radius server')
+ 'Openconnect accounting mode radius requires at least one RADIUS server')
default_values_radius_port = \
default_values['accounting']['radius']['server']['port']
for server, params in origin['accounting']['radius'][
@@ -105,8 +105,11 @@ def T2665_default_dict_cleanup(origin: dict, default_values: dict) -> dict:
return origin
-def get_config():
- conf = Config()
+def get_config(config=None):
+ if config:
+ conf = config
+ else:
+ conf = Config()
base = ['vpn', 'openconnect']
if not conf.exists(base):
return None
@@ -116,8 +119,8 @@ def get_config():
# options which we need to update into the dictionary retrived.
default_values = defaults(base)
ocserv = dict_merge(default_values, ocserv)
- # workaround a "know limitation" - https://phabricator.vyos.net/T2665
- ocserv = T2665_default_dict_cleanup(ocserv, default_values)
+ # workaround a "know limitation" - https://vyos.dev/T2665
+ ocserv = _default_dict_cleanup(ocserv, default_values)
if ocserv:
ocserv['pki'] = conf.get_config_dict(['pki'], key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
diff --git a/src/etc/commit/post-hooks.d/00vyos-sync b/src/etc/commit/post-hooks.d/00vyos-sync
index e3bde3abb..8ec732df0 100755
--- a/src/etc/commit/post-hooks.d/00vyos-sync
+++ b/src/etc/commit/post-hooks.d/00vyos-sync
@@ -2,6 +2,6 @@
# When power is lost right after a commit modified files, the
# system can be corrupted and e.g. login is no longer possible.
# Always sync files to the backend storage after a commit.
-# https://phabricator.vyos.net/T4975
+# https://vyos.dev/T4975
sync
diff --git a/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks b/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks
new file mode 100644
index 000000000..b4b4d516d
--- /dev/null
+++ b/src/etc/dhcp/dhclient-enter-hooks.d/99-run-user-hooks
@@ -0,0 +1,5 @@
+#!/bin/bash
+DHCP_PRE_HOOKS="/config/scripts/dhcp-client/pre-hooks.d/"
+if [ -d "${DHCP_PRE_HOOKS}" ] ; then
+ run-parts "${DHCP_PRE_HOOKS}"
+fi
diff --git a/src/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks b/src/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks
new file mode 100755
index 000000000..442419d79
--- /dev/null
+++ b/src/etc/dhcp/dhclient-exit-hooks.d/99-run-user-hooks
@@ -0,0 +1,5 @@
+#!/bin/bash
+DHCP_POST_HOOKS="/config/scripts/dhcp-client/post-hooks.d/"
+if [ -d "${DHCP_POST_HOOKS}" ] ; then
+ run-parts "${DHCP_POST_HOOKS}"
+fi
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index 4880605d6..f5d84be4b 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -19,7 +19,7 @@ kernel.core_pattern=/var/core/core-%e-%p-%t
# arp_filter defaults to 1 so set all to 0 so vrrp interfaces can override it.
net.ipv4.conf.all.arp_filter=0
-# https://phabricator.vyos.net/T300
+# https://vyos.dev/T300
net.ipv4.conf.all.arp_ignore=0
net.ipv4.conf.all.arp_announce=2
diff --git a/src/etc/systemd/system/ddclient.service.d/override.conf b/src/etc/systemd/system/ddclient.service.d/override.conf
index d9c9963b0..09d929d39 100644
--- a/src/etc/systemd/system/ddclient.service.d/override.conf
+++ b/src/etc/systemd/system/ddclient.service.d/override.conf
@@ -8,4 +8,4 @@ WorkingDirectory=/run/ddclient
PIDFile=
PIDFile=/run/ddclient/ddclient.pid
ExecStart=
-ExecStart=/usr/sbin/ddclient -cache /run/ddclient/ddclient.cache -pid /run/ddclient/ddclient.pid -file /run/ddclient/ddclient.conf
+ExecStart=/usr/bin/ddclient -cache /run/ddclient/ddclient.cache -pid /run/ddclient/ddclient.pid -file /run/ddclient/ddclient.conf
diff --git a/src/migration-scripts/interfaces/0-to-1 b/src/migration-scripts/interfaces/0-to-1
index ee4d6b82c..c7f324661 100755
--- a/src/migration-scripts/interfaces/0-to-1
+++ b/src/migration-scripts/interfaces/0-to-1
@@ -3,7 +3,7 @@
# Change syntax of bridge interface
# - move interface based bridge-group to actual bridge (de-nest)
# - make stp and igmp-snooping nodes valueless
-# https://phabricator.vyos.net/T1556
+# https://vyos.dev/T1556
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/interfaces/1-to-2 b/src/migration-scripts/interfaces/1-to-2
index 050137318..c75404d85 100755
--- a/src/migration-scripts/interfaces/1-to-2
+++ b/src/migration-scripts/interfaces/1-to-2
@@ -2,7 +2,7 @@
# Change syntax of bond interface
# - move interface based bond-group to actual bond (de-nest)
-# https://phabricator.vyos.net/T1614
+# https://vyos.dev/T1614
import sys
from vyos.configtree import ConfigTree
@@ -40,7 +40,7 @@ else:
# some combinations were allowed in the past from a CLI perspective
# but the kernel overwrote them - remove from CLI to not confuse the users.
# In addition new consitency checks are in place so users can't repeat the
- # mistake. One of those nice issues is https://phabricator.vyos.net/T532
+ # mistake. One of those nice issues is https://vyos.dev/T532
for bond in config.list_nodes(base):
if config.exists(base + [bond, 'arp-monitor', 'interval']) and config.exists(base + [bond, 'mode']):
mode = config.return_value(base + [bond, 'mode'])
diff --git a/src/migration-scripts/interfaces/16-to-17 b/src/migration-scripts/interfaces/16-to-17
index a6b4c7663..d123be06f 100755
--- a/src/migration-scripts/interfaces/16-to-17
+++ b/src/migration-scripts/interfaces/16-to-17
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Command line migration of port mirroring
-# https://phabricator.vyos.net/T3089
+# https://vyos.dev/T3089
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/interfaces/2-to-3 b/src/migration-scripts/interfaces/2-to-3
index a63a54cdf..68d41de39 100755
--- a/src/migration-scripts/interfaces/2-to-3
+++ b/src/migration-scripts/interfaces/2-to-3
@@ -2,7 +2,7 @@
# Change syntax of openvpn encryption settings
# - move cipher from encryption to encryption cipher
-# https://phabricator.vyos.net/T1704
+# https://vyos.dev/T1704
import sys
from vyos.configtree import ConfigTree
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index 0bd858760..cb1c36882 100755
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# T3619: mirror Linux Kernel defaults for ethernet offloading options into VyOS
-# CLI. See https://phabricator.vyos.net/T3619#102254 for all the details.
+# CLI. See https://vyos.dev/T3619#102254 for all the details.
# T3787: Remove deprecated UDP fragmentation offloading option
from sys import argv
diff --git a/src/migration-scripts/interfaces/26-to-27 b/src/migration-scripts/interfaces/26-to-27
new file mode 100755
index 000000000..949cc55b6
--- /dev/null
+++ b/src/migration-scripts/interfaces/26-to-27
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 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/>.
+
+# T4995: pppoe, wwan, sstpc-client rename "authentication user" CLI node
+# to "authentication username"
+
+from sys import argv
+
+from vyos.ethtool import Ethtool
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+for type in ['pppoe', 'sstpc-client', 'wwam']:
+ base = ['interfaces', type]
+ if not config.exists(base):
+ continue
+ for interface in config.list_nodes(base):
+ auth_base = base + [interface, 'authentication', 'user']
+ if config.exists(auth_base):
+ config.rename(auth_base, 'username')
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/interfaces/27-to-28 b/src/migration-scripts/interfaces/27-to-28
new file mode 100755
index 000000000..6225d6414
--- /dev/null
+++ b/src/migration-scripts/interfaces/27-to-28
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 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/>.
+
+# T5034: tunnel: rename "multicast enable" CLI node to "enable-multicast"
+# valueless node.
+
+from sys import argv
+
+from vyos.ethtool import Ethtool
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['interfaces', 'tunnel']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ exit(0)
+
+for ifname in config.list_nodes(base):
+ print(ifname)
+ multicast_base = base + [ifname, 'multicast']
+ if config.exists(multicast_base):
+ tmp = config.return_value(multicast_base)
+ print(tmp)
+ # Delete old Config node
+ config.delete(multicast_base)
+ if tmp == 'enable':
+ config.set(base + [ifname, 'enable-multicast'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/interfaces/4-to-5 b/src/migration-scripts/interfaces/4-to-5
index 2a42c60ff..f645c5aeb 100755
--- a/src/migration-scripts/interfaces/4-to-5
+++ b/src/migration-scripts/interfaces/4-to-5
@@ -50,7 +50,7 @@ def migrate_dialer(config, tree, intf):
# Remove IPv6 router-advert nodes as this makes no sense on a
# client diale rinterface to send RAs back into the network
- # https://phabricator.vyos.net/T2055
+ # https://vyos.dev/T2055
ipv6_ra = pppoe_base + ['ipv6', 'router-advert']
if config.exists(ipv6_ra):
config.delete(ipv6_ra)
diff --git a/src/migration-scripts/ipsec/11-to-12 b/src/migration-scripts/ipsec/11-to-12
new file mode 100755
index 000000000..8bbde5efa
--- /dev/null
+++ b/src/migration-scripts/ipsec/11-to-12
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 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/>.
+
+# Remove legacy ipsec.conf and ipsec.secrets - Not supported with swanctl
+
+import re
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if (len(argv) < 1):
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+base = ['vpn', 'ipsec']
+config = ConfigTree(config_file)
+
+if not config.exists(base):
+ # Nothing to do
+ exit(0)
+
+if config.exists(base + ['include-ipsec-conf']):
+ config.delete(base + ['include-ipsec-conf'])
+
+if config.exists(base + ['include-ipsec-secrets']):
+ config.delete(base + ['include-ipsec-secrets'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print(f'Failed to save the modified config: {e}')
+ exit(1)
diff --git a/src/migration-scripts/qos/1-to-2 b/src/migration-scripts/qos/1-to-2
index 41026cbd6..14d3a6e0a 100755
--- a/src/migration-scripts/qos/1-to-2
+++ b/src/migration-scripts/qos/1-to-2
@@ -118,6 +118,28 @@ if config.exists(netem_base):
if config.exists(netem_base + [policy_name, 'burst']):
config.delete(netem_base + [policy_name, 'burst'])
+# Change bandwidth unit MBit -> mbit as tc only supports mbit
+base = ['qos', 'policy']
+if config.exists(base):
+ for policy_type in config.list_nodes(base):
+ for policy in config.list_nodes(base + [policy_type]):
+ policy_base = base + [policy_type, policy]
+ if config.exists(policy_base + ['bandwidth']):
+ tmp = config.return_value(policy_base + ['bandwidth'])
+ config.set(policy_base + ['bandwidth'], value=tmp.lower())
+
+ if config.exists(policy_base + ['class']):
+ for cls in config.list_nodes(policy_base + ['class']):
+ cls_base = policy_base + ['class', cls]
+ if config.exists(cls_base + ['bandwidth']):
+ tmp = config.return_value(cls_base + ['bandwidth'])
+ config.set(cls_base + ['bandwidth'], value=tmp.lower())
+
+ if config.exists(policy_base + ['default', 'bandwidth']):
+ if config.exists(policy_base + ['default', 'bandwidth']):
+ tmp = config.return_value(policy_base + ['default', 'bandwidth'])
+ config.set(policy_base + ['default', 'bandwidth'], value=tmp.lower())
+
try:
with open(file_name, 'w') as f:
f.write(config.to_string())
diff --git a/src/migration-scripts/snmp/0-to-1 b/src/migration-scripts/snmp/0-to-1
index a836f7011..096ba779d 100755
--- a/src/migration-scripts/snmp/0-to-1
+++ b/src/migration-scripts/snmp/0-to-1
@@ -33,18 +33,18 @@ if not config.exists(config_base):
# Nothing to do
sys.exit(0)
else:
- # we no longer support a per trap target engine ID (https://phabricator.vyos.net/T818)
+ # we no longer support a per trap target engine ID (https://vyos.dev/T818)
if config.exists(config_base + ['v3', 'trap-target']):
for target in config.list_nodes(config_base + ['v3', 'trap-target']):
config.delete(config_base + ['v3', 'trap-target', target, 'engineid'])
- # we no longer support a per user engine ID (https://phabricator.vyos.net/T818)
+ # we no longer support a per user engine ID (https://vyos.dev/T818)
if config.exists(config_base + ['v3', 'user']):
for user in config.list_nodes(config_base + ['v3', 'user']):
config.delete(config_base + ['v3', 'user', user, 'engineid'])
# we drop TSM support as there seem to be no users and this code is untested
- # https://phabricator.vyos.net/T1769
+ # https://vyos.dev/T1769
if config.exists(config_base + ['v3', 'tsm']):
config.delete(config_base + ['v3', 'tsm'])
diff --git a/src/op_mode/accelppp.py b/src/op_mode/accelppp.py
index 2fd045dc3..87a25bb96 100755
--- a/src/op_mode/accelppp.py
+++ b/src/op_mode/accelppp.py
@@ -27,29 +27,51 @@ from vyos.util import rc_cmd
accel_dict = {
'ipoe': {
'port': 2002,
- 'path': 'service ipoe-server'
+ 'path': 'service ipoe-server',
+ 'base_path': 'service ipoe-server'
},
'pppoe': {
'port': 2001,
- 'path': 'service pppoe-server'
+ 'path': 'service pppoe-server',
+ 'base_path': 'service pppoe-server'
},
'pptp': {
'port': 2003,
- 'path': 'vpn pptp'
+ 'path': 'vpn pptp',
+ 'base_path': 'vpn pptp'
},
'l2tp': {
'port': 2004,
- 'path': 'vpn l2tp'
+ 'path': 'vpn l2tp',
+ 'base_path': 'vpn l2tp remote-access'
},
'sstp': {
'port': 2005,
- 'path': 'vpn sstp'
+ 'path': 'vpn sstp',
+ 'base_path': 'vpn sstp'
}
}
-def _get_raw_statistics(accel_output, pattern):
- return vyos.accel_ppp.get_server_statistics(accel_output, pattern, sep=':')
+def _get_config_settings(protocol):
+ '''Get config dict from VyOS configuration'''
+ conf = ConfigTreeQuery()
+ base_path = accel_dict[protocol]['base_path']
+ data = conf.get_config_dict(base_path,
+ key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+ if conf.exists(f'{base_path} authentication local-users'):
+ # Delete sensitive data
+ del data['authentication']['local_users']
+ return {'config_option': data}
+
+
+def _get_raw_statistics(accel_output, pattern, protocol):
+ return {
+ **vyos.accel_ppp.get_server_statistics(accel_output, pattern, sep=':'),
+ **_get_config_settings(protocol)
+ }
def _get_raw_sessions(port):
@@ -103,7 +125,7 @@ def show_statistics(raw: bool, protocol: str):
rc, output = rc_cmd(f'/usr/bin/accel-cmd -p {port} show stat')
if raw:
- return _get_raw_statistics(output, pattern)
+ return _get_raw_statistics(output, pattern, protocol)
return output
diff --git a/src/op_mode/generate_public_key_command.py b/src/op_mode/generate_public_key_command.py
index f071ae350..8ba55c901 100755
--- a/src/op_mode/generate_public_key_command.py
+++ b/src/op_mode/generate_public_key_command.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -19,28 +19,51 @@ import sys
import urllib.parse
import vyos.remote
+from vyos.template import generate_uuid4
-def get_key(path):
+
+def get_key(path) -> list:
+ """Get public keys from a local file or remote URL
+
+ Args:
+ path: Path to the public keys file
+
+ Returns: list of public keys split by new line
+
+ """
url = urllib.parse.urlparse(path)
if url.scheme == 'file' or url.scheme == '':
with open(os.path.expanduser(path), 'r') as f:
key_string = f.read()
else:
key_string = vyos.remote.get_remote_config(path)
- return key_string.split()
-
-try:
- username = sys.argv[1]
- algorithm, key, identifier = get_key(sys.argv[2])
-except Exception as e:
- print("Failed to retrieve the public key: {}".format(e))
- sys.exit(1)
-
-print('# To add this key as an embedded key, run the following commands:')
-print('configure')
-print(f'set system login user {username} authentication public-keys {identifier} key {key}')
-print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}')
-print('commit')
-print('save')
-print('exit')
+ return key_string.split('\n')
+
+
+if __name__ == "__main__":
+ first_loop = True
+
+ for k in get_key(sys.argv[2]):
+ k = k.split()
+ # Skip empty list entry
+ if k == []:
+ continue
+
+ try:
+ username = sys.argv[1]
+ # Github keys don't have identifier for example 'vyos@localhost'
+ # 'ssh-rsa AAAA... vyos@localhost'
+ # Generate uuid4 identifier
+ identifier = f'github@{generate_uuid4("")}' if sys.argv[2].startswith('https://github.com') else k[2]
+ algorithm, key = k[0], k[1]
+ except Exception as e:
+ print("Failed to retrieve the public key: {}".format(e))
+ sys.exit(1)
+
+ if first_loop:
+ print('# To add this key as an embedded key, run the following commands:')
+ print('configure')
+ print(f'set system login user {username} authentication public-keys {identifier} key {key}')
+ print(f'set system login user {username} authentication public-keys {identifier} type {algorithm}')
+ first_loop = False
diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py
index f6417764a..8e76f4cc0 100755
--- a/src/op_mode/ipsec.py
+++ b/src/op_mode/ipsec.py
@@ -14,25 +14,19 @@
# 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 re
import sys
import typing
-from collections import OrderedDict
from hurry import filesize
from re import split as re_split
from tabulate import tabulate
-from subprocess import TimeoutExpired
-from vyos.util import call
from vyos.util import convert_data
from vyos.util import seconds_to_human
import vyos.opmode
-
-
-SWANCTL_CONF = '/etc/swanctl/swanctl.conf'
+import vyos.ipsec
def _convert(text):
@@ -43,22 +37,13 @@ def _alphanum_key(key):
return [_convert(c) for c in re_split('([0-9]+)', str(key))]
-def _get_vici_sas():
- from vici import Session as vici_session
-
- try:
- session = vici_session()
- except Exception:
- raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized")
- sas = list(session.list_sas())
- return sas
-
-
def _get_raw_data_sas():
- get_sas = _get_vici_sas()
- sas = convert_data(get_sas)
- return sas
-
+ try:
+ get_sas = vyos.ipsec.get_vici_sas()
+ sas = convert_data(get_sas)
+ return sas
+ except (vyos.ipsec.ViciInitiateError) as err:
+ raise vyos.opmode.UnconfiguredSubsystem(err)
def _get_formatted_output_sas(sas):
sa_data = []
@@ -139,22 +124,14 @@ def _get_formatted_output_sas(sas):
# Connections block
-def _get_vici_connections():
- from vici import Session as vici_session
-
- try:
- session = vici_session()
- except Exception:
- raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized")
- connections = list(session.list_conns())
- return connections
-
def _get_convert_data_connections():
- get_connections = _get_vici_connections()
- connections = convert_data(get_connections)
- return connections
-
+ try:
+ get_connections = vyos.ipsec.get_vici_connections()
+ connections = convert_data(get_connections)
+ return connections
+ except (vyos.ipsec.ViciInitiateError) as err:
+ raise vyos.opmode.UnconfiguredSubsystem(err)
def _get_parent_sa_proposal(connection_name: str, data: list) -> dict:
"""Get parent SA proposals by connection name
@@ -239,7 +216,8 @@ def _get_child_sa_state(connection_name: str, tunnel_name: str,
# Get all child SA states
# there can be multiple SAs per tunnel
child_sa_states = [
- v['state'] for k, v in child_sas.items() if v['name'] == tunnel_name
+ v['state'] for k, v in child_sas.items() if
+ v['name'] == tunnel_name
]
return 'up' if 'INSTALLED' in child_sa_states else child_sa
@@ -406,39 +384,46 @@ def _get_formatted_output_conections(data):
# Connections block end
-def get_peer_connections(peer, tunnel):
- search = rf'^[\s]*({peer}-(tunnel-[\d]+|vti)).*'
- matches = []
- if not os.path.exists(SWANCTL_CONF):
- raise vyos.opmode.UnconfiguredSubsystem("IPsec not initialized")
- suffix = None if tunnel is None else (f'tunnel-{tunnel}' if
- tunnel.isnumeric() else tunnel)
- with open(SWANCTL_CONF, 'r') as f:
- for line in f.readlines():
- result = re.match(search, line)
- if result:
- if tunnel is None:
- matches.append(result[1])
- else:
- if result[2] == suffix:
- matches.append(result[1])
- return matches
-
-
-def reset_peer(peer: str, tunnel:typing.Optional[str]):
- conns = get_peer_connections(peer, tunnel)
-
- if not conns:
- raise vyos.opmode.IncorrectValue('Peer or tunnel(s) not found, aborting')
-
- for conn in conns:
- try:
- call(f'sudo /usr/sbin/ipsec down {conn}{{*}}', timeout = 10)
- call(f'sudo /usr/sbin/ipsec up {conn}', timeout = 10)
- except TimeoutExpired as e:
- raise vyos.opmode.InternalError(f'Timed out while resetting {conn}')
-
- print('Peer reset result: success')
+def _get_childsa_id_list(ike_sas: list) -> list:
+ """
+ Generate list of CHILD SA ids based on list of OrderingDict
+ wich is returned by vici
+ :param ike_sas: list of IKE SAs generated by vici
+ :type ike_sas: list
+ :return: list of IKE SAs ids
+ :rtype: list
+ """
+ list_childsa_id: list = []
+ for ike in ike_sas:
+ for ike_sa in ike.values():
+ for child_sa in ike_sa['child-sas'].values():
+ list_childsa_id.append(child_sa['uniqueid'].decode('ascii'))
+ return list_childsa_id
+
+
+def reset_peer(peer: str, tunnel: typing.Optional[str] = None):
+ # Convert tunnel to Strongwan format of CHILD_SA
+ if tunnel:
+ if tunnel.isnumeric():
+ tunnel = f'{peer}-tunnel-{tunnel}'
+ elif tunnel == 'vti':
+ tunnel = f'{peer}-vti'
+ try:
+ sa_list: list = vyos.ipsec.get_vici_sas_by_name(peer, tunnel)
+
+ if not sa_list:
+ raise vyos.opmode.IncorrectValue('Peer not found, aborting')
+ if tunnel and sa_list:
+ childsa_id_list: list = _get_childsa_id_list(sa_list)
+ if not childsa_id_list:
+ raise vyos.opmode.IncorrectValue(
+ 'Peer or tunnel(s) not found, aborting')
+ vyos.ipsec.terminate_vici_by_name(peer, tunnel)
+ print('Peer reset result: success')
+ except (vyos.ipsec.ViciInitiateError) as err:
+ raise vyos.opmode.UnconfiguredSubsystem(err)
+ except (vyos.ipsec.ViciInitiateError) as err:
+ raise vyos.opmode.IncorrectValue(err)
def show_sa(raw: bool):
diff --git a/src/op_mode/nhrp.py b/src/op_mode/nhrp.py
new file mode 100755
index 000000000..5ff91a59c
--- /dev/null
+++ b/src/op_mode/nhrp.py
@@ -0,0 +1,101 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 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 sys
+import tabulate
+import vyos.opmode
+
+from vyos.util import cmd
+from vyos.util import process_named_running
+from vyos.util import colon_separated_to_dict
+
+
+def _get_formatted_output(output_dict: dict) -> str:
+ """
+ Create formatted table for CLI output
+ :param output_dict: dictionary for API
+ :type output_dict: dict
+ :return: tabulate string
+ :rtype: str
+ """
+ print(f"Status: {output_dict['Status']}")
+ output: str = tabulate.tabulate(output_dict['routes'], headers='keys',
+ numalign="left")
+ return output
+
+
+def _get_formatted_dict(output_string: str) -> dict:
+ """
+ Format string returned from CMD to API list
+ :param output_string: String received by CMD
+ :type output_string: str
+ :return: dictionary for API
+ :rtype: dict
+ """
+ formatted_dict: dict = {
+ 'Status': '',
+ 'routes': []
+ }
+ output_list: list = output_string.split('\n\n')
+ for list_a in output_list:
+ output_dict = colon_separated_to_dict(list_a, True)
+ if 'Status' in output_dict:
+ formatted_dict['Status'] = output_dict['Status']
+ else:
+ formatted_dict['routes'].append(output_dict)
+ return formatted_dict
+
+
+def show_interface(raw: bool):
+ """
+ Command 'show nhrp interface'
+ :param raw: if API
+ :type raw: bool
+ """
+ if not process_named_running('opennhrp'):
+ raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.')
+ interface_string: str = cmd('sudo opennhrpctl interface show')
+ interface_dict: dict = _get_formatted_dict(interface_string)
+ if raw:
+ return interface_dict
+ else:
+ return _get_formatted_output(interface_dict)
+
+
+def show_tunnel(raw: bool):
+ """
+ Command 'show nhrp tunnel'
+ :param raw: if API
+ :type raw: bool
+ """
+ if not process_named_running('opennhrp'):
+ raise vyos.opmode.UnconfiguredSubsystem('OpenNHRP is not running.')
+ tunnel_string: str = cmd('sudo opennhrpctl show')
+ tunnel_dict: list = _get_formatted_dict(tunnel_string)
+ if raw:
+ return tunnel_dict
+ else:
+ return _get_formatted_output(tunnel_dict)
+
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index 3797a7153..79130c7c0 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2022 VyOS maintainers and contributors
+# Copyright (C) 2022-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
@@ -153,6 +153,8 @@ def _get_raw_data(mode: str) -> dict:
d = data[intf]
d['local_host'] = conf_dict[intf].get('local-host', '')
d['local_port'] = conf_dict[intf].get('local-port', '')
+ if conf.exists(f'interfaces openvpn {intf} server client'):
+ d['configured_clients'] = conf.list_nodes(f'interfaces openvpn {intf} server client')
if mode in ['client', 'site-to-site']:
for client in d['clients']:
if 'shared-secret-key-file' in list(conf_dict[intf]):
@@ -171,8 +173,8 @@ def _format_openvpn(data: dict) -> str:
'TX bytes', 'RX bytes', 'Connected Since']
out = ''
- data_out = []
for intf in list(data):
+ data_out = []
l_host = data[intf]['local_host']
l_port = data[intf]['local_port']
for client in list(data[intf]['clients']):
@@ -190,7 +192,9 @@ def _format_openvpn(data: dict) -> str:
data_out.append([name, remote, tunnel, local, tx_bytes,
rx_bytes, online_since])
- out += tabulate(data_out, headers)
+ if data_out:
+ out += tabulate(data_out, headers)
+ out += "\n"
return out
diff --git a/src/op_mode/show_openconnect_otp.py b/src/op_mode/show_openconnect_otp.py
index ae532ccc9..88982c50b 100755
--- a/src/op_mode/show_openconnect_otp.py
+++ b/src/op_mode/show_openconnect_otp.py
@@ -46,7 +46,7 @@ def get_otp_ocserv(username):
# options which we need to update into the dictionary retrived.
default_values = defaults(base)
ocserv = dict_merge(default_values, ocserv)
- # workaround a "know limitation" - https://phabricator.vyos.net/T2665
+ # workaround a "know limitation" - https://vyos.dev/T2665
del ocserv['authentication']['local_users']['username']['otp']
if not ocserv["authentication"]["local_users"]["username"]:
return None
diff --git a/src/services/api/graphql/generate/config_session_function.py b/src/services/api/graphql/generate/config_session_function.py
index fc0dd7a87..20fc7cc1d 100644
--- a/src/services/api/graphql/generate/config_session_function.py
+++ b/src/services/api/graphql/generate/config_session_function.py
@@ -8,8 +8,12 @@ def show_config(path: list[str], configFormat: typing.Optional[str]):
def show(path: list[str]):
pass
+def show_user_info(user: str):
+ pass
+
queries = {'show_config': show_config,
- 'show': show}
+ 'show': show,
+ 'show_user_info': show_user_info}
def save_config_file(fileName: typing.Optional[str]):
pass
diff --git a/src/services/api/graphql/graphql/auth_token_mutation.py b/src/services/api/graphql/graphql/auth_token_mutation.py
index 21ac40094..603a13758 100644
--- a/src/services/api/graphql/graphql/auth_token_mutation.py
+++ b/src/services/api/graphql/graphql/auth_token_mutation.py
@@ -20,6 +20,7 @@ from ariadne import ObjectType, UnionType
from graphql import GraphQLResolveInfo
from .. libs.token_auth import generate_token
+from .. session.session import get_user_info
from .. import state
auth_token_mutation = ObjectType("Mutation")
@@ -36,13 +37,24 @@ def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
datetime.timedelta(seconds=exp_interval))
res = generate_token(user, passwd, secret, expiration)
- if res:
+ try:
+ res |= get_user_info(user)
+ except ValueError:
+ # non-existent user already caught
+ pass
+ if 'token' in res:
data['result'] = res
return {
"success": True,
"data": data
}
+ if 'errors' in res:
+ return {
+ "success": False,
+ "errors": res['errors']
+ }
+
return {
"success": False,
"errors": ['token generation failed']
diff --git a/src/services/api/graphql/libs/token_auth.py b/src/services/api/graphql/libs/token_auth.py
index 2100eba7f..8585485c9 100644
--- a/src/services/api/graphql/libs/token_auth.py
+++ b/src/services/api/graphql/libs/token_auth.py
@@ -29,14 +29,13 @@ def generate_token(user: str, passwd: str, secret: str, exp: int) -> dict:
payload_data = {'iss': user, 'sub': user_id, 'exp': exp}
secret = state.settings.get('secret')
if secret is None:
- return {
- "success": False,
- "errors": ['failed secret generation']
- }
+ return {"errors": ['missing secret']}
token = jwt.encode(payload=payload_data, key=secret, algorithm="HS256")
users |= {user_id: user}
return {'token': token}
+ else:
+ return {"errors": ['failed pam authentication']}
def get_user_context(request):
context = {}
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 0b77b1433..3c5a062b6 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -29,6 +29,28 @@ from api.graphql.libs.op_mode import normalize_output
op_mode_include_file = os.path.join(directories['data'], 'op-mode-standardized.json')
+def get_config_dict(path=[], effective=False, key_mangling=None,
+ get_first_key=False, no_multi_convert=False,
+ no_tag_node_value_mangle=False):
+ config = Config()
+ return config.get_config_dict(path=path, effective=effective,
+ key_mangling=key_mangling,
+ get_first_key=get_first_key,
+ no_multi_convert=no_multi_convert,
+ no_tag_node_value_mangle=no_tag_node_value_mangle)
+
+def get_user_info(user):
+ user_info = {}
+ info = get_config_dict(['system', 'login', 'user', user],
+ get_first_key=True)
+ if not info:
+ raise ValueError("No such user")
+
+ user_info['user'] = user
+ user_info['full_name'] = info.get('full-name', '')
+
+ return user_info
+
class Session:
"""
Wrapper for calling configsession functions based on GraphQL requests.
@@ -116,6 +138,19 @@ class Session:
return res
+ def show_user_info(self):
+ session = self._session
+ data = self._data
+
+ user_info = {}
+ user = data['user']
+ try:
+ user_info = get_user_info(user)
+ except Exception as error:
+ raise error
+
+ return user_info
+
def system_status(self):
import api.graphql.session.composite.system_status as system_status
diff --git a/src/services/vyos-http-api-server b/src/services/vyos-http-api-server
index f59e089ae..cd73f38ec 100755
--- a/src/services/vyos-http-api-server
+++ b/src/services/vyos-http-api-server
@@ -425,7 +425,7 @@ async def validation_exception_handler(request, exc):
return error(400, str(exc.errors()[0]))
@app.post('/configure')
-def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
+async def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
session = app.state.vyos_session
env = session.get_session_env()
config = vyos.config.Config(session_env=env)
@@ -494,7 +494,7 @@ def configure_op(data: Union[ConfigureModel, ConfigureListModel]):
return success(None)
@app.post("/retrieve")
-def retrieve_op(data: RetrieveModel):
+async def retrieve_op(data: RetrieveModel):
session = app.state.vyos_session
env = session.get_session_env()
config = vyos.config.Config(session_env=env)
diff --git a/src/tests/test_configverify.py b/src/tests/test_configverify.py
index ad7e053db..6fb43ece2 100644
--- a/src/tests/test_configverify.py
+++ b/src/tests/test_configverify.py
@@ -27,11 +27,6 @@ class TestDictSearch(TestCase):
def test_dh_key_none(self):
self.assertFalse(verify_diffie_hellman_length('/tmp/non_existing_file', '1024'))
- def test_dh_key_256(self):
- key_len = '256'
- cmd(f'openssl dhparam -out {dh_file} {key_len}')
- self.assertTrue(verify_diffie_hellman_length(dh_file, key_len))
-
def test_dh_key_512(self):
key_len = '512'
cmd(f'openssl dhparam -out {dh_file} {key_len}')
diff --git a/src/validators/timezone b/src/validators/timezone
index baf5abca2..107571181 100755
--- a/src/validators/timezone
+++ b/src/validators/timezone
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# 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
@@ -25,7 +25,7 @@ if __name__ == '__main__':
parser.add_argument("--validate", action="store", required=True, help="Check if timezone is valid")
args = parser.parse_args()
- tz_data = cmd('find /usr/share/zoneinfo/posix -type f -or -type l | sed -e s:/usr/share/zoneinfo/posix/::')
+ tz_data = cmd('timedatectl list-timezones')
tz_data = tz_data.split('\n')
if args.validate not in tz_data:
diff --git a/src/xdp/common/common_libbpf.c b/src/xdp/common/common_libbpf.c
index 5788ecd9e..443ca4c66 100644
--- a/src/xdp/common/common_libbpf.c
+++ b/src/xdp/common/common_libbpf.c
@@ -24,10 +24,6 @@ static inline bool IS_ERR_OR_NULL(const void *ptr)
int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr,
struct bpf_object **pobj, int *prog_fd)
{
- struct bpf_object_open_attr open_attr = {
- .file = attr->file,
- .prog_type = attr->prog_type,
- };
struct bpf_program *prog, *first_prog = NULL;
enum bpf_attach_type expected_attach_type;
enum bpf_prog_type prog_type;
@@ -41,10 +37,13 @@ int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr,
if (!attr->file)
return -EINVAL;
+ obj = bpf_object__open_file(attr->file, NULL);
- obj = bpf_object__open_xattr(&open_attr);
- if (IS_ERR_OR_NULL(obj))
- return -ENOENT;
+ if (libbpf_get_error(obj))
+ return -EINVAL;
+
+ prog = bpf_object__next_program(obj, NULL);
+ bpf_program__set_type(prog, attr->prog_type);
bpf_object__for_each_program(prog, obj) {
/*
@@ -82,7 +81,7 @@ int bpf_prog_load_xattr_maps(const struct bpf_prog_load_attr_maps *attr,
bpf_map__for_each(map, obj) {
const char* mapname = bpf_map__name(map);
- if (!bpf_map__is_offload_neutral(map))
+ if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
bpf_map__set_ifindex(map, attr->ifindex);
/* Was: map->map_ifindex = attr->ifindex; */
diff --git a/src/xdp/common/common_user_bpf_xdp.c b/src/xdp/common/common_user_bpf_xdp.c
index faf7f4f91..524f08c9d 100644
--- a/src/xdp/common/common_user_bpf_xdp.c
+++ b/src/xdp/common/common_user_bpf_xdp.c
@@ -21,7 +21,7 @@ int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
int err;
/* libbpf provide the XDP net_device link-level hook attach helper */
- err = bpf_set_link_xdp_fd(ifindex, prog_fd, xdp_flags);
+ err = bpf_xdp_attach(ifindex, prog_fd, xdp_flags, NULL);
if (err == -EEXIST && !(xdp_flags & XDP_FLAGS_UPDATE_IF_NOEXIST)) {
/* Force mode didn't work, probably because a program of the
* opposite type is loaded. Let's unload that and try loading
@@ -32,9 +32,9 @@ int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
xdp_flags &= ~XDP_FLAGS_MODES;
xdp_flags |= (old_flags & XDP_FLAGS_SKB_MODE) ? XDP_FLAGS_DRV_MODE : XDP_FLAGS_SKB_MODE;
- err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags);
+ err = bpf_xdp_detach(ifindex, xdp_flags, NULL);
if (!err)
- err = bpf_set_link_xdp_fd(ifindex, prog_fd, old_flags);
+ err = bpf_xdp_attach(ifindex, prog_fd, old_flags, NULL);
}
if (err < 0) {
fprintf(stderr, "ERR: "
@@ -65,7 +65,7 @@ int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id)
__u32 curr_prog_id;
int err;
- err = bpf_get_link_xdp_id(ifindex, &curr_prog_id, xdp_flags);
+ err = bpf_xdp_query_id(ifindex, xdp_flags, &curr_prog_id);
if (err) {
fprintf(stderr, "ERR: get link xdp id failed (err=%d): %s\n",
-err, strerror(-err));
@@ -86,7 +86,7 @@ int xdp_link_detach(int ifindex, __u32 xdp_flags, __u32 expected_prog_id)
return EXIT_FAIL;
}
- if ((err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags)) < 0) {
+ if ((err = bpf_xdp_detach(ifindex, xdp_flags, NULL)) < 0) {
fprintf(stderr, "ERR: %s() link set xdp failed (err=%d): %s\n",
__func__, err, strerror(-err));
return EXIT_FAIL_XDP;
@@ -109,22 +109,28 @@ struct bpf_object *load_bpf_object_file(const char *filename, int ifindex)
* hardware offloading XDP programs (note this sets libbpf
* bpf_program->prog_ifindex and foreach bpf_map->map_ifindex).
*/
- struct bpf_prog_load_attr prog_load_attr = {
- .prog_type = BPF_PROG_TYPE_XDP,
- .ifindex = ifindex,
- };
- prog_load_attr.file = filename;
+ struct bpf_program *prog;
+ obj = bpf_object__open_file(filename, NULL);
+
+ if (libbpf_get_error(obj))
+ return NULL;
+
+ prog = bpf_object__next_program(obj, NULL);
+ bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
+ bpf_program__set_ifindex(prog, ifindex);
/* Use libbpf for extracting BPF byte-code from BPF-ELF object, and
* loading this into the kernel via bpf-syscall
*/
- err = bpf_prog_load_xattr(&prog_load_attr, &obj, &first_prog_fd);
+ err = bpf_object__load(obj);
if (err) {
fprintf(stderr, "ERR: loading BPF-OBJ file(%s) (%d): %s\n",
filename, err, strerror(-err));
return NULL;
}
+ first_prog_fd = bpf_program__fd(prog);
+
/* Notice how a pointer to a libbpf bpf_object is returned */
return obj;
}
@@ -136,12 +142,15 @@ static struct bpf_object *open_bpf_object(const char *file, int ifindex)
struct bpf_map *map;
struct bpf_program *prog, *first_prog = NULL;
- struct bpf_object_open_attr open_attr = {
- .file = file,
- .prog_type = BPF_PROG_TYPE_XDP,
- };
+ obj = bpf_object__open_file(file, NULL);
- obj = bpf_object__open_xattr(&open_attr);
+ if (libbpf_get_error(obj))
+ return NULL;
+
+ prog = bpf_object__next_program(obj, NULL);
+ bpf_program__set_type(prog, BPF_PROG_TYPE_XDP);
+
+ err = bpf_object__load(obj);
if (IS_ERR_OR_NULL(obj)) {
err = -PTR_ERR(obj);
fprintf(stderr, "ERR: opening BPF-OBJ file(%s) (%d): %s\n",
@@ -157,7 +166,7 @@ static struct bpf_object *open_bpf_object(const char *file, int ifindex)
}
bpf_object__for_each_map(map, obj) {
- if (!bpf_map__is_offload_neutral(map))
+ if (bpf_map__type(map) != BPF_MAP_TYPE_PERF_EVENT_ARRAY)
bpf_map__set_ifindex(map, ifindex);
}
@@ -264,10 +273,10 @@ struct bpf_object *load_bpf_and_xdp_attach(struct config *cfg)
if (cfg->progsec[0])
/* Find a matching BPF prog section name */
- bpf_prog = bpf_object__find_program_by_title(bpf_obj, cfg->progsec);
+ bpf_prog = bpf_object__find_program_by_name(bpf_obj, cfg->progsec);
else
/* Find the first program */
- bpf_prog = bpf_program__next(NULL, bpf_obj);
+ bpf_prog = bpf_object__next_program(bpf_obj, NULL);
if (!bpf_prog) {
fprintf(stderr, "ERR: couldn't find a program in ELF section '%s'\n", cfg->progsec);
diff --git a/src/xdp/common/xdp_stats_kern.h b/src/xdp/common/xdp_stats_kern.h
index 4e08551a0..c061a149d 100644
--- a/src/xdp/common/xdp_stats_kern.h
+++ b/src/xdp/common/xdp_stats_kern.h
@@ -13,12 +13,12 @@
#endif
/* Keeps stats per (enum) xdp_action */
-struct bpf_map_def SEC("maps") xdp_stats_map = {
- .type = BPF_MAP_TYPE_PERCPU_ARRAY,
- .key_size = sizeof(__u32),
- .value_size = sizeof(struct datarec),
- .max_entries = XDP_ACTION_MAX,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
+ __type(key, __u32);
+ __type(value, struct datarec);
+ __uint(max_entries, XDP_ACTION_MAX);
+} xdp_stats_map SEC(".maps");
static __always_inline
__u32 xdp_stats_record_action(struct xdp_md *ctx, __u32 action)
diff --git a/src/xdp/xdp_prog_kern.c b/src/xdp/xdp_prog_kern.c
index a1eb395af..59308325d 100644
--- a/src/xdp/xdp_prog_kern.c
+++ b/src/xdp/xdp_prog_kern.c
@@ -16,19 +16,19 @@
#define memcpy(dest, src, n) __builtin_memcpy((dest), (src), (n))
#endif
-struct bpf_map_def SEC("maps") tx_port = {
- .type = BPF_MAP_TYPE_DEVMAP,
- .key_size = sizeof(int),
- .value_size = sizeof(int),
- .max_entries = 256,
-};
-
-struct bpf_map_def SEC("maps") redirect_params = {
- .type = BPF_MAP_TYPE_HASH,
- .key_size = ETH_ALEN,
- .value_size = ETH_ALEN,
- .max_entries = 1,
-};
+struct {
+ __uint(type, BPF_MAP_TYPE_DEVMAP);
+ __type(key, int);
+ __type(value, int);
+ __uint(max_entries, 256);
+} tx_port SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __type(key, ETH_ALEN);
+ __type(value, ETH_ALEN);
+ __uint(max_entries, 1);
+} redirect_params SEC(".maps");
static __always_inline __u16 csum_fold_helper(__u32 csum)
{
@@ -208,8 +208,12 @@ out:
return xdp_stats_record_action(ctx, action);
}
+#ifndef AF_INET
#define AF_INET 2
+#endif
+#ifndef AF_INET6
#define AF_INET6 10
+#endif
#define IPV6_FLOWINFO_MASK bpf_htonl(0x0FFFFFFF)
/* from include/net/ip.h */