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 ();
60  uint32_t GetRWnd (void) 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),
91  Simulator::Schedule (Seconds (0.0),
93 
94  /*
95  * Case for transmitted block:
96  * -> transmitted packets are marked differently for m_lost under some scenarios
97  * -> packets could be small than MSS when socket buffer is not a multiple of MSS.
98  * -> during retransmission, the sender tries to send a full segment but it
99  * should stop to merge items when they have different values for m_lost.
100  */
101  Simulator::Schedule (Seconds (0.0),
103 
104  Simulator::Run ();
105  Simulator::Destroy ();
106 }
107 
108 void
110 {
111  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
113  SequenceNumber32 head (1);
114  txBuf->SetHeadSequence (head);
115  SequenceNumber32 ret;
116  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
117  txBuf->SetSegmentSize (1000);
118  txBuf->SetDupAckThresh (3);
119 
120  txBuf->Add(Create<Packet> (10000));
121 
122  for (uint8_t i = 0; i <10 ; ++i)
123  txBuf->CopyFromSequence (1000, SequenceNumber32((i*1000)+1));
124 
125  for (uint8_t i = 0; i < 10 ; ++i)
126  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
127  "Lost is true, but it's not");
128 
130  txBuf->Update(sack->GetSackList());
131 
132  for (uint8_t i = 0; i < 10 ; ++i)
133  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
134  "Lost is true, but it's not");
135 
137  txBuf->Update(sack->GetSackList());
138 
139  for (uint8_t i = 0; i < 10 ; ++i)
140  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
141  "Lost is true, but it's not");
142 
144  txBuf->Update(sack->GetSackList());
145 
147  "Lost is true, but it's not");
148 
149  for (uint8_t i = 1; i < 10 ; ++i)
150  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32((i*1000)+1)), false,
151  "Lost is true, but it's not");
152 
153 
154 }
155 
156 uint32_t
158 {
159  // Assume unlimited receiver window
161 }
162 
163 void
165 {
166  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();;
168  SequenceNumber32 head (1);
169  SequenceNumber32 ret;
170  SequenceNumber32 retHigh;
171  txBuf->SetSegmentSize (150);
172  txBuf->SetDupAckThresh (3);
173  uint32_t dupThresh = 3;
174  uint32_t segmentSize = 150;
175  Ptr<TcpOptionSack> sack = CreateObject<TcpOptionSack> ();
176 
177  // At the beginning the values of dupThresh and segmentSize don't matter
178  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
179  "NextSeq should not be returned at the beginning");
180 
181  txBuf->SetHeadSequence (head);
182  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), false,
183  "NextSeq should not be returned with no data");
184 
185  // Add a single, 30000-bytes long, packet
186  txBuf->Add (Create<Packet> (30000));
187  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
188  "No NextSeq with data at beginning");
189  NS_TEST_ASSERT_MSG_EQ (ret.GetValue (), head.GetValue (),
190  "Different NextSeq than expected at the beginning");
191 
192  // Simulate sending 100 packets, 150 bytes long each, from seq 1
193  for (uint32_t i=0; i<100; ++i)
194  {
195  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
196  "No NextSeq with data while \"transmitting\"");
197  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
198  "Different NextSeq than expected while \"transmitting\"");
199  txBuf->CopyFromSequence (segmentSize, ret);
200  }
201 
202  // Ok, now simulate we lost the first segment [1;151], and that we have
203  // limited transmit. NextSeg should return (up to dupThresh-1) new pieces of data
204  SequenceNumber32 lastRet = ret; // This is like m_highTx
205  for (uint32_t i=1; i<dupThresh; ++i) // iterate dupThresh-1 times (limited transmit)
206  {
207  SequenceNumber32 begin = head + (segmentSize * i);
208  SequenceNumber32 end = begin + segmentSize;
209  sack->AddSackBlock (TcpOptionSack::SackBlock (begin, end));
210  txBuf->Update (sack->GetSackList ());
211 
212  // new data expected and sent
213  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
214  "No NextSeq with SACK block while \"transmitting\"");
215  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
216  "Different NextSeq than expected in limited transmit");
217  txBuf->CopyFromSequence (segmentSize, ret);
218  sack->ClearSackList ();
219  lastRet = ret;
220  }
221 
222  // Limited transmit was ok; now there is the dupThresh-th dupack.
223  // Now we need to retransmit the first block..
224  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh)),
225  head + (segmentSize * (dupThresh)) + segmentSize));
226  txBuf->Update (sack->GetSackList ());
227  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
228  "No NextSeq with SACK block for Fast Recovery");
229  NS_TEST_ASSERT_MSG_EQ (ret, head,
230  "Different NextSeq than expected for Fast Recovery");
231  txBuf->CopyFromSequence (segmentSize, ret);
232  sack->ClearSackList ();
233 
234  // Fast Retransmission was ok; now check some additional dupacks.
235  for (uint32_t i=1; i<=4; ++i)
236  {
237  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+i)),
238  head + (segmentSize * (dupThresh+i)) + segmentSize));
239  txBuf->Update (sack->GetSackList ());
240  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
241  "No NextSeq with SACK block after recv dupacks in FR");
242  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
243  "Different NextSeq than expected after recv dupacks in FR");
244  txBuf->CopyFromSequence (segmentSize, ret);
245  sack->ClearSackList ();
246  lastRet = ret;
247  }
248 
249  // Well now we receive a partial ACK, corresponding to the segment we retransmitted.
250  // Unfortunately, the next one is lost as well; but NextSeg should be smart enough
251  // to give us the next segment (head + segmentSize) to retransmit.
252  /* In this particular case, we are checking the fact that we have badly crafted
253  * the SACK blocks. Talking in segment, we transmitted 1,2,3,4,5 ... and then
254  * received dupack for 1. While receiving these, we crafted SACK block in the
255  * way that 2,3,4,... were correctly received. Now, if we receive an ACK for 2,
256  * we clearly crafted the corresponding ACK wrongly. TcpTxBuffer should be able
257  * to "backoff" that flag on its HEAD (segment 2). We still don't know for segment
258  * 3,4 .. so keep them.
259  */
260  head = head + segmentSize;
261  txBuf->DiscardUpTo (head);
262 
263  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
264  "No NextSeq with SACK block after receiving partial ACK");
265  NS_TEST_ASSERT_MSG_EQ (ret, head,
266  "Different NextSeq than expected after receiving partial ACK ");
267  txBuf->CopyFromSequence (segmentSize, ret);
268 
269  // Now, check for one more dupack...
270  sack->AddSackBlock (TcpOptionSack::SackBlock (head + (segmentSize * (dupThresh+6)),
271  head + (segmentSize * (dupThresh+6)) + segmentSize));
272  txBuf->Update (sack->GetSackList ());
273  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
274  "No NextSeq with SACK block after recv dupacks after partial ack");
275  NS_TEST_ASSERT_MSG_EQ (ret, lastRet + segmentSize,
276  "Different NextSeq than expected after recv dupacks after partial ack");
277  txBuf->CopyFromSequence (segmentSize, ret);
278  sack->ClearSackList ();
279  head = lastRet = ret + segmentSize;
280 
281  // And now ack everything we sent to date!
282  txBuf->DiscardUpTo (head);
283 
284  // And continue normally until the end
285  for (uint32_t i=0; i<93; ++i)
286  {
287  NS_TEST_ASSERT_MSG_EQ (txBuf->NextSeg (&ret, &retHigh, false), true,
288  "No NextSeq with data while \"transmitting\"");
289  NS_TEST_ASSERT_MSG_EQ (ret, head + (segmentSize * i),
290  "Different NextSeq than expected while \"transmitting\"");
291  txBuf->CopyFromSequence (segmentSize, ret);
292  }
293 
294  txBuf->DiscardUpTo (ret+segmentSize);
295  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
296  "Data inside the buffer");
297 }
298 
299 void
301 {
302  // Manually recreating all the conditions
303  Ptr<TcpTxBuffer> txBuf = CreateObject<TcpTxBuffer> ();
305  txBuf->SetHeadSequence (SequenceNumber32 (1));
306  txBuf->SetSegmentSize (100);
307 
308  // get a packet which is exactly the same stored
309  Ptr<Packet> p1 = Create<Packet> (100);
310  txBuf->Add (p1);
311 
313  "TxBuf miscalculates size");
314  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
315  "TxBuf miscalculates size of in flight segments");
316 
317  Ptr<Packet> ret = txBuf->CopyFromSequence (100, SequenceNumber32 (1))->GetPacketCopy ();
318  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 100,
319  "Returned packet has different size than requested");
321  "TxBuf miscalculates size");
322  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 100,
323  "TxBuf miscalculates size of in flight segments");
324 
325  txBuf->DiscardUpTo (SequenceNumber32 (101));
327  "TxBuf miscalculates size");
328  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 0,
329  "TxBuf miscalculates size of in flight segments");
330 
331  // starts over the boundary, but ends earlier
332 
333  Ptr<Packet> p2 = Create<Packet> (100);
334  txBuf->Add (p2);
335 
336  ret = txBuf->CopyFromSequence (50, SequenceNumber32 (101))->GetPacketCopy ();
337  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 50,
338  "Returned packet has different size than requested");
340  "TxBuf miscalculates size");
341  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 50,
342  "TxBuf miscalculates size of in flight segments");
343 
344  // starts over the boundary, but ends after
345  Ptr<Packet> p3 = Create<Packet> (100);
346  txBuf->Add (p3);
347 
348  ret = txBuf->CopyFromSequence (70, SequenceNumber32 (151))->GetPacketCopy ();
349  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 70,
350  "Returned packet has different size than requested");
352  "TxBuf miscalculates size");
353  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 120,
354  "TxBuf miscalculates size of in flight segments");
355 
356  ret = txBuf->CopyFromSequence (3000, SequenceNumber32 (221))->GetPacketCopy ();
357  NS_TEST_ASSERT_MSG_EQ (ret->GetSize (), 80,
358  "Returned packet has different size than requested");
360  "TxBuf miscalculates size");
361  NS_TEST_ASSERT_MSG_EQ (txBuf->BytesInFlight (), 200,
362  "TxBuf miscalculates size of in flight segments");
363 
364  // Clear everything
365  txBuf->DiscardUpTo (SequenceNumber32 (381));
366  NS_TEST_ASSERT_MSG_EQ (txBuf->Size (), 0,
367  "Size is different than expected");
368 }
369 
370 void
372 {
373  TcpTxBuffer txBuf;
374  SequenceNumber32 head (1);
375  txBuf.SetHeadSequence (head);
376  txBuf.SetSegmentSize (2000);
377 
378  txBuf.Add(Create<Packet> (2000));
379  txBuf.CopyFromSequence (1000, SequenceNumber32(1));
380  txBuf.CopyFromSequence (1000, SequenceNumber32(1001));
381  txBuf.MarkHeadAsLost();
382 
383  // GetTransmittedSegment() will be called and handle the case that two items
384  // have different m_lost value.
385  txBuf.CopyFromSequence (2000, SequenceNumber32(1));
386 }
387 
388 void
390 {
391 }
392 
393 void
395 {
396 }
397 
405 {
406 public:
408  : TestSuite ("tcp-tx-buffer", UNIT)
409  {
410  AddTestCase (new TcpTxBufferTestCase, TestCase::QUICK);
411  }
412 };
413 
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:1289
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.