Skip to main content

Transport Layer & TCP Variants: Analyze TCP goodput vs. offered load in a 10 Gbps link with controlled packet loss | NS3 Project 22

Transport Layer & TCP Variants: Analyze TCP goodput vs. offered load in a 10 Gbps link with controlled packet loss | NS3 Project 22

Aim:

To analyze TCP goodput vs. offered load over a 10 Gbps link under controlled packet loss conditions, using NS-3 simulation. The goal is to observe how increasing offered load impacts actual useful throughput (goodput) for different TCP variants. Also visualize packet flow using NetAnim.

Network Topology:

          Two nodes:

·      n0 → Sender

·      n1 → Receiver

Link:

·      Bandwidth: 10 Gbps

·      Delay: 2 ms

·      TCP Bulk transfer with controlled packet loss

 

n0  ----------------------  n1

     10 Gbps, 2 ms delay

 

Code:

 
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/internet-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/applications-module.h"
#include "ns3/error-model.h"
#include "ns3/flow-monitor-module.h"
#include "ns3/netanim-module.h"
#include <fstream>
#include <string>
 
using namespace ns3;
 
NS_LOG_COMPONENT_DEFINE ("TcpGoodputExample");
 
double RunSimulation(double loadGbps)
{
    NodeContainer nodes;
    nodes.Create(2);
 
    Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(8960));
    Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(1 << 26)); // 64MB
    Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(1 << 26)); // 64MB
    Config::SetDefault("ns3::TcpSocketBase::WindowScaling", BooleanValue(true));
    Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue("ns3::TcpNewReno"));
    //Config::SetDefault("ns3::TcpL4Protocol::SocketType", TypeIdValue(TcpCubic::GetTypeId())); or Bbr
 
    PointToPointHelper p2p;
    p2p.SetDeviceAttribute("DataRate", StringValue("10Gbps"));
    p2p.SetChannelAttribute("Delay", StringValue("2ms"));
    p2p.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("30000p"));
    NetDeviceContainer devices = p2p.Install(nodes);
    // --- Controlled Packet Loss ---
    Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
    em->SetAttribute("ErrorRate", DoubleValue(10e-9));
    devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em));
 
    InternetStackHelper stack;
    stack.Install(nodes);
 
    Ipv4AddressHelper address;
    address.SetBase("10.1.1.0", "255.255.255.0");
    Ipv4InterfaceContainer interfaces = address.Assign(devices);
 
    uint16_t port = 8080;
 
    // --- Sink Application (Receiver) ---
    PacketSinkHelper sinkHelper("ns3::TcpSocketFactory",
                                InetSocketAddress(Ipv4Address::GetAny(), port));
    ApplicationContainer sinkApp = sinkHelper.Install(nodes.Get(1));
    sinkApp.Start(Seconds(0.0));
    sinkApp.Stop(Seconds(30.0));
 
    // --- Source Application (Sender) ---
    OnOffHelper sourceHelper("ns3::TcpSocketFactory",
                             InetSocketAddress(interfaces.GetAddress(1), port));
   
    std::ostringstream rate;
    rate << loadGbps << "Gbps";
    sourceHelper.SetAttribute("DataRate", StringValue(rate.str()));
    sourceHelper.SetAttribute("PacketSize", UintegerValue(8960));
    sourceHelper.SetAttribute("OnTime", StringValue("ns3::ConstantRandomVariable[Constant=1]"));
    sourceHelper.SetAttribute("OffTime", StringValue("ns3::ConstantRandomVariable[Constant=0]"));
 
    ApplicationContainer sourceApp = sourceHelper.Install(nodes.Get(0));
    sourceApp.Start(Seconds(1.0)); // Starts at 1s
    sourceApp.Stop(Seconds(30.0)); // Stops at 30s
 
    // --- Simulation Execution ---
    Simulator::Stop(Seconds(30.1));
    Simulator::Run();
 
    // --- Goodput Calculation ---
    // We access the Sink application directly to see how many bytes were delivered
    Ptr<PacketSink> sink = DynamicCast<PacketSink>(sinkApp.Get(0));
    uint64_t totalBytesReceived = sink->GetTotalRx();
    // Duration is 29 seconds (from 1.0 to 30.0)
    double duration = 29.0;
    double goodputGbps = (totalBytesReceived * 8.0) / (duration * 1e9);
 
    Simulator::Destroy();
    return goodputGbps;
}
int main(int argc, char *argv[])
{
    std::ofstream outFile;
    outFile.open("load_vs_goodput.dat", std::ios::trunc);
    outFile << "# Load(Gbps) Goodput(Gbps)" << std::endl;
    double loads[] = {1, 2, 4, 6, 8, 10};
    for (double load : loads)
    {
        std::cout << "Running simulation for Load: " << load << " Gbps..." << std::flush;
        double goodput = RunSimulation(load);
        outFile << load << " " << goodput << std::endl;
        std::cout << " Done. Goodput: " << goodput << " Gbps" << std::endl;
    }
    outFile.close();
    std::cout << "\nResults saved to load_vs_goodput.dat\n";
    return 0;
}

 

