A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-tx-buffer-test.cc
Go to the documentation of this file.
1/*
2 * SPDX-License-Identifier: GPL-2.0-only
3 *
4 */
5
6#include "ns3/log.h"
7#include "ns3/packet.h"
8#include "ns3/simulator.h"
9#include "ns3/tcp-tx-buffer.h"
10#include "ns3/test.h"
11
12#include <limits>
13
14using namespace ns3;
15
16NS_LOG_COMPONENT_DEFINE("TcpTxBufferTestSuite");
17
18/**
19 * @ingroup internet-test
20 * @ingroup tests
21 *
22 * @brief The TcpTxBuffer Test
23 */
25{
26 public:
27 /** @brief Constructor */
29
30 private:
31 void DoRun() override;
32 void DoTeardown() override;
33
34 /** @brief Test if a segment is really set as lost */
35 void TestIsLost();
36 /** @brief Test the generation of an unsent block */
37 void TestNewBlock();
38 /** @brief Test the generation of a previously sent block */
40 /** @brief Test the generation of the "next" block */
41 void TestNextSeg();
42 /** @brief Test the logic of merging items in GetTransmittedSegment()
43 * which is triggered by CopyFromSequence()*/
45 /**
46 * @brief Callback to provide a value of receiver window
47 * @returns the receiver window size
48 */
49 uint32_t GetRWnd() const;
50};
51
53 : TestCase("TcpTxBuffer Test")
54{
55}
56
57void
59{
61 /*
62 * Cases for new block:
63 * -> is exactly the same as stored
64 * -> starts over the boundary, but ends earlier
65 * -> starts over the boundary, but ends after
66 */
68
69 /*
70 * Cases for transmitted block:
71 * -> is exactly the same as previous
72 * -> starts over the boundary, but ends earlier
73 * -> starts over the boundary, but ends after
74 * -> starts inside a packet, ends right
75 * -> starts inside a packet, ends earlier in the same packet
76 * -> starts inside a packet, ends in another packet
77 */
80
81 /*
82 * Case for transmitted block:
83 * -> transmitted packets are marked differently for m_lost under some scenarios
84 * -> packets could be small than MSS when socket buffer is not a multiple of MSS.
85 * -> during retransmission, the sender tries to send a full segment but it
86 * should stop to merge items when they have different values for m_lost.
87 */
90 this);
91
94}
95
96void
98{
100 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
101 SequenceNumber32 head(1);
102 txBuf->SetHeadSequence(head);
104 txBuf->SetSegmentSize(1000);
105 txBuf->SetDupAckThresh(3);
106
107 txBuf->Add(Create<Packet>(10000));
108
109 for (uint8_t i = 0; i < 10; ++i)
110 {
111 txBuf->CopyFromSequence(1000, SequenceNumber32((i * 1000) + 1));
112 }
113
114 for (uint8_t i = 0; i < 10; ++i)
115 {
116 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
117 false,
118 "Lost is true, but it's not");
119 }
120
121 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(1001), SequenceNumber32(2001)));
122 txBuf->Update(sack->GetSackList());
123
124 for (uint8_t i = 0; i < 10; ++i)
125 {
126 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
127 false,
128 "Lost is true, but it's not");
129 }
130
131 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(2001), SequenceNumber32(3001)));
132 txBuf->Update(sack->GetSackList());
133
134 for (uint8_t i = 0; i < 10; ++i)
135 {
136 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
137 false,
138 "Lost is true, but it's not");
139 }
140
141 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(3001), SequenceNumber32(4001)));
142 txBuf->Update(sack->GetSackList());
143
144 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32(1)), true, "Lost is true, but it's not");
145
146 for (uint8_t i = 1; i < 10; ++i)
147 {
148 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
149 false,
150 "Lost is true, but it's not");
151 }
152}
153
156{
157 // Assume unlimited receiver window
158 return std::numeric_limits<uint32_t>::max();
159}
160
161void
163{
165 ;
166 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
167 SequenceNumber32 head(1);
169 SequenceNumber32 retHigh;
170 txBuf->SetSegmentSize(150);
171 txBuf->SetDupAckThresh(3);
172 uint32_t dupThresh = 3;
173 uint32_t segmentSize = 150;
175
176 // At the beginning the values of dupThresh and segmentSize don't matter
177 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
178 false,
179 "NextSeq should not be returned at the beginning");
180
181 txBuf->SetHeadSequence(head);
182 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
183 false,
184 "NextSeq should not be returned with no data");
185
186 // Add a single, 30000-bytes long, packet
187 txBuf->Add(Create<Packet>(30000));
188 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
189 true,
190 "No NextSeq with data at beginning");
192 head.GetValue(),
193 "Different NextSeq than expected at the beginning");
194
195 // Simulate sending 100 packets, 150 bytes long each, from seq 1
196 for (uint32_t i = 0; i < 100; ++i)
197 {
198 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
199 true,
200 "No NextSeq with data while \"transmitting\"");
202 head + (segmentSize * i),
203 "Different NextSeq than expected while \"transmitting\"");
204 txBuf->CopyFromSequence(segmentSize, ret);
205 }
206
207 // Ok, now simulate we lost the first segment [1;151], and that we have
208 // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
209 SequenceNumber32 lastRet = ret; // This is like m_highTx
210 for (uint32_t i = 1; i < dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
211 {
212 SequenceNumber32 begin = head + (segmentSize * i);
213 SequenceNumber32 end = begin + segmentSize;
214 sack->AddSackBlock(TcpOptionSack::SackBlock(begin, end));
215 txBuf->Update(sack->GetSackList());
216
217 // new data expected and sent
218 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
219 true,
220 "No NextSeq with SACK block while \"transmitting\"");
222 lastRet + segmentSize,
223 "Different NextSeq than expected in limited transmit");
224 txBuf->CopyFromSequence(segmentSize, ret);
225 sack->ClearSackList();
226 lastRet = ret;
227 }
228
229 // Limited transmit was ok; now there is the dupThresh-th dupack.
230 // Now we need to retransmit the first block..
231 sack->AddSackBlock(TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh)),
232 head + (segmentSize * (dupThresh)) + segmentSize));
233 txBuf->Update(sack->GetSackList());
234 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
235 true,
236 "No NextSeq with SACK block for Fast Recovery");
237 NS_TEST_ASSERT_MSG_EQ(ret, head, "Different NextSeq than expected for Fast Recovery");
238 txBuf->CopyFromSequence(segmentSize, ret);
239 sack->ClearSackList();
240
241 // Fast Retransmission was ok; now check some additional dupacks.
242 for (uint32_t i = 1; i <= 4; ++i)
243 {
244 sack->AddSackBlock(
245 TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh + i)),
246 head + (segmentSize * (dupThresh + i)) + segmentSize));
247 txBuf->Update(sack->GetSackList());
248 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
249 true,
250 "No NextSeq with SACK block after recv dupacks in FR");
252 lastRet + segmentSize,
253 "Different NextSeq than expected after recv dupacks in FR");
254 txBuf->CopyFromSequence(segmentSize, ret);
255 sack->ClearSackList();
256 lastRet = ret;
257 }
258
259 // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
260 // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
261 // to give us the next segment (head + segmentSize) to retransmit.
262 /* In this particular case, we are checking the fact that we have badly crafted
263 * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
264 * received dupack for 1. While receiving these, we crafted SACK block in the
265 * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
266 * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
267 * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
268 * 3,4 .. so keep them.
269 */
270 head = head + segmentSize;
271 txBuf->DiscardUpTo(head);
272
273 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
274 true,
275 "No NextSeq with SACK block after receiving partial ACK");
277 head,
278 "Different NextSeq than expected after receiving partial ACK ");
279 txBuf->CopyFromSequence(segmentSize, ret);
280
281 // Now, check for one more dupack...
282 sack->AddSackBlock(
283 TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh + 6)),
284 head + (segmentSize * (dupThresh + 6)) + segmentSize));
285 txBuf->Update(sack->GetSackList());
286 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
287 true,
288 "No NextSeq with SACK block after recv dupacks after partial ack");
290 lastRet + segmentSize,
291 "Different NextSeq than expected after recv dupacks after partial ack");
292 txBuf->CopyFromSequence(segmentSize, ret);
293 sack->ClearSackList();
294 head = lastRet = ret + segmentSize;
295
296 // And now ack everything we sent to date!
297 txBuf->DiscardUpTo(head);
298
299 // And continue normally until the end
300 for (uint32_t i = 0; i < 93; ++i)
301 {
302 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
303 true,
304 "No NextSeq with data while \"transmitting\"");
306 head + (segmentSize * i),
307 "Different NextSeq than expected while \"transmitting\"");
308 txBuf->CopyFromSequence(segmentSize, ret);
309 }
310
311 txBuf->DiscardUpTo(ret + segmentSize);
312 NS_TEST_ASSERT_MSG_EQ(txBuf->Size(), 0, "Data inside the buffer");
313}
314
315void
317{
318 // Manually recreating all the conditions
320 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
321 txBuf->SetHeadSequence(SequenceNumber32(1));
322 txBuf->SetSegmentSize(100);
323
324 // get a packet which is exactly the same stored
325 Ptr<Packet> p1 = Create<Packet>(100);
326 txBuf->Add(p1);
327
328 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(1)),
329 100,
330 "TxBuf miscalculates size");
331 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
332 0,
333 "TxBuf miscalculates size of in flight segments");
334
335 Ptr<Packet> ret = txBuf->CopyFromSequence(100, SequenceNumber32(1))->GetPacketCopy();
336 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 100, "Returned packet has different size than requested");
337 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(1)),
338 100,
339 "TxBuf miscalculates size");
340 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
341 100,
342 "TxBuf miscalculates size of in flight segments");
343
344 txBuf->DiscardUpTo(SequenceNumber32(101));
345 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(101)),
346 0,
347 "TxBuf miscalculates size");
348 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
349 0,
350 "TxBuf miscalculates size of in flight segments");
351
352 // starts over the boundary, but ends earlier
353
354 Ptr<Packet> p2 = Create<Packet>(100);
355 txBuf->Add(p2);
356
357 ret = txBuf->CopyFromSequence(50, SequenceNumber32(101))->GetPacketCopy();
358 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 50, "Returned packet has different size than requested");
359 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(151)),
360 50,
361 "TxBuf miscalculates size");
362 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
363 50,
364 "TxBuf miscalculates size of in flight segments");
365
366 // starts over the boundary, but ends after
367 Ptr<Packet> p3 = Create<Packet>(100);
368 txBuf->Add(p3);
369
370 ret = txBuf->CopyFromSequence(70, SequenceNumber32(151))->GetPacketCopy();
371 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 70, "Returned packet has different size than requested");
372 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(221)),
373 80,
374 "TxBuf miscalculates size");
375 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
376 120,
377 "TxBuf miscalculates size of in flight segments");
378
379 ret = txBuf->CopyFromSequence(3000, SequenceNumber32(221))->GetPacketCopy();
380 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 80, "Returned packet has different size than requested");
381 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(301)),
382 0,
383 "TxBuf miscalculates size");
384 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
385 200,
386 "TxBuf miscalculates size of in flight segments");
387
388 // Clear everything
389 txBuf->DiscardUpTo(SequenceNumber32(381));
390 NS_TEST_ASSERT_MSG_EQ(txBuf->Size(), 0, "Size is different than expected");
391}
392
393void
395{
396 TcpTxBuffer txBuf;
397 SequenceNumber32 head(1);
398 txBuf.SetHeadSequence(head);
399 txBuf.SetSegmentSize(2000);
400
401 txBuf.Add(Create<Packet>(2000));
402 txBuf.CopyFromSequence(1000, SequenceNumber32(1));
403 txBuf.CopyFromSequence(1000, SequenceNumber32(1001));
404 txBuf.MarkHeadAsLost();
405
406 // GetTransmittedSegment() will be called and handle the case that two items
407 // have different m_lost value.
408 txBuf.CopyFromSequence(2000, SequenceNumber32(1));
409}
410
411void
415
416void
420
421/**
422 * @ingroup internet-test
423 *
424 * @brief the TestSuite for the TcpTxBuffer test case
425 */
427{
428 public:
434};
435
436static TcpTxBufferTestSuite g_tcpTxBufferTestSuite; //!< Static variable for test initialization
The TcpTxBuffer Test.
void TestTransmittedBlock()
Test the generation of a previously sent block.
void TestMergeItemsWhenGetTransmittedSegment()
Test the logic of merging items in GetTransmittedSegment() which is triggered by CopyFromSequence().
void DoRun() override
Implementation to actually run this TestCase.
void TestNewBlock()
Test the generation of an unsent block.
uint32_t GetRWnd() const
Callback to provide a value of receiver window.
void TestIsLost()
Test if a segment is really set as lost.
void TestNextSeg()
Test the generation of the "next" block.
TcpTxBufferTestCase()
Constructor.
void DoTeardown() override
Implementation to do any local setup required for this TestCase.
the TestSuite for the TcpTxBuffer test case
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
constexpr NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
static EventId Schedule(const Time &delay, FUNC f, Ts &&... args)
Schedule an event to expire after delay.
Definition simulator.h:580
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition simulator.cc:125
static void Run()
Run the simulation.
Definition simulator.cc:161
std::pair< SequenceNumber32, SequenceNumber32 > SackBlock
SACK block definition.
Tcp sender buffer.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
void SetSegmentSize(uint32_t segmentSize)
Set the segment size.
TcpTxItem * CopyFromSequence(uint32_t numBytes, const SequenceNumber32 &seq)
Copy data from the range [seq, seq+numBytes) into a packet.
void MarkHeadAsLost()
Mark the head of the sent list as lost.
void SetHeadSequence(const SequenceNumber32 &seq)
Set the head sequence of the buffer.
void AddTestCase(TestCase *testCase, Duration duration=Duration::QUICK)
Add an individual child TestCase to this test suite.
Definition test.cc:296
@ QUICK
Fast test.
Definition test.h:1057
TestCase(const TestCase &)=delete
Caller graph was not generated because of its size.
Type
Type of test.
Definition test.h:1271
@ UNIT
This test suite implements a Unit Test.
Definition test.h:1273
TestSuite(std::string name, Type type=Type::UNIT)
Construct a new test suite.
Definition test.cc:494
uint32_t segmentSize
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition callback.h:690
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition log.h:194
Ptr< T > CreateObject(Args &&... args)
Create an object by type, with varying number of constructor parameters.
Definition object.h:627
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:492
SequenceNumber< uint32_t > SequenceNumber32
32 bit Sequence number.
#define NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
Test that an actual and expected (limit) value are equal and report and abort if not.
Definition test.h:133
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition nstime.h:1273
Every class exported by the ns3 library is enclosed in the ns3 namespace.
static TcpTxBufferTestSuite g_tcpTxBufferTestSuite
Static variable for test initialization.