A Discrete-Event Network Simulator
API
tcp-pacing-test.cc
Go to the documentation of this file.
1 /* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2020 NITK Surathkal
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  * Authors: Deepak Kumaraswamy <deepakkavoor99@gmail.com>
19  *
20  */
21 #include "ns3/log.h"
22 #include "ns3/simple-channel.h"
23 #include "ns3/config.h"
24 #include "ns3/test.h"
25 #include "tcp-general-test.h"
26 
27 using namespace ns3;
28 
29 NS_LOG_COMPONENT_DEFINE ("TcpPacingTestSuite");
30 
90 class
92 {
93 public:
107  TcpPacingTest (uint32_t segmentSize, uint32_t packetSize,
108  uint32_t packets, uint16_t pacingSsRatio, uint16_t pacingCaRatio,
109  uint32_t ssThresh, bool paceInitialWindow, uint32_t delAckMaxCount,
110  const TypeId& congControl, const std::string &desc);
111 
112 protected:
113  virtual void CWndTrace (uint32_t oldValue, uint32_t newValue);
114  virtual void RttTrace (Time oldTime, Time newTime);
115  virtual void BytesInFlightTrace (uint32_t oldValue, uint32_t newValue);
116  virtual void Tx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who);
117  virtual void Rx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who);
118  virtual void QueueDrop (SocketWho who);
119  virtual void PhyDrop (SocketWho who);
120  virtual void NormalClose (SocketWho who);
121 
125  virtual void UpdateExpectedInterval (void);
126 
127  virtual void ConfigureEnvironment ();
128  virtual void ConfigureProperties ();
129 
130 private:
131  uint32_t m_segmentSize;
132  uint32_t m_packetSize;
133  uint32_t m_packets;
135  bool m_initial;
136  uint32_t m_initialCwnd;
137  uint32_t m_curCwnd;
139  uint32_t m_bytesInFlight;
141  uint16_t m_pacingSsRatio;
142  uint16_t m_pacingCaRatio;
143  uint32_t m_ssThresh;
145  uint32_t m_delAckMaxCount;
149  uint32_t m_packetsSent;
152 };
153 
154 
156  uint32_t packetSize,
157  uint32_t packets,
158  uint16_t pacingSsRatio,
159  uint16_t pacingCaRatio,
160  uint32_t ssThresh,
161  bool paceInitialWindow,
162  uint32_t delAckMaxCount,
163  const TypeId &typeId,
164  const std::string &desc)
165  : TcpGeneralTest (desc),
166  m_segmentSize (segmentSize),
167  m_packetSize (packetSize),
168  m_packets (packets),
169  m_initial (true),
170  m_initialCwnd (10),
171  m_curCwnd (0),
172  m_isFullCwndSent (true),
173  m_bytesInFlight (0),
174  m_prevTxTime (0),
175  m_pacingSsRatio (pacingSsRatio),
176  m_pacingCaRatio (pacingCaRatio),
177  m_ssThresh (ssThresh),
178  m_paceInitialWindow (paceInitialWindow),
179  m_delAckMaxCount (delAckMaxCount),
180  m_isConnAboutToEnd (false),
181  m_transmissionStartTime (Seconds (0)),
182  m_expectedInterval (Seconds (0)),
183  m_packetsSent (0),
184  m_nextPacketInterval (Seconds (0)),
185  m_tracedRtt (Seconds (0))
186 {
187  m_congControlTypeId = typeId;
188 }
189 
190 void
192 {
193  TcpGeneralTest::ConfigureEnvironment ();
197  SetMTU (1500);
200 }
201 
203 {
204  TcpGeneralTest::ConfigureProperties ();
208  SetPacingStatus (SENDER, true);
211  NS_LOG_DEBUG ("segSize: " << m_segmentSize << " ssthresh: " << m_ssThresh <<
212  " paceInitialWindow: " << m_paceInitialWindow << " delAckMaxCount " << m_delAckMaxCount);
213 }
214 
215 void
217 {
218  NS_LOG_FUNCTION (this << oldTime << newTime);
219  m_tracedRtt = newTime;
221 }
222 
223 void
224 TcpPacingTest::CWndTrace (uint32_t oldValue, uint32_t newValue)
225 {
226  NS_LOG_FUNCTION (this << oldValue << newValue);
227  m_curCwnd = newValue;
228  if (m_initial)
229  {
230  m_initial = false;
231  }
232  // CWndTrace () is called after Rx ()
233  // Therefore, call UpdateExpectedInterval () here instead of in Rx ()
235 }
236 
237 void
238 TcpPacingTest::BytesInFlightTrace (uint32_t oldValue, uint32_t newValue)
239 {
240  m_bytesInFlight = newValue;
241 }
242 
243 void
245 {
246  double_t factor;
247  Time rtt = 2 * GetPropagationDelay ();
248  if (m_curCwnd < m_ssThresh / 2)
249  {
250  factor = static_cast<double> (m_pacingSsRatio) / 100;
251  }
252  else
253  {
254  factor = static_cast<double> (m_pacingCaRatio) / 100;
255  }
256 
258  {
259  // If initial cwnd is not paced, we expect packet pacing interval to be zero
261  }
262  else
263  {
264  // Use the estimate according to update equation
266  }
267 }
268 
269 void
271 {
272  if (who == SENDER)
273  {
274  uint8_t flags = h.GetFlags ();
275  uint8_t hasFin = flags & TcpHeader::FIN;
276  uint8_t hasAck = flags & TcpHeader::ACK;
277  if (hasFin && hasAck)
278  {
279  m_isConnAboutToEnd = true;
280  NS_LOG_DEBUG ("Sender received a FIN/ACK packet");
281  }
282  else
283  {
284  m_isConnAboutToEnd = false;
285  NS_LOG_DEBUG ("Sender received an ACK packet");
286  }
287  }
288 }
289 
290 void
292 {
293  NS_LOG_FUNCTION (this << p << h << who);
294 
295  if (who == SENDER)
296  {
297  m_packetsSent++;
298  // Start pacing checks from the second data packet onwards because
299  // an interval to check does not exist for the first data packet.
300  // The first two (non-data) packets correspond to SYN and an
301  // empty ACK, respectively, so start checking after three packets are sent
302  bool beyondInitialDataSegment = (m_packetsSent > 3);
303  Time actualInterval = Simulator::Now () - m_prevTxTime;
304  NS_LOG_DEBUG ("TX sent: packetsSent: " << m_packetsSent << " fullCwnd: " << m_isFullCwndSent << " nearEnd: " <<
305  m_isConnAboutToEnd << " beyondInitialDataSegment " << beyondInitialDataSegment);
306  if (!m_isFullCwndSent && !m_isConnAboutToEnd && beyondInitialDataSegment)
307  {
308  // Consider a small error margin, and ensure that the actual and expected intervals lie within this error
309  Time errorMargin = NanoSeconds (10);
310  NS_TEST_ASSERT_MSG_LT_OR_EQ (std::abs ((actualInterval - m_nextPacketInterval).GetSeconds ()), errorMargin.GetSeconds (), "Packet delivery in slow start didn't match pacing rate");
311  NS_LOG_DEBUG ("Pacing Check: interval (s): " << actualInterval.GetSeconds () <<
312  " expected interval (s): " << m_nextPacketInterval.GetSeconds () <<
313  " difference (s): " << std::abs ((actualInterval - m_nextPacketInterval).GetSeconds ()) <<
314  " errorMargin (s): " << errorMargin.GetSeconds ());
315  }
316 
318  // bytesInFlight isn't updated yet. Its trace is called after Tx
319  // so add an additional m_segmentSize to bytesInFlight
320  uint32_t soonBytesInFlight = m_bytesInFlight + m_segmentSize;
321  bool canPacketBeSent = ((m_curCwnd - soonBytesInFlight) >= m_segmentSize);
322  if (!canPacketBeSent || (m_curCwnd == 0))
323  {
324  m_isFullCwndSent = true;
325  }
326  else
327  {
328  m_isFullCwndSent = false;
329  }
331  NS_LOG_DEBUG ("Next expected interval (s): " << m_nextPacketInterval.GetSeconds ());
332  }
333 }
334 
335 void
337 {
338  NS_FATAL_ERROR ("Drop on the queue; cannot validate congestion avoidance");
339 }
340 
341 void
343 {
344  NS_FATAL_ERROR ("Drop on the phy: cannot validate congestion avoidance");
345 }
346 
347 void
349 {
350  if (who == SENDER)
351  {
352  m_event.Cancel ();
353  }
354 }
355 
363 {
364 public:
365  TcpPacingTestSuite () : TestSuite ("tcp-pacing-test", UNIT)
366  {
367  uint16_t pacingSsRatio = 200;
368  uint16_t pacingCaRatio = 120;
369  uint32_t segmentSize = 1000;
370  uint32_t packetSize = 1000;
371  uint32_t numPackets = 40;
372  uint32_t delAckMaxCount = 1;
373  TypeId tid = TcpNewReno::GetTypeId ();
374  uint32_t ssThresh = 1e9; // default large value
375  bool paceInitialWindow = false;
376  std::string description;
377 
378  description = std::string ("Pacing case 1: Slow start only, no initial pacing");
379  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
380 
381  paceInitialWindow = true;
382  description = std::string ("Pacing case 2: Slow start only, initial pacing");
383  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
384 
385  // set ssThresh to some smaller value to check that pacing
386  // slows down in second half of slow start, then transitions to CA
387  description = std::string ("Pacing case 3: Slow start, followed by transition to Congestion avoidance, no initial pacing");
388  paceInitialWindow = false;
389  ssThresh = 40;
390  numPackets = 60;
391  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
392 
393  // Repeat tests, but with more typical delAckMaxCount == 2
394  delAckMaxCount = 2;
395  paceInitialWindow = false;
396  ssThresh = 1e9;
397  numPackets = 40;
398  description = std::string ("Pacing case 4: Slow start only, no initial pacing, delayed ACKs");
399  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
400 
401  paceInitialWindow = true;
402  description = std::string ("Pacing case 5: Slow start only, initial pacing, delayed ACKs");
403  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
404 
405  description = std::string ("Pacing case 6: Slow start, followed by transition to Congestion avoidance, no initial pacing, delayed ACKs");
406  paceInitialWindow = false;
407  ssThresh = 40;
408  numPackets = 60;
409  AddTestCase (new TcpPacingTest (segmentSize, packetSize, numPackets, pacingSsRatio, pacingCaRatio, ssThresh, paceInitialWindow, delAckMaxCount, tid, description), TestCase::QUICK);
410  }
411 };
412 
virtual void BytesInFlightTrace(uint32_t oldValue, uint32_t newValue)
Bytes in flight changes.
uint32_t m_ssThresh
Slow start threshold.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:103
#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.
uint32_t m_packetsSent
Number of packets sent by sender so far.
bool m_isConnAboutToEnd
True when sender receives a FIN/ACK from receiver.
Test the behavior of TCP pacing.
static const uint32_t packetSize
A suite of tests to run.
Definition: test.h:1343
double GetSeconds(void) const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:379
uint32_t segmentSize
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1286
void SetAppPktSize(uint32_t pktSize)
Set app packet size.
virtual void ConfigureProperties()
Change the configuration of the socket properties.
void SetTransmitStart(Time startTime)
Set the initial time at which the application sends the first data packet.
uint32_t m_bytesInFlight
Current bytes in flight.
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
void SetPaceInitialWindow(SocketWho who, bool paceWindow)
Enable or disable pacing of the initial window.
virtual void PhyDrop(SocketWho who)
Link drop.
TcpPacingTest(uint32_t segmentSize, uint32_t packetSize, uint32_t packets, uint16_t pacingSsRatio, uint16_t pacingCaRatio, uint32_t ssThresh, bool paceInitialWindow, uint32_t delAckMaxCount, const TypeId &congControl, const std::string &desc)
Constructor.
EventId m_event
Check event.
Time GetPropagationDelay() const
Get the channel Propagation Delay.
virtual void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet received from IP layer.
bool m_isFullCwndSent
True if all bytes for that cWnd is sent and sender is waiting for an ACK.
virtual void NormalClose(SocketWho who)
Socket closed normally.
Time m_nextPacketInterval
Time maintained by Tx () trace about interval at which next packet will be sent.
void SetDelAckMaxCount(SocketWho who, uint32_t count)
Forcefully set the delayed acknowledgement count.
Time m_transmissionStartTime
Time at which sender starts data transmission.
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1302
static TcpPacingTestSuite g_tcpPacingTest
Static variable for test initialization.
void SetMTU(uint32_t mtu)
MTU of the bottleneck link.
uint16_t m_pacingCaRatio
Pacing factor during Congestion Avoidance.
uint32_t m_packetSize
Size of the packets.
uint32_t m_packets
Number of packets.
Time m_tracedRtt
Traced value of RTT, which may be different from the environment RTT in case of delayed ACKs...
void SetPacingStatus(SocketWho who, bool pacing)
Enable or disable pacing in the TCP socket.
uint32_t m_delAckMaxCount
Delayed ack count for receiver.
virtual void CWndTrace(uint32_t oldValue, uint32_t newValue)
Tracks the congestion window changes.
void SetInitialSsThresh(SocketWho who, uint32_t initialSsThresh)
Forcefully set the initial ssthresh.
Time m_expectedInterval
Theoretical estimate of the time at which next packet is scheduled for transmission.
TestSuite for the behavior of TCP pacing.
virtual void ConfigureEnvironment()
Change the configuration of the environment.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
virtual void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who)
Packet transmitted down to IP layer.
Header for the Transmission Control Protocol.
Definition: tcp-header.h:44
virtual void UpdateExpectedInterval(void)
Update the expected interval at which next packet will be sent.
void SetAppPktCount(uint32_t pktCount)
Set app packet count.
uint32_t m_initialCwnd
Initial value of cWnd.
void SetAppPktInterval(Time pktInterval)
Interval between app-generated packet.
uint32_t m_segmentSize
Segment size.
bool m_initial
True on first run.
SocketWho
Used as parameter of methods, specifies on what node the caller is interested (e.g.
virtual void RttTrace(Time oldTime, Time newTime)
Rtt changes.
uint32_t m_curCwnd
Current sender cWnd.
General infrastructure for TCP testing.
uint8_t GetFlags() const
Get the flags.
Definition: tcp-header.cc:173
An identifier for simulation events.
Definition: event-id.h:53
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1278
void SetInitialCwnd(SocketWho who, uint32_t initialCwnd)
Forcefully set the initial cwnd.
void Cancel(void)
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:53
virtual void QueueDrop(SocketWho who)
Drop on the queue.
Time Now(void)
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:287
#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
a unique identifier for an interface.
Definition: type-id.h:58
Time m_prevTxTime
Time when Tx was previously called.
uint16_t m_pacingSsRatio
Pacing factor during Slow Start.
void SetPropagationDelay(Time propDelay)
Propagation delay of the bottleneck link.
bool m_paceInitialWindow
True if initial window should be paced.
TypeId m_congControlTypeId
Congestion control.