summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarcus Hoff <marcus.hoff@ring2.dk>2020-09-20 11:59:32 +0200
committerMarcus Hoff <marcus.hoff@ring2.dk>2020-09-20 11:59:32 +0200
commit45b30adfaaec7065f768d04085138a75a76ed376 (patch)
treea9cd47236468077141eee56068ba23027b0d4c7d /src
parent46fb580fa0131f6815bbcfc95631654f6fe999a8 (diff)
parente0797331774a02ca23e8363fbcfe5a49fb3ca2bd (diff)
downloadvyos-1x-45b30adfaaec7065f768d04085138a75a76ed376.tar.gz
vyos-1x-45b30adfaaec7065f768d04085138a75a76ed376.zip
Merge remote-tracking branch 'upstream/current' into current
Diffstat (limited to 'src')
-rwxr-xr-xsrc/completion/list_interfaces.py16
-rwxr-xr-xsrc/conf_mode/dns_forwarding.py1
-rwxr-xr-xsrc/conf_mode/dynamic_dns.py217
-rwxr-xr-xsrc/conf_mode/intel_qat.py145
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py42
-rwxr-xr-xsrc/conf_mode/interfaces-l2tpv3.py5
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py6
-rwxr-xr-xsrc/conf_mode/vpn_openconnect.py (renamed from src/conf_mode/vpn_anyconnect.py)14
-rwxr-xr-xsrc/op_mode/force_mtu_host.sh52
-rwxr-xr-xsrc/op_mode/monitor_bandwidth_test.sh30
-rwxr-xr-xsrc/op_mode/openconnect-control.py (renamed from src/op_mode/anyconnect-control.py)10
-rwxr-xr-xsrc/op_mode/show_version.py5
-rwxr-xr-xsrc/services/vyos-configd21
-rw-r--r--src/shim/.gitignore2
-rw-r--r--src/shim/vyshim.c10
15 files changed, 302 insertions, 274 deletions
diff --git a/src/completion/list_interfaces.py b/src/completion/list_interfaces.py
index e27281433..b19b90156 100755
--- a/src/completion/list_interfaces.py
+++ b/src/completion/list_interfaces.py
@@ -1,16 +1,28 @@
#!/usr/bin/env python3
+#
+# Copyright (C) 2019-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.ifconfig import Section
-
def matching(feature):
for section in Section.feature(feature):
for intf in Section.interfaces(section):
yield intf
-
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-t", "--type", type=str, help="List interfaces of specific type")
diff --git a/src/conf_mode/dns_forwarding.py b/src/conf_mode/dns_forwarding.py
index 51631dc16..53bc37882 100755
--- a/src/conf_mode/dns_forwarding.py
+++ b/src/conf_mode/dns_forwarding.py
@@ -127,6 +127,7 @@ def verify(conf, dns):
f'Error: No server configured for domain {domain}'))
no_system_nameservers = False
+ conf.set_level([])
if dns['system'] and not (
conf.exists(['system', 'name-server']) or
conf.exists(['system', 'name-servers-dhcp']) ):
diff --git a/src/conf_mode/dynamic_dns.py b/src/conf_mode/dynamic_dns.py
index 57c910a68..93e995b78 100755
--- a/src/conf_mode/dynamic_dns.py
+++ b/src/conf_mode/dynamic_dns.py
@@ -17,14 +17,13 @@
import os
from sys import exit
-from copy import deepcopy
-from stat import S_IRUSR, S_IWUSR
from vyos.config import Config
-from vyos import ConfigError
-from vyos.util import call
+from vyos.configdict import dict_merge
from vyos.template import render
-
+from vyos.util import call
+from vyos.xml import defaults
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -45,197 +44,101 @@ default_service_protocol = {
'zoneedit': 'zoneedit1'
}
-default_config_data = {
- 'interfaces': [],
- 'deleted': False
-}
-
def get_config(config=None):
- dyndns = deepcopy(default_config_data)
if config:
conf = config
else:
conf = Config()
- base_level = ['service', 'dns', 'dynamic']
+ base_level = ['service', 'dns', 'dynamic']
if not conf.exists(base_level):
- dyndns['deleted'] = True
- return dyndns
-
- for interface in conf.list_nodes(base_level + ['interface']):
- node = {
- 'interface': interface,
- 'rfc2136': [],
- 'service': [],
- 'web_skip': '',
- 'web_url': ''
- }
-
- # set config level to e.g. "service dns dynamic interface eth0"
- conf.set_level(base_level + ['interface', interface])
- # Handle RFC2136 - Dynamic Updates in the Domain Name System
- for rfc2136 in conf.list_nodes(['rfc2136']):
- rfc = {
- 'name': rfc2136,
- 'keyfile': '',
- 'record': [],
- 'server': '',
- 'ttl': '600',
- 'zone': ''
- }
-
- # set config level
- conf.set_level(base_level + ['interface', interface, 'rfc2136', rfc2136])
-
- if conf.exists(['key']):
- rfc['keyfile'] = conf.return_value(['key'])
-
- if conf.exists(['record']):
- rfc['record'] = conf.return_values(['record'])
-
- if conf.exists(['server']):
- rfc['server'] = conf.return_value(['server'])
-
- if conf.exists(['ttl']):
- rfc['ttl'] = conf.return_value(['ttl'])
-
- if conf.exists(['zone']):
- rfc['zone'] = conf.return_value(['zone'])
-
- node['rfc2136'].append(rfc)
-
- # set config level to e.g. "service dns dynamic interface eth0"
- conf.set_level(base_level + ['interface', interface])
- # Handle DynDNS service providers
- for service in conf.list_nodes(['service']):
- srv = {
- 'provider': service,
- 'host': [],
- 'login': '',
- 'password': '',
- 'protocol': '',
- 'server': '',
- 'custom' : False,
- 'zone' : ''
- }
-
- # set config level
- conf.set_level(base_level + ['interface', interface, 'service', service])
-
- # preload protocol from default service mapping
- if service in default_service_protocol.keys():
- srv['protocol'] = default_service_protocol[service]
- else:
- srv['custom'] = True
-
- if conf.exists(['login']):
- srv['login'] = conf.return_value(['login'])
-
- if conf.exists(['host-name']):
- srv['host'] = conf.return_values(['host-name'])
-
- if conf.exists(['protocol']):
- srv['protocol'] = conf.return_value(['protocol'])
-
- if conf.exists(['password']):
- srv['password'] = conf.return_value(['password'])
-
- if conf.exists(['server']):
- srv['server'] = conf.return_value(['server'])
-
- if conf.exists(['zone']):
- srv['zone'] = conf.return_value(['zone'])
- elif srv['provider'] == 'cloudflare':
- # default populate zone entry with bar.tld if
- # host-name is foo.bar.tld
- srv['zone'] = srv['host'][0].split('.',1)[1]
-
- node['service'].append(srv)
-
- # Set config back to appropriate level for these options
- conf.set_level(base_level + ['interface', interface])
-
- # Additional settings in CLI
- if conf.exists(['use-web', 'skip']):
- node['web_skip'] = conf.return_value(['use-web', 'skip'])
-
- if conf.exists(['use-web', 'url']):
- node['web_url'] = conf.return_value(['use-web', 'url'])
-
- # set config level back to top level
- conf.set_level(base_level)
-
- dyndns['interfaces'].append(node)
+ return None
+
+ dyndns = conf.get_config_dict(base_level, key_mangling=('-', '_'), get_first_key=True)
+
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ for interface in dyndns['interface']:
+ if 'service' in dyndns['interface'][interface]:
+ # 'Autodetect' protocol used by DynDNS service
+ for service in dyndns['interface'][interface]['service']:
+ if service in default_service_protocol:
+ dyndns['interface'][interface]['service'][service].update(
+ {'protocol' : default_service_protocol.get(service)})
+ else:
+ dyndns['interface'][interface]['service'][service].update(
+ {'custom': ''})
+
+ if 'rfc2136' in dyndns['interface'][interface]:
+ default_values = defaults(base_level + ['interface', 'rfc2136'])
+ for rfc2136 in dyndns['interface'][interface]['rfc2136']:
+ dyndns['interface'][interface]['rfc2136'][rfc2136] = dict_merge(
+ default_values, dyndns['interface'][interface]['rfc2136'][rfc2136])
return dyndns
def verify(dyndns):
# bail out early - looks like removal from running config
- if dyndns['deleted']:
+ if not dyndns:
return None
# A 'node' corresponds to an interface
- for node in dyndns['interfaces']:
+ if 'interface' not in dyndns:
+ return None
+ for interface in dyndns['interface']:
# RFC2136 - configuration validation
- for rfc2136 in node['rfc2136']:
- if not rfc2136['record']:
- raise ConfigError('Set key for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
+ if 'rfc2136' in dyndns['interface'][interface]:
+ for rfc2136, config in dyndns['interface'][interface]['rfc2136'].items():
- if not rfc2136['zone']:
- raise ConfigError('Set zone for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
+ for tmp in ['record', 'zone', 'server', 'key']:
+ if tmp not in config:
+ raise ConfigError(f'"{tmp}" required for rfc2136 based '
+ f'DynDNS service on "{interface}"')
- if not rfc2136['keyfile']:
- raise ConfigError('Set keyfile for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
- else:
- if not os.path.isfile(rfc2136['keyfile']):
- raise ConfigError('Keyfile for service "{0}" to send DDNS updates for interface "{1}" does not exist'.format(rfc2136['name'], node['interface']))
-
- if not rfc2136['server']:
- raise ConfigError('Set server for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
+ if not os.path.isfile(config['key']):
+ raise ConfigError(f'"key"-file not found for rfc2136 based '
+ f'DynDNS service on "{interface}"')
# DynDNS service provider - configuration validation
- for service in node['service']:
- if not service['host']:
- raise ConfigError('Set host-name for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
+ if 'service' in dyndns['interface'][interface]:
+ for service, config in dyndns['interface'][interface]['service'].items():
+ error_msg = f'required for DynDNS service "{service}" on "{interface}"'
+ if 'host_name' not in config:
+ raise ConfigError(f'"host-name" {error_msg}')
- if not service['login']:
- raise ConfigError('Set login for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
+ if 'login' not in config:
+ raise ConfigError(f'"login" (username) {error_msg}')
- if not service['password']:
- raise ConfigError('Set password for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
+ if 'password' not in config:
+ raise ConfigError(f'"password" {error_msg}')
- if service['custom'] is True:
- if not service['protocol']:
- raise ConfigError('Set protocol for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
+ if 'zone' in config:
+ if service != 'cloudflare':
+ raise ConfigError(f'"zone" option only supported with CloudFlare')
- if not service['server']:
- raise ConfigError('Set server for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
+ if 'custom' in config:
+ if 'protocol' not in config:
+ raise ConfigError(f'"protocol" {error_msg}')
- if service['zone']:
- if service['provider'] != 'cloudflare':
- raise ConfigError('Zone option not allowed for "{0}", it can only be used for CloudFlare'.format(service['provider']))
+ if 'server' not in config:
+ raise ConfigError(f'"server" {error_msg}')
return None
def generate(dyndns):
# bail out early - looks like removal from running config
- if dyndns['deleted']:
+ if not dyndns:
return None
- render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns)
-
- # Config file must be accessible only by its owner
- os.chmod(config_file, S_IRUSR | S_IWUSR)
-
+ render(config_file, 'dynamic-dns/ddclient.conf.tmpl', dyndns, trim_blocks=True, permission=0o600)
return None
def apply(dyndns):
- if dyndns['deleted']:
+ if not dyndns:
call('systemctl stop ddclient.service')
if os.path.exists(config_file):
os.unlink(config_file)
-
else:
call('systemctl restart ddclient.service')
diff --git a/src/conf_mode/intel_qat.py b/src/conf_mode/intel_qat.py
index 1e5101a9f..86dbccaf0 100755
--- a/src/conf_mode/intel_qat.py
+++ b/src/conf_mode/intel_qat.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-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,94 +13,87 @@
#
# 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 os
import re
+from sys import exit
+
from vyos.config import Config
-from vyos import ConfigError
from vyos.util import popen, run
-
+from vyos import ConfigError
from vyos import airbag
airbag.enable()
-# Define for recovering
-gl_ipsec_conf = None
+qat_init_script = '/etc/init.d/qat_service'
def get_config(config=None):
- if config:
- c = config
- else:
- c = Config()
- config_data = {
- 'qat_conf' : None,
- 'ipsec_conf' : None,
- 'openvpn_conf' : None,
- }
-
- if c.exists('system acceleration qat'):
- config_data['qat_conf'] = True
-
- if c.exists('vpn ipsec '):
- gl_ipsec_conf = True
- config_data['ipsec_conf'] = True
-
- if c.exists('interfaces openvpn'):
- config_data['openvpn_conf'] = True
-
- return config_data
-
-# Control configured VPN service which can use QAT
-def vpn_control(action):
- # XXX: Should these commands report failure
- if action == 'restore' and gl_ipsec_conf:
- return run('ipsec start')
- return run(f'ipsec {action}')
-
-def verify(c):
- # Check if QAT service installed
- if not os.path.exists('/etc/init.d/qat_service'):
- raise ConfigError("Warning: QAT init file not found")
-
- if c['qat_conf'] == None:
- return
-
- # Check if QAT device exist
- output, err = popen('lspci -nn', decode='utf-8')
- if not err:
- data = re.findall('(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output)
- #If QAT devices found
- if not data:
- print("\t No QAT acceleration device found")
- sys.exit(1)
-
-def apply(c):
- if c['ipsec_conf']:
+ if config:
+ conf = config
+ else:
+ conf = Config()
+
+ data = {}
+
+ if conf.exists(['system', 'acceleration', 'qat']):
+ data.update({'qat_enable' : ''})
+
+ if conf.exists(['vpn', 'ipsec']):
+ data.update({'ipsec' : ''})
+
+ if conf.exists(['interfaces', 'openvpn']):
+ data.update({'openvpn' : ''})
+
+ return data
+
+
+def vpn_control(action, force_ipsec=False):
+ # XXX: Should these commands report failure?
+ if action == 'restore' and force_ipsec:
+ return run('ipsec start')
+
+ return run(f'ipsec {action}')
+
+
+def verify(qat):
+ if 'qat_enable' not in qat:
+ return
+
+ # Check if QAT service installed
+ if not os.path.exists(qat_init_script):
+ raise ConfigError('QAT init script not found')
+
+ # Check if QAT device exist
+ output, err = popen('lspci -nn', decode='utf-8')
+ if not err:
+ data = re.findall(
+ '(8086:19e2)|(8086:37c8)|(8086:0435)|(8086:6f54)', output)
+ # If QAT devices found
+ if not data:
+ raise ConfigError('No QAT acceleration device found')
+
+def apply(qat):
# Shutdown VPN service which can use QAT
- vpn_control('stop')
+ if 'ipsec' in qat:
+ vpn_control('stop')
+
+ # Enable/Disable QAT service
+ if 'qat_enable' in qat:
+ run(f'{qat_init_script} start')
+ else:
+ run(f'{qat_init_script} stop')
- # Disable QAT service
- if c['qat_conf'] == None:
- run('/etc/init.d/qat_service stop')
- if c['ipsec_conf']:
- vpn_control('start')
- return
+ # Recover VPN service
+ if 'ipsec' in qat:
+ vpn_control('start')
- # Run qat init.d script
- run('/etc/init.d/qat_service start')
- if c['ipsec_conf']:
- # Recovery VPN service
- vpn_control('start')
if __name__ == '__main__':
- try:
- c = get_config()
- verify(c)
- apply(c)
- except ConfigError as e:
- print(e)
- vpn_control('restore')
- sys.exit(1)
+ try:
+ c = get_config()
+ verify(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ vpn_control('restore', force_ipsec=('ipsec' in c))
+ exit(1)
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 16e6e4f6e..a9679b47c 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -29,6 +29,7 @@ from vyos.configverify import verify_source_interface
from vyos.configverify import verify_vlan_config
from vyos.configverify import verify_vrf
from vyos.ifconfig import BondIf
+from vyos.ifconfig import Section
from vyos.validate import is_member
from vyos.validate import has_address_configured
from vyos import ConfigError
@@ -69,31 +70,33 @@ def get_config(config=None):
# into a dictionary - we will use this to add additional information
# later on for wach member
if 'member' in bond and 'interface' in bond['member']:
- # first convert it to a list if only one member is given
- if isinstance(bond['member']['interface'], str):
- bond['member']['interface'] = [bond['member']['interface']]
-
- tmp={}
- for interface in bond['member']['interface']:
- tmp.update({interface: {}})
-
- bond['member']['interface'] = tmp
+ # convert list if member interfaces to a dictionary
+ bond['member']['interface'] = dict.fromkeys(
+ bond['member']['interface'], {})
if 'mode' in bond:
bond['mode'] = get_bond_mode(bond['mode'])
tmp = leaf_node_changed(conf, ['mode'])
- if tmp:
- bond.update({'shutdown_required': ''})
+ if tmp: bond.update({'shutdown_required': {}})
# determine which members have been removed
- tmp = leaf_node_changed(conf, ['member', 'interface'])
- if tmp:
- bond.update({'shutdown_required': ''})
- if 'member' in bond:
- bond['member'].update({'interface_remove': tmp })
- else:
- bond.update({'member': {'interface_remove': tmp }})
+ interfaces_removed = leaf_node_changed(conf, ['member', 'interface'])
+ if interfaces_removed:
+ bond.update({'shutdown_required': {}})
+ if 'member' not in bond:
+ bond.update({'member': {}})
+
+ tmp = {}
+ for interface in interfaces_removed:
+ section = Section.section(interface) # this will be 'ethernet' for 'eth0'
+ if conf.exists(['insterfaces', section, interface, 'disable']):
+ tmp.update({interface : {'disable': ''}})
+ else:
+ tmp.update({interface : {}})
+
+ # also present the interfaces to be removed from the bond as dictionary
+ bond['member'].update({'interface_remove': tmp})
if 'member' in bond and 'interface' in bond['member']:
for interface, interface_config in bond['member']['interface'].items():
@@ -109,8 +112,7 @@ def get_config(config=None):
# bond members must not have an assigned address
tmp = has_address_configured(conf, interface)
- if tmp:
- interface_config.update({'has_address' : ''})
+ if tmp: interface_config.update({'has_address' : ''})
return bond
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 8250a3df8..144cee5fe 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -56,10 +56,11 @@ def get_config(config=None):
# To delete an l2tpv3 interface we need the current tunnel and session-id
if 'deleted' in l2tpv3:
tmp = leaf_node_changed(conf, ['tunnel-id'])
- l2tpv3.update({'tunnel_id': tmp})
+ # leaf_node_changed() returns a list
+ l2tpv3.update({'tunnel_id': tmp[0]})
tmp = leaf_node_changed(conf, ['session-id'])
- l2tpv3.update({'session_id': tmp})
+ l2tpv3.update({'session_id': tmp[0]})
return l2tpv3
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 9861f72db..c6c843e7b 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -33,6 +33,7 @@ from vyos.configverify import verify_vrf
from vyos.ifconfig import WiFiIf
from vyos.template import render
from vyos.util import call
+from vyos.util import vyos_dict_search
from vyos import ConfigError
from vyos import airbag
airbag.enable()
@@ -213,6 +214,11 @@ def generate(wifi):
mac.dialect = mac_unix_expanded
wifi['mac'] = str(mac)
+ # XXX: Jinja2 can not operate on a dictionary key when it starts of with a number
+ if '40mhz_incapable' in (vyos_dict_search('capabilities.ht', wifi) or []):
+ wifi['capabilities']['ht']['fourtymhz_incapable'] = wifi['capabilities']['ht']['40mhz_incapable']
+ del wifi['capabilities']['ht']['40mhz_incapable']
+
# render appropriate new config files depending on access-point or station mode
if wifi['type'] == 'access-point':
render(hostapd_conf.format(**wifi), 'wifi/hostapd.conf.tmpl', wifi, trim_blocks=True)
diff --git a/src/conf_mode/vpn_anyconnect.py b/src/conf_mode/vpn_openconnect.py
index 158e1a117..af8604972 100755
--- a/src/conf_mode/vpn_anyconnect.py
+++ b/src/conf_mode/vpn_openconnect.py
@@ -42,7 +42,7 @@ def get_hash(password):
def get_config():
conf = Config()
- base = ['vpn', 'anyconnect']
+ base = ['vpn', 'openconnect']
if not conf.exists(base):
return None
@@ -61,24 +61,24 @@ def verify(ocserv):
if "mode" in ocserv["authentication"]:
if "local" in ocserv["authentication"]["mode"]:
if not ocserv["authentication"]["local_users"] or not ocserv["authentication"]["local_users"]["username"]:
- raise ConfigError('Anyconect mode local required at leat one user')
+ raise ConfigError('openconnect mode local required at leat one user')
else:
for user in ocserv["authentication"]["local_users"]["username"]:
if not "password" in ocserv["authentication"]["local_users"]["username"][user]:
raise ConfigError(f'password required for user {user}')
else:
- raise ConfigError('anyconnect authentication mode required')
+ raise ConfigError('openconnect authentication mode required')
else:
- raise ConfigError('anyconnect authentication credentials required')
+ raise ConfigError('openconnect authentication credentials required')
# Check ssl
if "ssl" in ocserv:
req_cert = ['ca_cert_file', 'cert_file', 'key_file']
for cert in req_cert:
if not cert in ocserv["ssl"]:
- raise ConfigError('anyconnect ssl {0} required'.format(cert.replace('_', '-')))
+ raise ConfigError('openconnect ssl {0} required'.format(cert.replace('_', '-')))
else:
- raise ConfigError('anyconnect ssl required')
+ raise ConfigError('openconnect ssl required')
# Check network settings
if "network_settings" in ocserv:
@@ -90,7 +90,7 @@ def verify(ocserv):
else:
ocserv["network_settings"]["push_route"] = "default"
else:
- raise ConfigError('anyconnect network settings required')
+ raise ConfigError('openconnect network settings required')
def generate(ocserv):
diff --git a/src/op_mode/force_mtu_host.sh b/src/op_mode/force_mtu_host.sh
new file mode 100755
index 000000000..02955c729
--- /dev/null
+++ b/src/op_mode/force_mtu_host.sh
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+#
+# Module: vyos-show-ram.sh
+# Displays memory usage information in minimalistic format
+#
+# 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 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/>.
+
+target=$1
+interface=$2
+
+# IPv4 header 20 byte + TCP header 20 byte
+ipv4_overhead=40
+
+# IPv6 headter 40 byte + TCP header 20 byte
+ipv6_overhead=60
+
+# If no arguments
+if [[ $# -eq 0 ]] ; then
+ echo "Target host not defined"
+ exit 1
+fi
+
+# If one argument, it's ip address. If 2, the second arg "interface"
+if [[ $# -eq 1 ]] ; then
+ mtu=$(sudo nmap -T4 --script path-mtu -F $target | grep "PMTU" | awk {'print $NF'})
+elif [[ $# -eq 2 ]]; then
+ mtu=$(sudo nmap -T4 -e $interface --script path-mtu -F $target | grep "PMTU" | awk {'print $NF'})
+fi
+
+tcpv4_mss=$(($mtu-$ipv4_overhead))
+tcpv6_mss=$(($mtu-$ipv6_overhead))
+
+echo "
+Recommended maximum values (or less) for target $target:
+---
+MTU: $mtu
+TCP-MSS: $tcpv4_mss
+TCP-MSS_IPv6: $tcpv6_mss
+"
+
diff --git a/src/op_mode/monitor_bandwidth_test.sh b/src/op_mode/monitor_bandwidth_test.sh
new file mode 100755
index 000000000..6da0291c5
--- /dev/null
+++ b/src/op_mode/monitor_bandwidth_test.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# 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/>.
+
+if ipaddrcheck --is-ipv6 $1; then
+ # Set address family to IPv6 when an IPv6 address was specified
+ OPT="-V"
+elif [[ $(dig $1 AAAA +short | grep -v '\.$' | wc -l) -gt 0 ]]; then
+ # CNAME is also part of the dig answer thus we must remove any
+ # CNAME response and only shot the AAAA response(s), this is done
+ # by grep -v '\.$'
+
+ # Set address family to IPv6 when FQDN has at least one AAAA record
+ OPT="-V"
+fi
+
+/usr/bin/iperf $OPT -c $1
+
diff --git a/src/op_mode/anyconnect-control.py b/src/op_mode/openconnect-control.py
index 6382016b7..ef9fe618c 100755
--- a/src/op_mode/anyconnect-control.py
+++ b/src/op_mode/openconnect-control.py
@@ -28,7 +28,7 @@ occtl_socket = '/run/ocserv/occtl.socket'
def show_sessions():
out, code = popen("sudo {0} -j -s {1} show users".format(occtl, occtl_socket),stderr=DEVNULL)
if code:
- sys.exit('Cannot get anyconnect users information')
+ sys.exit('Cannot get openconnect users information')
else:
headers = ["interface", "username", "ip", "remote IP", "RX", "TX", "state", "uptime"]
sessions = json.loads(out)
@@ -38,11 +38,11 @@ def show_sessions():
if len(ses_list) > 0:
print(tabulate(ses_list, headers))
else:
- print("No active anyconnect sessions")
+ print("No active openconnect sessions")
def is_ocserv_configured():
- if not Config().exists_effective('vpn anyconnect'):
- print("vpn anyconnect server is not configured")
+ if not Config().exists_effective('vpn openconnect'):
+ print("vpn openconnect server is not configured")
sys.exit(1)
def main():
@@ -54,7 +54,7 @@ def main():
args = parser.parse_args()
- # Check is IPoE configured
+ # Check is Openconnect server configured
is_ocserv_configured()
if args.action == "restart":
diff --git a/src/op_mode/show_version.py b/src/op_mode/show_version.py
index d0d5c6785..5bbc2e1f1 100755
--- a/src/op_mode/show_version.py
+++ b/src/op_mode/show_version.py
@@ -27,7 +27,6 @@ from sys import exit
from vyos.util import call
parser = argparse.ArgumentParser()
-parser.add_argument("-a", "--all", action="store_true", help="Include individual package versions")
parser.add_argument("-f", "--funny", action="store_true", help="Add something funny to the output")
parser.add_argument("-j", "--json", action="store_true", help="Produce JSON output")
@@ -65,9 +64,5 @@ if __name__ == '__main__':
tmpl = Template(version_output_tmpl)
print(tmpl.render(version_data))
- if args.all:
- print("Package versions:")
- call("dpkg -l")
-
if args.funny:
print(vyos.limericks.get_random())
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 75f84d3df..642952936 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -62,6 +62,8 @@ configd_env_file = '/etc/default/vyos-configd-env'
active_string = ''
session_string = ''
+session_tty = None
+
def key_name_from_file_name(f):
return os.path.splitext(f)[0]
@@ -105,6 +107,14 @@ conf_mode_scripts = dict(zip(imports, modules))
exclude_set = {key_name_from_file_name(f) for f in filenames if f not in include}
include_set = {key_name_from_file_name(f) for f in filenames if f in include}
+def explicit_print(t, m):
+ try:
+ with open(t, 'w') as f:
+ f.write(m)
+ f.write("\n")
+ f.flush()
+ except Exception:
+ pass
def run_script(script, config) -> int:
config.set_level([])
@@ -115,6 +125,7 @@ def run_script(script, config) -> int:
script.apply(c)
except ConfigError as e:
logger.critical(e)
+ explicit_print(session_tty, str(e))
return R_ERROR_COMMIT
except Exception:
return R_ERROR_DAEMON
@@ -122,6 +133,7 @@ def run_script(script, config) -> int:
return R_SUCCESS
def initialization(socket):
+ global session_tty
# Reset config strings:
active_string = ''
session_string = ''
@@ -132,6 +144,15 @@ def initialization(socket):
session_string = socket.recv().decode()
resp = "session"
socket.send(resp.encode())
+ pid_string = socket.recv().decode()
+ resp = "pid"
+ socket.send(resp.encode())
+
+ logger.debug(f"config session pid is {pid_string}")
+ try:
+ session_tty = os.readlink(f"/proc/{pid_string}/fd/1")
+ except FileNotFoundError:
+ session_tty = None
configsource = ConfigSourceString(running_config_text=active_string,
session_config_text=session_string)
diff --git a/src/shim/.gitignore b/src/shim/.gitignore
new file mode 100644
index 000000000..d33538138
--- /dev/null
+++ b/src/shim/.gitignore
@@ -0,0 +1,2 @@
+/mkjson/obj/
+/vyshim
diff --git a/src/shim/vyshim.c b/src/shim/vyshim.c
index 8b6feab99..196e3221e 100644
--- a/src/shim/vyshim.c
+++ b/src/shim/vyshim.c
@@ -162,6 +162,10 @@ int initialization(void* Requester)
double prev_time_value, time_value;
double time_diff;
+ char *pid_val = getenv("VYATTA_CONFIG_TMP");
+ strsep(&pid_val, "_");
+ debug_print("config session pid: %s\n", pid_val);
+
debug_print("Sending init announcement\n");
char *init_announce = mkjson(MKJSON_OBJ, 1,
MKJSON_STRING, "type", "init");
@@ -219,6 +223,12 @@ int initialization(void* Requester)
free(session_str);
+ debug_print("Sending config session pid\n");
+ zmq_send(Requester, pid_val, strlen(pid_val), 0);
+ zmq_recv(Requester, buffer, 16, 0);
+ debug_print("Received pid receipt\n");
+
+
return 0;
}