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 
32 // This unit test checks that the slow start and congestion avoidance
33 // behavior matches Linux behavior as follows:
34 // 1) in both slow start and congestion avoidance phases, presence or
35 // absence of delayed acks does not alter the window growth
36 // 2) in congestion avoidance phase, the arithmetic for counting the number
37 // of segments acked and deciding when to increment the congestion window
38 // (i.e. following the Linux function tcp_cong_avoid_ai()) is followed.
39 // Different segment sizes (524 bytes and 1500 bytes) are tested.
40 
41 class
43 {
44 public:
56  TcpLinuxRenoSSTest (uint32_t segmentSize, uint32_t packetSize, uint32_t packets,
57  uint32_t initialCwnd, uint32_t delayedAck,
58  uint32_t expectedCwnd,
59  TypeId& congControl, const std::string &desc);
60 
61 protected:
62  virtual void CWndTrace (uint32_t oldValue, uint32_t newValue);
63  void QueueDrop (SocketWho who);
64  void PhyDrop (SocketWho who);
65 
66  virtual void ConfigureEnvironment ();
67  virtual void ConfigureProperties ();
68  virtual void DoTeardown ();
69 
70  bool m_initial;
71 
72 private:
73  virtual void Tx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
74  virtual void Rx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
75  uint32_t m_segmentSize;
76  uint32_t m_packetSize;
77  uint32_t m_packets;
78  uint32_t m_initialCwnd;
79  uint32_t m_delayedAck;
80  uint32_t m_lastCwnd;
81  uint32_t m_expectedCwnd;
82 };
83 
85  uint32_t packetSize,
86  uint32_t packets,
87  uint32_t initialCwnd,
88  uint32_t delayedAck,
89  uint32_t expectedCwnd,
90  TypeId &typeId,
91  const std::string &desc)
92  : TcpGeneralTest (desc),
93  m_initial (true),
94  m_segmentSize (segmentSize),
95  m_packetSize (packetSize),
96  m_packets (packets),
97  m_initialCwnd (initialCwnd),
98  m_delayedAck (delayedAck),
99  m_lastCwnd (0),
100  m_expectedCwnd (expectedCwnd)
101 {
102  m_congControlTypeId = typeId;
103 }
104 
105 void
107 {
108  TcpGeneralTest::ConfigureEnvironment ();
112 }
113 
114 void
116 {
117  TcpGeneralTest::ConfigureProperties ();
122 }
123 
124 void
126 {
127  NS_FATAL_ERROR ("Drop on the queue; cannot validate slow start");
128 }
129 
130 void
132 {
133  NS_FATAL_ERROR ("Drop on the phy: cannot validate slow start");
134 }
135 
136 void
137 TcpLinuxRenoSSTest::CWndTrace (uint32_t oldValue, uint32_t newValue)
138 {
139  NS_LOG_FUNCTION (this << oldValue << newValue);
140  uint32_t segSize = GetSegSize (TcpGeneralTest::SENDER);
141  uint32_t increase = newValue - oldValue;
142  m_lastCwnd = newValue;
143 
144  if (m_initial)
145  {
146  m_initial = false;
147  NS_TEST_ASSERT_MSG_EQ (newValue, m_initialCwnd * m_segmentSize, "The first update is for ACK of SYN and should initialize cwnd");
148  return;
149  }
150 
151  // ACK for first data packet is received to speed up the connection
152  if (oldValue == m_initialCwnd * m_segmentSize)
153  {
154  return;
155  }
156 
157  NS_TEST_ASSERT_MSG_EQ (increase, m_delayedAck * segSize, "Increase different than segsize");
158  NS_TEST_ASSERT_MSG_LT_OR_EQ (newValue, GetInitialSsThresh (SENDER), "cWnd increased over ssth");
159 
160  NS_LOG_INFO ("Incremented cWnd by " << m_delayedAck * segSize << " bytes in Slow Start " <<
161  "achieving a value of " << newValue);
162 }
163 
164 void
166 {
167  NS_LOG_FUNCTION (this << p << h << who);
168 }
169 
170 void
172 {
173  NS_LOG_FUNCTION (this << p << h << who);
174 }
175 
176 
177 void
179 {
180  NS_TEST_ASSERT_MSG_EQ (m_lastCwnd, m_expectedCwnd, "Congestion window did not evolve as expected");
181  TcpGeneralTest::DoTeardown (); // call up to base class method to finish
182 }
183 
184 class
186 {
187 public:
201  uint32_t packets, uint32_t initialCwnd,
202  uint32_t initialSSThresh, uint32_t delayedAck,
203  uint32_t expectedCwnd,
204  TypeId& congControl, const std::string &desc);
205 
206 protected:
207  virtual void CWndTrace (uint32_t oldValue, uint32_t newValue);
208  virtual void QueueDrop (SocketWho who);
209  virtual void PhyDrop (SocketWho who);
210 
211  virtual void ConfigureEnvironment ();
212  virtual void ConfigureProperties ();
213  virtual void DoTeardown ();
214 
215 private:
216  virtual void Tx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
217  virtual void Rx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
218  uint32_t m_segmentSize;
219  uint32_t m_packetSize;
220  uint32_t m_packets;
221  uint32_t m_initialCwnd;
222  uint32_t m_initialSSThresh;
223  uint32_t m_delayedAck;
224  uint32_t m_lastCwnd;
225  uint32_t m_expectedCwnd;
226  uint32_t m_increment;
227  bool m_initial;
230 };
231 
232 
234  uint32_t packetSize,
235  uint32_t packets,
236  uint32_t initialCwnd,
237  uint32_t initialSSThresh,
238  uint32_t delayedAck,
239  uint32_t expectedCwnd,
240  TypeId &typeId,
241  const std::string &desc)
242  : TcpGeneralTest (desc),
243  m_segmentSize (segmentSize),
244  m_packetSize (packetSize),
245  m_packets (packets),
246  m_initialCwnd (initialCwnd),
247  m_initialSSThresh (initialSSThresh),
248  m_delayedAck (delayedAck),
249  m_lastCwnd (0),
250  m_expectedCwnd (expectedCwnd),
251  m_increment (0),
252  m_initial (true),
253  m_inCongAvoidance (false),
254  m_inSlowStartPhase (true)
255 {
256  m_congControlTypeId = typeId;
257 }
258 
259 void
261 {
262  TcpGeneralTest::ConfigureEnvironment ();
265  SetMTU (1500);
266 }
267 
269 {
270  TcpGeneralTest::ConfigureProperties ();
276 }
277 
278 void
279 TcpLinuxRenoCongAvoidTest::CWndTrace (uint32_t oldValue, uint32_t newValue)
280 {
281  NS_LOG_FUNCTION (this << oldValue << newValue);
282  m_lastCwnd = newValue;
283  if (m_initial)
284  {
285  m_initial = false;
286  NS_TEST_ASSERT_MSG_EQ (newValue, m_initialCwnd * m_segmentSize, "The first update is for ACK of SYN and should initialize cwnd");
287  return;
288  }
289 
290  if ((newValue >= m_initialSSThresh * m_segmentSize) && !m_inCongAvoidance && (oldValue != m_initialSSThresh))
291  {
292  m_inCongAvoidance = true;
293  m_inSlowStartPhase = false;
294  return;
295  }
296 
297  if (m_inSlowStartPhase)
298  {
299  return;
300  }
301 
302  m_increment = newValue - oldValue;
303 
304  NS_TEST_ASSERT_MSG_EQ (m_increment, m_segmentSize, "Increase different than segsize");
305 }
306 
307 void
309 {
310  NS_FATAL_ERROR ("Drop on the queue; cannot validate congestion avoidance");
311 }
312 
313 void
315 {
316  NS_FATAL_ERROR ("Drop on the phy: cannot validate congestion avoidance");
317 }
318 
319 void
321 {
322  NS_LOG_FUNCTION (this << p << h << who);
323 }
324 
325 void
327 {
328  NS_LOG_FUNCTION (this << p << h << who);
329 }
330 
331 void
333 {
334  NS_TEST_ASSERT_MSG_EQ (m_lastCwnd, m_expectedCwnd, "Congestion window did not evolve as expected");
335  TcpGeneralTest::DoTeardown (); // call up to base class method to finish
336 }
337 
345 {
346 public:
347  TcpLinuxRenoTestSuite () : TestSuite ("tcp-linux-reno-test", UNIT)
348  {
349  TypeId cong_control_type = TcpLinuxReno::GetTypeId ();
350  // Test the behavior of Slow Start phase with small segment size
351  // (524 bytes) and delayed acknowledgement of 1 and 2 segments
352  //
353  // Expected data pattern starting at simulation time 10:
354  // (cwnd = 2 segments) 1 ->
355  // (cwnd = 2 segments) 2 ->
356  // (time 10.01s) <- ACK of 1
357  // cwnd increased to 3 segments; send two more
358  // (cwnd = 3 segments) 3 ->
359  // (cwnd = 3 segments) 4 ->
360  // (time 10.011s) <- ACK of 2
361  // cwnd increased to 4 segments; send two more
362  // (cwnd = 4 segments) 5 ->
363  // (cwnd = 4 segments) 6 ->
364  // (time 10.02s) <- ACK of 3
365  // cwnd increased to 5 segments; send two more but only one more to send
366  // (cwnd = 5 segments) 7+FIN ->
367  // <- ACK of 4
368  // <- ACK of 5
369  // <- ACK of 6
370  // <- ACK of 7
371  // cwnd should be at 9 segments
372  // <- FIN/ACK
373  AddTestCase (new TcpLinuxRenoSSTest (524, // segment size
374  524, // socket send size
375  7, // socket sends (i.e. packets)
376  2, // initial cwnd
377  1, // delayed ack count
378  9 * 524, // expected final cWnd
379  cong_control_type,
380  "Slow Start MSS = 524, socket send size = 524, delack = 1 " + cong_control_type.GetName ()),
381  TestCase::QUICK);
382 
383  // Next, enabling delayed acks should not have an effect on the final
384  // cWnd achieved
385  AddTestCase (new TcpLinuxRenoSSTest (524, // segment size
386  524, // socket send size
387  7, // socket sends
388  2, // initial cwnd
389  2, // delayed ack count
390  9 * 524, // expected final cWnd
391  cong_control_type,
392  "Slow Start MSS = 524, socket send size = 524, delack = 2 " + cong_control_type.GetName ()),
393  TestCase::QUICK);
394 
395  // Test the behavior of Slow Start phase with standard segment size
396  // (1500 bytes) and delayed acknowledgement of 1 and 2 segments
397  //
398  // We still expect m_cWnd to end up at 9 segments
399  AddTestCase (new TcpLinuxRenoSSTest (1500, // segment size
400  1500, // socket send size
401  7, // socket sends
402  2, // initial cwnd
403  1, // delayed ack count
404  9 * 1500, // expected final cWnd
405  cong_control_type,
406  "Slow Start MSS = 1500, socket send size = 524, delack = 1 " + cong_control_type.GetName ()),
407  TestCase::QUICK);
408 
409  // Enable delayed acks; we still expect m_cWnd to end up at 9 segments
410  AddTestCase (new TcpLinuxRenoSSTest (1500, // segment size
411  1500, // socket send size
412  7, // socket sends
413  2, // initial cwnd
414  2, // delayed ack count
415  9 * 1500, // expected final cWnd
416  cong_control_type,
417  "Slow Start MSS = 1500, socket send size = 524, delack = 2 " + cong_control_type.GetName ()),
418  TestCase::QUICK);
419 
420  // Test the behavior of Congestion Avoidance phase with small segment size
421  // (524 bytes) and delayed acknowledgement of 1 and 2. One important thing
422  // to confirm is that delayed ACK behavior does not affect the congestion
423  // window growth and final value because LinuxReno TCP counts segments acked
424  //
425  // Expected data pattern starting at simulation time 10:
426  // (cwnd = 1 segment) 1 ->
427  // (time 11s) <- ACK of 1
428  // (cwnd = 2 slow start) 2 ->
429  // (can send one more ) 3 ->
430  // (time 12s ) <- ACK of 2
431  // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
432  // snd_cwnd_count will be increased to 1, but less than current window 2
433  // send one new segment to replace the one that was acked
434  // (cwnd = 2 CA ) 4 ->
435  // (again, time 12s ) <- ACK of 3
436  // at this ACK, snd_cwnd >= ssthresh of 2, so stay in CongestionAvoidance
437  // snd_cwnd_count (m_cWndCnt) will be increased to 2, equal to w
438  // We can increase cWnd to three segments and reset snd_cwnd_count
439  // 5 ->
440  // 6+FIN ->
441  // (time 13s ) <- ACK of 4
442  // increase m_cWndCnt to 1
443  // (time 13s ) <- ACK of 5
444  // increase m_cWndCnt to 2
445  // (time 13s ) <- ACK of 6
446  // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
447  // Final value of m_cWnd should be 4 * 524 = 2096
448  AddTestCase (new TcpLinuxRenoCongAvoidTest (524, // segment size
449  524, // socket send size
450  6, // socket sends
451  1, // initial cwnd
452  2 * 524, // initial ssthresh
453  1, // delayed ack count
454  4 * 524, // expected final cWnd
455  cong_control_type,
456  "Congestion Avoidance MSS = 524, socket send size = 524, delack = 1 " + cong_control_type.GetName ()),
457  TestCase::QUICK);
458 
459  // Repeat with delayed acks enabled: should result in same final cWnd
460  // Expected data pattern starting at simulation time 10:
461  // (cwnd = 1 segment) 1 ->
462  // (time 11s) <- ACK of 1, ns-3 will always ack 1st seg
463  // (cwnd = 2 slow start) 2 ->
464  // (can send one more ) 3 ->
465  // (time 12s ) <- ACK of 3 (combined ack of 2 segments)
466  // at this ACK, snd_cwnd >= ssthresh of 2, so go into CongestionAvoidance
467  // snd_cwnd_count will be increased to 1+1 = current window 2
468  // send one new segment to replace the one that was acked
469  // (cwnd = 3 CA ) 4 ->
470  // (cwnd = 3 CA ) 5 ->
471  // (cwnd = 3 CA ) 6 ->
472  // (time 13s ) <- ACK of 5 (combined ack of 2 segments)
473  // (time 13s ) <- ACK of 6 (ack of 1 segment due to FIN)
474  // increase m_cWndCnt to 3, equal to window, so increase m_cWnd by one seg.
475  // Final value of m_cWnd should be 4 * 524 = 2096
476  AddTestCase (new TcpLinuxRenoCongAvoidTest (524, // segment size
477  524, // socket send size
478  6, // socket sends
479  1, // initial cwnd
480  2, // initial ssthresh
481  2, // delayed ack count
482  4 * 524, // expected final cWnd
483  cong_control_type,
484  "Congestion Avoidance MSS = 524, socket send size = 524, delack = 2 " + cong_control_type.GetName ()),
485  TestCase::QUICK);
486 
487  // Test the behavior of Congestion Avoidance phase with standard segment size (i.e 1500 bytes)
488  // and delayed acknowledgement of 1 and 2
489  // Test the behavior of Congestion Avoidance phase with standard segment
490  // size (1500 bytes) and delayed acknowledgement of 1 and 2.
491  // This should result in the same pattern of segment exchanges as
492  // above.
493  AddTestCase (new TcpLinuxRenoCongAvoidTest (1500, // segment size
494  1500, // socket send size
495  6, // socket sends
496  1, // initial cwnd
497  2, // initial ssthresh
498  1, // delayed ack count
499  4 * 1500, // expected final cWnd
500  cong_control_type,
501  "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 1 " + cong_control_type.GetName ()),
502  TestCase::QUICK);
503 
504  AddTestCase (new TcpLinuxRenoCongAvoidTest (1500, // segment size
505  1500, // socket send size
506  6, // socket sends
507  1, // initial cwnd
508  2, // initial ssthresh
509  2, // delayed ack count
510  4 * 1500, // expected final cWnd
511  cong_control_type,
512  "Congestion Avoidance MSS = 1500, socket send size = 1500, delack = 2 " + cong_control_type.GetName ()),
513  TestCase::QUICK);
514  }
515 };
516 
uint32_t m_packetSize
Packet size.
std::string GetName(void) const
Get the name.
Definition: type-id.cc:977
uint32_t m_lastCwnd
Last cWnd value reported.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
void SetSegmentSize(SocketWho who, uint32_t segmentSize)
Forcefully set the segment size.
virtual void CWndTrace(uint32_t oldValue, uint32_t newValue)
Tracks the congestion window changes.
static const uint32_t packetSize
virtual void DoTeardown()
Teardown the TCP test.
A suite of tests to run.
Definition: test.h:1343
virtual void QueueDrop(SocketWho who)
Drop on the queue.
uint32_t m_segmentSize
Segment size.
uint32_t segmentSize
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
virtual void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet received from IP layer.
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1286
virtual void DoTeardown()
Teardown the TCP test.
void SetAppPktSize(uint32_t pktSize)
Set app packet size.
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
TestSuite for the behavior of Linux Reno.
uint32_t m_delayedAck
Delayed Acknowledgement.
virtual void ConfigureProperties()
Change the configuration of the socket properties.
static TcpLinuxRenoTestSuite g_tcpLinuxRenoTestSuite
Static variable for test initialization.
uint32_t m_initialSSThresh
Initial slow start threshold (bytes)
virtual void PhyDrop(SocketWho who)
Link drop.
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.
uint32_t m_initialCwnd
Initial congestion window (segments)
void SetDelAckMaxCount(SocketWho who, uint32_t count)
Forcefully set the delayed acknowledgement count.
bool m_inSlowStartPhase
True if in slow start.
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
virtual void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet transmitted down to IP layer.
void SetMTU(uint32_t mtu)
MTU of the bottleneck link.
uint32_t GetInitialSsThresh(SocketWho who)
Get the initial slow start threshold.
#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:166
void SetInitialSsThresh(SocketWho who, uint32_t initialSsThresh)
Forcefully set the initial ssthresh.
uint32_t m_initialCwnd
Initial congestion window.
uint32_t m_segmentSize
Segment size.
virtual void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet transmitted down to IP layer.
uint32_t m_expectedCwnd
Expected final cWnd value.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
uint32_t m_packets
Number of packets to send to the socket.
uint32_t GetSegSize(SocketWho who)
Get the segment size of the node specified.
Header for the Transmission Control Protocol.
Definition: tcp-header.h:44
bool m_initial
True on first run.
uint32_t m_expectedCwnd
Expected final cWnd value.
void SetAppPktCount(uint32_t pktCount)
Set app packet count.
uint32_t m_lastCwnd
Last cWnd value reported.
virtual void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet received from IP layer.
uint32_t m_delayedAck
Delayed Acknowledgement.
virtual void ConfigureProperties()
Change the configuration of the socket properties.
SocketWho
Used as parameter of methods, specifies on what node the caller is interested (e.g.
bool m_inCongAvoidance
True if in congestion avoidance.
uint32_t m_increment
Congestion window increment.
General infrastructure for TCP testing.
uint32_t m_packets
Packet counter.
void SetInitialCwnd(SocketWho who, uint32_t initialCwnd)
Forcefully set the initial cwnd.
bool m_initial
First cycle flag.
virtual void ConfigureEnvironment()
Change the configuration of the environment.
#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:830
This test suite implements a Unit Test.
Definition: test.h:1353
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.
virtual void CWndTrace(uint32_t oldValue, uint32_t newValue)
Tracks the congestion window changes.
a unique identifier for an interface.
Definition: type-id.h:58
uint32_t m_packetSize
Size of the packets used in socket writes.
void PhyDrop(SocketWho who)
Link drop.
void QueueDrop(SocketWho who)
Drop on the queue.
void SetPropagationDelay(Time propDelay)
Propagation delay of the bottleneck link.
virtual void ConfigureEnvironment()
Change the configuration of the environment.
TypeId m_congControlTypeId
Congestion control.