A Discrete-Event Network Simulator
API
tcp-tx-buffer-test.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation;
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15  *
16  */
17 
18 #include <limits>
19 #include "ns3/test.h"
20 #include "ns3/tcp-tx-buffer.h"
21 #include "ns3/packet.h"
22 #include "ns3/simulator.h"
23 #include "ns3/log.h"
24 
25 using namespace ns3;
26 
27 NS_LOG_COMPONENT_DEFINE ("TcpTxBufferTestSuite");
28 
36 {
37 public:
40 
41 private:
42  virtual void DoRun (void);
43  virtual void DoTeardown (void);
44 
46  void TestIsLost ();
48  void TestNewBlock ();
50  void TestTransmittedBlock ();
52  void TestNextSeg ();
55  void TestMergeItemsWhenGetTransmittedSegment ();
57  uint32_t GetRWnd (void) const;
58 };
59 
61  : TestCase ("TcpTxBuffer Test")
62 {
63 }
64 
65 void
67 {
68  Simulator::Schedule (Seconds (0.0), &TcpTxBufferTestCase::TestIsLost, this);
69  /*
70  * Cases for new block:
71  * -> is exactly the same as stored
72  * -> starts over the boundary, but ends earlier
73  * -> starts over the boundary, but ends after
74  */
75  Simulator::Schedule (Seconds (0.0), &TcpTxBufferTestCase::TestNewBlock, this);
76 
77  /*
78  * Cases for transmitted block:
79  * -> is exactly the same as previous
80  * -> starts over the boundary, but ends earlier
81  * -> starts over the boundary, but ends after
82  * -> starts inside a packet, ends right
83  * -> starts inside a packet, ends earlier in the same packet
84  * -> starts inside a packet, ends in another packet
85  */
86  Simulator::Schedule (Seconds (0.0),
88  Simulator::Schedule (Seconds (0.0),
90 
91  /*
92  * Case for transmitted block:
93  * -> transmitted packets are marked differently for m_lost under some scenarios
94  * -> packets could be small than MSS when socket buffer is not a multiple of MSS.
95  * -> during retransmission, the sender tries to send a full segment but it
96  * should stop to merge items when they have different values for m_lost.
97  */
98  Simulator::Schedule (Seconds (0.0),
100 
101  Simulator::Run ();
102  Simulator::Destroy ();
103 }
104 
105 void
107 {
108  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
110  SequenceNumber32 head (1);
111  txBuf->SetHeadSequence (head);
112  SequenceNumber32 ret;
113  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
114  txBuf->SetSegmentSize (1000);
115  txBuf->SetDupAckThresh (3);
116 
117  txBuf->Add(Create<Packet> (10000));
118 
119  for (uint8_t i = 0; i <10 ; ++i)
120  txBuf->CopyFromSequence (1000, SequenceNumber32((i*1000)+1));
121 
122  for (uint8_t i = 0; i < 10 ; ++i)
123  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
124  "Lost is true, but it's not");
125 
127  txBuf->Update(sack->GetSackList());
128 
129  for (uint8_t i = 0; i < 10 ; ++i)
130  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
131  "Lost is true, but it's not");
132 
134  txBuf->Update(sack->GetSackList());
135 
136  for (uint8_t i = 0; i < 10 ; ++i)
137  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
138  "Lost is true, but it's not");
139 
141  txBuf->Update(sack->GetSackList());
142 
144  "Lost is true, but it's not");
145 
146  for (uint8_t i = 1; i < 10 ; ++i)
147  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
148  "Lost is true, but it's not");
149 
150 
151 }
152 
153 uint32_t
155 {
156  // Assume unlimited receiver window
158 }
159 
160 void
162 {
163  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();;
165  SequenceNumber32 head (1);
166  SequenceNumber32 ret;
167  SequenceNumber32 retHigh;
168  txBuf->SetSegmentSize (150);
169  txBuf->SetDupAckThresh (3);
170  uint32_t dupThresh = 3;
171  uint32_t segmentSize = 150;
172  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
173 
174  // At the beginning the values of dupThresh and segmentSize don't matter
175  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
176  "NextSeq should not be returned at the beginning");
177 
178  txBuf->SetHeadSequence (head);
179  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
180  "NextSeq should not be returned with no data");
181 
182  // Add a single, 30000-bytes long, packet
183  txBuf->Add (Create<Packet> (30000));
184  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
185  "No NextSeq with data at beginning");
186  NS_TEST_ASSERT_MSG_EQ (ret.GetValue (), head.GetValue (),
187  "Different NextSeq than expected at the beginning");
188 
189  // Simulate sending 100 packets, 150 bytes long each, from seq 1
190  for (uint32_t i=0; i<100; ++i)
191  {
192  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
193  "No NextSeq with data while \"transmitting\"");
194  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
195  "Different NextSeq than expected while \"transmitting\"");
196  txBuf->CopyFromSequence (segmentSize, ret);
197  }
198 
199  // Ok, now simulate we lost the first segment [1;151], and that we have
200  // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
201  SequenceNumber32 lastRet = ret; // This is like m_highTx
202  for (uint32_t i=1; i<dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
203  {
204  SequenceNumber32 begin = head + (segmentSize * i);
205  SequenceNumber32 end = begin + segmentSize;
206  sack->AddSackBlock (TcpOptionSack::SackBlock (begin, end));
207  txBuf->Update (sack->GetSackList ());
208 
209  // new data expected and sent
210  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
211  "No NextSeq with SACK block while \"transmitting\"");
212  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
213  "Different NextSeq than expected in limited transmit");
214  txBuf->CopyFromSequence (segmentSize, ret);
215  sack->ClearSackList ();
216  lastRet = ret;
217  }
218 
219  // Limited transmit was ok; now there is the dupThresh-th dupack.
220  // Now we need to retransmit the first block..
221  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh)),
222  head + (segmentSize * (dupThresh)) + segmentSize));
223  txBuf->Update (sack->GetSackList ());
224  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
225  "No NextSeq with SACK block for Fast Recovery");
226  NS_TEST_ASSERT_MSG_EQ (ret, head,
227  "Different NextSeq than expected for Fast Recovery");
228  txBuf->CopyFromSequence (segmentSize, ret);
229  sack->ClearSackList ();
230 
231  // Fast Retransmission was ok; now check some additional dupacks.
232  for (uint32_t i=1; i<=4; ++i)
233  {
234  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+i)),
235  head + (segmentSize * (dupThresh+i)) + segmentSize));
236  txBuf->Update (sack->GetSackList ());
237  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
238  "No NextSeq with SACK block after recv dupacks in FR");
239  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
240  "Different NextSeq than expected after recv dupacks in FR");
241  txBuf->CopyFromSequence (segmentSize, ret);
242  sack->ClearSackList ();
243  lastRet = ret;
244  }
245 
246  // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
247  // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
248  // to give us the next segment (head + segmentSize) to retransmit.
249  /* In this particular case, we are checking the fact that we have badly crafted
250  * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
251  * received dupack for 1. While receiving these, we crafted SACK block in the
252  * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
253  * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
254  * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
255  * 3,4 .. so keep them.
256  */
257  head = head + segmentSize;
258  txBuf->DiscardUpTo (head);
259 
260  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
261  "No NextSeq with SACK block after receiving partial ACK");
262  NS_TEST_ASSERT_MSG_EQ (ret, head,
263  "Different NextSeq than expected after receiving partial ACK ");
264  txBuf->CopyFromSequence (segmentSize, ret);
265 
266  // Now, check for one more dupack...
267  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+6)),
268  head + (segmentSize * (dupThresh+6)) + segmentSize));
269  txBuf->Update (sack->GetSackList ());
270  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
271  "No NextSeq with SACK block after recv dupacks after partial ack");
272  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
273  "Different NextSeq than expected after recv dupacks after partial ack");
274  txBuf->CopyFromSequence (segmentSize, ret);
275  sack->ClearSackList ();
276  head = lastRet = ret + segmentSize;
277 
278  // And now ack everything we sent to date!
279  txBuf->DiscardUpTo (head);
280 
281  // And continue normally until the end
282  for (uint32_t i=0; i<93; ++i)
283  {
284  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
285  "No NextSeq with data while \"transmitting\"");
286  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
287  "Different NextSeq than expected while \"transmitting\"");
288  txBuf->CopyFromSequence (segmentSize, ret);
289  }
290 
291  txBuf->DiscardUpTo (ret+segmentSize);
292  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
293  "Data inside the buffer");
294 }
295 
296 void
298 {
299  // Manually recreating all the conditions
300  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
302  txBuf->SetHeadSequence (SequenceNumber32 (1));
303  txBuf->SetSegmentSize (100);
304 
305  // get a packet which is exactly the same stored
306  Ptr<Packet> p1 = Create<Packet> (100);
307  txBuf->Add (p1);
308 
310  "TxBuf miscalculates size");
311  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
312  "TxBuf miscalculates size of in flight segments");
313 
314  Ptr<Packet> ret = txBuf->CopyFromSequence (100, SequenceNumber32 (1))->GetPacketCopy ();
315  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 100,
316  "Returned packet has different size than requested");
318  "TxBuf miscalculates size");
319  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 100,
320  "TxBuf miscalculates size of in flight segments");
321 
322  txBuf->DiscardUpTo (SequenceNumber32 (101));
324  "TxBuf miscalculates size");
325  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
326  "TxBuf miscalculates size of in flight segments");
327 
328  // starts over the boundary, but ends earlier
329 
330  Ptr<Packet> p2 = Create<Packet> (100);
331  txBuf->Add (p2);
332 
333  ret = txBuf->CopyFromSequence (50, SequenceNumber32 (101))->GetPacketCopy ();
334  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 50,
335  "Returned packet has different size than requested");
337  "TxBuf miscalculates size");
338  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 50,
339  "TxBuf miscalculates size of in flight segments");
340 
341  // starts over the boundary, but ends after
342  Ptr<Packet> p3 = Create<Packet> (100);
343  txBuf->Add (p3);
344 
345  ret = txBuf->CopyFromSequence (70, SequenceNumber32 (151))->GetPacketCopy ();
346  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 70,
347  "Returned packet has different size than requested");
349  "TxBuf miscalculates size");
350  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 120,
351  "TxBuf miscalculates size of in flight segments");
352 
353  ret = txBuf->CopyFromSequence (3000, SequenceNumber32 (221))->GetPacketCopy ();
354  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 80,
355  "Returned packet has different size than requested");
357  "TxBuf miscalculates size");
358  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 200,
359  "TxBuf miscalculates size of in flight segments");
360 
361  // Clear everything
362  txBuf->DiscardUpTo (SequenceNumber32 (381));
363  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
364  "Size is different than expected");
365 }
366 
367 void
369 {
370  TcpTxBuffer txBuf;
371  SequenceNumber32 head (1);
372  txBuf.SetHeadSequence (head);
373  txBuf.SetSegmentSize (2000);
374 
375  txBuf.Add(Create<Packet> (2000));
376  txBuf.CopyFromSequence (1000, SequenceNumber32(1));
377  txBuf.CopyFromSequence (1000, SequenceNumber32(1001));
378  txBuf.MarkHeadAsLost();
379 
380  // GetTransmittedSegment() will be called and handle the case that two items
381  // have different m_lost value.
382  txBuf.CopyFromSequence (2000, SequenceNumber32(1));
383 }
384 
385 void
387 {
388 }
389 
390 void
392 {
393 }
394 
402 {
403 public:
405  : TestSuite ("tcp-tx-buffer", UNIT)
406  {
407  AddTestCase (new TcpTxBufferTestCase, TestCase::QUICK);
408  }
409 };
410 
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
SequenceNumber< uint32_t, int32_t > SequenceNumber32
32 bit Sequence number.
TcpTxBufferTestCase()
Constructor.
A suite of tests to run.
Definition: test.h:1343
uint32_t segmentSize
void TestNextSeg()
Test the generation of the "next" block.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
encapsulates test code
Definition: test.h:1153
void AddSackBlock(SackBlock s)
Add a SACK block.
void TestIsLost()
Test if a segment is really set as lost.
The TcpTxBuffer Test.
#define max(a, b)
Definition: 80211b.c:43
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
uint32_t GetRWnd(void) const
Callback to provide a value of receiver window.
the TestSuite for the TcpTxBuffer test case
#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:166
void TestMergeItemsWhenGetTransmittedSegment()
Test the logic of merging items in GetTransmittedSegment() which is triggered by CopyFromSequence() ...
void DiscardUpTo(const SequenceNumber32 &seq, const Callback< void, TcpTxItem *> &beforeDelCb=m_nullCb)
Discard data up to but not including this sequence number.
TcpTxItem * CopyFromSequence(uint32_t numBytes, const SequenceNumber32 &seq)
Copy data from the range [seq, seq+numBytes) into a packet.
Tcp sender buffer.
void SetRWndCallback(Callback< uint32_t > rWndCallback)
Set callback to obtain receiver window value.
uint32_t SizeFromSequence(const SequenceNumber32 &seq) const
Returns the number of bytes from the buffer in the range [seq, tailSequence)
void TestTransmittedBlock()
Test the generation of a previously sent block.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
virtual void DoTeardown(void)
Implementation to do any local setup required for this TestCase.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
void TestNewBlock()
Test the generation of an unsent block.
void SetDupAckThresh(uint32_t dupAckThresh)
Set the DupAckThresh.
void MarkHeadAsLost()
Mark the head of the sent list as lost.
std::pair< SequenceNumber32, SequenceNumber32 > SackBlock
SACK block definition.
void SetSegmentSize(uint32_t segmentSize)
Set the segment size.
static TcpTxBufferTestSuite g_tcpTxBufferTestSuite
Static variable for test initialization.
uint32_t Size(void) const
Returns total number of bytes in this buffer.
Ptr< Packet > GetPacketCopy(void) const
Get a copy of the Packet underlying this item.
Definition: tcp-tx-item.cc:80
bool IsLost(const SequenceNumber32 &seq) const
Check if a segment is lost.
uint32_t Update(const TcpOptionSack::SackList &list, const Callback< void, TcpTxItem *> &sackedCb=m_nullCb)
Update the scoreboard.
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1278
virtual void DoRun(void)
Implementation to actually run this TestCase.
void ClearSackList(void)
Clear the SACK list.
bool NextSeg(SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
Get the next sequence number to transmit, according to RFC 6675.
This test suite implements a Unit Test.
Definition: test.h:1353
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
Callback< R, Ts... > MakeCallback(R(T::*memPtr)(Ts...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:1642
uint32_t BytesInFlight() const
Return total bytes in flight.
SackList GetSackList(void) const
Get the SACK list.
void SetHeadSequence(const SequenceNumber32 &seq)
Set the head sequence of the buffer.