summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/completion/list_ipoe.py16
-rwxr-xr-xsrc/conf_mode/host_name.py16
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py290
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py7
-rwxr-xr-xsrc/conf_mode/service-ipoe.py93
-rwxr-xr-xsrc/conf_mode/snmp.py20
-rwxr-xr-xsrc/conf_mode/system-login.py8
-rw-r--r--src/etc/systemd/system/pdns-recursor.service.d/override.conf2
-rwxr-xr-xsrc/op_mode/dns_forwarding_reset.py6
-rwxr-xr-xsrc/op_mode/dns_forwarding_statistics.py7
-rwxr-xr-xsrc/op_mode/ipoe-control.py65
-rwxr-xr-xsrc/op_mode/ppp-server-ctrl.py71
-rwxr-xr-xsrc/op_mode/version.py4
-rwxr-xr-xsrc/system/on-dhcp-event.sh2
-rwxr-xr-xsrc/validators/ipv63
15 files changed, 444 insertions, 166 deletions
diff --git a/src/completion/list_ipoe.py b/src/completion/list_ipoe.py
new file mode 100755
index 000000000..c386b46a2
--- /dev/null
+++ b/src/completion/list_ipoe.py
@@ -0,0 +1,16 @@
+#!/usr/bin/env python3
+
+import argparse
+from vyos.util import popen
+
+if __name__ == '__main__':
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--selector', help='Selector: username|ifname|sid', required=True)
+ args = parser.parse_args()
+
+ output, err = popen("accel-cmd -p 2002 show sessions {0}".format(args.selector))
+ if not err:
+ res = output.split("\r\n")
+ # Delete header from list
+ del res[:2]
+ print(' '.join(res))
diff --git a/src/conf_mode/host_name.py b/src/conf_mode/host_name.py
index dd5819f9f..a669580ae 100755
--- a/src/conf_mode/host_name.py
+++ b/src/conf_mode/host_name.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018 VyOS maintainers and contributors
+# Copyright (C) 2018-2020 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
@@ -13,8 +13,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-#
"""
conf-mode script for 'system host-name' and 'system domain-name'.
@@ -33,10 +31,7 @@ import vyos.hostsd_client
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import cmd
-from vyos.util import call
-from vyos.util import run
-
+from vyos.util import cmd, call, run, process_named_running
default_config_data = {
'hostname': 'vyos',
@@ -166,12 +161,11 @@ def apply(config):
call("systemctl restart rsyslog.service")
# If SNMP is running, restart it too
- ret = run("pgrep snmpd")
- if ret == 0:
- call("systemctl restart snmpd.service")
+ if process_named_running('snmpd'):
+ call('systemctl restart snmpd.service')
# restart pdns if it is used
- ret = run('/usr/bin/rec_control ping')
+ ret = run('/usr/bin/rec_control --socket-dir=/run/powerdns ping')
if ret == 0:
call('systemctl restart pdns-recursor.service')
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index b42765586..836deb64b 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -19,7 +19,7 @@ import re
from copy import deepcopy
from sys import exit,stderr
-from ipaddress import IPv4Address,IPv4Network,summarize_address_range
+from ipaddress import ip_address,ip_network,IPv4Address,IPv4Network,IPv6Address,IPv6Network,summarize_address_range
from netifaces import interfaces
from time import sleep
from shutil import rmtree
@@ -53,9 +53,11 @@ default_config_data = {
'ipv6_eui64_prefix': '',
'ipv6_forwarding': 1,
'ipv6_dup_addr_detect': 1,
+ 'ipv6_local_address': [],
+ 'ipv6_remote_address': [],
'ping_restart': '60',
'ping_interval': '10',
- 'local_address': '',
+ 'local_address': [],
'local_address_subnet': '',
'local_host': '',
'local_port': '',
@@ -65,21 +67,30 @@ default_config_data = {
'persistent_tunnel': False,
'protocol': 'udp',
'redirect_gateway': '',
- 'remote_address': '',
+ 'remote_address': [],
'remote_host': [],
'remote_port': '',
'client': [],
'server_domain': '',
'server_max_conn': '',
'server_dns_nameserver': [],
- 'server_pool': False,
+ 'server_pool': True,
'server_pool_start': '',
'server_pool_stop': '',
'server_pool_netmask': '',
'server_push_route': [],
'server_reject_unconfigured': False,
- 'server_subnet': '',
- 'server_topology': 'net30',
+ 'server_subnet': [],
+ 'server_topology': '',
+ 'server_ipv6_dns_nameserver': [],
+ 'server_ipv6_local': '',
+ 'server_ipv6_prefixlen': '',
+ 'server_ipv6_remote': '',
+ 'server_ipv6_pool': True,
+ 'server_ipv6_pool_base': '',
+ 'server_ipv6_pool_prefixlen': '',
+ 'server_ipv6_push_route': [],
+ 'server_ipv6_subnet': [],
'shared_secret_file': '',
'tls': False,
'tls_auth': '',
@@ -119,18 +130,14 @@ def checkCertHeader(header, filename):
def getDefaultServer(network, topology, devtype):
"""
- Gets the default server parameters for a "server" directive.
- Currently only IPv4 routed but may be extended to support bridged and/or IPv6 in the future.
+ Gets the default server parameters for a IPv4 "server" directive.
Logic from openvpn's src/openvpn/helper.c.
Returns a dict with addresses or False if the input parameters were incorrect.
"""
- if not (topology and devtype):
- return False
-
if not (devtype == 'tun' or devtype == 'tap'):
return False
- if not network.prefixlen:
+ if not network.version == 4:
return False
elif (devtype == 'tun' and network.prefixlen > 29) or (devtype == 'tap' and network.prefixlen > 30):
return False
@@ -198,6 +205,11 @@ def get_config():
if intf == openvpn['intf']:
openvpn['bridge_member'].append(intf)
+ # bridged server should not have a pool by default (but can be specified manually)
+ if openvpn['bridge_member']:
+ openvpn['server_pool'] = False
+ openvpn['server_ipv6_pool'] = False
+
# set configuration level
conf.set_level('interfaces openvpn ' + openvpn['intf'])
@@ -275,9 +287,15 @@ def get_config():
# Local IP address of tunnel - even as it is a tag node - we can only work
# on the first address
if conf.exists('local-address'):
- openvpn['local_address'] = conf.list_nodes('local-address')[0]
- if conf.exists('local-address {} subnet-mask'.format(openvpn['local_address'])):
- openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(openvpn['local_address']))
+ for tmp in conf.list_nodes('local-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['local_address'].append(tmp)
+ if conf.exists('local-address {} subnet-mask'.format(tmp)):
+ openvpn['local_address_subnet'] = conf.return_value('local-address {} subnet-mask'.format(tmp))
+ elif tmp_ip.version == 6:
+ # input IPv6 address could be expanded so get the compressed version
+ openvpn['ipv6_local_address'].append(str(tmp_ip))
# Local IP address to accept connections
if conf.exists('local-host'):
@@ -321,7 +339,12 @@ def get_config():
# IP address of remote end of tunnel
if conf.exists('remote-address'):
- openvpn['remote_address'] = conf.return_value('remote-address')
+ for tmp in conf.return_values('remote-address'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['remote_address'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['ipv6_remote_address'].append(str(tmp_ip))
# Remote host to connect to (dynamic if not set)
if conf.exists('remote-host'):
@@ -345,11 +368,18 @@ def get_config():
openvpn['server_topology'] = conf.return_value('server topology')
# Server-mode subnet (from which client IPs are allocated)
+ server_network_v4 = None
+ server_network_v6 = None
if conf.exists('server subnet'):
- # server_network is used later in this function
- server_network = IPv4Network(conf.return_value('server subnet'))
- # convert the network in format: "192.0.2.0 255.255.255.0" for later use in template
- openvpn['server_subnet'] = server_network.with_netmask.replace(r'/', ' ')
+ for tmp in conf.return_values('server subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ server_network_v4 = tmp_ip
+ # convert the network to format: "192.0.2.0 255.255.255.0" for later use in template
+ openvpn['server_subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ server_network_v6 = tmp_ip
+ openvpn['server_ipv6_subnet'].append(str(tmp_ip))
# Client-specific settings
for client in conf.list_nodes('server client'):
@@ -358,7 +388,11 @@ def get_config():
data = {
'name': client,
'disable': False,
- 'ip': '',
+ 'ip': [],
+ 'ipv6_ip': [],
+ 'ipv6_remote': '',
+ 'ipv6_push_route': [],
+ 'ipv6_subnet': [],
'push_route': [],
'subnet': [],
'remote_netmask': ''
@@ -369,16 +403,28 @@ def get_config():
data['disable'] = True
# IP address of the client
- if conf.exists('ip'):
- data['ip'] = conf.return_value('ip')
+ for tmp in conf.return_values('ip'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ data['ip'].append(tmp)
+ elif tmp_ip.version == 6:
+ data['ipv6_ip'].append(str(tmp_ip))
# Route to be pushed to the client
- for network in conf.return_values('push-route'):
- data['push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_push_route'].append(str(tmp_ip))
# Subnet belonging to the client
- for network in conf.return_values('subnet'):
- data['subnet'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('subnet'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ data['subnet'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ data['ipv6_subnet'].append(str(tmp_ip))
# Append to global client list
openvpn['client'].append(data)
@@ -388,16 +434,34 @@ def get_config():
# Server client IP pool
if conf.exists('server client-ip-pool'):
- openvpn['server_pool'] = True
+ conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ip-pool')
+
+ # enable or disable server_pool where necessary
+ # default is enabled, or disabled in bridge mode
+ openvpn['server_pool'] = not conf.exists('disable')
- if conf.exists('server client-ip-pool start'):
- openvpn['server_pool_start'] = conf.return_value('server client-ip-pool start')
+ if conf.exists('start'):
+ openvpn['server_pool_start'] = conf.return_value('start')
- if conf.exists('server client-ip-pool stop'):
- openvpn['server_pool_stop'] = conf.return_value('server client-ip-pool stop')
+ if conf.exists('stop'):
+ openvpn['server_pool_stop'] = conf.return_value('stop')
- if conf.exists('server client-ip-pool netmask'):
- openvpn['server_pool_netmask'] = conf.return_value('server client-ip-pool netmask')
+ if conf.exists('netmask'):
+ openvpn['server_pool_netmask'] = conf.return_value('netmask')
+
+ conf.set_level('interfaces openvpn ' + openvpn['intf'])
+
+ # Server client IPv6 pool
+ if conf.exists('server client-ipv6-pool'):
+ conf.set_level('interfaces openvpn ' + openvpn['intf'] + ' server client-ipv6-pool')
+ openvpn['server_ipv6_pool'] = not conf.exists('disable')
+ if conf.exists('base'):
+ tmp = conf.return_value('base').split('/')
+ openvpn['server_ipv6_pool_base'] = str(IPv6Address(tmp[0]))
+ if 1 < len(tmp):
+ openvpn['server_ipv6_pool_prefixlen'] = tmp[1]
+
+ conf.set_level('interfaces openvpn ' + openvpn['intf'])
# DNS suffix to be pushed to all clients
if conf.exists('server domain-name'):
@@ -409,12 +473,21 @@ def get_config():
# Domain Name Server (DNS)
if conf.exists('server name-server'):
- openvpn['server_dns_nameserver'] = conf.return_values('server name-server')
+ for tmp in conf.return_values('server name-server'):
+ tmp_ip = ip_address(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_dns_nameserver'].append(tmp)
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_dns_nameserver'].append(str(tmp_ip))
# Route to be pushed to all clients
if conf.exists('server push-route'):
- for network in conf.return_values('server push-route'):
- openvpn['server_push_route'].append(IPv4Network(network).with_netmask.replace(r'/', ' '))
+ for tmp in conf.return_values('server push-route'):
+ tmp_ip = ip_network(tmp)
+ if tmp_ip.version == 4:
+ openvpn['server_push_route'].append(tmp_ip.with_netmask.replace(r'/', ' '))
+ elif tmp_ip.version == 6:
+ openvpn['server_ipv6_push_route'].append(str(tmp_ip))
# Reject connections from clients that are not explicitly configured
if conf.exists('server reject-unconfigured-clients'):
@@ -476,25 +549,50 @@ def get_config():
if not openvpn['tls_dh'] and openvpn['tls_key'] and checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
openvpn['tls_dh'] = 'none'
+ # set default server topology to net30
+ if openvpn['mode'] == 'server' and not openvpn['server_topology']:
+ openvpn['server_topology'] = 'net30'
+
# Set defaults where necessary.
- # If any of the input parameters are missing or wrong,
+ # If any of the input parameters are wrong,
# this will return False and no defaults will be set.
- default_server = getDefaultServer(server_network, openvpn['server_topology'], openvpn['type'])
- if default_server:
- # server-bridge doesn't require a pool so don't set defaults for it
- if not openvpn['bridge_member']:
- openvpn['server_pool'] = True
- if not openvpn['server_pool_start']:
- openvpn['server_pool_start'] = default_server['pool_start']
+ if server_network_v4 and openvpn['server_topology'] and openvpn['type']:
+ default_server = None
+ default_server = getDefaultServer(server_network_v4, openvpn['server_topology'], openvpn['type'])
+ if default_server:
+ # server-bridge doesn't require a pool so don't set defaults for it
+ if openvpn['server_pool'] and not openvpn['bridge_member']:
+ if not openvpn['server_pool_start']:
+ openvpn['server_pool_start'] = default_server['pool_start']
- if not openvpn['server_pool_stop']:
- openvpn['server_pool_stop'] = default_server['pool_stop']
+ if not openvpn['server_pool_stop']:
+ openvpn['server_pool_stop'] = default_server['pool_stop']
- if not openvpn['server_pool_netmask']:
- openvpn['server_pool_netmask'] = default_server['pool_netmask']
+ if not openvpn['server_pool_netmask']:
+ openvpn['server_pool_netmask'] = default_server['pool_netmask']
+
+ for client in openvpn['client']:
+ client['remote_netmask'] = default_server['client_remote_netmask']
+
+ if server_network_v6:
+ if not openvpn['server_ipv6_local']:
+ openvpn['server_ipv6_local'] = server_network_v6[1]
+ if not openvpn['server_ipv6_prefixlen']:
+ openvpn['server_ipv6_prefixlen'] = server_network_v6.prefixlen
+ if not openvpn['server_ipv6_remote']:
+ openvpn['server_ipv6_remote'] = server_network_v6[2]
+
+ if openvpn['server_ipv6_pool'] and server_network_v6.prefixlen < 112:
+ if not openvpn['server_ipv6_pool_base']:
+ openvpn['server_ipv6_pool_base'] = server_network_v6[0x1000]
+ if not openvpn['server_ipv6_pool_prefixlen']:
+ openvpn['server_ipv6_pool_prefixlen'] = openvpn['server_ipv6_prefixlen']
for client in openvpn['client']:
- client['remote_netmask'] = default_server['client_remote_netmask']
+ client['ipv6_remote'] = openvpn['server_ipv6_local']
+
+ if openvpn['redirect_gateway']:
+ openvpn['redirect_gateway'] += ' ipv6'
return openvpn
@@ -549,26 +647,45 @@ def verify(openvpn):
raise ConfigError('encryption ncp-ciphers cannot be specified in site-to-site mode, only server or client')
if openvpn['mode'] == 'site-to-site' and not openvpn['bridge_member']:
- if not openvpn['local_address']:
+ if not (openvpn['local_address'] or openvpn['ipv6_local_address']):
raise ConfigError('Must specify "local-address" or "bridge member interface"')
+ if len(openvpn['local_address']) > 1 or len(openvpn['ipv6_local_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "local-address"')
+
+ if len(openvpn['remote_address']) > 1 or len(openvpn['ipv6_remote_address']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 "remote-address"')
+
for host in openvpn['remote_host']:
- if host == openvpn['remote_address']:
+ if host in openvpn['remote_address'] or host in openvpn['ipv6_remote_address']:
raise ConfigError('"remote-address" cannot be the same as "remote-host"')
+ if openvpn['local_address'] and not (openvpn['remote_address'] or openvpn['local_address_subnet']):
+ raise ConfigError('IPv4 "local-address" requires IPv4 "remote-address" or IPv4 "local-address subnet"')
+
+ if openvpn['remote_address'] and not openvpn['local_address']:
+ raise ConfigError('IPv4 "remote-address" requires IPv4 "local-address"')
+
+ if openvpn['ipv6_local_address'] and not openvpn['ipv6_remote_address']:
+ raise ConfigError('IPv6 "local-address" requires IPv6 "remote-address"')
+
+ if openvpn['ipv6_remote_address'] and not openvpn['ipv6_local_address']:
+ raise ConfigError('IPv6 "remote-address" requires IPv6 "local-address"')
+
if openvpn['type'] == 'tun':
- if not openvpn['remote_address']:
+ if not (openvpn['remote_address'] or openvpn['ipv6_remote_address']):
raise ConfigError('Must specify "remote-address"')
- if openvpn['local_address'] == openvpn['remote_address']:
+ if ( (openvpn['local_address'] and openvpn['local_address'] == openvpn['remote_address']) or
+ (openvpn['ipv6_local_address'] and openvpn['ipv6_local_address'] == openvpn['ipv6_remote_address']) ):
raise ConfigError('"local-address" and "remote-address" cannot be the same')
- if openvpn['local_address'] == openvpn['local_host']:
+ if openvpn['local_host'] in openvpn['local_address'] or openvpn['local_host'] in openvpn['ipv6_local_address']:
raise ConfigError('"local-address" cannot be the same as "local-host"')
else:
# checks for client-server or site-to-site bridged
- if openvpn['local_address'] or openvpn['remote_address']:
+ if openvpn['local_address'] or openvpn['ipv6_local_address'] or openvpn['remote_address'] or openvpn['ipv6_remote_address']:
raise ConfigError('Cannot specify "local-address" or "remote-address" in client-server or bridge mode')
#
@@ -590,8 +707,15 @@ def verify(openvpn):
if not openvpn['tls_dh'] and not checkCertHeader('-----BEGIN EC PRIVATE KEY-----', openvpn['tls_key']):
raise ConfigError('Must specify "tls dh-file" when not using EC keys in server mode')
+ if len(openvpn['server_subnet']) > 1 or len(openvpn['server_ipv6_subnet']) > 1:
+ raise ConfigError('Cannot specify more than 1 IPv4 and 1 IPv6 server subnet')
+
+ for client in openvpn['client']:
+ if len(client['ip']) > 1 or len(client['ipv6_ip']) > 1:
+ raise ConfigError(f'Server client "{client["name"]}": cannot specify more than 1 IPv4 and 1 IPv6 IP')
+
if openvpn['server_subnet']:
- subnet = IPv4Network(openvpn['server_subnet'].replace(' ', '/'))
+ subnet = IPv4Network(openvpn['server_subnet'][0].replace(' ', '/'))
if openvpn['type'] == 'tun' and subnet.prefixlen > 29:
raise ConfigError('Server subnets smaller than /29 with device type "tun" are not supported')
@@ -599,14 +723,13 @@ def verify(openvpn):
raise ConfigError('Server subnets smaller than /30 with device type "tap" are not supported')
for client in openvpn['client']:
- if client['ip'] and not IPv4Address(client['ip']) in subnet:
- raise ConfigError(f'Client IP "{client["ip"]}" not in server subnet "{subnet}"')
+ if client['ip'] and not IPv4Address(client['ip'][0]) in subnet:
+ raise ConfigError(f'Client "{client["name"]}" IP {client["ip"][0]} not in server subnet {subnet}')
else:
if not openvpn['bridge_member']:
raise ConfigError('Must specify "server subnet" or "bridge member interface" in server mode')
-
if openvpn['server_pool']:
if not (openvpn['server_pool_start'] and openvpn['server_pool_stop']):
raise ConfigError('Server client-ip-pool requires both start and stop addresses in bridged mode')
@@ -615,17 +738,56 @@ def verify(openvpn):
v4PoolStop = IPv4Address(openvpn['server_pool_stop'])
if v4PoolStart > v4PoolStop:
raise ConfigError(f'Server client-ip-pool start address {v4PoolStart} is larger than stop address {v4PoolStop}')
- if (int(v4PoolStop) - int(v4PoolStart) >= 65536):
- raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop}], maximum is 65536 addresses.')
+
+ v4PoolSize = int(v4PoolStop) - int(v4PoolStart)
+ if v4PoolSize >= 65536:
+ raise ConfigError(f'Server client-ip-pool is too large [{v4PoolStart} -> {v4PoolStop} = {v4PoolSize}], maximum is 65536 addresses.')
v4PoolNets = list(summarize_address_range(v4PoolStart, v4PoolStop))
for client in openvpn['client']:
if client['ip']:
for v4PoolNet in v4PoolNets:
- if IPv4Address(client['ip']) in v4PoolNet:
- print(f'Warning: Client "{client["name"]}" IP {client["ip"]} is in server IP pool, it is not reserved for this client.',
+ if IPv4Address(client['ip'][0]) in v4PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ip"][0]} is in server IP pool, it is not reserved for this client.',
file=stderr)
+ if openvpn['server_ipv6_subnet']:
+ if not openvpn['server_subnet']:
+ raise ConfigError('IPv6 server requires an IPv4 server subnet')
+
+ if openvpn['server_ipv6_pool']:
+ if not openvpn['server_pool']:
+ raise ConfigError('IPv6 server pool requires an IPv4 server pool')
+
+ if int(openvpn['server_ipv6_pool_prefixlen']) >= 112:
+ raise ConfigError('IPv6 server pool must be larger than /112')
+
+ v6PoolStart = IPv6Address(openvpn['server_ipv6_pool_base'])
+ v6PoolStop = IPv6Network((v6PoolStart, openvpn['server_ipv6_pool_prefixlen']), strict=False)[-1] # don't remove the parentheses, it's a 2-tuple
+ v6PoolSize = int(v6PoolStop) - int(v6PoolStart) if int(openvpn['server_ipv6_pool_prefixlen']) > 96 else 65536
+ if v6PoolSize < v4PoolSize:
+ raise ConfigError(f'IPv6 server pool must be at least as large as the IPv4 pool (current sizes: IPv6={v6PoolSize} IPv4={v4PoolSize})')
+
+ v6PoolNets = list(summarize_address_range(v6PoolStart, v6PoolStop))
+ for client in openvpn['client']:
+ if client['ipv6_ip']:
+ for v6PoolNet in v6PoolNets:
+ if IPv6Address(client['ipv6_ip'][0]) in v6PoolNet:
+ print(f'Warning: Client "{client["name"]}" IP {client["ipv6_ip"][0]} is in server IP pool, it is not reserved for this client.',
+ file=stderr)
+
+ else:
+ if openvpn['server_ipv6_push_route']:
+ raise ConfigError('IPv6 push-route requires an IPv6 server subnet')
+
+ for client in openvpn ['client']:
+ if client['ipv6_ip']:
+ raise ConfigError(f'Server client "{client["name"]}" IPv6 IP requires an IPv6 server subnet')
+ if client['ipv6_push_route']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 push-route requires an IPv6 server subnet"')
+ if client['ipv6_subnet']:
+ raise ConfigError(f'Server client "{client["name"]} IPv6 subnet requires an IPv6 server subnet"')
+
else:
# checks for both client and site-to-site go here
if openvpn['server_reject_unconfigured']:
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index c51048aeb..06c2ea29b 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -19,6 +19,7 @@ import netifaces
from sys import exit
from copy import deepcopy
+from netifaces import interfaces
from vyos.config import Config
from vyos.ifconfig import Interface, GREIf, GRETapIf, IPIPIf, IP6GREIf, IPIP6If, IP6IP6If, SitIf, Sit6RDIf
@@ -506,6 +507,12 @@ def verify(conf):
if ipv6_count and not IP6 in kls.ip:
print(f'Should not use IPv6 addresses on tunnel {iftype} {ifname}')
+ # vrf check
+
+ vrf = options['vrf']
+ if vrf and vrf not in interfaces():
+ raise ConfigError(f'VRF "{vrf}" does not exist')
+
# tunnel encapsulation check
convert = {
diff --git a/src/conf_mode/service-ipoe.py b/src/conf_mode/service-ipoe.py
index 3a14d92ef..76aa80a10 100755
--- a/src/conf_mode/service-ipoe.py
+++ b/src/conf_mode/service-ipoe.py
@@ -17,27 +17,18 @@
import os
import re
-from socket import socket, AF_INET, SOCK_STREAM
from sys import exit
from time import sleep
+from stat import S_IRUSR, S_IWUSR, S_IRGRP
from vyos.config import Config
from vyos import ConfigError
-from vyos.util import run
+from vyos.util import call
from vyos.template import render
-ipoe_cnf_dir = r'/etc/accel-ppp/ipoe'
-ipoe_cnf = ipoe_cnf_dir + r'/ipoe.config'
-
-pidfile = r'/var/run/accel_ipoe.pid'
-cmd_port = r'2002'
-
-chap_secrets = ipoe_cnf_dir + '/chap-secrets'
-## accel-pppd -d -c /etc/accel-ppp/pppoe/pppoe.config -p /var/run/accel_pppoe.pid
-
-if not os.path.exists(ipoe_cnf_dir):
- os.makedirs(ipoe_cnf_dir)
+ipoe_conf = '/run/accel-pppd/ipoe.conf'
+ipoe_chap_secrets = '/run/accel-pppd/ipoe.chap-secrets'
def _get_cpu():
@@ -49,33 +40,14 @@ def _get_cpu():
return cpu_cnt
-def _chk_con():
- cnt = 0
- s = socket(AF_INET, SOCK_STREAM)
- while True:
- try:
- s.connect(("127.0.0.1", int(cmd_port)))
- break
- except ConnectionRefusedError:
- sleep(0.5)
- cnt += 1
- if cnt == 100:
- raise("failed to start pppoe server")
- break
-
-
-def _accel_cmd(command):
- return run('/usr/bin/accel-cmd -p {cmd_port} {command}')
-
-##### Inline functions end ####
-
-
def get_config():
c = Config()
if not c.exists(['service', 'ipoe-server']):
return None
- config_data = {}
+ config_data = {
+ 'chap_secrets_file' : ipoe_chap_secrets
+ }
c.set_level(['service', 'ipoe-server'])
config_data['interfaces'] = {}
@@ -215,20 +187,26 @@ def get_config():
return config_data
-def generate(c):
- if c == None or not c:
+def generate(ipoe):
+ if not ipoe:
return None
- c['thread_cnt'] = _get_cpu()
+ dirname = os.path.dirname(ipoe_conf)
+ if not os.path.exists(dirname):
+ os.mkdir(dirname)
- if c['auth']['mech'] == 'local':
- old_umask = os.umask(0o077)
- render(chap_secrets, 'ipoe-server/chap-secrets.tmpl', c, trim_blocks=True)
- os.umask(old_umask)
+ ipoe['thread_cnt'] = _get_cpu()
+ render(ipoe_conf, 'ipoe-server/ipoe.config.tmpl', ipoe, trim_blocks=True)
- render(ipoe_cnf, 'ipoe-server/ipoe.config.tmpl', c, trim_blocks=True)
- # return c ??
- return c
+ if ipoe['auth']['mech'] == 'local':
+ render(ipoe_chap_secrets, 'ipoe-server/chap-secrets.tmpl', ipoe)
+ os.chmod(ipoe_chap_secrets, S_IRUSR | S_IWUSR | S_IRGRP)
+
+ else:
+ if os.path.exists(ipoe_chap_secrets):
+ os.unlink(ipoe_chap_secrets)
+
+ return None
def verify(c):
@@ -280,22 +258,19 @@ def verify(c):
return c
-def apply(c):
- if c == None:
- if os.path.exists(pidfile):
- _accel_cmd('shutdown hard')
- if os.path.exists(pidfile):
- os.remove(pidfile)
+def apply(ipoe):
+ if ipoe == None:
+ call('systemctl stop accel-ppp@ipoe.service')
+
+ if os.path.exists(ipoe_conf):
+ os.unlink(ipoe_conf)
+
+ if os.path.exists(ipoe_chap_secrets):
+ os.unlink(ipoe_chap_secrets)
+
return None
- if not os.path.exists(pidfile):
- ret = run(f'/usr/sbin/accel-pppd -c {ipoe_cnf} -p {pidfile} -d')
- _chk_con()
- if ret != 0 and os.path.exists(pidfile):
- os.remove(pidfile)
- raise ConfigError('accel-pppd failed to start')
- else:
- _accel_cmd('restart')
+ call('systemctl restart accel-ppp@ipoe.service')
if __name__ == '__main__':
diff --git a/src/conf_mode/snmp.py b/src/conf_mode/snmp.py
index d654dcb84..7530da2dc 100755
--- a/src/conf_mode/snmp.py
+++ b/src/conf_mode/snmp.py
@@ -535,23 +535,9 @@ def apply(snmp):
# start SNMP daemon
call("systemctl restart snmpd.service")
- # Passwords are not available immediately in the configuration file,
- # after daemon startup - we wait until they have been processed by
- # snmpd, which we see when a magic line appears in this file.
- while True:
- while not os.path.exists(config_file_user):
- sleep(0.5)
-
- try:
- with open(config_file_user, 'r') as f:
- for line in f:
- # Search for our magic string inside the file
- if 'usmUser' in line:
- break
- except IOError:
- continue
- else:
- break
+ while (call('systemctl -q is-active snmpd.service') != 0):
+ print("service not yet started")
+ sleep(0.5)
# net-snmp is now regenerating the configuration file in the background
# thus we need to re-open and re-read the file as the content changed.
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
index 6008ca0b3..91e2b369f 100755
--- a/src/conf_mode/system-login.py
+++ b/src/conf_mode/system-login.py
@@ -16,6 +16,7 @@
import os
+from crypt import crypt, METHOD_SHA512
from psutil import users
from pwd import getpwall, getpwnam
from stat import S_IRUSR, S_IWUSR, S_IRWXU, S_IRGRP, S_IXGRP
@@ -52,11 +53,6 @@ def get_local_users():
return local_users
-
-def get_crypt_pw(password):
- return cmd(f'/usr/bin/mkpasswd --method=sha-512 {password}')
-
-
def get_config():
login = default_config_data
conf = Config()
@@ -204,7 +200,7 @@ def generate(login):
# calculate users encrypted password
for user in login['add_users']:
if user['password_plaintext']:
- user['password_encrypted'] = get_crypt_pw(user['password_plaintext'])
+ user['password_encrypted'] = crypt(user['password_plaintext'], METHOD_SHA512)
user['password_plaintext'] = ''
# remove old plaintext password
diff --git a/src/etc/systemd/system/pdns-recursor.service.d/override.conf b/src/etc/systemd/system/pdns-recursor.service.d/override.conf
index 602d7b774..ef4dec303 100644
--- a/src/etc/systemd/system/pdns-recursor.service.d/override.conf
+++ b/src/etc/systemd/system/pdns-recursor.service.d/override.conf
@@ -2,4 +2,4 @@
WorkingDirectory=
WorkingDirectory=/run/powerdns
ExecStart=
-ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns
+ExecStart=/usr/sbin/pdns_recursor --daemon=no --write-pid=no --disable-syslog --log-timestamp=no --config-dir=/run/powerdns --socket-dir=/run/powerdns
diff --git a/src/op_mode/dns_forwarding_reset.py b/src/op_mode/dns_forwarding_reset.py
index 8e2ee546c..bfc640a26 100755
--- a/src/op_mode/dns_forwarding_reset.py
+++ b/src/op_mode/dns_forwarding_reset.py
@@ -27,6 +27,8 @@ from sys import exit
from vyos.config import Config
from vyos.util import call
+PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
+
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--all", action="store_true", help="Reset all cache")
parser.add_argument("domain", type=str, nargs="?", help="Domain to reset cache entries for")
@@ -41,11 +43,11 @@ if __name__ == '__main__':
exit(0)
if args.all:
- call("rec_control wipe-cache \'.$\'")
+ call(f"{PDNS_CMD} wipe-cache \'.$\'")
exit(0)
elif args.domain:
- call("rec_control wipe-cache \'{0}$\'".format(args.domain))
+ call(f"{PDNS_CMD} wipe-cache \'{0}$\'".format(args.domain))
else:
parser.print_help()
diff --git a/src/op_mode/dns_forwarding_statistics.py b/src/op_mode/dns_forwarding_statistics.py
index c400a72cd..8ae92beb7 100755
--- a/src/op_mode/dns_forwarding_statistics.py
+++ b/src/op_mode/dns_forwarding_statistics.py
@@ -1,12 +1,12 @@
#!/usr/bin/env python3
import jinja2
-import sys
+from sys import exit
from vyos.config import Config
from vyos.config import cmd
-PDNS_CMD='/usr/bin/rec_control'
+PDNS_CMD='/usr/bin/rec_control --socket-dir=/run/powerdns'
OUT_TMPL_SRC = """
DNS forwarding statistics:
@@ -16,13 +16,12 @@ Cache size: {{ cache_size }} kbytes
"""
-
if __name__ == '__main__':
# Do nothing if service is not configured
c = Config()
if not c.exists_effective('service dns forwarding'):
print("DNS forwarding is not configured")
- sys.exit(0)
+ exit(0)
data = {}
diff --git a/src/op_mode/ipoe-control.py b/src/op_mode/ipoe-control.py
new file mode 100755
index 000000000..7111498b2
--- /dev/null
+++ b/src/op_mode/ipoe-control.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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 argparse
+
+from vyos.config import Config
+from vyos.util import popen, run
+
+cmd_dict = {
+ 'cmd_base' : '/usr/bin/accel-cmd -p 2002 ',
+ 'selector' : ['if', 'username', 'sid'],
+ 'actions' : {
+ 'show_sessions' : 'show sessions',
+ 'show_stat' : 'show stat',
+ 'terminate' : 'teminate'
+ }
+}
+
+def is_ipoe_configured():
+ if not Config().exists_effective('service ipoe-server'):
+ print("Service IPoE is not configured")
+ sys.exit(1)
+
+def main():
+ #parese args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--action', help='Control action', required=True)
+ parser.add_argument('--selector', help='Selector username|ifname|sid', required=False)
+ parser.add_argument('--target', help='Target must contain username|ifname|sid', required=False)
+ args = parser.parse_args()
+
+
+ # Check is IPoE configured
+ is_ipoe_configured()
+
+ if args.action == "restart":
+ run(cmd_dict['cmd_base'] + "restart")
+ sys.exit(0)
+
+ if args.action in cmd_dict['actions']:
+ if args.selector in cmd_dict['selector'] and args.target:
+ run(cmd_dict['cmd_base'] + "{0} {1} {2}".format(args.action, args.selector, args.target))
+ else:
+ output, err = popen(cmd_dict['cmd_base'] + cmd_dict['actions'][args.action], decode='utf-8')
+ if not err:
+ print(output)
+ else:
+ print("IPoE server is not running")
+
+if __name__ == '__main__':
+ main()
diff --git a/src/op_mode/ppp-server-ctrl.py b/src/op_mode/ppp-server-ctrl.py
new file mode 100755
index 000000000..171107b4a
--- /dev/null
+++ b/src/op_mode/ppp-server-ctrl.py
@@ -0,0 +1,71 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2019 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 argparse
+
+from vyos.config import Config
+from vyos.util import popen, DEVNULL
+
+cmd_dict = {
+ 'cmd_base' : '/usr/bin/accel-cmd -p {} ',
+ 'vpn_types' : {
+ 'pppoe' : 2001,
+ 'pptp' : 2003,
+ 'l2tp' : 2004,
+ 'sstp' : 2005
+ },
+ 'conf_proto' : {
+ 'pppoe' : 'service pppoe-server',
+ 'pptp' : 'vpn pptp remote-access',
+ 'l2tp' : 'vpn l2tp remote-access',
+ 'sstp' : 'vpn sstp'
+ }
+}
+
+def is_service_configured(proto):
+ if not Config().exists_effective(cmd_dict['conf_proto'][proto]):
+ print("Service {} is not configured".format(proto))
+ sys.exit(1)
+
+def main():
+ #parese args
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--proto', help='Possible protocols pppoe|pptp|l2tp|sstp', required=True)
+ parser.add_argument('--action', help='Action command', required=True)
+ args = parser.parse_args()
+
+ if args.proto in cmd_dict['vpn_types'] and args.action:
+ # Check is service configured
+ is_service_configured(args.proto)
+
+ if args.action == "show sessions":
+ ses_pattern = " ifname,username,ip,ip6,ip6-dp,calling-sid,rate-limit,state,uptime,rx-bytes,tx-bytes"
+ else:
+ ses_pattern = ""
+
+ output, err = popen(cmd_dict['cmd_base'].format(cmd_dict['vpn_types'][args.proto]) + args.action + ses_pattern, stderr=DEVNULL, decode='utf-8')
+ if not err:
+ print(output)
+ else:
+ print("{} server is not running".format(args.proto))
+
+ else:
+ print("Param --proto and --action required")
+ sys.exit(1)
+
+if __name__ == '__main__':
+ main()
diff --git a/src/op_mode/version.py b/src/op_mode/version.py
index 8599c958f..2791f5e5c 100755
--- a/src/op_mode/version.py
+++ b/src/op_mode/version.py
@@ -53,6 +53,8 @@ def read_file(name):
version_output_tmpl = """
Version: VyOS {{version}}
+Release Train: {{release_train}}
+
Built by: {{built_by}}
Built on: {{built_on}}
Build UUID: {{build_uuid}}
@@ -102,7 +104,7 @@ if __name__ == '__main__':
elif run(""" grep '^overlay /' /proc/mounts >/dev/null """) != 0:
boot_via = "legacy non-image installation"
version_data['boot_via'] = boot_via
-
+
# Get hardware details from DMI
version_data['hardware_vendor'] = read_file('/sys/class/dmi/id/sys_vendor')
diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh
index 5046912a6..385ae460f 100755
--- a/src/system/on-dhcp-event.sh
+++ b/src/system/on-dhcp-event.sh
@@ -63,7 +63,7 @@ if [ $changes -gt 0 ]; then
echo Success
pid=`pgrep pdns_recursor`
if [ -n "$pid" ]; then
- sudo rec_control reload-zones
+ sudo rec_control --socket-dir=/run/powerdns reload-zones
fi
else
echo No changes made
diff --git a/src/validators/ipv6 b/src/validators/ipv6
new file mode 100755
index 000000000..f18d4a63e
--- /dev/null
+++ b/src/validators/ipv6
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+ipaddrcheck --is-ipv6 $1