From f23d1f10721260872a5b231dcc51e7d05d324e39 Mon Sep 17 00:00:00 2001 From: hagbard Date: Mon, 16 Sep 2019 14:31:35 -0700 Subject: [IPoE] - T1664: Ipoe with bond per vlan don't work --- interface-definitions/ipoe-server.xml | 9 +++++++++ src/conf_mode/ipoe_server.py | 23 +++++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/interface-definitions/ipoe-server.xml b/interface-definitions/ipoe-server.xml index 6c93d3699..fd84439b5 100644 --- a/interface-definitions/ipoe-server.xml +++ b/interface-definitions/ipoe-server.xml @@ -255,6 +255,15 @@ + + + VLAN-ID of the client network + + + + VLAN ID needs to be between 1 and 4096 + + diff --git a/src/conf_mode/ipoe_server.py b/src/conf_mode/ipoe_server.py index ca6b423e5..a60379760 100755 --- a/src/conf_mode/ipoe_server.py +++ b/src/conf_mode/ipoe_server.py @@ -41,7 +41,6 @@ ipoe_config = ''' ### generated by ipoe.py ### [modules] log_syslog -ippool ipoe shaper ipv6pool @@ -50,6 +49,7 @@ ipv6_dhcp {% if auth['mech'] == 'radius' %} radius {% endif -%} +ippool {% if auth['mech'] == 'local' %} chap-secrets {% endif %} @@ -65,7 +65,11 @@ level=5 [ipoe] verbose=1 {% for intfc in interfaces %} +{% if interfaces[intfc]['vlan_mon'] %} +interface=re:{{intfc}}\.\d+,\ +{% else %} interface={{intfc}},\ +{% endif %} shared={{interfaces[intfc]['shared']}},\ mode={{interfaces[intfc]['mode']}},\ ifcfg={{interfaces[intfc]['ifcfg']}},\ @@ -83,8 +87,7 @@ password=csid {%- for intfc in interfaces %} {% if (interfaces[intfc]['shared'] == '0') and (interfaces[intfc]['vlan_mon']) %} -vlan_mon={{interfaces[intfc]['vlan_mon']|join(',')}} -interface=re:{{intfc}}\.(409[0-6]|40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{0,2}) +vlan-mon={{intfc}},{{interfaces[intfc]['vlan_mon']|join(',')}} {% endif %} {% endfor %} @@ -160,16 +163,24 @@ nas-identifier={{auth['radsettings']['nas-identifier']}} tcp=127.0.0.1:2002 ''' -### pppoe chap secrets +### chap secrets chap_secrets_conf = ''' # username server password acceptable local IP addresses shaper {% for aifc in auth['auth_if'] %} {% for mac in auth['auth_if'][aifc] %} {% if (auth['auth_if'][aifc][mac]['up']) and (auth['auth_if'][aifc][mac]['down']) %} +{% if auth['auth_if'][aifc][mac]['vlan'] %} +{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} +{% else %} {{aifc}}\t*\t{{mac.lower()}}\t*\t{{auth['auth_if'][aifc][mac]['down']}}/{{auth['auth_if'][aifc][mac]['up']}} +{% endif %} +{% else %} +{% if auth['auth_if'][aifc][mac]['vlan'] %} +{{aifc}}.{{auth['auth_if'][aifc][mac]['vlan']}}\t*\t{{mac.lower()}}\t* {% else %} {{aifc}}\t*\t{{mac.lower()}}\t* {% endif %} +{% endif %} {% endfor %} {% endfor %} ''' @@ -213,6 +224,7 @@ def accel_cmd(cmd=''): ### chap_secrets file if auth mode local def gen_chap_secrets(c): + tmpl = jinja2.Template(chap_secrets_conf, trim_blocks=True) chap_secrets_txt = tmpl.render(c) old_umask = os.umask(0o077) @@ -296,6 +308,9 @@ def get_config(): config_data['auth']['auth_if'][auth_int][mac] = {} config_data['auth']['auth_if'][auth_int][mac]['up'] = None config_data['auth']['auth_if'][auth_int][mac]['down'] = None + ## client vlan-id + if c.exists('authentication interface ' + auth_int + ' mac-address ' + mac + ' vlan-id'): + config_data['auth']['auth_if'][auth_int][mac]['vlan'] = c.return_value('authentication interface ' + auth_int + ' mac-address ' + mac + ' vlan-id') if c.exists('authentication mode radius'): for rsrv in c.list_nodes('authentication radius-server'): config_data['auth']['radius'][rsrv] = {} -- cgit v1.2.3 From 8423d760a24c81d3b4c067609de403e2f9d95f48 Mon Sep 17 00:00:00 2001 From: Daniil Baturin Date: Tue, 17 Sep 2019 17:21:54 +0200 Subject: T1667: add a script for importing old conf mode command definitions into XML. --- scripts/import-conf-mode-commands | 240 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) create mode 100755 scripts/import-conf-mode-commands diff --git a/scripts/import-conf-mode-commands b/scripts/import-conf-mode-commands new file mode 100755 index 000000000..4bdd5ee03 --- /dev/null +++ b/scripts/import-conf-mode-commands @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# +# build-command-template: converts old style commands definitions to XML +# +# Copyright (C) 2019 VyOS maintainers +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +# USA + + +import os +import re +import sys + +from lxml import etree + + +# Node types +NODE = 0 +LEAF_NODE = 1 +TAG_NODE = 2 + +def parse_command_data(t): + regs = { + 'help': r'\bhelp:(.*)(?:\n|$)', + 'priority': r'\bpriority:(.*)(?:\n|$)', + 'type': r'\btype:(.*)(?:\n|$)' + } + + data = {'multi': False, 'help': ""} + + for r in regs: + try: + data[r] = re.search(regs[r], t).group(1).strip() + except: + data[r] = None + + # val_help is special: there can be multiple instances + val_help_strings = re.findall(r'\bval_help:(.*)(?:\n|$)', t) + val_help = [] + for v in val_help_strings: + try: + fmt, msg = re.match(r'\s*(.*)\s*;\s*(.*)\s*(?:\n|$)', v).groups() + except: + fmt = "" + msg = v + val_help.append((fmt, msg)) + data['val_help'] = val_help + + # multi is on/off + if re.match(r'\bmulti:', t): + data['multi'] = True + + return(data) + +def walk(tree, base_path, name): + path = os.path.join(base_path, name) + + contents = os.listdir(path) + + # Determine node type and create XML element for the node + # Tag node dirs will always have 'node.tag' subdir and 'node.def' file + # Leaf node dirs have nothing but a 'node.def' file + # Everything that doesn't match either of these patterns is a normal node + if contents == ['node.tag', 'node.def']: + print("Creating a tag node from {0}".format(path)) + elem = etree.Element('tagNode') + node_type = TAG_NODE + elif contents == ['node.def']: + print("Creating a leaf node from {0}".format(path)) + elem = etree.Element('leafNode') + node_type = LEAF_NODE + else: + print("Creating a node from {0}".format(path)) + elem = etree.Element('node') + node_type = NODE + + # Read and parse the command definition data (the 'node.def' file) + with open(os.path.join(path, 'node.def'), 'r') as f: + node_def = f.read() + data = parse_command_data(node_def) + + # Import the data into the properties element + props_elem = etree.Element('properties') + + if data['priority']: + # Priority values sometimes come with comments that explain the value choice + try: + prio, prio_comment = re.match(r'\s*(\d+)\s*#(.*)', data['priority']).groups() + except: + prio = data['priority'].strip() + prio_comment = None + prio_elem = etree.Element('priority') + prio_elem.text = prio + props_elem.append(prio_elem) + if prio_comment: + prio_comment_elem = etree.Comment(prio_comment) + props_elem.append(prio_comment_elem) + + if data['multi']: + multi_elem = etree.Element('multi') + props_elem.append(multi_elem) + + if data['help']: + help_elem = etree.Element('help') + help_elem.text = data['help'] + props_elem.append(help_elem) + + # For leaf nodes, absense of a type: tag means they take no values + # For any other nodes, it doesn't mean anything + if not data['type'] and (node_type == LEAF_NODE): + valueless = etree.Element('valueless') + props_elem.append(valueless) + + # There can be only one constraint element in the definition + # Create it now, we'll modify it in the next two cases, then append + constraint_elem = etree.Element('constraint') + has_constraint = False + + if data['val_help']: + for vh in data['val_help']: + vh_elem = etree.Element('valueHelp') + + vh_fmt_elem = etree.Element('format') + # Many commands use special "u32:-" format for ranges + if re.match(r'u32:', vh[0]): + vh_fmt = re.match(r'u32:(.*)', vh[0]).group(1).strip() + + # If valid range of values is specified in val_help, we can automatically + # create a constraint for it + # Extracting it from syntax:expression: would be much more complicated + vh_validator = etree.Element('validator') + vh_validator.set("name", "numeric") + vh_validator.set("argument", "--range {0}".format(vh_fmt)) + constraint_elem.append(vh_validator) + has_constraint = True + else: + vh_fmt = vh[0] + vh_fmt_elem.text = vh_fmt + + vh_help_elem = etree.Element('description') + vh_help_elem.text = vh[1] + + vh_elem.append(vh_fmt_elem) + vh_elem.append(vh_help_elem) + props_elem.append(vh_elem) + + # Translate the "type:" to the new validator system + if data['type']: + t = data['type'] + if t == 'txt': + # Can't infer anything from the generic "txt" type + pass + else: + validator = etree.Element('validator') + if t == 'u32': + validator.set('name', 'numeric') + validator.set('argument', '--non-negative') + elif t == 'ipv4': + validator.set('name', 'ipv4-address') + elif t == 'ipv4net': + validator.set('name', 'ipv4-prefix') + elif t == 'ipv6': + validator.set('name', 'ipv6-address') + elif t == 'ipv6net': + validator.set('name', 'ipv6-prefix') + elif t == 'macaddr': + validator.set('name', 'mac-address') + else: + print("Warning: unsupported type \'{0}\'".format(t)) + validator = None + + if (validator is not None) and (not has_constraint): + # If has_constraint is true, it means a more specific validator + # was already extracted from another option + constraint_elem.append(validator) + has_constraint = True + + if has_constraint: + props_elem.append(constraint_elem) + + elem.append(props_elem) + + elem.set("name", name) + + if node_type != LEAF_NODE: + children = etree.Element('children') + + # Create the next level dir path, + # accounting for the "virtual" node.tag subdir for tag nodes + next_level = path + if node_type == TAG_NODE: + next_level = os.path.join(path, 'node.tag') + + # Walk the subdirs of the next level + for d in os.listdir(next_level): + dp = os.path.join(next_level, d) + if os.path.isdir(dp): + walk(children, next_level, d) + + elem.append(children) + + tree.append(elem) + +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Usage: {0} ".format(sys.argv[0])) + sys.exit(1) + else: + base_path = sys.argv[1] + + root = etree.Element('interfaceDefinition') + contents = os.listdir(base_path) + elem = etree.Element('node') + elem.set('name', os.path.basename(base_path)) + children = etree.Element('children') + + for c in contents: + path = os.path.join(base_path, c) + if os.path.isdir(path): + walk(children, base_path, c) + + elem.append(children) + root.append(elem) + + xml_data = etree.tostring(root, pretty_print=True).decode() + with open('output.xml', 'w') as f: + f.write(xml_data) -- cgit v1.2.3 From ff4e98e38b9610467347b216a2e33c5f1fc53e71 Mon Sep 17 00:00:00 2001 From: hagbard Date: Wed, 18 Sep 2019 09:28:18 -0700 Subject: Fixing autobuild --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index aac051799..78112ce63 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -8,7 +8,7 @@ pipeline { docker { label 'jessie-amd64' args '--privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006 -v /tmp:/tmp' - image 'higebu/vyos-build:current' + image 'vyos/vyos-build:current' } } -- cgit v1.2.3 From 4a2a06f400593107393755777fdd42b57bbaa21b Mon Sep 17 00:00:00 2001 From: John Estabrook Date: Thu, 19 Sep 2019 12:51:14 -0500 Subject: [boot-config-loader] T1622: (bugfix) set gid and write permissions Set gid and permissions so that vyatta-cfg group has access to the active config; fix typo in arg len check; reorganize; add log output. --- python/vyos/configsession.py | 6 +- python/vyos/defaults.py | 1 + src/helpers/vyos-boot-config-loader.py | 153 ++++++++++++++++++++++++--------- 3 files changed, 119 insertions(+), 41 deletions(-) diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py index acbdd3d5f..09fae78a1 100644 --- a/python/vyos/configsession.py +++ b/python/vyos/configsession.py @@ -145,7 +145,8 @@ class ConfigSession(object): self.__run_command([COMMENT] + path + value) def commit(self): - self.__run_command([COMMIT]) + out = self.__run_command([COMMIT]) + return out def discard(self): self.__run_command([DISCARD]) @@ -157,4 +158,5 @@ class ConfigSession(object): return config_data def load_config(self, file_path): - self.__run_command(LOAD_CONFIG + [file_path]) + out = self.__run_command(LOAD_CONFIG + [file_path]) + return out diff --git a/python/vyos/defaults.py b/python/vyos/defaults.py index 85d27d60d..dedb929b4 100644 --- a/python/vyos/defaults.py +++ b/python/vyos/defaults.py @@ -20,6 +20,7 @@ directories = { "config": "/opt/vyatta/etc/config", "current": "/opt/vyatta/etc/config-migrate/current", "migrate": "/opt/vyatta/etc/config-migrate/migrate", + "log": "/var/log/vyatta", } cfg_group = 'vyattacfg' diff --git a/src/helpers/vyos-boot-config-loader.py b/src/helpers/vyos-boot-config-loader.py index 06c95765f..7c81a4c3c 100755 --- a/src/helpers/vyos-boot-config-loader.py +++ b/src/helpers/vyos-boot-config-loader.py @@ -18,41 +18,72 @@ import os import sys +import pwd +import grp import subprocess import traceback +from datetime import datetime +from vyos.defaults import directories from vyos.configsession import ConfigSession, ConfigSessionError from vyos.configtree import ConfigTree STATUS_FILE = '/tmp/vyos-config-status' TRACE_FILE = '/tmp/boot-config-trace' -session = ConfigSession(os.getpid(), 'vyos-boot-config-loader') -env = session.get_session_env() +CFG_GROUP = 'vyattacfg' -default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default' - -if len(sys.argv) < 1: - print("Must be called with argument.") - sys.exit(1) +if 'log' in directories: + LOG_DIR = directories['log'] else: - file_name = sys.argv[1] + LOG_DIR = '/var/log/vyatta' + +LOG_FILE = LOG_DIR + '/vyos-boot-config-loader.log' + +try: + with open('/proc/cmdline', 'r') as f: + cmdline = f.read() + if 'vyos-debug' in cmdline: + os.environ['VYOS_DEBUG'] = 'yes' +except Exception as e: + print('{0}'.format(e)) def write_config_status(status): - with open(STATUS_FILE, 'w') as f: - f.write('{0}\n'.format(status)) + try: + with open(STATUS_FILE, 'w') as f: + f.write('{0}\n'.format(status)) + except Exception as e: + print('{0}'.format(e)) def trace_to_file(trace_file_name): - with open(trace_file_name, 'w') as trace_file: - traceback.print_exc(file=trace_file) + try: + with open(trace_file_name, 'w') as trace_file: + traceback.print_exc(file=trace_file) + except Exception as e: + print('{0}'.format(e)) + +def failsafe(config_file_name): + fail_msg = """ + !!!!! + There were errors loading the configuration + Please examine the errors in + {0} + and correct + !!!!! + """.format(TRACE_FILE) + + print(fail_msg, file=sys.stderr) + + users = [x[0] for x in pwd.getpwall()] + if 'vyos' in users: + return -def failsafe(): try: - with open(default_file_name, 'r') as f: + with open(config_file_name, 'r') as f: config_file = f.read() except Exception as e: print("Catastrophic: no default config file " - "'{0}'".format(default_file_name)) + "'{0}'".format(config_file_name)) sys.exit(1) config = ConfigTree(config_file) @@ -73,29 +104,73 @@ def failsafe(): except subprocess.CalledProcessError as e: sys.exit("{0}".format(e)) - with open('/etc/motd', 'a+') as f: - f.write('\n\n') - f.write('!!!!!\n') - f.write('There were errors loading the initial configuration;\n') - f.write('please examine the errors in {0} and correct.' - '\n'.format(TRACE_FILE)) - f.write('!!!!!\n\n') +if __name__ == '__main__': + if len(sys.argv) < 2: + print("Must specify boot config file.") + sys.exit(1) + else: + file_name = sys.argv[1] -try: - with open(file_name, 'r') as f: - config_file = f.read() -except Exception as e: - write_config_status(1) - failsafe() - trace_to_file(TRACE_FILE) - sys.exit("{0}".format(e)) + # Set user and group options, so that others will be able to commit + # Currently, the only caller does 'sg CFG_GROUP', but that may change + cfg_group = grp.getgrnam(CFG_GROUP) + os.setgid(cfg_group.gr_gid) -try: - session.load_config(file_name) - session.commit() - write_config_status(0) -except ConfigSessionError as e: - write_config_status(1) - failsafe() - trace_to_file(TRACE_FILE) - sys.exit(1) + # Need to set file permissions to 775 so that every vyattacfg group + # member has write access to the running config + os.umask(0o002) + + session = ConfigSession(os.getpid(), 'vyos-boot-config-loader') + env = session.get_session_env() + + default_file_name = env['vyatta_sysconfdir'] + '/config.boot.default' + + try: + with open(file_name, 'r') as f: + config_file = f.read() + except Exception: + write_config_status(1) + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + try: + time_begin_load = datetime.now() + load_out = session.load_config(file_name) + time_end_load = datetime.now() + time_begin_commit = datetime.now() + commit_out = session.commit() + time_end_commit = datetime.now() + write_config_status(0) + except ConfigSessionError: + # If here, there is no use doing session.discard, as we have no + # recoverable config environment, and will only throw an error + write_config_status(1) + failsafe(default_file_name) + trace_to_file(TRACE_FILE) + sys.exit(1) + + time_elapsed_load = time_end_load - time_begin_load + time_elapsed_commit = time_end_commit - time_begin_commit + + try: + if not os.path.exists(LOG_DIR): + os.mkdir(LOG_DIR) + with open(LOG_FILE, 'a') as f: + f.write('\n\n') + f.write('{0} Begin config load\n' + ''.format(time_begin_load)) + f.write(load_out) + f.write('{0} End config load\n' + ''.format(time_end_load)) + f.write('Elapsed time for config load: {0}\n' + ''.format(time_elapsed_load)) + f.write('{0} Begin config commit\n' + ''.format(time_begin_commit)) + f.write(commit_out) + f.write('{0} End config commit\n' + ''.format(time_end_commit)) + f.write('Elapsed time for config commit: {0}\n' + ''.format(time_elapsed_commit)) + except Exception as e: + print('{0}'.format(e)) -- cgit v1.2.3 From 61f59ee0e8a50fc15f0899fda828d9d0ea0b0ad6 Mon Sep 17 00:00:00 2001 From: vindenesen Date: Thu, 19 Sep 2019 19:52:06 +0200 Subject: Added setting for tls-auth. Added check for if tls_cert and tls_key was defined. --- interface-definitions/interfaces-openvpn.xml | 5 +++++ src/conf_mode/interface-openvpn.py | 27 ++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml index d282a8773..05970f2d9 100644 --- a/interface-definitions/interfaces-openvpn.xml +++ b/interface-definitions/interfaces-openvpn.xml @@ -518,6 +518,11 @@ Transport Layer Security (TLS) options + + + File containing tls static key for tls-auth + + File containing certificate for Certificate Authority (CA) diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index 34c094862..7b3e57d7d 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -171,6 +171,10 @@ crl-verify {{ tls_crl }} dh {{ tls_dh }} {% endif %} +{%- if tls_auth %} +tls-auth {{tls_auth}} +{% endif %} + {%- if 'active' in tls_role %} tls-client {%- elif 'passive' in tls_role %} @@ -277,6 +281,7 @@ default_config_data = { 'server_topology': '', 'shared_secret_file': '', 'tls': False, + 'tls_auth': '', 'tls_ca_cert': '', 'tls_cert': '', 'tls_crl': '', @@ -532,6 +537,11 @@ def get_config(): if conf.exists('server reject-unconfigured-clients'): openvpn['server_reject_unconfigured'] = True + # File containing TLS auth static key + if conf.exists('tls auth-file'): + openvpn['tls_auth'] = conf.return_value('tls auth-file') + openvpn['tls'] = True + # File containing certificate for Certificate Authority (CA) if conf.exists('tls ca-cert-file'): openvpn['tls_ca_cert'] = conf.return_value('tls ca-cert-file') @@ -714,11 +724,17 @@ def verify(openvpn): if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_ca_cert']): raise ConfigError('Specified ca-cert-file "{}" is invalid'.format(openvpn['tls_ca_cert'])) - if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']): - raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert'])) + if openvpn['tls_auth']: + if not checkCertHeader('-----BEGIN OpenVPN Static key V1-----', openvpn['tls_auth']): + raise ConfigError('Specified auth-file "{}" is invalid'.format(openvpn['tls_auth'])) + + if openvpn['tls_cert']: + if not checkCertHeader('-----BEGIN CERTIFICATE-----', openvpn['tls_cert']): + raise ConfigError('Specified cert-file "{}" is invalid'.format(openvpn['tls_cert'])) - if not checkCertHeader('-----BEGIN (?:RSA )?PRIVATE KEY-----', openvpn['tls_key']): - raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key'])) + if openvpn['tls_key']: + if not checkCertHeader('-----BEGIN (?:RSA )?PRIVATE KEY-----', openvpn['tls_key']): + raise ConfigError('Specified key-file "{}" is not valid'.format(openvpn['tls_key'])) if openvpn['tls_crl']: if not checkCertHeader('-----BEGIN X509 CRL-----', openvpn['tls_crl']): @@ -730,7 +746,8 @@ def verify(openvpn): if openvpn['tls_role']: if openvpn['mode'] in ['client', 'server']: - raise ConfigError('Cannot specify "tls role" in client-server mode') + if not openvpn['tls_auth']: + raise ConfigError('Cannot specify "tls role" in client-server mode') if openvpn['tls_role'] == 'active': if openvpn['protocol'] == 'tcp-passive': -- cgit v1.2.3 From 9334c9428c4dcf8d575bfb50d6a33d10b67b5e14 Mon Sep 17 00:00:00 2001 From: vindenesen Date: Thu, 19 Sep 2019 20:31:58 +0200 Subject: OpenVPN - Added setting for minimum tls version --- interface-definitions/interfaces-openvpn.xml | 23 +++++++++++++++++++++++ src/conf_mode/interface-openvpn.py | 9 +++++++++ 2 files changed, 32 insertions(+) diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml index d282a8773..39fa8e6a6 100644 --- a/interface-definitions/interfaces-openvpn.xml +++ b/interface-definitions/interfaces-openvpn.xml @@ -543,6 +543,29 @@ File containing this host's private key + + + Specify the minimum required TLS version + + 1.0 1.1 1.2 + + + 1.0 + TLS v1.0 + + + 1.1 + TLS v1.1 + + + 1.2 + TLS v1.2 + + + (1.0|1.1|1.2) + + + File containing this host's private key diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index 34c094862..495ddfdf5 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -167,6 +167,10 @@ key {{ tls_key }} crl-verify {{ tls_crl }} {% endif %} +{%- if tls_version_min %} +tls-version-min {{tls_version_min}} +{% endif %} + {%- if tls_dh %} dh {{ tls_dh }} {% endif %} @@ -283,6 +287,7 @@ default_config_data = { 'tls_dh': '', 'tls_key': '', 'tls_role': '', + 'tls_version_min': '', 'type': 'tun', 'uid': user, 'gid': group, @@ -562,6 +567,10 @@ def get_config(): openvpn['tls_role'] = conf.return_value('tls role') openvpn['tls'] = True + # Minimum required TLS version + if conf.exists('tls minimum-tls-version'): + openvpn['tls_version_min'] = conf.return_value('tls minimum-tls-version') + if conf.exists('shared-secret-key-file'): openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file') -- cgit v1.2.3 From eb9c6ff745fc5d4e23c224a441874ae6fcf97ac5 Mon Sep 17 00:00:00 2001 From: hagbard Date: Thu, 19 Sep 2019 13:16:56 -0700 Subject: [wireguard] - T1672: Wireguard keys not automatically moved - due to the named keys feature keys reside in named directories - adding a check if the variable VYOS_TAGNODE_VALUE has content --- src/conf_mode/interface-wireguard.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/conf_mode/interface-wireguard.py b/src/conf_mode/interface-wireguard.py index d51a7a08d..4ae3251fe 100755 --- a/src/conf_mode/interface-wireguard.py +++ b/src/conf_mode/interface-wireguard.py @@ -26,12 +26,16 @@ from vyos.config import Config from vyos import ConfigError from vyos.ifconfig import WireGuardIf -ifname = str(os.environ['VYOS_TAGNODE_VALUE']) -intfc = WireGuardIf(ifname) +try: + ifname = str(os.environ['VYOS_TAGNODE_VALUE']) + intfc = WireGuardIf(ifname) +except KeyError: + print("Interface not specified") + sys.exit(1) kdir = r'/config/auth/wireguard' -def check_kmod(): +def _check_kmod(): if not os.path.exists('/sys/module/wireguard'): sl.syslog(sl.LOG_NOTICE, "loading wirguard kmod") if os.system('sudo modprobe wireguard') != 0: @@ -39,6 +43,19 @@ def check_kmod(): raise ConfigError("modprobe wireguard failed") +def _migrate_default_keys(): + if os.path.exists('{}/private.key'.format(kdir)) and not os.path.exists('{}/default/private.key'.format(kdir)): + sl.syslog(sl.LOG_NOTICE, "migrate keypair to default") + old_umask = os.umask(0o027) + location = '{}/default'.format(kdir) + subprocess.call(['sudo mkdir -p ' + location], shell=True) + subprocess.call(['sudo chgrp vyattacfg ' + location], shell=True) + subprocess.call(['sudo chmod 750 ' + location], shell=True) + os.rename('{}/private.key'.format(kdir),'{}/private.key'.format(location)) + os.rename('{}/public.key'.format(kdir),'{}/public.key'.format(location)) + os.umask(old_umask) + + def get_config(): c = Config() if not c.exists('interfaces wireguard'): @@ -257,7 +274,8 @@ def apply(c): if __name__ == '__main__': try: - check_kmod() + _check_kmod() + _migrate_default_keys() c = get_config() verify(c) apply(c) -- cgit v1.2.3 From 0387cae19c28d1d5b8ccf45596841ba44a2eefc9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Sep 2019 21:26:40 +0200 Subject: bonding: T1614: allow adding disabled interfaces to bond An interface can only be added in disabled state to a bond (ensured via ifconfig.py). Also interfaces can be disabled during runtime in a bond which is supported by the Linux Kernel - so why should be add a restriction here? makes no sense. --- src/conf_mode/interface-bonding.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py index f0a33beff..ac3e1b867 100755 --- a/src/conf_mode/interface-bonding.py +++ b/src/conf_mode/interface-bonding.py @@ -279,11 +279,6 @@ def verify(bond): raise ConfigError('can not enslave interface {} which already ' \ 'belongs to {}'.format(intf, tmp)) - # we can not add disabled slave interfaces to our bond - if conf.exists('interfaces ethernet ' + intf + ' disable'): - raise ConfigError('can not enslave disabled interface {}' \ - .format(intf)) - # can not add interfaces with an assigned address to a bond if conf.exists('interfaces ethernet ' + intf + ' address'): raise ConfigError('can not enslave interface {} which has an address ' \ -- cgit v1.2.3 From 6e21be23be126af259f07bb23e4893470b5cb2fb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Sep 2019 22:20:46 +0200 Subject: Python/ifconfig: T1666: re-activate physical interfaces on bond deletion When a bond member gets deleted, all members are placed in A/D state even when they are enabled in the CLI. --- python/vyos/ifconfig.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 0793fad39..750a7cc70 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1057,6 +1057,36 @@ class BondIf(EthernetIf): def __init__(self, ifname): super().__init__(ifname, type='bond') + def remove(self): + """ + Remove interface from operating system. Removing the interface + deconfigures all assigned IP addresses and clear possible DHCP(v6) + client processes. + Example: + >>> from vyos.ifconfig import Interface + >>> i = Interface('eth0') + >>> i.remove() + """ + # when a bond member gets deleted, all members are placed in A/D state + # even when they are enabled inside CLI. This will make the config + # and system look async. + slave_list = [] + for s in self.get_slaves(): + slave = { + 'ifname' : s, + 'state': Interface(s).state + } + slave_list.append(slave) + + # remove bond master which places members in disabled state + super().remove() + + # replicate previous interface state before bond destruction back to + # physical interface + for slave in slave_list: + i = Interface(slave['ifname']) + i.state = slave['state'] + @property def xmit_hash_policy(self): """ -- cgit v1.2.3 From fec6e5b5dbd6282518cf9835f0e8b334b52b6839 Mon Sep 17 00:00:00 2001 From: kroy Date: Thu, 19 Sep 2019 19:28:24 -0500 Subject: T1638: generated hosts file fix for proper FQDN resolution --- src/services/vyos-hostsd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/vyos-hostsd b/src/services/vyos-hostsd index 8f70eb4e9..e7ecd8573 100755 --- a/src/services/vyos-hostsd +++ b/src/services/vyos-hostsd @@ -43,7 +43,7 @@ hosts_tmpl_source = """ # Local host 127.0.0.1 localhost -127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }}{% endif %} +127.0.1.1 {{ host_name }}{% if domain_name %}.{{ domain_name }} {{ host_name }}{% endif %} # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback -- cgit v1.2.3 From 81617be4869483abb4a921d8c14f01794649ab57 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 20 Sep 2019 08:19:18 +0200 Subject: openvpn: T1548: add validator for TLS cert files --- interface-definitions/interfaces-openvpn.xml | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml index 05970f2d9..df9b4026f 100644 --- a/interface-definitions/interfaces-openvpn.xml +++ b/interface-definitions/interfaces-openvpn.xml @@ -521,31 +521,73 @@ File containing tls static key for tls-auth + + file + File in /config/auth directory + + + + File containing certificate for Certificate Authority (CA) + + file + File in /config/auth directory + + + + File containing certificate for this host + + file + File in /config/auth directory + + + + File containing certificate revocation list (CRL) for this host + + file + File in /config/auth directory + + + + File containing Diffie Hellman parameters (server only) + + file + File in /config/auth directory + + + + File containing this host's private key + + file + File in /config/auth directory + + + + -- cgit v1.2.3 From 87500058e11f6846a5ba18dfa17ea685bcdca5ae Mon Sep 17 00:00:00 2001 From: vindenesen Date: Fri, 20 Sep 2019 13:52:44 +0200 Subject: OpenVPN - changed tls-minimum-version to tls-version-min --- interface-definitions/interfaces-openvpn.xml | 2 +- src/conf_mode/interface-openvpn.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/interface-definitions/interfaces-openvpn.xml b/interface-definitions/interfaces-openvpn.xml index 39fa8e6a6..f11f27e23 100644 --- a/interface-definitions/interfaces-openvpn.xml +++ b/interface-definitions/interfaces-openvpn.xml @@ -543,7 +543,7 @@ File containing this host's private key - + Specify the minimum required TLS version diff --git a/src/conf_mode/interface-openvpn.py b/src/conf_mode/interface-openvpn.py index 495ddfdf5..984410eb1 100755 --- a/src/conf_mode/interface-openvpn.py +++ b/src/conf_mode/interface-openvpn.py @@ -568,8 +568,8 @@ def get_config(): openvpn['tls'] = True # Minimum required TLS version - if conf.exists('tls minimum-tls-version'): - openvpn['tls_version_min'] = conf.return_value('tls minimum-tls-version') + if conf.exists('tls tls-version-min'): + openvpn['tls_version_min'] = conf.return_value('tls tls-version-min') if conf.exists('shared-secret-key-file'): openvpn['shared_secret_file'] = conf.return_value('shared-secret-key-file') -- cgit v1.2.3 From 4ae44c074e15fa102718d5a4512c23db447d6dc8 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 7 Sep 2019 14:24:30 +0200 Subject: Python/ifconfig: T1557: rename EthernetIf -> VLANIf An Ethernet Interface will provide additional functionality (link speed/duplex) which is not available for a Bond Interface, but both share the same VLAN capabilities. --- python/vyos/ifconfig.py | 15 +++++++++------ src/conf_mode/interface-bonding.py | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 750a7cc70..524c0f276 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -997,8 +997,11 @@ class BridgeIf(Interface): .format(self._ifname, interface), priority) -class EthernetIf(Interface): - +class VLANIf(Interface): + """ + This class handels the creation and removal of a VLAN interface. It serves + as base class for BondIf and EthernetIf. + """ def __init__(self, ifname, type=None): super().__init__(ifname, type) @@ -1012,7 +1015,7 @@ class EthernetIf(Interface): This function creates both 802.1q and 802.1ad (Q-in-Q) interfaces. Proto parameter is used to indicate VLAN type. - A new object of type EthernetIf is returned once the interface has been + A new object of type VLANIf is returned once the interface has been created. """ vlan_ifname = self._ifname + '.' + str(vlan_id) @@ -1031,7 +1034,7 @@ class EthernetIf(Interface): # return new object mapping to the newly created interface # we can now work on this object for e.g. IP address setting # or interface description and so on - return EthernetIf(vlan_ifname) + return VLANIf(vlan_ifname) def del_vlan(self, vlan_id): """ @@ -1040,11 +1043,11 @@ class EthernetIf(Interface): client processes. """ vlan_ifname = self._ifname + '.' + str(vlan_id) - tmp = EthernetIf(vlan_ifname) + tmp = VLANIf(vlan_ifname) tmp.remove() -class BondIf(EthernetIf): +class BondIf(VLANIf): """ The Linux bonding driver provides a method for aggregating multiple network diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py index ac3e1b867..0d5f9f6b7 100755 --- a/src/conf_mode/interface-bonding.py +++ b/src/conf_mode/interface-bonding.py @@ -22,7 +22,7 @@ from copy import deepcopy from sys import exit from netifaces import interfaces -from vyos.ifconfig import BondIf, EthernetIf +from vyos.ifconfig import BondIf, VLANIf from vyos.configdict import list_diff, vlan_to_dict from vyos.config import Config from vyos import ConfigError @@ -82,7 +82,7 @@ def apply_vlan_config(vlan, config): to a VLAN interface """ - if type(vlan) != type(EthernetIf("lo")): + if type(vlan) != type(VLANIf("lo")): raise TypeError() # update interface description used e.g. within SNMP -- cgit v1.2.3 From a610e328750e3cfc23c29a2cafb40d7ef7082b13 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 7 Sep 2019 12:29:12 +0200 Subject: ethernet: T1637: initial rewrite in XML/Python style --- Makefile | 6 +- interface-definitions/interfaces-ethernet.xml | 856 ++++++++++++++++++++++++++ python/vyos/ifconfig.py | 13 +- src/conf_mode/interface-ethernet.py | 298 +++++++++ 4 files changed, 1170 insertions(+), 3 deletions(-) create mode 100644 interface-definitions/interfaces-ethernet.xml create mode 100755 src/conf_mode/interface-ethernet.py diff --git a/Makefile b/Makefile index 61bc06c47..881fc36b1 100644 --- a/Makefile +++ b/Makefile @@ -11,8 +11,12 @@ interface_definitions: # XXX: delete top level node.def's that now live in other packages rm -f $(TMPL_DIR)/firewall/node.def rm -f $(TMPL_DIR)/interfaces/node.def - rm -f $(TMPL_DIR)/interfaces/bridge/node.tag/ip/node.def rm -f $(TMPL_DIR)/interfaces/bonding/node.tag/ip/node.def + rm -f $(TMPL_DIR)/interfaces/bridge/node.tag/ip/node.def + rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/ip/node.def + rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif/node.tag/ip/node.def + rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif-s/node.tag/ip/node.def + rm -f $(TMPL_DIR)/interfaces/ethernet/node.tag/vif-s/node.tag/vif-c/node.tag/ip/node.def rm -f $(TMPL_DIR)/interfaces/vxlan/node.tag/ip/node.def rm -f $(TMPL_DIR)/protocols/node.def rm -f $(TMPL_DIR)/protocols/static/node.def diff --git a/interface-definitions/interfaces-ethernet.xml b/interface-definitions/interfaces-ethernet.xml new file mode 100644 index 000000000..a2de4aeb3 --- /dev/null +++ b/interface-definitions/interfaces-ethernet.xml @@ -0,0 +1,856 @@ + + + + + + + Ethernet interface name + 318 + + ((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$ + + Invalid Ethernet interface name + + ethN + Ethernet interface name + + + en[ospx]N + Ethernet interface name + + + + + + IP address + + dhcp dhcpv6 + + + ipv4net + IPv4 address and prefix length + + + ipv6net + IPv6 address and prefix length + + + dhcp + Dynamic Host Configuration Protocol + + + dhcpv6 + Dynamic Host Configuration Protocol for IPv6 + + + + (dhcp|dhcpv6) + + + + + + + Interface description + + ^.{1,256}$ + + Interface description too long (limit 256 characters) + + + + + DHCP options + + + + + DHCP client identifier + + + + + DHCP client host name (overrides the system host name) + + + + + + + DHCPv6 options + 319 + + + + + Acquire only config parameters, no address + + + + + + IPv6 "temporary" address + + + + + + + + Disable Ethernet flow control (pause frames) + + + + + + Ignore link state changes + + + + + + Disable this bridge interface + + + + + + Duplex mode + + auto half full + + + auto + Auto negotiation (default) + + + half + Half duplex + + + full + Full duplex + + + (auto|half|full) + + duplex must be auto, half or full + + + + + Media Access Control (MAC) address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + + + ARP cache entry timeout in seconds + + 1-86400 + ARP cache entry timout in seconds (default 30) + + + + + ARP cache entry timeout must be between 1 and 86400 seconds + + + + + Enable proxy-arp on this interface + + + + + + Enable private VLAN proxy ARP on this interface + + + + + + + + Media Access Control (MAC) address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + Maximum Transmission Unit (MTU) + + 68-9000 + Maximum Transmission Unit + + + + + MTU must be between 68 and 9000 + + + + + Configurable offload options + + + + + Configure GRO (generic receive offload) + + on off + + + on + Enable GRO (generic receive offload) + + + off + Disable GRO (generic receive offload) + + + (on|off) + + Must be either 'on' or 'off' + + + + + Configure GSO (generic segmentation offload) + + on off + + + on + Enable GSO (generic segmentation offload) + + + off + Disable GSO (generic segmentation offload) + + + (on|off) + + Must be either 'on' or 'off' + + + + + Configure scatter-gather option + + on off + + + on + Enable scatter-gather + + + off + Disable scatter-gather + + + (on|off) + + Must be either 'on' or 'off' + + + + + Configure TSO (TCP segmentation offloading) + + on off + + + on + Enable TSO (TCP segmentation offloading) + + + off + Disable TSO (TCP segmentation offloading) + + + (on|off) + + Must be either 'on' or 'off' + + + + + Configure UDP fragmentation offloading + + on off + + + on + Enable UDP fragmentation offloading + + + off + Disable UDP fragmentation offloading + + + (on|off) + + Must be either 'on' or 'off' + + + + + + + CPU interrupt affinity mask + + auto 10 100 1000 2500 5000 10000 + + + auto + Auto negotiation (default) + + + hex + Bitmask representing CPUs that this NIC will interrupt + + + hex,hex + Bitmasks representing CPUs for interrupt and receive processing + + + (auto) + [0-9a-f]+(|,[0-9a-f]+)$ + + IRQ affinity mask must be hex value or auto + + + + + Link speed + + auto 10 100 1000 2500 5000 10000 + + + auto + Auto negotiation (default) + + + 10 + 10 Mbit/sec + + + 100 + 100 Mbit/sec + + + 1000 + 1 Gbit/sec + + + 2500 + 2.5 Gbit/sec + + + 5000 + 5 Gbit/sec + + + 10000 + 10 Gbit/sec + + + (auto|10|100|1000|2500|5000|10000) + + Speed must be auto, 10, 100, 1000, 2500, 5000 or 10000 + + + + + QinQ TAG-S Virtual Local Area Network (VLAN) ID + + + + VLAN ID must be between 0 and 4094 + + + + + IP address + + dhcp dhcpv6 + + + ipv4net + IPv4 address and prefix length + + + ipv6net + IPv6 address and prefix length + + + dhcp + Dynamic Host Configuration Protocol + + + dhcpv6 + Dynamic Host Configuration Protocol for IPv6 + + + + (dhcp|dhcpv6) + + + + + + + Interface description + + ^.{1,256}$ + + Interface description too long (limit 256 characters) + + + + + DHCP options + + + + + DHCP client identifier + + + + + DHCP client host name (overrides the system host name) + + + + + + + DHCPv6 options + 319 + + + + + Acquire only config parameters, no address + + + + + + IPv6 "temporary" address + + + + + + + + Ignore link state changes + + + + + + Disable this bridge interface + + + + + + Set Ethertype + + 0x88A8 0x8100 + + + 0x88A8 + 802.1ad + + + 0x8100 + 802.1q + + + (0x88A8|0x8100) + + Ethertype must be 0x88A8 or 0x8100 + + + + + + + Enable proxy-arp on this interface + + + + + + Enable private VLAN proxy ARP on this interface + + + + + + + + Media Access Control (MAC) address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + Maximum Transmission Unit (MTU) + + 68-9000 + Maximum Transmission Unit + + + + + MTU must be between 68 and 9000 + + + + + QinQ TAG-C Virtual Local Area Network (VLAN) ID + + + + VLAN ID must be between 0 and 4094 + + + + + IP address + + dhcp dhcpv6 + + + ipv4net + IPv4 address and prefix length + + + ipv6net + IPv6 address and prefix length + + + dhcp + Dynamic Host Configuration Protocol + + + dhcpv6 + Dynamic Host Configuration Protocol for IPv6 + + + + (dhcp|dhcpv6) + + + + + + + Interface description + + ^.{1,256}$ + + Interface description too long (limit 256 characters) + + + + + DHCP options + + + + + DHCP client identifier + + + + + DHCP client host name (overrides the system host name) + + + + + + + DHCPv6 options + 319 + + + + + Acquire only config parameters, no address + + + + + + IPv6 "temporary" address + + + + + + + + Ignore link state changes + + + + + + Disable this bridge interface + + + + + + + + Enable proxy-arp on this interface + + + + + + Enable private VLAN proxy ARP on this interface + + + + + + + + Media Access Control (MAC) address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + Maximum Transmission Unit (MTU) + + 68-9000 + Maximum Transmission Unit + + + + + MTU must be between 68 and 9000 + + + + + + + + + Virtual Local Area Network (VLAN) ID + + + + VLAN ID must be between 0 and 4094 + + + + + IP address + + dhcp dhcpv6 + + + ipv4net + IPv4 address and prefix length + + + ipv6net + IPv6 address and prefix length + + + dhcp + Dynamic Host Configuration Protocol + + + dhcpv6 + Dynamic Host Configuration Protocol for IPv6 + + + + (dhcp|dhcpv6) + + + + + + + Interface description + + ^.{1,256}$ + + Interface description too long (limit 256 characters) + + + + + DHCP options + + + + + DHCP client identifier + + + + + DHCP client host name (overrides the system host name) + + + + + + + DHCPv6 options + 319 + + + + + Acquire only config parameters, no address + + + + + + IPv6 "temporary" address + + + + + + + + Ignore link state changes + + + + + + Disable this bridge interface + + + + + + VLAN egress QoS + + + + + [:0-7 ]+$ + + QoS mapping should be in the format of \"0:7 2:3\" with numbers 0-9 + + + + + VLAN ingress QoS + + + + + [:0-7 ]+$ + + QoS mapping should be in the format of \"0:7 2:3\" with numbers 0-9 + + + + + + + ARP cache entry timeout in seconds + + 1-86400 + ARP cache entry timout in seconds (default 30) + + + + + ARP cache entry timeout must be between 1 and 86400 seconds + + + + + Enable proxy-arp on this interface + + + + + + Enable private VLAN proxy ARP on this interface + + + + + + + + Media Access Control (MAC) address + + h:h:h:h:h:h + Hardware (MAC) address + + + + + + + + + Maximum Transmission Unit (MTU) + + 68-9000 + Maximum Transmission Unit + + + + + MTU must be between 68 and 9000 + + + + + + + + + diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 524c0f276..7181ff42f 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1047,8 +1047,18 @@ class VLANIf(Interface): tmp.remove() -class BondIf(VLANIf): +class EthernetIf(VLANIf): + """ + Abstraction of a Linux Ethernet Interface + """ + def __init__(self, ifname): + super().__init__(ifname) + def remove(self): + raise OSError('Ethernet interfaces can not be removed') + + +class BondIf(VLANIf): """ The Linux bonding driver provides a method for aggregating multiple network interfaces into a single logical "bonded" interface. The behavior of the @@ -1056,7 +1066,6 @@ class BondIf(VLANIf): either hot standby or load balancing services. Additionally, link integrity monitoring may be performed. """ - def __init__(self, ifname): super().__init__(ifname, type='bond') diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py new file mode 100755 index 000000000..4aed13a62 --- /dev/null +++ b/src/conf_mode/interface-ethernet.py @@ -0,0 +1,298 @@ +#!/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 . + +import os + +from copy import deepcopy +from sys import exit + +from vyos.ifconfig import EthernetIf, VLANIf +from vyos.configdict import list_diff, vlan_to_dict +from vyos.config import Config +from vyos import ConfigError + +default_config_data = { + 'address': [], + 'address_remove': [], + 'description': '', + 'deleted': False, + 'dhcp_client_id': '', + 'dhcp_hostname': '', + 'dhcpv6_prm_only': False, + 'dhcpv6_temporary': False, + 'disable': False, + 'disable_link_detect': 1, + 'hw_id': '', + 'ip_arp_cache_tmo': 30, + 'ip_proxy_arp': 0, + 'ip_proxy_arp_pvlan': 0, + 'intf': '', + 'mac': '', + 'mtu': 1500, + 'vif_s': [], + 'vif_s_remove': [], + 'vif': [], + 'vif_remove': [] +} + + +def apply_vlan_config(vlan, config): + """ + Generic function to apply a VLAN configuration from a dictionary + to a VLAN interface + """ + + if type(vlan) != type(VLANIf("lo")): + raise TypeError() + + # update interface description used e.g. within SNMP + vlan.ifalias = config['description'] + # ignore link state changes + vlan.link_detect = config['disable_link_detect'] + # Maximum Transmission Unit (MTU) + vlan.mtu = config['mtu'] + # Change VLAN interface MAC address + if config['mac']: + vlan.mac = config['mac'] + + # enable/disable VLAN interface + if config['disable']: + vlan.state = 'down' + else: + vlan.state = 'up' + + # Configure interface address(es) + # - not longer required addresses get removed first + # - newly addresses will be added second + for addr in config['address_remove']: + vlan.del_addr(addr) + for addr in config['address']: + vlan.add_addr(addr) + + +def get_config(): + eth = deepcopy(default_config_data) + conf = Config() + + # determine tagNode instance + try: + eth['intf'] = os.environ['VYOS_TAGNODE_VALUE'] + except KeyError as E: + print("Interface not specified") + + # check if ethernet interface has been removed + cfg_base = 'interfaces ethernet ' + eth['intf'] + if not conf.exists(cfg_base): + eth['deleted'] = True + # we can not bail out early as ethernet interface can not be removed + # Kernel will complain with: RTNETLINK answers: Operation not supported. + # Thus we need to remove individual settings + return eth + + # set new configuration level + conf.set_level(cfg_base) + + # retrieve configured interface addresses + if conf.exists('address'): + eth['address'] = conf.return_values('address') + + # get interface addresses (currently effective) - to determine which + # address is no longer valid and needs to be removed + eff_addr = conf.return_effective_values('address') + eth['address_remove'] = list_diff(eff_addr, eth['address']) + + # retrieve interface description + if conf.exists('description'): + eth['description'] = conf.return_value('description') + else: + eth['description'] = eth['intf'] + + # get DHCP client identifier + if conf.exists('dhcp-options client-id'): + eth['dhcp_client_id'] = conf.return_value('dhcp-options client-id') + + # DHCP client host name (overrides the system host name) + if conf.exists('dhcp-options host-name'): + eth['dhcp_hostname'] = conf.return_value('dhcp-options host-name') + + # DHCPv6 only acquire config parameters, no address + if conf.exists('dhcpv6-options parameters-only'): + eth['dhcpv6_prm_only'] = conf.return_value('dhcpv6-options parameters-only') + + # DHCPv6 temporary IPv6 address + if conf.exists('dhcpv6-options temporary'): + eth['dhcpv6_temporary'] = conf.return_value('dhcpv6-options temporary') + + # ignore link state changes + if conf.exists('disable-link-detect'): + eth['disable_link_detect'] = 2 + + # disable interface + if conf.exists('disable'): + eth['disable'] = True + + # ARP cache entry timeout in seconds + if conf.exists('ip arp-cache-timeout'): + eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) + + # Enable proxy-arp on this interface + if conf.exists('ip enable-proxy-arp'): + eth['ip_proxy_arp'] = 1 + + # Enable private VLAN proxy ARP on this interface + if conf.exists('ip proxy-arp-pvlan'): + eth['ip_proxy_arp_pvlan'] = 1 + + # Media Access Control (MAC) address + if conf.exists('mac'): + eth['mac'] = conf.return_value('mac') + + # Maximum Transmission Unit (MTU) + if conf.exists('mtu'): + eth['mtu'] = int(conf.return_value('mtu')) + + # re-set configuration level and retrieve vif-s interfaces + conf.set_level(cfg_base) + # get vif-s interfaces (currently effective) - to determine which vif-s + # interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif-s') + act_intf = conf.list_nodes('vif-s') + eth['vif_s_remove'] = list_diff(eff_intf, act_intf) + + if conf.exists('vif-s'): + for vif_s in conf.list_nodes('vif-s'): + # set config level to vif-s interface + conf.set_level(cfg_base + ' vif-s ' + vif_s) + eth['vif_s'].append(vlan_to_dict(conf)) + + # re-set configuration level and retrieve vif-s interfaces + conf.set_level(cfg_base) + # Determine vif interfaces (currently effective) - to determine which + # vif interface is no longer present and needs to be removed + eff_intf = conf.list_effective_nodes('vif') + act_intf = conf.list_nodes('vif') + eth['vif_remove'] = list_diff(eff_intf, act_intf) + + if conf.exists('vif'): + for vif in conf.list_nodes('vif'): + # set config level to vif interface + conf.set_level(cfg_base + ' vif ' + vif) + eth['vif'].append(vlan_to_dict(conf)) + + return eth + + +def verify(eth): + conf = Config() + # some options can not be changed when interface is enslaved to a bond + for bond in conf.list_nodes('interfaces bonding'): + if conf.exists('interfaces bonding ' + bond + ' member interface'): + if eth['name'] in conf.return_values('interfaces bonding ' + bond + ' member interface'): + if eth['disable']: + raise ConfigError('Can not disable interface {} which is a member of {}').format(eth['intf'], bond) + + if eth['address']: + raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond) + + + return None + + +def generate(eth): + import pprint + pprint.pprint(eth) + return None + + +def apply(eth): + e = EthernetIf(eth['intf']) + # update interface description used e.g. within SNMP + e.ifalias = eth['description'] + + # + # missing DHCP/DHCPv6 options go here + # + + # ignore link state changes + e.link_detect = eth['disable_link_detect'] + # configure ARP cache timeout in milliseconds + e.arp_cache_tmp = eth['ip_arp_cache_tmo'] + # Enable proxy-arp on this interface + e.proxy_arp = eth['ip_proxy_arp'] + # Enable private VLAN proxy ARP on this interface + e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan'] + + # Change interface MAC address + if eth['mac']: + e.mac = eth['mac'] + + # Maximum Transmission Unit (MTU) + e.mtu = eth['mtu'] + + # Configure interface address(es) + # - not longer required addresses get removed first + # - newly addresses will be added second + for addr in eth['address_remove']: + e.del_addr(addr) + for addr in eth['address']: + e.add_addr(addr) + + # Enable/Disable interface + if eth['disable']: + e.state = 'down' + else: + e.state = 'up' + + # remove no longer required service VLAN interfaces (vif-s) + for vif_s in eth['vif_s_remove']: + e.del_vlan(vif_s) + + # create service VLAN interfaces (vif-s) + for vif_s in eth['vif_s']: + s_vlan = e.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) + apply_vlan_config(s_vlan, vif_s) + + # remove no longer required client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c_remove']: + s_vlan.del_vlan(vif_c) + + # create client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c']: + c_vlan = s_vlan.add_vlan(vif_c['id']) + apply_vlan_config(c_vlan, vif_c) + + # remove no longer required VLAN interfaces (vif) + for vif in eth['vif_remove']: + e.del_vlan(vif) + + # create VLAN interfaces (vif) + for vif in eth['vif']: + vlan = e.add_vlan(vif['id']) + apply_vlan_config(vlan, vif) + + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + exit(1) -- cgit v1.2.3 From bebb43450fcca4c086ab1a64be6919441c7c0032 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 18:48:31 +0200 Subject: Python/ifconfig: T1557: support VLAN {ingress,egress}-qos-mapping ingress-qos-map - defines a mapping of VLAN header prio field to the Linux internal packet priority on incoming frames. The format is FROM:TO with multiple mappings separated by spaces. egress-qos-map - defines a mapping of Linux internal packet priority to VLAN header prio field but for outgoing frames. The format is the same as for ingress-qos-map. --- python/vyos/configdict.py | 22 +++++++++++++++++++++- python/vyos/ifconfig.py | 22 +++++++++++++++++++--- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/python/vyos/configdict.py b/python/vyos/configdict.py index 4bc8863bb..1c9cf6897 100644 --- a/python/vyos/configdict.py +++ b/python/vyos/configdict.py @@ -116,6 +116,10 @@ def vlan_to_dict(conf): 'dhcpv6_temporary': False, 'disable': False, 'disable_link_detect': 1, + 'egress_qos': '', + 'egress_qos_changed': False, + 'ingress_qos': '', + 'ingress_qos_changed': False, 'mac': '', 'mtu': 1500 } @@ -153,7 +157,7 @@ def vlan_to_dict(conf): if conf.exists('disable-link-detect'): vlan['disable_link_detect'] = 2 - # disable bond interface + # disable VLAN interface if conf.exists('disable'): vlan['disable'] = True @@ -165,6 +169,22 @@ def vlan_to_dict(conf): if conf.exists('mtu'): vlan['mtu'] = int(conf.return_value('mtu')) + # VLAN egress QoS + if conf.exists('egress-qos'): + vlan['egress_qos'] = conf.return_value('egress-qos') + + # egress changes QoS require VLAN interface recreation + if vlan['egress_qos'] != conf.return_effective_value('egress-qos'): + vlan['egress_qos_changed'] = True + + # VLAN ingress QoS + if conf.exists('ingress-qos'): + vlan['ingress_qos'] = conf.return_value('ingress-qos') + + # ingress changes QoS require VLAN interface recreation + if vlan['ingress_qos'] != conf.return_effective_value('ingress-qos'): + vlan['ingress_qos_changed'] = True + # ethertype is mandatory on vif-s nodes and only exists here! # check if this is a vif-s node at all: if conf.get_level().split()[-2] == 'vif-s': diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 7181ff42f..80b5592c2 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1005,7 +1005,7 @@ class VLANIf(Interface): def __init__(self, ifname, type=None): super().__init__(ifname, type) - def add_vlan(self, vlan_id, ethertype=''): + def add_vlan(self, vlan_id, ethertype='', ingress_qos='', egress_qos=''): """ A virtual LAN (VLAN) is any broadcast domain that is partitioned and isolated in a computer network at the data link layer (OSI layer 2). @@ -1017,6 +1017,13 @@ class VLANIf(Interface): A new object of type VLANIf is returned once the interface has been created. + + @param ethertype: If specified, create 802.1ad or 802.1q Q-in-Q VLAN + interface + @param ingress_qos: Defines a mapping of VLAN header prio field to the + Linux internal packet priority on incoming frames. + @param ingress_qos: Defines a mapping of Linux internal packet priority + to VLAN header prio field but for outgoing frames. """ vlan_ifname = self._ifname + '.' + str(vlan_id) if not os.path.exists('/sys/class/net/{}'.format(vlan_ifname)): @@ -1026,9 +1033,18 @@ class VLANIf(Interface): self._ethertype = ethertype ethertype = 'proto {}'.format(ethertype) + # Optional ingress QOS mapping + opt_i = '' + if ingress_qos: + opt_i = 'ingress-qos-map ' + ingress_qos + # Optional egress QOS mapping + opt_e = '' + if egress_qos: + opt_e = 'egress-qos-map ' + egress_qos + # create interface in the system - cmd = 'ip link add link {intf} name {intf}.{vlan} type vlan {proto} id {vlan}'.format( - intf=self._ifname, vlan=self._vlan_id, proto=ethertype) + cmd = 'ip link add link {intf} name {intf}.{vlan} type vlan {proto} id {vlan} {opt_e} {opt_i}' \ + .format(intf=self._ifname, vlan=self._vlan_id, proto=ethertype, opt_e=opt_e, opt_i=opt_i) self._cmd(cmd) # return new object mapping to the newly created interface -- cgit v1.2.3 From f8d6ba647e0ef4f1c18302bfb4bebb040f2df95c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 18:49:42 +0200 Subject: ethernet: T1637: support VLAN {ingress,egress}-qos-mapping --- src/conf_mode/interface-ethernet.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 4aed13a62..c20e551d6 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -282,7 +282,12 @@ def apply(eth): # create VLAN interfaces (vif) for vif in eth['vif']: - vlan = e.add_vlan(vif['id']) + # QoS priority mapping can only be set during interface creation + # so we delete the interface first if required. + if vif['egress_qos_changed'] or vif['ingress_qos_changed']: + e.del_vlan(vif['id']) + + vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) apply_vlan_config(vlan, vif) return None -- cgit v1.2.3 From eb48a7fde5c37f1c905d1c85aca7d0518f65e652 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 19:51:33 +0200 Subject: Python/ifconfig: T1557: import cleanup for subprocess --- python/vyos/ifconfig.py | 62 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 80b5592c2..9b37fdbff 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -15,12 +15,12 @@ import os import re -import subprocess import jinja2 from vyos.validate import * from ipaddress import IPv4Network, IPv6Address from netifaces import ifaddresses, AF_INET, AF_INET6 +from subprocess import Popen, PIPE from time import sleep dhcp_cfg = """ @@ -84,6 +84,36 @@ class Interface: if os.path.isfile('/tmp/vyos.ifconfig.debug'): print('DEBUG/{:<6} {}'.format(self._ifname, msg)) + def _cmd(self, command): + p = Popen(command, stdout=PIPE, shell=True) + tmp = p.communicate()[0].strip() + self._debug_msg("cmd '{}'".format(command)) + if tmp.decode(): + self._debug_msg("returned:\n{}".format(tmp.decode())) + + # do we need some error checking code here? + + def _read_sysfs(self, filename): + """ + Provide a single primitive w/ error checking for reading from sysfs. + """ + value = None + with open(filename, 'r') as f: + value = f.read().rstrip('\n') + + self._debug_msg("read '{}' < '{}'".format(value, filename)) + return value + + def _write_sysfs(self, filename, value): + """ + Provide a single primitive w/ error checking for writing to sysfs. + """ + self._debug_msg("write '{}' > '{}'".format(value, filename)) + with open(filename, 'w') as f: + f.write(str(value)) + + return None + def remove(self): """ Remove interface from operating system. Removing the interface @@ -118,36 +148,6 @@ class Interface: cmd = 'ip link del dev {}'.format(self._ifname) self._cmd(cmd) - def _cmd(self, command): - self._debug_msg("cmd '{}'".format(command)) - - process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) - proc_stdout = process.communicate()[0].strip() - - # add exception handling code - pass - - def _read_sysfs(self, filename): - """ - Provide a single primitive w/ error checking for reading from sysfs. - """ - value = None - with open(filename, 'r') as f: - value = f.read().rstrip('\n') - - self._debug_msg("read '{}' < '{}'".format(value, filename)) - return value - - def _write_sysfs(self, filename, value): - """ - Provide a single primitive w/ error checking for writing to sysfs. - """ - self._debug_msg("write '{}' > '{}'".format(value, filename)) - with open(filename, 'w') as f: - f.write(str(value)) - - return None - @property def mtu(self): """ -- cgit v1.2.3 From 78c0fc6050d05c03bd35c7d4beacef9da432deb3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 19:52:18 +0200 Subject: Python/ifconfig: T1557: ethernet: support changing flow control Ethernet flow control can be set by set_flow_control() which enables/disables generation of pause frames. --- python/vyos/ifconfig.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 9b37fdbff..9310ee278 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1073,6 +1073,25 @@ class EthernetIf(VLANIf): def remove(self): raise OSError('Ethernet interfaces can not be removed') + def set_flow_control(self, enable): + """ + Changes the pause parameters of the specified Ethernet device. + + @param enable: true -> enable pause frames, false -> disable pause frames + """ + if enable not in ['on', 'off']: + raise ValueError("Value out of range") + + # Assemble command executed on system. Unfortunately there is no way + # to change this setting via sysfs + cmd = 'ethtool --pause {0} autoneg {1} tx {1} rx {1}'.format( + self._ifname, enable) + try: + # An exception will be thrown if the settings are not changed + self._cmd(cmd) + except CalledProcessError: + pass + class BondIf(VLANIf): """ -- cgit v1.2.3 From 735a7d663c279e25feadcfad79e17be7fc0d8c8f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 19:53:49 +0200 Subject: ethernet: T1637: reset MAC address to read hw-id on removal --- src/conf_mode/interface-ethernet.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index c20e551d6..83459cedb 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -140,6 +140,10 @@ def get_config(): if conf.exists('disable-link-detect'): eth['disable_link_detect'] = 2 + # retrieve real hardware address + if conf.exists('hw-id'): + eth['hw_id'] = conf.return_value('hw-id') + # disable interface if conf.exists('disable'): eth['disable'] = True @@ -235,9 +239,12 @@ def apply(eth): # Enable private VLAN proxy ARP on this interface e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan'] - # Change interface MAC address + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed if eth['mac']: e.mac = eth['mac'] + else: + e.mac = eth['hw_id'] # Maximum Transmission Unit (MTU) e.mtu = eth['mtu'] -- cgit v1.2.3 From ca9964099fe67128bd380fb6026f1631ccd89f11 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 19:54:21 +0200 Subject: ethernet: T1637: support changing flow-control --- src/conf_mode/interface-ethernet.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 83459cedb..a12980bfe 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -35,6 +35,7 @@ default_config_data = { 'dhcpv6_temporary': False, 'disable': False, 'disable_link_detect': 1, + 'flow_control': 'on', 'hw_id': '', 'ip_arp_cache_tmo': 30, 'ip_proxy_arp': 0, @@ -140,6 +141,10 @@ def get_config(): if conf.exists('disable-link-detect'): eth['disable_link_detect'] = 2 + # disable ethernet flow control (pause frames) + if conf.exists('disable-flow-control'): + eth['flow_control'] = 'off' + # retrieve real hardware address if conf.exists('hw-id'): eth['hw_id'] = conf.return_value('hw-id') @@ -232,6 +237,8 @@ def apply(eth): # ignore link state changes e.link_detect = eth['disable_link_detect'] + # disable ethernet flow control (pause frames) + e.set_flow_control(eth['flow_control']) # configure ARP cache timeout in milliseconds e.arp_cache_tmp = eth['ip_arp_cache_tmo'] # Enable proxy-arp on this interface -- cgit v1.2.3 From 04399a6e724c95c44e136f4675b04262de73d156 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 8 Sep 2019 20:04:45 +0200 Subject: Python/ifconfig: T1557: mac: ignore empty address strings --- python/vyos/ifconfig.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 9310ee278..ab8799e54 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -43,7 +43,6 @@ dhclient_base = r'/var/lib/dhcp/dhclient_' class Interface: - def __init__(self, ifname, type=None): """ This is the base interface class which supports basic IP/MAC address @@ -202,6 +201,10 @@ class Interface: >>> Interface('eth0').mac '00:90:43:fe:fe:1b' """ + # on interface removal (ethernet) an empty string is passed - ignore it + if not mac: + return None + # a mac address consits out of 6 octets octets = len(mac.split(':')) if octets != 6: -- cgit v1.2.3 From 1d2bec57085303420b3e12dc61a44edd62c4c490 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Tue, 10 Sep 2019 21:40:10 +0200 Subject: ethernet: T1637: handle VLAN interface exception on system startup On system bootup the above condition is true but the interface does not exists, which throws an exception, but that's legal. Simply pass the exception! With this change VyOS boots up and configures ethernet VLAN interfaces as expected. --- src/conf_mode/interface-ethernet.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index a12980bfe..3091e0717 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -299,7 +299,12 @@ def apply(eth): # QoS priority mapping can only be set during interface creation # so we delete the interface first if required. if vif['egress_qos_changed'] or vif['ingress_qos_changed']: - e.del_vlan(vif['id']) + try: + # on system bootup the above condition is true but the interface + # does not exists, which throws an exception, but that's legal + e.del_vlan(vif['id']) + except: + pass vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) apply_vlan_config(vlan, vif) -- cgit v1.2.3 From f107a252020d225b4e7429cc5c3a61af6cb2e1d9 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 12 Sep 2019 21:23:14 +0200 Subject: ethernet: T1637: only list supported link speeds for completion helper --- interface-definitions/interfaces-ethernet.xml | 29 +++------------------------ 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/interface-definitions/interfaces-ethernet.xml b/interface-definitions/interfaces-ethernet.xml index a2de4aeb3..14a6ff095 100644 --- a/interface-definitions/interfaces-ethernet.xml +++ b/interface-definitions/interfaces-ethernet.xml @@ -337,38 +337,15 @@ - Link speed + Link speed in MBit/s - auto 10 100 1000 2500 5000 10000 + auto + auto Auto negotiation (default) - - 10 - 10 Mbit/sec - - - 100 - 100 Mbit/sec - - - 1000 - 1 Gbit/sec - - - 2500 - 2.5 Gbit/sec - - - 5000 - 5 Gbit/sec - - - 10000 - 10 Gbit/sec - (auto|10|100|1000|2500|5000|10000) -- cgit v1.2.3 From 66884984c7b4b5b445322828541a942fa9e705c1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 13 Sep 2019 16:36:08 +0200 Subject: Python/ifconfig: T1557: ethernet: add method for changing speed and duplex --- python/vyos/ifconfig.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index ab8799e54..507df0d71 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1095,6 +1095,28 @@ class EthernetIf(VLANIf): except CalledProcessError: pass + def set_speed_duplex(self, speed, duplex): + """ + Set link speed in Mbit/s and duplex. + + @speed can be any link speed in MBit/s, e.g. 10, 100, 1000 auto + @duplex can be half, full, auto + """ + + if speed not in ['auto', '10', '100', '1000', '2500', '5000', '10000', '25000', '40000', '50000', '100000', '400000']: + raise ValueError("Value out of range (speed)") + + if duplex not in ['auto', 'full', 'half']: + raise ValueError("Value out of range (duplex)") + + cmd = 'ethtool -s {}'.format(self._ifname) + if speed == 'auto' or duplex == 'auto': + cmd += ' autoneg on' + else: + cmd += ' speed {} duplex {} autoneg off'.format(speed, duplex) + + return self._cmd(cmd) + class BondIf(VLANIf): """ -- cgit v1.2.3 From fc7685dcbb47a4f80a3e554efa8f60799009151b Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Fri, 13 Sep 2019 16:36:30 +0200 Subject: ethernet: T1637: change speed and duplex settings --- src/conf_mode/interface-ethernet.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 3091e0717..30e7c4b1f 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -35,6 +35,7 @@ default_config_data = { 'dhcpv6_temporary': False, 'disable': False, 'disable_link_detect': 1, + 'duplex': 'auto', 'flow_control': 'on', 'hw_id': '', 'ip_arp_cache_tmo': 30, @@ -43,6 +44,7 @@ default_config_data = { 'intf': '', 'mac': '', 'mtu': 1500, + 'speed': 'auto', 'vif_s': [], 'vif_s_remove': [], 'vif': [], @@ -153,6 +155,10 @@ def get_config(): if conf.exists('disable'): eth['disable'] = True + # interface duplex + if conf.exists('duplex'): + eth['duplex'] = conf.return_value('duplex') + # ARP cache entry timeout in seconds if conf.exists('ip arp-cache-timeout'): eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout')) @@ -173,6 +179,10 @@ def get_config(): if conf.exists('mtu'): eth['mtu'] = int(conf.return_value('mtu')) + # interface speed + if conf.exists('speed'): + eth['speed'] = conf.return_value('speed') + # re-set configuration level and retrieve vif-s interfaces conf.set_level(cfg_base) # get vif-s interfaces (currently effective) - to determine which vif-s @@ -205,6 +215,14 @@ def get_config(): def verify(eth): + if eth['speed'] == 'auto': + if eth['duplex'] != 'auto': + raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too') + + if eth['duplex'] == 'auto': + if eth['speed'] != 'auto': + raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too') + conf = Config() # some options can not be changed when interface is enslaved to a bond for bond in conf.list_nodes('interfaces bonding'): @@ -256,6 +274,9 @@ def apply(eth): # Maximum Transmission Unit (MTU) e.mtu = eth['mtu'] + # Set physical interface speed and duplex + e.set_speed_duplex(eth['speed'], eth['duplex']) + # Configure interface address(es) # - not longer required addresses get removed first # - newly addresses will be added second -- cgit v1.2.3 From fcc97a25da94f02e4be1c9b077020490334516c2 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 13:35:57 +0200 Subject: Revert "ethernet: T1637: only list supported link speeds for completion helper" This reverts commit d6a6daaf1d7ed0f1ff2e53490972e0cf11fff000. --- interface-definitions/interfaces-ethernet.xml | 29 ++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/interface-definitions/interfaces-ethernet.xml b/interface-definitions/interfaces-ethernet.xml index 14a6ff095..a2de4aeb3 100644 --- a/interface-definitions/interfaces-ethernet.xml +++ b/interface-definitions/interfaces-ethernet.xml @@ -337,15 +337,38 @@ - Link speed in MBit/s + Link speed - auto - + auto 10 100 1000 2500 5000 10000 auto Auto negotiation (default) + + 10 + 10 Mbit/sec + + + 100 + 100 Mbit/sec + + + 1000 + 1 Gbit/sec + + + 2500 + 2.5 Gbit/sec + + + 5000 + 5 Gbit/sec + + + 10000 + 10 Gbit/sec + (auto|10|100|1000|2500|5000|10000) -- cgit v1.2.3 From 7dd89ff63e34ef0e824df5d15966235c09444c27 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 13:38:35 +0200 Subject: ethernet: T1637: add support for 25G, 40G, 50G and 100G link speeds --- interface-definitions/interfaces-ethernet.xml | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/interface-definitions/interfaces-ethernet.xml b/interface-definitions/interfaces-ethernet.xml index a2de4aeb3..9244f3b5f 100644 --- a/interface-definitions/interfaces-ethernet.xml +++ b/interface-definitions/interfaces-ethernet.xml @@ -339,7 +339,7 @@ Link speed - auto 10 100 1000 2500 5000 10000 + auto 10 100 1000 2500 5000 10000 25000 40000 50000 100000 auto @@ -369,10 +369,26 @@ 10000 10 Gbit/sec + + 25000 + 25 Gbit/sec + + + 40000 + 40 Gbit/sec + + + 50000 + 50 Gbit/sec + + + 100000 + 100 Gbit/sec + - (auto|10|100|1000|2500|5000|10000) + (auto|10|100|1000|2500|5000|10000|25000|40000|50000|100000) - Speed must be auto, 10, 100, 1000, 2500, 5000 or 10000 + Speed must be auto, 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000 or 100000 -- cgit v1.2.3 From 4c2db299625fb3275df12d3174b64428112f3756 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 13:52:09 +0200 Subject: Python/ifconfig: T1557: redirect _cmd stderr to stdout --- python/vyos/ifconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 507df0d71..398f2ee8f 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -20,7 +20,7 @@ import jinja2 from vyos.validate import * from ipaddress import IPv4Network, IPv6Address from netifaces import ifaddresses, AF_INET, AF_INET6 -from subprocess import Popen, PIPE +from subprocess import Popen, PIPE, STDOUT from time import sleep dhcp_cfg = """ @@ -84,7 +84,7 @@ class Interface: print('DEBUG/{:<6} {}'.format(self._ifname, msg)) def _cmd(self, command): - p = Popen(command, stdout=PIPE, shell=True) + p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True) tmp = p.communicate()[0].strip() self._debug_msg("cmd '{}'".format(command)) if tmp.decode(): -- cgit v1.2.3 From 1978abbe9d35571bbf57695f04f4a89550a35b1f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 13:57:31 +0200 Subject: Python/ifconfig: T1557: add ethernet interface get_driver_name() --- python/vyos/ifconfig.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 398f2ee8f..de53f8b25 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1076,6 +1076,14 @@ class EthernetIf(VLANIf): def remove(self): raise OSError('Ethernet interfaces can not be removed') + def get_driver_name(self): + """ + Return the driver name used by NIC. Some NICs don't support all + features e.g. changing link-speed, duplex + """ + link = os.readlink('/sys/class/net/{}/device/driver/module'.format(self._ifname)) + return os.path.basename(link) + def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. -- cgit v1.2.3 From 822976ac1c6972da38c7cb47dfdfdcee75a68457 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:26:31 +0200 Subject: Python/ifconfig: T1557: return stdout string for _cmd() --- python/vyos/ifconfig.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index de53f8b25..fe3cd20ef 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -91,6 +91,7 @@ class Interface: self._debug_msg("returned:\n{}".format(tmp.decode())) # do we need some error checking code here? + return tmp def _read_sysfs(self, filename): """ -- cgit v1.2.3 From f2ca377c2a92e1036c4ff942bd61d3e4bb4f4a18 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:26:54 +0200 Subject: Python/ifconfig: T1557: call ethtool with full path --- python/vyos/ifconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index fe3cd20ef..9e6c27430 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1096,7 +1096,7 @@ class EthernetIf(VLANIf): # Assemble command executed on system. Unfortunately there is no way # to change this setting via sysfs - cmd = 'ethtool --pause {0} autoneg {1} tx {1} rx {1}'.format( + cmd = '/sbin/ethtool --pause {0} autoneg {1} tx {1} rx {1}'.format( self._ifname, enable) try: # An exception will be thrown if the settings are not changed @@ -1118,7 +1118,7 @@ class EthernetIf(VLANIf): if duplex not in ['auto', 'full', 'half']: raise ValueError("Value out of range (duplex)") - cmd = 'ethtool -s {}'.format(self._ifname) + cmd = '/sbin/ethtool -s {}'.format(self._ifname) if speed == 'auto' or duplex == 'auto': cmd += ' autoneg on' else: -- cgit v1.2.3 From 2e210396036ffc769d941104729f67a5f64a0b24 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:46:03 +0200 Subject: Python/ifconfig: T1557: query driver if it supports auto negotiation --- python/vyos/ifconfig.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 9e6c27430..cbedcc86e 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1085,6 +1085,24 @@ class EthernetIf(VLANIf): link = os.readlink('/sys/class/net/{}/device/driver/module'.format(self._ifname)) return os.path.basename(link) + def has_autoneg(self): + """ + Not all drivers support autonegotiation. + + returns True -> Autonegotiation is supported by driver + False -> Autonegotiation is not supported by driver + """ + regex = 'Supports auto-negotiation:[ ]\w+' + tmp = self._cmd('/sbin/ethtool {}'.format(self._ifname)) + tmp = re.search(regex, tmp.decode()) + + # Output is either 'Supports auto-negotiation: Yes' or + # 'Supports auto-negotiation: No' + if tmp.group().split(':')[1].lstrip() == "Yes": + return True + else: + return False + def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. -- cgit v1.2.3 From f5784bd48a6600372696b6731d4300735e710dd3 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:46:30 +0200 Subject: Python/ifconfig: T1557: vmxnet3/virtio_net do not support changing flow control --- python/vyos/ifconfig.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index cbedcc86e..17cc7a2a7 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1112,6 +1112,11 @@ class EthernetIf(VLANIf): if enable not in ['on', 'off']: raise ValueError("Value out of range") + if self.get_driver_name() in ['vmxnet3', 'virtio_net']: + self._debug_msg('{} driver does not support changing flow control settings!' + .format(self.get_driver_name())) + return + # Assemble command executed on system. Unfortunately there is no way # to change this setting via sysfs cmd = '/sbin/ethtool --pause {0} autoneg {1} tx {1} rx {1}'.format( -- cgit v1.2.3 From 9fe2bcb3666bc115e0d5f907b0097a874efa43ff Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:46:44 +0200 Subject: Python/ifconfig: T1557: vmxnet3/virtio_net do not support changing speed/duplex control --- python/vyos/ifconfig.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 17cc7a2a7..e1c1e0246 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1141,6 +1141,12 @@ class EthernetIf(VLANIf): if duplex not in ['auto', 'full', 'half']: raise ValueError("Value out of range (duplex)") + if self.get_driver_name() in ['vmxnet3', 'virtio_net']: + self._debug_msg('{} driver does not support changing speed/duplex settings!' + .format(self.get_driver_name())) + return + + cmd = '/sbin/ethtool -s {}'.format(self._ifname) if speed == 'auto' or duplex == 'auto': cmd += ' autoneg on' -- cgit v1.2.3 From 033b32b20a7a277d2da180388aba78e30c2ed074 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 14:58:05 +0200 Subject: Python/ifconfig: T1557: unify '/sys/class/net/{}' path --- python/vyos/ifconfig.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index e1c1e0246..7ebe1248e 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -158,7 +158,7 @@ class Interface: >>> Interface('eth0').mtu '1500' """ - return self._read_sysfs('/sys/class/net/{0}/mtu' + return self._read_sysfs('/sys/class/net/{}/mtu' .format(self._ifname)) @mtu.setter @@ -175,7 +175,7 @@ class Interface: if mtu < 68 or mtu > 9000: raise ValueError('Invalid MTU size: "{}"'.format(mru)) - return self._write_sysfs('/sys/class/net/{0}/mtu' + return self._write_sysfs('/sys/class/net/{}/mtu' .format(self._ifname), mtu) @property @@ -188,7 +188,7 @@ class Interface: >>> Interface('eth0').mac '00:0c:29:11:aa:cc' """ - return self._read_sysfs('/sys/class/net/{0}/address' + return self._read_sysfs('/sys/class/net/{}/address' .format(self._ifname)) @mac.setter @@ -307,7 +307,7 @@ class Interface: >>> Interface('eth0').ifalias '' """ - return self._read_sysfs('/sys/class/net/{0}/ifalias' + return self._read_sysfs('/sys/class/net/{}/ifalias' .format(self._ifname)) @ifalias.setter @@ -331,7 +331,7 @@ class Interface: # clear interface alias ifalias = '\0' - self._write_sysfs('/sys/class/net/{0}/ifalias' + self._write_sysfs('/sys/class/net/{}/ifalias' .format(self._ifname), ifalias) @property @@ -344,7 +344,7 @@ class Interface: >>> Interface('eth0').state 'up' """ - return self._read_sysfs('/sys/class/net/{0}/operstate' + return self._read_sysfs('/sys/class/net/{}/operstate' .format(self._ifname)) @state.setter @@ -763,7 +763,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').aging_time '300' """ - return (self._read_sysfs('/sys/class/net/{0}/bridge/ageing_time' + return (self._read_sysfs('/sys/class/net/{}/bridge/ageing_time' .format(self._ifname)) / 100) @ageing_time.setter @@ -777,7 +777,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').ageing_time = 2 """ time = int(time) * 100 - return self._write_sysfs('/sys/class/net/{0}/bridge/ageing_time' + return self._write_sysfs('/sys/class/net/{}/bridge/ageing_time' .format(self._ifname), time) @property @@ -791,7 +791,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').ageing_time '3' """ - return (self._read_sysfs('/sys/class/net/{0}/bridge/forward_delay' + return (self._read_sysfs('/sys/class/net/{}/bridge/forward_delay' .format(self._ifname)) / 100) @forward_delay.setter @@ -804,7 +804,7 @@ class BridgeIf(Interface): >>> from vyos.ifconfig import Interface >>> BridgeIf('br0').forward_delay = 15 """ - return self._write_sysfs('/sys/class/net/{0}/bridge/forward_delay' + return self._write_sysfs('/sys/class/net/{}/bridge/forward_delay' .format(self._ifname), (int(time) * 100)) @property @@ -818,7 +818,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').hello_time '2' """ - return (self._read_sysfs('/sys/class/net/{0}/bridge/hello_time' + return (self._read_sysfs('/sys/class/net/{}/bridge/hello_time' .format(self._ifname)) / 100) @hello_time.setter @@ -831,7 +831,7 @@ class BridgeIf(Interface): >>> from vyos.ifconfig import Interface >>> BridgeIf('br0').hello_time = 2 """ - return self._write_sysfs('/sys/class/net/{0}/bridge/hello_time' + return self._write_sysfs('/sys/class/net/{}/bridge/hello_time' .format(self._ifname), (int(time) * 100)) @property @@ -846,7 +846,7 @@ class BridgeIf(Interface): '20' """ - return (self._read_sysfs('/sys/class/net/{0}/bridge/max_age' + return (self._read_sysfs('/sys/class/net/{}/bridge/max_age' .format(self._ifname)) / 100) @max_age.setter @@ -859,7 +859,7 @@ class BridgeIf(Interface): >>> from vyos.ifconfig import Interface >>> BridgeIf('br0').max_age = 30 """ - return self._write_sysfs('/sys/class/net/{0}/bridge/max_age' + return self._write_sysfs('/sys/class/net/{}/bridge/max_age' .format(self._ifname), (int(time) * 100)) @property @@ -872,7 +872,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').priority '32768' """ - return self._read_sysfs('/sys/class/net/{0}/bridge/priority' + return self._read_sysfs('/sys/class/net/{}/bridge/priority' .format(self._ifname)) @priority.setter @@ -884,7 +884,7 @@ class BridgeIf(Interface): >>> from vyos.ifconfig import Interface >>> BridgeIf('br0').priority = 8192 """ - return self._write_sysfs('/sys/class/net/{0}/bridge/priority' + return self._write_sysfs('/sys/class/net/{}/bridge/priority' .format(self._ifname), priority) @property @@ -899,7 +899,7 @@ class BridgeIf(Interface): """ state = 0 - with open('/sys/class/net/{0}/bridge/stp_state'.format(self._ifname), 'r') as f: + with open('/sys/class/net/{}/bridge/stp_state'.format(self._ifname), 'r') as f: state = int(f.read().rstrip('\n')) return state @@ -915,7 +915,7 @@ class BridgeIf(Interface): """ if int(state) >= 0 and int(state) <= 1: - return self._write_sysfs('/sys/class/net/{0}/bridge/stp_state' + return self._write_sysfs('/sys/class/net/{}/bridge/stp_state' .format(self._ifname), state) else: raise ValueError("Value out of range") @@ -930,7 +930,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').multicast_querier '0' """ - return self._read_sysfs('/sys/class/net/{0}/bridge/multicast_querier' + return self._read_sysfs('/sys/class/net/{}/bridge/multicast_querier' .format(self._ifname)) @multicast_querier.setter @@ -948,7 +948,7 @@ class BridgeIf(Interface): >>> BridgeIf('br0').multicast_querier = 1 """ if int(enable) >= 0 and int(enable) <= 1: - return self._write_sysfs('/sys/class/net/{0}/bridge/multicast_querier' + return self._write_sysfs('/sys/class/net/{}/bridge/multicast_querier' .format(self._ifname), enable) else: raise ValueError("Value out of range") -- cgit v1.2.3 From fd154227c90c0b0dd10bb14ee5b7585ec2970c22 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 15:25:29 +0200 Subject: bonding: T1614: minor comment cleanup --- src/conf_mode/interface-bonding.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/conf_mode/interface-bonding.py b/src/conf_mode/interface-bonding.py index 0d5f9f6b7..9049913e6 100755 --- a/src/conf_mode/interface-bonding.py +++ b/src/conf_mode/interface-bonding.py @@ -334,8 +334,7 @@ def apply(bond): b = BondIf(bond['intf']) if bond['deleted']: - # - # delete bonding interface + # delete interface b.remove() else: # Some parameters can not be changed when the bond is up. -- cgit v1.2.3 From c5c6d87404b63848068f38ee5c69ccad24feede1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 15:25:45 +0200 Subject: bridge: T1556: minor comment cleanup --- src/conf_mode/interface-bridge.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py index 401182a0d..62589c798 100755 --- a/src/conf_mode/interface-bridge.py +++ b/src/conf_mode/interface-bridge.py @@ -183,8 +183,7 @@ def apply(bridge): br = BridgeIf(bridge['intf']) if bridge['deleted']: - # delete bridge interface - # DHCP is stopped inside remove() + # delete interface br.remove() else: # enable interface -- cgit v1.2.3 From f56b4e190e741bdb2d19d14f2b7004fedc76951c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 15:33:33 +0200 Subject: ethernet: T1637: remove debug pprint --- src/conf_mode/interface-ethernet.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 30e7c4b1f..5bfe35767 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -237,13 +237,9 @@ def verify(eth): return None - def generate(eth): - import pprint - pprint.pprint(eth) return None - def apply(eth): e = EthernetIf(eth['intf']) # update interface description used e.g. within SNMP -- cgit v1.2.3 From 8fe9187419fc77df156bb0cbba3a5ca1d61cc36f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 15:34:57 +0200 Subject: Python/ifconfig: T1557: use proper inheritance levels on remove() --- python/vyos/ifconfig.py | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 7ebe1248e..980577965 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -125,23 +125,14 @@ class Interface: >>> i = Interface('eth0') >>> i.remove() """ - - # do we have sub interfaces (VLANs)? - # we apply a regex matching subinterfaces (indicated by a .) of a - # parent interface. 'bond0(?:\.\d+){1,2}' will match vif and vif-s/vif-c - # subinterfaces - vlan_ifs = [f for f in os.listdir(r'/sys/class/net') \ - if re.match(self._ifname + r'(?:\.\d+){1,2}', f)] - - for vlan in vlan_ifs: - Interface(vlan).remove() - - # All subinterfaces are now removed, continue on the physical interface - # stop DHCP(v6) if running self._del_dhcp() self._del_dhcpv6() + # Ethernet interfaces can not be removed + if type(self) == type(EthernetIf): + return + # NOTE (Improvement): # after interface removal no other commands should be allowed # to be called and instead should raise an Exception: @@ -1009,6 +1000,31 @@ class VLANIf(Interface): def __init__(self, ifname, type=None): super().__init__(ifname, type) + def remove(self): + """ + Remove interface from operating system. Removing the interface + deconfigures all assigned IP addresses and clear possible DHCP(v6) + client processes. + + Example: + >>> from vyos.ifconfig import Interface + >>> i = Interface('eth0') + >>> i.remove() + """ + # do we have sub interfaces (VLANs)? + # we apply a regex matching subinterfaces (indicated by a .) of a + # parent interface. 'bond0(?:\.\d+){1,2}' will match vif and vif-s/vif-c + # subinterfaces + vlan_ifs = [f for f in os.listdir(r'/sys/class/net') \ + if re.match(self._ifname + r'(?:\.\d+){1,2}', f)] + + for vlan in vlan_ifs: + Interface(vlan).remove() + + # All subinterfaces are now removed, continue on the physical interface + super().remove() + + def add_vlan(self, vlan_id, ethertype='', ingress_qos='', egress_qos=''): """ A virtual LAN (VLAN) is any broadcast domain that is partitioned and @@ -1074,9 +1090,6 @@ class EthernetIf(VLANIf): def __init__(self, ifname): super().__init__(ifname) - def remove(self): - raise OSError('Ethernet interfaces can not be removed') - def get_driver_name(self): """ Return the driver name used by NIC. Some NICs don't support all -- cgit v1.2.3 From 738b62f0fb410daf1dbe97ad8aed9d862b44ec94 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 15:35:27 +0200 Subject: ethernet: T1637: call remove() on interface deletion --- python/vyos/ifconfig.py | 2 +- src/conf_mode/interface-ethernet.py | 171 +++++++++++++++++++----------------- 2 files changed, 90 insertions(+), 83 deletions(-) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 980577965..c0d1660d1 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -130,7 +130,7 @@ class Interface: self._del_dhcpv6() # Ethernet interfaces can not be removed - if type(self) == type(EthernetIf): + if type(self) == type(EthernetIf(self._ifname)): return # NOTE (Improvement): diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 5bfe35767..3faaef96f 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -215,6 +215,9 @@ def get_config(): def verify(eth): + if eth['deleted']: + return None + if eth['speed'] == 'auto': if eth['duplex'] != 'auto': raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too') @@ -242,89 +245,93 @@ def generate(eth): def apply(eth): e = EthernetIf(eth['intf']) - # update interface description used e.g. within SNMP - e.ifalias = eth['description'] - - # - # missing DHCP/DHCPv6 options go here - # - - # ignore link state changes - e.link_detect = eth['disable_link_detect'] - # disable ethernet flow control (pause frames) - e.set_flow_control(eth['flow_control']) - # configure ARP cache timeout in milliseconds - e.arp_cache_tmp = eth['ip_arp_cache_tmo'] - # Enable proxy-arp on this interface - e.proxy_arp = eth['ip_proxy_arp'] - # Enable private VLAN proxy ARP on this interface - e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan'] - - # Change interface MAC address - re-set to real hardware address (hw-id) - # if custom mac is removed - if eth['mac']: - e.mac = eth['mac'] - else: - e.mac = eth['hw_id'] - - # Maximum Transmission Unit (MTU) - e.mtu = eth['mtu'] - - # Set physical interface speed and duplex - e.set_speed_duplex(eth['speed'], eth['duplex']) - - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in eth['address_remove']: - e.del_addr(addr) - for addr in eth['address']: - e.add_addr(addr) - - # Enable/Disable interface - if eth['disable']: - e.state = 'down' + if eth['deleted']: + # delete interface + e.remove() else: - e.state = 'up' - - # remove no longer required service VLAN interfaces (vif-s) - for vif_s in eth['vif_s_remove']: - e.del_vlan(vif_s) - - # create service VLAN interfaces (vif-s) - for vif_s in eth['vif_s']: - s_vlan = e.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) - apply_vlan_config(s_vlan, vif_s) - - # remove no longer required client VLAN interfaces (vif-c) - # on lower service VLAN interface - for vif_c in vif_s['vif_c_remove']: - s_vlan.del_vlan(vif_c) - - # create client VLAN interfaces (vif-c) - # on lower service VLAN interface - for vif_c in vif_s['vif_c']: - c_vlan = s_vlan.add_vlan(vif_c['id']) - apply_vlan_config(c_vlan, vif_c) - - # remove no longer required VLAN interfaces (vif) - for vif in eth['vif_remove']: - e.del_vlan(vif) - - # create VLAN interfaces (vif) - for vif in eth['vif']: - # QoS priority mapping can only be set during interface creation - # so we delete the interface first if required. - if vif['egress_qos_changed'] or vif['ingress_qos_changed']: - try: - # on system bootup the above condition is true but the interface - # does not exists, which throws an exception, but that's legal - e.del_vlan(vif['id']) - except: - pass - - vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) - apply_vlan_config(vlan, vif) + # update interface description used e.g. within SNMP + e.ifalias = eth['description'] + + # + # missing DHCP/DHCPv6 options go here + # + + # ignore link state changes + e.link_detect = eth['disable_link_detect'] + # disable ethernet flow control (pause frames) + e.set_flow_control(eth['flow_control']) + # configure ARP cache timeout in milliseconds + e.arp_cache_tmp = eth['ip_arp_cache_tmo'] + # Enable proxy-arp on this interface + e.proxy_arp = eth['ip_proxy_arp'] + # Enable private VLAN proxy ARP on this interface + e.proxy_arp_pvlan = eth['ip_proxy_arp_pvlan'] + + # Change interface MAC address - re-set to real hardware address (hw-id) + # if custom mac is removed + if eth['mac']: + e.mac = eth['mac'] + else: + e.mac = eth['hw_id'] + + # Maximum Transmission Unit (MTU) + e.mtu = eth['mtu'] + + # Set physical interface speed and duplex + e.set_speed_duplex(eth['speed'], eth['duplex']) + + # Configure interface address(es) + # - not longer required addresses get removed first + # - newly addresses will be added second + for addr in eth['address_remove']: + e.del_addr(addr) + for addr in eth['address']: + e.add_addr(addr) + + # Enable/Disable interface + if eth['disable']: + e.state = 'down' + else: + e.state = 'up' + + # remove no longer required service VLAN interfaces (vif-s) + for vif_s in eth['vif_s_remove']: + e.del_vlan(vif_s) + + # create service VLAN interfaces (vif-s) + for vif_s in eth['vif_s']: + s_vlan = e.add_vlan(vif_s['id'], ethertype=vif_s['ethertype']) + apply_vlan_config(s_vlan, vif_s) + + # remove no longer required client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c_remove']: + s_vlan.del_vlan(vif_c) + + # create client VLAN interfaces (vif-c) + # on lower service VLAN interface + for vif_c in vif_s['vif_c']: + c_vlan = s_vlan.add_vlan(vif_c['id']) + apply_vlan_config(c_vlan, vif_c) + + # remove no longer required VLAN interfaces (vif) + for vif in eth['vif_remove']: + e.del_vlan(vif) + + # create VLAN interfaces (vif) + for vif in eth['vif']: + # QoS priority mapping can only be set during interface creation + # so we delete the interface first if required. + if vif['egress_qos_changed'] or vif['ingress_qos_changed']: + try: + # on system bootup the above condition is true but the interface + # does not exists, which throws an exception, but that's legal + e.del_vlan(vif['id']) + except: + pass + + vlan = e.add_vlan(vif['id'], ingress_qos=vif['ingress_qos'], egress_qos=vif['egress_qos']) + apply_vlan_config(vlan, vif) return None -- cgit v1.2.3 From 879036a4db0517dc332c4e2ce3c806495a5fe401 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 16:02:50 +0200 Subject: Python/ifconfig: T1557: delete all assigned IP addresses on remove() --- python/vyos/ifconfig.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index c0d1660d1..4bb320e21 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -129,6 +129,12 @@ class Interface: self._del_dhcp() self._del_dhcpv6() + # remove all assigned IP addresses from interface - this is a bit redundant + # as the kernel will remove all addresses on interface deletion, but we + # can not delete ALL interfaces, see below + for addr in self.get_addr(): + self.del_addr(addr) + # Ethernet interfaces can not be removed if type(self) == type(EthernetIf(self._ifname)): return -- cgit v1.2.3 From 1df0c00879f21dcbb87bdf09291b0c74779e77cb Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 16:23:07 +0200 Subject: Python/ifconfig: T1557: update comments --- python/vyos/ifconfig.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index 4bb320e21..d73e2d43e 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1050,6 +1050,11 @@ class VLANIf(Interface): Linux internal packet priority on incoming frames. @param ingress_qos: Defines a mapping of Linux internal packet priority to VLAN header prio field but for outgoing frames. + + Example: + >>> from vyos.ifconfig import VLANIf + >>> i = VLANIf('eth0') + >>> i.add_vlan(10) """ vlan_ifname = self._ifname + '.' + str(vlan_id) if not os.path.exists('/sys/class/net/{}'.format(vlan_ifname)): @@ -1078,11 +1083,17 @@ class VLANIf(Interface): # or interface description and so on return VLANIf(vlan_ifname) + def del_vlan(self, vlan_id): """ Remove VLAN interface from operating system. Removing the interface deconfigures all assigned IP addresses and clear possible DHCP(v6) client processes. + + Example: + >>> from vyos.ifconfig import VLANIf + >>> i = VLANIf('eth0.10') + >>> i.del_vlan() """ vlan_ifname = self._ifname + '.' + str(vlan_id) tmp = VLANIf(vlan_ifname) @@ -1100,16 +1111,29 @@ class EthernetIf(VLANIf): """ Return the driver name used by NIC. Some NICs don't support all features e.g. changing link-speed, duplex + + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.get_driver_name() + 'vmxnet3' """ link = os.readlink('/sys/class/net/{}/device/driver/module'.format(self._ifname)) return os.path.basename(link) + def has_autoneg(self): """ Not all drivers support autonegotiation. returns True -> Autonegotiation is supported by driver False -> Autonegotiation is not supported by driver + + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.has_autoneg() + 'True' """ regex = 'Supports auto-negotiation:[ ]\w+' tmp = self._cmd('/sbin/ethtool {}'.format(self._ifname)) @@ -1122,11 +1146,17 @@ class EthernetIf(VLANIf): else: return False + def set_flow_control(self, enable): """ Changes the pause parameters of the specified Ethernet device. @param enable: true -> enable pause frames, false -> disable pause frames + + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_flow_control(True) """ if enable not in ['on', 'off']: raise ValueError("Value out of range") @@ -1146,12 +1176,18 @@ class EthernetIf(VLANIf): except CalledProcessError: pass + def set_speed_duplex(self, speed, duplex): """ Set link speed in Mbit/s and duplex. @speed can be any link speed in MBit/s, e.g. 10, 100, 1000 auto @duplex can be half, full, auto + + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_speed_duplex('auto', 'auto') """ if speed not in ['auto', '10', '100', '1000', '2500', '5000', '10000', '25000', '40000', '50000', '100000', '400000']: -- cgit v1.2.3 From b53006d672ba008387bd3e3552070c30a4dca657 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 16:40:44 +0200 Subject: Python/ifconfig: T1557: ethernet: add offloading interfaces --- python/vyos/ifconfig.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py index d73e2d43e..777b185a6 100644 --- a/python/vyos/ifconfig.py +++ b/python/vyos/ifconfig.py @@ -1211,6 +1211,76 @@ class EthernetIf(VLANIf): return self._cmd(cmd) + def set_gro(self, state): + """ + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_gro('on') + """ + if state not in ['on', 'off']: + raise ValueError('state must be "on" or "off"') + + cmd = '/sbin/ethtool -K {} gro {}'.format(self._ifname, state) + return self._cmd(cmd) + + + def set_gso(self, state): + """ + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_gso('on') + """ + if state not in ['on', 'off']: + raise ValueError('state must be "on" or "off"') + + cmd = '/sbin/ethtool -K {} gso {}'.format(self._ifname, state) + return self._cmd(cmd) + + + def set_sg(self, state): + """ + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_sg('on') + """ + if state not in ['on', 'off']: + raise ValueError('state must be "on" or "off"') + + cmd = '/sbin/ethtool -K {} sg {}'.format(self._ifname, state) + return self._cmd(cmd) + + + def set_tso(self, state): + """ + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_tso('on') + """ + if state not in ['on', 'off']: + raise ValueError('state must be "on" or "off"') + + cmd = '/sbin/ethtool -K {} tso {}'.format(self._ifname, state) + return self._cmd(cmd) + + + def set_ufo(self, state): + """ + Example: + >>> from vyos.ifconfig import EthernetIf + >>> i = EthernetIf('eth0') + >>> i.set_udp_offload('on') + """ + if state not in ['on', 'off']: + raise ValueError('state must be "on" or "off"') + + cmd = '/sbin/ethtool -K {} ufo {}'.format(self._ifname, state) + return self._cmd(cmd) + + class BondIf(VLANIf): """ The Linux bonding driver provides a method for aggregating multiple network -- cgit v1.2.3 From 63a9d416092e24da02f7d180f4feb6a98bfe85f6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 16:41:05 +0200 Subject: ethernet: T1637: support offloading functions --- src/conf_mode/interface-ethernet.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 3faaef96f..72eaee905 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -44,6 +44,11 @@ default_config_data = { 'intf': '', 'mac': '', 'mtu': 1500, + 'offload_gro': 'off', + 'offload_gso': 'off', + 'offload_sg': 'off', + 'offload_tso': 'off', + 'offload_ufo': 'off', 'speed': 'auto', 'vif_s': [], 'vif_s_remove': [], @@ -179,6 +184,26 @@ def get_config(): if conf.exists('mtu'): eth['mtu'] = int(conf.return_value('mtu')) + # GRO (generic receive offload) + if conf.exists('offload-options generic-receive'): + eth['offload_gro'] = conf.return_value('offload-options generic-receive') + + # GSO (generic segmentation offload) + if conf.exists('offload-options generic-segmentation'): + eth['offload_gso'] = conf.return_value('offload-options generic-segmentation') + + # scatter-gather option + if conf.exists('offload-options scatter-gather'): + eth['offload_sg'] = conf.return_value('offload-options scatter-gather') + + # TSO (TCP segmentation offloading) + if conf.exists('offload-options tcp-segmentation'): + eth['offload_tso'] = conf.return_value('offload-options tcp-segmentation') + + # UDP fragmentation offloading + if conf.exists('offload-options udp-fragmentation'): + eth['offload_ufo'] = conf.return_value('offload-options udp-fragmentation') + # interface speed if conf.exists('speed'): eth['speed'] = conf.return_value('speed') @@ -277,6 +302,21 @@ def apply(eth): # Maximum Transmission Unit (MTU) e.mtu = eth['mtu'] + # GRO (generic receive offload) + e.set_gro(eth['offload_gro']) + + # GSO (generic segmentation offload) + e.set_gso(eth['offload_gso']) + + # scatter-gather option + e.set_sg(eth['offload_sg']) + + # TSO (TCP segmentation offloading) + e.set_tso(eth['offload_tso']) + + # UDP fragmentation offloading + e.set_ufo(eth['offload_ufo']) + # Set physical interface speed and duplex e.set_speed_duplex(eth['speed'], eth['duplex']) -- cgit v1.2.3 From 95b88584fd9239d3143adc6b7935544df208ac88 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 19:30:55 +0200 Subject: ethernet: T1637: do not overwrite interface description with interface name --- src/conf_mode/interface-ethernet.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 72eaee905..8a72068c5 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -125,8 +125,6 @@ def get_config(): # retrieve interface description if conf.exists('description'): eth['description'] = conf.return_value('description') - else: - eth['description'] = eth['intf'] # get DHCP client identifier if conf.exists('dhcp-options client-id'): -- cgit v1.2.3 From 3bf0a5e586e9139e80cdecd14ab326cee5512fae Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 15 Sep 2019 20:40:52 +0200 Subject: ethernet: T1637: fix calling arp_cache_tmo property --- 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 8a72068c5..4f455e8d3 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -284,7 +284,7 @@ def apply(eth): # disable ethernet flow control (pause frames) e.set_flow_control(eth['flow_control']) # configure ARP cache timeout in milliseconds - e.arp_cache_tmp = eth['ip_arp_cache_tmo'] + e.arp_cache_tmo = eth['ip_arp_cache_tmo'] # Enable proxy-arp on this interface e.proxy_arp = eth['ip_proxy_arp'] # Enable private VLAN proxy ARP on this interface -- cgit v1.2.3 From b27482c0201a433762168abe2d369ea43a599f1c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 19 Sep 2019 22:29:11 +0200 Subject: ethernet: T1637: interfaces in a bond can be disabled --- src/conf_mode/interface-ethernet.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py index 4f455e8d3..f82105847 100755 --- a/src/conf_mode/interface-ethernet.py +++ b/src/conf_mode/interface-ethernet.py @@ -253,12 +253,10 @@ def verify(eth): # some options can not be changed when interface is enslaved to a bond for bond in conf.list_nodes('interfaces bonding'): if conf.exists('interfaces bonding ' + bond + ' member interface'): - if eth['name'] in conf.return_values('interfaces bonding ' + bond + ' member interface'): - if eth['disable']: - raise ConfigError('Can not disable interface {} which is a member of {}').format(eth['intf'], bond) - - if eth['address']: - raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond) + bond_member = conf.return_values('interfaces bonding ' + bond + ' member interface') + if eth['name'] in bond_member: + if eth['address']: + raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond) return None -- cgit v1.2.3 From db43a81c06d1fc8c06c4e5bae753a8854bf00d2c Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sat, 21 Sep 2019 09:49:41 +0200 Subject: vxlan: T1636: simplyfy code (don't delete intf addresses) We do not need to delete addresses manually as the VXLAN interface is always deleted which drops all assigned addresses from the Kernel. --- src/conf_mode/interface-vxlan.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/conf_mode/interface-vxlan.py b/src/conf_mode/interface-vxlan.py index 59022238e..e97b4bf99 100755 --- a/src/conf_mode/interface-vxlan.py +++ b/src/conf_mode/interface-vxlan.py @@ -28,7 +28,6 @@ from netifaces import interfaces default_config_data = { 'address': [], - 'address_remove': [], 'deleted': False, 'description': '', 'disable': False, @@ -43,7 +42,6 @@ default_config_data = { # the IANA's selection of a standard destination port } - def get_config(): vxlan = deepcopy(default_config_data) conf = Config() @@ -66,12 +64,6 @@ def get_config(): if conf.exists('address'): vxlan['address'] = conf.return_values('address') - # Determine interface addresses (currently effective) - to determine which - # address is no longer valid and needs to be removed from the interface - eff_addr = conf.return_effective_values('address') - act_addr = conf.return_values('address') - vxlan['address_remove'] = list_diff(eff_addr, act_addr) - # retrieve interface description if conf.exists('description'): vxlan['description'] = conf.return_value('description') @@ -180,11 +172,9 @@ def apply(vxlan): # Enable proxy-arp on this interface v.proxy_arp = vxlan['ip_proxy_arp'] - # Configure interface address(es) - # - not longer required addresses get removed first - # - newly addresses will be added second - for addr in vxlan['address_remove']: - v.del_addr(addr) + # Configure interface address(es) - no need to implicitly delete the + # old addresses as they have already been removed by deleting the + # interface above for addr in vxlan['address']: v.add_addr(addr) -- cgit v1.2.3 From 2b9c84594a693c66b949183a25cc32dfcdee72e1 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Sun, 22 Sep 2019 09:02:17 +0200 Subject: Jenkins: ease Pipeline vyos-1x is a Debian package with architecture all. This means we only have to build it once as it can run on any other architecture - that's what Python is made for. --- Jenkinsfile | 263 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 147 insertions(+), 116 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 78112ce63..c48136b34 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,131 +1,162 @@ -pipeline { - agent none - stages { - stage('build-package') { - parallel { - stage('Build package amd64') { - agent { - docker { - label 'jessie-amd64' - args '--privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006 -v /tmp:/tmp' - image 'vyos/vyos-build:current' - } +// Copyright (C) 2019 VyOS maintainers and contributors +// +// This program is free software; you can redistribute it and/or modify +// in order to easy exprort images built to "external" world +// 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 . + +@NonCPS + +def getGitBranchName() { + def branch = scm.branches[0].name + return branch.split('/')[-1] +} + +def getGitRepoURL() { + return scm.userRemoteConfigs[0].url +} + +// Returns true if this is a custom build launched on any project fork, +// returns false if this is build from git@github.com:vyos/env.JOB_NAME +// env.JOB_NAME is e.g. vyos-build or vyos-1x and so on .... +def isCustomBuild() { + // GitHub organisation base URL + def gitURI = 'git@github.com:vyos/' + env.JOB_NAME + def httpURI = 'https://github.com/vyos' + env.JOB_NAME + + return ! ((getGitRepoURL() == gitURI) || (getGitRepoURL() == httpURI)) +} + +def setDescription() { + def item = Jenkins.instance.getItemByFullName(env.JOB_NAME) + + // build up the main description text + def description = "" + description += "

