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  m_isFullCwndSent = (!canPacketBeSent || m_curCwnd == 0);
341  NS_LOG_DEBUG("Next expected interval (s): " << m_nextPacketInterval.GetSeconds());
342  }
343 }
344 
345 void
347 {
348  NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
349 }
350 
351 void
353 {
354  NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
355 }
356 
357 void
359 {
360  if (who == SENDER)
361  {
362  m_event.Cancel();
363  }
364 }
365 
372 {
373  public:
375  : TestSuite("tcp-pacing-test", UNIT)
376  {
377  uint16_t pacingSsRatio = 200;
378  uint16_t pacingCaRatio = 120;
379  uint32_t segmentSize = 1000;
380  uint32_t packetSize = 1000;
381  uint32_t numPackets = 40;
382  uint32_t delAckMaxCount = 1;
383  TypeId tid = TcpNewReno::GetTypeId();
384  uint32_t ssThresh = 1e9; // default large value
385  bool paceInitialWindow = false;
386  std::string description;
387 
388  description = std::string("Pacing case 1: Slow start only, no initial pacing");
390  packetSize,
391  numPackets,
392  pacingSsRatio,
393  pacingCaRatio,
394  ssThresh,
395  paceInitialWindow,
396  delAckMaxCount,
397  tid,
398  description),
399  TestCase::QUICK);
400 
401  paceInitialWindow = true;
402  description = std::string("Pacing case 2: Slow start only, initial pacing");
404  packetSize,
405  numPackets,
406  pacingSsRatio,
407  pacingCaRatio,
408  ssThresh,
409  paceInitialWindow,
410  delAckMaxCount,
411  tid,
412  description),
413  TestCase::QUICK);
414 
415  // set ssThresh to some smaller value to check that pacing
416  // slows down in second half of slow start, then transitions to CA
417  description = std::string("Pacing case 3: Slow start, followed by transition to Congestion "
418  "avoidance, no initial pacing");
419  paceInitialWindow = false;
420  ssThresh = 40;
421  numPackets = 60;
423  packetSize,
424  numPackets,
425  pacingSsRatio,
426  pacingCaRatio,
427  ssThresh,
428  paceInitialWindow,
429  delAckMaxCount,
430  tid,
431  description),
432  TestCase::QUICK);
433 
434  // Repeat tests, but with more typical delAckMaxCount == 2
435  delAckMaxCount = 2;
436  paceInitialWindow = false;
437  ssThresh = 1e9;
438  numPackets = 40;
439  description =
440  std::string("Pacing case 4: Slow start only, no initial pacing, delayed ACKs");
442  packetSize,
443  numPackets,
444  pacingSsRatio,
445  pacingCaRatio,
446  ssThresh,
447  paceInitialWindow,
448  delAckMaxCount,
449  tid,
450  description),
451  TestCase::QUICK);
452 
453  paceInitialWindow = true;
454  description = std::string("Pacing case 5: Slow start only, initial pacing, delayed ACKs");
456  packetSize,
457  numPackets,
458  pacingSsRatio,
459  pacingCaRatio,
460  ssThresh,
461  paceInitialWindow,
462  delAckMaxCount,
463  tid,
464  description),
465  TestCase::QUICK);
466 
467  description = std::string("Pacing case 6: Slow start, followed by transition to Congestion "
468  "avoidance, no initial pacing, delayed ACKs");
469  paceInitialWindow = false;
470  ssThresh = 40;
471  numPackets = 60;
473  packetSize,
474  numPackets,
475  pacingSsRatio,
476  pacingCaRatio,
477  ssThresh,
478  paceInitialWindow,
479  delAckMaxCount,
480  tid,
481  description),
482  TestCase::QUICK);
483  }
484 };
485 
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:47
uint8_t GetFlags() const
Get the flags.
Definition: tcp-header.cc:148
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
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:403
a unique identifier for an interface.
Definition: type-id.h:59
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:305
#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:1362
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
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 TcpPacingTestSuite g_tcpPacingTest
Static variable for test initialization.
static const uint32_t packetSize
Packet size generated at the AP.