A Discrete-Event Network Simulator
API
tcp-linux-reno-test.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 Apoorva Bhargava <apoorvabhargava13@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  */
18 #include "tcp-general-test.h"
19 
20 #include "ns3/config.h"
21 #include "ns3/log.h"
22 #include "ns3/node.h"
23 #include "ns3/simple-channel.h"
24 #include "ns3/tcp-header.h"
25 #include "ns3/tcp-linux-reno.h"
26 #include "ns3/test.h"
27 
28 using namespace ns3;
29 
30 NS_LOG_COMPONENT_DEFINE("TcpLinuxRenoTest");
31 
47 {
48  public:
61  uint32_t packetSize,
62  uint32_t packets,
63  uint32_t initialCwnd,
64  uint32_t delayedAck,
65  uint32_t expectedCwnd,
66  TypeId& congControl,
67  const std::string& desc);
68 
69  protected:
70  void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
71  void QueueDrop(SocketWho who) override;
72  void PhyDrop(SocketWho who) override;
73 
74  void ConfigureEnvironment() override;
75  void ConfigureProperties() override;
76  void DoTeardown() override;
77 
78  bool m_initial;
79 
80  private:
81  void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
82  void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
83  uint32_t m_segmentSize;
84  uint32_t m_packetSize;
85  uint32_t m_packets;
86  uint32_t m_initialCwnd;
87  uint32_t m_delayedAck;
88  uint32_t m_lastCwnd;
89  uint32_t m_expectedCwnd;
90 };
91 
93  uint32_t packetSize,
94  uint32_t packets,
95  uint32_t initialCwnd,
96  uint32_t delayedAck,
97  uint32_t expectedCwnd,
98  TypeId& typeId,
99  const std::string& desc)
100  : TcpGeneralTest(desc),
101  m_initial(true),
102  m_segmentSize(segmentSize),
103  m_packetSize(packetSize),
104  m_packets(packets),
105  m_initialCwnd(initialCwnd),
106  m_delayedAck(delayedAck),
107  m_lastCwnd(0),
108  m_expectedCwnd(expectedCwnd)
109 {
110  m_congControlTypeId = typeId;
111 }
112 
113 void
115 {
116  TcpGeneralTest::ConfigureEnvironment();
120 }
121 
122 void
124 {
125  TcpGeneralTest::ConfigureProperties();
130 }
131 
132 void
134 {
135  NS_FATAL_ERROR("Drop on the queue; cannot validate slow start");
136 }
137 
138 void
140 {
141  NS_FATAL_ERROR("Drop on the phy: cannot validate slow start");
142 }
143 
144 void
145 TcpLinuxRenoSSTest::CWndTrace(uint32_t oldValue, uint32_t newValue)
146 {
147  NS_LOG_FUNCTION(this << oldValue << newValue);
148  uint32_t segSize = GetSegSize(TcpGeneralTest::SENDER);
149  uint32_t increase = newValue - oldValue;
150  m_lastCwnd = newValue;
151 
152  if (m_initial)
153  {
154  m_initial = false;
155  NS_TEST_ASSERT_MSG_EQ(newValue,
157  "The first update is for ACK of SYN and should initialize cwnd");
158  return;
159  }
160 
161  // ACK for first data packet is received to speed up the connection
162  if (oldValue == m_initialCwnd * m_segmentSize)
163  {
164  return;
165  }
166 
167  NS_TEST_ASSERT_MSG_EQ(increase, m_delayedAck * segSize, "Increase different than segsize");
168  NS_TEST_ASSERT_MSG_LT_OR_EQ(newValue, GetInitialSsThresh(SENDER), "cWnd increased over ssth");
169 
170  NS_LOG_INFO("Incremented cWnd by " << m_delayedAck * segSize << " bytes in Slow Start "
171  << "achieving a value of " << newValue);
172 }
173 
174 void
176 {
177  NS_LOG_FUNCTION(this << p << h << who);
178 }
179 
180 void
182 {
183  NS_LOG_FUNCTION(this << p << h << who);
184 }
185 
186 void
188 {
191  "Congestion window did not evolve as expected");
192  TcpGeneralTest::DoTeardown(); // call up to base class method to finish
193 }
194 
210 {
211  public:
225  uint32_t packetSize,
226  uint32_t packets,
227  uint32_t initialCwnd,
228  uint32_t initialSSThresh,
229  uint32_t delayedAck,
230  uint32_t expectedCwnd,
231  TypeId& congControl,
232  const std::string& desc);
233 
234  protected:
235  void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
236  void QueueDrop(SocketWho who) override;
237  void PhyDrop(SocketWho who) override;
238 
239  void ConfigureEnvironment() override;
240  void ConfigureProperties() override;
241  void DoTeardown() override;
242 
243  private:
244  void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
245  void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
246  uint32_t m_segmentSize;
247  uint32_t m_packetSize;
248  uint32_t m_packets;
249  uint32_t m_initialCwnd;
250  uint32_t m_initialSSThresh;
251  uint32_t m_delayedAck;
252  uint32_t m_lastCwnd;
253  uint32_t m_expectedCwnd;
254  uint32_t m_increment;
255  bool m_initial;
258 };
259 
261  uint32_t packetSize,
262  uint32_t packets,
263  uint32_t initialCwnd,
264  uint32_t initialSSThresh,
265  uint32_t delayedAck,
266  uint32_t expectedCwnd,
267  TypeId& typeId,
268  const std::string& desc)
269  : TcpGeneralTest(desc),
270  m_segmentSize(segmentSize),
271  m_packetSize(packetSize),
272  m_packets(packets),
273  m_initialCwnd(initialCwnd),
274  m_initialSSThresh(initialSSThresh),
275  m_delayedAck(delayedAck),
276  m_lastCwnd(0),
277  m_expectedCwnd(expectedCwnd),
278  m_increment(0),
279  m_initial(true),
280  m_inCongAvoidance(false),
281  m_inSlowStartPhase(true)
282 {
283  m_congControlTypeId = typeId;
284 }
285 
286 void
288 {
289  TcpGeneralTest::ConfigureEnvironment();
292  SetMTU(1500);
293 }
294 
295 void
297 {
298  TcpGeneralTest::ConfigureProperties();
304 }
305 
306 void
307 TcpLinuxRenoCongAvoidTest::CWndTrace(uint32_t oldValue, uint32_t newValue)
308 {
309  NS_LOG_FUNCTION(this << oldValue << newValue);
310  m_lastCwnd = newValue;
311  if (m_initial)
312  {
313  m_initial = false;
314  NS_TEST_ASSERT_MSG_EQ(newValue,
316  "The first update is for ACK of SYN and should initialize cwnd");
317  return;
318  }
319 
320  if ((newValue >= m_initialSSThresh * m_segmentSize) && !m_inCongAvoidance &&
321  (oldValue != m_initialSSThresh))
322  {
323  m_inCongAvoidance = true;
324  m_inSlowStartPhase = false;
325  return;
326  }
327 
328  if (m_inSlowStartPhase)
329  {
330  return;
331  }
332 
333  m_increment = newValue - oldValue;
334 
335  NS_TEST_ASSERT_MSG_EQ(m_increment, m_segmentSize, "Increase different than segsize");
336 }
337 
338 void
340 {
341  NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
342 }
343 
344 void
346 {
347  NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
348 }
349 
350 void
352 {
353  NS_LOG_FUNCTION(this << p << h << who);
354 }
355 
356 void
358 {
359  NS_LOG_FUNCTION(this << p << h << who);
360 }
361 
362 void
364 {
367  "Congestion window did not evolve as expected");
368  TcpGeneralTest::DoTeardown(); // call up to base class method to finish
369 }
370 
377 {
378  public:
380  : TestSuite("tcp-linux-reno-test", UNIT)
381  {
382  TypeId cong_control_type = TcpLinuxReno::GetTypeId();
383  // Test the behavior of Slow Start phase with small segment size
384  // (524 bytes) and delayed acknowledgement of 1 and 2 segments
385  //
386  // Expected data pattern starting at simulation time 10:
387  // (cwnd = 2 segments) 1 ->
388  // (cwnd = 2 segments) 2 ->
389  // (time 10.01s) <- ACK of 1
390  // cwnd increased to 3 segments; send two more
391  // (cwnd = 3 segments) 3 ->
392  // (cwnd = 3 segments) 4 ->
393  // (time 10.011s) <- ACK of 2
394  // cwnd increased to 4 segments; send two more
395  // (cwnd = 4 segments) 5 ->
396  // (cwnd = 4 segments) 6 ->
397  // (time 10.02s) <- ACK of 3
398  // cwnd increased to 5 segments; send two more but only one more to send
399  // (cwnd = 5 segments) 7+FIN ->
400  // <- ACK of 4
401  // <- ACK of 5
402  // <- ACK of 6
403  // <- ACK of 7
404  // cwnd should be at 9 segments
405  // <- FIN/ACK
406  AddTestCase(
407  new TcpLinuxRenoSSTest(524, // segment size
408  524, // socket send size
409  7, // socket sends (i.e. packets)
410  2, // initial cwnd
411  1, // delayed ack count
412  9 * 524, // expected final cWnd
413  cong_control_type,
414  "Slow Start MSS = 524, socket send size = 524, delack = 1 " +
415  cong_control_type.GetName()),
416  TestCase::QUICK);
417 
418  // Next, enabling delayed acks should not have an effect on the final
419  // cWnd achieved
420  AddTestCase(
421  new TcpLinuxRenoSSTest(524, // segment size
422  524, // socket send size
423  7, // socket sends
424  2, // initial cwnd
425  2, // delayed ack count
426  9 * 524, // expected final cWnd
427  cong_control_type,
428  "Slow Start MSS = 524, socket send size = 524, delack = 2 " +
429  cong_control_type.GetName()),
430  TestCase::QUICK);
431 
432  // Test the behavior of Slow Start phase with standard segment size
433  // (1500 bytes) and delayed acknowledgement of 1 and 2 segments
434  //
435  // We still expect m_cWnd to end up at 9 segments
436  AddTestCase(
437  new TcpLinuxRenoSSTest(1500, // segment size
438  1500, // socket send size
439  7, // socket sends
440  2, // initial cwnd
441  1, // delayed ack count
442  9 * 1500, // expected final cWnd
443  cong_control_type,
444  "Slow Start MSS = 1500, socket send size = 524, delack = 1 " +
445  cong_control_type.GetName()),
446  TestCase::QUICK);
447 
448  // Enable delayed acks; we still expect m_cWnd to end up at 9 segments
449  AddTestCase(
450  new TcpLinuxRenoSSTest(1500, // segment size
451  1500, // socket send size
452  7, // socket sends
453  2, // initial cwnd
454  2, // delayed ack count
455  9 * 1500, // expected final cWnd
456  cong_control_type,
457  "Slow Start MSS = 1500, socket send size = 524, delack = 2 " +
458  cong_control_type.GetName()),
459  TestCase::QUICK);
460 
461  // Test the behavior of Congestion Avoidance phase with small segment size
462  // (524 bytes) and delayed acknowledgement of 1 and 2. One important thing
463  // to confirm is that delayed ACK behavior does not affect the congestion
464  // window growth and final value because LinuxReno TCP counts segments acked
465  //
466  // Expected data pattern starting at simulation time 10:
467  // (cwnd = 1 segment) 1 ->
468  // (time 11s) <- ACK of 1
469  // (cwnd = 2 slow start) 2 ->
470  // (can send one more ) 3 ->
471  // (time 12s ) <- ACK of 2
472  // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
473  // snd_cwnd_count will be increased to 1, but less than current window 2
474  // send one new segment to replace the one that was acked
475  // (cwnd = 2 CA ) 4 ->
476  // (again, time 12s ) <- ACK of 3
477  // at this ACK, snd_cwnd >= ssthresh of 2, so stay in CongestionAvoidance
478  // snd_cwnd_count (m_cWndCnt) will be increased to 2, equal to w
479  // We can increase cWnd to three segments and reset snd_cwnd_count
480  // 5 ->
481  // 6+FIN ->
482  // (time 13s ) <- ACK of 4
483  // increase m_cWndCnt to 1
484  // (time 13s ) <- ACK of 5
485  // increase m_cWndCnt to 2
486  // (time 13s ) <- ACK of 6
487  // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
488  // Final value of m_cWnd should be 4 * 524 = 2096
490  524, // segment size
491  524, // socket send size
492  6, // socket sends
493  1, // initial cwnd
494  2 * 524, // initial ssthresh
495  1, // delayed ack count
496  4 * 524, // expected final cWnd
497  cong_control_type,
498  "Congestion Avoidance MSS = 524, socket send size = 524, delack = 1 " +
499  cong_control_type.GetName()),
500  TestCase::QUICK);
501 
502  // Repeat with delayed acks enabled: should result in same final cWnd
503  // Expected data pattern starting at simulation time 10:
504  // (cwnd = 1 segment) 1 ->
505  // (time 11s) <- ACK of 1, ns-3 will always ack 1st seg
506  // (cwnd = 2 slow start) 2 ->
507  // (can send one more ) 3 ->
508  // (time 12s ) <- ACK of 3 (combined ack of 2 segments)
509  // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
510  // snd_cwnd_count will be increased to 1+1 = current window 2
511  // send one new segment to replace the one that was acked
512  // (cwnd = 3 CA ) 4 ->
513  // (cwnd = 3 CA ) 5 ->
514  // (cwnd = 3 CA ) 6 ->
515  // (time 13s ) <- ACK of 5 (combined ack of 2 segments)
516  // (time 13s ) <- ACK of 6 (ack of 1 segment due to FIN)
517  // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
518  // Final value of m_cWnd should be 4 * 524 = 2096
520  524, // segment size
521  524, // socket send size
522  6, // socket sends
523  1, // initial cwnd
524  2, // initial ssthresh
525  2, // delayed ack count
526  4 * 524, // expected final cWnd
527  cong_control_type,
528  "Congestion Avoidance MSS = 524, socket send size = 524, delack = 2 " +
529  cong_control_type.GetName()),
530  TestCase::QUICK);
531 
532  // Test the behavior of Congestion Avoidance phase with standard segment size (i.e 1500
533  // bytes) and delayed acknowledgement of 1 and 2 Test the behavior of Congestion Avoidance
534  // phase with standard segment size (1500 bytes) and delayed acknowledgement of 1 and 2.
535  // This should result in the same pattern of segment exchanges as
536  // above.
538  1500, // segment size
539  1500, // socket send size
540  6, // socket sends
541  1, // initial cwnd
542  2, // initial ssthresh
543  1, // delayed ack count
544  4 * 1500, // expected final cWnd
545  cong_control_type,
546  "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 1 " +
547  cong_control_type.GetName()),
548  TestCase::QUICK);
549 
551  1500, // segment size
552  1500, // socket send size
553  6, // socket sends
554  1, // initial cwnd
555  2, // initial ssthresh
556  2, // delayed ack count
557  4 * 1500, // expected final cWnd
558  cong_control_type,
559  "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 2 " +
560  cong_control_type.GetName()),
561  TestCase::QUICK);
562  }
563 };
564 
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
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
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
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
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
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
TestSuite for the behavior of Linux Reno.
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.
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.
void SetSegmentSize(SocketWho who, uint32_t segmentSize)
Forcefully set the segment size.
Header for the Transmission Control Protocol.
Definition: tcp-header.h:47
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:301
A suite of tests to run.
Definition: test.h:1256
@ UNIT
This test suite implements a Unit Test.
Definition: test.h:1265
a unique identifier for an interface.
Definition: type-id.h:59
std::string GetName() const
Get the name.
Definition: type-id.cc:991
uint32_t segmentSize
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#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:275
#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:144
#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:750
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
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.