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