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