summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface-definitions/system-login.xml.in5
-rw-r--r--op-mode-definitions/show-interfaces-ethernet.xml.in2
-rw-r--r--python/vyos/ethtool.py82
-rw-r--r--python/vyos/ifconfig/ethernet.py79
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py11
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-2123
6 files changed, 134 insertions, 68 deletions
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
index 3c2c7dfa5..f4613b8a2 100644
--- a/interface-definitions/system-login.xml.in
+++ b/interface-definitions/system-login.xml.in
@@ -12,7 +12,7 @@
<properties>
<help>Local user account information</help>
<constraint>
- <regex>[a-zA-Z0-9\-_\.]{1,100}</regex>
+ <regex>^[-_a-zA-Z0-9.]{1,100}</regex>
</constraint>
<constraintErrorMessage>Username contains illegal characters or\nexceeds 100 character limitation.</constraintErrorMessage>
</properties>
@@ -44,6 +44,9 @@
<tagNode name="public-keys">
<properties>
<help>Remote access public keys</help>
+ <constraint>
+ <regex>^[-_a-zA-Z0-9@]+$</regex>
+ </constraint>
<valueHelp>
<format>txt</format>
<description>Key identifier used by ssh-keygen (usually of form user@host)</description>
diff --git a/op-mode-definitions/show-interfaces-ethernet.xml.in b/op-mode-definitions/show-interfaces-ethernet.xml.in
index 6d50d6e90..c42efd21f 100644
--- a/op-mode-definitions/show-interfaces-ethernet.xml.in
+++ b/op-mode-definitions/show-interfaces-ethernet.xml.in
@@ -29,7 +29,7 @@
<properties>
<help>Show physical device information for specified ethernet interface</help>
</properties>
- <command>ethtool "$4"; ethtool --driver "$4"</command>
+ <command>ethtool "$4"; ethtool --show-ring "$4"; ethtool --driver "$4"</command>
<children>
<leafNode name="offload">
<properties>
diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 7dcb68346..609d83b5e 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -13,7 +13,9 @@
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
+import os
import re
+
from vyos.util import popen
class Ethtool:
@@ -41,8 +43,22 @@ class Ethtool:
# }
_speed_duplex = { }
_ring_buffers = { }
+ _ring_buffers_max = { }
+ _driver_name = None
+ _auto_negotiation = None
+ _flow_control = None
+ _flow_control_enabled = None
def __init__(self, ifname):
+ # Get driver used for interface
+ sysfs_file = f'/sys/class/net/{ifname}/device/driver/module'
+ if os.path.exists(sysfs_file):
+ link = os.readlink(sysfs_file)
+ self._driver_name = os.path.basename(link)
+
+ if not self._driver_name:
+ raise ValueError(f'Could not determine driver for interface {ifname}!')
+
# Build a dictinary of supported link-speed and dupley settings.
out, err = popen(f'ethtool {ifname}')
reading = False
@@ -53,7 +69,6 @@ class Ethtool:
reading = True
if 'Supported pause frame use:' in line:
reading = False
- break
if reading:
for block in line.split():
if pattern.search(block):
@@ -63,6 +78,15 @@ class Ethtool:
self._speed_duplex.update({ speed : {}})
if duplex not in self._speed_duplex[speed]:
self._speed_duplex[speed].update({ duplex : ''})
+ if 'Auto-negotiation:' in line:
+ # Split the following string: Auto-negotiation: off
+ # we are only interested in off or on
+ tmp = line.split()[-1]
+ self._auto_negotiation = bool(tmp == 'on')
+
+ if self._auto_negotiation == None:
+ raise ValueError(f'Could not determine auto-negotiation settings '\
+ f'for interface {ifname}!')
# Now populate features dictionaty
out, err = popen(f'ethtool --show-features {ifname}')
@@ -78,7 +102,7 @@ class Ethtool:
'fixed' : fixed
}
- out, err = popen(f'ethtool -g {ifname}')
+ out, err = popen(f'ethtool --show-ring {ifname}')
# We are only interested in line 2-5 which contains the device maximum
# ringbuffers
for line in out.splitlines()[2:6]:
@@ -89,7 +113,26 @@ class Ethtool:
# output format from 0 -> n/a. As we are only interested in the
# tx/rx keys we do not care about RX Mini/Jumbo.
if value.isdigit():
- self._ring_buffers[key] = int(value)
+ self._ring_buffers_max[key] = value
+ # Now we wan't to get the current RX/TX ringbuffer values - used for
+ for line in out.splitlines()[7:11]:
+ if ':' in line:
+ key, value = [s.strip() for s in line.strip().split(":", 1)]
+ key = key.lower().replace(' ', '_')
+ # T3645: ethtool version used on Debian Bullseye changed the
+ # output format from 0 -> n/a. As we are only interested in the
+ # tx/rx keys we do not care about RX Mini/Jumbo.
+ if value.isdigit():
+ self._ring_buffers[key] = value
+
+ # Get current flow control settings, but this is not supported by
+ # all NICs (e.g. vmxnet3 does not support is)
+ out, err = popen(f'ethtool --show-pause {ifname}')
+ if len(out.splitlines()) > 1:
+ self._flow_control = True
+ # read current flow control setting, this returns:
+ # ['Autonegotiate:', 'on']
+ self._flow_control_enabled = out.splitlines()[1].split()[-1]
def _get_generic(self, feature):
"""
@@ -122,15 +165,19 @@ class Ethtool:
def get_tcp_segmentation_offload(self):
return self._get_generic('tcp-segmentation-offload')
- def get_rx_buffer(self):
- # Configuration of RX ring-buffers is not supported on every device,
+ def get_ring_buffer_max(self, rx_tx):
+ # Configuration of RX/TX ring-buffers is not supported on every device,
# thus when it's impossible return None
- return self._ring_buffers.get('rx', None)
+ if rx_tx not in ['rx', 'tx']:
+ ValueError('Ring-buffer type must be either "rx" or "tx"')
+ return self._ring_buffers_max.get(rx_tx, None)
- def get_tx_buffer(self):
- # Configuration of TX ring-buffers is not supported on every device,
+ def get_ring_buffer(self, rx_tx):
+ # Configuration of RX/TX ring-buffers is not supported on every device,
# thus when it's impossible return None
- return self._ring_buffers.get('tx', None)
+ if rx_tx not in ['rx', 'tx']:
+ ValueError('Ring-buffer type must be either "rx" or "tx"')
+ return str(self._ring_buffers.get(rx_tx, None))
def check_speed_duplex(self, speed, duplex):
""" Check if the passed speed and duplex combination is supported by
@@ -142,8 +189,25 @@ class Ethtool:
if duplex not in ['full', 'half']:
raise ValueError(f'Value "{duplex}" for duplex is invalid!')
+ if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']:
+ return False
+
if speed in self._speed_duplex:
if duplex in self._speed_duplex[speed]:
return True
return False
+ def check_flow_control(self):
+ """ Check if the NIC supports flow-control """
+ if self._driver_name in ['vmxnet3', 'virtio_net', 'xen_netfront']:
+ return False
+ return self._flow_control
+
+ def get_flow_control(self):
+ if self._flow_control_enabled == None:
+ raise ValueError('Interface does not support changing '\
+ 'flow-control settings!')
+ return self._flow_control_enabled
+
+ def get_auto_negotiation(self):
+ return self._auto_negotiation
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index 76ed3fd92..7bd269491 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -20,6 +20,7 @@ from vyos.ethtool import Ethtool
from vyos.ifconfig.interface import Interface
from vyos.util import run
from vyos.util import dict_search
+from vyos.util import read_file
from vyos.validate import assert_list
@Interface.register
@@ -120,38 +121,16 @@ class EthernetIf(Interface):
'flow control settings!')
return
- # Get current flow control settings:
- cmd = f'ethtool --show-pause {ifname}'
- output, code = self._popen(cmd)
- if code == 76:
- # the interface does not support it
- return ''
- if code:
- # never fail here as it prevent vyos to boot
- print(f'unexpected return code {code} from {cmd}')
- return ''
-
- # The above command returns - with tabs:
- #
- # Pause parameters for eth0:
- # Autonegotiate: on
- # RX: off
- # TX: off
- if re.search("Autonegotiate:\ton", output):
- if enable == "on":
- # flowcontrol is already enabled - no need to re-enable it again
- # this will prevent the interface from flapping as applying the
- # flow-control settings will take the interface down and bring
- # it back up every time.
- return ''
-
- # Assemble command executed on system. Unfortunately there is no way
- # to change this setting via sysfs
- cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}'
- output, code = self._popen(cmd)
- if code:
- print(f'could not set flowcontrol for {ifname}')
- return output
+ current = self.ethtool.get_flow_control()
+ if current != enable:
+ # Assemble command executed on system. Unfortunately there is no way
+ # to change this setting via sysfs
+ cmd = f'ethtool --pause {ifname} autoneg {enable} tx {enable} rx {enable}'
+ output, code = self._popen(cmd)
+ if code:
+ print(f'Could not set flowcontrol for {ifname}')
+ return output
+ return None
def set_speed_duplex(self, speed, duplex):
"""
@@ -181,32 +160,19 @@ class EthernetIf(Interface):
# Get current speed and duplex settings:
ifname = self.config['ifname']
- cmd = f'ethtool {ifname}'
- tmp = self._cmd(cmd)
-
- if re.search("\tAuto-negotiation: on", tmp):
+ if self.ethtool.get_auto_negotiation():
if speed == 'auto' and duplex == 'auto':
# bail out early as nothing is to change
return
else:
# read in current speed and duplex settings
- cur_speed = 0
- cur_duplex = ''
- for line in tmp.splitlines():
- if line.lstrip().startswith("Speed:"):
- non_decimal = re.compile(r'[^\d.]+')
- cur_speed = non_decimal.sub('', line)
- continue
-
- if line.lstrip().startswith("Duplex:"):
- cur_duplex = line.split()[-1].lower()
- break
-
+ cur_speed = read_file(f'/sys/class/net/{ifname}/speed')
+ cur_duplex = read_file(f'/sys/class/net/{ifname}/duplex')
if (cur_speed == speed) and (cur_duplex == duplex):
# bail out early as nothing is to change
return
- cmd = f'ethtool -s {ifname}'
+ cmd = f'ethtool --change {ifname}'
if speed == 'auto' or duplex == 'auto':
cmd += ' autoneg on'
else:
@@ -328,21 +294,26 @@ class EthernetIf(Interface):
print('Adapter does not support changing tcp-segmentation-offload settings!')
return False
- def set_ring_buffer(self, b_type, b_size):
+ def set_ring_buffer(self, rx_tx, size):
"""
Example:
>>> from vyos.ifconfig import EthernetIf
>>> i = EthernetIf('eth0')
>>> i.set_ring_buffer('rx', '4096')
"""
+ current_size = self.ethtool.get_ring_buffer(rx_tx)
+ if current_size == size:
+ # bail out early if nothing is about to change
+ return None
+
ifname = self.config['ifname']
- cmd = f'ethtool -G {ifname} {b_type} {b_size}'
+ cmd = f'ethtool --set-ring {ifname} {rx_tx} {size}'
output, code = self._popen(cmd)
# ethtool error codes:
# 80 - value already setted
# 81 - does not possible to set value
if code and code != 80:
- print(f'could not set "{b_type}" ring-buffer for {ifname}')
+ print(f'could not set "{rx_tx}" ring-buffer for {ifname}')
return output
def update(self, config):
@@ -381,8 +352,8 @@ class EthernetIf(Interface):
# Set interface ring buffer
if 'ring_buffer' in config:
- for b_type in config['ring_buffer']:
- self.set_ring_buffer(b_type, config['ring_buffer'][b_type])
+ for rx_tx, size in config['ring_buffer'].items():
+ self.set_ring_buffer(rx_tx, size)
# call base class first
super().update(config)
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 27c4a7c38..81ed36bf2 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -85,14 +85,19 @@ def verify(ethernet):
speed = ethernet['speed']
duplex = ethernet['duplex']
if not ethtool.check_speed_duplex(speed, duplex):
- raise ConfigError(f'Adapter does not support speed "{speed}" and duplex "{duplex}"!')
+ raise ConfigError(f'Adapter does not support changing speed and duplex '\
+ f'settings to: {speed}/{duplex}!')
+
+ if 'disable_flow_control' in ethernet:
+ if not ethtool.check_flow_control():
+ raise ConfigError('Adapter does not support changing flow-control settings!')
if 'ring_buffer' in ethernet:
- max_rx = ethtool.get_rx_buffer()
+ max_rx = ethtool.get_ring_buffer_max('rx')
if not max_rx:
raise ConfigError('Driver does not support RX ring-buffer configuration!')
- max_tx = ethtool.get_tx_buffer()
+ max_tx = ethtool.get_ring_buffer_max('tx')
if not max_tx:
raise ConfigError('Driver does not support TX ring-buffer configuration!')
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index 4b0e70d35..0bd858760 100755
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -89,6 +89,29 @@ for ifname in config.list_nodes(base):
if config.exists(base + [ifname, 'offload', 'ufo']):
config.delete(base + [ifname, 'offload', 'ufo'])
+ # Also while processing the interface configuration, not all adapters support
+ # changing the speed and duplex settings. If the desired speed and duplex
+ # values do not work for the NIC driver, we change them back to the default
+ # value of "auto" - which will be applied if the CLI node is deleted.
+ speed_path = base + [ifname, 'speed']
+ duplex_path = base + [ifname, 'duplex']
+ # speed and duplex must always be set at the same time if not set to "auto"
+ if config.exists(speed_path) and config.exists(duplex_path):
+ speed = config.return_value(speed_path)
+ duplex = config.return_value(duplex_path)
+ if speed != 'auto' and duplex != 'auto':
+ if not eth.check_speed_duplex(speed, duplex):
+ config.delete(speed_path)
+ config.delete(duplex_path)
+
+ # Also while processing the interface configuration, not all adapters support
+ # changing disabling flow-control - or change this setting. If disabling
+ # flow-control is not supported by the NIC, we remove the setting from CLI
+ flow_control_path = base + [ifname, 'disable-flow-control']
+ if config.exists(flow_control_path):
+ if not eth.check_flow_control():
+ config.delete(flow_control_path)
+
try:
with open(file_name, 'w') as f:
f.write(config.to_string())