Output:

 

tcpnewreno:



# Load(Gbps) Goodput(Gbps)

1 0.953423

2 1.35257

4 1.31719

6 1.24898

8 1.14828

10 1.26265

 

tcpcubic:



# Load(Gbps) Goodput(Gbps)

1 0.999862

2 1.9007

4 1.81446

6 1.67706

8 1.74666

10 1.80455

 

tcpbbr:

 



# Load(Gbps) Goodput(Gbps)

1 0.999862

2 1.99972

4 3.99942

6 4.00503

8 4.00497

10 4.00574

 

Graph:

 



 

da3.plt

 

set terminal png

set output 'graph.png'

set title "Load vs Goodput"

set xlabel "Offered Load (Gbps)"

set ylabel "Goodput (Gbps)"

 

plot "load_vs_goodput.dat" with linespoints title "NewReno", \

     "load_vs_goodput2.dat" with linespoints title "Cubic", \

     "load_vs_goodput3.dat" with linespoints title "BBR"

 

Inference:

 

1. The "Linear Scaling" Region

            At the 1 Gbps mark, the graph shows all three protocols overlapping perfectly.

·      Analysis: When the offered load is low, the time between packet loss events is long enough for even the most inefficient protocol (NewReno) to recover and deliver 100% of the traffic. At this stage, performance is limited by the Application Source, not the network protocol.

2. The Efficiency Divergence(The “Ceiling” Effect)

As the load surpasses 2 Gbps, we see a sharp divergence. This plateau represents the maximum "steady-state" throughput each protocol can maintain under a 10−9 BER.

·      NewReno (The Floor): Stabilizes at ~1.3 Gbps. The linear "Additive Increase" is simply too slow to reclaim the bandwidth before the next random bit error occurs. It is trapped in a perpetual state of recovery.

·      Cubic (The Middle Ground): Stabilises at ~1.8 - 1.9 Gbps. Using a cubic window growth function yields higher speeds than Reno. However, it still falls victim to the "loss-equals-congestion" fallacy, causing it to throttle itself unnecessarily.

·      BBR (The Leader): Stabilises at ~4.0 Gbps. Because it models the pipe (Bandwidth/Delay) rather than reacting to individual losses, it ignores the "noise" of bit errors. Its ceiling here is likely defined by the Socket Buffer/BDP limit or internal ns-3 pacing rather than the error rate.

3. Saturation and "Congestive Collapse"

Looking at the 8 Gbps to 10 Gbps range on the graph, there is a slight downward trend in Goodput for NewReno and Cubic.

·      Analysis: This is a "Saturation Degradation." As you increase the offered load, you pump more packets into the wire per second. Statistically, this increases the number of bit errors encountered per second.

