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 * 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
27using namespace ns3;
28
29NS_LOG_COMPONENT_DEFINE("TcpPacingTestSuite");
30
31/**
32 * \ingroup internet-test
33 *
34 * \brief Test the behavior of TCP pacing
35 *
36 * This test checks that packets are paced at correct intervals. The test
37 * uses a shadow pacing rate calculation assumed to match the internal
38 * pacing calculation. Therefore, if you modify the values of
39 * pacingSsRatio and pacingCaRatio herein, ensure that you also change
40 * values used in the TCP implementation to match.
41 *
42 * This test environment uses an RTT of 100ms
43 * The time interval between two consecutive packet transmissions is measured
44 * Pacing rate should be at least cwnd / rtt to transmit cwnd segments per
45 * rtt. Linux multiples this basic ratio by an additional factor, to yield
46 * pacingRate = (factor * cwnd) / rtt
47 * Since pacingRate can also be written as segmentSize / interval
48 * we can solve for interval = (segmentSize * rtt) / (factor * cwnd)
49 *
50 * The test checks whether the measured interval lies within a tolerance
51 * value of the expected interval. The tolerance or error margin
52 * was chosen to be 10 Nanoseconds, that could be due to delay introduced
53 * by the application's send process.
54 *
55 * This check should not be performed for a packet transmission after the
56 * sender has sent all bytes corresponding to the window and is awaiting an
57 * ACK from the receiver (corresponding to m_isFullCwndSent).
58 * Pacing check should be performed when the sender is actively sending packets from cwnd.
59 *
60 * The same argument applies when the sender has finished sending packets and is awaiting a
61 * FIN/ACK from the receiver, to send the final ACK
62 *
63 * As can be seen in TcpSocketBase::UpdatePacingRate (), different pacing
64 * ratios are used when cwnd < ssThresh / 2 and when cwnd > ssThresh / 2
65 *
66 * A few key points to note:
67 * - In TcpSocketBase, pacing rate is updated whenever an ACK is received.
68 *
69 * - The factors that could contribute to a different value of pacing rate include
70 * congestion window and RTT.
71 *
72 * - However, the cWnd trace is called after Rx () trace and we therefore
73 * update the expected interval in cWnd trace.
74 *
75 * - An RTT trace is also necessary, since using delayed ACKs may lead to a
76 * higher RTT measurement at the sender's end. The expected interval should
77 * be updated here as well.
78 *
79 * - When sending the current packet, TcpSocketBase automatically decides the time at
80 * which next packet should be sent. So, if say packet 3 is sent and an ACK is
81 * received before packet 4 is sent (thus updating the pacing rate), this does not change
82 * the time at which packet 4 is to be sent. When packet 4 is indeed sent later, the new
83 * pacing rate is used to decide when packet 5 will be sent. This behavior is captured in
84 * m_nextPacketInterval, which is not affected by change of pacing rate before the next
85 * packet is sent. The important observation here is to realize the contrast between
86 * m_expectedInterval and m_nextPacketInterval.
87 *
88 */
90{
91 public:
92 /**
93 * \brief Constructor.
94 * \param segmentSize Segment size at the TCP layer (bytes).
95 * \param packetSize Size of packets sent at the application layer (bytes).
96 * \param packets Number of packets.
97 * \param pacingSsRatio Pacing Ratio during Slow Start (multiplied by 100)
98 * \param pacingCaRatio Pacing Ratio during Congestion Avoidance (multiplied by 100)
99 * \param ssThresh slow start threshold (bytes)
100 * \param paceInitialWindow whether to pace the initial window
101 * \param delAckMaxCount Delayed ACK max count parameter
102 * \param congControl Type of congestion control.
103 * \param desc The test description.
104 */
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
126 /**
127 * \brief Update the expected interval at which next packet will be sent
128 */
129 virtual void UpdateExpectedInterval();
130
131 void ConfigureEnvironment() override;
132 void ConfigureProperties() override;
133
134 private:
135 uint32_t m_segmentSize; //!< Segment size
136 uint32_t m_packetSize; //!< Size of the packets
137 uint32_t m_packets; //!< Number of packets
138 EventId m_event; //!< Check event
139 bool m_initial; //!< True on first run
140 uint32_t m_initialCwnd; //!< Initial value of cWnd
141 uint32_t m_curCwnd; //!< Current sender cWnd
142 bool m_isFullCwndSent; //!< True if all bytes for that cWnd is sent and sender is waiting for an
143 //!< ACK
144 uint32_t m_bytesInFlight; //!< Current bytes in flight
145 Time m_prevTxTime; //!< Time when Tx was previously called
146 uint16_t m_pacingSsRatio; //!< Pacing factor during Slow Start
147 uint16_t m_pacingCaRatio; //!< Pacing factor during Congestion Avoidance
148 uint32_t m_ssThresh; //!< Slow start threshold
149 bool m_paceInitialWindow; //!< True if initial window should be paced
150 uint32_t m_delAckMaxCount; //!< Delayed ack count for receiver
151 bool m_isConnAboutToEnd; //!< True when sender receives a FIN/ACK from receiver
152 Time m_transmissionStartTime; //!< Time at which sender starts data transmission
153 Time m_expectedInterval; //!< Theoretical estimate of the time at which next packet is scheduled
154 //!< for transmission
155 uint32_t m_packetsSent; //!< Number of packets sent by sender so far
156 Time m_nextPacketInterval; //!< Time maintained by Tx () trace about interval at which next
157 //!< packet will be sent
158 Time m_tracedRtt; //!< Traced value of RTT, which may be different from the environment RTT in
159 //!< case of delayed ACKs
160};
161
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
197void
199{
204 SetMTU(1500);
207}
208
209void
211{
216 SetPacingStatus(SENDER, true);
219 NS_LOG_DEBUG("segSize: " << m_segmentSize << " ssthresh: " << m_ssThresh
220 << " paceInitialWindow: " << m_paceInitialWindow << " delAckMaxCount "
222}
223
224void
226{
227 NS_LOG_FUNCTION(this << oldTime << newTime);
228 m_tracedRtt = newTime;
230}
231
232void
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
246void
248{
249 m_bytesInFlight = newValue;
250}
251
252void
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
279void
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
300void
302{
303 NS_LOG_FUNCTION(this << p << h << who);
304
305 if (who == SENDER)
306 {
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
345void
347{
348 NS_FATAL_ERROR("Drop on the queue; cannot validate congestion avoidance");
349}
350
351void
353{
354 NS_FATAL_ERROR("Drop on the phy: cannot validate congestion avoidance");
355}
356
357void
359{
360 if (who == SENDER)
361 {
362 m_event.Cancel();
363 }
364}
365
366/**
367 * \ingroup internet-test
368 *
369 * \brief TestSuite for the behavior of TCP pacing
370 */
372{
373 public:
375 : TestSuite("tcp-pacing-test", Type::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;
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");
391 numPackets,
392 pacingSsRatio,
393 pacingCaRatio,
394 ssThresh,
395 paceInitialWindow,
396 delAckMaxCount,
397 tid,
398 description),
399 TestCase::Duration::QUICK);
400
401 paceInitialWindow = true;
402 description = std::string("Pacing case 2: Slow start only, initial pacing");
405 numPackets,
406 pacingSsRatio,
407 pacingCaRatio,
408 ssThresh,
409 paceInitialWindow,
410 delAckMaxCount,
411 tid,
412 description),
413 TestCase::Duration::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;
424 numPackets,
425 pacingSsRatio,
426 pacingCaRatio,
427 ssThresh,
428 paceInitialWindow,
429 delAckMaxCount,
430 tid,
431 description),
432 TestCase::Duration::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");
443 numPackets,
444 pacingSsRatio,
445 pacingCaRatio,
446 ssThresh,
447 paceInitialWindow,
448 delAckMaxCount,
449 tid,
450 description),
451 TestCase::Duration::QUICK);
452
453 paceInitialWindow = true;
454 description = std::string("Pacing case 5: Slow start only, initial pacing, delayed ACKs");
457 numPackets,
458 pacingSsRatio,
459 pacingCaRatio,
460 ssThresh,
461 paceInitialWindow,
462 delAckMaxCount,
463 tid,
464 description),
465 TestCase::Duration::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;
474 numPackets,
475 pacingSsRatio,
476 pacingCaRatio,
477 ssThresh,
478 paceInitialWindow,
479 delAckMaxCount,
480 tid,
481 description),
482 TestCase::Duration::QUICK);
483 }
484};
485
486static 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:55
void Cancel()
This method is syntactic sugar for the ns3::Simulator::Cancel method.
Definition: event-id.cc:55
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
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:47
uint8_t GetFlags() const
Get the flags.
Definition: tcp-header.cc:148
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:301
A suite of tests to run.
Definition: test.h:1268
Type
Type of test.
Definition: test.h:1275
static constexpr auto UNIT
Definition: test.h:1286
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 ",...
#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:751
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.