A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-pacing-test.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 NITK Surathkal
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 *
6 * Authors: Deepak Kumaraswamy <deepakkavoor99@gmail.com>
7 *
8 */
9#include "tcp-general-test.h"
10
11#include "ns3/config.h"
12#include "ns3/log.h"
13#include "ns3/simple-channel.h"
14#include "ns3/test.h"
15
16using namespace ns3;
17
18NS_LOG_COMPONENT_DEFINE("TcpPacingTestSuite");
19
20/**
21 * @ingroup internet-test
22 *
23 * @brief Test the behavior of TCP pacing
24 *
25 * This test checks that packets are paced at correct intervals. The test
26 * uses a shadow pacing rate calculation assumed to match the internal
27 * pacing calculation. Therefore, if you modify the values of
28 * pacingSsRatio and pacingCaRatio herein, ensure that you also change
29 * values used in the TCP implementation to match.
30 *
31 * This test environment uses an RTT of 100ms
32 * The time interval between two consecutive packet transmissions is measured
33 * Pacing rate should be at least cwnd / rtt to transmit cwnd segments per
34 * rtt. Linux multiples this basic ratio by an additional factor, to yield
35 * pacingRate = (factor * cwnd) / rtt
36 * Since pacingRate can also be written as segmentSize / interval
37 * we can solve for interval = (segmentSize * rtt) / (factor * cwnd)
38 *
39 * The test checks whether the measured interval lies within a tolerance
40 * value of the expected interval. The tolerance or error margin
41 * was chosen to be 10 Nanoseconds, that could be due to delay introduced
42 * by the application's send process.
43 *
44 * This check should not be performed for a packet transmission after the
45 * sender has sent all bytes corresponding to the window and is awaiting an
46 * ACK from the receiver (corresponding to m_isFullCwndSent).
47 * Pacing check should be performed when the sender is actively sending packets from cwnd.
48 *
49 * The same argument applies when the sender has finished sending packets and is awaiting a
50 * FIN/ACK from the receiver, to send the final ACK
51 *
52 * As can be seen in TcpSocketBase::UpdatePacingRate (), different pacing
53 * ratios are used when cwnd < ssThresh / 2 and when cwnd > ssThresh / 2
54 *
55 * A few key points to note:
56 * - In TcpSocketBase, pacing rate is updated whenever an ACK is received.
57 *
58 * - The factors that could contribute to a different value of pacing rate include
59 * congestion window and RTT.
60 *
61 * - However, the cWnd trace is called after Rx () trace and we therefore
62 * update the expected interval in cWnd trace.
63 *
64 * - An RTT trace is also necessary, since using delayed ACKs may lead to a
65 * higher RTT measurement at the sender's end. The expected interval should
66 * be updated here as well.
67 *
68 * - When sending the current packet, TcpSocketBase automatically decides the time at
69 * which next packet should be sent. So, if say packet 3 is sent and an ACK is
70 * received before packet 4 is sent (thus updating the pacing rate), this does not change
71 * the time at which packet 4 is to be sent. When packet 4 is indeed sent later, the new
72 * pacing rate is used to decide when packet 5 will be sent. This behavior is captured in
73 * m_nextPacketInterval, which is not affected by change of pacing rate before the next
74 * packet is sent. The important observation here is to realize the contrast between
75 * m_expectedInterval and m_nextPacketInterval.
76 *
77 */
79{
80 public:
81 /**
82 * @brief Constructor.
83 * @param segmentSize Segment size at the TCP layer (bytes).
84 * @param packetSize Size of packets sent at the application layer (bytes).
85 * @param packets Number of packets.
86 * @param pacingSsRatio Pacing Ratio during Slow Start (multiplied by 100)
87 * @param pacingCaRatio Pacing Ratio during Congestion Avoidance (multiplied by 100)
88 * @param ssThresh slow start threshold (bytes)
89 * @param paceInitialWindow whether to pace the initial window
90 * @param delAckMaxCount Delayed ACK max count parameter
91 * @param congControl Type of congestion control.
92 * @param desc The test description.
93 */
96 uint32_t packets,
97 uint16_t pacingSsRatio,
98 uint16_t pacingCaRatio,
99 uint32_t ssThresh,
100 bool paceInitialWindow,
101 uint32_t delAckMaxCount,
102 const TypeId& congControl,
103 const std::string& desc);
104
105 protected:
106 void CWndTrace(uint32_t oldValue, uint32_t newValue) override;
107 void RttTrace(Time oldTime, Time newTime) override;
108 void BytesInFlightTrace(uint32_t oldValue, uint32_t newValue) override;
109 void Tx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
110 void Rx(const Ptr<const Packet> p, const TcpHeader& h, SocketWho who) override;
111 void QueueDrop(SocketWho who) override;
112 void PhyDrop(SocketWho who) override;
113 void NormalClose(SocketWho who) override;
114
115 /**
116 * @brief Update the expected interval at which next packet will be sent
117 */
118 virtual void UpdateExpectedInterval();
119
120 void ConfigureEnvironment() override;
121 void ConfigureProperties() override;
122
123 private:
124 uint32_t m_segmentSize; //!< Segment size
125 uint32_t m_packetSize; //!< Size of the packets
126 uint32_t m_packets; //!< Number of packets
127 EventId m_event; //!< Check event
128 bool m_initial; //!< True on first run
129 uint32_t m_initialCwnd; //!< Initial value of cWnd
130 uint32_t m_curCwnd; //!< Current sender cWnd
131 bool m_isFullCwndSent; //!< True if all bytes for that cWnd is sent and sender is waiting for an
132 //!< ACK
133 uint32_t m_bytesInFlight; //!< Current bytes in flight
134 Time m_prevTxTime; //!< Time when Tx was previously called
135 uint16_t m_pacingSsRatio; //!< Pacing factor during Slow Start
136 uint16_t m_pacingCaRatio; //!< Pacing factor during Congestion Avoidance
137 uint32_t m_ssThresh; //!< Slow start threshold
138 bool m_paceInitialWindow; //!< True if initial window should be paced
139 uint32_t m_delAckMaxCount; //!< Delayed ack count for receiver
140 bool m_isConnAboutToEnd; //!< True when sender receives a FIN/ACK from receiver
141 Time m_transmissionStartTime; //!< Time at which sender starts data transmission
142 Time m_expectedInterval; //!< Theoretical estimate of the time at which next packet is scheduled
143 //!< for transmission
144 uint32_t m_packetsSent; //!< Number of packets sent by sender so far
145 Time m_nextPacketInterval; //!< Time maintained by Tx () trace about interval at which next
146 //!< packet will be sent
147 Time m_tracedRtt; //!< Traced value of RTT, which may be different from the environment RTT in
148 //!< case of delayed ACKs
149};
150
153 uint32_t packets,
154 uint16_t pacingSsRatio,
155 uint16_t pacingCaRatio,
156 uint32_t ssThresh,
157 bool paceInitialWindow,
158 uint32_t delAckMaxCount,
159 const TypeId& typeId,
160 const std::string& desc)
161 : TcpGeneralTest(desc),
162 m_segmentSize(segmentSize),
163 m_packetSize(packetSize),
164 m_packets(packets),
165 m_initial(true),
166 m_initialCwnd(10),
167 m_curCwnd(0),
168 m_isFullCwndSent(true),
169 m_bytesInFlight(0),
170 m_prevTxTime(0),
171 m_pacingSsRatio(pacingSsRatio),
172 m_pacingCaRatio(pacingCaRatio),
173 m_ssThresh(ssThresh),
174 m_paceInitialWindow(paceInitialWindow),
175 m_delAckMaxCount(delAckMaxCount),
176 m_isConnAboutToEnd(false),
177 m_transmissionStartTime(),
178 m_expectedInterval(),
179 m_packetsSent(0),
180 m_nextPacketInterval(),
181 m_tracedRtt()
182{
183 m_congControlTypeId = typeId;
184}
185
186void
197
198void
212
213void
215{
216 NS_LOG_FUNCTION(this << oldTime << newTime);
217 m_tracedRtt = newTime;
219}
220
221void
223{
224 NS_LOG_FUNCTION(this << oldValue << newValue);
225 m_curCwnd = newValue;
226 if (m_initial)
227 {
228 m_initial = false;
229 }
230 // CWndTrace () is called after Rx ()
231 // Therefore, call UpdateExpectedInterval () here instead of in Rx ()
233}
234
235void
237{
238 m_bytesInFlight = newValue;
239}
240
241void
243{
244 double_t factor;
245 Time rtt = 2 * GetPropagationDelay();
246 if (m_curCwnd < m_ssThresh / 2)
247 {
248 factor = static_cast<double>(m_pacingSsRatio) / 100;
249 }
250 else
251 {
252 factor = static_cast<double>(m_pacingCaRatio) / 100;
253 }
254
256 {
257 // If initial cwnd is not paced, we expect packet pacing interval to be zero
259 }
260 else
261 {
262 // Use the estimate according to update equation
265 }
266}
267
268void
270{
271 if (who == SENDER)
272 {
273 uint8_t flags = h.GetFlags();
274 uint8_t hasFin = flags & TcpHeader::FIN;
275 uint8_t hasAck = flags & TcpHeader::ACK;
276 if (hasFin && hasAck)
277 {
278 m_isConnAboutToEnd = true;
279 NS_LOG_DEBUG("Sender received a FIN/ACK packet");
280 }
281 else
282 {
283 m_isConnAboutToEnd = false;
284 NS_LOG_DEBUG("Sender received an ACK packet");
285 }
286 }
287}
288
289void
291{
292 NS_LOG_FUNCTION(this << p << h << who);
293
294 if (who == SENDER)
295 {
297 // Start pacing checks from the second data packet onwards because
298 // an interval to check does not exist for the first data packet.
299 // The first two (non-data) packets correspond to SYN and an
300 // empty ACK, respectively, so start checking after three packets are sent
301 bool beyondInitialDataSegment = (m_packetsSent > 3);
302 Time actualInterval = Simulator::Now() - m_prevTxTime;
303 NS_LOG_DEBUG("TX sent: packetsSent: " << m_packetsSent << " fullCwnd: " << m_isFullCwndSent
304 << " nearEnd: " << m_isConnAboutToEnd
305 << " beyondInitialDataSegment "
306 << beyondInitialDataSegment);
307 if (!m_isFullCwndSent && !m_isConnAboutToEnd && beyondInitialDataSegment)
308 {
309 // Consider a small error margin, and ensure that the actual and expected intervals lie
310 // within this error
311 Time errorMargin = NanoSeconds(10);
313 std::abs((actualInterval - m_nextPacketInterval).GetSeconds()),
314 errorMargin.GetSeconds(),
315 "Packet delivery in slow start didn't match pacing rate");
316 NS_LOG_DEBUG("Pacing Check: interval (s): "
317 << actualInterval.GetSeconds() << " expected interval (s): "
318 << m_nextPacketInterval.GetSeconds() << " difference (s): "
319 << std::abs((actualInterval - m_nextPacketInterval).GetSeconds())
320 << " errorMargin (s): " << errorMargin.GetSeconds());
321 }
322
324 // bytesInFlight isn't updated yet. Its trace is called after Tx
325 // so add an additional m_segmentSize to bytesInFlight
326 uint32_t soonBytesInFlight = m_bytesInFlight + m_segmentSize;
327 bool canPacketBeSent = ((m_curCwnd - soonBytesInFlight) >= m_segmentSize);
328 m_isFullCwndSent = (!canPacketBeSent || m_curCwnd == 0);
330 NS_LOG_DEBUG("Next expected interval (s): " << m_nextPacketInterval.GetSeconds());
331 }
332}
333
334void
336{
337 NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
338}
339
340void
342{
343 NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
344}
345
346void
348{
349 if (who == SENDER)
350 {
351 m_event.Cancel();
352 }
353}
354
355/**
356 * @ingroup internet-test
357 *
358 * @brief TestSuite for the behavior of TCP pacing
359 */
361{
362 public:
364 : TestSuite("tcp-pacing-test", Type::UNIT)
365 {
366 uint16_t pacingSsRatio = 200;
367 uint16_t pacingCaRatio = 120;
368 uint32_t segmentSize = 1000;
369 uint32_t packetSize = 1000;
370 uint32_t numPackets = 40;
371 uint32_t delAckMaxCount = 1;
373 uint32_t ssThresh = 1e9; // default large value
374 bool paceInitialWindow = false;
375 std::string description;
376
377 description = std::string("Pacing case 1: Slow start only, no initial pacing");
380 numPackets,
381 pacingSsRatio,
382 pacingCaRatio,
383 ssThresh,
384 paceInitialWindow,
385 delAckMaxCount,
386 tid,
387 description),
388 TestCase::Duration::QUICK);
389
390 paceInitialWindow = true;
391 description = std::string("Pacing case 2: Slow start only, initial pacing");
394 numPackets,
395 pacingSsRatio,
396 pacingCaRatio,
397 ssThresh,
398 paceInitialWindow,
399 delAckMaxCount,
400 tid,
401 description),
402 TestCase::Duration::QUICK);
403
404 // set ssThresh to some smaller value to check that pacing
405 // slows down in second half of slow start, then transitions to CA
406 description = std::string("Pacing case 3: Slow start, followed by transition to Congestion "
407 "avoidance, no initial pacing");
408 paceInitialWindow = false;
409 ssThresh = 40;
410 numPackets = 60;
413 numPackets,
414 pacingSsRatio,
415 pacingCaRatio,
416 ssThresh,
417 paceInitialWindow,
418 delAckMaxCount,
419 tid,
420 description),
421 TestCase::Duration::QUICK);
422
423 // Repeat tests, but with more typical delAckMaxCount == 2
424 delAckMaxCount = 2;
425 paceInitialWindow = false;
426 ssThresh = 1e9;
427 numPackets = 40;
428 description =
429 std::string("Pacing case 4: Slow start only, no initial pacing, delayed ACKs");
432 numPackets,
433 pacingSsRatio,
434 pacingCaRatio,
435 ssThresh,
436 paceInitialWindow,
437 delAckMaxCount,
438 tid,
439 description),
440 TestCase::Duration::QUICK);
441
442 paceInitialWindow = true;
443 description = std::string("Pacing case 5: Slow start only, initial pacing, delayed ACKs");
446 numPackets,
447 pacingSsRatio,
448 pacingCaRatio,
449 ssThresh,
450 paceInitialWindow,
451 delAckMaxCount,
452 tid,
453 description),
454 TestCase::Duration::QUICK);
455
456 description = std::string("Pacing case 6: Slow start, followed by transition to Congestion "
457 "avoidance, no initial pacing, delayed ACKs");
458 paceInitialWindow = false;
459 ssThresh = 40;
460 numPackets = 60;
463 numPackets,
464 pacingSsRatio,
465 pacingCaRatio,
466 ssThresh,
467 paceInitialWindow,
468 delAckMaxCount,
469 tid,
470 description),
471 TestCase::Duration::QUICK);
472 }
473};
474
475static TcpPacingTestSuite g_tcpPacingTest; //!< Static variable for test initialization
Test the behavior of TCP pacing.
uint32_t m_initialCwnd
Initial value of cWnd.
void CWndTrace(uint32_t oldValue, uint32_t newValue) override
Tracks the congestion window changes.
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
Rtt changes.
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
Socket closed normally.
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
Link drop.
Time m_prevTxTime
Time when Tx was previously called.
void BytesInFlightTrace(uint32_t oldValue, uint32_t newValue) override
Bytes in flight changes.
Time m_transmissionStartTime
Time at which sender starts data transmission.
void QueueDrop(SocketWho who) override
Drop on the queue.
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:45
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition event-id.cc:44
Smart pointer class similar to boost::intrusive_ptr.
static Time Now()
Return the current simulation virtual time.
Definition simulator.cc:197
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.
virtual void ConfigureProperties()
Change the configuration of the socket properties.
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.
virtual void ConfigureEnvironment()
Change the configuration of the environment.
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:36
uint8_t GetFlags() const
Get the flags.
static TypeId GetTypeId()
Get the type ID.
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:292
A suite of tests to run.
Definition test.h:1267
Type
Type of test.
Definition test.h:1274
static constexpr auto UNIT
Definition test.h:1291
Simulation virtual time values and global simulation resolution.
Definition nstime.h:94
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition nstime.h:392
a unique identifier for an interface.
Definition type-id.h:48
uint32_t segmentSize
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:191
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:257
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#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:740
Time NanoSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1380
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1344
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition nstime.h:1356
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.