A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-linux-reno-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Apoorva Bhargava <apoorvabhargava13@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 */
7#include "tcp-general-test.h"
8
9#include "ns3/config.h"
10#include "ns3/log.h"
11#include "ns3/node.h"
12#include "ns3/simple-channel.h"
13#include "ns3/tcp-header.h"
14#include "ns3/tcp-linux-reno.h"
15#include "ns3/test.h"
16
17using namespace ns3;
18
19NS_LOG_COMPONENT_DEFINE("TcpLinuxRenoTest");
20
21/**
22 * \ingroup internet-test
23 *
24 * This unit test checks that the slow start and congestion avoidance
25 * behavior matches Linux behavior as follows:
26 * 1) in both slow start and congestion avoidance phases, presence or
27 * absence of delayed acks does not alter the window growth
28 * 2) in congestion avoidance phase, the arithmetic for counting the number
29 * of segments acked and deciding when to increment the congestion window
30 * (i.e. following the Linux function tcp_cong_avoid_ai()) is followed.
31 * Different segment sizes (524 bytes and 1500 bytes) are tested.
32 *
33 * This is the Slow Start test.
34 */
36{
37 public:
38 /**
39 * \brief Constructor.
40 * \param segmentSize Segment size.
41 * \param packetSize Size of the packets.
42 * \param packets Number of packets.
43 * \param initialCwnd Initial congestion window
44 * \param delayedAck Delayed Acknowledgement
45 * \param expectedCwnd Expected value of m_cWnd
46 * \param congControl Type of congestion control.
47 * \param desc The test description.
48 */
51 uint32_t packets,
52 uint32_t initialCwnd,
53 uint32_t delayedAck,
54 uint32_t expectedCwnd,
55 TypeId& congControl,
56 const std::string& desc);
57
58 protected:
59 void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
60 void QueueDrop(SocketWho who) override;
61 void PhyDrop(SocketWho who) override;
62
63 void ConfigureEnvironment() override;
64 void ConfigureProperties() override;
65 void DoTeardown() override;
66
67 bool m_initial; //!< First cycle flag.
68
69 private:
70 void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
71 void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
72 uint32_t m_segmentSize; //!< Segment size.
73 uint32_t m_packetSize; //!< Packet size.
74 uint32_t m_packets; //!< Packet counter.
75 uint32_t m_initialCwnd; //!< Initial congestion window
76 uint32_t m_delayedAck; //!< Delayed Acknowledgement
77 uint32_t m_lastCwnd; //!< Last cWnd value reported
78 uint32_t m_expectedCwnd; //!< Expected final cWnd value
79};
80
83 uint32_t packets,
84 uint32_t initialCwnd,
85 uint32_t delayedAck,
86 uint32_t expectedCwnd,
87 TypeId& typeId,
88 const std::string& desc)
89 : TcpGeneralTest(desc),
90 m_initial(true),
91 m_segmentSize(segmentSize),
92 m_packetSize(packetSize),
93 m_packets(packets),
94 m_initialCwnd(initialCwnd),
95 m_delayedAck(delayedAck),
96 m_lastCwnd(0),
97 m_expectedCwnd(expectedCwnd)
98{
99 m_congControlTypeId = typeId;
100}
101
102void
110
111void
120
121void
123{
124 NS_FATAL_ERROR("Drop on the queue; cannot validate slow start");
125}
126
127void
129{
130 NS_FATAL_ERROR("Drop on the phy: cannot validate slow start");
131}
132
133void
135{
136 NS_LOG_FUNCTION(this << oldValue << newValue);
138 uint32_t increase = newValue - oldValue;
139 m_lastCwnd = newValue;
140
141 if (m_initial)
142 {
143 m_initial = false;
144 NS_TEST_ASSERT_MSG_EQ(newValue,
146 "The first update is for ACK of SYN and should initialize cwnd");
147 return;
148 }
149
150 // ACK for first data packet is received to speed up the connection
151 if (oldValue == m_initialCwnd * m_segmentSize)
152 {
153 return;
154 }
155
156 NS_TEST_ASSERT_MSG_EQ(increase, m_delayedAck * segSize, "Increase different than segsize");
157 NS_TEST_ASSERT_MSG_LT_OR_EQ(newValue, GetInitialSsThresh(SENDER), "cWnd increased over ssth");
158
159 NS_LOG_INFO("Incremented cWnd by " << m_delayedAck * segSize << " bytes in Slow Start "
160 << "achieving a value of " << newValue);
161}
162
163void
165{
166 NS_LOG_FUNCTION(this << p << h << who);
167}
168
169void
171{
172 NS_LOG_FUNCTION(this << p << h << who);
173}
174
175void
177{
180 "Congestion window did not evolve as expected");
181 TcpGeneralTest::DoTeardown(); // call up to base class method to finish
182}
183
184/**
185 * \ingroup internet-test
186 *
187 * This unit test checks that the slow start and congestion avoidance
188 * behavior matches Linux behavior as follows:
189 * 1) in both slow start and congestion avoidance phases, presence or
190 * absence of delayed acks does not alter the window growth
191 * 2) in congestion avoidance phase, the arithmetic for counting the number
192 * of segments acked and deciding when to increment the congestion window
193 * (i.e. following the Linux function tcp_cong_avoid_ai()) is followed.
194 * Different segment sizes (524 bytes and 1500 bytes) are tested.
195 *
196 * This is the Congestion avoidance test.
197 */
199{
200 public:
201 /**
202 * \brief Constructor.
203 * \param segmentSize Segment size.
204 * \param packetSize Size of the packets.
205 * \param packets Number of packets.
206 * \param initialCwnd Initial congestion window (segments)
207 * \param initialSSThresh Initial slow start threshold (bytes)
208 * \param delayedAck Delayed Acknowledgement
209 * \param expectedCwnd Expected final m_cWnd value
210 * \param congControl Type of congestion control.
211 * \param desc The test description.
212 */
215 uint32_t packets,
216 uint32_t initialCwnd,
217 uint32_t initialSSThresh,
218 uint32_t delayedAck,
219 uint32_t expectedCwnd,
220 TypeId& congControl,
221 const std::string& desc);
222
223 protected:
224 void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
225 void QueueDrop(SocketWho who) override;
226 void PhyDrop(SocketWho who) override;
227
228 void ConfigureEnvironment() override;
229 void ConfigureProperties() override;
230 void DoTeardown() override;
231
232 private:
233 void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
234 void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
235 uint32_t m_segmentSize; //!< Segment size.
236 uint32_t m_packetSize; //!< Size of the packets used in socket writes.
237 uint32_t m_packets; //!< Number of packets to send to the socket.
238 uint32_t m_initialCwnd; //!< Initial congestion window (segments)
239 uint32_t m_initialSSThresh; //!< Initial slow start threshold (bytes)
240 uint32_t m_delayedAck; //!< Delayed Acknowledgement
241 uint32_t m_lastCwnd; //!< Last cWnd value reported
242 uint32_t m_expectedCwnd; //!< Expected final cWnd value
243 uint32_t m_increment; //!< Congestion window increment.
244 bool m_initial; //!< True on first run.
245 bool m_inCongAvoidance; //!< True if in congestion avoidance
246 bool m_inSlowStartPhase; //!< True if in slow start
247};
248
251 uint32_t packets,
252 uint32_t initialCwnd,
253 uint32_t initialSSThresh,
254 uint32_t delayedAck,
255 uint32_t expectedCwnd,
256 TypeId& typeId,
257 const std::string& desc)
258 : TcpGeneralTest(desc),
259 m_segmentSize(segmentSize),
260 m_packetSize(packetSize),
261 m_packets(packets),
262 m_initialCwnd(initialCwnd),
263 m_initialSSThresh(initialSSThresh),
264 m_delayedAck(delayedAck),
265 m_lastCwnd(0),
266 m_expectedCwnd(expectedCwnd),
267 m_increment(0),
268 m_initial(true),
269 m_inCongAvoidance(false),
270 m_inSlowStartPhase(true)
271{
272 m_congControlTypeId = typeId;
273}
274
275void
283
284void
294
295void
297{
298 NS_LOG_FUNCTION(this << oldValue << newValue);
299 m_lastCwnd = newValue;
300 if (m_initial)
301 {
302 m_initial = false;
303 NS_TEST_ASSERT_MSG_EQ(newValue,
305 "The first update is for ACK of SYN and should initialize cwnd");
306 return;
307 }
308
309 if ((newValue >= m_initialSSThresh * m_segmentSize) && !m_inCongAvoidance &&
310 (oldValue != m_initialSSThresh))
311 {
312 m_inCongAvoidance = true;
313 m_inSlowStartPhase = false;
314 return;
315 }
316
318 {
319 return;
320 }
321
322 m_increment = newValue - oldValue;
323
324 NS_TEST_ASSERT_MSG_EQ(m_increment, m_segmentSize, "Increase different than segsize");
325}
326
327void
329{
330 NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
331}
332
333void
335{
336 NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
337}
338
339void
341{
342 NS_LOG_FUNCTION(this << p << h << who);
343}
344
345void
347{
348 NS_LOG_FUNCTION(this << p << h << who);
349}
350
351void
353{
356 "Congestion window did not evolve as expected");
357 TcpGeneralTest::DoTeardown(); // call up to base class method to finish
358}
359
360/**
361 * \ingroup internet-test
362 *
363 * \brief TestSuite for the behavior of Linux Reno
364 */
366{
367 public:
369 : TestSuite("tcp-linux-reno-test", Type::UNIT)
370 {
371 TypeId cong_control_type = TcpLinuxReno::GetTypeId();
372 // Test the behavior of Slow Start phase with small segment size
373 // (524 bytes) and delayed acknowledgement of 1 and 2 segments
374 //
375 // Expected data pattern starting at simulation time 10:
376 // (cwnd = 2 segments) 1 ->
377 // (cwnd = 2 segments) 2 ->
378 // (time 10.01s) <- ACK of 1
379 // cwnd increased to 3 segments; send two more
380 // (cwnd = 3 segments) 3 ->
381 // (cwnd = 3 segments) 4 ->
382 // (time 10.011s) <- ACK of 2
383 // cwnd increased to 4 segments; send two more
384 // (cwnd = 4 segments) 5 ->
385 // (cwnd = 4 segments) 6 ->
386 // (time 10.02s) <- ACK of 3
387 // cwnd increased to 5 segments; send two more but only one more to send
388 // (cwnd = 5 segments) 7+FIN ->
389 // <- ACK of 4
390 // <- ACK of 5
391 // <- ACK of 6
392 // <- ACK of 7
393 // cwnd should be at 9 segments
394 // <- FIN/ACK
396 new TcpLinuxRenoSSTest(524, // segment size
397 524, // socket send size
398 7, // socket sends (i.e. packets)
399 2, // initial cwnd
400 1, // delayed ack count
401 9 * 524, // expected final cWnd
402 cong_control_type,
403 "Slow Start MSS = 524, socket send size = 524, delack = 1 " +
404 cong_control_type.GetName()),
405 TestCase::Duration::QUICK);
406
407 // Next, enabling delayed acks should not have an effect on the final
408 // cWnd achieved
410 new TcpLinuxRenoSSTest(524, // segment size
411 524, // socket send size
412 7, // socket sends
413 2, // initial cwnd
414 2, // delayed ack count
415 9 * 524, // expected final cWnd
416 cong_control_type,
417 "Slow Start MSS = 524, socket send size = 524, delack = 2 " +
418 cong_control_type.GetName()),
419 TestCase::Duration::QUICK);
420
421 // Test the behavior of Slow Start phase with standard segment size
422 // (1500 bytes) and delayed acknowledgement of 1 and 2 segments
423 //
424 // We still expect m_cWnd to end up at 9 segments
426 new TcpLinuxRenoSSTest(1500, // segment size
427 1500, // socket send size
428 7, // socket sends
429 2, // initial cwnd
430 1, // delayed ack count
431 9 * 1500, // expected final cWnd
432 cong_control_type,
433 "Slow Start MSS = 1500, socket send size = 524, delack = 1 " +
434 cong_control_type.GetName()),
435 TestCase::Duration::QUICK);
436
437 // Enable delayed acks; we still expect m_cWnd to end up at 9 segments
439 new TcpLinuxRenoSSTest(1500, // segment size
440 1500, // socket send size
441 7, // socket sends
442 2, // initial cwnd
443 2, // delayed ack count
444 9 * 1500, // expected final cWnd
445 cong_control_type,
446 "Slow Start MSS = 1500, socket send size = 524, delack = 2 " +
447 cong_control_type.GetName()),
448 TestCase::Duration::QUICK);
449
450 // Test the behavior of Congestion Avoidance phase with small segment size
451 // (524 bytes) and delayed acknowledgement of 1 and 2. One important thing
452 // to confirm is that delayed ACK behavior does not affect the congestion
453 // window growth and final value because LinuxReno TCP counts segments acked
454 //
455 // Expected data pattern starting at simulation time 10:
456 // (cwnd = 1 segment) 1 ->
457 // (time 11s) <- ACK of 1
458 // (cwnd = 2 slow start) 2 ->
459 // (can send one more ) 3 ->
460 // (time 12s ) <- ACK of 2
461 // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
462 // snd_cwnd_count will be increased to 1, but less than current window 2
463 // send one new segment to replace the one that was acked
464 // (cwnd = 2 CA ) 4 ->
465 // (again, time 12s ) <- ACK of 3
466 // at this ACK, snd_cwnd >= ssthresh of 2, so stay in CongestionAvoidance
467 // snd_cwnd_count (m_cWndCnt) will be increased to 2, equal to w
468 // We can increase cWnd to three segments and reset snd_cwnd_count
469 // 5 ->
470 // 6+FIN ->
471 // (time 13s ) <- ACK of 4
472 // increase m_cWndCnt to 1
473 // (time 13s ) <- ACK of 5
474 // increase m_cWndCnt to 2
475 // (time 13s ) <- ACK of 6
476 // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
477 // Final value of m_cWnd should be 4 * 524 = 2096
479 524, // segment size
480 524, // socket send size
481 6, // socket sends
482 1, // initial cwnd
483 2 * 524, // initial ssthresh
484 1, // delayed ack count
485 4 * 524, // expected final cWnd
486 cong_control_type,
487 "Congestion Avoidance MSS = 524, socket send size = 524, delack = 1 " +
488 cong_control_type.GetName()),
489 TestCase::Duration::QUICK);
490
491 // Repeat with delayed acks enabled: should result in same final cWnd
492 // Expected data pattern starting at simulation time 10:
493 // (cwnd = 1 segment) 1 ->
494 // (time 11s) <- ACK of 1, ns-3 will always ack 1st seg
495 // (cwnd = 2 slow start) 2 ->
496 // (can send one more ) 3 ->
497 // (time 12s ) <- ACK of 3 (combined ack of 2 segments)
498 // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
499 // snd_cwnd_count will be increased to 1+1 = current window 2
500 // send one new segment to replace the one that was acked
501 // (cwnd = 3 CA ) 4 ->
502 // (cwnd = 3 CA ) 5 ->
503 // (cwnd = 3 CA ) 6 ->
504 // (time 13s ) <- ACK of 5 (combined ack of 2 segments)
505 // (time 13s ) <- ACK of 6 (ack of 1 segment due to FIN)
506 // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
507 // Final value of m_cWnd should be 4 * 524 = 2096
509 524, // segment size
510 524, // socket send size
511 6, // socket sends
512 1, // initial cwnd
513 2, // initial ssthresh
514 2, // delayed ack count
515 4 * 524, // expected final cWnd
516 cong_control_type,
517 "Congestion Avoidance MSS = 524, socket send size = 524, delack = 2 " +
518 cong_control_type.GetName()),
519 TestCase::Duration::QUICK);
520
521 // Test the behavior of Congestion Avoidance phase with standard segment size (i.e 1500
522 // bytes) and delayed acknowledgement of 1 and 2 Test the behavior of Congestion Avoidance
523 // phase with standard segment size (1500 bytes) and delayed acknowledgement of 1 and 2.
524 // This should result in the same pattern of segment exchanges as
525 // above.
527 1500, // segment size
528 1500, // socket send size
529 6, // socket sends
530 1, // initial cwnd
531 2, // initial ssthresh
532 1, // delayed ack count
533 4 * 1500, // expected final cWnd
534 cong_control_type,
535 "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 1 " +
536 cong_control_type.GetName()),
537 TestCase::Duration::QUICK);
538
540 1500, // segment size
541 1500, // socket send size
542 6, // socket sends
543 1, // initial cwnd
544 2, // initial ssthresh
545 2, // delayed ack count
546 4 * 1500, // expected final cWnd
547 cong_control_type,
548 "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 2 " +
549 cong_control_type.GetName()),
550 TestCase::Duration::QUICK);
551 }
552};
553
554static TcpLinuxRenoTestSuite g_tcpLinuxRenoTestSuite; //!< Static variable for test initialization
This unit test checks that the slow start and congestion avoidance behavior matches Linux behavior as...
bool m_inCongAvoidance
True if in congestion avoidance.
uint32_t m_initialSSThresh
Initial slow start threshold (bytes)
uint32_t m_segmentSize
Segment size.
bool m_inSlowStartPhase
True if in slow start.
uint32_t m_delayedAck
Delayed Acknowledgement.
uint32_t m_packetSize
Size of the packets used in socket writes.
bool m_initial
True on first run.
void DoTeardown() override
Teardown the TCP test.
void CWndTrace(uint32_t oldValue, uint32_t newValue) override
Tracks the congestion window changes.
void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet received from IP layer.
void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet transmitted down to IP layer.
uint32_t m_packets
Number of packets to send to the socket.
uint32_t m_expectedCwnd
Expected final cWnd value.
void PhyDrop(SocketWho who) override
Link drop.
uint32_t m_lastCwnd
Last cWnd value reported.
uint32_t m_increment
Congestion window increment.
void ConfigureProperties() override
Change the configuration of the socket properties.
TcpLinuxRenoCongAvoidTest(uint32_t segmentSize, uint32_t packetSize, uint32_t packets, uint32_t initialCwnd, uint32_t initialSSThresh, uint32_t delayedAck, uint32_t expectedCwnd, TypeId &congControl, const std::string &desc)
Constructor.
void ConfigureEnvironment() override
Change the configuration of the environment.
uint32_t m_initialCwnd
Initial congestion window (segments)
void QueueDrop(SocketWho who) override
Drop on the queue.
This unit test checks that the slow start and congestion avoidance behavior matches Linux behavior as...
bool m_initial
First cycle flag.
void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet transmitted down to IP layer.
uint32_t m_lastCwnd
Last cWnd value reported.
void DoTeardown() override
Teardown the TCP test.
uint32_t m_initialCwnd
Initial congestion window.
uint32_t m_delayedAck
Delayed Acknowledgement.
void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet received from IP layer.
void ConfigureProperties() override
Change the configuration of the socket properties.
void ConfigureEnvironment() override
Change the configuration of the environment.
void CWndTrace(uint32_t oldValue, uint32_t newValue) override
Tracks the congestion window changes.
TcpLinuxRenoSSTest(uint32_t segmentSize, uint32_t packetSize, uint32_t packets, uint32_t initialCwnd, uint32_t delayedAck, uint32_t expectedCwnd, TypeId &congControl, const std::string &desc)
Constructor.
uint32_t m_packetSize
Packet size.
void QueueDrop(SocketWho who) override
Drop on the queue.
uint32_t m_expectedCwnd
Expected final cWnd value.
uint32_t m_packets
Packet counter.
uint32_t m_segmentSize
Segment size.
void PhyDrop(SocketWho who) override
Link drop.
TestSuite for the behavior of Linux Reno.
Smart pointer class similar to boost::intrusive_ptr.
General infrastructure for TCP testing.
void SetPropagationDelay(Time propDelay)
Propagation delay of the bottleneck link.
void SetAppPktCount(uint32_t pktCount)
Set app packet count.
void SetDelAckMaxCount(SocketWho who, uint32_t count)
Forcefully set the delayed acknowledgement count.
SocketWho
Used as parameter of methods, specifies on what node the caller is interested (e.g.
@ RECEIVER
Receiver node.
void SetAppPktSize(uint32_t pktSize)
Set app packet size.
void SetInitialCwnd(SocketWho who, uint32_t initialCwnd)
Forcefully set the initial cwnd.
uint32_t GetInitialSsThresh(SocketWho who)
Get the initial slow start threshold.
virtual void ConfigureProperties()
Change the configuration of the socket properties.
void SetMTU(uint32_t mtu)
MTU of the bottleneck link.
uint32_t GetSegSize(SocketWho who)
Get the segment size of the node specified.
TypeId m_congControlTypeId
Congestion control.
void SetInitialSsThresh(SocketWho who, uint32_t initialSsThresh)
Forcefully set the initial ssthresh.
virtual void ConfigureEnvironment()
Change the configuration of the environment.
void DoTeardown() override
Teardown the TCP test.
void SetSegmentSize(SocketWho who, uint32_t segmentSize)
Forcefully set the segment size.
Header for the Transmission Control Protocol.
Definition tcp-header.h:36
static TypeId GetTypeId()
Get the type ID.
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
static constexpr auto UNIT
Definition test.h:1291
a unique identifier for an interface.
Definition type-id.h:48
std::string GetName() const
Get the name.
Definition type-id.cc:1061
uint32_t segmentSize
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition log.h:264
#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
#define NS_TEST_ASSERT_MSG_LT_OR_EQ(actual, limit, msg)
Test that an actual value is less than or equal to a limit and report and abort if not.
Definition test.h:740
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 TcpLinuxRenoTestSuite g_tcpLinuxRenoTestSuite
Static variable for test initialization.
static const uint32_t packetSize
Packet size generated at the AP.