·      Inference: For loss-based protocols (Reno/Cubic), more frequent errors mean more frequent window-halving events. This leads to a state where the protocol spends more time in "recovery" than in "data transfer," causing the actual goodput to drop even as you try to send more data.

Key Takeaway:

The simulation demonstrates that bandwidth is not the bottleneck—protocol logic is. 1. NewReno is obsolete for 10 Gbps links with any degree of noise. 2. Cubic offers a 45% improvement over Reno but still leaves ~80% of the link capacity unused. 3. BBR is the only variant capable of maintaining high-speed throughput in "noisy" environments, outperforming NewReno by ~300%.

 

Comments

Popular posts from this blog

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide]

How to Create Ubuntu 24.04 Bootable USB Using Rufus [Step-by-Step Guide] Are you planning to install or try Ubuntu 24.04 LTS ? The easiest and most reliable method is to create a bootable USB drive using Rufus on a Windows system. This detailed guide will help you create a Ubuntu 24.04 USB bootloader using Rufus with easy-to-follow steps and screenshots (optional). Here is the complete video of the bootloader creation and OS installation in Windows 11. 🧰 Requirements A USB flash drive (minimum 8GB recommended) A Windows PC Ubuntu 24.04 LTS ISO file Rufus USB creation tool 🧾 Steps to Create a Ubuntu 24.04 Bootable USB Using Rufus ✅ Step 1: Download Ubuntu 24.04 ISO Visit the official Ubuntu website and download the Ubuntu 24.04 LTS ISO file . ✅ Step 2: Download and Run Rufus Head to Rufus official site and download the latest version. Open the executable file (no installation required). ✅ Step 3: Insert USB Drive Plug in your USB drive. Rufus ...

Installing ns3 in Ubuntu 22.04 | Complete Instructions

In this post, we are going to see how to install ns-3.36.1 in Ubuntu 22.04. You can follow the video for complete details Tools used in this simulation: NS3 version ns-3.36.1  OS Used: Ubuntu 22.04 LTS Installation of NS3 (ns-3.36.1) There are some changes in the ns3 installation procedure and the dependencies. So open a terminal and issue the following commands Step 1:  Prerequisites $ sudo apt update In the following packages, all the required dependencies are taken care and you can install all these packages for the complete use of ns3. $ sudo apt install g++ python3 python3-dev pkg-config sqlite3 cmake python3-setuptools git qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools gir1.2-goocanvas-2.0 python3-gi python3-gi-cairo python3-pygraphviz gir1.2-gtk-3.0 ipython3 openmpi-bin openmpi-common openmpi-doc libopenmpi-dev autoconf cvs bzr unrar gsl-bin libgsl-dev libgslcblas0 wireshark tcpdump sqlite sqlite3 libsqlite3-dev  libxml2 libxml2-dev libc6-dev libc6-dev-i386 libc...

NS2 (NS-2.35) Installation in Ubuntu 11.10

This post will help you in installing Network Simulator 2 version NS2.35 in Ubuntu 11.10 Instructions Install Ubuntu Download NS-2.35 ( http://sourceforge.net/projects/nsnam/files/allinone/ns-allinone-2.35/ns-allinone-2.35.tar.gz/download ) Unzip or untar it to any folder (recommended is /home/ loginname) using the following commands one by one sudo apt-get update sudo apt-get install build-essential autoconf automake libxmu-dev tar zxvf ns-allinone-2.35.tar.gz cd ns-allinone-2.35 ./install Once installed the PATH information will be provided to you. Copy the PATH and LD_LIBRARY_PATH Variable to .bashrc (see a dot in the beginning) Input the path information in .bashrc file like this export PATH=$PATH:<Place your paths here> export LD_LIBRARY_PATH=$LD_LIBRARY_PATH: <place the LD_LIBRARY_PATHS> here. Once done, save the file and close execute the command source .bashrc try ns or nam to see whether your installation succeeded.