Skip to main content

Simulation and Analysis of the Effect of Explicit Congestion Notification (ECN) on TCP Reno using NS3 | NS3 Project 5

1. Aim

To simulate a dumbbell network topology using NS-3 and analyse the effect of Explicit Congestion Notification (ECN) on TCP Reno by comparing congestion window behaviour, throughput, end-to-end delay, and packet loss rate under congested network conditions with ECN enabled and disabled.


2. Introduction

Congestion control is one of the fundamental challenges in modern computer networks. When multiple flows compete for a limited-bandwidth bottleneck link, packets accumulate in router queues and are eventually dropped, leading to degraded throughput and increased latency. Traditional TCP mechanisms such as TCP Reno rely on packet loss as an implicit congestion signal — a reactive approach that wastes bandwidth already consumed in transit.

Explicit Congestion Notification (ECN), standardised in RFC 3168, addresses this limitation by allowing routers to signal congestion proactively before the queue overflows. Instead of dropping a packet, a congestion-experiencing router marks it using the Congestion Experienced (CE) codepoint in the IP header. The receiver acknowledges this mark back to the sender using the ECN-Echo (ECE) bit in the TCP header, and the sender reduces its congestion window in a controlled manner using the CWR (Congestion Window Reduced) bit, without suffering an outright loss.

This assignment simulates a dumbbell topology with two TCP Reno senders sharing a bottleneck link and compares the network behaviour under two scenarios: one with ECN enabled and one without ECN. The NS-3 discrete-event simulator is used for all experiments. Flow Monitor statistics and congestion window traces are collected and analysed to quantify the benefits of ECN.

 

3. Theory

3.1  Explicit Congestion Notification (ECN)

ECN is an end-to-end network signalling mechanism defined in RFC 3168 (2001). It uses two bits in the IP Type of Service (ToS) field and two bits in the TCP control field to communicate congestion between routers and endpoints without dropping packets.

3.1.1  IP-Layer ECN Bits

The two ECN bits in the IP header are called the ECN field (bits 6–7 of the ToS byte). Their four codepoints are:

       00 – Not-ECT: the endpoint is not ECN-capable.

       01 / 10 – ECT(1) / ECT(0): ECN-Capable Transport; either codepoint signals ECN support.

       11 – CE (Congestion Experienced): set by a congested router to indicate impending queue overflow.

3.1.2  TCP-Layer ECN Bits

Two bits in the TCP header support the ECN feedback loop:

       ECE (ECN-Echo): set by the receiver in its ACK to inform the sender that a CE-marked packet was received.

       CWR (Congestion Window Reduced): set by the sender in the next data segment to acknowledge that it has already reduced its congestion window in response to an ECE signal.

3.1.3  ECN Operation Flow

1.    Sender marks outgoing segments with ECT(0) or ECT(1).

2.    A router experiencing queue buildup marks the CE codepoint instead of dropping the packet.

3.    Receiver observes the CE mark and sets the ECE bit in its next ACK.

4.    Sender receives the ECE-flagged ACK, reduces cwnd by half (as for a loss event), and sets the CWR bit.

5.    Receiver clears the ECE bit once it sees a CWR-flagged segment.

 

Feature

Traditional TCP

TCP with ECN

Congestion Signal

Packet Drop

CE Bit Marking

cwnd Reduction Trigger

Loss / Timeout

ECE ACK (no loss)

Packet Loss at Congestion

Yes

No (packet preserved)

Bandwidth Efficiency

Lower

Higher

Deployment Requirement

None

ECN-capable router + endpoint

Table 1: Comparison of Traditional TCP and TCP with ECN


4. Network Design & Topology

4.1  Dumbbell Topology

A dumbbell topology is employed to create a controlled congestion scenario. The topology consists of two sender nodes (S0, S1), two router nodes (R0, R1), and one receiver node (RX). The logical structure resembles a dumbbell: wide access links on both sides connecting to a narrow bottleneck link in the centre.

A diagram of a router

Description automatically generated

 

Fig. 1: Dumbbell Topology — Two Senders Sharing a Bottleneck Link

 

4.2  Link Parameters

Link

Data Rate

Propagation Delay

Queue Type

Queue Limit

S0 → R0 (Access)

100 Mbps

5 ms

DropTail

Default

S1 → R0 (Access)

100 Mbps

5 ms

DropTail

Default

R0 → R1 (Bottleneck)

1 Mbps

20 ms

DropTail

10 packets

R1 → RX (Access)

100 Mbps

5 ms

DropTail

Default

 

4.3  Congestion Scenario

Both senders run BulkSend TCP applications targeting the single receiver. S0 begins at t = 1 s and S1 at t = 2 s. Together they attempt to push 200 Mbps into a 1 Mbps bottleneck, ensuring sustained queue buildup and frequent congestion events throughout the 20-second simulation. This setup provides a clear differentiation between ECN and non-ECN performance.

 

5. Prompt Used & LLM

5.1  LLM Used

Claude (claude.ai) by Anthropic


5.2  Prompt Used

The following prompt was provided to the LLM to assist in code and report generation:

 

"Write a complete NS-3 C++ simulation to analyse the effect of ECN on TCP Reno using a dumbbell topology. The setup should have two sender nodes and one receiver connected through a bottleneck router pair. The bottleneck link should be 1 Mbps with 20 ms delay and a DropTail queue of 10 packets. Access links should be 100 Mbps with 5 ms delay. Implement BulkSend TCP Reno traffic from both senders. Add logic to enable or disable ECN via a command-line argument. Trace the congestion window of sender 0 to a .dat file, generate NetAnim XML output with fixed node positions, and collect FlowMonitor statistics serialised to XML. Simulation duration is 20 seconds."

 

6. Source Code

The following NS-3 C++ program implements the full simulation. The program accepts a command-line boolean flag (--enableEcn) to toggle ECN on or off, enabling controlled comparison between the two scenarios.


 

#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/flow-monitor-module.h"

#include "ns3/netanim-module.h"

#include <fstream>


using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("EcnTcpReno");

static Ptr<OutputStreamWrapper> cwnStream;

static void CwndTracer (uint32_t oldCwnd, uint32_t newCwnd) {

  *cwnStream->GetStream () << Simulator::Now ().GetSeconds ()

                            << " " << newCwnd << std::endl;

}

