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