how to know the number of retransmitted packets

272 views
Skip to first unread message

H D

unread,
Mar 18, 2024, 2:31:09 PMMar 18
to ns-3-users
Hello,
I am running a Flow in TCP protocol, and I am looking for a way to print the number of packets that were retransmitted, but I haven't found such a way.
Do you know of a way to do this?
note: I am using BulkSendHelper, and I gathered all the other information like this:
..
    FlowMonitorHelper flowmon;
    Ptr<FlowMonitor> monitor = flowmon.InstallAll();
    Simulator::Stop(Seconds(15));
    // Run simulation
    Simulator::Run();

    Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier>(flowmon.GetClassifier());
    std::map<FlowId, FlowMonitor::FlowStats> stats = monitor->GetFlowStats();

    for (long unsigned i = 1; i <= stats.size(); i++) {
        std::cout << std::endl << "*** Flow monitor statistics ***" << std::endl;
        Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(i);
        std::cout << "  Flow ID: " << i << ", Src Addr: " << t.sourceAddress << ", Dst Addr: " << t.destinationAddress << std::endl;
        std::cout << "  Tx Packets/Bytes:   " << stats[i].txPackets << " / " << stats[i].txBytes << std::endl;
        std::cout << "  Rx Packets/Bytes:   " << stats[i].rxPackets << " / " << stats[i].rxBytes << std::endl;
        uint32_t packetsDroppedByQueueDisc = 0;
        uint64_t bytesDroppedByQueueDisc = 0;
        if (stats[i].packetsDropped.size() > Ipv4FlowProbe::DROP_QUEUE_DISC)
        {
            packetsDroppedByQueueDisc = stats[i].packetsDropped[Ipv4FlowProbe::DROP_QUEUE_DISC];
            bytesDroppedByQueueDisc = stats[i].bytesDropped[Ipv4FlowProbe::DROP_QUEUE_DISC];
        }
        std::cout << "  Packets/Bytes Dropped by Queue Disc:   " << packetsDroppedByQueueDisc << " / " << bytesDroppedByQueueDisc << std::endl;
        uint32_t packetsDroppedByNetDevice = 0;
        uint64_t bytesDroppedByNetDevice = 0;
        if (stats[i].packetsDropped.size() > Ipv4FlowProbe::DROP_QUEUE)
        {
            packetsDroppedByNetDevice = stats[i].packetsDropped[Ipv4FlowProbe::DROP_QUEUE];
            bytesDroppedByNetDevice = stats[i].bytesDropped[Ipv4FlowProbe::DROP_QUEUE];
        }
        std::cout << "  Packets/Bytes Dropped by NetDevice:   " << packetsDroppedByNetDevice << " / " << bytesDroppedByNetDevice << std::endl;
    }

H D

unread,
Apr 7, 2024, 5:10:15 AMApr 7
to ns-3-users
can someone help me please with this one?

Tommaso Pecorella

unread,
Apr 7, 2024, 5:04:37 PMApr 7
to ns-3-users
FlowMonitor can not track retransmitted packets because they're retransmitted by L4 (TCP), and the only thing FlowMonitor cares about from the L4 is the src/dst port.

