summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile6
-rw-r--r--interface-definitions/interfaces-ethernet.xml872
-rw-r--r--python/vyos/configdict.py22
-rw-r--r--python/vyos/ifconfig.py377
-rwxr-xr-xsrc/conf_mode/interface-bonding.py7
-rwxr-xr-xsrc/conf_mode/interface-bridge.py3
-rwxr-xr-xsrc/conf_mode/interface-ethernet.py382
7 files changed, 1590 insertions, 79 deletions
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..9244f3b5f
--- /dev/null
+++ b/interface-definitions/interfaces-ethernet.xml
@@ -0,0 +1,872 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="interfaces">
+ <children>
+ <tagNode name="ethernet" owner="${vyos_conf_scripts_dir}/interface-ethernet.py">
+ <properties>
+ <help>Ethernet interface name</help>
+ <priority>318</priority>
+ <constraint>
+ <regex>((eth|lan)[0-9]+|(eno|ens|enp|enx).+)$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid Ethernet interface name</constraintErrorMessage>
+ <valueHelp>
+ <format>ethN</format>
+ <description>Ethernet interface name</description>
+ </valueHelp>
+ <valueHelp>
+ <format>en[ospx]N</format>
+ <description>Ethernet interface name</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcp-options">
+ <properties>
+ <help>DHCP options</help>
+ </properties>
+ <children>
+ <leafNode name="client-id">
+ <properties>
+ <help>DHCP client identifier</help>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>DHCP client host name (overrides the system host name)</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ <priority>319</priority>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-flow-control">
+ <properties>
+ <help>Disable Ethernet flow control (pause frames)</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="duplex">
+ <properties>
+ <help>Duplex mode</help>
+ <completionHelp>
+ <list>auto half full</list>
+ </completionHelp>
+ <valueHelp>
+ <format>auto</format>
+ <description>Auto negotiation (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>half</format>
+ <description>Half duplex</description>
+ </valueHelp>
+ <valueHelp>
+ <format>full</format>
+ <description>Full duplex</description>
+ </valueHelp>
+ <constraint>
+ <regex>(auto|half|full)</regex>
+ </constraint>
+ <constraintErrorMessage>duplex must be auto, half or full</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="hw-id">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <node name="ip">
+ <children>
+ <leafNode name="arp-cache-timeout">
+ <properties>
+ <help>ARP cache entry timeout in seconds</help>
+ <valueHelp>
+ <format>1-86400</format>
+ <description>ARP cache entry timout in seconds (default 30)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ <constraintErrorMessage>ARP cache entry timeout must be between 1 and 86400 seconds</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="enable-proxy-arp">
+ <properties>
+ <help>Enable proxy-arp on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="proxy-arp-pvlan">
+ <properties>
+ <help>Enable private VLAN proxy ARP on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>68-9000</format>
+ <description>Maximum Transmission Unit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 68-9000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 68 and 9000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="offload-options">
+ <properties>
+ <help>Configurable offload options</help>
+ </properties>
+ <children>
+ <leafNode name="generic-receive">
+ <properties>
+ <help>Configure GRO (generic receive offload)</help>
+ <completionHelp>
+ <list>on off</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on</format>
+ <description>Enable GRO (generic receive offload)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>off</format>
+ <description>Disable GRO (generic receive offload)</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on|off)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="generic-segmentation">
+ <properties>
+ <help>Configure GSO (generic segmentation offload)</help>
+ <completionHelp>
+ <list>on off</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on</format>
+ <description>Enable GSO (generic segmentation offload)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>off</format>
+ <description>Disable GSO (generic segmentation offload)</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on|off)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="scatter-gather">
+ <properties>
+ <help>Configure scatter-gather option</help>
+ <completionHelp>
+ <list>on off</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on</format>
+ <description>Enable scatter-gather</description>
+ </valueHelp>
+ <valueHelp>
+ <format>off</format>
+ <description>Disable scatter-gather</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on|off)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="tcp-segmentation">
+ <properties>
+ <help>Configure TSO (TCP segmentation offloading)</help>
+ <completionHelp>
+ <list>on off</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on</format>
+ <description>Enable TSO (TCP segmentation offloading)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>off</format>
+ <description>Disable TSO (TCP segmentation offloading)</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on|off)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="udp-fragmentation">
+ <properties>
+ <help>Configure UDP fragmentation offloading</help>
+ <completionHelp>
+ <list>on off</list>
+ </completionHelp>
+ <valueHelp>
+ <format>on</format>
+ <description>Enable UDP fragmentation offloading</description>
+ </valueHelp>
+ <valueHelp>
+ <format>off</format>
+ <description>Disable UDP fragmentation offloading</description>
+ </valueHelp>
+ <constraint>
+ <regex>(on|off)</regex>
+ </constraint>
+ <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="smp-affinity">
+ <properties>
+ <help>CPU interrupt affinity mask</help>
+ <completionHelp>
+ <list>auto 10 100 1000 2500 5000 10000</list>
+ </completionHelp>
+ <valueHelp>
+ <format>auto</format>
+ <description>Auto negotiation (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hex</format>
+ <description>Bitmask representing CPUs that this NIC will interrupt</description>
+ </valueHelp>
+ <valueHelp>
+ <format>hex,hex</format>
+ <description>Bitmasks representing CPUs for interrupt and receive processing</description>
+ </valueHelp>
+ <constraint>
+ <regex>(auto)</regex>
+ <regex>[0-9a-f]+(|,[0-9a-f]+)$</regex>
+ </constraint>
+ <constraintErrorMessage>IRQ affinity mask must be hex value or auto</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="speed">
+ <properties>
+ <help>Link speed</help>
+ <completionHelp>
+ <list>auto 10 100 1000 2500 5000 10000 25000 40000 50000 100000</list>
+ </completionHelp>
+ <valueHelp>
+ <format>auto</format>
+ <description>Auto negotiation (default)</description>
+ </valueHelp>
+ <valueHelp>
+ <format>10</format>
+ <description>10 Mbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>100</format>
+ <description>100 Mbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>1000</format>
+ <description>1 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>2500</format>
+ <description>2.5 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>5000</format>
+ <description>5 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>10000</format>
+ <description>10 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>25000</format>
+ <description>25 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>40000</format>
+ <description>40 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>50000</format>
+ <description>50 Gbit/sec</description>
+ </valueHelp>
+ <valueHelp>
+ <format>100000</format>
+ <description>100 Gbit/sec</description>
+ </valueHelp>
+ <constraint>
+ <regex>(auto|10|100|1000|2500|5000|10000|25000|40000|50000|100000)</regex>
+ </constraint>
+ <constraintErrorMessage>Speed must be auto, 10, 100, 1000, 2500, 5000, 10000, 25000, 40000, 50000 or 100000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <tagNode name="vif-s">
+ <properties>
+ <help>QinQ TAG-S Virtual Local Area Network (VLAN) ID</help>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcp-options">
+ <properties>
+ <help>DHCP options</help>
+ </properties>
+ <children>
+ <leafNode name="client-id">
+ <properties>
+ <help>DHCP client identifier</help>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>DHCP client host name (overrides the system host name)</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ <priority>319</priority>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="ethertype">
+ <properties>
+ <help>Set Ethertype</help>
+ <completionHelp>
+ <list>0x88A8 0x8100</list>
+ </completionHelp>
+ <valueHelp>
+ <format>0x88A8</format>
+ <description>802.1ad</description>
+ </valueHelp>
+ <valueHelp>
+ <format>0x8100</format>
+ <description>802.1q</description>
+ </valueHelp>
+ <constraint>
+ <regex>(0x88A8|0x8100)</regex>
+ </constraint>
+ <constraintErrorMessage>Ethertype must be 0x88A8 or 0x8100</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="ip">
+ <children>
+ <leafNode name="enable-proxy-arp">
+ <properties>
+ <help>Enable proxy-arp on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="proxy-arp-pvlan">
+ <properties>
+ <help>Enable private VLAN proxy ARP on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>68-9000</format>
+ <description>Maximum Transmission Unit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 68-9000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 68 and 9000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <tagNode name="vif-c">
+ <properties>
+ <help>QinQ TAG-C Virtual Local Area Network (VLAN) ID</help>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcp-options">
+ <properties>
+ <help>DHCP options</help>
+ </properties>
+ <children>
+ <leafNode name="client-id">
+ <properties>
+ <help>DHCP client identifier</help>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>DHCP client host name (overrides the system host name)</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ <priority>319</priority>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <node name="ip">
+ <children>
+ <leafNode name="enable-proxy-arp">
+ <properties>
+ <help>Enable proxy-arp on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="proxy-arp-pvlan">
+ <properties>
+ <help>Enable private VLAN proxy ARP on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>68-9000</format>
+ <description>Maximum Transmission Unit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 68-9000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 68 and 9000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="vif">
+ <properties>
+ <help>Virtual Local Area Network (VLAN) ID</help>
+ <constraint>
+ <validator name="numeric" argument="--range 0-4094"/>
+ </constraint>
+ <constraintErrorMessage>VLAN ID must be between 0 and 4094</constraintErrorMessage>
+ </properties>
+ <children>
+ <leafNode name="address">
+ <properties>
+ <help>IP address</help>
+ <completionHelp>
+ <list>dhcp dhcpv6</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ipv4net</format>
+ <description>IPv4 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6net</format>
+ <description>IPv6 address and prefix length</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcp</format>
+ <description>Dynamic Host Configuration Protocol</description>
+ </valueHelp>
+ <valueHelp>
+ <format>dhcpv6</format>
+ <description>Dynamic Host Configuration Protocol for IPv6</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ip-cidr"/>
+ <regex>(dhcp|dhcpv6)</regex>
+ </constraint>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="description">
+ <properties>
+ <help>Interface description</help>
+ <constraint>
+ <regex>^.{1,256}$</regex>
+ </constraint>
+ <constraintErrorMessage>Interface description too long (limit 256 characters)</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="dhcp-options">
+ <properties>
+ <help>DHCP options</help>
+ </properties>
+ <children>
+ <leafNode name="client-id">
+ <properties>
+ <help>DHCP client identifier</help>
+ </properties>
+ </leafNode>
+ <leafNode name="host-name">
+ <properties>
+ <help>DHCP client host name (overrides the system host name)</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <node name="dhcpv6-options">
+ <properties>
+ <help>DHCPv6 options</help>
+ <priority>319</priority>
+ </properties>
+ <children>
+ <leafNode name="parameters-only">
+ <properties>
+ <help>Acquire only config parameters, no address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="temporary">
+ <properties>
+ <help>IPv6 "temporary" address</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="disable-link-detect">
+ <properties>
+ <help>Ignore link state changes</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="disable">
+ <properties>
+ <help>Disable this bridge interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="egress-qos">
+ <properties>
+ <help>VLAN egress QoS</help>
+ <completionHelp>
+ <script>echo Format for qos mapping \"0:1 1:6 7:6\"</script>
+ </completionHelp>
+ <constraint>
+ <regex>[:0-7 ]+$</regex>
+ </constraint>
+ <constraintErrorMessage>QoS mapping should be in the format of \"0:7 2:3\" with numbers 0-9</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="ingress-qos">
+ <properties>
+ <help>VLAN ingress QoS</help>
+ <completionHelp>
+ <script>echo Format for qos mapping \"0:1 1:6 7:6\"</script>
+ </completionHelp>
+ <constraint>
+ <regex>[:0-7 ]+$</regex>
+ </constraint>
+ <constraintErrorMessage>QoS mapping should be in the format of \"0:7 2:3\" with numbers 0-9</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <node name="ip">
+ <children>
+ <leafNode name="arp-cache-timeout">
+ <properties>
+ <help>ARP cache entry timeout in seconds</help>
+ <valueHelp>
+ <format>1-86400</format>
+ <description>ARP cache entry timout in seconds (default 30)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-86400"/>
+ </constraint>
+ <constraintErrorMessage>ARP cache entry timeout must be between 1 and 86400 seconds</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="enable-proxy-arp">
+ <properties>
+ <help>Enable proxy-arp on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ <leafNode name="proxy-arp-pvlan">
+ <properties>
+ <help>Enable private VLAN proxy ARP on this interface</help>
+ <valueless/>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="mac">
+ <properties>
+ <help>Media Access Control (MAC) address</help>
+ <valueHelp>
+ <format>h:h:h:h:h:h</format>
+ <description>Hardware (MAC) address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="mac-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="mtu">
+ <properties>
+ <help>Maximum Transmission Unit (MTU)</help>
+ <valueHelp>
+ <format>68-9000</format>
+ <description>Maximum Transmission Unit</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 68-9000"/>
+ </constraint>
+ <constraintErrorMessage>MTU must be between 68 and 9000</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+</interfaceDefinition>
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 750a7cc70..777b185a6 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, STDOUT
from time import sleep
dhcp_cfg = """
@@ -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
@@ -84,48 +83,15 @@ class Interface:
if os.path.isfile('/tmp/vyos.ifconfig.debug'):
print('DEBUG/{:<6} {}'.format(self._ifname, msg))
- 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
-
- # stop DHCP(v6) if running
- self._del_dhcp()
- self._del_dhcpv6()
-
- # NOTE (Improvement):
- # after interface removal no other commands should be allowed
- # to be called and instead should raise an Exception:
- cmd = 'ip link del dev {}'.format(self._ifname)
- self._cmd(cmd)
-
def _cmd(self, command):
+ p = Popen(command, stdout=PIPE, stderr=STDOUT, shell=True)
+ tmp = p.communicate()[0].strip()
self._debug_msg("cmd '{}'".format(command))
+ if tmp.decode():
+ self._debug_msg("returned:\n{}".format(tmp.decode()))
- process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True)
- proc_stdout = process.communicate()[0].strip()
-
- # add exception handling code
- pass
+ # do we need some error checking code here?
+ return tmp
def _read_sysfs(self, filename):
"""
@@ -148,6 +114,37 @@ class Interface:
return None
+ 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()
+ """
+ # stop DHCP(v6) if running
+ 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
+
+ # NOTE (Improvement):
+ # after interface removal no other commands should be allowed
+ # to be called and instead should raise an Exception:
+ cmd = 'ip link del dev {}'.format(self._ifname)
+ self._cmd(cmd)
+
@property
def mtu(self):
"""
@@ -158,7 +155,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 +172,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 +185,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
@@ -202,6 +199,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:
@@ -303,7 +304,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
@@ -327,7 +328,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
@@ -340,7 +341,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
@@ -759,7 +760,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
@@ -773,7 +774,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
@@ -787,7 +788,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
@@ -800,7 +801,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
@@ -814,7 +815,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
@@ -827,7 +828,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
@@ -842,7 +843,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
@@ -855,7 +856,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
@@ -868,7 +869,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
@@ -880,7 +881,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
@@ -895,7 +896,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
@@ -911,7 +912,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")
@@ -926,7 +927,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
@@ -944,7 +945,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")
@@ -997,12 +998,40 @@ 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)
- def add_vlan(self, vlan_id, ethertype=''):
+ 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
isolated in a computer network at the data link layer (OSI layer 2).
@@ -1012,8 +1041,20 @@ 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.
+
+ @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.
+
+ 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)):
@@ -1023,29 +1064,224 @@ class EthernetIf(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
# 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):
"""
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 = EthernetIf(vlan_ifname)
+ tmp = VLANIf(vlan_ifname)
tmp.remove()
-class BondIf(EthernetIf):
+class EthernetIf(VLANIf):
+ """
+ Abstraction of a Linux Ethernet Interface
+ """
+ def __init__(self, ifname):
+ super().__init__(ifname)
+
+ 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
+
+ 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))
+ 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.
+
+ @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")
+
+ 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(
+ self._ifname, enable)
+ try:
+ # An exception will be thrown if the settings are not changed
+ self._cmd(cmd)
+ 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']:
+ raise ValueError("Value out of range (speed)")
+
+ 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'
+ else:
+ cmd += ' speed {} duplex {} autoneg off'.format(speed, duplex)
+
+ 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
interfaces into a single logical "bonded" interface. The behavior of the
@@ -1053,7 +1289,6 @@ class BondIf(EthernetIf):
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-bonding.py b/src/conf_mode/interface-bonding.py
index ac3e1b867..9049913e6 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
@@ -334,8 +334,7 @@ def apply(bond):
b = BondIf(bond['intf'])
if bond['deleted']:
- #
- # delete bonding interface
+ # delete interface
b.remove()
else:
# Some parameters can not be changed when the bond is up.
diff --git a/src/conf_mode/interface-bridge.py b/src/conf_mode/interface-bridge.py
index 401182a0d..62589c798 100755
--- a/src/conf_mode/interface-bridge.py
+++ b/src/conf_mode/interface-bridge.py
@@ -183,8 +183,7 @@ def apply(bridge):
br = BridgeIf(bridge['intf'])
if bridge['deleted']:
- # delete bridge interface
- # DHCP is stopped inside remove()
+ # delete interface
br.remove()
else:
# enable interface
diff --git a/src/conf_mode/interface-ethernet.py b/src/conf_mode/interface-ethernet.py
new file mode 100755
index 000000000..f82105847
--- /dev/null
+++ b/src/conf_mode/interface-ethernet.py
@@ -0,0 +1,382 @@
+#!/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 <http://www.gnu.org/licenses/>.
+
+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,
+ 'duplex': 'auto',
+ 'flow_control': 'on',
+ 'hw_id': '',
+ 'ip_arp_cache_tmo': 30,
+ 'ip_proxy_arp': 0,
+ 'ip_proxy_arp_pvlan': 0,
+ 'intf': '',
+ 'mac': '',
+ 'mtu': 1500,
+ 'offload_gro': 'off',
+ 'offload_gso': 'off',
+ 'offload_sg': 'off',
+ 'offload_tso': 'off',
+ 'offload_ufo': 'off',
+ 'speed': 'auto',
+ 'vif_s': [],
+ 'vif_s_remove': [],
+ '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')
+
+ # 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 ethernet flow control (pause frames)
+ if conf.exists('disable-flow-control'):
+ eth['flow_control'] = 'off'
+
+ # retrieve real hardware address
+ if conf.exists('hw-id'):
+ eth['hw_id'] = conf.return_value('hw-id')
+
+ # disable interface
+ if conf.exists('disable'):
+ eth['disable'] = True
+
+ # interface duplex
+ if conf.exists('duplex'):
+ eth['duplex'] = conf.return_value('duplex')
+
+ # ARP cache entry timeout in seconds
+ if conf.exists('ip arp-cache-timeout'):
+ eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))
+
+ # 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'))
+
+ # GRO (generic receive offload)
+ if conf.exists('offload-options generic-receive'):
+ eth['offload_gro'] = conf.return_value('offload-options generic-receive')
+
+ # GSO (generic segmentation offload)
+ if conf.exists('offload-options generic-segmentation'):
+ eth['offload_gso'] = conf.return_value('offload-options generic-segmentation')
+
+ # scatter-gather option
+ if conf.exists('offload-options scatter-gather'):
+ eth['offload_sg'] = conf.return_value('offload-options scatter-gather')
+
+ # TSO (TCP segmentation offloading)
+ if conf.exists('offload-options tcp-segmentation'):
+ eth['offload_tso'] = conf.return_value('offload-options tcp-segmentation')
+
+ # UDP fragmentation offloading
+ if conf.exists('offload-options udp-fragmentation'):
+ eth['offload_ufo'] = conf.return_value('offload-options udp-fragmentation')
+
+ # interface speed
+ if conf.exists('speed'):
+ eth['speed'] = conf.return_value('speed')
+
+ # 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):
+ if eth['deleted']:
+ return None
+
+ if eth['speed'] == 'auto':
+ if eth['duplex'] != 'auto':
+ raise ConfigError('If speed is hardcoded, duplex must be hardcoded, too')
+
+ if eth['duplex'] == 'auto':
+ if eth['speed'] != 'auto':
+ raise ConfigError('If duplex is hardcoded, speed must be hardcoded, too')
+
+ conf = Config()
+ # some options can not be changed when interface is enslaved to a bond
+ for bond in conf.list_nodes('interfaces bonding'):
+ if conf.exists('interfaces bonding ' + bond + ' member interface'):
+ bond_member = conf.return_values('interfaces bonding ' + bond + ' member interface')
+ if eth['name'] in bond_member:
+ if eth['address']:
+ raise ConfigError('Can not assign address to interface {} which is a member of {}').format(eth['intf'], bond)
+
+
+ return None
+
+def generate(eth):
+ return None
+
+def apply(eth):
+ e = EthernetIf(eth['intf'])
+ if eth['deleted']:
+ # delete interface
+ e.remove()
+ else:
+ # 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_tmo = eth['ip_arp_cache_tmo']
+ # Enable proxy-arp on this interface
+ e.proxy_arp = eth['ip_proxy_arp']
+ # Enable private VLAN proxy ARP on this interface
+ 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']
+
+ # GRO (generic receive offload)
+ e.set_gro(eth['offload_gro'])
+
+ # GSO (generic segmentation offload)
+ e.set_gso(eth['offload_gso'])
+
+ # scatter-gather option
+ e.set_sg(eth['offload_sg'])
+
+ # TSO (TCP segmentation offloading)
+ e.set_tso(eth['offload_tso'])
+
+ # UDP fragmentation offloading
+ e.set_ufo(eth['offload_ufo'])
+
+ # Set physical interface speed and duplex
+ e.set_speed_duplex(eth['speed'], eth['duplex'])
+
+ # 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
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ exit(1)