A Discrete-Event Network Simulator
API
tcp-pacing-test.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020 NITK Surathkal
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  * Authors: Deepak Kumaraswamy <deepakkavoor99@gmail.com>
18  *
19  */
20 #include "tcp-general-test.h"
21 
22 #include "ns3/config.h"
23 #include "ns3/log.h"
24 #include "ns3/simple-channel.h"
25 #include "ns3/test.h"
26 
27 using namespace ns3;
28 
29 NS_LOG_COMPONENT_DEFINE("TcpPacingTestSuite");
30 
90 {
91  public:
105  TcpPacingTest(uint32_t segmentSize,
106  uint32_t packetSize,
107  uint32_t packets,
108  uint16_t pacingSsRatio,
109  uint16_t pacingCaRatio,
110  uint32_t ssThresh,
111  bool paceInitialWindow,
112  uint32_t delAckMaxCount,
113  const TypeId& congControl,
114  const std::string& desc);
115 
116  protected:
117  void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
118  void RttTrace(Time oldTime, Time newTime) override;
119  void BytesInFlightTrace(uint32_t oldValue, uint32_t newValue) override;
120  void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
121  void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
122  void QueueDrop(SocketWho who) override;
123  void PhyDrop(SocketWho who) override;
124  void NormalClose(SocketWho who) override;
125 
129  virtual void UpdateExpectedInterval();
130 
131  void ConfigureEnvironment() override;
132  void ConfigureProperties() override;
133 
134  private:
135  uint32_t m_segmentSize;
136  uint32_t m_packetSize;
137  uint32_t m_packets;
139  bool m_initial;
140  uint32_t m_initialCwnd;
141  uint32_t m_curCwnd;
144  uint32_t m_bytesInFlight;
146  uint16_t m_pacingSsRatio;
147  uint16_t m_pacingCaRatio;
148  uint32_t m_ssThresh;
150  uint32_t m_delAckMaxCount;
155  uint32_t m_packetsSent;
160 };
161 
163  uint32_t packetSize,
164  uint32_t packets,
165  uint16_t pacingSsRatio,
166  uint16_t pacingCaRatio,
167  uint32_t ssThresh,
168  bool paceInitialWindow,
169  uint32_t delAckMaxCount,
170  const TypeId& typeId,
171  const std::string& desc)
172  : TcpGeneralTest(desc),
173  m_segmentSize(segmentSize),
174  m_packetSize(packetSize),
175  m_packets(packets),
176  m_initial(true),
177  m_initialCwnd(10),
178  m_curCwnd(0),
179  m_isFullCwndSent(true),
180  m_bytesInFlight(0),
181  m_prevTxTime(0),
182  m_pacingSsRatio(pacingSsRatio),
183  m_pacingCaRatio(pacingCaRatio),
184  m_ssThresh(ssThresh),
185  m_paceInitialWindow(paceInitialWindow),
186  m_delAckMaxCount(delAckMaxCount),
187  m_isConnAboutToEnd(false),
188  m_transmissionStartTime(Seconds(0)),
189  m_expectedInterval(Seconds(0)),
190  m_packetsSent(0),
191  m_nextPacketInterval(Seconds(0)),
192  m_tracedRtt(Seconds(0))
193 {
194  m_congControlTypeId = typeId;
195 }
196 
197 void
199 {
200  TcpGeneralTest::ConfigureEnvironment();
204  SetMTU(1500);
207 }
208 
209 void
211 {
212  TcpGeneralTest::ConfigureProperties();
216  SetPacingStatus(SENDER, true);
219  NS_LOG_DEBUG("segSize: " << m_segmentSize << " ssthresh: " << m_ssThresh
220  << " paceInitialWindow: " << m_paceInitialWindow << " delAckMaxCount "
221  << m_delAckMaxCount);
222 }
223 
224 void
226 {
227  NS_LOG_FUNCTION(this << oldTime << newTime);
228  m_tracedRtt = newTime;
230 }
231 
232 void
233 TcpPacingTest::CWndTrace(uint32_t oldValue, uint32_t newValue)
234 {
235  NS_LOG_FUNCTION(this << oldValue << newValue);
236  m_curCwnd = newValue;
237  if (m_initial)
238  {
239  m_initial = false;
240  }
241  // CWndTrace () is called after Rx ()
242  // Therefore, call UpdateExpectedInterval () here instead of in Rx ()
244 }
245 
246 void
247 TcpPacingTest::BytesInFlightTrace(uint32_t oldValue, uint32_t newValue)
248 {
249  m_bytesInFlight = newValue;
250 }
251 
252 void
254 {
255  double_t factor;
256  Time rtt = 2 * GetPropagationDelay();
257  if (m_curCwnd < m_ssThresh / 2)
258  {
259  factor = static_cast<double>(m_pacingSsRatio) / 100;
260  }
261  else
262  {
263  factor = static_cast<double>(m_pacingCaRatio) / 100;
264  }
265 
267  {
268  // If initial cwnd is not paced, we expect packet pacing interval to be zero
270  }
271  else
272  {
273  // Use the estimate according to update equation
276  }
277 }
278 
279 void
281 {
282  if (who == SENDER)
283  {
284  uint8_t flags = h.GetFlags();
285  uint8_t hasFin = flags & TcpHeader::FIN;
286  uint8_t hasAck = flags & TcpHeader::ACK;
287  if (hasFin && hasAck)
288  {
289  m_isConnAboutToEnd = true;
290  NS_LOG_DEBUG("Sender received a FIN/ACK packet");
291  }
292  else
293  {
294  m_isConnAboutToEnd = false;
295  NS_LOG_DEBUG("Sender received an ACK packet");
296  }
297  }
298 }
299 
300 void
302 {
303  NS_LOG_FUNCTION(this << p << h << who);
304 
305  if (who == SENDER)
306  {
307  m_packetsSent++;
308  // Start pacing checks from the second data packet onwards because
309  // an interval to check does not exist for the first data packet.
310  // The first two (non-data) packets correspond to SYN and an
311  // empty ACK, respectively, so start checking after three packets are sent
312  bool beyondInitialDataSegment = (m_packetsSent > 3);
313  Time actualInterval = Simulator::Now() - m_prevTxTime;
314  NS_LOG_DEBUG("TX sent: packetsSent: " << m_packetsSent << " fullCwnd: " << m_isFullCwndSent
315  << " nearEnd: " << m_isConnAboutToEnd
316  << " beyondInitialDataSegment "
317  << beyondInitialDataSegment);
318  if (!m_isFullCwndSent && !m_isConnAboutToEnd && beyondInitialDataSegment)
319  {
320  // Consider a small error margin, and ensure that the actual and expected intervals lie
321  // within this error
322  Time errorMargin = NanoSeconds(10);
324  std::abs((actualInterval - m_nextPacketInterval).GetSeconds()),
325  errorMargin.GetSeconds(),
326  "Packet delivery in slow start didn't match pacing rate");
327  NS_LOG_DEBUG("Pacing Check: interval (s): "
328  << actualInterval.GetSeconds() << " expected interval (s): "
329  << m_nextPacketInterval.GetSeconds() << " difference (s): "
330  << std::abs((actualInterval - m_nextPacketInterval).GetSeconds())
331  << " errorMargin (s): " << errorMargin.GetSeconds());
332  }
333 
335  // bytesInFlight isn't updated yet. Its trace is called after Tx
336  // so add an additional m_segmentSize to bytesInFlight
337  uint32_t soonBytesInFlight = m_bytesInFlight + m_segmentSize;
338  bool canPacketBeSent = ((m_curCwnd - soonBytesInFlight) >= m_segmentSize);
339  if (!canPacketBeSent || (m_curCwnd == 0))
340  {
341  m_isFullCwndSent = true;
342  }
343  else
344  {
345  m_isFullCwndSent = false;
346  }
348  NS_LOG_DEBUG("Next expected interval (s): " << m_nextPacketInterval.GetSeconds());
349  }
350 }
351 
352 void
354 {
355  NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
356 }
357 
358 void
360 {
361  NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
362 }
363 
364 void
366 {
367  if (who == SENDER)
368  {
369  m_event.Cancel();
370  }
371 }
372 
379 {
380  public:
382  : TestSuite("tcp-pacing-test", UNIT)
383  {
384  uint16_t pacingSsRatio = 200;
385  uint16_t pacingCaRatio = 120;
386  uint32_t segmentSize = 1000;
387  uint32_t packetSize = 1000;
388  uint32_t numPackets = 40;
389  uint32_t delAckMaxCount = 1;
390  TypeId tid = TcpNewReno::GetTypeId();
391  uint32_t ssThresh = 1e9; // default large value
392  bool paceInitialWindow = false;
393  std::string description;
394 
395  description = std::string("Pacing case 1: Slow start only, no initial pacing");
397  packetSize,
398  numPackets,
399  pacingSsRatio,
400  pacingCaRatio,
401  ssThresh,
402  paceInitialWindow,
403  delAckMaxCount,
404  tid,
405  description),
406  TestCase::QUICK);
407 
408  paceInitialWindow = true;
409  description = std::string("Pacing case 2: Slow start only, initial pacing");
411  packetSize,
412  numPackets,
413  pacingSsRatio,
414  pacingCaRatio,
415  ssThresh,
416  paceInitialWindow,
417  delAckMaxCount,
418  tid,
419  description),
420  TestCase::QUICK);
421 
422  // set ssThresh to some smaller value to check that pacing
423  // slows down in second half of slow start, then transitions to CA
424  description = std::string("Pacing case 3: Slow start, followed by transition to Congestion "
425  "avoidance, no initial pacing");
426  paceInitialWindow = false;
427  ssThresh = 40;
428  numPackets = 60;
430  packetSize,
431  numPackets,
432  pacingSsRatio,
433  pacingCaRatio,
434  ssThresh,
435  paceInitialWindow,
436  delAckMaxCount,
437  tid,
438  description),
439  TestCase::QUICK);
440 
441  // Repeat tests, but with more typical delAckMaxCount == 2
442  delAckMaxCount = 2;
443  paceInitialWindow = false;
444  ssThresh = 1e9;
445  numPackets = 40;
446  description =
447  std::string("Pacing case 4: Slow start only, no initial pacing, delayed ACKs");
449  packetSize,
450  numPackets,
451  pacingSsRatio,
452  pacingCaRatio,
453  ssThresh,
454  paceInitialWindow,
455  delAckMaxCount,
456  tid,
457  description),
458  TestCase::QUICK);
459 
460  paceInitialWindow = true;
461  description = std::string("Pacing case 5: Slow start only, initial pacing, delayed ACKs");
463  packetSize,
464  numPackets,
465  pacingSsRatio,
466  pacingCaRatio,
467  ssThresh,
468  paceInitialWindow,
469  delAckMaxCount,
470  tid,
471  description),
472  TestCase::QUICK);
473 
474  description = std::string("Pacing case 6: Slow start, followed by transition to Congestion "
475  "avoidance, no initial pacing, delayed ACKs");
476  paceInitialWindow = false;
477  ssThresh = 40;
478  numPackets = 60;
480  packetSize,
481  numPackets,
482  pacingSsRatio,
483  pacingCaRatio,
484  ssThresh,
485  paceInitialWindow,
486  delAckMaxCount,
487  tid,
488  description),
489  TestCase::QUICK);
490  }
491 };
492 
Test the behavior of TCP pacing.
uint32_t m_initialCwnd
Initial value of cWnd.
void CWndTrace(uint32_t oldValue, uint32_t newValue) override
uint16_t m_pacingSsRatio
Pacing factor during Slow Start.
void ConfigureProperties() override
Change the configuration of the socket properties.
uint32_t m_delAckMaxCount
Delayed ack count for receiver.
Time m_tracedRtt
Traced value of RTT, which may be different from the environment RTT in case of delayed ACKs.
uint32_t m_ssThresh
Slow start threshold.
Time m_expectedInterval
Theoretical estimate of the time at which next packet is scheduled for transmission.
void RttTrace(Time oldTime, Time newTime) override
void Tx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet transmitted down to IP layer.
Time m_nextPacketInterval
Time maintained by Tx () trace about interval at which next packet will be sent.
void NormalClose(SocketWho who) override
uint32_t m_segmentSize
Segment size.
void Rx(const Ptr< const Packet > p, const TcpHeader &h, SocketWho who) override
Packet received from IP layer.
void PhyDrop(SocketWho who) override
Time m_prevTxTime
Time when Tx was previously called.
void BytesInFlightTrace(uint32_t oldValue, uint32_t newValue) override
Time m_transmissionStartTime
Time at which sender starts data transmission.
void QueueDrop(SocketWho who) override
bool m_isConnAboutToEnd
True when sender receives a FIN/ACK from receiver.
uint16_t m_pacingCaRatio
Pacing factor during Congestion Avoidance.
uint32_t m_bytesInFlight
Current bytes in flight.
bool m_paceInitialWindow
True if initial window should be paced.
uint32_t m_packets
Number of packets.
EventId m_event
Check event.
uint32_t m_curCwnd
Current sender cWnd.
uint32_t m_packetsSent
Number of packets sent by sender so far.
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.
bool m_isFullCwndSent
True if all bytes for that cWnd is sent and sender is waiting for an ACK.
void ConfigureEnvironment() override
Change the configuration of the environment.
virtual void UpdateExpectedInterval()
Update the expected interval at which next packet will be sent.
bool m_initial
True on first run.
uint32_t m_packetSize
Size of the packets.
TestSuite for the behavior of TCP pacing.
An identifier for simulation events.
Definition: event-id.h:55
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:55
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.
void SetPaceInitialWindow(SocketWho who, bool paceWindow)
Enable or disable pacing of the initial window.
void SetMTU(uint32_t mtu)
MTU of the bottleneck link.
Time GetPropagationDelay() const
Get the channel Propagation Delay.
void SetAppPktInterval(Time pktInterval)
Interval between app-generated packet.
TypeId m_congControlTypeId
Congestion control.
void SetInitialSsThresh(SocketWho who, uint32_t initialSsThresh)
Forcefully set the initial ssthresh.
void SetPacingStatus(SocketWho who, bool pacing)
Enable or disable pacing in the TCP socket.
void SetTransmitStart(Time startTime)
Set the initial time at which the application sends the first data packet.
void SetSegmentSize(SocketWho who, uint32_t segmentSize)
Forcefully set the segment size.
Header for the Transmission Control Protocol.
Definition: tcp-header.h:46
uint8_t GetFlags() const
Get the flags.
Definition: tcp-header.cc:167
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:305
A suite of tests to run.
Definition: test.h:1256
@ UNIT
This test suite implements a Unit Test.
Definition: test.h:1265
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:402
a unique identifier for an interface.
Definition: type-id.h:60
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_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Time Now()
create an ns3::Time instance which contains the current simulation time.
Definition: simulator.cc:296
#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 NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1372
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1336
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1348
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static TcpPacingTestSuite g_tcpPacingTest
Static variable for test initialization.
static const uint32_t packetSize
Packet size generated at the AP.