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(+) (limited to 'python/vyos/ifconfig.py') 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 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(-) (limited to 'python/vyos/ifconfig.py') 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 (limited to 'python/vyos/ifconfig.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(-) (limited to 'python/vyos/ifconfig.py') 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 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(-) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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 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(-) (limited to 'python/vyos/ifconfig.py') 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 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(+) (limited to 'python/vyos/ifconfig.py') 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 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(-) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(-) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(-) (limited to 'python/vyos/ifconfig.py') 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 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(-) (limited to 'python/vyos/ifconfig.py') 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(-) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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(+) (limited to 'python/vyos/ifconfig.py') 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