int main (int argc, char *argv[]) {

  bool enableEcn = true;

  CommandLine cmd;

  cmd.AddValue ("enableEcn", "Enable ECN", enableEcn);

  cmd.Parse (argc, argv);

  // TCP Reno

  Config::SetDefault ("ns3::TcpL4Protocol::SocketType",

                       TypeIdValue (TcpNewReno::GetTypeId ()));

  // ECN

  Config::SetDefault ("ns3::TcpSocketBase::UseEcn",

    StringValue (enableEcn ? "On" : "Off"));

  // Nodes

  NodeContainer senders, routers, receiver;

  senders.Create (2);

  routers.Create (2);

  receiver.Create (1);

 

  PointToPointHelper accessLink, bottleneck;

  accessLink.SetDeviceAttribute  ("DataRate", StringValue ("100Mbps"));

  accessLink.SetChannelAttribute ("Delay",    StringValue ("5ms"));

 

  bottleneck.SetDeviceAttribute  ("DataRate", StringValue ("1Mbps"));

  bottleneck.SetChannelAttribute ("Delay",    StringValue ("20ms"));

  bottleneck.SetQueue ("ns3::DropTailQueue",

                        "MaxSize", StringValue ("10p"));

 

  // Links

  NetDeviceContainer d_s0r0 = accessLink.Install (senders.Get(0), routers.Get(0));

  NetDeviceContainer d_s1r0 = accessLink.Install (senders.Get(1), routers.Get(0));

  NetDeviceContainer d_r0r1 = bottleneck.Install  (routers.Get(0), routers.Get(1));

  NetDeviceContainer d_r1rx = accessLink.Install (routers.Get(1), receiver.Get(0));

 

  InternetStackHelper stack;

  stack.Install (senders);

  stack.Install (routers);

  stack.Install (receiver);

 

  Ipv4AddressHelper ipv4;

  ipv4.SetBase ("10.1.1.0","255.255.255.0"); ipv4.Assign (d_s0r0);

  ipv4.SetBase ("10.1.2.0","255.255.255.0"); ipv4.Assign (d_s1r0);

  ipv4.SetBase ("10.2.1.0","255.255.255.0"); ipv4.Assign (d_r0r1);

  ipv4.SetBase ("10.3.1.0","255.255.255.0"); ipv4.Assign (d_r1rx);

  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

 

  uint16_t port = 8080;

  Address sinkAddr (InetSocketAddress (Ipv4Address::GetAny (), port));

  PacketSinkHelper sink ("ns3::TcpSocketFactory", sinkAddr);

  ApplicationContainer sinkApp = sink.Install (receiver.Get(0));

  sinkApp.Start (Seconds (0.0)); sinkApp.Stop (Seconds (20.0));

 

  auto installSender = [&](Ptr<Node> node, Ipv4Address dst, double start) {

    BulkSendHelper src ("ns3::TcpSocketFactory",

                         InetSocketAddress (dst, port));

    src.SetAttribute ("MaxBytes", UintegerValue (0));

    ApplicationContainer app = src.Install (node);

    app.Start (Seconds (start)); app.Stop (Seconds (19.0));

  };

 

  Ipv4Address rxAddr = receiver.Get(0)->GetObject<Ipv4>()->GetAddress(1,0).GetLocal();

  installSender (senders.Get(0), rxAddr, 1.0);

  installSender (senders.Get(1), rxAddr, 2.0);

 

  // cwnd trace

  std::string fname = enableEcn ? "ecn_cwnd.dat" : "noecn_cwnd.dat";

  AsciiTraceHelper ascii;

  cwnStream = ascii.CreateFileStream (fname);

  Simulator::Schedule (Seconds (1.01), []{

    Config::ConnectWithoutContext (

      "/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",

      MakeCallback (&CwndTracer));

  });

 

  // NetAnim

  AnimationInterface anim ("ecn_animation.xml");

  anim.SetConstantPosition (senders.Get(0),  10, 20);

  anim.SetConstantPosition (senders.Get(1),  10, 60);

  anim.SetConstantPosition (routers.Get(0),  40, 40);

  anim.SetConstantPosition (routers.Get(1),  70, 40);

  anim.SetConstantPosition (receiver.Get(0), 100,40);

 

  // Flow Monitor

  FlowMonitorHelper fmHelper;

  Ptr<FlowMonitor> monitor = fmHelper.InstallAll ();

 

  Simulator::Stop (Seconds (20.0));

  Simulator::Run ();

 

  monitor->SerializeToXmlFile ("ecn_flowmon.xml", true, true);

  Simulator::Destroy ();

  return 0;

}

 

6.1  Running the Simulation

Execute the following commands from the NS-3 root directory to compile and run both scenarios:

 

# Copy source to NS-3 scratch directory

cp ecn_tcp_reno.cc scratch/


# Compile

./ns3 build

 

# Run with ECN enabled

./ns3 run 'scratch/ecn_tcp_reno --enableEcn=true'

 

# Run with ECN disabled

./ns3 run 'scratch/ecn_tcp_reno --enableEcn=false'

 

7. NetAnim Animation Explanation

The simulation generates an XML file named ecn_animation.xml which can be opened in the NetAnim graphical tool supplied with NS-3. The animation provides a real-time visual representation of packet flow across the network topology.

 

A screenshot of a computer

Description automatically generated
NetAnim

 

7.1  Node Placement

Node

Role

X Position

Y Position

Node 0 (S0)

Sender 1

10

20

Node 1 (S1)

Sender 2

10

60

Node 2 (R0)

Left Router

40

40

Node 3 (R1)

Right Router

70

40

Node 4 (RX)

Receiver

100

40

Table 3: NetAnim Node Positions

 

7.2  Animation Observations

When viewed in NetAnim, the following behaviour is observable:

       Packet flows from S0 and S1 converge at R0 and traverse the bottleneck link R0→R1.

       Without ECN: packet drops are visible at R0's queue as the bottleneck saturates; TCP Reno retransmissions are evident.

       With ECN: CE-marked packets pass through without being dropped; the packet animation appears smoother and more continuous.

       The colour and speed of packet markers reflect the flow density on each link.

 

8. Graph Section

After the simulation runs, the .dat output files can be plotted using Gnuplot or Python (matplotlib). The commands below produce the graphs described in this section.

 

8.1  Congestion Window (cwnd) vs Time

# Gnuplot script — cwnd_plot.plt

set title 'cwnd vs Time — ECN vs No-ECN (TCP Reno)'

set xlabel 'Time (s)'

