Manual Stream Configuration¶
Goals¶
In this example we demonstrate manual configuration of stream identification, stream splitting, stream merging, stream encoding and stream decoding to achieve the desired stream redundancy.
4.4
The Model¶
In this configuration we replicate a network topology that is presented in the IEEE 802.1 CB amendment. The network contains one source and on destination nodes, where the source sends a redundant data stream through five switches. The stream is duplicated in three of the switches and merged in two of them.
Here is the network:
Here is the configuration:
[General]
network = ManualConfigurationShowcase
sim-time-limit = 0.1s
description = "Manual static stream redundancy configuration"
# disable automatic MAC forwarding table configuration
*.macForwardingTableConfigurator.typename = ""
# all Ethernet interfaces have 100 Mbps speed
*.*.eth[*].bitrate = 100Mbps
# decrease throughput measurement interval
**.throughput.interval = 10ms
# link breaks between switches
*.scenarioManager.script = xml("<scenario> \
<at t='0.1'> \
<disconnect src-module='s1' dest-module='s2a'/> \
</at> \
<at t='0.2'> \
<disconnect src-module='s2b' dest-module='s3b'/> \
</at> \
</scenario>")
# enable frame replication and elimination
*.*.hasStreamRedundancy = true
# source application
*.source.numApps = 1
*.source.app[0].typename = "UdpSourceApp"
*.source.app[0].io.destAddress = "destination"
*.source.app[0].io.destPort = 1000
*.source.app[0].source.displayStringTextFormat = "sent %p pk (%l)"
*.source.app[0].source.packetLength = 1200B
*.source.app[0].source.productionInterval = truncnormal(100us,50us)
# destination application
*.destination.numApps = 1
*.destination.app[0].typename = "UdpSinkApp"
*.destination.app[0].io.localPort = 1000
# all interfaces must have the same address to accept packets from all streams
*.destination.eth[*].address = "0A-AA-12-34-56-78"
# visualizer
*.visualizer.infoVisualizer.modules = "*.source.app[0].source or *.destination.app[0].sink"
# configure all egress traffic as part of stream s1, start sequence numbering
*.source.bridging.streamIdentifier.identifier.mapping = [{packetFilter: "*", stream: "s1", sequenceNumbering: true}]
# encode egress stream s1 to VLAN 1
*.source.bridging.streamCoder.encoder.mapping = [{stream: "s1", vlan: 1}]
# map destination MAC address and VLAN pairs to network interfaces
*.s1.macTable.forwardingTable = [{address: "destination", vlan: 1, interface: "eth0"},
{address: "destination", vlan: 2, interface: "eth1"}]
# allow ingress traffic from VLAN 1
*.s1.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1]
# enable stream policing in layer 2 bridging
*.s1.bridging.streamRelay.typename = "StreamRelayLayer"
*.s1.bridging.streamCoder.typename = "StreamCoderLayer"
# map eth2 VLAN 1 to stream s1
*.s1.bridging.streamCoder.decoder.mapping = [{interface: "eth2", vlan: 1, stream: "s1"}]
# eliminate duplicates from stream s1
*.s1.bridging.streamRelay.merger.mapping = {s1: "s1"}
# split stream s1 into s2a and s2b
*.s1.bridging.streamRelay.splitter.mapping = {s1: ["s2a", "s2b"]}
# map stream s2a to VLAN 1 and s2b to VLAN 2
*.s1.bridging.streamCoder.encoder.mapping = [{stream: "s2a", vlan: 1},
{stream: "s2b", vlan: 2}]
# map destination MAC address and VLAN pairs to network interfaces
*.s2a.macTable.forwardingTable = [{address: "destination", vlan: 1, interface: "eth0"},
{address: "destination", vlan: 2, interface: "eth1"}]
# allow ingress traffic from VLAN 1 and 2
*.s2a.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1, 2]
# enable stream policing in layer 2 bridging
*.s2a.bridging.streamRelay.typename = "StreamRelayLayer"
*.s2a.bridging.streamCoder.typename = "StreamCoderLayer"
# map eth2 VLAN 1 to stream s2a and eth1 VLAN 2 to stream s2b-s2a
*.s2a.bridging.streamCoder.decoder.mapping = [{interface: "eth2", vlan: 1, stream: "s2a"},
{interface: "eth1", vlan: 2, stream: "s2b-s2a"}]
# merge streams s2a and s2b-s2a in into s3a
*.s2a.bridging.streamRelay.merger.mapping = {s2a: "s3a", "s2b-s2a": "s3a"}
# split stream s2a into s3a and s2b
*.s2a.bridging.streamRelay.splitter.mapping = {s3a: ["s3a", "s2b"]}
# map stream s3a to VLAN 1 and s2b to VLAN 2
*.s2a.bridging.streamCoder.encoder.mapping = [{stream: "s3a", vlan: 1},
{stream: "s2b", vlan: 2}]
# map destination MAC address and VLAN pairs to network interfaces
*.s2b.macTable.forwardingTable = [{address: "destination", vlan: 1, interface: "eth0"},
{address: "destination", vlan: 2, interface: "eth1"}]
# allow ingress traffic from VLAN 1 and 2
*.s2b.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1, 2]
# enable stream policing in layer 2 bridging
*.s2b.bridging.streamRelay.typename = "StreamRelayLayer"
*.s2b.bridging.streamCoder.typename = "StreamCoderLayer"
# map eth2 VLAN 2 to stream s2b and eth1 VLAN 1 to stream s2a-s2b
*.s2b.bridging.streamCoder.decoder.mapping = [{interface: "eth2", vlan: 2, stream: "s2b"},
{interface: "eth1", vlan: 2, stream: "s2a-s2b"}]
# merge streams s2b and s2a-s2b in into s3b
*.s2b.bridging.streamRelay.merger.mapping = {s2b: "s3b", "s2a-s2b": "s3b"}
# split stream s2b into s3b and s2a
*.s2b.bridging.streamRelay.splitter.mapping = {s3b: ["s3b", "s2a"]}
# stream s3a maps to VLAN 1 and s2a to VLAN 2
*.s2b.bridging.streamCoder.encoder.mapping = [{stream: "s3b", vlan: 1},
{stream: "s2a", vlan: 2}]
# map destination MAC address and VLAN pairs to network interfaces
*.s3a.macTable.forwardingTable = [{address: "destination", vlan: 1, interface: "eth0"}]
# map eth1 VLAN 1 to stream s3a
*.s3a.bridging.streamCoder.decoder.mapping = [{interface: "eth1", vlan: 1, stream: "s3a"}]
# stream s3a maps to VLAN 1
*.s3a.bridging.streamCoder.encoder.mapping = [{stream: "s3a", vlan: 1}]
# allow ingress traffic from VLAN 1
*.s3a.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1]
# map destination MAC address and VLAN pairs to network interfaces
*.s3b.macTable.forwardingTable = [{address: "destination", vlan: 1, interface: "eth0"}]
# map eth1 VLAN 1 to stream s3b
*.s3b.bridging.streamCoder.decoder.mapping = [{interface: "eth1", vlan: 1, stream: "s3b"}]
# stream s3b maps to VLAN 1
*.s3b.bridging.streamCoder.encoder.mapping = [{stream: "s3b", vlan: 1}]
# allow ingress traffic from VLAN 1
*.s3b.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1]
# allow ingress traffic from VLAN 1
*.destination.ieee8021q.qTagHeaderChecker.vlanIdFilter = [1]
# map eth0 VLAN 1 to stream s3a and eth1 VLAN 1 to stream s3b
*.destination.bridging.streamCoder.decoder.mapping = [{interface: "eth0", vlan: 1, stream: "s3a"},
{interface: "eth1", vlan: 1, stream: "s3b"}]
# merge streams s3a and s3b into null stream
*.destination.bridging.streamRelay.merger.mapping = {s3a: "", s3b: ""}
Results¶
Here are the number of received and sent packets:
Here is the ratio of received and sent packets:
The expected number of successfully received packets relative to the number of sent packets is verified by the python scripts. The expected result is around 0.657.
Sources: omnetpp.ini
, ManualConfigurationShowcase.ned
Try It Yourself¶
If you already have INET and OMNeT++ installed, start the IDE by typing
omnetpp
, import the INET project into the IDE, then navigate to the
inet/showcases/tsn/framereplication/manualconfiguration
folder in the Project Explorer. There, you can view
and edit the showcase files, run simulations, and analyze results.
Otherwise, there is an easy way to install INET and OMNeT++ using opp_env, and run the simulation interactively.
Ensure that opp_env
is installed on your system, then execute:
$ opp_env run inet-4.4 --init -w inet-workspace --install --chdir \
-c 'cd inet-4.4.*/showcases/tsn/framereplication/manualconfiguration && inet'
This command creates an inet-workspace
directory, installs the appropriate
versions of INET and OMNeT++ within it, and launches the inet
command in the
showcase directory for interactive simulation.
Alternatively, for a more hands-on experience, you can first set up the workspace and then open an interactive shell:
$ opp_env install --init -w inet-workspace inet-4.4
$ cd inet-workspace
$ opp_env shell
Inside the shell, start the IDE by typing omnetpp
, import the INET project,
then start exploring.