Skip to content

gtpUser: fix: a GTP-U tunnel keeps the region tags of the packet it carries - #3

Open
tabgab wants to merge 1 commit into
inet-framework:devfrom
tabgab:fix/gtp-region-tags
Open

tabgab wants to merge 1 commit into
inet-framework:devfrom
tabgab:fix/gtp-region-tags

Conversation

@tabgab

@tabgab tabgab commented Nov 7, 2026 •

Copy link
Copy Markdown

On dev 764db6f, one commit. (Issues are disabled in this repository, so the problem is described here.)

Problem

A region tag that a packet carries before a gNB or a UPF tunnels it (CreationTimeTag, ResidenceTimeTag, ElapsedTimeTag, ... added with Packet::addRegionTag) is gone after the other end of the GTP-U tunnel. This happens in an IP and in an Ethernet PDU session, uplink and downlink, and across the UPF's relay from one tunnel into another.

Tags attached to a chunk survive (the chunk is shared), which hides the problem when a source tags the chunk it creates. Consumers lose their measurements: the end-to-end delay from CreationTimeTag, the residence times of TSN bridging.

Cause

src/simu5g/corenetwork/gtp/GtpUser.cc (dev 764db6f) builds a new Packet for the GTP-U message and moves the data over with insertAtBack(peekData()). That copies chunks only; the region tags of a Packet live in Packet::regionTags, not in its chunks.

handleFromTrafficFlowFilter: gtpPacket = new Packet; insertAtFront(header); insertAtBack(datagram->peekData())
handleFromUdp:               originalPacket = new Packet; pkt->popAtFront<GtpUserMsg>(); insertAtBack(pkt->peekData())
tunnelDownlink:              gtpMsg = new Packet; insertAtFront(header); insertAtBack(datagram->peekData())

INET's convention is that the region tags of a packet travel with its data through every encapsulation. INET's own tunnelling inserts the Ipv4/Udp/Ethernet headers into the same Packet, and Packet::insertAt / removeAt shift the region tags. Where INET builds a second Packet from the data of the first, it copies the region tags explicitly with Packet::copyRegionTags (EthernetCutthroughSource.cc:38, CutthroughSource.cc:38).

Reproduction

Tag a frame with addRegionTag<CreationTimeTag>() where it is about to enter the tunnel (for example as upf.ethBridge.ethernet.fcsInserter pushes it out, or as the gNB PDCP rx entity passes it up), and look for the tag where it has left the tunnel: no byte carries it. The two tests of this PR do this.

Fix

GtpUser creates a new Packet for every GTP-U message and copies the data of the original packet into it with insertAtBack(peekData()). That copies the chunks, so the tags of a chunk survive, but the region tags of the Packet were lost at each end of a tunnel. The three places that build a Packet from another one now copy the region tags of the data with Packet::copyRegionTags, as INET does where it builds a second Packet (EthernetCutthroughSource, CutthroughSource):

// handleFromTrafficFlowFilter
gtpPacket->copyRegionTags(*datagram, datagram->getFrontOffset(), header->getChunkLength(), datagram->getDataLength());
// handleFromUdp
originalPacket->copyRegionTags(*pkt, pkt->getFrontOffset(), b(0), pkt->getDataLength());
// tunnelDownlink
gtpMsg->copyRegionTags(*datagram, datagram->getFrontOffset(), header->getChunkLength(), datagram->getDataLength());

Region tag offsets count from the start of the packet content, popped front bytes included. So the source offset is the front offset of the data, and the target offset is where the data starts in the new packet (after the GTP-U header, or 0 in the decapsulated packet). copyRegionTags clips the copy to the data, so tags over the popped GTP-U header or the removed Ethernet FCS do not leak. tunnelUplinkOverN6 inserts its UDP and IP headers into the same Packet and needs no change.

Not changed on purpose: the packet-level tags (Packet::copyTags). GtpUser sets the ones it needs (PacketProtocolTag, QFI, session) itself; a blanket copy would carry stale ones such as the socket and interface tags of the outer packet.

Tests (tests/module)

  • GtpRegionTags.test: Ethernet PDU session, both directions. A probe adds a CreationTimeTag over the whole frame where it is about to enter the tunnel (downlink: packetPushedOut of upf.ethBridge.ethernet.fcsInserter; uplink: sentPacketToUpperLayer of the gNB PDCP rx entity). It counts the tagged bytes where the frame has left the tunnel (downlink: receivedPacketFromUpperLayer of the gNB PDCP tx entity; uplink: packetPushedIn of upf.ethBridge.ethernet.fcsChecker).
  • GtpRegionTagsIp.test: IP session, two gNBs. ue[0] at gnb sends UDP to ue[1] at gnb2, so the datagram is encapsulated at gnb, decapsulated and encapsulated again at the UPF (tunnelDownlink), and decapsulated at gnb2.
test direction tagged at entry dev with the fix
GtpRegionTags downlink 118 of 118 bytes 0 of 115 (FAIL) 114 of 115 (PASS): only the SDAP header is untagged
GtpRegionTags uplink 116 of 116 bytes 0 of 118 (FAIL) 112 of 118 (PASS): the 4-byte FCS the UPF rebuilds, and 2 bytes the gNB stack already leaves untagged before GtpUser
GtpRegionTagsIp UE to UE 129 of 129 bytes 0 of 128 (FAIL) 128 of 128 (PASS)

Each of the three lines is needed: without site 1 or 2 both tests fail, and without site 3 GtpRegionTagsIp fails.

Evidence

Simu5G dev 764db6f against INET 4.7.0, OMNeT++ 6.5.0, release, macOS arm64 (./runtest in tests/module; the build here also carries the macOS intval_t change of #1, which this PR does not contain). The other module tests: 13 PASS + 1 EXPECTEDFAIL (TwoSessionsPerUe). EthernetSessionQosRuleFields fails with and without the fix on this INET (Object (inet::EthernetFcs) has no member named 'dest').

Not run: debug build; the fingerprint, unit and physics suites (no region tag enters a fingerprint ingredient, not checked); INET master; Linux.

…arries

GtpUser creates a new Packet for every GTP-U message and copies the data
of the original packet into it with insertAtBack(peekData()). That
copies the chunks, so the tags of a chunk survive, but the region tags
of the Packet, which are not part of its chunks, were lost at each end
of a tunnel: a CreationTimeTag or a ResidenceTimeTag set before the
gNB or the UPF was gone after the other end. In INET the region tags of
a packet travel with its data through every encapsulation.

The three places that build a Packet from another one now copy the
region tags of the data: the encapsulation of a packet of the traffic
flow filter, the decapsulation of a GTP-U message, and the
encapsulation of a packet into a downlink tunnel.

The tests tag a frame of an Ethernet PDU session before it enters the
tunnel, uplink and downlink, and a datagram that the UPF relays from
the tunnel of one UE into the tunnel of another, and count the bytes
that carry the tag where the packet has left the tunnel.
@avarga

avarga commented Nov 7, 2026

Copy link
Copy Markdown
Member

The reported issue is a duplicate of Unipisa#309, and the fix (a different one from this) is present in branch v1.8.x. Currently it is recommended to use v1.8.x instead of dev for projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants