A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
ns3tcp-state-test-suite.cc
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 #include <string>
21 
22 #include "ns3/log.h"
23 #include "ns3/abort.h"
24 #include "ns3/test.h"
25 #include "ns3/pcap-file.h"
26 #include "ns3/config.h"
27 #include "ns3/string.h"
28 #include "ns3/uinteger.h"
29 #include "ns3/data-rate.h"
30 #include "ns3/inet-socket-address.h"
31 #include "ns3/point-to-point-helper.h"
32 #include "ns3/internet-stack-helper.h"
33 #include "ns3/ipv4-global-routing-helper.h"
34 #include "ns3/ipv4-address-helper.h"
35 #include "ns3/packet-sink-helper.h"
36 #include "ns3/tcp-socket-factory.h"
37 #include "ns3/node-container.h"
38 #include "ns3/simulator.h"
39 #include "ns3/error-model.h"
40 #include "ns3/pointer.h"
41 #include "ns3tcp-socket-writer.h"
42 
43 using namespace ns3;
44 
45 NS_LOG_COMPONENT_DEFINE ("Ns3TcpStateTest");
46 
47 const bool WRITE_VECTORS = false; // set to true to write response vectors
48 const bool WRITE_LOGGING = false; // set to true to write logging
49 const uint32_t PCAP_LINK_TYPE = 1187373554; // Some large random number -- we use to verify data was written by this program
50 const uint32_t PCAP_SNAPLEN = 64; // Don't bother to save much data
51 
52 // ===========================================================================
53 // Tests of TCP implementation loss behavior
54 // ===========================================================================
55 //
56 
58 {
59 public:
61  Ns3TcpStateTestCase (uint32_t testCase);
62  virtual ~Ns3TcpStateTestCase () {}
63 
64 private:
65  virtual void DoSetup (void);
66  virtual void DoRun (void);
67  virtual void DoTeardown (void);
68 
69  std::string m_pcapFilename;
70  PcapFile m_pcapFile;
71  uint32_t m_testCase;
72  uint32_t m_totalTxBytes;
73  uint32_t m_currentTxBytes;
74  bool m_writeVectors;
75  bool m_writeResults;
76  bool m_writeLogging;
77  bool m_needToClose;
78 
79  void Ipv4L3Tx (std::string context, Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface);
80  void WriteUntilBufferFull (Ptr<Socket> localSocket, uint32_t txSpace);
81  void StartFlow (Ptr<Socket> localSocket,
82  Ipv4Address servAddress,
83  uint16_t servPort);
84 
85 };
86 
87 Ns3TcpStateTestCase::Ns3TcpStateTestCase ()
88  : TestCase ("Check the operation of the TCP state machine for several cases"),
89  m_testCase (0),
90  m_totalTxBytes (20000),
91  m_currentTxBytes (0),
92  m_writeVectors (WRITE_VECTORS),
93  m_writeResults (false),
94  m_writeLogging (WRITE_LOGGING),
95  m_needToClose (true)
96 {
97 }
98 
99 Ns3TcpStateTestCase::Ns3TcpStateTestCase (uint32_t testCase)
100  : TestCase ("Check the operation of the TCP state machine for several cases"),
101  m_testCase (testCase),
102  m_totalTxBytes (20000),
103  m_currentTxBytes (0),
104  m_writeVectors (WRITE_VECTORS),
105  m_writeResults (false),
106  m_writeLogging (WRITE_LOGGING),
107  m_needToClose (true)
108 {
109 }
110 
111 void
113 {
114  //
115  // We expect there to be a file called ns3tcp-state-response-vectors.pcap in
116  // response-vectors/ of this directory
117  //
118  std::ostringstream oss;
119  oss << "/response-vectors/ns3tcp-state" << m_testCase << "-response-vectors.pcap";
120  m_pcapFilename = static_cast<std::string> (NS_TEST_SOURCEDIR) + oss.str ();
121 
122  if (m_writeVectors)
123  {
124  m_pcapFile.Open (m_pcapFilename, std::ios::out|std::ios::binary);
125  m_pcapFile.Init (PCAP_LINK_TYPE, PCAP_SNAPLEN);
126  }
127  else
128  {
129  m_pcapFile.Open (m_pcapFilename, std::ios::in|std::ios::binary);
130  NS_ABORT_MSG_UNLESS (m_pcapFile.GetDataLinkType () == PCAP_LINK_TYPE, "Wrong response vectors in directory");
131  }
132 }
133 
134 void
136 {
137  m_pcapFile.Close ();
138 }
139 
140 void
141 Ns3TcpStateTestCase::Ipv4L3Tx (std::string context, Ptr<const Packet> packet, Ptr<Ipv4> ipv4, uint32_t interface)
142 {
143  //
144  // We're not testing IP so remove and toss the header. In order to do this,
145  // though, we need to copy the packet since we have a const version.
146  //
147  Ptr<Packet> p = packet->Copy ();
148  Ipv4Header ipHeader;
149  p->RemoveHeader (ipHeader);
150 
151  //
152  // What is left is the TCP header and any data that may be sent. We aren't
153  // sending any TCP data, so we expect what remains is only TCP header, which
154  // is a small thing to save.
155  //
156  if (m_writeVectors)
157  {
158  //
159  // Save the TCP under test response for later testing.
160  //
161  Time tNow = Simulator::Now ();
162  int64_t tMicroSeconds = tNow.GetMicroSeconds ();
163 
164  uint32_t size = p->GetSize ();
165  uint8_t *buf = new uint8_t[size];
166  p->CopyData (buf, size);
167 
168  m_pcapFile.Write (uint32_t (tMicroSeconds / 1000000),
169  uint32_t (tMicroSeconds % 1000000),
170  buf,
171  size);
172  delete [] buf;
173  }
174  else
175  {
176  //
177  // Read the TCP under test expected response from the expected vector
178  // file and see if it still does the right thing.
179  //
180  uint8_t expected[PCAP_SNAPLEN];
181  uint32_t tsSec, tsUsec, inclLen, origLen, readLen;
182  m_pcapFile.Read (expected, sizeof(expected), tsSec, tsUsec, inclLen, origLen, readLen);
183 
184  uint8_t *actual = new uint8_t[readLen];
185  p->CopyData (actual, readLen);
186 
187  uint32_t result = memcmp (actual, expected, readLen);
188 
189  delete [] actual;
190 
191  //
192  // Avoid streams of errors -- only report the first.
193  //
194  if (IsStatusSuccess ())
195  {
196  NS_TEST_EXPECT_MSG_EQ (result, 0, "Expected data comparison error");
197  }
198  }
199 }
200 
202 // Implementing an "application" to send bytes over a TCP connection
203 void
204 Ns3TcpStateTestCase::WriteUntilBufferFull (Ptr<Socket> localSocket, uint32_t txSpace)
205 {
206  while (m_currentTxBytes < m_totalTxBytes)
207  {
208  uint32_t left = m_totalTxBytes - m_currentTxBytes;
209  uint32_t dataOffset = m_currentTxBytes % 1040;
210  uint32_t toWrite = 1040 - dataOffset;
211  uint32_t txAvail = localSocket->GetTxAvailable ();
212  toWrite = std::min (toWrite, left);
213  toWrite = std::min (toWrite, txAvail);
214  if (txAvail == 0)
215  {
216  return;
217  };
218  if (m_writeLogging)
219  {
220  std::clog << "Submitting "
221  << toWrite << " bytes to TCP socket" << std::endl;
222  }
223  int amountSent = localSocket->Send (0, toWrite, 0);
224  NS_ASSERT (amountSent > 0); // Given GetTxAvailable() non-zero, amountSent should not be zero
225  m_currentTxBytes += amountSent;
226  }
227  if (m_needToClose)
228  {
229  if (m_writeLogging)
230  {
231  std::clog << "Close socket at "
232  << Simulator::Now ().GetSeconds ()
233  << std::endl;
234  }
235  localSocket->Close ();
236  m_needToClose = false;
237  }
238 }
239 
240 void
241 Ns3TcpStateTestCase::StartFlow (Ptr<Socket> localSocket,
242  Ipv4Address servAddress,
243  uint16_t servPort)
244 {
245  if (m_writeLogging)
246  {
247  std::clog << "Starting flow at time "
248  << Simulator::Now ().GetSeconds ()
249  << std::endl;
250  }
251 
252  localSocket->Connect (InetSocketAddress (servAddress, servPort)); // connect
253 
254  // tell the tcp implementation to call WriteUntilBufferFull again
255  // if we blocked and new tx buffer space becomes available
256  localSocket->SetSendCallback (MakeCallback
257  (&Ns3TcpStateTestCase::WriteUntilBufferFull,
258  this));
259  WriteUntilBufferFull (localSocket, localSocket->GetTxAvailable ());
260 }
261 
262 void
264 {
265  // Network topology
266  //
267  // 10Mb/s, 0.1ms 10Mb/s, 0.1ms
268  // n0-----------------n1-----------------n2
269 
270  std::string tcpModel ("ns3::TcpNewReno");
271 
272  Config::SetDefault ("ns3::TcpL4Protocol::SocketType", StringValue (tcpModel));
273  Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (1000));
274  Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (1));
275  Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (20));
276 
277  if (m_writeLogging)
278  {
279  LogComponentEnableAll (LOG_PREFIX_FUNC);
280  LogComponentEnable ("TcpTestCases", LOG_LEVEL_ALL);
281  LogComponentEnable ("ErrorModel", LOG_LEVEL_DEBUG);
282  LogComponentEnable ("TcpTestCases", LOG_LEVEL_ALL);
283  LogComponentEnable ("TcpNewReno", LOG_LEVEL_INFO);
284  LogComponentEnable ("TcpReno", LOG_LEVEL_INFO);
285  LogComponentEnable ("TcpTahoe", LOG_LEVEL_INFO);
286  LogComponentEnable ("TcpSocketBase", LOG_LEVEL_INFO);
287  }
288 
290  // Topology construction
291  //
292 
293  // Create three nodes
294  NodeContainer n0n1;
295  n0n1.Create (2);
296 
297  NodeContainer n1n2;
298  n1n2.Add (n0n1.Get (1));
299  n1n2.Create (1);
300 
301  // Set up TCP/IP stack to all nodes (and create loopback device at device 0)
302  InternetStackHelper internet;
303  internet.InstallAll ();
304 
305  // Connect the nodes
306  PointToPointHelper p2p;
307  p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (1000000)));
308  p2p.SetChannelAttribute ("Delay", TimeValue (Seconds (0.0001)));
309  NetDeviceContainer dev0 = p2p.Install (n0n1);
310  NetDeviceContainer dev1 = p2p.Install (n1n2);
311 
312  // Add IP addresses to each network interfaces
313  Ipv4AddressHelper ipv4;
314  ipv4.SetBase ("10.1.3.0", "255.255.255.0");
315  ipv4.Assign (dev0);
316  ipv4.SetBase ("10.1.2.0", "255.255.255.0");
317  Ipv4InterfaceContainer ipInterfs = ipv4.Assign (dev1);
318 
319  // Set up routes to all nodes
320  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
321 
323  // A flow from node n0 to node n2
324  //
325 
326  // Create a packet sink to receive packets on node n2
327  uint16_t servPort = 50000; // Destination port number
328  PacketSinkHelper sink ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), servPort));
329  ApplicationContainer sinkApps = sink.Install (n1n2.Get (1));
330  sinkApps.Start (Seconds (0.0));
331  sinkApps.Stop (Seconds (100.0));
332 
333  // Create a data source to send packets on node n0
334  // Instead of full application, here use the socket directly by
335  // registering callbacks in function StarFlow().
336  Ptr<Socket> localSocket = Socket::CreateSocket (n0n1.Get (0),
337  TcpSocketFactory::GetTypeId ());
338  localSocket->Bind ();
339  Simulator::ScheduleNow (&Ns3TcpStateTestCase::StartFlow, this,
340  localSocket, ipInterfs.GetAddress (1), servPort);
341 
342  Config::Connect ("/NodeList/0/$ns3::Ipv4L3Protocol/Tx",
343  MakeCallback (&Ns3TcpStateTestCase::Ipv4L3Tx, this));
344 
346  // Set up different test cases: Lost model at node n1, different file size
347  //
348 
349  std::list<uint32_t> dropListN0;
350  std::list<uint32_t> dropListN1;
351  std::string caseDescription;
352  switch (m_testCase)
353  {
354  case 0:
355  m_totalTxBytes = 1000;
356  caseDescription = "Verify connection establishment";
357  break;
358  case 1:
359  m_totalTxBytes = 100*1000;
360  caseDescription = "Verify a bigger (100 pkts) transfer: Sliding window operation, etc.";
361  break;
362  case 2:
363  m_totalTxBytes = 1000;
364  caseDescription = "Survive a SYN lost";
365  dropListN0.push_back (0);
366  break;
367  case 3:
368  m_totalTxBytes = 2000;
369  caseDescription = "Survive a SYN+ACK lost";
370  dropListN1.push_back (0);
371  break;
372  case 4:
373  m_totalTxBytes = 2000;
374  caseDescription = "Survive a ACK (last packet in 3-way handshake) lost";
375  dropListN0.push_back (1);
376  break;
377  case 5:
378  m_totalTxBytes = 0;
379  caseDescription = "Immediate FIN upon SYN_RCVD";
380  m_needToClose = false;
381  dropListN0.push_back (1); // Hide the ACK in 3WHS
382  Simulator::Schedule (Seconds (0.002), &Socket::Close, localSocket);
383  break;
384  case 6:
385  m_totalTxBytes = 5000;
386  caseDescription = "Simulated simultaneous close";
387  dropListN1.push_back (5); // Hide the ACK-to-FIN from n2
388  break;
389  case 7:
390  m_totalTxBytes = 5000;
391  caseDescription = "FIN check 1: Loss of initiator's FIN. Wait until app close";
392  m_needToClose = false;
393  dropListN0.push_back (7); // Hide the FIN from n0
394  Simulator::Schedule (Seconds (0.04), &Socket::Close, localSocket);
395  break;
396  case 8:
397  m_totalTxBytes = 5000;
398  caseDescription = "FIN check 2: Loss responder's FIN. FIN will be resent after last ack timeout";
399  dropListN1.push_back (6); // Hide the FIN from n2
400  break;
401  default:
402  NS_FATAL_ERROR ("Program fatal error: specified test case not supported: "
403  << m_testCase);
404  break;
405  }
406 
407  Ptr<ReceiveListErrorModel> errN0 = CreateObject<ReceiveListErrorModel> ();
408  errN0->SetList (dropListN0);
409  dev0.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (errN0));
410 
411  Ptr<ReceiveListErrorModel> errN1 = CreateObject<ReceiveListErrorModel> ();
412  errN1->SetList (dropListN1);
413  dev1.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (errN1));
414 
415  std::ostringstream oss;
416  oss << "tcp-state" << m_testCase << "-test-case";
417  if (m_writeResults)
418  {
419  p2p.EnablePcapAll (oss.str ());
420  p2p.EnableAsciiAll (oss.str ());
421  }
422 
423  if (m_writeLogging)
424  {
425  Ptr<OutputStreamWrapper> osw = Create<OutputStreamWrapper> (&std::clog);
426  *(osw->GetStream ()) << std::setprecision (9) << std::fixed;
427  p2p.EnableAsciiAll (osw);
428 
429  std::clog << std::endl << "Running TCP test-case " << m_testCase << ": "
430  << caseDescription << std::endl;
431  }
432 
433  // Finally, set up the simulator to run. The 1000 second hard limit is a
434  // failsafe in case some change above causes the simulation to never end
435  Simulator::Stop (Seconds (1000));
436  Simulator::Run ();
437  Simulator::Destroy ();
438 
439 
440 }
441 
443 {
444 public:
446 };
447 
448 Ns3TcpStateTestSuite::Ns3TcpStateTestSuite ()
449  : TestSuite ("ns3-tcp-state", SYSTEM)
450 {
451  Packet::EnablePrinting (); // Enable packet metadata for all test cases
452  AddTestCase (new Ns3TcpStateTestCase (0), TestCase::QUICK);
453  AddTestCase (new Ns3TcpStateTestCase (1), TestCase::QUICK);
454  AddTestCase (new Ns3TcpStateTestCase (2), TestCase::QUICK);
455  AddTestCase (new Ns3TcpStateTestCase (3), TestCase::QUICK);
456  AddTestCase (new Ns3TcpStateTestCase (4), TestCase::QUICK);
457  AddTestCase (new Ns3TcpStateTestCase (5), TestCase::QUICK);
458  AddTestCase (new Ns3TcpStateTestCase (6), TestCase::QUICK);
459  AddTestCase (new Ns3TcpStateTestCase (7), TestCase::QUICK);
460  AddTestCase (new Ns3TcpStateTestCase (8), TestCase::QUICK);
461 }
462 
463 static Ns3TcpStateTestSuite ns3TcpLossTestSuite;
uint32_t RemoveHeader(Header &header)
Definition: packet.cc:285
holds a vector of ns3::Application pointers.
void LogComponentEnableAll(enum LogLevel level)
Definition: log.cc:335
keep track of time unit.
Definition: nstime.h:149
an Inet address class
holds a vector of std::pair of Ptr<Ipv4> and interface index.
hold variables of type string
Definition: string.h:19
Ptr< NetDevice > Get(uint32_t i) const
Get the Ptr<NetDevice> stored in this container at a given index.
NetDeviceContainer Install(NodeContainer c)
A suite of tests to run.
Definition: test.h:962
#define NS_ASSERT(condition)
Definition: assert.h:64
#define NS_LOG_COMPONENT_DEFINE(name)
Definition: log.h:122
aggregate IP/TCP/UDP functionality to existing Nodes.
uint32_t GetSize(void) const
Definition: packet.h:620
A helper to make it easier to instantiate an ns3::PacketSinkApplication on a set of nodes...
Build a set of PointToPointNetDevice objects.
encapsulates test code
Definition: test.h:834
void SetDeviceAttribute(std::string name, const AttributeValue &value)
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if cond is false.
Definition: abort.h:131
#define NS_FATAL_ERROR(msg)
fatal error handling
Definition: fatal-error.h:72
Class for representing data rates.
Definition: data-rate.h:71
Packet header for IPv4.
Definition: ipv4-header.h:31
void EnablePcapAll(std::string prefix, bool promiscuous=false)
Enable pcap output on each device (which is of the appropriate type) in the set of all nodes created ...
int64_t GetMicroSeconds(void) const
Definition: nstime.h:279
hold objects of type ns3::Time
Definition: nstime.h:700
void Read(uint8_t *const data, uint32_t maxBytes, uint32_t &tsSec, uint32_t &tsUsec, uint32_t &inclLen, uint32_t &origLen, uint32_t &readLen)
Read next packet from file.
Definition: pcap-file.cc:438
Hold an unsigned integer type.
Definition: uinteger.h:46
holds a vector of ns3::NetDevice pointers
Callback< R > MakeCallback(R(T::*memPtr)(void), OBJ objPtr)
Definition: callback.h:502
void Start(Time start)
Arrange for all of the Applications in this container to Start() at the Time given as a parameter...
virtual void DoRun(void)
Implementation to actually run this test case.
virtual void DoTeardown(void)
Implementation to do any local setup required for this test case.
virtual int Connect(const Address &address)=0
Initiate a connection to a remote host.
Ptr< Packet > Copy(void) const
Definition: packet.cc:131
virtual int Bind(const Address &address)=0
Allocate a local endpoint for this socket.
keep track of a set of node pointers.
hold objects of type Ptr<T>
Definition: pointer.h:33
void SetList(const std::list< uint32_t > &packetlist)
Definition: error-model.cc:515
virtual void DoSetup(void)
Implementation to do any local setup required for this test case.
void Init(uint32_t dataLinkType, uint32_t snapLen=SNAPLEN_DEFAULT, int32_t timeZoneCorrection=ZONE_DEFAULT, bool swapMode=false)
Definition: pcap-file.cc:330
void Close(void)
Definition: pcap-file.cc:87
void AddTestCase(TestCase *testCase) NS_DEPRECATED
Add an individual test case to this test suite.
Definition: test.cc:172
void SetSendCallback(Callback< void, Ptr< Socket >, uint32_t > sendCb)
Notify application when space in transmit buffer is added.
Definition: socket.cc:120
bool IsStatusSuccess(void) const
Definition: test.cc:329
void Open(std::string const &filename, std::ios::openmode mode)
Definition: pcap-file.cc:311
void SetChannelAttribute(std::string name, const AttributeValue &value)
Ipv4 addresses are stored in host order in this class.
Definition: ipv4-address.h:38
void Stop(Time stop)
Arrange for all of the Applications in this container to Stop() at the Time given as a parameter...
Ipv4InterfaceContainer Assign(const NetDeviceContainer &c)
Assign IP addresses to the net devices specified in the container based on the current network prefix...
Time Seconds(double seconds)
create ns3::Time instances in units of seconds.
Definition: nstime.h:586
void Add(NodeContainer other)
Append the contents of another NodeContainer to the end of this container.
hold objects of type ns3::DataRate
Ptr< Node > Get(uint32_t i) const
Get the Ptr<Node> stored in this container at a given index.
uint32_t CopyData(uint8_t *buffer, uint32_t size) const
Definition: packet.cc:398
ApplicationContainer Install(NodeContainer c) const
A helper class to make life easier while doing simple IPv4 address assignment in scripts.
void EnableAsciiAll(std::string prefix)
Enable ascii trace output on each device (which is of the appropriate type) in the set of all nodes c...
void Create(uint32_t n)
Create n nodes and append pointers to them to the end of this NodeContainer.
void Write(uint32_t tsSec, uint32_t tsUsec, uint8_t const *const data, uint32_t totalLen)
Write next packet to file.
Definition: pcap-file.cc:405
virtual int Send(Ptr< Packet > p, uint32_t flags)=0
Send data (or dummy data) to the remote host.
virtual int Close(void)=0
Close a socket.
virtual uint32_t GetTxAvailable(void) const =0
Returns the number of bytes which can be sent in a single call to Send.
std::ostream * GetStream(void)
void SetBase(Ipv4Address network, Ipv4Mask mask, Ipv4Address base="0.0.0.1")
Set the base network number, network mask and base address.
void LogComponentEnable(char const *name, enum LogLevel level)
Definition: log.cc:311
Ipv4Address GetAddress(uint32_t i, uint32_t j=0) const