A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-ecn-example.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2025 NITK Surathkal
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Authors: N Nagabhushanam <thechosentwins2005@gmail.com>
7 * Namburi Yaswanth <yaswanthnamburi1010@gmail.com>
8 * Vishruth S Kumar <vishruthskumar@gmail.com>
9 * Yashas <yashas80dj@gmail.com>
10 * Mohit P. Tahiliani <tahiliani@nitk.edu.in>
11 */
12
13// This program simulates the following topology:
14//
15// 1000 Mbps 10Mbps 1000 Mbps
16// Sender -------------- R1 -------------- R2 -------------- Receiver
17// 5ms 10ms 5ms
18//
19// This topology is the same as the topology present in tcp-bbr-example.
20// The link between R1 and R2 is a bottleneck link with 10 Mbps. All other
21// links are 1000 Mbps.
22//
23// This program runs by default for 100 seconds and creates a new directory
24// called 'ecn-results' in the ns-3 root directory. The program creates one
25// sub-directory called 'pcap' in 'ecn-results' directory (if pcap generation
26// is enabled) and three .dat files.
27//
28// (1) 'pcap' sub-directory contains six PCAP files:
29// * ecn-0-0.pcap for the interface on Sender
30// * ecn-1-0.pcap for the interface on Receiver
31// * ecn-2-0.pcap for the first interface on R1
32// * ecn-2-1.pcap for the second interface on R1
33// * ecn-3-0.pcap for the first interface on R2
34// * ecn-3-1.pcap for the second interface on R2
35// (2) cwnd.dat file contains congestion window trace for the sender node
36// (3) throughput.dat file contains sender side throughput trace (throughput is in Mbit/s)
37// (4) queueSize.dat file contains queue length trace from the bottleneck link
38//
39// ABE (Alternative Backoff with ECN) modifies the traditional TCP response to
40// ECN (Explicit Congestion Notification) markings. While ECN signals
41// congestion before queue overflows, standard TCP still reduces its congestion
42// window aggressively (by 30% in TcpCubic) upon receiving these marks.
43//
44// ABE improves upon this by backing off less aggressively when ECN marks are
45// received, typically reducing the window (by 15% in TcpCubic instead). This results in
46// better throughput and faster recovery in high-bandwidth, ECN-enabled networks.
47
48#include "ns3/applications-module.h"
49#include "ns3/core-module.h"
50#include "ns3/flow-monitor-module.h"
51#include "ns3/internet-module.h"
52#include "ns3/network-module.h"
53#include "ns3/point-to-point-module.h"
54#include "ns3/traffic-control-module.h"
55
56#include <filesystem>
57
58using namespace ns3;
59using namespace ns3::SystemPath;
60
61std::string dir; //!< The directory where the output files will be stored
62std::ofstream throughput; //!< The stream where the throughput will be stored
63std::ofstream queueSize; //!< The stream where the queue size will be stored
64
65uint32_t prev = 0; //!< Previous transmitted bytes
66Time prevTime; //!< Previous time for throughput calculation
67
68/**
69 * Traces the throughput of the flow monitor.
70 * @param monitor The flow monitor to trace.
71 */
72static void
74{
75 FlowMonitor::FlowStatsContainer stats = monitor->GetFlowStats();
76 if (!stats.empty())
77 {
78 auto itr = stats.begin();
79 Time curTime = Now();
80
81 // Convert (curTime - prevTime) to microseconds so that throughput is in bits per
82 // microsecond (which is equivalent to Mbps)
83 throughput << curTime.GetSeconds() << "s "
84 << 8 * (itr->second.txBytes - prev) / ((curTime - prevTime).ToDouble(Time::US))
85 << " Mbps" << std::endl;
86 prevTime = curTime;
87 prev = itr->second.txBytes;
88 }
90}
91
92/**
93 * Checks the queue size of the queue discipline.
94 * @param qd The queue discipline to check.
95 */
96void
98{
99 uint32_t qsize = qd->GetCurrentSize().GetValue();
101 queueSize << Simulator::Now().GetSeconds() << " " << qsize << std::endl;
102}
103
104/**
105 * Writes the cWnd to the output stream.
106 * @param stream The output stream to write to.
107 * @param oldval The old congestion window value.
108 * @param newval The new congestion window value.
109 */
110static void
112{
113 *stream->GetStream() << Simulator::Now().GetSeconds() << " " << newval / 1448.0 << std::endl;
114}
115
116/**
117 * Traces the congestion window of the socket.
118 * @param nodeId The node ID of the socket.
119 * @param socketId The socket ID of the socket.
120 */
121void
122TraceCwnd(uint32_t nodeId, uint32_t socketId)
123{
124 AsciiTraceHelper ascii;
125 Ptr<OutputStreamWrapper> stream = ascii.CreateFileStream(dir + "/cwnd.dat");
126 Config::ConnectWithoutContext("/NodeList/" + std::to_string(nodeId) +
127 "/$ns3::TcpL4Protocol/SocketList/" +
128 std::to_string(socketId) + "/CongestionWindow",
129 MakeBoundCallback(&CwndTracer, stream));
130}
131
132int
133main(int argc, char* argv[])
134{
135 // Naming the output directory using local system time
136 time_t rawtime;
137 struct tm* timeinfo;
138 char buffer[80];
139 time(&rawtime);
140 timeinfo = localtime(&rawtime);
141 strftime(buffer, sizeof(buffer), "%d-%m-%Y-%I-%M-%S", timeinfo);
142 std::string currentTime(buffer);
143
144 std::string tcpTypeId = "TcpCubic";
145 uint32_t delAckCount = 2;
146 bool bql = true;
147 bool enablePcap = false;
148 bool useEcn = true;
149 bool useAbe = false;
150 Time stopTime = Seconds(100);
151
152 CommandLine cmd(__FILE__);
153 cmd.AddValue("tcpTypeId",
154 "Transport protocol to use: TcpNewReno,TcpCubic, TcpLinuxReno",
155 tcpTypeId);
156 cmd.AddValue("delAckCount", "Delayed ACK count", delAckCount);
157 cmd.AddValue("enablePcap", "Enable/Disable pcap file generation", enablePcap);
158 cmd.AddValue("useEcn", "Enable/Disable ECN", useEcn);
159 cmd.AddValue("useAbe", "Enable/Disable ABE", useAbe);
160 cmd.AddValue("stopTime",
161 "Stop time for applications / simulation time will be stopTime + 1",
162 stopTime);
163 cmd.Parse(argc, argv);
164
165 if (tcpTypeId.find("ns3::") == std::string::npos)
166 {
167 tcpTypeId = "ns3::" + tcpTypeId;
168 }
169
170 Config::SetDefault("ns3::TcpL4Protocol::SocketType", StringValue(tcpTypeId));
171
172 // The maximum send buffer size is set to 4194304 bytes (4MB) and the
173 // maximum receive buffer size is set to 6291456 bytes (6MB) in the Linux
174 // kernel. The same buffer sizes are used as default in this example.
175 Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(4194304));
176 Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(6291456));
177 Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
178 Config::SetDefault("ns3::TcpSocket::DelAckCount", UintegerValue(delAckCount));
179 Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(1448));
180 Config::SetDefault("ns3::DropTailQueue<Packet>::MaxSize", QueueSizeValue(QueueSize("1p")));
181
182 // AQM
183 std::string queueDisc = "ns3::CoDelQueueDisc";
184 std::string bottleNeckLinkBw = "10Mbps";
185 std::string bottleNeckLinkDelay = "10ms";
186
187 // CoDel parameters
188 Config::SetDefault("ns3::CoDelQueueDisc::MaxSize", QueueSizeValue(QueueSize("1000p")));
189 Config::SetDefault("ns3::CoDelQueueDisc::Interval", TimeValue(MilliSeconds(100)));
190 Config::SetDefault("ns3::CoDelQueueDisc::Target", TimeValue(MilliSeconds(5)));
191 Config::SetDefault("ns3::CoDelQueueDisc::UseEcn", BooleanValue(true));
192
193 if (useAbe)
194 {
195 Config::SetDefault("ns3::TcpSocketBase::UseAbe", BooleanValue(true));
196 }
197 if (useEcn)
198 {
199 Config::SetDefault("ns3::TcpSocketBase::UseEcn", EnumValue(TcpSocketState::On));
200 }
201
202 NodeContainer sender;
203 NodeContainer receiver;
204 NodeContainer routers;
205 sender.Create(1);
206 receiver.Create(1);
207 routers.Create(2);
208
209 // Create the point-to-point link helpers
210 PointToPointHelper bottleneckLink;
211 bottleneckLink.SetDeviceAttribute("DataRate", StringValue("10Mbps"));
212 bottleneckLink.SetChannelAttribute("Delay", StringValue("10ms"));
213
214 PointToPointHelper edgeLink;
215 edgeLink.SetDeviceAttribute("DataRate", StringValue("1000Mbps"));
216 edgeLink.SetChannelAttribute("Delay", StringValue("5ms"));
217
218 // Create NetDevice containers
219 NetDeviceContainer senderEdge = edgeLink.Install(sender.Get(0), routers.Get(0));
220 NetDeviceContainer r1r2 = bottleneckLink.Install(routers.Get(0), routers.Get(1));
221 NetDeviceContainer receiverEdge = edgeLink.Install(routers.Get(1), receiver.Get(0));
222
223 // Install Stack
225 internet.Install(sender);
226 internet.Install(receiver);
227 internet.Install(routers);
228
229 // Configure the root queue discipline
231 tch.SetRootQueueDisc(queueDisc);
232
233 if (bql)
234 {
235 tch.SetQueueLimits("ns3::DynamicQueueLimits", "HoldTime", StringValue("1000ms"));
236 }
237 tch.Install(senderEdge);
238 tch.Install(receiverEdge);
239
240 // Assign IP addresses
242 ipv4.SetBase("10.0.0.0", "255.255.255.0");
243
244 Ipv4InterfaceContainer i1i2 = ipv4.Assign(r1r2);
245
246 ipv4.NewNetwork();
247 Ipv4InterfaceContainer is1 = ipv4.Assign(senderEdge);
248
249 ipv4.NewNetwork();
250 Ipv4InterfaceContainer ir1 = ipv4.Assign(receiverEdge);
251
252 // Populate routing tables
254
255 // Select sender side port
256 uint16_t port = 50001;
257
258 // Install application on the sender
259 BulkSendHelper source("ns3::TcpSocketFactory", InetSocketAddress(ir1.GetAddress(1), port));
260 source.SetAttribute("MaxBytes", UintegerValue(0));
261 ApplicationContainer sourceApps = source.Install(sender.Get(0));
262 sourceApps.Start(Seconds(0.1));
263 // Hook trace source after application starts
265 sourceApps.Stop(stopTime);
266
267 // Install application on the receiver
268 PacketSinkHelper sink("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), port));
269 ApplicationContainer sinkApps = sink.Install(receiver.Get(0));
270 sinkApps.Start(Seconds(0));
271 sinkApps.Stop(stopTime);
272 // Create a new directory to store the output of the program
273 dir = "ecn-results/" + currentTime + "/";
275
276 // The plotting scripts are provided in the following repository, if needed:
277 // https://github.com/mohittahiliani/BBR-Validation/
278 //
279 // Download 'PlotScripts' directory (which is inside ns-3 scripts directory)
280 // from the link given above and place it in the ns-3 root directory.
281 // Uncomment the following three lines to copy plot scripts for
282 // Congestion Window, sender side throughput and queue occupancy on the
283 // bottleneck link into the output directory.
284 //
285 // std::filesystem::copy("PlotScripts/gnuplotScriptCwnd", dir);
286 // std::filesystem::copy("PlotScripts/gnuplotScriptThroughput", dir);
287 // std::filesystem::copy("PlotScripts/gnuplotScriptQueueSize", dir);
288
289 // Trace the queue occupancy on the second interface of R1
290 tch.Uninstall(routers.Get(0)->GetDevice(1));
292 qd = tch.Install(routers.Get(0)->GetDevice(1));
294
295 // Generate PCAP traces if it is enabled
296 if (enablePcap)
297 {
298 MakeDirectories(dir + "pcap/");
299 bottleneckLink.EnablePcapAll(dir + "/pcap/ecn", true);
300 }
301
302 // Open files for writing throughput traces and queue size
303 throughput.open(dir + "/throughput.dat", std::ios::out);
304 queueSize.open(dir + "/queueSize.dat", std::ios::out);
305
306 NS_ASSERT_MSG(throughput.is_open(), "Throughput file was not opened correctly");
307 NS_ASSERT_MSG(queueSize.is_open(), "Queue size file was not opened correctly");
308
309 // Check for dropped packets using Flow Monitor
310 FlowMonitorHelper flowmon;
311 Ptr<FlowMonitor> monitor = flowmon.InstallAll();
313
317
318 throughput.close();
319 queueSize.close();
320
321 return 0;
322}
Ipv4InterfaceContainer i1i2
IPv4 interface container i1 + i2.
holds a vector of ns3::Application pointers.
void Start(Time start) const
Start all of the Applications in this container at the start time given as a parameter.
void Stop(Time stop) const
Arrange for all of the Applications in this container to Stop() at the Time given as a parameter.
Manage ASCII trace files for device models.
Ptr< OutputStreamWrapper > CreateFileStream(std::string filename, std::ios::openmode filemode=std::ios::out)
Create and initialize an output stream object we'll use to write the traced bits.
AttributeValue implementation for Boolean.
Definition boolean.h:26
A helper to make it easier to instantiate an ns3::BulkSendApplication on a set of nodes.
Parse command-line arguments.
Hold variables of type enum.
Definition enum.h:52
Helper to enable IP flow monitoring on a set of Nodes.
Ptr< FlowMonitor > InstallAll()
Enable flow monitoring on all nodes.
std::map< FlowId, FlowStats > FlowStatsContainer
Container: FlowId, FlowStats.
an Inet address class
aggregate IP/TCP/UDP functionality to existing Nodes.
A helper class to make life easier while doing simple IPv4 address assignment in scripts.
static Ipv4Address GetAny()
static void PopulateRoutingTables()
Build a routing database and initialize the routing tables of the nodes in the simulation.
holds a vector of std::pair of Ptr<Ipv4> and interface index.
Ipv4Address GetAddress(uint32_t i, uint32_t j=0) const
holds a vector of ns3::NetDevice pointers
keep track of a set of node pointers.
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
Ptr< NetDevice > GetDevice(uint32_t index) const
Retrieve the index-th NetDevice associated to this node.
Definition node.cc:138
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
void EnablePcapAll(std::string prefix, bool promiscuous=false)
Enable pcap output on each device (which is of the appropriate type) in the set of all nodes created ...
Build a set of PointToPointNetDevice objects.
void SetDeviceAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each NetDevice created by the helper.
void SetChannelAttribute(std::string name, const AttributeValue &value)
Set an attribute value to be propagated to each Channel created by the helper.
NetDeviceContainer Install(NodeContainer c)
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:67
Holds a vector of ns3::QueueDisc pointers.
Ptr< QueueDisc > Get(std::size_t i) const
Get the Ptr<QueueDisc> stored in this container at a given index.
Class for representing queue sizes.
Definition queue-size.h:85
AttributeValue implementation for QueueSize.
Definition queue-size.h:210
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:561
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:131
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
static void Run()
Run the simulation.
Definition simulator.cc:167
static EventId ScheduleNow(FUNC f, Ts &&... args)
Schedule an event to expire Now.
Definition simulator.h:595
static void Stop()
Tell the Simulator the calling event should be the last one executed.
Definition simulator.cc:175
Hold variables of type string.
Definition string.h:45
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:394
Time TimeStep(uint64_t ts)
Scheduler interface.
Definition nstime.h:1451
@ US
microsecond
Definition nstime.h:109
AttributeValue implementation for Time.
Definition nstime.h:1456
Build a set of QueueDisc objects.
QueueDiscContainer Install(NetDeviceContainer c)
uint16_t SetRootQueueDisc(const std::string &type, Args &&... args)
Helper function used to set a root queue disc of the given type and with the given attributes.
void SetQueueLimits(std::string type, Args &&... args)
Helper function used to add a queue limits object to the transmission queues of the devices.
void Uninstall(NetDeviceContainer c)
Hold an unsigned integer type.
Definition uinteger.h:34
uint16_t port
Definition dsdv-manet.cc:33
Time stopTime
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:886
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:946
auto MakeBoundCallback(R(*fnPtr)(Args...), BArgs &&... bargs)
Make Callbacks with varying number of bound arguments.
Definition callback.h:745
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition simulator.cc:294
void MakeDirectories(std::string path)
Create all the directories leading to path.
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1369
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1381
Namespace for various file and directory path functions.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::ofstream queueSize
Time prevTime
static void CwndTracer(Ptr< OutputStreamWrapper > stream, uint32_t oldval, uint32_t newval)
void TraceCwnd(uint32_t nodeId, uint32_t socketId)
std::ofstream throughput
uint32_t prev
static void TraceThroughput(Ptr< FlowMonitor > monitor)
std::string dir
void CheckQueueSize(Ptr< QueueDisc > qd)
static void CwndTracer(Ptr< OutputStreamWrapper > stream, uint32_t oldval, uint32_t newval)
Writes the cWnd to the output stream.
void TraceCwnd(uint32_t nodeId, uint32_t socketId)
Traces the congestion window of the socket.
static void TraceThroughput(Ptr< FlowMonitor > monitor)
Traces the throughput of the flow monitor.
void CheckQueueSize(Ptr< QueueDisc > qd)
Checks the queue size of the queue discipline.
Ptr< PacketSink > sink
Pointer to the packet sink application.
Definition wifi-tcp.cc:44