A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
ns3tcp-cubic-test-suite.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010 University of Washington (trace writing)
3 * Copyright (c) 2018-20 NITK Surathkal (topology setup)
4 * Copyright (c) 2024 Tom Henderson (test definition)
5 *
6 * SPDX-License-Identifier: GPL-2.0-only
7 */
8
9// Test suite based on tcp-bbr-example.cc modified topology setup:
10//
11// 1 Gbps 50/100Mbps 1 Gbps
12// Sender -------------- R1 -------------- R2 -------------- Receiver
13// 5us baseRtt/2 5us
14//
15// This is a test suite written initially to test the addition of
16// TCP friendliness but can also be extended for other Cubic tests. By
17// changing some compile-time constants below, it can be used to also produce
18// gnuplots and pcaps that demonstrate the behavior. See the WRITE_PCAP and
19// WRITE_GNUPLOT booleans below to enable this output.
20//
21// There are four cases configured presently.
22// 1. A Cubic TCP on a 5 ms RTT bottleneck, with no fast convergence nor
23// TCP friendliness configured
24// 2. A Cubic TCP on a 5 ms RTT bottleneck, with fast convergence but no
25// TCP friendliness configured. This exhibits a different cwnd plot from 1.
26// 3. A Cubic TCP on a 20 ms RTT bottleneck, with fast convergence but no
27// TCP friendliness configured. There is a step change increase in
28// bottleneck link capacity but the TCP is slow to exploit it.
29// 4. A Cubic TCP on a 20 ms RTT bottleneck, with fast convergence and
30// TCP friendliness configured. There is a step change increase in
31// bottleneck link capacity and the TCP responds more quickly than in 3.
32
33#include "ns3/abort.h"
34#include "ns3/bulk-send-helper.h"
35#include "ns3/config.h"
36#include "ns3/data-rate.h"
37#include "ns3/error-model.h"
38#include "ns3/gnuplot.h"
39#include "ns3/inet-socket-address.h"
40#include "ns3/internet-stack-helper.h"
41#include "ns3/ipv4-address-helper.h"
42#include "ns3/ipv4-global-routing-helper.h"
43#include "ns3/log.h"
44#include "ns3/node-container.h"
45#include "ns3/output-stream-wrapper.h"
46#include "ns3/packet-sink-helper.h"
47#include "ns3/pcap-file.h"
48#include "ns3/point-to-point-helper.h"
49#include "ns3/point-to-point-net-device.h"
50#include "ns3/pointer.h"
51#include "ns3/simulator.h"
52#include "ns3/string.h"
53#include "ns3/tcp-header.h"
54#include "ns3/tcp-socket-factory.h"
55#include "ns3/test.h"
56#include "ns3/trace-helper.h"
57#include "ns3/traffic-control-helper.h"
58#include "ns3/uinteger.h"
59
60#include <iomanip>
61
62using namespace ns3;
63
64NS_LOG_COMPONENT_DEFINE("Ns3TcpCubicTest");
65
66static constexpr bool WRITE_PCAP = false; //!< Set to true to write out pcap.
67static constexpr bool WRITE_GNUPLOT = false; //!< Set to true to write out gnuplot.
68
69/**
70 * \ingroup system-tests-tcp
71 *
72 * Add sample trace values to data structures
73 * \param gnuplotTimeSeries Gnuplot data structure
74 * \param timeSeries time series of cwnd changes
75 * \param oldval old value of cwnd
76 * \param newval new value of cwnd
77 */
78void
80 std::map<Time, double>* timeSeries,
81 uint32_t oldval,
82 uint32_t newval)
83{
84 // We store data in two structures because Gnuplot2DDataset data is not exportable
85 NS_LOG_DEBUG("cwnd " << newval / 1448.0);
86 gnuplotTimeSeries->Add(Simulator::Now().GetSeconds(), newval / 1448.0);
87 timeSeries->insert_or_assign(Simulator::Now(), newval / 1448.0);
88}
89
90/**
91 * \ingroup system-tests-tcp
92 * Test Cubic response
93 */
95{
96 public:
97 /**
98 * Constructor.
99 *
100 * \param testCase testcase name
101 * \param prefix filename prefix if writing output files
102 * \param fastConvergence whether to enable fast convergence
103 * \param tcpFriendliness whether to enable TCP friendliness
104 * \param baseRtt base RTT to use for test case
105 * \param capacityIncrease whether to trigger a sudden capacity increase
106 */
107 Ns3TcpCubicTestCase(std::string testCase,
108 std::string prefix,
109 bool fastConvergence,
110 bool tcpFriendliness,
111 Time baseRtt,
112 bool capacityIncrease);
113 ~Ns3TcpCubicTestCase() override;
114
115 private:
116 void DoRun() override;
117
118 /**
119 * Connect TCP cwnd trace after socket is instantiated
120 *
121 * \param nodeId node ID to connect to
122 * \param socketId socket ID to connect to
123 */
124 void ConnectCwndTrace(uint32_t nodeId, uint32_t socketId);
125
126 /**
127 * Increases the device bandwidth to 100 Mbps
128 * \param device device to modify
129 */
131
132 /**
133 * Check that time series values within a time range are within a value range.
134 * \param start start of time range
135 * \param end end of time range
136 * \param lowerBound lower bound of acceptable values
137 * \param upperBound upper bound of acceptable values
138 * \return true if values are within range
139 */
140 bool CheckValues(Time start, Time end, double lowerBound, double upperBound);
141
142 bool m_writeResults{WRITE_PCAP}; //!< Whether to write pcaps
143 bool m_writeGnuplot{WRITE_GNUPLOT}; //!< Whether to write gnuplot files
144 Gnuplot2dDataset m_cwndTimeSeries; //!< cwnd time series
145 std::map<Time, double> m_timeSeries; //!< time series to check
146 std::string m_prefix; //!< filename prefix if writing files
147 bool m_fastConvergence; //!< whether to enable fast convergence
148 bool m_tcpFriendliness; //!< whether to enable TCP friendliness
149 Time m_baseRtt; //!< the base RTT to use
150 bool m_capacityIncrease; //!< whether to trigger a capacity increase
151};
152
154 std::string prefix,
155 bool fastConvergence,
156 bool tcpFriendliness,
157 Time baseRtt,
158 bool capacityIncrease)
159 : TestCase(testCase),
160 m_prefix(prefix),
161 m_fastConvergence(fastConvergence),
162 m_tcpFriendliness(tcpFriendliness),
163 m_baseRtt(baseRtt),
164 m_capacityIncrease(capacityIncrease)
165{
166}
167
171
172void
174{
175 device->SetDataRate(DataRate("100Mbps"));
176}
177
178void
180{
182 "/NodeList/" + std::to_string(nodeId) + "/$ns3::TcpL4Protocol/SocketList/" +
183 std::to_string(socketId) + "/CongestionWindow",
185}
186
187bool
188Ns3TcpCubicTestCase::CheckValues(Time start, Time end, double lowerBound, double upperBound)
189{
190 auto itStart = m_timeSeries.lower_bound(start);
191 auto itEnd = m_timeSeries.upper_bound(end);
192 for (auto it = itStart; it != itEnd; it++)
193 {
194 if (it->second < lowerBound || it->second > upperBound)
195 {
196 NS_LOG_DEBUG("Value " << it->second << " at time " << it->first.GetSeconds()
197 << " outside range (" << lowerBound << "," << upperBound << ")");
198 return false;
199 }
200 }
201 return true;
202}
203
204void
206{
207 NS_LOG_DEBUG("Running " << m_prefix);
208 // The maximum bandwidth delay product is 20 ms (base RTT) times 100 Mbps
209 // which works out to 250KB. Setting the socket buffer sizes to four times
210 // that value (1MB) should be more than enough.
211 Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(1000000));
212 Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(1000000));
213 Config::SetDefault("ns3::TcpSocket::InitialCwnd", UintegerValue(10));
214 Config::SetDefault("ns3::TcpSocket::DelAckCount", UintegerValue(2));
215 Config::SetDefault("ns3::TcpSocket::SegmentSize", UintegerValue(1448));
216 Config::SetDefault("ns3::TcpSocketState::EnablePacing", BooleanValue(true));
217 Config::SetDefault("ns3::TcpSocketState::PaceInitialWindow", BooleanValue(true));
218 Config::SetDefault("ns3::TcpCubic::FastConvergence", BooleanValue(m_fastConvergence));
219 Config::SetDefault("ns3::TcpCubic::TcpFriendliness", BooleanValue(m_tcpFriendliness));
220
221 Time stopTime = Seconds(20);
222
223 NodeContainer sender;
224 NodeContainer receiver;
225 NodeContainer routers;
226 sender.Create(1);
227 receiver.Create(1);
228 routers.Create(2);
229
230 // Create the point-to-point link helpers
231 PointToPointHelper bottleneckLink;
232 // With the DynamicQueueLimits setting below, the following statement
233 // will minimize the buffering in the device and force most buffering
234 // into the AQM
235 bottleneckLink.SetQueue("ns3::DropTailQueue", "MaxSize", StringValue("2p"));
237 {
238 // Start at a lower capacity, and increase later
239 bottleneckLink.SetDeviceAttribute("DataRate", StringValue("50Mbps"));
240 }
241 else
242 {
243 bottleneckLink.SetDeviceAttribute("DataRate", StringValue("100Mbps"));
244 }
245 bottleneckLink.SetChannelAttribute("Delay", TimeValue(m_baseRtt / 2));
246
247 PointToPointHelper edgeLink;
248 edgeLink.SetDeviceAttribute("DataRate", StringValue("1000Mbps"));
249 edgeLink.SetChannelAttribute("Delay", StringValue("5us"));
250
251 // Create NetDevice containers
252 NetDeviceContainer senderEdge = edgeLink.Install(sender.Get(0), routers.Get(0));
253 NetDeviceContainer r1r2 = bottleneckLink.Install(routers.Get(0), routers.Get(1));
254 NetDeviceContainer receiverEdge = edgeLink.Install(routers.Get(1), receiver.Get(0));
255
256 // Install Stack
257 InternetStackHelper internet;
258 internet.Install(sender);
259 internet.Install(receiver);
260 internet.Install(routers);
261
262 // Configure the root queue discipline
264 tch.SetRootQueueDisc("ns3::FqCoDelQueueDisc");
265 tch.SetQueueLimits("ns3::DynamicQueueLimits");
266
267 tch.Install(senderEdge);
268 tch.Install(receiverEdge);
269
270 // Assign IP addresses
272 ipv4.SetBase("10.0.0.0", "255.255.255.0");
273
274 Ipv4InterfaceContainer i1i2 = ipv4.Assign(r1r2);
275 ipv4.NewNetwork();
276 Ipv4InterfaceContainer is1 = ipv4.Assign(senderEdge);
277 ipv4.NewNetwork();
278 Ipv4InterfaceContainer ir1 = ipv4.Assign(receiverEdge);
279
280 // Populate routing tables
282
283 // Select sender side port
284 uint16_t port = 50001;
285
286 // Install application on the sender
287 BulkSendHelper source("ns3::TcpSocketFactory", InetSocketAddress(ir1.GetAddress(1), port));
288 source.SetAttribute("MaxBytes", UintegerValue(0));
289 ApplicationContainer sourceApps = source.Install(sender.Get(0));
290 sourceApps.Start(Seconds(0.1));
291 // Hook trace source after application starts
294 this,
295 0,
296 0);
297 sourceApps.Stop(stopTime);
298
299 // Install application on the receiver
300 PacketSinkHelper sink("ns3::TcpSocketFactory", InetSocketAddress(Ipv4Address::GetAny(), port));
301 ApplicationContainer sinkApps = sink.Install(receiver.Get(0));
302 sinkApps.Start(Seconds(0.0));
303 sinkApps.Stop(stopTime);
304
306 {
307 // Double the capacity of the bottleneck link at 10 seconds
308 Ptr<PointToPointNetDevice> device = r1r2.Get(0)->GetObject<PointToPointNetDevice>();
310 }
311
312 std::ostringstream oss;
313 oss << m_prefix;
314 if (m_writeResults)
315 {
316 edgeLink.EnablePcap(oss.str(), senderEdge.Get(0));
317 edgeLink.EnablePcap(oss.str(), receiverEdge.Get(0));
318 edgeLink.EnableAscii(oss.str(), senderEdge.Get(0));
319 edgeLink.EnableAscii(oss.str(), receiverEdge.Get(0));
320 }
323
324 if (m_writeGnuplot)
325 {
326 std::ofstream outfile(oss.str() + ".plt");
327
328 std::string capacityIncreaseString;
330 {
331 capacityIncreaseString = " capacity increase = 1";
332 }
333 Gnuplot gnuplot = Gnuplot(
334 oss.str() + ".eps",
335 "Cubic concave/convex response (" + std::to_string(m_baseRtt.GetMilliSeconds()) +
336 " ms RTT, 1448 byte segs, 100 Mbps bottleneck)\\nFast convergence = " +
337 std::to_string(m_fastConvergence) +
338 " friendliness = " + std::to_string(m_tcpFriendliness) + capacityIncreaseString);
339 gnuplot.SetTerminal("post eps color enhanced");
340 gnuplot.SetLegend("Time (seconds)", "Cwnd (segments)");
343 gnuplot.GenerateOutput(outfile);
344 }
345
346 // Check that cwnd values are within tolerance of expected values
347 // The expected values were determined by inspecting the plots generated
348 if (m_prefix == "ns3-tcp-cubic-no-heuristic")
349 {
350 // Check overall min and max
352 true,
353 "cwnd outside range");
354 // Time just before a reduction does not have much variation
356 true,
357 "cwnd outside range");
358 }
359 else if (m_prefix == "ns3-tcp-cubic-fast-conv")
360 {
361 // Check overall min and max
363 true,
364 "cwnd outside range");
365 // Initial convex region does not have much variation
367 true,
368 "cwnd outside range");
369 }
370 else if (m_prefix == "ns3-tcp-cubic-no-friendly")
371 {
372 // Between time 11 and 15, cwnd should be fairly constant
373 // because without TCP friendliness, Cubic does not respond quickly
375 true,
376 "cwnd outside range");
377 // After time 17.5, cwnd should have grown much higher
378 NS_TEST_ASSERT_MSG_EQ(CheckValues(Seconds(17.5), Seconds(18.5), 169, 215),
379 true,
380 "cwnd outside range");
381 }
382 else if (m_prefix == "ns3-tcp-cubic-friendly")
383 {
384 // In contrast to previous case, cwnd should grow above 150 much sooner
386 true,
387 "cwnd outside range");
388 }
389
391}
392
393/**
394 * \ingroup system-tests-tcp
395 * TestSuite for module tcp-cubic
396 */
398{
399 public:
401};
402
404 : TestSuite("ns3-tcp-cubic", Type::UNIT)
405{
406 // Test Cubic with no fast convergence or TCP friendliness enabled
407 // This results in a cwnd plot that has only the concave portion of
408 // returning cwnd to Wmax
409 AddTestCase(new Ns3TcpCubicTestCase("no heuristic",
410 "ns3-tcp-cubic-no-heuristic",
411 false,
412 false,
413 MilliSeconds(5),
414 false),
415 TestCase::Duration::QUICK);
416
417 // Test Cubic with fast convergence but no TCP friendliness enabled
418 // This results in a cwnd plot that has concave and convex regions, as
419 // well as convex-only regions; also observed on Linux testbeds
420 AddTestCase(new Ns3TcpCubicTestCase("fast convergence on",
421 "ns3-tcp-cubic-fast-conv",
422 true,
423 false,
424 MilliSeconds(5),
425 false),
426 TestCase::Duration::QUICK);
427
428 // Test Cubic with fast convergence but no TCP friendliness enabled
429 // with a higher RTT (20ms) and a step change in capacity at time 10s.
430 // This results in a sluggish response by TCP Cubic to use the new capacity.
431 AddTestCase(new Ns3TcpCubicTestCase("fast convergence on, Reno friendliness off",
432 "ns3-tcp-cubic-no-friendly",
433 true,
434 false,
435 MilliSeconds(20),
436 true),
437 TestCase::Duration::QUICK);
438
439 // Test Cubic with fast convergence but with TCP friendliness enabled
440 // with a higher RTT (20ms) and a step change in capacity at time 10s.
441 // This results in a faster response by TCP Cubic to use the new capacity.
442 AddTestCase(new Ns3TcpCubicTestCase("fast convergence on, Reno friendliness on",
443 "ns3-tcp-cubic-friendly",
444 true,
445 true,
446 MilliSeconds(20),
447 true),
448 TestCase::Duration::QUICK);
449}
450
451/**
452 * Static variable for test initialization
453 */
Ipv4InterfaceContainer i1i2
IPv4 interface container i1 + i2.
Gnuplot2dDataset m_cwndTimeSeries
cwnd time series
bool m_tcpFriendliness
whether to enable TCP friendliness
void DoRun() override
Implementation to actually run this TestCase.
Time m_baseRtt
the base RTT to use
void ConnectCwndTrace(uint32_t nodeId, uint32_t socketId)
Connect TCP cwnd trace after socket is instantiated.
std::string m_prefix
filename prefix if writing files
Ns3TcpCubicTestCase(std::string testCase, std::string prefix, bool fastConvergence, bool tcpFriendliness, Time baseRtt, bool capacityIncrease)
Constructor.
bool m_writeGnuplot
Whether to write gnuplot files.
bool m_capacityIncrease
whether to trigger a capacity increase
bool m_fastConvergence
whether to enable fast convergence
bool m_writeResults
Whether to write pcaps.
std::map< Time, double > m_timeSeries
time series to check
void IncreaseBandwidth(Ptr< PointToPointNetDevice > device)
Increases the device bandwidth to 100 Mbps.
bool CheckValues(Time start, Time end, double lowerBound, double upperBound)
Check that time series values within a time range are within a value range.
TestSuite for module tcp-cubic.
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.
ApplicationContainer Install(NodeContainer c)
Install an application on each node of the input container configured with all the attributes set wit...
void SetAttribute(const std::string &name, const AttributeValue &value)
Helper function used to set the underlying application attributes.
void EnableAscii(std::string prefix, Ptr< NetDevice > nd, bool explicitFilename=false)
Enable ascii trace output on the indicated net device.
A helper to make it easier to instantiate an ns3::BulkSendApplication on a set of nodes.
Class for representing data rates.
Definition data-rate.h:78
Class to represent a 2D points plot.
Definition gnuplot.h:105
void Add(double x, double y)
Definition gnuplot.cc:366
void SetTitle(const std::string &title)
Change line title.
Definition gnuplot.cc:137
a simple class to generate gnuplot-ready plotting commands from a set of datasets.
Definition gnuplot.h:359
void AddDataset(const GnuplotDataset &dataset)
Definition gnuplot.cc:785
void SetLegend(const std::string &xLegend, const std::string &yLegend)
Definition gnuplot.cc:765
void SetTerminal(const std::string &terminal)
Definition gnuplot.cc:753
void GenerateOutput(std::ostream &os)
Writes gnuplot commands and data values to a single output stream.
Definition gnuplot.cc:791
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
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
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.
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes.
void EnablePcap(std::string prefix, Ptr< NetDevice > nd, bool promiscuous=false, bool explicitFilename=false)
Enable pcap output the indicated net device.
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.
void SetQueue(std::string type, Ts &&... args)
Each point to point net device must have a queue to pass packets through.
NetDeviceContainer Install(NodeContainer c)
A Device for a Point to Point Network Link.
Smart pointer class similar to boost::intrusive_ptr.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:560
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 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
encapsulates test code
Definition test.h:1050
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
int64_t GetMilliSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:397
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.
Hold an unsigned integer type.
Definition uinteger.h:34
uint16_t port
Definition dsdv-manet.cc:33
Time stopTime
void SetDefault(std::string name, const AttributeValue &value)
Definition config.cc:883
void ConnectWithoutContext(std::string path, const CallbackBase &cb)
Definition config.cc:943
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
auto MakeBoundCallback(R(*fnPtr)(Args...), BArgs &&... bargs)
Make Callbacks with varying number of bound arguments.
Definition callback.h:745
void CubicCwndTracer(Gnuplot2dDataset *gnuplotTimeSeries, std::map< Time, double > *timeSeries, uint32_t oldval, uint32_t newval)
Add sample trace values to data structures.
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:134
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1308
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1320
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static constexpr bool WRITE_PCAP
Set to true to write out pcap.
static Ns3TcpCubicTestSuite ns3TcpCubicTestSuite
Static variable for test initialization.
static constexpr bool WRITE_GNUPLOT
Set to true to write out gnuplot.
Ptr< PacketSink > sink
Pointer to the packet sink application.
Definition wifi-tcp.cc:44