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 "ns3/test.h"
19 #include "ns3/tcp-tx-buffer.h"
20 #include "ns3/packet.h"
21 #include "ns3/simulator.h"
22 #include "ns3/log.h"
23 
24 using namespace ns3;
25 
26 NS_LOG_COMPONENT_DEFINE ("TcpTxBufferTestSuite");
27 
35 {
36 public:
39 
40 private:
41  virtual void DoRun (void);
42  virtual void DoTeardown (void);
43 
45  void TestNewBlock ();
47  void TestTransmittedBlock ();
49  void TestNextSeg ();
51  void TestUpdateScoreboardWithCraftedSACK ();
52 };
53 
55  : TestCase ("TcpTxBuffer Test")
56 {
57 }
58 
59 void
61 {
62  /*
63  * Cases for new block:
64  * -> is exactly the same as stored
65  * -> starts over the boundary, but ends earlier
66  * -> starts over the boundary, but ends after
67  */
68  Simulator::Schedule (Seconds (0.0), &TcpTxBufferTestCase::TestNewBlock, this);
69 
70  /*
71  * Cases for transmitted block:
72  * -> is exactly the same as previous
73  * -> starts over the boundary, but ends earlier
74  * -> starts over the boundary, but ends after
75  * -> starts inside a packet, ends right
76  * -> starts inside a packet, ends earlier in the same packet
77  * -> starts inside a packet, ends in another packet
78  */
79  Simulator::Schedule (Seconds (0.0),
81  Simulator::Schedule (Seconds (0.0),
83  Simulator::Schedule (Seconds (0.0),
85 
86  Simulator::Run ();
87  Simulator::Destroy ();
88 }
89 
90 void
92 {
93  TcpTxBuffer txBuf;
94  SequenceNumber32 head (1);
95  SequenceNumber32 ret;
96  uint32_t dupThresh = 3;
97  uint32_t segmentSize = 150;
98  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
99 
100  // At the beginning the values of dupThresh and segmentSize don't matter
101  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, 0, 0, false), false,
102  "NextSeq should not be returned at the beginning");
103 
104  txBuf.SetHeadSequence (head);
105  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, 0, 0, false), false,
106  "NextSeq should not be returned with no data");
107 
108  // Add a single, 3000-bytes long, packet
109  txBuf.Add (Create<Packet> (30000));
110  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, 0, 0, false), true,
111  "No NextSeq with data at beginning");
112  NS_TEST_ASSERT_MSG_EQ (ret.GetValue (), head.GetValue (),
113  "Different NextSeq than expected at the beginning");
114 
115  // Simulate sending 100 packets, 150 bytes long each, from seq 1
116  for (uint32_t i=0; i<100; ++i)
117  {
118  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
119  "No NextSeq with data while \"transmitting\"");
120  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
121  "Different NextSeq than expected while \"transmitting\"");
122  txBuf.CopyFromSequence (segmentSize, ret);
123  }
124 
125  // Ok, now simulate we lost the first segment [1;151], and that we have
126  // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
127  SequenceNumber32 lastRet = ret; // This is like m_highTx
128  for (uint32_t i=1; i<dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
129  {
130  SequenceNumber32 begin = head + (segmentSize * i);
131  SequenceNumber32 end = begin + segmentSize;
132  sack->AddSackBlock (TcpOptionSack::SackBlock (begin, end));
133  txBuf.Update (sack->GetSackList ());
134 
135  // new data expected and sent
136  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
137  "No NextSeq with SACK block while \"transmitting\"");
138  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
139  "Different NextSeq than expected in limited transmit");
140  txBuf.CopyFromSequence (segmentSize, ret);
141  sack->ClearSackList ();
142  lastRet = ret;
143  }
144 
145  // Limited transmit was ok; now there is the dupThresh-th dupack.
146  // Now we need to retransmit the first block..
147  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh)),
148  head + (segmentSize * (dupThresh)) + segmentSize));
149  txBuf.Update (sack->GetSackList ());
150  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
151  "No NextSeq with SACK block for Fast Recovery");
152  NS_TEST_ASSERT_MSG_EQ (ret, head,
153  "Different NextSeq than expected for Fast Recovery");
154  txBuf.CopyFromSequence (segmentSize, ret);
155  sack->ClearSackList ();
156 
157  // Fast Retransmission was ok; now check some additional dupacks.
158  for (uint32_t i=1; i<=4; ++i)
159  {
160  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+i)),
161  head + (segmentSize * (dupThresh+i)) + segmentSize));
162  txBuf.Update (sack->GetSackList ());
163  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
164  "No NextSeq with SACK block after recv dupacks in FR");
165  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
166  "Different NextSeq than expected after recv dupacks in FR");
167  txBuf.CopyFromSequence (segmentSize, ret);
168  sack->ClearSackList ();
169  lastRet = ret;
170  }
171 
172  // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
173  // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
174  // to give us the next segment (head + segmentSize) to retransmit.
175  /* In this particular case, we are checking the fact that we have badly crafted
176  * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
177  * received dupack for 1. While receiving these, we crafted SACK block in the
178  * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
179  * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
180  * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
181  * 3,4 .. so keep them.
182  */
183  head = head + segmentSize;
184  txBuf.DiscardUpTo (head);
185  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
186  "No NextSeq with SACK block after receiving partial ACK");
187  NS_TEST_ASSERT_MSG_EQ (ret, head,
188  "Different NextSeq than expected after receiving partial ACK");
189  txBuf.CopyFromSequence (segmentSize, ret);
190 
191  // Now, check for one more dupack...
192  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+6)),
193  head + (segmentSize * (dupThresh+6)) + segmentSize));
194  txBuf.Update (sack->GetSackList ());
195  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
196  "No NextSeq with SACK block after recv dupacks after partial ack");
197  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
198  "Different NextSeq than expected after recv dupacks after partial ack");
199  txBuf.CopyFromSequence (segmentSize, ret);
200  sack->ClearSackList ();
201  head = lastRet = ret + segmentSize;
202 
203  // And now ack everything we sent to date!
204  txBuf.DiscardUpTo (head);
205 
206  // And continue normally until the end
207  for (uint32_t i=0; i<93; ++i)
208  {
209  NS_TEST_ASSERT_MSG_EQ (txBuf.NextSeg (&ret, dupThresh, segmentSize, false), true,
210  "No NextSeq with data while \"transmitting\"");
211  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
212  "Different NextSeq than expected while \"transmitting\"");
213  txBuf.CopyFromSequence (segmentSize, ret);
214  }
215 
216  txBuf.DiscardUpTo (ret+segmentSize);
217  NS_TEST_ASSERT_MSG_EQ (txBuf.Size (), 0,
218  "Data inside the buffer");
219 }
220 
221 void
223 {
224  // Manually recreating all the conditions
225  TcpTxBuffer txBuf;
226  txBuf.SetHeadSequence (SequenceNumber32 (1));
227 
228  // get a packet which is exactly the same stored
229  Ptr<Packet> p1 = Create<Packet> (100);
230  txBuf.Add (p1);
231 
233  "TxBuf miscalculates size");
234  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 0,
235  // "TxBuf miscalculates size of in flight segments");
236 
237  Ptr<Packet> ret = txBuf.CopyFromSequence (100, SequenceNumber32 (1));
238  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 100,
239  "Returned packet has different size than requested");
241  "TxBuf miscalculates size");
242  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 100,
243  // "TxBuf miscalculates size of in flight segments");
244 
245  txBuf.DiscardUpTo (SequenceNumber32 (101));
247  "TxBuf miscalculates size");
248  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 0,
249  // "TxBuf miscalculates size of in flight segments");
250 
251  // starts over the boundary, but ends earlier
252 
253  Ptr<Packet> p2 = Create<Packet> (100);
254  txBuf.Add (p2);
255 
256  ret = txBuf.CopyFromSequence (50, SequenceNumber32 (101));
257  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 50,
258  "Returned packet has different size than requested");
260  "TxBuf miscalculates size");
261  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 50,
262  // "TxBuf miscalculates size of in flight segments");
263 
264  // starts over the boundary, but ends after
265  Ptr<Packet> p3 = Create<Packet> (100);
266  txBuf.Add (p3);
267 
268  ret = txBuf.CopyFromSequence (70, SequenceNumber32 (151));
269  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 70,
270  "Returned packet has different size than requested");
272  "TxBuf miscalculates size");
273  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 120,
274  // "TxBuf miscalculates size of in flight segments");
275 
276  ret = txBuf.CopyFromSequence (3000, SequenceNumber32 (221));
277  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 80,
278  "Returned packet has different size than requested");
280  "TxBuf miscalculates size");
281  //NS_TEST_ASSERT_MSG_EQ (txBuf.BytesInFlight (), 200,
282  // "TxBuf miscalculates size of in flight segments");
283 
284  // Clear everything
285  txBuf.DiscardUpTo (SequenceNumber32 (381));
286  NS_TEST_ASSERT_MSG_EQ (txBuf.Size (), 0,
287  "Size is different than expected");
288 }
289 
290 void
292 {
293  TcpTxBuffer txBuf;
294  SequenceNumber32 head (1);
295  txBuf.SetHeadSequence (head);
296 
297  // Add a single, 3000-bytes long, packet
298  txBuf.Add (Create<Packet> (30000));
299 
300  // Simulate sending 100 packets, 150 bytes long each, from seq 1
301  for (uint32_t i=0; i<100; ++i)
302  {
303  txBuf.CopyFromSequence (150, head + (150 * i));
304  }
305 
306  // Now we have 100 packets sent, 100 waiting (well, that 100 are condensed in one)
307 
308  // Suppose now we receive 99 dupacks, because the first was lost.
309  for (uint32_t i=0; i<99; ++i)
310  {
311  Ptr<const TcpOptionSack> sack = txBuf.CraftSackOption (head, 32); // 3 maximum sack block
312 
313  // For iteration 0 and 1 we have 1 and 2 sack blocks, respectively.
314  // For all others, maximum 3
315  if (i == 0)
316  {
318  "Different block number than expected");
319  }
320  else if (i == 1)
321  {
323  "Different block number than expected");
324  }
325  else if (i >= 2)
326  {
328  "More blocks than expected");
329  }
330 
331  TcpOptionSack::SackList sackList = sack->GetSackList ();
332  TcpOptionSack::SackBlock block = sackList.front ();
333  sackList.pop_front ();
334 
335  // The first block, assuming all the other are SACKed in order (from 2nd
336  // onward) has seq = 1 + (150 * (i+1)) --> i+1 because the first sent
337  // block cannot be SACKed
338  NS_TEST_ASSERT_MSG_EQ (block.first, SequenceNumber32 (1 + (150*(i+1))),
339  "First sack block is wrong (on the left)");
340  NS_TEST_ASSERT_MSG_EQ (block.second, block.first + 150,
341  "First sack block is wrong (on the right)");
342 
343  SequenceNumber32 left = block.first;
344  for (TcpOptionSack::SackList::iterator it = sackList.begin (); it != sackList.end (); ++it)
345  {
346  block = (*it);
347 
348  // the blocks go backwards here. To understand better, an example
349  // of SACK option list: [1351;1501], [1201;1351], [1051;1201]
350  NS_TEST_ASSERT_MSG_EQ (block.first, left - 150,
351  "First sack block is wrong (on the left)");
352  NS_TEST_ASSERT_MSG_EQ (block.second, left,
353  "First sack block is wrong (on the right)");
354  left -= 150;
355  }
356 
357  txBuf.Update (sack->GetSackList ());
358 
359  if (i == 0)
360  {
361  NS_TEST_ASSERT_MSG_EQ (false, txBuf.IsLost (SequenceNumber32 (1), 3, 150),
362  "SequenceNumber 1 isLost, but for RFC 6675 is not");
363  }
364  else if (i == 1)
365  {
366  NS_TEST_ASSERT_MSG_EQ (false, txBuf.IsLost (SequenceNumber32 (1), 3, 150),
367  "SequenceNumber 1 isLost, but for RFC 6675 is not");
368  SequenceNumber32 lost (1 + (150 * i));
369  NS_TEST_ASSERT_MSG_EQ (false, txBuf.IsLost (lost, 3, 150),
370  "SequenceNumber " << lost <<
371  "isLost, but for RFC 6675 is because is SACKed");
372  }
373  else if (i >= 2)
374  {
375  NS_TEST_ASSERT_MSG_EQ (true, txBuf.IsLost (SequenceNumber32 (1), 3, 150),
376  "SequenceNumber 1 ! isLost, but for RFC 6675 is");
377  SequenceNumber32 lost (1 + (150 * i));
378  NS_TEST_ASSERT_MSG_EQ (false, txBuf.IsLost (lost, 3, 150),
379  "SequenceNumber " << lost <<
380  "isLost, but for RFC 6675 is because is SACKed");
381  }
382  }
383 
384  for (uint32_t i=0; i<100; ++i)
385  {
386  txBuf.DiscardUpTo (SequenceNumber32 (30001));
387  }
388 
389  NS_TEST_ASSERT_MSG_EQ (txBuf.Size (), 0,
390  "Data inside the buffer");
391 }
392 
393 void
395 {
396 }
397 
398 void
400 {
401 }
402 
410 {
411 public:
413  : TestSuite ("tcp-tx-buffer", UNIT)
414  {
415  AddTestCase (new TcpTxBufferTestCase, TestCase::QUICK);
416  }
417 };
418 
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
uint32_t Size(void) const
Returns total number of bytes in this buffer.
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
void DiscardUpTo(const SequenceNumber32 &seq)
Discard data up to but not including this sequence number.
uint32_t SizeFromSequence(const SequenceNumber32 &seq) const
Returns the number of bytes from the buffer in the range [seq, tailSequence)
bool IsLost(const SequenceNumber32 &seq, uint32_t dupThresh, uint32_t segmentSize) const
Check if a segment is lost per RFC 6675.
TcpTxBufferTestCase()
Constructor.
A suite of tests to run.
Definition: test.h:1342
std::list< SackBlock > SackList
SACK list definition.
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:201
void TestUpdateScoreboardWithCraftedSACK()
Test the scoreboard with emulated SACK.
encapsulates test code
Definition: test.h:1155
This test suite implements a Unit Test.
Definition: test.h:1352
void AddSackBlock(SackBlock s)
Add a SACK block.
The TcpTxBuffer Test.
SackList GetSackList(void) const
Get the SACK list.
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
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:168
Tcp sender buffer.
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 NextSeg(SequenceNumber32 *seq, uint32_t dupThresh, uint32_t segmentSize, bool isRecovery) const
Get the next sequence number to transmit, according to RFC 6675.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
void TestNewBlock()
Test the generation of an unsent block.
uint32_t GetNumSackBlocks(void) const
Count the total number of SACK blocks.
std::pair< SequenceNumber32, SequenceNumber32 > SackBlock
SACK block definition.
bool Update(const TcpOptionSack::SackList &list)
Update the scoreboard.
static TcpTxBufferTestSuite g_tcpTxBufferTestSuite
Static variable for test initialization.
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:993
virtual void DoRun(void)
Implementation to actually run this TestCase.
void ClearSackList(void)
Clear the SACK list.
Ptr< const TcpOptionSack > CraftSackOption(const SequenceNumber32 &seq, uint8_t available) const
Craft a SACK block.
void SetHeadSequence(const SequenceNumber32 &seq)
Set the head sequence of the buffer.
Ptr< Packet > CopyFromSequence(uint32_t numBytes, const SequenceNumber32 &seq)
Copy data from the range [seq, seq+numBytes) into a packet.