summaryrefslogtreecommitdiff
path: root/src/conf_mode/interfaces-ethernet.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/conf_mode/interfaces-ethernet.py')
-rwxr-xr-xsrc/conf_mode/interfaces-ethernet.py262
1 files changed, 64 insertions, 198 deletions
diff --git a/src/conf_mode/interfaces-ethernet.py b/src/conf_mode/interfaces-ethernet.py
index 5a977d797..f45a77a3e 100755
--- a/src/conf_mode/interfaces-ethernet.py
+++ b/src/conf_mode/interfaces-ethernet.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2019 VyOS maintainers and contributors
+# Copyright (C) 2019-2020 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
@@ -21,66 +21,49 @@ from copy import deepcopy
from netifaces import interfaces
from vyos.ifconfig import EthernetIf
-from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
-from vyos.configdict import list_diff, vlan_to_dict
+from vyos.ifconfig_vlan import apply_all_vlans, verify_vlan_config
+from vyos.configdict import list_diff, intf_to_dict, add_to_dict, interface_default_data
+from vyos.validate import is_member
from vyos.config import Config
from vyos import ConfigError
default_config_data = {
- 'address': [],
- 'address_remove': [],
- 'description': '',
+ **interface_default_data,
'deleted': False,
- 'dhcp_client_id': '',
- 'dhcp_hostname': '',
- 'dhcp_vendor_class_id': '',
- '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_disable_arp_filter': 1,
- 'ip_enable_arp_accept': 0,
- 'ip_enable_arp_announce': 0,
- 'ip_enable_arp_ignore': 0,
- 'ip_proxy_arp': 0,
'ip_proxy_arp_pvlan': 0,
- 'ipv6_autoconf': 0,
- 'ipv6_eui64_prefix': '',
- 'ipv6_forwarding': 1,
- 'ipv6_dup_addr_detect': 1,
+ 'is_bond_member': False,
'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': {},
'vif_s_remove': [],
- 'vif': [],
+ 'vif': {},
'vif_remove': [],
'vrf': ''
}
-def get_config():
- eth = deepcopy(default_config_data)
- conf = Config()
+def get_config():
# determine tagNode instance
if 'VYOS_TAGNODE_VALUE' not in os.environ:
raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')
- eth['intf'] = os.environ['VYOS_TAGNODE_VALUE']
+ ifname = os.environ['VYOS_TAGNODE_VALUE']
+ conf = Config()
# check if ethernet interface has been removed
- cfg_base = ['interfaces', 'ethernet', eth['intf']]
+ cfg_base = ['interfaces', 'ethernet', ifname]
if not conf.exists(cfg_base):
+ eth = deepcopy(default_config_data)
+ eth['intf'] = ifname
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.
@@ -90,42 +73,7 @@ def get_config():
# 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')
-
- # DHCP client vendor identifier
- if conf.exists('dhcp-options vendor-class-id'):
- eth['dhcp_vendor_class_id'] = conf.return_value('dhcp-options vendor-class-id')
-
- # DHCPv6 only acquire config parameters, no address
- if conf.exists('dhcpv6-options parameters-only'):
- eth['dhcpv6_prm_only'] = True
-
- # DHCPv6 temporary IPv6 address
- if conf.exists('dhcpv6-options temporary'):
- eth['dhcpv6_temporary'] = True
-
- # ignore link state changes
- if conf.exists('disable-link-detect'):
- eth['disable_link_detect'] = 2
+ eth, disabled = intf_to_dict(conf, default_config_data)
# disable ethernet flow control (pause frames)
if conf.exists('disable-flow-control'):
@@ -135,10 +83,6 @@ def get_config():
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')
@@ -147,53 +91,12 @@ def get_config():
if conf.exists('ip arp-cache-timeout'):
eth['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))
- # ARP filter configuration
- if conf.exists('ip disable-arp-filter'):
- eth['ip_disable_arp_filter'] = 0
-
- # ARP enable accept
- if conf.exists('ip enable-arp-accept'):
- eth['ip_enable_arp_accept'] = 1
-
- # ARP enable announce
- if conf.exists('ip enable-arp-announce'):
- eth['ip_enable_arp_announce'] = 1
-
- # ARP enable ignore
- if conf.exists('ip enable-arp-ignore'):
- eth['ip_enable_arp_ignore'] = 1
-
- # 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
- # Enable acquisition of IPv6 address using stateless autoconfig (SLAAC)
- if conf.exists('ipv6 address autoconf'):
- eth['ipv6_autoconf'] = 1
-
- # Get prefix for IPv6 addressing based on MAC address (EUI-64)
- if conf.exists('ipv6 address eui64'):
- eth['ipv6_eui64_prefix'] = conf.return_value('ipv6 address eui64')
-
- # Disable IPv6 forwarding on this interface
- if conf.exists('ipv6 disable-forwarding'):
- eth['ipv6_forwarding'] = 0
-
- # IPv6 Duplicate Address Detection (DAD) tries
- if conf.exists('ipv6 dup-addr-detect-transmits'):
- eth['ipv6_dup_addr_detect'] = int(conf.return_value('ipv6 dup-addr-detect-transmits'))
-
- # 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'))
+ # check if we are a member of any bond
+ eth['is_bond_member'] = is_member(conf, eth['intf'], 'bonding')
# GRO (generic receive offload)
if conf.exists('offload-options generic-receive'):
@@ -219,37 +122,13 @@ def get_config():
if conf.exists('speed'):
eth['speed'] = conf.return_value('speed')
- # retrieve VRF instance
- if conf.exists('vrf'):
- eth['vrf'] = conf.return_value('vrf')
+ # remove default IPv6 link-local address if member of a bond
+ if eth['is_bond_member'] and 'fe80::/64' in eth['ipv6_eui64_prefix']:
+ eth['ipv6_eui64_prefix'].remove('fe80::/64')
+ eth['ipv6_eui64_prefix_remove'].append('fe80::/64')
- # re-set configuration level to parse new nodes
- 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 to parse new nodes
- 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))
+ add_to_dict(conf, disabled, eth, 'vif', 'vif')
+ add_to_dict(conf, disabled, eth, 'vif-s', 'vif_s')
return eth
@@ -272,18 +151,24 @@ def verify(eth):
if eth['dhcpv6_prm_only'] and eth['dhcpv6_temporary']:
raise ConfigError('DHCPv6 temporary and parameters-only options are mutually exclusive!')
- vrf_name = eth['vrf']
- if vrf_name and vrf_name not in interfaces():
- raise ConfigError(f'VRF "{vrf_name}" does not exist')
+ memberof = eth['is_bridge_member'] if eth['is_bridge_member'] else eth['is_bond_member']
- 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['intf'] in bond_member:
- if eth['address']:
- raise ConfigError(f"Can not assign address to interface {eth['intf']} which is a member of {bond}")
+ if ( memberof
+ and ( eth['address']
+ or eth['ipv6_eui64_prefix']
+ or eth['ipv6_autoconf'] ) ):
+ raise ConfigError((
+ f'Cannot assign address to interface "{eth["intf"]}" '
+ f'as it is a member of "{memberof}"!'))
+
+ if eth['vrf']:
+ if eth['vrf'] not in interfaces():
+ raise ConfigError(f'VRF "{eth["vrf"]}" does not exist')
+
+ if memberof:
+ raise ConfigError((
+ f'Interface "{eth["intf"]}" cannot be member of VRF "{eth["vrf"]}" '
+ f'and "{memberof}" at the same time!'))
# use common function to verify VLAN configuration
verify_vlan_config(eth)
@@ -316,6 +201,9 @@ def apply(eth):
if eth['dhcpv6_temporary']:
e.dhcp.v6.options['dhcpv6_temporary'] = True
+ if eth['dhcpv6_pd']:
+ e.dhcp.v6.options['dhcpv6_pd'] = e['dhcpv6_pd']
+
# ignore link state changes
e.set_link_detect(eth['disable_link_detect'])
# disable ethernet flow control (pause frames)
@@ -334,15 +222,19 @@ def apply(eth):
e.set_proxy_arp(eth['ip_proxy_arp'])
# Enable private VLAN proxy ARP on this interface
e.set_proxy_arp_pvlan(eth['ip_proxy_arp_pvlan'])
+ # IPv6 accept RA
+ e.set_ipv6_accept_ra(eth['ipv6_accept_ra'])
# IPv6 address autoconfiguration
e.set_ipv6_autoconf(eth['ipv6_autoconf'])
- # IPv6 EUI-based address
- e.set_ipv6_eui64_address(eth['ipv6_eui64_prefix'])
# IPv6 forwarding
e.set_ipv6_forwarding(eth['ipv6_forwarding'])
# IPv6 Duplicate Address Detection (DAD) tries
e.set_ipv6_dad_messages(eth['ipv6_dup_addr_detect'])
+ # Delete old IPv6 EUI64 addresses before changing MAC
+ for addr in eth['ipv6_eui64_prefix_remove']:
+ e.del_ipv6_eui64_address(addr)
+
# Change interface MAC address - re-set to real hardware address (hw-id)
# if custom mac is removed
if eth['mac']:
@@ -350,6 +242,10 @@ def apply(eth):
elif eth['hw_id']:
e.set_mac(eth['hw_id'])
+ # Add IPv6 EUI-based addresses
+ for addr in eth['ipv6_eui64_prefix']:
+ e.add_ipv6_eui64_address(addr)
+
# Maximum Transmission Unit (MTU)
e.set_mtu(eth['mtu'])
@@ -385,47 +281,17 @@ def apply(eth):
for addr in eth['address']:
e.add_addr(addr)
- # assign/remove VRF
- e.set_vrf(eth['vrf'])
-
- # 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)
+ # assign/remove VRF (ONLY when not a member of a bridge or bond,
+ # otherwise 'nomaster' removes it from it)
+ if not ( eth['is_bridge_member'] or eth['is_bond_member'] ):
+ e.set_vrf(eth['vrf'])
+
+ # re-add ourselves to any bridge we might have fallen out of
+ if eth['is_bridge_member']:
+ e.add_to_bridge(eth['is_bridge_member'])
+
+ # apply all vlans to interface
+ apply_all_vlans(e, eth)
return None