Skip to content

Commit 0945a19

Browse files
committed
Bridge-assisted multi_access links are isolated bridging domains (#2150)
A multi-access link without a VLAN attribute is usually a layer-3 segment. Our implementation of bridge-assisted multi-access links has to reflect that -- we have to create a separate VLAN for each such link.
1 parent 2487302 commit 0945a19

File tree

2 files changed

+73
-22
lines changed

2 files changed

+73
-22
lines changed

Diff for: docs/node-roles.md

+31-20
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ Most hosts listen to IPv6 RA messages to get the IPv6 default route. _netlab_ ca
3333
(node-role-bridge)=
3434
## Bridges
3535

36-
The **bridge** role is a thin abstraction layer on top of the [**vlan** configuration module](module-vlan), making deploying simple topologies with a single bridge connecting multiple routers or hosts easier. Do not try to build complex topologies with bridges; use the VLAN configuration module.
36+
The **bridge** role is a thin abstraction layer on top of the [**vlan** configuration module](module-vlan), making deploying simple topologies with a single bridge connecting multiple routers or hosts easier. You can also use a **bridge** node to test failover scenarios using a familiar layer-2 device[^SD].
37+
38+
[^SD]: It's easier to shut down an interface on a familiar device than trying to figure out how to do that on a Linux bridge.
39+
40+
Do not try to build complex topologies with bridges; use the VLAN configuration module.
3741

3842
Bridges are simple layer-2 packet forwarding devices[^VM]. They do not have a loopback interface and might not even have a data-plane IP address. Without additional parameters, _netlab_ configures them the way non-VLAN bridges have been working for decades -- bridge interfaces do not use VLAN tagging and belong to a single layer-2 forwarding domain.
3943

@@ -58,23 +62,6 @@ links: [ rtr-br, h1-br, h2-br ]
5862

5963
In the above topology, *netlab* assigns an IP prefix from the **lan** pool to the VLAN segment connecting the four devices ([you can change that](node-bridge-details)).
6064

61-
In the lab topology, you can use a multi-access link with a single bridge attached instead of a series of point-to-point links. The following topology is equivalent to the one above; the multi-access link is expanded into a series of point-to-point links with the **br** device.
62-
63-
```
64-
nodes:
65-
rtr:
66-
device: eos
67-
h1:
68-
device: linux
69-
h2:
70-
device: linux
71-
br:
72-
device: ioll2
73-
role: bridge
74-
75-
links: [ rtr-h1-h2-br ]
76-
```
77-
7865
You can also connect multiple bridges into a larger bridged network. This scenario stretches the limitations of the **bridge** nodes (using the [**vlan** configuration module](module-vlan) would be better). If you decide to use it in your topology, you SHOULD define a global **br_default** VLAN (defined as **vlans.br_default** topology attribute) to share the same IP subnet across all bridges.
7966

8067
```
@@ -100,7 +87,7 @@ _netlab_ does not implement multiple independent bridge domains for the same VLA
10087
```
10188

10289
(node-bridge-details)=
103-
### Implementation Details
90+
### Bridge Implementation Details
10491

10592
_netlab_ uses the **vlan** configuration module to implement the *simple bridging* functionality -- it places all bridge interfaces without an explicit **vlan** parameter into the same access VLAN.
10693

@@ -112,4 +99,28 @@ You can use the node- or global VLAN definition of the **br_default** VLAN to ch
11299

113100
[^BRID]: You can change the VLAN tag of the default bridge VLAN with the `topology.defaults.const.bridge.default_vlan.id` parameter
114101

115-
For more VLAN configuration- and implementation details, read the [**vlan** configuration module documentation](module-vlan).
102+
For more VLAN configuration- and implementation details, read the [**vlan** configuration module documentation](module-vlan).
103+
104+
(node-bridge-lan)=
105+
### Implementing Multi-Access Links with Bridges
106+
107+
To build a LAN segment with a hub-and-spoke topology of point-to-point links attaching nodes to the bridge, you can define a multi-access link with a single bridge attached to it.
108+
109+
For example, you can use the following topology to create a topology equivalent to the one above; the multi-access link is expanded into a series of point-to-point links with the **br** device.
110+
111+
```
112+
nodes:
113+
rtr:
114+
device: eos
115+
h1:
116+
device: linux
117+
h2:
118+
device: linux
119+
br:
120+
device: ioll2
121+
role: bridge
122+
123+
links: [ rtr-h1-h2-br ]
124+
```
125+
126+
Each multi-access link implemented with a bridge node is a separate bridging domain. _netlab_ creates a separate VLAN with a topology-wide unique VLAN ID on the bridge node for every multi-access link and copies the link attributes into the VLAN attributes. Thus, it's safe to use the same bridge node to implement multiple multi-access links.

Diff for: netsim/roles/bridge.py

+42-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ..utils import log
1313
from ..data import global_vars,append_to_list,get_box
1414
from ..augment import links
15+
from ..modules import _dataplane
1516
from . import select_nodes_by_role
1617

1718
"""
@@ -66,12 +67,32 @@ def add_default_access_vlan(topology: Box) -> bool:
6667

6768
return link_found
6869

70+
"""
71+
Get next internal VLAN for an isolated bridge domain
72+
"""
73+
def get_next_internal_vlan(start: int, use: str) -> int:
74+
while start < 4090: # Skip some of the high-end VLANs
75+
if not _dataplane.is_id_used('vlan_id',start): # Is the VLAN used anywhere?
76+
_dataplane.extend_id_set('vlan_id',set([start])) # No, grab it
77+
return start # ... and return it to the caller
78+
start = start + 1 # Otherwise, try the next one
79+
80+
log.error(
81+
f'Cannot allocate an internal VLAN for {use} -- I ran out of VLANs',
82+
category=log.IncorrectValue,
83+
module='vlan')
84+
return 1 # We need some value, and it doesn't matter
85+
6986
"""
7087
When exactly one of the nodes on link with more than two nodes is a bridge,
7188
expand the link into multiple P2P links with the bridge node. Print a warning
7289
(and do nothing) if there are more than two bridges attached to the link
7390
"""
7491
def expand_multiaccess_links(topology: Box) -> None:
92+
skip_linkattr = list(topology.defaults.vlan.attributes.phy_ifattr)
93+
del_linkattr = ['linkindex','interfaces']
94+
int_vlan_id = global_vars.get_const('bridge.internal_vlan.start',100)
95+
7596
for link in list(topology.get('links',[])):
7697
if '_br_list' not in link: # Is this one of the relevant links?
7798
continue # No, move on
@@ -90,10 +111,29 @@ def expand_multiaccess_links(topology: Box) -> None:
90111
module='bridge')
91112
continue
92113

93-
br_intf = br_list[0] # Remember the bridge interface
114+
# Do we have any of the physical attributes on the link? For the moment, let's assume that's an error
115+
#
116+
wrong_attr = [ k for k in link.keys() if k in skip_linkattr and k not in del_linkattr ]
117+
if wrong_attr:
118+
log.error(
119+
text=f'Attribute(s) {",".join(wrong_attr)} cannot be used on multi-access link {link._linkname}' + \
120+
' implemented with a bridge',
121+
category=log.IncorrectAttr,
122+
module='bridge')
123+
continue
124+
125+
br_intf = br_list[0] # Get the bridge interface
126+
br_node = topology.nodes[br_intf.node] # And the corresponding node
127+
int_vlan_id = get_next_internal_vlan(int_vlan_id,'multi-access link {link._linkname}')
128+
vname = f'br_vlan_{int_vlan_id}' # Create the internal VLAN
129+
br_node.vlans[vname] = { k:v for k,v in link.items() if k not in del_linkattr }
130+
br_node.vlans[vname].id = int_vlan_id # ... set its ID
131+
br_node.vlans[vname].mode = 'bridge' # ... and make it a L2-only VLAN
132+
br_intf.vlan.access = vname # ... and use it on the bridge interface
133+
94134
link_cnt = 0
95135
for intf in link.interfaces:
96-
if intf != br_intf: # Did we find an interesting interface?
136+
if intf.node != br_intf.node: # Is this a non-bridge interface?
97137
l_data = get_box(link) # Copy the link data
98138
link_cnt += 1
99139
l_data._linkname = f'{link._linkname}.{link_cnt}' # Create unique link and and linkindex

0 commit comments

Comments
 (0)