Skip to main content

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

Simulation and Analysis of Explicit Congestion Notification (ECN) on TCP Reno Using ns-3

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.

Dumbbell Topology Diagram

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

LLM Used: Claude (claude.ai) by Anthropic

Prompt Used:

"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.

NetAnim Topology View
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
Congestion Window vs Time Graph

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%
Throughput Comparison Chart

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.

Simulation Run Console Output

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...

Installation of NS2 in Ubuntu 22.04 | NS2 Tutorial 2

NS-2.35 installation in Ubuntu 22.04 This post shows how to install ns-2.35 in Ubuntu 22.04 Operating System Since ns-2.35 is too old, it needs the following packages gcc-4.8 g++-4.8 gawk and some more libraries Follow the video for more instructions So, here are the steps to install this software: To download and extract the ns2 software Download the software from the following link http://sourceforge.net/projects/nsnam/files/allinone/ns-allinone-2.35/ns-allinone-2.35.tar.gz/download Extract it to home folder and in my case its /home/pradeepkumar (I recommend to install it under your home folder) $ tar zxvf ns-allinone-2.35.tar.gz or Right click over the file and click extract here and select the home folder. $ sudo apt update $ sudo apt install build-essential autoconf automake libxmu-dev gawk To install gcc-4.8 and g++-4.8 $ sudo gedit /etc/apt/sources.list make an entry in the above file deb http://in.archive.ubuntu.com/ubuntu/ bionic main universe $ sudo apt update Since, it...