Table Of Contents
Table Of Contents

Communicating with Tags


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.


Simple network node structure in the IDE

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.


Complex network node structure in Qtenv

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.