Communicating with Tags¶
Overview¶
Modules often exchange information by sending packets along with supplementary data, referred to as tags. A tag is usually a small data structure that focuses on a single parameterization aspect of a protocol. Tags can be attached to the whole packet, known as packet tags, or to specific parts of the packet, known as region tags. Tags are implemented as data container C++ classes and they are usually generated by the OMNeT++ MSG compiler. These are the primary types of tags:
requests carry information from higher layers to lower layers (e.g.
MacAddressReq
).indications carry information from lower layers to higher layers (e.g.
InterfaceInd
).plain tags contain some meta-information (e.g.
PacketProtocolTag
).base classes must not be attached to packets (e.g.
TagBase
).
For example, a request tag that specifies the source and destination MAC address could be implemented in an MSG file as follows:
class MacAddressReq extends TagBase
{
MacAddress srcAddress; // may be unspecified
MacAddress destAddress; // always specified
}
The following list gives a short description of several often used packet tags:
PacketProtcolTag
specifies the protocol of the packet’s contents
DispatchProtocolReq
specifies the receiver protocol module inside the network node
EncapsulationProcotolReq
specifies the requested protocol header encapsulation order
SocketReq
specifies the application socket
L4PortReq
specifies the source and destination ports
L3AddressReq
specifies source and destination network addresses
InterfaceReq
specifies the outgoing network interface
NextHopAddressReq
specifies the next hop address for packet routing
VlanReq
specifies the virtual LAN identifier of IEEE 802.1Q
PcpReq
specifies the priority code point of IEEE 802.1Q
StreamReq
specifies the TSN stream identifier inside the network node
MacAddressReq
specifies source and destination MAC addresses
Ieee80211ModeReq
specifies the IEEE 802.11 PHY mode
Ieee80211ChannelReq
specifies the IEEE 802.11 channel
SignalPowerReq
specifies transmit signal power
All request tags have their indication counterparts. For example, there are
indications such as SocketInd
, InterfaceInd
, StreamInd
.
The requests are usually attached to outgoing packets, the indications are
usually attached to incoming packets.
The following list gives a short description of several often used region tags:
IdentityTag
uniquely identifies individual bits in the network over the lifetime of the whole simulation
CreationTimeTag
specifies the creation time of data regions for lifetime measurements
FlowTag
specifies the packet flows of data regions for various flow specific measurements
PacketEventTag
carries information about queueing, processing, transmission, etc. events that happened to data regions
Communicating Through Protocol Layers¶
Tags can pass through protocol modules, and they can reach far beyond the module that initially attached them, in both downward and upward directions. Typically, tags are removed at the point where they are processed, usually being transformed into header fields within a packet, or used for some protocol specific decisions. Protocols have the liberty to disregard any tags at their discretion based on their configuration and state.
Both packet tags and region tags remain unchanged for many operations that protocol modules carry out with packets. For example, when packets are enqueued/dequeued, encapsulated/decapsulated, cloned, buffered, or stored for later reuse, the tags remain unchanged.
Specifying the Protocol of a Packet¶
The most important packet tag is the PacketProtocolTag
. It specifies the
outermost protocol of the packet. This tag should always be present, because the
packet protocol cannot be correctly determined just by looking at the raw data.
In contrast, the inner protocol headers in the packet can be usually recursively
identified by protocol fields such as the protocol ID field of the IPv4 header.
The PacketProtocolTag
is used among others for dissecting the packet
along the protocol headers, or for printing the packet as a human readable
string to help interpreting its contents.
Normally a packet is transformed from one protocol to another in a single step,
so the packet protocol tag either specifies the protocol before the operation
or the protocol after the operation. For example, the Udp protocol module
encapsulates the outgoing packet using a UdpHeader
. The packet protocol
is set to an application specific protocol before the UDP encapsulation and it’s
set to UDP protocol after the encapsulation.
Sometimes, protocol implementations themselves are split up into several smaller modules. For example, the modular Ethernet implementation uses a separate module for the insertion of the Ethernet MAC header and the Ethernet FCS. This module structure implies that the packet can be seen between the modules as a partially built Ethernet MAC protocol packet. In such a case the packet protocol tag can only specify the inner protocol that is being encapsulated into an Ethernet MAC frame.
Dispatching Packets to Protocol Modules¶
Inside a network node, protocol modules interact with one another by sending
Packet
or Message
objects. INET is very flexible in terms of what
structure the protocol modules can be connected. Protocols can be connected
directly to each other, or they can be connected through one or more
MessageDispatcher modules. This flexibility allows for the creation of
both simple and complex network node architectures.
How to Connect Protocol Modules¶
Simple network nodes can be constructed, for example, using a linear protocol stack, where protocol modules are directly connected to one another without using message dispatcher modules.
More complex network nodes can be created by grouping protocols into layers and connecting them through MessageDispatcher modules, which facilitates many-to-one and many-to-many relationships among the protocols of the layers.
It’s also possible to use message dispatcher modules hierarchically within multiple levels of nested compound modules. Ultimately, one could even connect all protocols to a single central message dispatcher module. There is an important limitation though, only one instance of a given protocol module can be connected to a message dispatcher.
To support the packet dispatching mechanism, certain additional requirements must be met in C++ code:
protocols must be registered using
registerProtocol
packets must have
DispatchProtocolReq
tags attached
Registering Protocols¶
Protocol modules must call the registerProtocol
function from the initialize
method to inform connected MessageDispatcher
modules of their presence. The following code fragment demonstrates this for the
IPv4 protocol implementation:
void Ipv4::initialize(int stage)
{
if (stage == INITSTAGE_NETWORK_LAYER) {
registerService(Protocol::ipv4, gate("transportIn"), gate("transportOut"));
registerProtocol(Protocol::ipv4, gate("queueOut"), gate("queueIn"));
}
}
Registering the protocols allows the dispatcher modules to learn which gates the protocol modules are connected to. The same protocol is not allowed to be registered in the same message dispatcher using different gates, because that would make the dispatching mechanism ambiguous.
Sending Packets with Dispatch Request¶
Packets and messages must have the DispatchProtocolReq
tag attached to
them in order for the message dispatcher modules to correctly dispatch them to
the intended recipient within the network node. The following example shows how
a MAC protocol could send up a packet to the Ipv4 protocol module without
actually knowing where that module is connected in the network node architecture:
void Mac::sendUp(Packet *packet)
{
auto req = packet->addTagIfAbsent<DispatchProtocolReq>();
req->setProtocol(&Protocol::ipv4); // set destination protocol
req->setServicePrimitive(SP_INDICATION); // determine receiving gate
send(packet, "upperLayerOut");
}
The DispatchProtocolReq
tag specifies both the intended recipient protocol
and the requested service primitive. The service primitive, similarly to OSI
terminology, can be one of:
SP_REQUEST
for service requests from layer N+1 to layer N
SP_CONFIRM
for service confirmations from layer N to layer N+1
SP_INDICATION
for protocol indications from layer N to layer N+1
SP_RESPONSE
for protocol response from layer N+1 to layer N
Currently, INET modules only use the SP_REQUEST
and SP_INDICATION
service primitives, the other two are only present for completeness. The request
service primitive is used when a higher layer protocol module (e.g. Tcp)
wants to deliver a packet to a lower layer protocol module (e.g. Ipv4).
Similarly, the SP_INDICATION
service primitive is used when a lower layer
protocol module (e.g. Ethernet) wants to deliver a packet to a higher layer
protocol module (e.g. Ipv4).
Determining the Next Protocol¶
A protocol module has several options for determining which protocol to forward a packet to. The list below shows some possibilities:
The next protocol can be hard-coded in C++. For example, the UDP protocol is hard-coded in C++ in the
UdpSocket
class, and similarly other protocols are also hard-coded in other protocol specific sockets.The next protocol can be specified by a module parameter, as is the case of a network interface module specifying its expected protocol. The Ipv4 module uses this information to dispatch a packet to the expected protocol of the selected route’s network interface.
The next protocol can be dependent on module state. For example, the TSN stream encoder module forwards packets that match the TSN stream mapping to the 802.1 Q-TAG protocol for encapsulation.
The next protocol can be determined by a packet header field. For example, the Ipv4 module uses the IP protocol ID header field from the
Ipv4Header
to look up the next protocol as shown below:const Protocol *Ipv4::getNextProtocol(Packet *packet) { auto ipv4Header = packet->peekAtFront<Ipv4Header>(); auto ipProtocolId = ipv4Header->getProtocolId(); return ProtocolGroup::getIpProtocolGroup()->getProtocol(ipProtocolId); }The next protocol can be determined by some packet meta-data. For example, the Tcp module uses the type of the destination address from the
L3AddressReq
tag to determine if the packet should be sent to the Ipv4 or Ipv6 module.The next protocol can be indirectly specified by a protocol encapsulation request. For example, other modules may have attached an
EncapsulationProtocolReq
to the packet in an earlier stage of the packet processing.
Controlling the Packet Encapsulation Order¶
A packet typically contains multiple protocol-specific headers, such as TCP, IP, Ethernet, and sometimes additional optional headers like 802.1Q, 802.1R, 802.1AE, and others. The order of packet headers is determined by the order in which the packet reaches the relevant protocol modules for encapsulation.
The encapsulation process may need to be different for each packet. For example,
an application may need to send a packet to a specific VLAN. In this case, the
application should attach a VlanReq
tag to the packet with the desired
VLAN ID. However, it cannot directly send the packet to the relevant 802.1Q
protocol module, because the packet may need to be delivered to the UDP protocol
first. To achieve the desired protocol encapsulation order, the application should
also attach an EncapsulationProtocolReq
tag specifying that the packet
should ultimately be delivered to the 802.1Q protocol for encapsulation. The
underlying protocol modules will use this information to determine when the
802.1Q encapsulation should take place.
The EncapsulationProtocolReq
generally outlines the sequence of protocol
modules that a packet should be delivered to for further encapsulation. Additional
tags attached to the packet are used as additional parameters for the requested
processing steps. The attached encapsulation request may be changed several times
during packet processing, new protocols may be added, already added protocols
may be removed, and so on.
For example, the IP protocol determines the outgoing interface using the routing
table and the destination address. The selected network interface specifies the
expected protocol that the packet should have in order for the interface module
to operate properly. The specified protocol is appended to the end of the requested
encapsulation protocols of the packet, because it should be the last encapsulation
before the packet reaches the network interface. For example, if the IP module
selects an Ethernet network interface, then it appends the Ethernet MAC protocol
to the EncapsulationProtocolReq
, and the packet is ultimately encapsulated
into an EthernetMacHeader
before reaching the network interface module.
Transforming Inbound Packets to Outbound¶
As part of the forwarding process of Ethernet switches, an inbound packet is transformed into an outbound packet. This process is carried out by default in the PacketDirectionReverser module. The transformation is more like a policy and it can be replaced by the user with other modules. The default module doesn’t change the packet contents except for removing the already popped front and back parts, but it changes the attached packet tags significantly.
The inbound packet usually contains a few tags such as PacketProtocolTag
and DirectionTag
, and it also contains several indications such as the
InterfaceInd
, MacAddressInd
, VlandInd
, PcpInd
,
EncapsulationProtocolInd
and so on. The transformation keeps only the
PacketProtocolTag
, it removes all attached indications, and attaches a
set of requests so that the packet will be encapsulated in the same protocol
headers, and it will be sent out on the same interface as it came in.
Of course, this is just the start of the processing of the outbound packet. During the several steps that follows any of the attached requests can be replaced with new ones potentially ultimately resulting in the packet to be handled in a completely different way.