summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/container.py6
-rwxr-xr-xsrc/conf_mode/interfaces_wireguard.py36
-rwxr-xr-xsrc/conf_mode/pki.py61
-rwxr-xr-xsrc/conf_mode/protocols_static_multicast.py6
-rwxr-xr-xsrc/conf_mode/qos.py55
-rwxr-xr-xsrc/conf_mode/service_dhcp-server.py8
-rwxr-xr-xsrc/conf_mode/service_ipoe-server.py35
-rwxr-xr-xsrc/init/vyos-router6
-rw-r--r--src/migration-scripts/qos/2-to-334
-rwxr-xr-xsrc/op_mode/show_configuration_files.sh10
-rwxr-xr-xsrc/services/vyos-configd28
-rw-r--r--src/shim/vyshim.c12
12 files changed, 201 insertions, 96 deletions
diff --git a/src/conf_mode/container.py b/src/conf_mode/container.py
index 14387cbbf..a7dc33d9d 100755
--- a/src/conf_mode/container.py
+++ b/src/conf_mode/container.py
@@ -419,12 +419,18 @@ def generate(container):
'dns_enabled': True,
'ipam_options': {
'driver': 'host-local'
+ },
+ 'options': {
+ 'mtu': '1500'
}
}
if 'no_name_server' in network_config:
tmp['dns_enabled'] = False
+ if 'mtu' in network_config:
+ tmp['options']['mtu'] = network_config['mtu']
+
for prefix in network_config['prefix']:
net = {'subnet': prefix, 'gateway': inc_ip(prefix, 1)}
tmp['subnets'].append(net)
diff --git a/src/conf_mode/interfaces_wireguard.py b/src/conf_mode/interfaces_wireguard.py
index 7abdfdbfa..b6fd6b0b2 100755
--- a/src/conf_mode/interfaces_wireguard.py
+++ b/src/conf_mode/interfaces_wireguard.py
@@ -70,9 +70,6 @@ def verify(wireguard):
if 'private_key' not in wireguard:
raise ConfigError('Wireguard private-key not defined')
- if 'peer' not in wireguard:
- raise ConfigError('At least one Wireguard peer is required!')
-
if 'port' in wireguard and 'port_changed' in wireguard:
listen_port = int(wireguard['port'])
if check_port_availability('0.0.0.0', listen_port, 'udp') is not True:
@@ -80,28 +77,29 @@ def verify(wireguard):
'cannot be used for the interface!')
# run checks on individual configured WireGuard peer
- public_keys = []
- for tmp in wireguard['peer']:
- peer = wireguard['peer'][tmp]
+ if 'peer' in wireguard:
+ public_keys = []
+ for tmp in wireguard['peer']:
+ peer = wireguard['peer'][tmp]
- if 'allowed_ips' not in peer:
- raise ConfigError(f'Wireguard allowed-ips required for peer "{tmp}"!')
+ if 'allowed_ips' not in peer:
+ raise ConfigError(f'Wireguard allowed-ips required for peer "{tmp}"!')
- if 'public_key' not in peer:
- raise ConfigError(f'Wireguard public-key required for peer "{tmp}"!')
+ if 'public_key' not in peer:
+ raise ConfigError(f'Wireguard public-key required for peer "{tmp}"!')
- if ('address' in peer and 'port' not in peer) or ('port' in peer and 'address' not in peer):
- raise ConfigError('Both Wireguard port and address must be defined '
- f'for peer "{tmp}" if either one of them is set!')
+ if ('address' in peer and 'port' not in peer) or ('port' in peer and 'address' not in peer):
+ raise ConfigError('Both Wireguard port and address must be defined '
+ f'for peer "{tmp}" if either one of them is set!')
- if peer['public_key'] in public_keys:
- raise ConfigError(f'Duplicate public-key defined on peer "{tmp}"')
+ if peer['public_key'] in public_keys:
+ raise ConfigError(f'Duplicate public-key defined on peer "{tmp}"')
- if 'disable' not in peer:
- if is_wireguard_key_pair(wireguard['private_key'], peer['public_key']):
- raise ConfigError(f'Peer "{tmp}" has the same public key as the interface "{wireguard["ifname"]}"')
+ if 'disable' not in peer:
+ if is_wireguard_key_pair(wireguard['private_key'], peer['public_key']):
+ raise ConfigError(f'Peer "{tmp}" has the same public key as the interface "{wireguard["ifname"]}"')
- public_keys.append(peer['public_key'])
+ public_keys.append(peer['public_key'])
def generate(wireguard):
return None
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 45e0129a3..acea2c9be 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -50,6 +50,7 @@ from vyos import airbag
airbag.enable()
vyos_certbot_dir = directories['certbot']
+vyos_ca_certificates_dir = directories['ca_certificates']
# keys to recursively search for under specified path
sync_search = [
@@ -149,35 +150,15 @@ def get_config(config=None):
if len(argv) > 1 and argv[1] == 'certbot_renew':
pki['certbot_renew'] = {}
- tmp = node_changed(conf, base + ['ca'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'ca' : tmp})
-
- tmp = node_changed(conf, base + ['certificate'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'certificate' : tmp})
+ changed_keys = ['ca', 'certificate', 'dh', 'key-pair', 'openssh', 'openvpn']
- tmp = node_changed(conf, base + ['dh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'dh' : tmp})
+ for key in changed_keys:
+ tmp = node_changed(conf, base + [key], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- tmp = node_changed(conf, base + ['key-pair'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'key_pair' : tmp})
-
- tmp = node_changed(conf, base + ['openssh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'openssh' : tmp})
+ if 'changed' not in pki:
+ pki.update({'changed':{}})
- tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
- if tmp:
- if 'changed' not in pki: pki.update({'changed':{}})
- pki['changed'].update({'openvpn' : tmp})
+ pki['changed'].update({key.replace('-', '_') : tmp})
# We only merge on the defaults of there is a configuration at all
if conf.exists(base):
@@ -417,10 +398,33 @@ def verify(pki):
return None
+def cleanup_system_ca():
+ if not os.path.exists(vyos_ca_certificates_dir):
+ os.mkdir(vyos_ca_certificates_dir)
+ else:
+ for filename in os.listdir(vyos_ca_certificates_dir):
+ full_path = os.path.join(vyos_ca_certificates_dir, filename)
+ if os.path.isfile(full_path):
+ os.unlink(full_path)
+
def generate(pki):
if not pki:
+ cleanup_system_ca()
return None
+ # Create or cleanup CA install directory
+ if 'changed' in pki and 'ca' in pki['changed']:
+ cleanup_system_ca()
+
+ if 'ca' in pki:
+ for ca, ca_conf in pki['ca'].items():
+ if 'system_install' in ca_conf:
+ ca_obj = load_certificate(ca_conf['certificate'])
+ ca_path = os.path.join(vyos_ca_certificates_dir, f'{ca}.crt')
+
+ with open(ca_path, 'w') as f:
+ f.write(encode_certificate(ca_obj))
+
# Certbot renewal only needs to re-trigger the services to load up the
# new PEM file
if 'certbot_renew' in pki:
@@ -487,6 +491,7 @@ def apply(pki):
systemd_certbot_name = 'certbot.timer'
if not pki:
call(f'systemctl stop {systemd_certbot_name}')
+ call('update-ca-certificates')
return None
has_certbot = False
@@ -504,6 +509,10 @@ def apply(pki):
if 'changed' in pki:
call_dependents()
+ # Rebuild ca-certificates bundle
+ if 'ca' in pki['changed']:
+ call('update-ca-certificates')
+
return None
if __name__ == '__main__':
diff --git a/src/conf_mode/protocols_static_multicast.py b/src/conf_mode/protocols_static_multicast.py
index d323ceb4f..c8894fd41 100755
--- a/src/conf_mode/protocols_static_multicast.py
+++ b/src/conf_mode/protocols_static_multicast.py
@@ -86,10 +86,10 @@ def verify(mroute):
if mroute is None:
return None
- for route in mroute['mroute']:
- route = route.split('/')
+ for mcast_route in mroute['mroute']:
+ route = mcast_route.split('/')
if IPv4Address(route[0]) < IPv4Address('224.0.0.0'):
- raise ConfigError(route + " not a multicast network")
+ raise ConfigError(f'{mcast_route} not a multicast network')
def generate(mroute):
diff --git a/src/conf_mode/qos.py b/src/conf_mode/qos.py
index a4d5f44e7..59e307a39 100755
--- a/src/conf_mode/qos.py
+++ b/src/conf_mode/qos.py
@@ -198,10 +198,16 @@ def get_config(config=None):
def _verify_match(cls_config: dict) -> None:
if 'match' in cls_config:
for match, match_config in cls_config['match'].items():
- if {'ip', 'ipv6'} <= set(match_config):
+ filters = set(match_config)
+ if {'ip', 'ipv6'} <= filters:
raise ConfigError(
f'Can not use both IPv6 and IPv4 in one match ({match})!')
+ if {'interface', 'vif'} & filters:
+ if {'ip', 'ipv6', 'ether'} & filters:
+ raise ConfigError(
+ f'Can not combine protocol and interface or vlan tag match ({match})!')
+
def _verify_match_group_exist(cls_config, qos):
if 'match_group' in cls_config:
@@ -210,6 +216,46 @@ def _verify_match_group_exist(cls_config, qos):
Warning(f'Match group "{group}" does not exist!')
+def _verify_default_policy_exist(policy, policy_config):
+ if 'default' not in policy_config:
+ raise ConfigError(f'Policy {policy} misses "default" class!')
+
+
+def _check_shaper_hfsc_rate(cls, cls_conf):
+ is_m2_exist = False
+ for crit in TrafficShaperHFSC.criteria:
+ if cls_conf.get(crit, {}).get('m2') is not None:
+ is_m2_exist = True
+
+ if cls_conf.get(crit, {}).get('m1') is not None:
+ for crit_val in ['m2', 'd']:
+ if cls_conf.get(crit, {}).get(crit_val) is None:
+ raise ConfigError(
+ f'{cls} {crit} m1 value is set, but no {crit_val} was found!'
+ )
+
+ if not is_m2_exist:
+ raise ConfigError(f'At least one m2 value needs to be set for class: {cls}')
+
+ if (
+ cls_conf.get('upperlimit', {}).get('m2') is not None
+ and cls_conf.get('linkshare', {}).get('m2') is None
+ ):
+ raise ConfigError(
+ f'Linkshare m2 needs to be defined to use upperlimit m2 for class: {cls}'
+ )
+
+
+def _verify_shaper_hfsc(policy, policy_config):
+ _verify_default_policy_exist(policy, policy_config)
+
+ _check_shaper_hfsc_rate('default', policy_config.get('default'))
+
+ if 'class' in policy_config:
+ for cls, cls_conf in policy_config['class'].items():
+ _check_shaper_hfsc_rate(cls, cls_conf)
+
+
def verify(qos):
if not qos or 'interface' not in qos:
return None
@@ -253,11 +299,13 @@ def verify(qos):
if queue_lim < max_tr:
raise ConfigError(f'Policy "{policy}" uses queue-limit "{queue_lim}" < max-threshold "{max_tr}"!')
if policy_type in ['priority_queue']:
- if 'default' not in policy_config:
- raise ConfigError(f'Policy {policy} misses "default" class!')
+ _verify_default_policy_exist(policy, policy_config)
if policy_type in ['rate_control']:
if 'bandwidth' not in policy_config:
raise ConfigError('Bandwidth not defined')
+ if policy_type in ['shaper_hfsc']:
+ _verify_shaper_hfsc(policy, policy_config)
+
if 'default' in policy_config:
if 'bandwidth' not in policy_config['default'] and policy_type not in ['priority_queue', 'round_robin', 'shaper_hfsc']:
raise ConfigError('Bandwidth not defined for default traffic!')
@@ -293,6 +341,7 @@ def generate(qos):
return None
+
def apply(qos):
# Always delete "old" shapers first
for interface in interfaces():
diff --git a/src/conf_mode/service_dhcp-server.py b/src/conf_mode/service_dhcp-server.py
index e89448e2d..9c59aa63d 100755
--- a/src/conf_mode/service_dhcp-server.py
+++ b/src/conf_mode/service_dhcp-server.py
@@ -87,6 +87,10 @@ def dhcp_slice_range(exclude_list, range_dict):
'start' : range_start,
'stop' : str(ip_address(e) -1)
}
+
+ if 'option' in range_dict:
+ r['option'] = range_dict['option']
+
# On the next run our address range will start one address after
# the exclude address
range_start = str(ip_address(e) + 1)
@@ -104,6 +108,10 @@ def dhcp_slice_range(exclude_list, range_dict):
'start': str(ip_address(e) + 1),
'stop': str(range_stop)
}
+
+ if 'option' in range_dict:
+ r['option'] = range_dict['option']
+
if not (ip_address(r['start']) > ip_address(r['stop'])):
output.append(r)
else:
diff --git a/src/conf_mode/service_ipoe-server.py b/src/conf_mode/service_ipoe-server.py
index c7e3ef033..a14d4b5b6 100755
--- a/src/conf_mode/service_ipoe-server.py
+++ b/src/conf_mode/service_ipoe-server.py
@@ -31,6 +31,7 @@ from vyos.accel_ppp_util import verify_accel_ppp_ip_pool
from vyos.accel_ppp_util import verify_accel_ppp_authentication
from vyos import ConfigError
from vyos import airbag
+
airbag.enable()
@@ -52,7 +53,9 @@ def get_config(config=None):
if dict_search('client_ip_pool', ipoe):
# Multiple named pools require ordered values T5099
- ipoe['ordered_named_pools'] = get_pools_in_order(dict_search('client_ip_pool', ipoe))
+ ipoe['ordered_named_pools'] = get_pools_in_order(
+ dict_search('client_ip_pool', ipoe)
+ )
ipoe['server_type'] = 'ipoe'
return ipoe
@@ -68,11 +71,23 @@ def verify(ipoe):
for interface, iface_config in ipoe['interface'].items():
verify_interface_exists(ipoe, interface, warning_only=True)
if 'client_subnet' in iface_config and 'vlan' in iface_config:
- raise ConfigError('Option "client-subnet" and "vlan" are mutually exclusive, '
- 'use "client-ip-pool" instead!')
- if 'vlan_mon' in iface_config and not 'vlan' in iface_config:
+ raise ConfigError(
+ 'Options "client-subnet" and "vlan" are mutually exclusive, '
+ 'use "client-ip-pool" instead!'
+ )
+ if 'vlan_mon' in iface_config and 'vlan' not in iface_config:
raise ConfigError('Option "vlan-mon" requires "vlan" to be set!')
+ if 'lua_username' in iface_config:
+ if 'lua_file' not in ipoe:
+ raise ConfigError(
+ 'Option "lua-username" requires "lua-file" to be set!'
+ )
+ if dict_search('authentication.mode', ipoe) != 'radius':
+ raise ConfigError(
+ 'Can configure username with Lua script only for RADIUS authentication'
+ )
+
verify_accel_ppp_authentication(ipoe, local_users=False)
verify_accel_ppp_ip_pool(ipoe)
verify_accel_ppp_name_servers(ipoe)
@@ -88,14 +103,15 @@ def generate(ipoe):
render(ipoe_conf, 'accel-ppp/ipoe.config.j2', ipoe)
if dict_search('authentication.mode', ipoe) == 'local':
- render(ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2',
- ipoe, permission=0o640)
+ render(
+ ipoe_chap_secrets, 'accel-ppp/chap-secrets.ipoe.j2', ipoe, permission=0o640
+ )
return None
def apply(ipoe):
systemd_service = 'accel-ppp@ipoe.service'
- if ipoe == None:
+ if ipoe is None:
call(f'systemctl stop {systemd_service}')
for file in [ipoe_conf, ipoe_chap_secrets]:
if os.path.exists(file):
@@ -103,7 +119,10 @@ def apply(ipoe):
return None
- call(f'systemctl reload-or-restart {systemd_service}')
+ # Accel-pppd does not do soft-reload correctly.
+ # Most of the changes require restarting the service
+ call(f'systemctl restart {systemd_service}')
+
if __name__ == '__main__':
try:
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 8825cc16a..f8cc87507 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -471,6 +471,12 @@ start ()
touch /tmp/vyos.smoketest.debug
fi
+ # Cleanup PKI CAs
+ if [ -d /usr/local/share/ca-certificates/vyos ]; then
+ rm -f /usr/local/share/ca-certificates/vyos/*.crt
+ update-ca-certificates >/dev/null 2>&1
+ fi
+
log_action_begin_msg "Mounting VyOS Config"
# ensure the vyatta_configdir supports a large number of inodes since
# the config hierarchy is often inode-bound (instead of size).
diff --git a/src/migration-scripts/qos/2-to-3 b/src/migration-scripts/qos/2-to-3
new file mode 100644
index 000000000..284fe828e
--- /dev/null
+++ b/src/migration-scripts/qos/2-to-3
@@ -0,0 +1,34 @@
+# Copyright 2024 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+from vyos.configtree import ConfigTree
+
+
+def migrate(config: ConfigTree) -> None:
+ base = ['qos', 'policy', 'cake']
+ if config.exists(base):
+ for policy in config.list_nodes(base):
+ if config.exists(base + [policy, 'flow-isolation']):
+ isolation = None
+ for isol in config.list_nodes(base + [policy, 'flow-isolation']):
+ if isol == 'nat':
+ config.set(base + [policy, 'flow-isolation-nat'])
+ else:
+ isolation = isol
+
+ config.delete(base + [policy, 'flow-isolation'])
+
+ if isolation:
+ config.set(base + [policy, 'flow-isolation'], value=isolation)
diff --git a/src/op_mode/show_configuration_files.sh b/src/op_mode/show_configuration_files.sh
deleted file mode 100755
index ad8e0747c..000000000
--- a/src/op_mode/show_configuration_files.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-# Wrapper script for the show configuration files command
-find ${vyatta_sysconfdir}/config/ \
- -type f \
- -not -name ".*" \
- -not -name "config.boot.*" \
- -printf "%f\t(%Tc)\t%T@\n" \
- | sort -r -k3 \
- | awk -F"\t" '{printf ("%-20s\t%s\n", $1,$2) ;}'
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index cb23642dc..d977ba2cb 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -56,6 +56,7 @@ else:
SOCKET_PATH = 'ipc:///run/vyos-configd.sock'
MAX_MSG_SIZE = 65535
+PAD_MSG_SIZE = 6
# Response error codes
R_SUCCESS = 1
@@ -256,25 +257,14 @@ def process_node_data(config, data, _last: bool = False) -> tuple[int, str]:
def send_result(sock, err, msg):
- msg_size = min(MAX_MSG_SIZE, len(msg)) if msg else 0
-
- err_rep = err.to_bytes(1, byteorder=sys.byteorder)
- logger.debug(f'Sending reply: {err}')
- sock.send(err_rep)
-
- # size req from vyshim client
- size_req = sock.recv().decode()
- logger.debug(f'Received request: {size_req}')
- msg_size_rep = hex(msg_size).encode()
- sock.send(msg_size_rep)
- logger.debug(f'Sending reply: {msg_size}')
-
- if msg_size > 0:
- # send req is sent from vyshim client only if msg_size > 0
- send_req = sock.recv().decode()
- logger.debug(f'Received request: {send_req}')
- sock.send(msg.encode())
- logger.debug('Sending reply with output')
+ msg = msg if msg else ''
+ msg_size = min(MAX_MSG_SIZE, len(msg))
+
+ err_rep = err.to_bytes(1)
+ msg_size_rep = f'{msg_size:#0{PAD_MSG_SIZE}x}'
+
+ logger.debug(f'Sending reply: error_code {err} with output')
+ sock.send_multipart([err_rep, msg_size_rep.encode(), msg.encode()])
write_stdout_log(script_stdout_log, msg)
diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c
index 68e6c4015..1eb653cbf 100644
--- a/src/shim/vyshim.c
+++ b/src/shim/vyshim.c
@@ -119,21 +119,17 @@ int main(int argc, char* argv[])
zmq_send(requester, string_node_data_msg, strlen(string_node_data_msg), 0);
zmq_recv(requester, error_code, 1, 0);
- debug_print("Received node data receipt\n");
+ debug_print("Received node data receipt with error_code\n");
char msg_size_str[7];
- zmq_send(requester, "msg_size", 8, 0);
zmq_recv(requester, msg_size_str, 6, 0);
msg_size_str[6] = '\0';
int msg_size = (int)strtol(msg_size_str, NULL, 16);
debug_print("msg_size: %d\n", msg_size);
- if (msg_size > 0) {
- zmq_send(requester, "send", 4, 0);
- char *msg = s_recv_string(requester, msg_size);
- printf("%s", msg);
- free(msg);
- }
+ char *msg = s_recv_string(requester, msg_size);
+ printf("%s", msg);
+ free(msg);
free(string_node_data_msg);