A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
ns3tcp-loss-test-suite.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2010 University of Washington
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 #include <iomanip>
20 
21 #include "ns3/log.h"
22 #include "ns3/abort.h"
23 #include "ns3/test.h"
24 #include "ns3/pcap-file.h"
25 #include "ns3/config.h"
26 #include "ns3/string.h"
27 #include "ns3/uinteger.h"
28 #include "ns3/data-rate.h"
29 #include "ns3/inet-socket-address.h"
30 #include "ns3/point-to-point-helper.h"
31 #include "ns3/internet-stack-helper.h"
32 #include "ns3/ipv4-global-routing-helper.h"
33 #include "ns3/ipv4-address-helper.h"
34 #include "ns3/packet-sink-helper.h"
35 #include "ns3/tcp-socket-factory.h"
36 #include "ns3/node-container.h"
37 #include "ns3/simulator.h"
38 #include "ns3/error-model.h"
39 #include "ns3/pointer.h"
40 #include "ns3tcp-socket-writer.h"
41 
42 using namespace ns3;
43 
44 NS_LOG_COMPONENT_DEFINE ("Ns3TcpLossTest");
45 
46 const bool WRITE_VECTORS = false; // set to true to write response vectors
47 const bool WRITE_LOGGING = false; // set to true to write logging
48 const uint32_t PCAP_LINK_TYPE = 1187373557; // Some large random number -- we use to verify data was written by this program
49 const uint32_t PCAP_SNAPLEN = 64; // Don't bother to save much data
50 
51 // ===========================================================================
52 // Tests of TCP implementation loss behavior
53 // ===========================================================================
54 //
55 
57 {
58 public:
60  Ns3TcpLossTestCase (std::string tcpModel, uint32_t testCase);
61  virtual ~Ns3TcpLossTestCase () {}
62 
63 private:
64  virtual void DoSetup (void);
65  virtual void DoRun (void);
66  virtual void DoTeardown (void);
67 
69  std::string m_pcapFilename;
71  uint32_t m_testCase;
72  uint32_t m_totalTxBytes;
73  uint32_t m_currentTxBytes;
78  std::string m_tcpModel;
79 
80  void Ipv4L3Tx (std::string context, Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface);
81  void CwndTracer (uint32_t oldval, uint32_t newval);
82  void WriteUntilBufferFull (Ptr<Socket> localSocket, uint32_t txSpace);
83  void StartFlow (Ptr<Socket> localSocket,
84  Ipv4Address servAddress,
85  uint16_t servPort);
86 
87 };
88 
90  : TestCase ("Check the operation of the TCP state machine for several cases"),
91  m_testCase (0),
92  m_totalTxBytes (200000),
93  m_currentTxBytes (0),
94  m_writeVectors (WRITE_VECTORS),
95  m_writeResults (false),
96  m_writeLogging (WRITE_LOGGING),
97  m_needToClose (true),
98  m_tcpModel ("ns3::TcpTahoe")
99 {
100 }
101 
102 Ns3TcpLossTestCase::Ns3TcpLossTestCase (std::string tcpModel, uint32_t testCase)
103  : TestCase ("Check the behaviour of TCP upon packet losses"),
104  m_testCase (testCase),
105  m_totalTxBytes (200000),
106  m_currentTxBytes (0),
107  m_writeVectors (WRITE_VECTORS),
108  m_writeResults (false),
109  m_writeLogging (WRITE_LOGGING),
110  m_needToClose (true),
111  m_tcpModel (tcpModel)
112 {
113 }
114 
115 void
117 {
118  //
119  // We expect there to be a file called ns3tcp-state-response-vectors.pcap in
120  // response-vectors/ of this directory
121  //
122  std::ostringstream oss;
123  oss << "/response-vectors/ns3tcp-loss-" << m_tcpModel << m_testCase << "-response-vectors.pcap";
125 
126  if (m_writeVectors)
127  {
128  m_pcapFile.Open (m_pcapFilename, std::ios::out|std::ios::binary);
130  }
131  else
132  {
133  m_pcapFile.Open (m_pcapFilename, std::ios::in|std::ios::binary);
134  NS_ABORT_MSG_UNLESS (m_pcapFile.GetDataLinkType () == PCAP_LINK_TYPE, "Wrong response vectors in directory");
135  }
136 }
137 
138 void
140 {
141  m_pcapFile.Close ();
142 }
143 
144 void
145 Ns3TcpLossTestCase::Ipv4L3Tx (std::string context, Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface)
146 {
147  //
148  // We're not testing IP so remove and toss the header. In order to do this,
149  // though, we need to copy the packet since we have a const version.
150  //
151  Ptr<Packet> p = packet->Copy ();
152  Ipv4Header ipHeader;
153  p->RemoveHeader (ipHeader);
154 
155  //
156  // What is left is the TCP header and any data that may be sent. We aren't
157  // sending any TCP data, so we expect what remains is only TCP header, which
158  // is a small thing to save.
159  //
160  if (m_writeVectors)
161  {
162  //
163  // Save the TCP under test response for later testing.
164  //
165  Time tNow = Simulator::Now ();
166  int64_t tMicroSeconds = tNow.GetMicroSeconds ();
167 
168  uint32_t size = p->GetSize ();
169  uint8_t *buf = new uint8_t[size];
170  p->CopyData (buf, size);
171 
172  m_pcapFile.Write (uint32_t (tMicroSeconds / 1000000),
173  uint32_t (tMicroSeconds % 1000000),
174  buf,
175  size);
176  delete [] buf;
177  }
178  else
179  {
180  //
181  // Read the TCP under test expected response from the expected vector
182  // file and see if it still does the right thing.
183  //
184  uint8_t expected[PCAP_SNAPLEN];
185  uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
186  m_pcapFile.Read (expected, sizeof(expected), tsSec, tsUsec, inclLen, origLen, readLen);
187 
188  NS_LOG_DEBUG ("read " << readLen);
189 
190  uint8_t *actual = new uint8_t[readLen];
191  p->CopyData (actual, readLen);
192 
193  uint32_t result = memcmp (actual, expected, readLen);
194 
195  delete [] actual;
196 
197  //
198  // Avoid streams of errors -- only report the first.
199  //
200  if (IsStatusSuccess ())
201  {
202  NS_TEST_EXPECT_MSG_EQ (result, 0, "Expected data comparison error");
203  }
204  }
205 }
206 
207 void
208 Ns3TcpLossTestCase::CwndTracer (uint32_t oldval, uint32_t newval)
209 {
210  if (m_writeLogging)
211  {
212  *(m_osw->GetStream ()) << "Moving cwnd from " << oldval << " to " << newval
213  << " at time " << Simulator::Now ().GetSeconds ()
214  << " seconds" << std::endl;
215  }
216 }
217 
219 // Implementing an "application" to send bytes over a TCP connection
220 void
222 {
224  {
225  uint32_t left = m_totalTxBytes - m_currentTxBytes;
226  uint32_t dataOffset = m_currentTxBytes % 1040;
227  uint32_t toWrite = 1040 - dataOffset;
228  uint32_t txAvail = localSocket->GetTxAvailable ();
229  toWrite = std::min (toWrite, left);
230  toWrite = std::min (toWrite, txAvail);
231  if (txAvail == 0)
232  {
233  return;
234  };
235  if (m_writeLogging)
236  {
237  std::clog << "Submitting " << toWrite
238  << " bytes to TCP socket" << std::endl;
239  }
240  int amountSent = localSocket->Send (0, toWrite, 0);
241  NS_ASSERT (amountSent > 0); // Given GetTxAvailable() non-zero, amountSent should not be zero
242  m_currentTxBytes += amountSent;
243  }
244  if (m_needToClose)
245  {
246  if (m_writeLogging)
247  {
248  std::clog << "Close socket at "
249  << Simulator::Now ().GetSeconds () << std::endl;
250  }
251  localSocket->Close ();
252  m_needToClose = false;
253  }
254 }
255 
256 void
258  Ipv4Address servAddress,
259  uint16_t servPort)
260 {
261  if (m_writeLogging)
262  {
263  std::clog << "Starting flow at time "
264  << Simulator::Now ().GetSeconds () << std::endl;
265  }
266  localSocket->Connect (InetSocketAddress (servAddress, servPort)); // connect
267 
268  // tell the tcp implementation to call WriteUntilBufferFull again
269  // if we blocked and new tx buffer space becomes available
270  localSocket->SetSendCallback (MakeCallback
272  this));
273  WriteUntilBufferFull (localSocket, localSocket->GetTxAvailable ());
274 }
275 
276 void
278 {
279  // Network topology
280  //
281  // 8Mb/s, 0.1ms 0.8Mb/s, 100ms
282  // s1-----------------r1-----------------k1
283  //
284  // Example corresponding to simulations in the paper "Simulation-based
285  // Comparisons of Tahoe, Reno, and SACK TCP
286 
287  std::ostringstream tcpModel;
288  tcpModel << "ns3::Tcp" << m_tcpModel;
289  Config::SetDefault ("ns3::TcpL4Protocol::SocketType",
290  StringValue (tcpModel.str ()));
291  Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (1000));
292  Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (1));
293 
294  if (m_writeLogging)
295  {
297  LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL);
298  LogComponentEnable ("ErrorModel", LOG_LEVEL_DEBUG);
299  LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL);
300  LogComponentEnable ("TcpNewReno", LOG_LEVEL_INFO);
301  LogComponentEnable ("TcpReno", LOG_LEVEL_INFO);
302  LogComponentEnable ("TcpTahoe", LOG_LEVEL_INFO);
303  LogComponentEnable ("TcpSocketBase", LOG_LEVEL_INFO);
304  }
305 
307  // Topology construction
308  //
309 
310  // Create three nodes: s1, r1, and k1
311  NodeContainer s1r1;
312  s1r1.Create (2);
313 
314  NodeContainer r1k1;
315  r1k1.Add (s1r1.Get (1));
316  r1k1.Create (1);
317 
318  // Set up TCP/IP stack to all nodes (and create loopback device at device 0)
319  InternetStackHelper internet;
320  internet.InstallAll ();
321 
322  // Connect the nodes
323  PointToPointHelper p2p;
324  p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (8000000)));
325  p2p.SetChannelAttribute ("Delay", TimeValue (Seconds (0.0001)));
326  NetDeviceContainer dev0 = p2p.Install (s1r1);
327  p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (800000)));
328  p2p.SetChannelAttribute ("Delay", TimeValue (Seconds (0.1)));
329  NetDeviceContainer dev1 = p2p.Install (r1k1);
330 
331  // Add IP addresses to each network interfaces
332  Ipv4AddressHelper ipv4;
333  ipv4.SetBase ("10.1.3.0", "255.255.255.0");
334  ipv4.Assign (dev0);
335  ipv4.SetBase ("10.1.2.0", "255.255.255.0");
336  Ipv4InterfaceContainer ipInterfs = ipv4.Assign (dev1);
337 
338  // Set up routes to all nodes
339  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
340 
342  // Send 20000 (totalTxBytes) bytes from node s1 to node k1
343  //
344 
345  // Create a packet sink to receive packets on node k1
346  uint16_t servPort = 50000; // Destination port number
347  PacketSinkHelper sink ("ns3::TcpSocketFactory",
348  InetSocketAddress (Ipv4Address::GetAny (), servPort));
349  ApplicationContainer apps = sink.Install (r1k1.Get (1));
350  apps.Start (Seconds (0.0));
351  apps.Stop (Seconds (100.0));
352 
353  // Create a data source to send packets on node s0.
354  // Instead of full application, here use the socket directly by
355  // registering callbacks in function StarFlow().
356  Ptr<Socket> localSocket = Socket::CreateSocket (s1r1.Get (0), TcpSocketFactory::GetTypeId ());
357  localSocket->Bind ();
358  Simulator::ScheduleNow (&Ns3TcpLossTestCase::StartFlow,
359  this,
360  localSocket,
361  ipInterfs.GetAddress (1),
362  servPort);
363 
364  Config::Connect ("/NodeList/0/$ns3::Ipv4L3Protocol/Tx",
366 
368  ("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
370 
372  // Set up loss model at node k1
373  //
374  std::list<uint32_t> sampleList;
375  switch (m_testCase)
376  {
377  case 0:
378  break;
379  case 1:
380  // Force a loss for 15th data packet. TCP cwnd will be at 14 segments
381  // (14000 bytes) when duplicate acknowledgments start to come.
382  sampleList.push_back (16);
383  break;
384  case 2:
385  sampleList.push_back (16);
386  sampleList.push_back (17);
387  break;
388  case 3:
389  sampleList.push_back (16);
390  sampleList.push_back (17);
391  sampleList.push_back (18);
392  break;
393  case 4:
394  sampleList.push_back (16);
395  sampleList.push_back (17);
396  sampleList.push_back (18);
397  sampleList.push_back (19);
398  break;
399  default:
400  NS_FATAL_ERROR ("Program fatal error: loss value " << m_testCase << " not supported.");
401  break;
402  }
403 
404  Ptr<ReceiveListErrorModel> pem = CreateObject<ReceiveListErrorModel> ();
405  pem->SetList (sampleList);
406  dev1.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (pem));
407 
408  // One can toggle the comment for the following line on or off to see the
409  // effects of finite send buffer modelling. One can also change the size of
410  // that buffer.
411  // localSocket->SetAttribute("SndBufSize", UintegerValue(4096));
412 
413  std::ostringstream oss;
414  oss << "tcp-loss-" << m_tcpModel << m_testCase << "-test-case";
415  if (m_writeResults)
416  {
417  p2p.EnablePcapAll (oss.str ());
418  p2p.EnableAsciiAll (oss.str ());
419  }
420 
421  std::ostringstream oss2;
422  oss2 << "src/test/ns3tcp/Tcp" << m_tcpModel << "." << m_testCase << ".log";
423  AsciiTraceHelper ascii;
424  if (m_writeLogging)
425  {
426  m_osw = ascii.CreateFileStream (oss2.str ());
427  *(m_osw->GetStream ()) << std::setprecision (9) << std::fixed;
428  p2p.EnableAsciiAll (m_osw);
429  }
430 
431  // Finally, set up the simulator to run. The 1000 second hard limit is a
432  // failsafe in case some change above causes the simulation to never end
433  Simulator::Stop (Seconds (1000));
434  Simulator::Run ();
435  Simulator::Destroy ();
436 }
437 
439 {
440 public:
442 };
443 
445  : TestSuite ("ns3-tcp-loss", SYSTEM)
446 {
447  SetDataDir (NS_TEST_SOURCEDIR);
448  Packet::EnablePrinting (); // Enable packet metadata for all test cases
449 #if 0
450  AddTestCase (new Ns3TcpLossTestCase ("Tahoe", 0));
451  AddTestCase (new Ns3TcpLossTestCase ("Tahoe", 1));
452  AddTestCase (new Ns3TcpLossTestCase ("Tahoe", 2));
453  AddTestCase (new Ns3TcpLossTestCase ("Tahoe", 3));
454  AddTestCase (new Ns3TcpLossTestCase ("Tahoe", 4));
455 
456  AddTestCase (new Ns3TcpLossTestCase ("Reno", 0));
457  AddTestCase (new Ns3TcpLossTestCase ("Reno", 1));
458  AddTestCase (new Ns3TcpLossTestCase ("Reno", 2));
459  AddTestCase (new Ns3TcpLossTestCase ("Reno", 3));
460  AddTestCase (new Ns3TcpLossTestCase ("Reno", 4));
461 
462  AddTestCase (new Ns3TcpLossTestCase ("NewReno", 0));
463  AddTestCase (new Ns3TcpLossTestCase ("NewReno", 1));
464  AddTestCase (new Ns3TcpLossTestCase ("NewReno", 2));
465  AddTestCase (new Ns3TcpLossTestCase ("NewReno", 3));
466  AddTestCase (new Ns3TcpLossTestCase ("NewReno", 4));
467 #endif
468 }
469