A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
tcp-tx-buffer-test.cc
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation;
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * You should have received a copy of the GNU General Public License
12 * along with this program; if not, write to the Free Software
13 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 *
15 */
16
17#include "ns3/log.h"
18#include "ns3/packet.h"
19#include "ns3/simulator.h"
20#include "ns3/tcp-tx-buffer.h"
21#include "ns3/test.h"
22
23#include <limits>
24
25using namespace ns3;
26
27NS_LOG_COMPONENT_DEFINE("TcpTxBufferTestSuite");
28
29/**
30 * \ingroup internet-test
31 * \ingroup tests
32 *
33 * \brief The TcpTxBuffer Test
34 */
36{
37 public:
38 /** \brief Constructor */
40
41 private:
42 void DoRun() override;
43 void DoTeardown() override;
44
45 /** \brief Test if a segment is really set as lost */
46 void TestIsLost();
47 /** \brief Test the generation of an unsent block */
48 void TestNewBlock();
49 /** \brief Test the generation of a previously sent block */
51 /** \brief Test the generation of the "next" block */
52 void TestNextSeg();
53 /** \brief Test the logic of merging items in GetTransmittedSegment()
54 * which is triggered by CopyFromSequence()*/
56 /**
57 * \brief Callback to provide a value of receiver window
58 * \returns the receiver window size
59 */
60 uint32_t GetRWnd() const;
61};
62
64 : TestCase("TcpTxBuffer Test")
65{
66}
67
68void
70{
72 /*
73 * Cases for new block:
74 * -> is exactly the same as stored
75 * -> starts over the boundary, but ends earlier
76 * -> starts over the boundary, but ends after
77 */
79
80 /*
81 * Cases for transmitted block:
82 * -> is exactly the same as previous
83 * -> starts over the boundary, but ends earlier
84 * -> starts over the boundary, but ends after
85 * -> starts inside a packet, ends right
86 * -> starts inside a packet, ends earlier in the same packet
87 * -> starts inside a packet, ends in another packet
88 */
91
92 /*
93 * Case for transmitted block:
94 * -> transmitted packets are marked differently for m_lost under some scenarios
95 * -> packets could be small than MSS when socket buffer is not a multiple of MSS.
96 * -> during retransmission, the sender tries to send a full segment but it
97 * should stop to merge items when they have different values for m_lost.
98 */
101 this);
102
105}
106
107void
109{
110 Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer>();
111 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
112 SequenceNumber32 head(1);
113 txBuf->SetHeadSequence(head);
115 Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack>();
116 txBuf->SetSegmentSize(1000);
117 txBuf->SetDupAckThresh(3);
118
119 txBuf->Add(Create<Packet>(10000));
120
121 for (uint8_t i = 0; i < 10; ++i)
122 {
123 txBuf->CopyFromSequence(1000, SequenceNumber32((i * 1000) + 1));
124 }
125
126 for (uint8_t i = 0; i < 10; ++i)
127 {
128 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
129 false,
130 "Lost is true, but it's not");
131 }
132
133 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(1001), SequenceNumber32(2001)));
134 txBuf->Update(sack->GetSackList());
135
136 for (uint8_t i = 0; i < 10; ++i)
137 {
138 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
139 false,
140 "Lost is true, but it's not");
141 }
142
143 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(2001), SequenceNumber32(3001)));
144 txBuf->Update(sack->GetSackList());
145
146 for (uint8_t i = 0; 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 sack->AddSackBlock(TcpOptionSack::SackBlock(SequenceNumber32(3001), SequenceNumber32(4001)));
154 txBuf->Update(sack->GetSackList());
155
156 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32(1)), true, "Lost is true, but it's not");
157
158 for (uint8_t i = 1; i < 10; ++i)
159 {
160 NS_TEST_ASSERT_MSG_EQ(txBuf->IsLost(SequenceNumber32((i * 1000) + 1)),
161 false,
162 "Lost is true, but it's not");
163 }
164}
165
168{
169 // Assume unlimited receiver window
170 return std::numeric_limits<uint32_t>::max();
171}
172
173void
175{
176 Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer>();
177 ;
178 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
179 SequenceNumber32 head(1);
181 SequenceNumber32 retHigh;
182 txBuf->SetSegmentSize(150);
183 txBuf->SetDupAckThresh(3);
184 uint32_t dupThresh = 3;
185 uint32_t segmentSize = 150;
186 Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack>();
187
188 // At the beginning the values of dupThresh and segmentSize don't matter
189 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
190 false,
191 "NextSeq should not be returned at the beginning");
192
193 txBuf->SetHeadSequence(head);
194 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
195 false,
196 "NextSeq should not be returned with no data");
197
198 // Add a single, 30000-bytes long, packet
199 txBuf->Add(Create<Packet>(30000));
200 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
201 true,
202 "No NextSeq with data at beginning");
204 head.GetValue(),
205 "Different NextSeq than expected at the beginning");
206
207 // Simulate sending 100 packets, 150 bytes long each, from seq 1
208 for (uint32_t i = 0; i < 100; ++i)
209 {
210 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
211 true,
212 "No NextSeq with data while \"transmitting\"");
214 head + (segmentSize * i),
215 "Different NextSeq than expected while \"transmitting\"");
216 txBuf->CopyFromSequence(segmentSize, ret);
217 }
218
219 // Ok, now simulate we lost the first segment [1;151], and that we have
220 // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
221 SequenceNumber32 lastRet = ret; // This is like m_highTx
222 for (uint32_t i = 1; i < dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
223 {
224 SequenceNumber32 begin = head + (segmentSize * i);
225 SequenceNumber32 end = begin + segmentSize;
226 sack->AddSackBlock(TcpOptionSack::SackBlock(begin, end));
227 txBuf->Update(sack->GetSackList());
228
229 // new data expected and sent
230 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
231 true,
232 "No NextSeq with SACK block while \"transmitting\"");
234 lastRet + segmentSize,
235 "Different NextSeq than expected in limited transmit");
236 txBuf->CopyFromSequence(segmentSize, ret);
237 sack->ClearSackList();
238 lastRet = ret;
239 }
240
241 // Limited transmit was ok; now there is the dupThresh-th dupack.
242 // Now we need to retransmit the first block..
243 sack->AddSackBlock(TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh)),
244 head + (segmentSize * (dupThresh)) + segmentSize));
245 txBuf->Update(sack->GetSackList());
246 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
247 true,
248 "No NextSeq with SACK block for Fast Recovery");
249 NS_TEST_ASSERT_MSG_EQ(ret, head, "Different NextSeq than expected for Fast Recovery");
250 txBuf->CopyFromSequence(segmentSize, ret);
251 sack->ClearSackList();
252
253 // Fast Retransmission was ok; now check some additional dupacks.
254 for (uint32_t i = 1; i <= 4; ++i)
255 {
256 sack->AddSackBlock(
257 TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh + i)),
258 head + (segmentSize * (dupThresh + i)) + segmentSize));
259 txBuf->Update(sack->GetSackList());
260 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
261 true,
262 "No NextSeq with SACK block after recv dupacks in FR");
264 lastRet + segmentSize,
265 "Different NextSeq than expected after recv dupacks in FR");
266 txBuf->CopyFromSequence(segmentSize, ret);
267 sack->ClearSackList();
268 lastRet = ret;
269 }
270
271 // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
272 // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
273 // to give us the next segment (head + segmentSize) to retransmit.
274 /* In this particular case, we are checking the fact that we have badly crafted
275 * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
276 * received dupack for 1. While receiving these, we crafted SACK block in the
277 * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
278 * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
279 * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
280 * 3,4 .. so keep them.
281 */
282 head = head + segmentSize;
283 txBuf->DiscardUpTo(head);
284
285 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
286 true,
287 "No NextSeq with SACK block after receiving partial ACK");
289 head,
290 "Different NextSeq than expected after receiving partial ACK ");
291 txBuf->CopyFromSequence(segmentSize, ret);
292
293 // Now, check for one more dupack...
294 sack->AddSackBlock(
295 TcpOptionSack::SackBlock(head + (segmentSize * (dupThresh + 6)),
296 head + (segmentSize * (dupThresh + 6)) + segmentSize));
297 txBuf->Update(sack->GetSackList());
298 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
299 true,
300 "No NextSeq with SACK block after recv dupacks after partial ack");
302 lastRet + segmentSize,
303 "Different NextSeq than expected after recv dupacks after partial ack");
304 txBuf->CopyFromSequence(segmentSize, ret);
305 sack->ClearSackList();
306 head = lastRet = ret + segmentSize;
307
308 // And now ack everything we sent to date!
309 txBuf->DiscardUpTo(head);
310
311 // And continue normally until the end
312 for (uint32_t i = 0; i < 93; ++i)
313 {
314 NS_TEST_ASSERT_MSG_EQ(txBuf->NextSeg(&ret, &retHigh, false),
315 true,
316 "No NextSeq with data while \"transmitting\"");
318 head + (segmentSize * i),
319 "Different NextSeq than expected while \"transmitting\"");
320 txBuf->CopyFromSequence(segmentSize, ret);
321 }
322
323 txBuf->DiscardUpTo(ret + segmentSize);
324 NS_TEST_ASSERT_MSG_EQ(txBuf->Size(), 0, "Data inside the buffer");
325}
326
327void
329{
330 // Manually recreating all the conditions
331 Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer>();
332 txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
333 txBuf->SetHeadSequence(SequenceNumber32(1));
334 txBuf->SetSegmentSize(100);
335
336 // get a packet which is exactly the same stored
337 Ptr<Packet> p1 = Create<Packet>(100);
338 txBuf->Add(p1);
339
340 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(1)),
341 100,
342 "TxBuf miscalculates size");
343 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
344 0,
345 "TxBuf miscalculates size of in flight segments");
346
347 Ptr<Packet> ret = txBuf->CopyFromSequence(100, SequenceNumber32(1))->GetPacketCopy();
348 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 100, "Returned packet has different size than requested");
349 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(1)),
350 100,
351 "TxBuf miscalculates size");
352 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
353 100,
354 "TxBuf miscalculates size of in flight segments");
355
356 txBuf->DiscardUpTo(SequenceNumber32(101));
357 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(101)),
358 0,
359 "TxBuf miscalculates size");
360 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
361 0,
362 "TxBuf miscalculates size of in flight segments");
363
364 // starts over the boundary, but ends earlier
365
366 Ptr<Packet> p2 = Create<Packet>(100);
367 txBuf->Add(p2);
368
369 ret = txBuf->CopyFromSequence(50, SequenceNumber32(101))->GetPacketCopy();
370 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 50, "Returned packet has different size than requested");
371 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(151)),
372 50,
373 "TxBuf miscalculates size");
374 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
375 50,
376 "TxBuf miscalculates size of in flight segments");
377
378 // starts over the boundary, but ends after
379 Ptr<Packet> p3 = Create<Packet>(100);
380 txBuf->Add(p3);
381
382 ret = txBuf->CopyFromSequence(70, SequenceNumber32(151))->GetPacketCopy();
383 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 70, "Returned packet has different size than requested");
384 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(221)),
385 80,
386 "TxBuf miscalculates size");
387 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
388 120,
389 "TxBuf miscalculates size of in flight segments");
390
391 ret = txBuf->CopyFromSequence(3000, SequenceNumber32(221))->GetPacketCopy();
392 NS_TEST_ASSERT_MSG_EQ(ret->GetSize(), 80, "Returned packet has different size than requested");
393 NS_TEST_ASSERT_MSG_EQ(txBuf->SizeFromSequence(SequenceNumber32(301)),
394 0,
395 "TxBuf miscalculates size");
396 NS_TEST_ASSERT_MSG_EQ(txBuf->BytesInFlight(),
397 200,
398 "TxBuf miscalculates size of in flight segments");
399
400 // Clear everything
401 txBuf->DiscardUpTo(SequenceNumber32(381));
402 NS_TEST_ASSERT_MSG_EQ(txBuf->Size(), 0, "Size is different than expected");
403}
404
405void
407{
408 TcpTxBuffer txBuf;
409 SequenceNumber32 head(1);
410 txBuf.SetHeadSequence(head);
411 txBuf.SetSegmentSize(2000);
412
413 txBuf.Add(Create<Packet>(2000));
414 txBuf.CopyFromSequence(1000, SequenceNumber32(1));
415 txBuf.CopyFromSequence(1000, SequenceNumber32(1001));
416 txBuf.MarkHeadAsLost();
417
418 // GetTransmittedSegment() will be called and handle the case that two items
419 // have different m_lost value.
420 txBuf.CopyFromSequence(2000, SequenceNumber32(1));
421}
422
423void
425{
426}
427
428void
430{
431}
432
433/**
434 * \ingroup internet-test
435 *
436 * \brief the TestSuite for the TcpTxBuffer test case
437 */
439{
440 public:
442 : TestSuite("tcp-tx-buffer", Type::UNIT)
443 {
444 AddTestCase(new TcpTxBufferTestCase, TestCase::Duration::QUICK);
445 }
446};
447
448static 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:77
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:571
static void Destroy()
Execute the events scheduled with ScheduleDestroy().
Definition: simulator.cc:142
static void Run()
Run the simulation.
Definition: simulator.cc:178
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.
encapsulates test code
Definition: test.h:1061
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
uint32_t segmentSize
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
SequenceNumber< uint32_t, int32_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:145
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1326
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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:704
static TcpTxBufferTestSuite g_tcpTxBufferTestSuite
Static variable for test initialization.