From a1ed1bf1db1eb23455a3555a6fc0842104efdeb6 Mon Sep 17 00:00:00 2001 From: DmitriyEshenko Date: Tue, 24 Sep 2019 16:48:42 +0000 Subject: [dhcp hostfile-update] T1678 migrate to vyos-hostsd-client --- src/system/on-dhcp-event.sh | 42 +++++------------------------------------- 1 file changed, 5 insertions(+), 37 deletions(-) diff --git a/src/system/on-dhcp-event.sh b/src/system/on-dhcp-event.sh index 02bbd4c3c..70a563d4c 100755 --- a/src/system/on-dhcp-event.sh +++ b/src/system/on-dhcp-event.sh @@ -37,50 +37,18 @@ fi case "$action" in commit) # add mapping for new lease - echo "- new lease event, setting static mapping for host "\ - "$client_fqdn_name (MAC=$client_mac, IP=$client_ip)" - # - # grep fails miserably with \t in the search expression. - # In the following line one is used after $client_search_expr - # followed by a single space - grep -q " $client_search_expr #on-dhcp-event " $file - if [ $? == 0 ]; then - echo pattern found, removing - wc1=`cat $file | wc -l` - sudo sed -i "/ $client_search_expr\t #on-dhcp-event /d" $file - wc2=`cat $file | wc -l` - if [ "$wc1" -eq "$wc2" ]; then - echo No change - fi - else - echo pattern NOT found - fi - - # check if hostname already exists (e.g. a static host mapping) - # if so don't overwrite - grep -q " $client_search_expr " $file + grep -q " $client_search_expr " $file if [ $? == 0 ]; then echo host $client_fqdn_name already exists, exiting exit 1 fi - - line="$client_ip\t $client_fqdn_name\t #on-dhcp-event $client_mac" - sudo sh -c "echo -e '$line' >> $file" - ((changes++)) - echo Entry was added + # add host + /usr/bin/vyos-hostsd-client --add-hosts --tag "DHCP-$client_ip" --host "$client_fqdn_name,$client_ip" ;; release) # delete mapping for released address - echo "- lease release event, deleting static mapping for host $client_fqdn_name" - wc1=`cat $file | wc -l` - sudo sed -i "/ $client_search_expr\t #on-dhcp-event /d" $file - wc2=`cat $file | wc -l` - if [ "$wc1" -eq "$wc2" ]; then - echo No change - else - echo Entry was removed - ((changes++)) - fi + # delete host + /usr/bin/vyos-hostsd-client --delete-hosts --tag "DHCP-$client_ip" ;; *) -- cgit v1.2.3 From d1848a9f9447408deac451edf6392b8351ef6f64 Mon Sep 17 00:00:00 2001 From: DmitriyEshenko Date: Sun, 29 Sep 2019 22:16:58 +0000 Subject: [vyos-hostsd] T1701 Fix check for delete domain-name and domain-search --- src/services/vyos-hostsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index e7ecd8573..5c2ea71c8 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -166,9 +166,9 @@ def delete_name_servers(data, tag): def set_host_name(state, data): if data['host_name']: state['host_name'] = data['host_name'] - if data['domain_name']: + if 'domain_name' in data: state['domain_name'] = data['domain_name'] - if data['search_domains']: + if 'search_domains' in data: state['search_domains'] = data['search_domains'] def get_name_servers(state, tag): -- cgit v1.2.3 From 41aa7bc4c804c0eba36ba6ec10b49b303e8c1df4 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Mon, 30 Sep 2019 16:15:10 +0200 Subject: T1496: use upstream accel-ppp rather then an unmaintained fork --- debian/control | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debian/control b/debian/control index dce463157..f7fafd828 100644 --- a/debian/control +++ b/debian/control @@ -52,8 +52,7 @@ Depends: python3, wireguard, tftpd-hpa, igmpproxy, - vyos-accel-ppp, - vyos-accel-ppp-ipoe-kmod, + accel-ppp, mdns-repeater, udp-broadcast-relay, pdns-recursor, -- cgit v1.2.3 From 387f9bb2f8f11af872f6f78f4b12d7cd20ea8c58 Mon Sep 17 00:00:00 2001 From: vindenesen Date: Thu, 26 Sep 2019 15:33:27 +0200 Subject: [OpenVPN]: T1688: Added aes-gcm encryptions --- interface-definitions/interfaces-openvpn.xml | 22 +++++++++++++++++----- src/conf_mode/interface-openvpn.py | 6 ++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml index fb2564cbd..365d80558 100644 --- a/interface-definitions/interfaces-openvpn.xml +++ b/interface-definitions/interfaces-openvpn.xml @@ -106,7 +106,7 @@ Data Encryption Algorithm - des 3des bf128 bf256 aes128 aes192 aes256 + des 3des bf128 bf256 aes128 aes128gcm aes192 aes192gcm aes256 aes256gcm des @@ -126,18 +126,30 @@ aes128 - AES algorithm with 128-bit key + AES algorithm with 128-bit key CBC + + + aes128gcm + AES algorithm with 128-bit key GCM aes192 - AES algorithm with 192-bit key + AES algorithm with 192-bit key CBC + + + aes192gcm + AES algorithm with 192-bit key GCM aes256 - AES algorithm with 256-bit key + AES algorithm with 256-bit key CBC + + + aes256gcm + AES algorithm with 256-bit key GCM - (des|3des|bf128|bf256|aes128|aes192|aes256) + (des|3des|bf128|bf256|aes128|aes128gcm|aes192|aes192gcm|aes256|aes256gcm) diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index a988e1ab1..d00671a85 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -207,10 +207,16 @@ keysize 128 {%- elif 'bf256' in encryption %} cipher bf-cbc keysize 25 +{%- elif 'aes128gcm' in encryption %} +cipher aes-128-gcm {%- elif 'aes128' in encryption %} cipher aes-128-cbc +{%- elif 'aes192gcm' in encryption %} +cipher aes-192-gcm {%- elif 'aes192' in encryption %} cipher aes-192-cbc +{%- elif 'aes256gcm' in encryption %} +cipher aes-256-gcm {%- elif 'aes256' in encryption %} cipher aes-256-cbc {% endif %} -- cgit v1.2.3 From 9a4f89ad6752d9ad859ae124c97e3e4657f81aad Mon Sep 17 00:00:00 2001 From: vindenesen Date: Mon, 30 Sep 2019 20:12:06 +0200 Subject: [OpenVPN]: T1688: Added check to see if encryption gcm is used in combination with shared-secret-key-file, which is not supported (OpenVPN throws error message) --- src/conf_mode/interface-openvpn.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index d00671a85..5345bf7a2 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -735,6 +735,9 @@ def verify(openvpn): # TLS/encryption # if openvpn['shared_secret_file']: + if openvpn['encryption'] in ['aes128gcm', 'aes192gcm', 'aes256gcm']: + raise ConfigError('GCM encryption with shared-secret-key-file is not supported') + if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['shared_secret_file']): raise ConfigError('Specified shared-secret-key-file "{}" is not valid'.format(openvpn['shared_secret_file'])) -- cgit v1.2.3 From daf2e29e3693a7eb2d8b6fc378d984b9a17d2aa3 Mon Sep 17 00:00:00 2001 From: hagbard Date: Mon, 30 Sep 2019 15:16:45 -0700 Subject: wireguard: T1700 - Wireguard FQDN endpoint doesn't work after reboot --- interface-definitions/interfaces-wireguard.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/interfaces-wireguard.xml b/interface-definitions/interfaces-wireguard.xml index f2a7cc316..727bb8278 100644 --- a/interface-definitions/interfaces-wireguard.xml +++ b/interface-definitions/interfaces-wireguard.xml @@ -5,7 +5,7 @@ WireGuard interface name - 459 + 999 ^wg[0-9]{1,4} -- cgit v1.2.3 From ab6d6ec47c8ea47b2ea05d62b72e2864d7895bd4 Mon Sep 17 00:00:00 2001 From: hagbard Date: Tue, 1 Oct 2019 08:24:05 -0700 Subject: Revert "wireguard: T1700 - Wireguard FQDN endpoint doesn't work after reboot" This reverts commit daf2e29e3693a7eb2d8b6fc378d984b9a17d2aa3. It had unknown side effects, undiscovered during testing --- interface-definitions/interfaces-wireguard.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface-definitions/interfaces-wireguard.xml b/interface-definitions/interfaces-wireguard.xml index 727bb8278..f2a7cc316 100644 --- a/interface-definitions/interfaces-wireguard.xml +++ b/interface-definitions/interfaces-wireguard.xml @@ -5,7 +5,7 @@ WireGuard interface name - 999 + 459 ^wg[0-9]{1,4} -- cgit v1.2.3 From 90ec8f4eacb53be74276b476bfb3ef51da42e72e Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Wed, 18 Sep 2019 11:38:28 -0500 Subject: T1424: Check for http error or redirect, when loading remote files. (ported from vyatta-cfg f051e369) --- python/vyos/remote.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/python/vyos/remote.py b/python/vyos/remote.py index 49936ec08..f8a21f068 100644 --- a/python/vyos/remote.py +++ b/python/vyos/remote.py @@ -121,16 +121,34 @@ def get_remote_config(remote_file): if request['protocol'] in ('scp', 'sftp'): check_and_add_host_key(request['host']) + redirect_opt = '' + + if request['protocol'] in ('http', 'https'): + redirect_opt = '-L' + # Try header first, and look for 'OK' or 'Moved' codes: + curl_cmd = 'curl {0} -q -I {1}'.format(redirect_opt, remote_file) + try: + curl_output = subprocess.check_output(curl_cmd, shell=True, + universal_newlines=True) + except subprocess.CalledProcessError: + sys.exit(1) + + return_vals = re.findall(r'^HTTP\/\d+\.?\d\s+(\d+)\s+(.*)$', + curl_output, re.MULTILINE) + for val in return_vals: + if int(val[0]) not in [200, 301, 302]: + print('HTTP error: {0} {1}'.format(*val)) + sys.exit(1) + if request['user'] and not request['passwd']: curl_cmd = 'curl -# -u {0} {1}'.format(request['user'], remote_file) else: - curl_cmd = 'curl -# {0}'.format(remote_file) + curl_cmd = 'curl {0} -# {1}'.format(redirect_opt, remote_file) - config_file = None try: config_file = subprocess.check_output(curl_cmd, shell=True, universal_newlines=True) - except subprocess.CalledProcessError as err: - print("Called process error: {}.".format(err)) + except subprocess.CalledProcessError: + config_file = None return config_file -- cgit v1.2.3 From f26e8927f83c3a897d4f474762bca9775467e74e Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Fri, 6 Sep 2019 19:16:47 -0500 Subject: T1424: Rewrite the config load script Rewrite of the load functionality of vyatta-load-config.pl, removing the dependency on Vyatta::Config. --- src/helpers/vyos-load-config.py | 90 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100755 src/helpers/vyos-load-config.py diff --git a/src/helpers/vyos-load-config.py b/src/helpers/vyos-load-config.py new file mode 100755 index 000000000..4e6d67efa --- /dev/null +++ b/src/helpers/vyos-load-config.py @@ -0,0 +1,90 @@ +#!/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 . +# +# + +"""Load config file from within config session. +Config file specified by URI or path (without scheme prefix). +Example: load https://somewhere.net/some.config + or + load /tmp/some.config +""" + +import sys +import tempfile +import vyos.defaults +import vyos.remote +from vyos.config import Config, VyOSError +from vyos.migrator import Migrator, MigratorError + +system_config_file = 'config.boot' + +class LoadConfig(Config): + """A subclass for calling 'loadFile'. + This does not belong in config.py, and only has a single caller. + """ + def load_config(self, file_path): + cmd = [self._cli_shell_api, 'loadFile', file_path] + self._run(cmd) + +if len(sys.argv) > 1: + file_name = sys.argv[1] +else: + file_name = system_config_file + +configdir = vyos.defaults.directories['config'] + +protocols = ['scp', 'sftp', 'http', 'https', 'ftp', 'tftp'] + +if any(x in file_name for x in protocols): + config_file = vyos.remote.get_remote_config(file_name) + if not config_file: + sys.exit("No config file by that name.") +else: + canonical_path = '{0}/{1}'.format(configdir, file_name) + try: + with open(canonical_path, 'r') as f: + config_file = f.read() + except OSError as err1: + try: + with open(file_name, 'r') as f: + config_file = f.read() + except OSError as err2: + sys.exit('{0}\n{1}'.format(err1, err2)) + +config = LoadConfig() + +print("Loading configuration from '{}'".format(file_name)) + +with tempfile.NamedTemporaryFile() as fp: + with open(fp.name, 'w') as fd: + fd.write(config_file) + + migration = Migrator(fp.name) + try: + migration.run() + except MigratorError as err: + sys.exit('{}'.format(err)) + + try: + config.load_config(fp.name) + except VyOSError as err: + sys.exit('{}'.format(err)) + +if config.session_changed(): + print("Load complete. Use 'commit' to make changes effective.") +else: + print("No configuration changes to commit.") -- cgit v1.2.3 From cf499f958423919264884e9f1c5c1b593fd9de0e Mon Sep 17 00:00:00 2001 From: hagbard Date: Tue, 1 Oct 2019 12:52:09 -0700 Subject: [wireguard] - T1706: wireguard broken in latest rolling --- src/conf_mode/interface-wireguard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py index 0be8b7b0f..0dcce6b1c 100755 --- a/src/conf_mode/interface-wireguard.py +++ b/src/conf_mode/interface-wireguard.py @@ -224,10 +224,10 @@ def apply(c): intfc.add_addr(ip) # interface mtu - intfc.mtu = int(c['mtu']) + intfc.set_mtu(int(c['mtu'])) # ifalias for snmp from description - intfc.ifalias = str(c['descr']) + intfc.set_alias(str(c['descr'])) # remove peers if c['peer_remove']: @@ -267,7 +267,7 @@ def apply(c): intfc.update() # interface state - intfc.state = c['state'] + intfc.set_state(c['state']) return None -- cgit v1.2.3 From 08b60ba1ccbb12d398a184f0303f2c150a15b918 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 3 Oct 2019 11:56:18 +0200 Subject: OpenVPN: T1689: Add full restart on 'reset openvpn interface ' --- op-mode-definitions/openvpn.xml | 2 +- src/op_mode/reset_openvpn.py | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100755 src/op_mode/reset_openvpn.py diff --git a/op-mode-definitions/openvpn.xml b/op-mode-definitions/openvpn.xml index 368cc9115..d7c4fc101 100644 --- a/op-mode-definitions/openvpn.xml +++ b/op-mode-definitions/openvpn.xml @@ -68,7 +68,7 @@ - sudo kill -SIGUSR1 $(cat /var/run/openvpn/$4.pid) + sudo ${vyos_op_scripts_dir}/reset_openvpn.py $4 diff --git a/src/op_mode/reset_openvpn.py b/src/op_mode/reset_openvpn.py new file mode 100755 index 000000000..7043ac261 --- /dev/null +++ b/src/op_mode/reset_openvpn.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2018 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 . + +import sys +import os + +from psutil import pid_exists +from subprocess import Popen, PIPE +from time import sleep +from netifaces import interfaces + +def get_config_name(intf): + cfg_file = r'/opt/vyatta/etc/openvpn/openvpn-{}.conf'.format(intf) + return cfg_file + +def get_pid_file(intf): + pid_file = r'/var/run/openvpn/{}.pid'.format(intf) + return pid_file + +def subprocess_cmd(command): + p = Popen(command, stdout=PIPE, shell=True) + p.communicate() + +if __name__ == '__main__': + if (len(sys.argv) < 1): + print("Must specify OpenVPN interface name!") + sys.exit(1) + + interface = sys.argv[1] + if os.path.isfile(get_config_name(interface)): + pidfile = '/var/run/openvpn/{}.pid'.format(interface) + if os.path.isfile(pidfile): + pid = 0 + with open(pidfile, 'r') as f: + pid = int(f.read()) + + if pid_exists(pid): + cmd = 'start-stop-daemon --stop --quiet' + cmd += ' --pidfile ' + pidfile + subprocess_cmd(cmd) + + # When stopping OpenVPN we need to wait for the 'old' interface to + # vanish from the Kernel, if it is not gone, OpenVPN will report: + # ERROR: Cannot ioctl TUNSETIFF vtun10: Device or resource busy (errno=16) + while interface in interfaces(): + sleep(0.250) # 250ms + + # re-start OpenVPN process + cmd = 'start-stop-daemon --start --quiet' + cmd += ' --pidfile ' + get_pid_file(interface) + cmd += ' --exec /usr/sbin/openvpn' + # now pass arguments to openvpn binary + cmd += ' --' + cmd += ' --config ' + get_config_name(interface) + + subprocess_cmd(cmd) + else: + print("OpenVPN interface {} does not exist!".format(interface)) + sys.exit(1) -- cgit v1.2.3 From 1182b44e6c90bf58ff0e1cf51a97f31c9c9d3f84 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 3 Oct 2019 22:46:52 +0200 Subject: dhcp-server: T1707: bugfix on subsequent DHCP exclude addresses Moved the code for splicing a DHCP range into its dedicated function as this will later be required again. Having subsequent DHCP exclude addresses e.g. 192.0.2.70 and 192.0.2.71 did not work as the previous algorithm created a range whose start address was after the end address. (cherry picked from commit 0f0f9f2835cf85c1fd3652ec83368528754764cd) --- src/conf_mode/dhcp_server.py | 107 +++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index 3e1381cd0..c5d6b1052 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -253,6 +253,68 @@ default_config_data = { 'shared_network': [], } +def dhcp_slice_range(exclude_list, range_list): + """ + This function is intended to slice a DHCP range. What does it mean? + + Lets assume we have a DHCP range from '192.0.2.1' to '192.0.2.100' + but want to exclude address '192.0.2.74' and '192.0.2.75'. We will + pass an input 'range_list' in the format: + [{'start' : '192.0.2.1', 'stop' : '192.0.2.100' }] + and we will receive an output list of: + [{'start' : '192.0.2.1' , 'stop' : '192.0.2.73' }, + {'start' : '192.0.2.76', 'stop' : '192.0.2.100' }] + The resulting list can then be used in turn to build the proper dhcpd + configuration file. + """ + output = [] + # exclude list must be sorted for this to work + exclude_list = sorted(exclude_list) + for ra in range_list: + range_start = ra['start'] + range_stop = ra['stop'] + range_last_exclude = '' + + for e in exclude_list: + if (ipaddress.ip_address(e) >= ipaddress.ip_address(range_start)) and \ + (ipaddress.ip_address(e) <= ipaddress.ip_address(range_stop)): + range_last_exclude = e + + for e in exclude_list: + if (ipaddress.ip_address(e) >= ipaddress.ip_address(range_start)) and \ + (ipaddress.ip_address(e) <= ipaddress.ip_address(range_stop)): + + # Build new IP address range ending one IP address before exclude address + r = { + 'start' : range_start, + 'stop' : str(ipaddress.ip_address(e) -1) + } + # On the next run our IP address range will start one address after the exclude address + range_start = str(ipaddress.ip_address(e) + 1) + + # on subsequent exclude addresses we can not + # append them to our output + if not (ipaddress.ip_address(r['start']) > ipaddress.ip_address(r['stop'])): + # Everything is fine, add range to result + output.append(r) + + # Take care of last IP address range spanning from the last exclude + # address (+1) to the end of the initial configured range + if ipaddress.ip_address(e) == ipaddress.ip_address(range_last_exclude): + r = { + 'start': str(ipaddress.ip_address(e) + 1), + 'stop': str(range_stop) + } + output.append(r) + else: + # if we have no exclude in the whole range - we just take the range + # as it is + if not range_last_exclude: + if ra not in output: + output.append(ra) + + return output + def get_config(): dhcp = default_config_data conf = Config() @@ -460,51 +522,8 @@ def get_config(): # IP address that needs to be excluded from DHCP lease range if conf.exists('exclude'): - # We have no need to store the exclude addresses. Exclude addresses - # are recalculated into several ranges - exclude = [] subnet['exclude'] = conf.return_values('exclude') - for addr in subnet['exclude']: - exclude.append(ipaddress.ip_address(addr)) - - # sort excluded IP addresses ascending - exclude = sorted(exclude) - - # calculate multipe ranges based on the excluded IP addresses - output = [] - for range in subnet['range']: - range_start = range['start'] - range_stop = range['stop'] - - for i in exclude: - # Excluded IP address must be in out specified range - if (i >= ipaddress.ip_address(range_start)) and (i <= ipaddress.ip_address(range_stop)): - # Build up new IP address range ending one IP address before - # our exclude address - range = { - 'start': str(range_start), - 'stop': str(i - 1) - } - # Our next IP address range will start one address after - # our exclude address - range_start = i + 1 - output.append(range) - - # Take care of last IP address range spanning from the last exclude - # address (+1) to the end of the initial configured range - if i is exclude[-1]: - last = { - 'start': str(i + 1), - 'stop': str(range_stop) - } - output.append(last) - else: - # IP address not inside search range, take range is it is - output.append(range) - - # We successfully build up a new list containing several IP address - # ranges, replace IP address range in our dictionary - subnet['range'] = output + subnet['range'] = dhcp_slice_range(subnet['exclude'], subnet['range']) # Static DHCP leases if conf.exists('static-mapping'): -- cgit v1.2.3 From 56235420984746dc1ce23479257c68db0c2a3d2f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 3 Oct 2019 22:48:20 +0200 Subject: dhcp-server: only import ip_address and ip_network from ipaddress class (cherry picked from commit bdf890cca40157b3f2a2386685e043e0fa220fac) --- src/conf_mode/dhcp_server.py | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index c5d6b1052..a08c39699 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2018 VyOS maintainers and contributors +# Copyright (C) 2018-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 @@ -13,18 +13,16 @@ # # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# -# import sys import os -import ipaddress import jinja2 import socket import struct import vyos.validate +from ipaddress import ip_address, ip_network from vyos.config import Config from vyos import ConfigError @@ -276,33 +274,33 @@ def dhcp_slice_range(exclude_list, range_list): range_last_exclude = '' for e in exclude_list: - if (ipaddress.ip_address(e) >= ipaddress.ip_address(range_start)) and \ - (ipaddress.ip_address(e) <= ipaddress.ip_address(range_stop)): + if (ip_address(e) >= ip_address(range_start)) and \ + (ip_address(e) <= ip_address(range_stop)): range_last_exclude = e for e in exclude_list: - if (ipaddress.ip_address(e) >= ipaddress.ip_address(range_start)) and \ - (ipaddress.ip_address(e) <= ipaddress.ip_address(range_stop)): + if (ip_address(e) >= ip_address(range_start)) and \ + (ip_address(e) <= ip_address(range_stop)): # Build new IP address range ending one IP address before exclude address r = { 'start' : range_start, - 'stop' : str(ipaddress.ip_address(e) -1) + 'stop' : str(ip_address(e) -1) } # On the next run our IP address range will start one address after the exclude address - range_start = str(ipaddress.ip_address(e) + 1) + range_start = str(ip_address(e) + 1) # on subsequent exclude addresses we can not # append them to our output - if not (ipaddress.ip_address(r['start']) > ipaddress.ip_address(r['stop'])): + if not (ip_address(r['start']) > ip_address(r['stop'])): # Everything is fine, add range to result output.append(r) # Take care of last IP address range spanning from the last exclude # address (+1) to the end of the initial configured range - if ipaddress.ip_address(e) == ipaddress.ip_address(range_last_exclude): + if ip_address(e) == ip_address(range_last_exclude): r = { - 'start': str(ipaddress.ip_address(e) + 1), + 'start': str(ip_address(e) + 1), 'stop': str(range_stop) } output.append(r) @@ -389,8 +387,8 @@ def get_config(): conf.set_level('service dhcp-server shared-network-name {0} subnet {1}'.format(network, net)) subnet = { 'network': net, - 'address': str(ipaddress.ip_network(net).network_address), - 'netmask': str(ipaddress.ip_network(net).netmask), + 'address': str(ip_network(net).network_address), + 'netmask': str(ip_network(net).netmask), 'bootfile_name': '', 'bootfile_server': '', 'client_prefix_length': '', @@ -581,7 +579,7 @@ def get_config(): # Option format is: # , , , , , , # where bytes with the value 0 are omitted. - net = ipaddress.ip_network(subnet['static_subnet']) + net = ip_network(subnet['static_subnet']) # add netmask string = str(net.prefixlen) + ',' # add network bytes @@ -701,17 +699,17 @@ def verify(dhcp): raise ConfigError('DHCP range stop address for start {0} is not defined!'.format(start)) # Start address must be inside network - if not ipaddress.ip_address(start) in ipaddress.ip_network(subnet['network']): + if not ip_address(start) in ip_network(subnet['network']): raise ConfigError('DHCP range start address {0} is not in subnet {1}\n' \ 'specified for shared network {2}!'.format(start, subnet['network'], network['name'])) # Stop address must be inside network - if not ipaddress.ip_address(stop) in ipaddress.ip_network(subnet['network']): + if not ip_address(stop) in ip_network(subnet['network']): raise ConfigError('DHCP range stop address {0} is not in subnet {1}\n' \ 'specified for shared network {2}!'.format(stop, subnet['network'], network['name'])) # Stop address must be greater or equal to start address - if not ipaddress.ip_address(stop) >= ipaddress.ip_address(start): + if not ip_address(stop) >= ip_address(start): raise ConfigError('DHCP range stop address {0} must be greater or equal\n' \ 'to the range start address {1}!'.format(stop, start)) @@ -731,7 +729,7 @@ def verify(dhcp): # Exclude addresses must be in bound for exclude in subnet['exclude']: - if not ipaddress.ip_address(exclude) in ipaddress.ip_network(subnet['network']): + if not ip_address(exclude) in ip_network(subnet['network']): raise ConfigError('Exclude IP address {0} is outside of the DHCP lease network {1}\n' \ 'under shared network {2}!'.format(exclude, subnet['network'], network['name'])) @@ -754,7 +752,7 @@ def verify(dhcp): if mapping['ip_address']: # Static IP address must be in bound - if not ipaddress.ip_address(mapping['ip_address']) in ipaddress.ip_network(subnet['network']): + if not ip_address(mapping['ip_address']) in ip_network(subnet['network']): raise ConfigError('DHCP static lease IP address {0} for static mapping {1}\n' \ 'in shared network {2} is outside DHCP lease subnet {3}!' \ .format(mapping['ip_address'], mapping['name'], network['name'], subnet['network'])) @@ -777,9 +775,9 @@ def verify(dhcp): subnets.append(subnet['network']) # Check for overlapping subnets - net = ipaddress.ip_network(subnet['network']) + net = ip_network(subnet['network']) for n in subnets: - net2 = ipaddress.ip_network(n) + net2 = ip_network(n) if (net != net2): if net.overlaps(net2): raise ConfigError('DHCP conflicting subnet ranges: {0} overlaps {1}'.format(net, net2)) -- cgit v1.2.3 From 5848a4d6095e6d7bc70e34e0b7b7e2c3d8e3303f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 3 Oct 2019 22:51:34 +0200 Subject: dhcp-server: T1707: remove DHCP static-mappings from address pool Previously when static-mappings have been created the address was still within the DHCP pool resulting in log entries as follows: dhcpd: Dynamic and static leases present for 192.0.2.51 dhcpd: Remove host declaration DMZ_PC2 or remove 192.0.2.51 dhcpd: from the dynamic address pool for DMZ (cherry picked from commit 6f954ab56768af9a07d8a1dc086f54ddefa58da7) --- src/conf_mode/dhcp_server.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/conf_mode/dhcp_server.py b/src/conf_mode/dhcp_server.py index a08c39699..f19bcb250 100755 --- a/src/conf_mode/dhcp_server.py +++ b/src/conf_mode/dhcp_server.py @@ -525,6 +525,7 @@ def get_config(): # Static DHCP leases if conf.exists('static-mapping'): + addresses_for_exclude = [] for mapping in conf.list_nodes('static-mapping'): conf.set_level('service dhcp-server shared-network-name {0} subnet {1} static-mapping {2}'.format(network, net, mapping)) mapping = { @@ -542,6 +543,7 @@ def get_config(): # IP address used for this DHCP client if conf.exists('ip-address'): mapping['ip_address'] = conf.return_value('ip-address') + addresses_for_exclude.append(mapping['ip_address']) # MAC address of requesting DHCP client if conf.exists('mac-address'): @@ -560,6 +562,13 @@ def get_config(): # append static-mapping configuration to subnet list subnet['static_mapping'].append(mapping) + # Now we have all static DHCP leases - we also need to slice them + # out of our DHCP ranges to avoid ISC DHCPd warnings as: + # dhcpd: Dynamic and static leases present for 192.0.2.51. + # dhcpd: Remove host declaration DMZ_PC1 or remove 192.0.2.51 + # dhcpd: from the dynamic address pool for DMZ + subnet['range'] = dhcp_slice_range(addresses_for_exclude, subnet['range']) + # Reset config level to matching hirachy conf.set_level('service dhcp-server shared-network-name {0} subnet {1}'.format(network, net)) -- cgit v1.2.3 From 6ca30cb10784b77dea79b6196f54852c49ed09b6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 15:10:50 +0200 Subject: Jenkins: add current Git commit ID to build description --- Jenkinsfile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 20eb2531d..bcce16dd0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -87,7 +87,8 @@ pipeline { steps { script { dir('build') { - git branch: getGitBranchName(), url: getGitRepoURL() + git branch: getGitBranchName(), + url: getGitRepoURL() } } } @@ -96,7 +97,10 @@ pipeline { steps { script { dir('build') { - sh "dpkg-buildpackage -b -us -uc -tc" + def commitId = sh(returnStdout: true, script: 'git rev-parse --short=11 HEAD').trim() + currentBuild.description = sprintf('Git SHA1: %s', commitId[-11..-1]) + + sh 'dpkg-buildpackage -b -us -uc -tc' } } } -- cgit v1.2.3 From d449e5bf41b568d998f2a2d2cfcdac4c3df4eba5 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 15:18:31 +0200 Subject: Python/ifconfig: T1557: add return in front of self._cmd() calls --- python/vyos/ifconfig.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index a77cde5e7..d4b1a73c1 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -142,7 +142,7 @@ class Interface: # after interface removal no other commands should be allowed # to be called and instead should raise an Exception: cmd = 'ip link del dev {}'.format(self._ifname) - self._cmd(cmd) + return self._cmd(cmd) def get_mtu(self): """ @@ -205,7 +205,7 @@ class Interface: # Assemble command executed on system. Unfortunately there is no way # of altering the MAC address via sysfs cmd = 'ip link set dev {} address {}'.format(self._ifname, mac) - self._cmd(cmd) + return self._cmd(cmd) def set_arp_cache_tmo(self, tmo): @@ -293,7 +293,7 @@ class Interface: # Assemble command executed on system. Unfortunately there is no way # to up/down an interface via sysfs cmd = 'ip link set dev {} {}'.format(self._ifname, state) - self._cmd(cmd) + return self._cmd(cmd) def set_proxy_arp(self, enable): """ @@ -402,7 +402,7 @@ class Interface: else: if not is_intf_addr_assigned(self._ifname, addr): cmd = 'ip addr add "{}" dev "{}"'.format(addr, self._ifname) - self._cmd(cmd) + return self._cmd(cmd) def del_addr(self, addr): """ @@ -433,7 +433,7 @@ class Interface: else: if is_intf_addr_assigned(self._ifname, addr): cmd = 'ip addr del "{}" dev "{}"'.format(addr, self._ifname) - self._cmd(cmd) + return self._cmd(cmd) # replace dhcpv4/v6 with systemd.networkd? def _set_dhcp(self): @@ -470,7 +470,7 @@ class Interface: # now pass arguments to dhclient binary cmd += ' -4 -nw -cf {} -pf {} -lf {} {}'.format( self._dhcp_cfg_file, self._dhcp_pid_file, self._dhcp_lease_file, self._ifname) - self._cmd(cmd) + return self._cmd(cmd) def _del_dhcp(self): @@ -559,7 +559,7 @@ class Interface: # now pass arguments to dhclient binary cmd += ' -6 -nw -cf {} -pf {} -lf {} {}'.format( self._dhcpv6_cfg_file, self._dhcpv6_pid_file, self._dhcpv6_lease_file, self._ifname) - self._cmd(cmd) + return self._cmd(cmd) def _del_dhcpv6(self): @@ -582,8 +582,7 @@ class Interface: return None # stop dhclient - cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format( - self._dhcpv6_pid_file) + cmd = 'start-stop-daemon --stop --quiet --pidfile {}'.format(self._dhcpv6_pid_file) self._cmd(cmd) # accept router announcements on this interface @@ -802,7 +801,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').add_port('eth1') """ cmd = 'ip link set dev {} master {}'.format(interface, self._ifname) - self._cmd(cmd) + return self._cmd(cmd) def del_port(self, interface): """ @@ -813,7 +812,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').del_port('eth1') """ cmd = 'ip link set dev {} nomaster'.format(interface) - self._cmd(cmd) + return self._cmd(cmd) class VLANIf(Interface): """ @@ -972,7 +971,7 @@ class EthernetIf(VLANIf): self._ifname, enable) try: # An exception will be thrown if the settings are not changed - self._cmd(cmd) + return self._cmd(cmd) except CalledProcessError: pass @@ -1376,7 +1375,7 @@ class WireGuardIf(Interface): """ cmd = "wg set {0} peer {1} remove".format( self._ifname, str(peerkey)) - self._cmd(cmd) + return self._cmd(cmd) class VXLANIf(Interface, ): -- cgit v1.2.3 From eddc97b9d12d2f31eba9a479eb538fbd7f0c3e55 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 15:26:55 +0200 Subject: ethernet: T1637: bugfix on wrong dict key in verify() This resulted in a commit error when Bonding interfaces have been involved. --- src/conf_mode/interface-ethernet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 99450b19e..317da5772 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -254,7 +254,7 @@ def verify(eth): for bond in conf.list_nodes('interfaces bonding'): if conf.exists('interfaces bonding ' + bond + ' member interface'): bond_member = conf.return_values('interfaces bonding ' + bond + ' member interface') - if eth['name'] in bond_member: + if eth['intf'] in bond_member: if eth['address']: raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond) -- cgit v1.2.3 From 1257d7851866d42287018b38dd871f279b87286a Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 16:16:50 +0200 Subject: Python/ifconfig: T1712: wait when changing interface state With some interfaces, for example bond vif, it take some time for the state change to really happen. Because of this later code, like starting DHCP client, might not work as expected as get_state() reports the old (real) state. Now when changing state of an interface we are (busy-)waiting up to 12.5 seconds before we inform the user that the interface could not be brought up. This should be more then enough time for any interface to start except when there is really no cable attached. --- python/vyos/ifconfig.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index d4b1a73c1..4ac605b54 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -293,7 +293,21 @@ class Interface: # Assemble command executed on system. Unfortunately there is no way # to up/down an interface via sysfs cmd = 'ip link set dev {} {}'.format(self._ifname, state) - return self._cmd(cmd) + tmp = self._cmd(cmd) + + # better safe then sorry - wait until the interface is really up + # but only for a given period of time to avoid potential deadlocks! + cnt = 0 + while self.get_state() != state: + cnt += 1 + if cnt == 50: + print('Interface {} could not be brought up in time ...'.format(self._ifname)) + break + + # sleep 250ms + sleep(0.250) + + return tmp def set_proxy_arp(self, enable): """ -- cgit v1.2.3 From c305a48bbf874e835435717a2cae456d326f4f18 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 20:11:38 +0200 Subject: ipoe-server: optimize port completion helper to match all others --- interface-definitions/ipoe-server.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface-definitions/ipoe-server.xml b/interface-definitions/ipoe-server.xml index fd84439b5..1fd61c072 100644 --- a/interface-definitions/ipoe-server.xml +++ b/interface-definitions/ipoe-server.xml @@ -50,7 +50,7 @@ vlan - One VLAN per client + One VLAN per client @@ -267,7 +267,7 @@ - + IP address of RADIUS server @@ -352,7 +352,7 @@ Port for Dynamic Authorization Extension server (DM/CoA) - number + 1-65535 port number -- cgit v1.2.3 From 21fe962befb2ebd1625eb7a6c28cb3e9005fe37e Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 6 Oct 2019 20:12:26 +0200 Subject: ipoe-server: XML: run through XMLlint --- interface-definitions/ipoe-server.xml | 324 +++++++++++++++++----------------- 1 file changed, 162 insertions(+), 162 deletions(-) diff --git a/interface-definitions/ipoe-server.xml b/interface-definitions/ipoe-server.xml index 1fd61c072..48f3e0fd9 100644 --- a/interface-definitions/ipoe-server.xml +++ b/interface-definitions/ipoe-server.xml @@ -97,7 +97,7 @@ VLAN ID needs to be between 1 and 4096 - + @@ -106,7 +106,7 @@ (409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2})-(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2}) - + @@ -173,13 +173,13 @@ Format: ipv6prefix/mask,prefix_len (e.g.: fc00:0:1::/48,64 - divides prefix into /64 subnets for clients) - + Format: ipv6prefix/mask,prefix_len (delegates prefix to clients via DHCPv6 prefix delegation - + @@ -211,164 +211,164 @@ Authentication disabled - - - - Network interface the client mac will appear on - - - - - - - - Client mac address allowed to receive an IP address - - h:h:h:h:h:h - Hardware (MAC) address - - - - - - - - - Upload/Download speed limits - - - - - Upload bandwidth limit in kbits/sec - - - - - - - - Download bandwidth limit in kbits/sec - - - - - - - - - - VLAN-ID of the client network - - - - VLAN ID needs to be between 1 and 4096 - - - - - - - - - IP address of RADIUS server - - ipv4 - IP address of RADIUS server - - - - - - Key for accessing the specified server - - - - - Maximum number of simultaneous requests to server (default: unlimited) - - - - - If server doesn't responds mark it as unavailable for this amount of time in seconds - - - - - - - RADIUS settings - - - - - Timeout to wait response from server (seconds) - - - - - Timeout to wait reply for Interim-Update packets. (default 3 seconds) - - - - - Maximum number of tries to send Access-Request/Accounting-Request queries - - - - - Value to send to RADIUS server in NAS-Identifier attribute and to be matched in DM/CoA requests. - - - - - Value to send to RADIUS server in NAS-IP-Address attribute and to be matched in DM/CoA requests. Also DM/CoA server will bind to that address. - - ipv4 - IPv4 address of the DAE Server - - - - - - - - - IPv4 address and port to bind Dynamic Authorization Extension server (DM/CoA) - - - - - IP address for Dynamic Authorization Extension server (DM/CoA) - - ipv4 - IPv4 address of the DAE Server - - - - - - - - - Port for Dynamic Authorization Extension server (DM/CoA) - - 1-65535 - port number - - - - - - - - - Secret for Dynamic Authorization Extension server (DM/CoA) - - - - - - + + + + Network interface the client mac will appear on + + + + + + + + Client mac address allowed to receive an IP address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + Upload/Download speed limits + + + + + Upload bandwidth limit in kbits/sec + + + + + + + + Download bandwidth limit in kbits/sec + + + + + + + + + + VLAN-ID of the client network + + + + VLAN ID needs to be between 1 and 4096 + + + + + + + + + IP address of RADIUS server + + ipv4 + IP address of RADIUS server + + + + + + Key for accessing the specified server + + + + + Maximum number of simultaneous requests to server (default: unlimited) + + + + + If server doesn't responds mark it as unavailable for this amount of time in seconds + + + + + + + RADIUS settings + + + + + Timeout to wait response from server (seconds) + + + + + Timeout to wait reply for Interim-Update packets. (default 3 seconds) + + + + + Maximum number of tries to send Access-Request/Accounting-Request queries + + + + + Value to send to RADIUS server in NAS-Identifier attribute and to be matched in DM/CoA requests. + + + + + Value to send to RADIUS server in NAS-IP-Address attribute and to be matched in DM/CoA requests. Also DM/CoA server will bind to that address. + + ipv4 + IPv4 address of the DAE Server + + + + + + + + + IPv4 address and port to bind Dynamic Authorization Extension server (DM/CoA) + + + + + IP address for Dynamic Authorization Extension server (DM/CoA) + + ipv4 + IPv4 address of the DAE Server + + + + + + + + + Port for Dynamic Authorization Extension server (DM/CoA) + + 1-65535 + port number + + + + + + + + + Secret for Dynamic Authorization Extension server (DM/CoA) + + + + + + -- cgit v1.2.3