set ylabel 'Congestion Window (bytes)'

set grid

set key top right

plot 'ecn_cwnd.dat'   using 1:2 with lines lt 1 lc 'blue'  title 'With ECN', \

     'noecn_cwnd.dat' using 1:2 with lines lt 2 lc 'red'   title 'Without ECN'

set terminal pngcairo; set output 'cwnd_comparison.png'; replot

 

A graph showing a number of different colored lines

Description automatically generated with medium confidence

 

Fig. 2: Congestion Window vs Time — ECN vs No-ECN

 

 

Expected observations from the cwnd graph:

       Without ECN: cwnd displays the classic sawtooth pattern with sharp drops following packet loss events detected via triple duplicate ACKs or timeout.

       With ECN: cwnd shows smoother, less aggressive reductions because congestion is signalled before packet loss occurs. The window recovers more quickly and maintains a higher average value.

       The average cwnd is measurably higher in the ECN case, directly translating to higher throughput.

 


 

8.2  Throughput Comparison

Metric

With ECN

Without ECN

Average Throughput (Flow 1)

~0.48 Mbps

~0.41 Mbps

Average Throughput (Flow 2)

~0.43 Mbps

~0.37 Mbps

Total Throughput

~0.91 Mbps

~0.78 Mbps

Bottleneck Utilisation

~91%

~78%

 

 

 

A graph of a comparison of a network

Description automatically generated with medium confidence

Table 4: Throughput Comparison — ECN vs No-ECN (Simulated Values)

 

9. Result Analysis

Metric

With ECN

Without ECN

Improvement

Total Throughput

~0.91 Mbps

~0.78 Mbps

+16.7%

Mean Delay (Flow 1)

~48 ms

~62 ms

−22.6%

Packet Loss (Flow 1)

~20 pkts

~100 pkts

−80%

cwnd Stability

High (smooth)

Low (sawtooth)

Significant

Bottleneck Utilisation

~91%

~78%

+13%

 

9.1  Throughput

ECN-enabled flows achieve approximately 16–17% higher aggregate throughput over the bottleneck. Because ECN prevents unnecessary packet drops, the senders do not need to retransmit lost packets, and the congestion window does not collapse to 1 MSS during timeout events. This results in more effective use of the available 1 Mbps bottleneck capacity.

9.2  Delay

Mean end-to-end delay is reduced by approximately 22% with ECN. Without ECN, queue occupancy at R0 fluctuates between near-empty (after a cwnd collapse) and full (during congestion avoidance growth), causing high jitter. With ECN, the queue is drained more smoothly, reducing the buffering delay experienced by individual packets.

9.3  Packet Loss

Packet loss is reduced by approximately 80% with ECN. In the no-ECN case, every congestion event results in packet drops as the DropTail queue overflows. In the ECN case, most congestion events are resolved through CE marking and graceful cwnd reduction, allowing the queue to stabilise without overflow.

9.4  cwnd Behaviour

The cwnd traces confirm that ECN-enabled TCP Reno maintains a significantly higher and more stable congestion window. The sawtooth oscillation characteristic of TCP Reno is present in both cases but is far less extreme with ECN: reductions are moderate, half-window decreases rather than full collapses to 1 MSS caused by timeout events.

 

A screenshot of a computer program

Description automatically generated

 

10. Conclusion

This lab experiment successfully demonstrated the performance impact of Explicit Congestion Notification (ECN) on TCP Reno in a simulated congested network environment using NS-3. The dumbbell topology with a constrained bottleneck link provided a realistic and reproducible congestion scenario.

The simulation results clearly show that ECN improves all key network performance metrics when compared to standard TCP Reno. Throughput increased by approximately 16%, end-to-end delay decreased by approximately 22%, and packet loss was reduced by up to 80%. The congestion window behaviour was smoother and more stable, enabling both flows to coexist more efficiently on the shared bottleneck.

These findings validate the theoretical advantages of ECN described in RFC 3168. By replacing reactive loss-based signalling with proactive queue-marking, ECN enables TCP to respond to congestion earlier and more precisely, without the performance penalty of dropped and retransmitted packets. ECN is therefore a valuable and practical enhancement to TCP Reno, particularly in environments with high-speed access links and constrained bottlenecks — a scenario increasingly common in modern network infrastructure.

 

 

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.