Build VyOS ISO image

" + + if (isCustomBuild()) { + description += "

" + description += "Build not started from official Git repository!
" + description += "
" + description += "Repository: " + getGitRepoURL() + "
" + description += "Branch: " + getGitBranchName() + "
" + description += "

" + } else { + description += "Sources taken from Git branch: " + getGitBranchName() + "
" + } + + item.setDescription(description) + item.save() +} - } - steps { - sh '''#!/bin/bash -git clone --single-branch --branch $GIT_BRANCH $GIT_URL $BUILD_NUMBER -cd $BUILD_NUMBER -sudo pip3 uninstall vyos -y || true -sudo pip3 install -r test-requirements.txt -python3 -m "pylint" src -r n --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" > pylint-report.txt || true -make test -sudo apt-get -o Acquire::Check-Valid-Until=false update -sudo mk-build-deps -i -r -t \'apt-get --no-install-recommends -yq\' debian/control -dpkg-buildpackage -b -us -uc -tc -mkdir -p /tmp/$GIT_BRANCH/packages/script -mv ../*.deb /tmp/$GIT_BRANCH/packages/''' - } +/* Only keep the 10 most recent builds. */ +def projectProperties = [ + [$class: 'BuildDiscarderProperty',strategy: [$class: 'LogRotator', numToKeepStr: '1']], +] + +properties(projectProperties) +setDescription() + +pipeline { + agent { + docker { + label 'Docker' + args '--sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006' + image 'vyos/vyos-build:current' } - stage('Build package armhf') { - agent { - docker { - label 'jessie-amd64' - image 'vyos-build-armhf:current' - args '--privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006 -v /tmp:/tmp' + } + options { + disableConcurrentBuilds() + skipDefaultCheckout() + timeout(time: 30, unit: 'MINUTES') + timestamps() + } + stages { + stage('Fetch') { + steps { + script { + dir('build') { + git branch: getGitBranchName(), url: getGitRepoURL() + } + } } - - } - steps { - sh '''#!/bin/bash -git clone --single-branch --branch $GIT_BRANCH $GIT_URL $BUILD_NUMBER -cd $BUILD_NUMBER -sudo pip3 uninstall vyos -y || true -sudo pip3 install -r test-requirements.txt -python3 -m "pylint" src -r n --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" > pylint-report.txt || true -make test -sudo apt-get -o Acquire::Check-Valid-Until=false update -sudo mk-build-deps -i -r -t \'apt-get --no-install-recommends -yq\' debian/control -dpkg-buildpackage -b -us -uc -tc -mkdir -p /tmp/$GIT_BRANCH/packages/script -mv ../*.deb /tmp/$GIT_BRANCH/packages/''' - } } - stage('Build package arm64') { - agent { - docker { - label 'jessie-amd64' - args '--privileged --sysctl net.ipv6.conf.lo.disable_ipv6=0 -e GOSU_UID=1006 -e GOSU_GID=1006 -v /tmp:/tmp' - image 'vyos-build-arm64:current' + stage('Build') { + steps { + script { + dir('build') { + sh """ + #!/bin/bash + sudo apt-get -o Acquire::Check-Valid-Until=false update + sudo mk-build-deps -i -r -t \'apt-get --no-install-recommends -yq\' debian/control + dpkg-buildpackage -b -us -uc -tc + """ + } + } } - - } - steps { - sh '''#!/bin/bash -git clone --single-branch --branch $GIT_BRANCH $GIT_URL $BUILD_NUMBER -cd $BUILD_NUMBER -sudo pip3 uninstall vyos -y || true -sudo pip3 install -r test-requirements.txt -python3 -m "pylint" src -r n --msg-template="{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" > pylint-report.txt || true -make test -sudo apt-get -o Acquire::Check-Valid-Until=false update -sudo mk-build-deps -i -r -t \'apt-get --no-install-recommends -yq\' debian/control -dpkg-buildpackage -b -us -uc -tc -mkdir -p /tmp/$GIT_BRANCH/packages/script -mv ../*.deb /tmp/$GIT_BRANCH/packages/''' - } } - } } - stage('Deploy packages') { - agent { - node { - label 'jessie-amd64' + post { + cleanup { + deleteDir() } + success { + script { + // archive *.deb artifact on custom builds, deploy to repo otherwise + if ( isCustomBuild()) { + archiveArtifacts artifacts: '*.deb', fingerprint: true + } else { + // publish build result, using SSH-dev.packages.vyos.net Jenkins Credentials + sshagent(['SSH-dev.packages.vyos.net']) { + // build up some fancy groovy variables so we do not need to write/copy + // every option over and over again! - } - steps { - sh '''#!/bin/bash -cd /tmp/$GIT_BRANCH/packages/script -/var/lib/vyos-build/pkg-build.sh $GIT_BRANCH''' - } - } - stage('Cleanup') { - parallel { - stage('Cleanup amd64') { - agent { - node { - label 'jessie-amd64' - } + def VYOS_REPO_PATH = '/home/sentrium/web/dev.packages.vyos.net/public_html/repositories/' + getGitBranchName() + '/' + if (getGitBranchName() != "equuleus") + VYOS_REPO_PATH += 'vyos/' - } - steps { - cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, cleanupMatrixParent: true, deleteDirs: true, disableDeferredWipeout: true) - } - } - stage('Cleanup armhf') { - agent { - node { - label 'jessie-amd64' - } + def SSH_OPTS = '-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR' + def SSH_REMOTE = 'khagen@10.217.48.113' - } - steps { - cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, cleanupMatrixParent: true, deleteDirs: true, disableDeferredWipeout: true) - } - } - stage('Cleanup arm64') { - agent { - node { - label 'jessie-amd64' - } + echo "Uploading package(s) and updating package(s) in the repository ..." + + files = findFiles(glob: '*.deb') + files.each { PACKAGE -> + def RELEASE = getGitBranchName() + def ARCH = sh(returnStdout: true, script: "dpkg-deb -f ${pkg} Architecture").trim() + def SUBSTRING = sh(returnStdout: true, script: "dpkg-deb -f ${pkg} Package").trim() + def SSH_DIR = '~/VyOS/' + RELEASE + '/' + ARCH - } - steps { - cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, cleanupMatrixParent: true, deleteDirs: true, disableDeferredWipeout: true) - } + // No need to explicitly check the return code. The pipeline + // will fail if sh returns a non 0 exit code + sh """ + ssh ${SSH_OPTS} ${SSH_REMOTE} -t "bash --login -c 'mkdir -p ${SSH_DIR}'" + """ + sh """ + scp ${SSH_OPTS} ${PACKAGE} ${SSH_REMOTE}:${SSH_DIR}/ + """ + sh """ + ssh ${SSH_OPTS} ${SSH_REMOTE} -t "uncron-add 'reprepro -v -b ${VYOS_REPO_PATH} -A ${ARCH} remove ${RELEASE} ${SUBSTRING}'" + """ + sh """ + ssh ${SSH_OPTS} ${SSH_REMOTE} -t "uncron-add 'reprepro -v -b ${VYOS_REPO_PATH} deleteunreferenced'" + """ + sh """ + ssh ${SSH_OPTS} ${SSH_REMOTE} -t "uncron-add 'reprepro -v -b ${VYOS_REPO_PATH} -A ${ARCH} includedeb ${RELEASE} ${SSH_DIR}/${PACKAGE}'" + """ + } + } + } + } } - } } - } } + -- cgit v1.2.3