Skip to content

Commit

Permalink
Merge pull request #677 from jack9603301/T3137
Browse files Browse the repository at this point in the history
bridge: T3137: Let VLAN aware bridge approach the behavior of professional equipment
  • Loading branch information
c-po committed Jan 16, 2021
2 parents 56ec325 + 2d1e8a2 commit 3315af9
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 174 deletions.
9 changes: 7 additions & 2 deletions interface-definitions/interfaces-bridge.xml.in
Expand Up @@ -86,6 +86,12 @@
#include <include/interface-ipv6-options.xml.i>
#include <include/interface-mac.xml.i>
#include <include/interface-mirror.xml.i>
<leafNode name="enable-vlan">
<properties>
<help>Enable VLAN aware bridge</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="max-age">
<properties>
<help>Interval at which neighbor bridges are removed</help>
Expand Down Expand Up @@ -138,7 +144,7 @@
<description>VLAN id range allowed on this interface (use '-' as delimiter)</description>
</valueHelp>
<constraint>
<regex>^([0-9]{1,4}-[0-9]{1,4})|([0-9]{1,4})$</regex>
<validator name="allowed-vlan"/>
</constraint>
<constraintErrorMessage>not a valid VLAN ID value or range</constraintErrorMessage>
<multi/>
Expand Down Expand Up @@ -196,7 +202,6 @@
<valueless/>
</properties>
</leafNode>
#include <include/vif-s.xml.i>
#include <include/vif.xml.i>
</children>
</tagNode>
Expand Down
125 changes: 61 additions & 64 deletions python/vyos/ifconfig/bridge.py
Expand Up @@ -22,6 +22,7 @@
from vyos.util import cmd
from vyos.util import dict_search
from vyos.configdict import get_vlan_ids
from vyos.configdict import list_diff

@Interface.register
class BridgeIf(Interface):
Expand Down Expand Up @@ -274,20 +275,36 @@ def update(self, config):
for member in (tmp or []):
if member in interfaces():
self.del_port(member)
vlan_filter = 0

vlan_del = set()
vlan_add = set()
# enable/disable Vlan Filter
vlan_filter = '1' if 'enable_vlan' in config else '0'
self.set_vlan_filter(vlan_filter)

if int(vlan_filter):
add_vlan = []
cur_vlan_ids = get_vlan_ids(ifname)

tmp = dict_search('vif', config)
if tmp:
for vif, vif_config in tmp.items():
add_vlan.append(vif)

# Remove redundant VLANs from the system
for vlan in list_diff(cur_vlan_ids, add_vlan):
cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
self._cmd(cmd)

for vlan in add_vlan:
cmd = f'bridge vlan add dev {ifname} vid {vif} self'
self._cmd(cmd)

# VLAN of bridge parent interface is always 1
# VLAN 1 is the default VLAN for all unlabeled packets
cmd = f'bridge vlan add dev {ifname} vid 1 pvid untagged self'
self._cmd(cmd)

tmp = dict_search('member.interface', config)
if tmp:
if self.get_vlan_filter():
bridge_vlan_ids = get_vlan_ids(ifname)
# Delete VLAN ID for the bridge
if 1 in bridge_vlan_ids:
bridge_vlan_ids.remove(1)
for vlan in bridge_vlan_ids:
vlan_del.add(str(vlan))

for interface, interface_config in tmp.items():
# if interface does yet not exist bail out early and
Expand Down Expand Up @@ -315,63 +332,43 @@ def update(self, config):
value = interface_config.get('priority')
lower.set_path_priority(value)

tmp = dict_search('native_vlan_removed', interface_config)

for vlan_id in (tmp or []):
cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
self._cmd(cmd)
cmd = f'bridge vlan add dev {interface} vid 1 pvid untagged master'
self._cmd(cmd)
vlan_del.add(vlan_id)
vlan_add.add(1)

tmp = dict_search('allowed_vlan_removed', interface_config)


for vlan_id in (tmp or []):
cmd = f'bridge vlan del dev {interface} vid {vlan_id}'
self._cmd(cmd)
vlan_del.add(vlan_id)

