A Discrete-Event Network Simulator
API
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 
25 using namespace ns3;
26 
27 NS_LOG_COMPONENT_DEFINE("TcpTxBufferTestSuite");
28 
36 {
37  public:
40 
41  private:
42  void DoRun() override;
43  void DoTeardown() override;
44 
46  void TestIsLost();
48  void TestNewBlock();
50  void TestTransmittedBlock();
52  void TestNextSeg();
55  void TestMergeItemsWhenGetTransmittedSegment();
60  uint32_t GetRWnd() const;
61 };
62 
64  : TestCase("TcpTxBuffer Test")
65 {
66 }
67 
68 void
70 {
71  Simulator::Schedule(Seconds(0.0), &TcpTxBufferTestCase::TestIsLost, this);
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  */
78  Simulator::Schedule(Seconds(0.0), &TcpTxBufferTestCase::TestNewBlock, this);
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  */
89  Simulator::Schedule(Seconds(0.0), &TcpTxBufferTestCase::TestTransmittedBlock, this);
90  Simulator::Schedule(Seconds(0.0), &TcpTxBufferTestCase::TestNextSeg, this);
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  */
99  Simulator::Schedule(Seconds(0.0),
101  this);
102 
103  Simulator::Run();
104  Simulator::Destroy();
105 }
106 
107 void
109 {
110  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer>();
111  txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
112  SequenceNumber32 head(1);
113  txBuf->SetHeadSequence(head);
114  SequenceNumber32 ret;
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 
166 uint32_t
168 {
169  // Assume unlimited receiver window
171 }
172 
173 void
175 {
176  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer>();
177  ;
178  txBuf->SetRWndCallback(MakeCallback(&TcpTxBufferTestCase::GetRWnd, this));
179  SequenceNumber32 head(1);
180  SequenceNumber32 ret;
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 
327 void
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 
405 void
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 
423 void
425 {
426 }
427 
428 void
430 {
431 }
432 
439 {
440  public:
442  : TestSuite("tcp-tx-buffer", UNIT)
443  {
444  AddTestCase(new TcpTxBufferTestCase, TestCase::QUICK);
445  }
446 };
447 
#define max(a, b)
Definition: 80211b.c:42
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
uint32_t GetSize() const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:861
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
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:1060
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
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:144
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.