I can have a "rough" number by comparing the amount of data transferred as reported by FlowMonitor and the TCP data transfer size - but it's a very rough estimate.
Alternatively, you could implement your own trace by checking the sequence numbers (they're in the "Tx" trace). 

Metaor Mani

unread,
Apr 8, 2024, 2:28:01 PMApr 8
to ns-3-users
Thank you for your response.
Could you please provide further detail regarding the second option? Is there any alternative method to achieve this? (Even without using FlowMonitor)

Tommaso Pecorella

unread,
Apr 8, 2024, 6:31:39 PMApr 8
to ns-3-users
As I said, I would:
- Find where in the code the TCP layer decides to retransmit a packet,
- Add a Trace source there.

Now, the beautiful thing about OpenSource is that anybody can look at the code, and modify it too.
The function is called TcpSocketBase::SendDataPacket, adding a trace with the indication about the packet being a retransmission (or not) is embarrassingly easy.

Read it, modify it, and get your own trace. Feel free to open a merge request when you're done, we'll be happy about it.

H D

unread,
May 3, 2024, 9:47:40 AMMay 3
to ns-3-users
I see it, thank you.
I tried to follow it (for example, I see that there is also the function DoRetransmit so in the end it triggers the function SendDataPacket) but it's not clear to me how I can implement it so that there is such a unique field for each flow.
My goal is to add such a field for each flow (relevant for both QUIC and TCP) so that I can get number of the retransmissions by this field for each FlowID using FlowMonitorHelper.
I would appreciate it if you could guide me on how to implement this.

Tommaso Pecorella

unread,
May 3, 2024, 4:33:43 PMMay 3
to ns-3-users
Short answer: you won't be able to add it to FlowMonitor without significant expertise and work.

As I said, and I hate to repeat myself: FlowMonitor can not track retransmitted packets because they're retransmitted by L4 (TCP), and the only thing FlowMonitor cares about from the L4 is the src/dst port.

What I was suggesting is to collect the data somewhere (study how to get data from a TraceSource, it's in the manual) and use them later. I.e., you can slam them into a table and, at the end of the simulation, dump the table.
However, it will NOT be handled by FlowMonitor, because modifying FlowMonitor to collect also these data is not in the scope of FlowMonitor.

Of course, since code is code, you could add a "TcpFlowClassifier", a "TcpFlowProbe", hook them in the right way to the right callback - eventually adding callbacks if needed, and have that collect stats.
Difficulty: 5/10, if you know what to do, 12/10 if you don't.
If you do it we'll be happy to have a Merge Request with the new functionality.

Tommaso Pecorella

unread,
May 3, 2024, 4:47:10 PMMay 3
to ns-3-users
Note: if you're looking for the 4-tuple IP/port - Src/Dst, they can be found by using this code - it works anywhere in TcpSocketBase:

if (m_endPoint)
{
Ipv4Address srcIp = m_endPoint->GetPeerAddress();
uint16_t srcPort = m_endPoint->GetPeerPort();
Ipv4Address dstIp = m_endPoint->GetLocalAddress();
uint16_t dstPort = m_endPoint->GetLocalPort();
// Do something with the above stuff
}
else if (m_endPoint6)
{
Ipv6Address srcIp = m_endPoint6->GetPeerAddress();
uint16_t srcPort = m_endPoint6->GetPeerPort();
Ipv6Address dstIp = m_endPoint6->GetLocalAddress();
uint16_t dstPort = m_endPoint6->GetLocalPort();
// Do something with the above stuff
}

H D

unread,
May 4, 2024, 5:43:42 PMMay 4
to ns-3-users
Thank you..
What type can I use for a variable so that conversion is possible from both Ipv4Address and Ipv6Address?
Alternatively, is there a simple way to convert  Ipv4Address/Ipv6Address to a string (std::string) that represents the address?

Tommaso Pecorella

unread,
May 5, 2024, 4:58:07 AMMay 5
to ns-3-users
Sorry, but this is in the manual and class documentation.

We're here to give suggestions on obscure problems, not on what is well documented and super-easy to find.

H D

unread,
May 6, 2024, 3:55:14 PMMay 6
to ns-3-users
I added the red part (for sanity) in src/internet/model/tcp-socket-base.cc file (see below)
But I am experiencing something a bit strange... when I run flows, I enter this condition if(isRetransmission) only (but not always) when the receiver (sinkApp) sends data back to the sender . For packets sent from the sender to the receiver, I never enter this condition.

/* Extract at most maxSize bytes from the TxBuffer at sequence seq, add the
    TCP header, and send to TcpL4Protocol */
uint32_t
TcpSocketBase::SendDataPacket(SequenceNumber32 seq, uint32_t maxSize, bool withAck)
{
    NS_LOG_FUNCTION(this << seq << maxSize << withAck);

    bool isStartOfTransmission = BytesInFlight() == 0U;
    TcpTxItem* outItem = m_txBuffer->CopyFromSequence(maxSize, seq);

    m_rateOps->SkbSent(outItem, isStartOfTransmission);

    bool isRetransmission = outItem->IsRetrans();
    Ptr<Packet> p = outItem->GetPacketCopy();
    uint32_t sz = p->GetSize(); // Size of packet
    uint8_t flags = withAck ? TcpHeader::ACK : 0;
    uint32_t remainingData = m_txBuffer->SizeFromSequence(seq + SequenceNumber32(sz));

        if(isRetransmission) {

                if (m_endPoint) {
                        Ipv4Address srcIp = m_endPoint->GetPeerAddress();
                        uint16_t srcPort = m_endPoint->GetPeerPort();
                        Ipv4Address dstIp = m_endPoint->GetLocalAddress();
                        uint16_t dstPort = m_endPoint->GetLocalPort();
                        std::cout << "srcIp: " << srcIp << ", srcPort:" << srcPort << ", dstIp: " << dstIp << ", dstPort" << dstPort << std::endl;
                }
        }

..
..

Tommaso Pecorella

unread,
May 7, 2024, 6:02:00 PMMay 7
to ns-3-users
I added these lines and tried to run tcp-variants-comparison with an error rate of 10%.

They do print stuff.

For the records, I added the same log (more or less) in TcpSocketBase::DoRetransmit, and (as expected) there's a difference in where you put the line, as the two places catch different events.

H D

unread,
May 8, 2024, 4:56:12 PMMay 8
to ns-3-users
I also added it in TcpSocketBase::DoRetransmit , but still can't see retransmissions for the transmitter..
There are no prints where 192.0.1.1 or 192.0.0.1  is the src

New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
...
...
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.4.2, srcPort:9090, dstIp: 192.0.1.1, dstPort49153
New.. srcIp: 192.0.3.2, srcPort:9090, dstIp: 192.0.0.1, dstPort49153

H D

unread,
May 9, 2024, 5:33:21 PMMay 9
to ns-3-users
I wonder what the explanation is for seeing only retransmissions for packets sent from the receiver back to the transmitter (I used PacketSinkHelper for the receiver and onoff for the transmitter) even when the flow contains a lot of bytes..
On the other hand, the receiver always has 0 lost packets, while the transmitter has some lost packets.

(the receiver: 0 lost packets and a few retransmissions  .. the sender: a few lost packets and 0 retransmissions)

H D

unread,
May 11, 2024, 2:13:51 PMMay 11
to ns-3-users
@Tommaso Pecorella‏ , can please assist with this?

Andreas Boltres

unread,
Jun 5, 2024, 8:04:16 AMJun 5
to ns-3-users
Since I also need an estimate of the amount of retransmitted data, I have implemented a retransmission trace into TcpSocketBase that is fired in TcpSocketBase::SendDataPacket() right after m_txTrace (if the packet is a retransmission, that is) and put the addition into a diff file "tcp-socket-base.diff" attached.

Now, If you're creating a TCP-compatible application in your code, the socket may not exist before the application is started. As a workaround, I have modified BulkSendApplication to "forward" the retransmission trace and make it accessible to the outside world via a trace source accessor called "TcpRetransmission" (see diff file "bulk-send-application.diff" attached). The connection between the inner and outer traced callback is made as soon as the socket is created/activated in BulkSendApplication::StartApplication().

Finally, you can use the BulkSendApplication's new trace source e.g. as follows:

app = CreateObject<BulkSendApplication>();
app->TraceConnectWithoutContext("TcpRetransmission", MakeCallback(&RegisterRetransmission));  // implement 
RegisterRetransmission() in your code

I'm open to create a merge request for this, after considering the following:
- The code compiles and seems to work as intended (providing a rough estimate of packet retransmissions only, of course). However, I welcome everybody/the maintainers to check the code for themselves.
- I'm open to shorter names for the traces.
- The trace "forwarding" in BulkSendApplication is just a quick hack for my personal needs, I'm open to more elegant integrations of the new callback if this should also be included in the merge request.

Cheers
bulk-send-application.diff
tcp-socket-base.diff

Tommaso Pecorella

unread,
Jun 6, 2024, 4:16:23 AMJun 6
to ns-3-users
@Andreas can you open a Merge Request? That would simplify greatly the code review and attribution.

Thanks.

Andreas Boltres

unread,
Jun 13, 2024, 4:56:05 AMJun 13
to ns-3-users
Yeah will do (I've noted it down to not forget about this), but it'll take me a few weeks since I'm busy currently.

Andreas Boltres

unread,
Jun 17, 2024, 9:20:14 AMJun 17
to ns-3-users
For reference: Merge Request opened at https://gitlab.com/nsnam/ns-3-dev/-/merge_requests/2027 .
Reply all
Reply to author
Forward
0 new messages