summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Poessinger <christian@poessinger.com>2019-09-03 18:58:27 +0200
committerChristian Poessinger <christian@poessinger.com>2019-09-04 11:25:31 +0200
commit1fc4c201d7771ac4164e9480d55a654b7674d098 (patch)
tree62d08c1c8843bc6031278cfa824c74f98e757b78
parent029085250c4e2dea8a98f3031cf6b8037ffdd534 (diff)
downloadvyos-1x-1fc4c201d7771ac4164e9480d55a654b7674d098.tar.gz
vyos-1x-1fc4c201d7771ac4164e9480d55a654b7674d098.zip
bonding: T1614: T1557: add vif/vif-s VLAN interface support
Support for vif-c interfaces is still missing
-rw-r--r--python/vyos/ifconfig.py47
-rwxr-xr-xsrc/conf_mode/interface-bonding.py101
2 files changed, 136 insertions, 12 deletions
diff --git a/python/vyos/ifconfig.py b/python/vyos/ifconfig.py
index 1d03e4e74..bb2d23d0d 100644
--- a/python/vyos/ifconfig.py
+++ b/python/vyos/ifconfig.py
@@ -87,7 +87,9 @@ class Interface:
def remove(self):
"""
- Remove system interface
+ 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
@@ -964,10 +966,53 @@ class BridgeIf(Interface):
.format(self._ifname, interface), priority)
+
class EthernetIf(Interface):
def __init__(self, ifname, type=None):
super().__init__(ifname, type)
+ def add_vlan(self, vlan_id, ethertype=''):
+ """
+ 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).
+ Use this function to create a new VLAN interface on a given physical
+ 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
+ created.
+ """
+ vlan_ifname = self._ifname + '.' + str(vlan_id)
+ if not os.path.exists('/sys/class/net/{}'.format(vlan_ifname)):
+ self._vlan_id = int(vlan_id)
+
+ if ethertype:
+ self._ethertype = ethertype
+ ethertype='proto {}'.format(ethertype)
+
+ # 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)
+ 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)
+
+
+ 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.
+ """
+ vlan_ifname = self._ifname + '.' + str(vlan_id)
+ tmp = EthernetIf(vlan_ifname)
+ tmp.remove()
+
+
class BondIf(EthernetIf):
"""
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 a623ca524..9e7be19f5 100755
--- a/src/conf_mode/interface-bonding.py
+++ b/src/conf_mode/interface-bonding.py
@@ -21,7 +21,8 @@ import os
from copy import deepcopy
from sys import exit
from netifaces import interfaces
-from vyos.ifconfig import BondIf
+
+from vyos.ifconfig import BondIf, EthernetIf
from vyos.config import Config
from vyos import ConfigError
@@ -49,7 +50,9 @@ default_config_data = {
'mtu': 1500,
'primary': '',
'vif_s': [],
- 'vif': []
+ 'vif_s_remove': [],
+ 'vif': [],
+ 'vif_remove': []
}
def diff(first, second):
@@ -74,6 +77,15 @@ def get_bond_mode(mode):
else:
raise ConfigError('invalid bond mode "{}"'.format(mode))
+def get_ethertype(ethertype_val):
+ if ethertype_val == '0x88A8':
+ return '802.1ad'
+ elif ethertype_val == '0x8100':
+ return '802.1q'
+ else:
+ raise ConfigError('invalid ethertype "{}"'.format(ethertype_val))
+
+
def vlan_to_dict(conf):
"""
Common used function which will extract VLAN related information from config
@@ -82,7 +94,7 @@ def vlan_to_dict(conf):
Function call's itself recursively if a vif-s/vif-c pair is detected.
"""
vlan = {
- 'id': conf.get_level().split(' ')[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100'
+ 'id': conf.get_level().split()[-1], # get the '100' in 'interfaces bonding bond0 vif-s 100'
'address': [],
'address_remove': [],
'description': '',
@@ -133,9 +145,14 @@ def vlan_to_dict(conf):
if conf.exists('disable'):
vlan['disable'] = True
- # ethertype (only on vif-s nodes)
- if conf.exists('ethertype'):
- vlan['ethertype'] = conf.return_value('ethertype')
+ # 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':
+ # ethertype uses a default of 0x88A8
+ tmp = '0x88A8'
+ if conf.exists('ethertype'):
+ tmp = conf.return_value('ethertype')
+ vlan['ethertype'] = get_ethertype(tmp)
# Media Access Control (MAC) address
if conf.exists('mac'):
@@ -158,6 +175,39 @@ def vlan_to_dict(conf):
return vlan
+
+def apply_vlan_config(vlan, config):
+ """
+ Generic function to apply a VLAN configuration from a dictionary
+ to a VLAN interface
+ """
+
+ if type(vlan) != type(EthernetIf("lo")):
+ raise TypeError()
+
+ # Configure interface address(es)
+ for addr in config['address_remove']:
+ vlan.del_addr(addr)
+ for addr in config['address']:
+ vlan.add_addr(addr)
+
+ # 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'
+
+
def get_config():
# initialize kernel module if not loaded
if not os.path.isfile('/sys/class/net/bonding_masters'):
@@ -271,6 +321,12 @@ def get_config():
# re-set configuration level and retrieve vif-s interfaces
conf.set_level(cfg_base)
+ # Determine 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')
+ bond['vif_s_remove'] = 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
@@ -279,6 +335,12 @@ def get_config():
# 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')
+ bond['vif_remove'] = diff(eff_intf, act_intf)
+
if conf.exists('vif'):
for vif in conf.list_nodes('vif'):
# set config level to vif interface
@@ -287,6 +349,7 @@ def get_config():
return bond
+
def verify(bond):
if len (bond['arp_mon_tgt']) > 16:
raise ConfigError('The maximum number of targets that can be specified is 16')
@@ -300,9 +363,11 @@ def verify(bond):
return None
+
def generate(bond):
return None
+
def apply(bond):
b = BondIf(bond['intf'])
@@ -378,13 +443,27 @@ def apply(bond):
# Primary device interface
b.primary = bond['primary']
- #
- # VLAN config goes here
- #
-
# Add (enslave) interfaces to bond
for intf in bond['member']:
- b.add_port(intf)
+ b.add_port(intf)
+
+ # remove no longer required service VLAN interfaces (vif-s)
+ for vif_s in bond['vif_s_remove']:
+ b.del_vlan(vif_s)
+
+ # create service VLAN interfaces (vif-s)
+ for vif_s in bond['vif_s']:
+ vlan = b.add_vlan(vif_s['id'], ethertype=vif_s['ethertype'])
+ apply_vlan_config(vlan, vif_s)
+
+ # remove no longer required VLAN interfaces (vif)
+ for vif in bond['vif_remove']:
+ b.del_vlan(vif)
+
+ # create VLAN interfaces (vif)
+ for vif in bond['vif']:
+ vlan = b.add_vlan(vif['id'])
+ apply_vlan_config(vlan, vif)
# As the bond interface is always disabled first when changing
# parameters we will only re-enable the interface if it is not