if 'native_vlan' in interface_config:
vlan_filter = 1
cmd = f'bridge vlan del dev {interface} vid 1'
self._cmd(cmd)
vlan_id = interface_config['native_vlan']
if int(vlan_id) != 1:
if 1 in vlan_add:
vlan_add.remove(1)
vlan_del.add(1)
cmd = f'bridge vlan add dev {interface} vid {vlan_id} pvid untagged master'
self._cmd(cmd)
vlan_add.add(vlan_id)
if vlan_id in vlan_del:
vlan_del.remove(vlan_id)

if 'allowed_vlan' in interface_config:
vlan_filter = 1
if 'native_vlan' not in interface_config:
cmd = f'bridge vlan del dev {interface} vid 1'
if int(vlan_filter):
add_vlan = []
native_vlan_id = None
allowed_vlan_ids= []
cur_vlan_ids = get_vlan_ids(interface)

if 'native_vlan' in interface_config:
vlan_id = interface_config['native_vlan']
add_vlan.append(vlan_id)
native_vlan_id = vlan_id
else:
# VLAN 1 is the default VLAN for all unlabeled packets
add_vlan.append(1)
native_vlan_id = 1

if 'allowed_vlan' in interface_config:
for vlan in interface_config['allowed_vlan']:
vlan_range = vlan.split('-')
if len(vlan_range) == 2:
for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1):
add_vlan.append(str(vlan_add))
allowed_vlan_ids.append(str(vlan_add))
else:
add_vlan.append(vlan)
allowed_vlan_ids.append(vlan)

# Remove redundant VLANs from the system
for vlan in list_diff(cur_vlan_ids, add_vlan):
cmd = f'bridge vlan del dev {interface} vid {vlan} master'
self._cmd(cmd)
vlan_del.add(1)
for vlan in interface_config['allowed_vlan']:

for vlan in allowed_vlan_ids:
cmd = f'bridge vlan add dev {interface} vid {vlan} master'
self._cmd(cmd)
vlan_add.add(vlan)
if vlan in vlan_del:
vlan_del.remove(vlan)

for vlan in vlan_del:
cmd = f'bridge vlan del dev {ifname} vid {vlan} self'
self._cmd(cmd)

for vlan in vlan_add:
cmd = f'bridge vlan add dev {ifname} vid {vlan} self'
self._cmd(cmd)

# enable/disable Vlan Filter
self.set_vlan_filter(vlan_filter)

# Setting native VLAN to system
cmd = f'bridge vlan add dev {interface} vid {native_vlan_id} pvid untagged master'
self._cmd(cmd)

# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
Expand Down
74 changes: 35 additions & 39 deletions python/vyos/ifconfig/interface.py
Expand Up @@ -900,49 +900,45 @@ def add_to_bridge(self, bridge_dict):
if 'priority' in bridge_config:
self.set_path_cost(bridge_config['priority'])

vlan_filter = 0
vlan_add = set()

del_ifname_vlan_ids = get_vlan_ids(ifname)
bridge_vlan_filter = Section.klass(bridge)(bridge, create=True).get_vlan_filter()

if bridge_vlan_filter:
if 1 in del_ifname_vlan_ids:
del_ifname_vlan_ids.remove(1)
vlan_filter = 1

for vlan in del_ifname_vlan_ids:
cmd = f'bridge vlan del dev {ifname} vid {vlan}'
self._cmd(cmd)

if 'native_vlan' in bridge_config:
vlan_filter = 1
cmd = f'bridge vlan del dev {self.ifname} vid 1'
self._cmd(cmd)
vlan_id = bridge_config['native_vlan']
cmd = f'bridge vlan add dev {self.ifname} vid {vlan_id} pvid untagged master'
self._cmd(cmd)
vlan_add.add(vlan_id)

if 'allowed_vlan' in bridge_config:
vlan_filter = 1
if 'native_vlan' not in bridge_config:
cmd = f'bridge vlan del dev {self.ifname} vid 1'
self._cmd(cmd)
for vlan in bridge_config['allowed_vlan']:
cmd = f'bridge vlan add dev {self.ifname} vid {vlan} master'
if int(bridge_vlan_filter):
cur_vlan_ids = get_vlan_ids(ifname)
add_vlan = []
native_vlan_id = None
allowed_vlan_ids= []

