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 ();
54  uint32_t GetRWnd (void) const;
55 };
56 
58  : TestCase ("TcpTxBuffer Test")
59 {
60 }
61 
62 void
64 {
65  Simulator::Schedule (Seconds (0.0), &TcpTxBufferTestCase::TestIsLost, this);
66  /*
67  * Cases for new block:
68  * -> is exactly the same as stored
69  * -> starts over the boundary, but ends earlier
70  * -> starts over the boundary, but ends after
71  */
72  Simulator::Schedule (Seconds (0.0), &TcpTxBufferTestCase::TestNewBlock, this);
73 
74  /*
75  * Cases for transmitted block:
76  * -> is exactly the same as previous
77  * -> starts over the boundary, but ends earlier
78  * -> starts over the boundary, but ends after
79  * -> starts inside a packet, ends right
80  * -> starts inside a packet, ends earlier in the same packet
81  * -> starts inside a packet, ends in another packet
82  */
83  Simulator::Schedule (Seconds (0.0),
85  Simulator::Schedule (Seconds (0.0),
87 
88  Simulator::Run ();
89  Simulator::Destroy ();
90 }
91 
92 void
94 {
95  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
97  SequenceNumber32 head (1);
98  txBuf->SetHeadSequence (head);
99  SequenceNumber32 ret;
100  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
101  txBuf->SetSegmentSize (1000);
102  txBuf->SetDupAckThresh (3);
103 
104  txBuf->Add(Create<Packet> (10000));
105 
106  for (uint8_t i = 0; i <10 ; ++i)
107  txBuf->CopyFromSequence (1000, SequenceNumber32((i*1000)+1));
108 
109  for (uint8_t i = 0; i < 10 ; ++i)
110  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
111  "Lost is true, but it's not");
112 
114  txBuf->Update(sack->GetSackList());
115 
116  for (uint8_t i = 0; i < 10 ; ++i)
117  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
118  "Lost is true, but it's not");
119 
121  txBuf->Update(sack->GetSackList());
122 
123  for (uint8_t i = 0; i < 10 ; ++i)
124  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
125  "Lost is true, but it's not");
126 
128  txBuf->Update(sack->GetSackList());
129 
131  "Lost is true, but it's not");
132 
133  for (uint8_t i = 1; i < 10 ; ++i)
134  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
135  "Lost is true, but it's not");
136 
137 
138 }
139 
140 uint32_t
142 {
143  // Assume unlimited receiver window
145 }
146 
147 void
149 {
150  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();;
152  SequenceNumber32 head (1);
153  SequenceNumber32 ret;
154  SequenceNumber32 retHigh;
155  txBuf->SetSegmentSize (150);
156  txBuf->SetDupAckThresh (3);
157  uint32_t dupThresh = 3;
158  uint32_t segmentSize = 150;
159  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
160 
161  // At the beginning the values of dupThresh and segmentSize don't matter
162  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
163  "NextSeq should not be returned at the beginning");
164 
165  txBuf->SetHeadSequence (head);
166  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
167  "NextSeq should not be returned with no data");
168 
169  // Add a single, 30000-bytes long, packet
170  txBuf->Add (Create<Packet> (30000));
171  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
172  "No NextSeq with data at beginning");
173  NS_TEST_ASSERT_MSG_EQ (ret.GetValue (), head.GetValue (),
174  "Different NextSeq than expected at the beginning");
175 
176  // Simulate sending 100 packets, 150 bytes long each, from seq 1
177  for (uint32_t i=0; i<100; ++i)
178  {
179  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
180  "No NextSeq with data while \"transmitting\"");
181  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
182  "Different NextSeq than expected while \"transmitting\"");
183  txBuf->CopyFromSequence (segmentSize, ret);
184  }
185 
186  // Ok, now simulate we lost the first segment [1;151], and that we have
187  // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
188  SequenceNumber32 lastRet = ret; // This is like m_highTx
189  for (uint32_t i=1; i<dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
190  {
191  SequenceNumber32 begin = head + (segmentSize * i);
192  SequenceNumber32 end = begin + segmentSize;
193  sack->AddSackBlock (TcpOptionSack::SackBlock (begin, end));
194  txBuf->Update (sack->GetSackList ());
195 
196  // new data expected and sent
197  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
198  "No NextSeq with SACK block while \"transmitting\"");
199  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
200  "Different NextSeq than expected in limited transmit");
201  txBuf->CopyFromSequence (segmentSize, ret);
202  sack->ClearSackList ();
203  lastRet = ret;
204  }
205 
206  // Limited transmit was ok; now there is the dupThresh-th dupack.
207  // Now we need to retransmit the first block..
208  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh)),
209  head + (segmentSize * (dupThresh)) + segmentSize));
210  txBuf->Update (sack->GetSackList ());
211  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
212  "No NextSeq with SACK block for Fast Recovery");
213  NS_TEST_ASSERT_MSG_EQ (ret, head,
214  "Different NextSeq than expected for Fast Recovery");
215  txBuf->CopyFromSequence (segmentSize, ret);
216  sack->ClearSackList ();
217 
218  // Fast Retransmission was ok; now check some additional dupacks.
219  for (uint32_t i=1; i<=4; ++i)
220  {
221  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+i)),
222  head + (segmentSize * (dupThresh+i)) + segmentSize));
223  txBuf->Update (sack->GetSackList ());
224  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
225  "No NextSeq with SACK block after recv dupacks in FR");
226  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
227  "Different NextSeq than expected after recv dupacks in FR");
228  txBuf->CopyFromSequence (segmentSize, ret);
229  sack->ClearSackList ();
230  lastRet = ret;
231  }
232 
233  // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
234  // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
235  // to give us the next segment (head + segmentSize) to retransmit.
236  /* In this particular case, we are checking the fact that we have badly crafted
237  * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
238  * received dupack for 1. While receiving these, we crafted SACK block in the
239  * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
240  * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
241  * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
242  * 3,4 .. so keep them.
243  */
244  head = head + segmentSize;
245  txBuf->DiscardUpTo (head);
246 
247  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
248  "No NextSeq with SACK block after receiving partial ACK");
249  NS_TEST_ASSERT_MSG_EQ (ret, head,
250  "Different NextSeq than expected after receiving partial ACK ");
251  txBuf->CopyFromSequence (segmentSize, ret);
252 
253  // Now, check for one more dupack...
254  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+6)),
255  head + (segmentSize * (dupThresh+6)) + segmentSize));
256  txBuf->Update (sack->GetSackList ());
257  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
258  "No NextSeq with SACK block after recv dupacks after partial ack");
259  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
260  "Different NextSeq than expected after recv dupacks after partial ack");
261  txBuf->CopyFromSequence (segmentSize, ret);
262  sack->ClearSackList ();
263  head = lastRet = ret + segmentSize;
264 
265  // And now ack everything we sent to date!
266  txBuf->DiscardUpTo (head);
267 
268  // And continue normally until the end
269  for (uint32_t i=0; i<93; ++i)
270  {
271  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
272  "No NextSeq with data while \"transmitting\"");
273  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
274  "Different NextSeq than expected while \"transmitting\"");
275  txBuf->CopyFromSequence (segmentSize, ret);
276  }
277 
278  txBuf->DiscardUpTo (ret+segmentSize);
279  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
280  "Data inside the buffer");
281 }
282 
283 void
285 {
286  // Manually recreating all the conditions
287  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
289  txBuf->SetHeadSequence (SequenceNumber32 (1));
290  txBuf->SetSegmentSize (100);
291 
292  // get a packet which is exactly the same stored
293  Ptr<Packet> p1 = Create<Packet> (100);
294  txBuf->Add (p1);
295 
297  "TxBuf miscalculates size");
298  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
299  "TxBuf miscalculates size of in flight segments");
300 
301  Ptr<Packet> ret = txBuf->CopyFromSequence (100, SequenceNumber32 (1))->GetPacketCopy ();
302  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 100,
303  "Returned packet has different size than requested");
305  "TxBuf miscalculates size");
306  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 100,
307  "TxBuf miscalculates size of in flight segments");
308 
309  txBuf->DiscardUpTo (SequenceNumber32 (101));
311  "TxBuf miscalculates size");
312  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
313  "TxBuf miscalculates size of in flight segments");
314 
315  // starts over the boundary, but ends earlier
316 
317  Ptr<Packet> p2 = Create<Packet> (100);
318  txBuf->Add (p2);
319 
320  ret = txBuf->CopyFromSequence (50, SequenceNumber32 (101))->GetPacketCopy ();
321  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 50,
322  "Returned packet has different size than requested");
324  "TxBuf miscalculates size");
325  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 50,
326  "TxBuf miscalculates size of in flight segments");
327 
328  // starts over the boundary, but ends after
329  Ptr<Packet> p3 = Create<Packet> (100);
330  txBuf->Add (p3);
331 
332  ret = txBuf->CopyFromSequence (70, SequenceNumber32 (151))->GetPacketCopy ();
333  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 70,
334  "Returned packet has different size than requested");
336  "TxBuf miscalculates size");
337  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 120,
338  "TxBuf miscalculates size of in flight segments");
339 
340  ret = txBuf->CopyFromSequence (3000, SequenceNumber32 (221))->GetPacketCopy ();
341  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 80,
342  "Returned packet has different size than requested");
344  "TxBuf miscalculates size");
345  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 200,
346  "TxBuf miscalculates size of in flight segments");
347 
348  // Clear everything
349  txBuf->DiscardUpTo (SequenceNumber32 (381));
350  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
351  "Size is different than expected");
352 }
353 
354 void
356 {
357 }
358 
359 void
361 {
362 }
363 
371 {
372 public:
374  : TestSuite ("tcp-tx-buffer", UNIT)
375  {
376  AddTestCase (new TcpTxBufferTestCase, TestCase::QUICK);
377  }
378 };
379 
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
TcpTxBufferTestCase()
Constructor.
A suite of tests to run.
Definition: test.h:1343
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 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.
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.
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:1062
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.