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> ();
112  txBuf->SetRWndCallback (MakeCallback (&TcpTxBufferTestCase::GetRWnd, this));
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 
129  sack->AddSackBlock (TcpOptionSack::SackBlock (SequenceNumber32 (1001), SequenceNumber32 (2001)));
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 
136  sack->AddSackBlock (TcpOptionSack::SackBlock (SequenceNumber32 (2001), SequenceNumber32 (3001)));
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 
143  sack->AddSackBlock (TcpOptionSack::SackBlock (SequenceNumber32 (3001), SequenceNumber32 (4001)));
144  txBuf->Update(sack->GetSackList());
145 
146  NS_TEST_ASSERT_MSG_EQ (txBuf->IsLost(SequenceNumber32(1)), true,
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> ();;
167  txBuf->SetRWndCallback (MakeCallback (&TcpTxBufferTestCase::GetRWnd, this));
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> ();
304  txBuf->SetRWndCallback (MakeCallback (&TcpTxBufferTestCase::GetRWnd, this));
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 
312  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (1)), 100,
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");
320  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (1)), 100,
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));
326  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (101)), 0,
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");
339  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (151)), 50,
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");
351  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (221)), 80,
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");
359  NS_TEST_ASSERT_MSG_EQ (txBuf->SizeFromSequence (SequenceNumber32 (301)), 0,
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 
#define max(a, b)
Definition: 80211b.c:43
The TcpTxBuffer Test.
virtual void DoRun(void)
Implementation to actually run this TestCase.
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 TestNewBlock()
Test the generation of an unsent block.
uint32_t GetRWnd(void) const
Callback to provide a value of receiver window.
virtual void DoTeardown(void)
Implementation to do any local setup required for this TestCase.
void TestIsLost()
Test if a segment is really set as lost.
void TestNextSeg()
Test the generation of the "next" block.
TcpTxBufferTestCase()
Constructor.
the TestSuite for the TcpTxBuffer test case
uint32_t GetSize(void) const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:856
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:74
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:994
void AddTestCase(TestCase *testCase, TestDuration duration=QUICK)
Add an individual child TestCase to this test suite.
Definition: test.cc:299
A suite of tests to run.
Definition: test.h:1188
@ UNIT
This test suite implements a Unit Test.
Definition: test.h:1197
uint32_t segmentSize
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
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:141
Time Seconds(double value)
Construct a Time in the indicated unit.
Definition: nstime.h:1244
Every class exported by the ns3 library is enclosed in the ns3 namespace.
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:1648
static TcpTxBufferTestSuite g_tcpTxBufferTestSuite
Static variable for test initialization.