if 'native_vlan' in bridge_config:
vlan_id = bridge_config['native_vlan']
add_vlan.append(vlan_id)
native_vlan_id = vlan_id
else:
# VLAN 1 is the default VLAN for all unlabeled packets
add_vlan.append(1)
native_vlan_id = 1

if 'allowed_vlan' in bridge_config:
for vlan in bridge_config['allowed_vlan']:
vlan_range = vlan.split('-')
if len(vlan_range) == 2:
for vlan_add in range(int(vlan_range[0]),int(vlan_range[1]) + 1):
add_vlan.append(str(vlan_add))
allowed_vlan_ids.append(str(vlan_add))
else:
add_vlan.append(vlan)
allowed_vlan_ids.append(vlan)

# Remove redundant VLANs from the system
for vlan in list_diff(cur_vlan_ids, add_vlan):
cmd = f'bridge vlan del dev {ifname} vid {vlan} master'
self._cmd(cmd)
vlan_add.add(vlan)

if vlan_filter:
# Setting VLAN ID for the bridge
for vlan in vlan_add:
cmd = f'bridge vlan add dev {bridge} vid {vlan} self'

for vlan in allowed_vlan_ids:
cmd = f'bridge vlan add dev {ifname} vid {vlan} master'
self._cmd(cmd)

# enable/disable Vlan Filter
# When the VLAN aware option is not detected, the setting of `bridge` should not be overwritten
Section.klass(bridge)(bridge, create=True).set_vlan_filter(vlan_filter)
# Setting native VLAN to system
cmd = f'bridge vlan add dev {ifname} vid {native_vlan_id} pvid untagged master'
self._cmd(cmd)

def set_dhcp(self, enable):
"""
Expand Down
24 changes: 20 additions & 4 deletions smoketest/scripts/cli/test_interfaces_bridge.py
Expand Up @@ -25,14 +25,14 @@
from vyos.ifconfig import Section
from vyos.util import cmd
from vyos.util import read_file
from vyos.validate import is_intf_addr_assigned

class BridgeInterfaceTest(BasicInterfaceTest.BaseTest):
def setUp(self):
self._test_ip = True
self._test_ipv6 = True
self._test_ipv6_pd = True
self._test_vlan = True
self._test_qinq = True
self._base_path = ['interfaces', 'bridge']
self._mirror_interfaces = ['dum21354']
self._members = []
Expand All @@ -52,6 +52,12 @@ def setUp(self):
self._interfaces = list(self._options)

super().setUp()

def tearDown(self):
for intf in self._interfaces:
self.session.delete(self._base_path + [intf])

super().tearDown()

def test_add_remove_bridge_member(self):
# Add member interfaces to bridge and set STP cost/priority
Expand Down Expand Up @@ -86,13 +92,23 @@ def test_add_remove_bridge_member(self):
self.session.delete(self._base_path + [interface, 'member'])

self.session.commit()

def test_8021q_vlan_interfaces(self):
for interface in self._interfaces:
base = self._base_path + [interface]
self.session.set(base + ['enable-vlan'])
super().test_8021q_vlan_interfaces()

def test_bridge_vlan_filter(self):

vif_vlan = 2
# Add member interface to bridge and set VLAN filter
for interface in self._interfaces:
base = self._base_path + [interface]
self.session.set(base + ['vif', '1', 'address', '192.0.2.1/24'])
self.session.set(base + ['vif', '2', 'address', '192.0.3.1/24'])
self.session.set(base + ['enable-vlan'])
self.session.set(base + ['address', '192.0.2.1/24'])
self.session.set(base + ['vif', str(vif_vlan), 'address', '192.0.3.1/24'])
self.session.set(base + ['vif', str(vif_vlan), 'mtu', self._mtu])

vlan_id = 101
allowed_vlan = 2
Expand Down Expand Up @@ -159,7 +175,7 @@ def test_bridge_vlan_filter(self):

for member in self._members:
self.assertIn(member, bridge_members)

# delete all members
for interface in self._interfaces:
self.session.delete(self._base_path + [interface, 'member'])
Expand Down

0 comments on commit 3315af9

Please sign in to comment.