From da85b3448732191069db7cc285290338df06a277 Mon Sep 17 00:00:00 2001 From: Josh Pelkey Date: Fri, 17 Dec 2010 13:57:22 -0500 Subject: [PATCH] merge new TCP code --- examples/tcp/tcp-loss-response.cc | 292 +++ examples/tcp/tcp-testcases.cc | 283 +++ examples/tcp/wscript | 8 + src/common/output-stream-wrapper.cc | 19 +- src/common/output-stream-wrapper.h | 4 +- src/helper/point-to-point-helper.cc | 7 + src/internet-stack/nsc-tcp-l4-protocol.cc | 2 - src/internet-stack/nsc-tcp-socket-impl.cc | 17 +- src/internet-stack/nsc-tcp-socket-impl.h | 6 +- src/internet-stack/tcp-header.cc | 1 - src/internet-stack/tcp-l4-protocol.cc | 309 +-- src/internet-stack/tcp-l4-protocol.h | 11 +- src/internet-stack/tcp-newreno.cc | 221 ++ src/internet-stack/tcp-newreno.h | 74 + src/internet-stack/tcp-reno.cc | 212 ++ src/internet-stack/tcp-reno.h | 75 + src/internet-stack/tcp-rfc793.cc | 91 + src/internet-stack/tcp-rfc793.h | 61 + src/internet-stack/tcp-rx-buffer.cc | 264 +++ src/internet-stack/tcp-rx-buffer.h | 89 + src/internet-stack/tcp-socket-base.cc | 1755 +++++++++++++++ src/internet-stack/tcp-socket-base.h | 222 ++ src/internet-stack/tcp-socket-impl.cc | 1877 ----------------- src/internet-stack/tcp-socket-impl.h | 254 --- src/internet-stack/tcp-tahoe.cc | 190 ++ src/internet-stack/tcp-tahoe.h | 78 + src/internet-stack/tcp-tx-buffer.cc | 250 +++ src/internet-stack/tcp-tx-buffer.h | 121 ++ src/internet-stack/tcp-typedefs.h | 112 - src/internet-stack/wscript | 8 +- src/node/simple-net-device.cc | 2 +- src/node/tcp-socket.cc | 10 +- src/node/tcp-socket.h | 20 + src/routing/aodv/test/tcp-chain-test-0-0.pcap | Bin 47366 -> 59968 bytes src/routing/aodv/test/tcp-chain-test-9-0.pcap | Bin 24372 -> 29392 bytes 35 files changed, 4397 insertions(+), 2548 deletions(-) create mode 100644 examples/tcp/tcp-loss-response.cc create mode 100644 examples/tcp/tcp-testcases.cc create mode 100644 src/internet-stack/tcp-newreno.cc create mode 100644 src/internet-stack/tcp-newreno.h create mode 100644 src/internet-stack/tcp-reno.cc create mode 100644 src/internet-stack/tcp-reno.h create mode 100644 src/internet-stack/tcp-rfc793.cc create mode 100644 src/internet-stack/tcp-rfc793.h create mode 100644 src/internet-stack/tcp-rx-buffer.cc create mode 100644 src/internet-stack/tcp-rx-buffer.h create mode 100644 src/internet-stack/tcp-socket-base.cc create mode 100644 src/internet-stack/tcp-socket-base.h delete mode 100644 src/internet-stack/tcp-socket-impl.cc delete mode 100644 src/internet-stack/tcp-socket-impl.h create mode 100644 src/internet-stack/tcp-tahoe.cc create mode 100644 src/internet-stack/tcp-tahoe.h create mode 100644 src/internet-stack/tcp-tx-buffer.cc create mode 100644 src/internet-stack/tcp-tx-buffer.h delete mode 100644 src/internet-stack/tcp-typedefs.h diff --git a/examples/tcp/tcp-loss-response.cc b/examples/tcp/tcp-loss-response.cc new file mode 100644 index 000000000..440934544 --- /dev/null +++ b/examples/tcp/tcp-loss-response.cc @@ -0,0 +1,292 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// +// Network topology +// +// 8Mb/s, 0.1ms 0.8Mb/s, 100ms +// s1-----------------r1-----------------k1 +// +// Example corresponding to simulations in the paper "Simulation-based +// Comparisons of Tahoe, Reno, and SACK TCP" +// +// To look at the time-sequence plots corresponding to the packet traces, +// the tcptrace tool (www.tcptrace.org) with the -S option can produce +// xplot plots (a2b.xpl, b2a.xpl) which can be viewed using xplot +// +// - Tracing of queues and packet receptions to file +// "tcp-loss-response.tr +// - pcap traces also generated in the following files +// "tcp-loss-response-s1-0.pcap" and "tcp-loss-response-k1-0.pcap" +// +// Usage (e.g.): ./waf --run tcp-loss-response +// + + +#include +#include +#include +#include +#include +#include + +#include "ns3/core-module.h" +#include "ns3/common-module.h" +#include "ns3/helper-module.h" +#include "ns3/node-module.h" +#include "ns3/simulator-module.h" +#include "ns3/ipv4-global-routing-helper.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("TcpLossResponse"); + +// The number of bytes to send in this simulation. +static const uint32_t totalTxBytes = 200000; +static uint32_t currentTxBytes = 0; +// Perform series of 26*40=1040 byte writes (this is a multiple of 26 since +// we want to detect data splicing in the output stream) +static const uint32_t writeSize = 1040; +uint8_t data[writeSize]; +// Need to invoke Socket::Close() on sender? +bool needToClose = true; + +// These are for starting the writing process, and handling the sending +// socket's notification upcalls (events). These two together more or less +// implement a sending "Application", although not a proper ns3::Application +// subclass. + +void StartFlow (Ptr, Ipv4Address, uint16_t); +void WriteUntilBufferFull (Ptr, uint32_t); + +static void +CwndTracer (uint32_t oldval, uint32_t newval) +{ + NS_LOG_INFO ("Moving cwnd from " << oldval << " to " << newval << " at time " << Simulator::Now ().GetSeconds () << " seconds"); +} + +int main (int argc, char *argv[]) +{ + std::string tcpModel ("ns3::TcpNewReno"); + uint32_t losses = 1; + bool verbose = false; + bool tracing = true; + + // Configuration and command line parameter parsing + CommandLine cmd; + cmd.AddValue ("tcpModel", "Tcp congestion control model", tcpModel); + cmd.AddValue ("losses", "number of packet losses", losses); + cmd.AddValue ("verbose", "turn on selected log components", verbose); + cmd.AddValue ("tracing", "turn on ascii and pcap tracing", tracing); + cmd.Parse (argc, argv); + + Config::SetDefault ("ns3::TcpL4Protocol::SocketType", StringValue (tcpModel)); + Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (1000)); // 1000-byte packet for easier reading + Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (1)); + + LogComponentEnableAll (LOG_PREFIX_FUNC); + LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL); + if (verbose) + { + //LogComponentEnableAll (LOG_PREFIX_TIME); + LogComponentEnable ("ErrorModel", LOG_LEVEL_DEBUG); + LogComponentEnable ("TcpLossResponse", LOG_LEVEL_ALL); + LogComponentEnable ("TcpNewReno", LOG_LEVEL_INFO); + LogComponentEnable ("TcpReno", LOG_LEVEL_INFO); + LogComponentEnable ("TcpTahoe", LOG_LEVEL_INFO); + LogComponentEnable ("TcpSocketBase", LOG_LEVEL_INFO); + } + + // initialize the tx buffer (fill with lowercase a to z) + for (uint32_t i = 0; i < writeSize; ++i) + { + char m = toascii (97 + i % 26); + data[i] = m; + } + + //////////////////////////////////////////////////////// + // Topology construction + // + + // Create three nodes: s1, r1, and k1 + NodeContainer s1r1; + s1r1.Create (2); + Names::Add ("s1", s1r1.Get (0)); + Names::Add ("r1", s1r1.Get (1)); + + NodeContainer r1k1; + r1k1.Add (s1r1.Get (1)); + r1k1.Create (1); + Names::Add ("k1", r1k1.Get (1)); + + // Set up TCP/IP stack to all nodes (and create loopback device at device 0) + InternetStackHelper internet; + internet.InstallAll (); + + // Connect the nodes + PointToPointHelper p2p; + p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (8000000))); + p2p.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (0.1))); + NetDeviceContainer dev0 = p2p.Install (s1r1); + p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (800000))); + p2p.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (100))); + NetDeviceContainer dev1 = p2p.Install (r1k1); + + // Add IP addresses to each network interfaces + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.3.0", "255.255.255.0"); + ipv4.Assign (dev0); + ipv4.SetBase ("10.1.2.0", "255.255.255.0"); + Ipv4InterfaceContainer ipInterfs = ipv4.Assign (dev1); + + // Set up routes to all nodes + Ipv4GlobalRoutingHelper::PopulateRoutingTables (); + + //////////////////////////////////////////////////////// + // Send 20000 (totalTxBytes) bytes from node s1 to node k1 + // + + // Create a packet sink to receive packets on node k1 + uint16_t servPort = 50000; // Destination port number + PacketSinkHelper sink ("ns3::TcpSocketFactory", + InetSocketAddress (Ipv4Address::GetAny (), servPort)); + ApplicationContainer apps = sink.Install (r1k1.Get (1)); + apps.Start (Seconds (0.0)); + apps.Stop (Seconds (100.0)); + + // Create a data source to send packets on node s0. + // Instead of full application, here use the socket directly by + // registering callbacks in function StarFlow(). + Ptr localSocket = Socket::CreateSocket (s1r1.Get (0), TcpSocketFactory::GetTypeId ()); + localSocket->Bind (); + Simulator::ScheduleNow (&StartFlow, localSocket, ipInterfs.GetAddress (1), servPort); + + // Trace changes to the congestion window (available in Tahoe and descendents) + Config::ConnectWithoutContext ("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", MakeCallback (&CwndTracer)); + + //////////////////////////////////////////////////////// + // Set up loss model at node k1 + // + + std::list sampleList; + switch (losses) + { + case 0: + break; + case 1: + // Force a loss for 15th data packet. TCP cwnd will be at 14 segments + // (14000 bytes) when duplicate acknowledgments start to come. + sampleList.push_back (16); + break; + case 2: + sampleList.push_back (16); + sampleList.push_back (17); + break; + case 3: + sampleList.push_back (16); + sampleList.push_back (17); + sampleList.push_back (18); + break; + case 4: + sampleList.push_back (16); + sampleList.push_back (17); + sampleList.push_back (18); + sampleList.push_back (19); + break; + default: + NS_FATAL_ERROR ("Program fatal error: loss value " << losses << " not supported."); + break; + } + + Ptr pem = CreateObject (); + pem->SetList (sampleList); + dev1.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (pem)); + + // One can toggle the comment for the following line on or off to see the + // effects of finite send buffer modelling. One can also change the size of + // that buffer. + // localSocket->SetAttribute("SndBufSize", UintegerValue(4096)); + + ///////////////////////////////////////////////////////// + // Set up trace and run the simulation + // + + if (tracing) + { + // Ask for ASCII and pcap traces of network traffic + AsciiTraceHelper ascii; + //Ptr osw = ascii.CreateFileStream ("tcp-loss-response.tr"); + Ptr osw = Create (&std::clog); + *(osw->GetStream ()) << std::setprecision(9) << std::fixed; + p2p.EnableAsciiAll (osw); +// p2p.EnablePcap ("tcp-loss-response", 0, 0); +// p2p.EnablePcap ("tcp-loss-response", 2, 0); + } + + // Finally, set up the simulator to run. The 1000 second hard limit is a + // failsafe in case some change above causes the simulation to never end + Simulator::Stop (Seconds (1000)); + Simulator::Run (); + Simulator::Destroy (); +} + + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// begin implementation of sending "Application" +void StartFlow (Ptr localSocket, + Ipv4Address servAddress, + uint16_t servPort) +{ + NS_LOG_LOGIC ("Starting flow at time " << Simulator::Now ().GetSeconds ()); + localSocket->Connect (InetSocketAddress (servAddress, servPort)); // connect + + // tell the tcp implementation to call WriteUntilBufferFull again + // if we blocked and new tx buffer space becomes available + localSocket->SetSendCallback (MakeCallback (&WriteUntilBufferFull)); + WriteUntilBufferFull (localSocket, localSocket->GetTxAvailable ()); +} + +void WriteUntilBufferFull (Ptr localSocket, uint32_t txSpace) +{ + while (currentTxBytes < totalTxBytes) + { + uint32_t left = totalTxBytes - currentTxBytes; + uint32_t dataOffset = currentTxBytes % writeSize; + uint32_t toWrite = writeSize - dataOffset; + uint32_t txAvail = localSocket->GetTxAvailable (); + toWrite = std::min (toWrite, left); + toWrite = std::min (toWrite, txAvail); + if (txAvail == 0) + { + NS_LOG_LOGIC ("TCP socket buffer full at " << Simulator::Now ().GetSeconds ()); + return; + }; + NS_LOG_LOGIC ("Submitting " << toWrite << " bytes to TCP socket"); + int amountSent = localSocket->Send (&data[dataOffset], toWrite, 0); + NS_ASSERT (amountSent > 0); // Given GetTxAvailable() non-zero, amountSent should not be zero + currentTxBytes += amountSent; + } + if (needToClose) + { + NS_LOG_LOGIC ("Close socket at " << Simulator::Now ().GetSeconds ()); + localSocket->Close (); + needToClose = false; + } +} + diff --git a/examples/tcp/tcp-testcases.cc b/examples/tcp/tcp-testcases.cc new file mode 100644 index 000000000..f4780ce10 --- /dev/null +++ b/examples/tcp/tcp-testcases.cc @@ -0,0 +1,283 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +// +// Network topology +// +// 10Mb/s, 0.1ms 10Mb/s, 0.1ms +// n0-----------------n1-----------------n2 +// +// Testcases for the correctness of TCP operations. +// +// Usage (e.g.): ./waf --run="tcp-testcases --case=1" +// + + +#include +#include +#include +#include +#include +#include + +#include "ns3/core-module.h" +#include "ns3/common-module.h" +#include "ns3/helper-module.h" +#include "ns3/node-module.h" +#include "ns3/simulator-module.h" +#include "ns3/ipv4-global-routing-helper.h" + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE ("TcpTestCases"); + +// The number of bytes to send in this simulation. +static uint32_t totalTxBytes = 200000; +static uint32_t currentTxBytes = 0; +// Perform series of 26*40=1040 byte writes (this is a multiple of 26 since +// we want to detect data splicing in the output stream) +static const uint32_t writeSize = 1040; +uint8_t data[writeSize]; +// Need to close the socket on node 0 upon finishing sending data +static bool needToClose = true; + +//////////////////////////////////////////////////////////////////// +// Implementing an "application" to send bytes over a TCP connection +void WriteUntilBufferFull (Ptr localSocket, uint32_t txSpace) +{ + while (currentTxBytes < totalTxBytes) + { + uint32_t left = totalTxBytes - currentTxBytes; + uint32_t dataOffset = currentTxBytes % writeSize; + uint32_t toWrite = writeSize - dataOffset; + uint32_t txAvail = localSocket->GetTxAvailable (); + toWrite = std::min (toWrite, left); + toWrite = std::min (toWrite, txAvail); + if (txAvail == 0) + { + return; + }; + NS_LOG_LOGIC ("Submitting " << toWrite << " bytes to TCP socket"); + int amountSent = localSocket->Send (&data[dataOffset], toWrite, 0); + NS_ASSERT (amountSent > 0); // Given GetTxAvailable() non-zero, amountSent should not be zero + currentTxBytes += amountSent; + } + if (needToClose) + { + NS_LOG_LOGIC ("Close socket at " << Simulator::Now ().GetSeconds ()); + localSocket->Close (); + needToClose = false; + } +} + +void StartFlow (Ptr localSocket, + Ipv4Address servAddress, + uint16_t servPort) +{ + NS_LOG_LOGIC ("Starting flow at time " << Simulator::Now ().GetSeconds ()); + localSocket->Connect (InetSocketAddress (servAddress, servPort)); // connect + + // tell the tcp implementation to call WriteUntilBufferFull again + // if we blocked and new tx buffer space becomes available + localSocket->SetSendCallback (MakeCallback (&WriteUntilBufferFull)); + WriteUntilBufferFull (localSocket, localSocket->GetTxAvailable ()); +} + +// cwnd tracer +static void +CwndTracer (uint32_t oldval, uint32_t newval) +{ + NS_LOG_INFO ("Moving cwnd from " << oldval << " to " << newval << " at time " << Simulator::Now ().GetSeconds () << " seconds"); +} + +int main (int argc, char *argv[]) +{ + std::string tcpModel ("ns3::TcpNewReno"); + uint32_t testcase = 1; + uint32_t verbose = 0; + bool tracing = true; + + // Configuration and command line parameter parsing + CommandLine cmd; + cmd.AddValue ("tcpModel", "TCP congestion control model", tcpModel); + cmd.AddValue ("testcase", "test case", testcase); + cmd.AddValue ("verbose", "turn on selected log components", verbose); + cmd.Parse (argc, argv); + + Config::SetDefault ("ns3::TcpL4Protocol::SocketType", StringValue (tcpModel)); + Config::SetDefault ("ns3::TcpSocket::SegmentSize", UintegerValue (1000)); // 1000-byte packet for easier reading + Config::SetDefault ("ns3::TcpSocket::DelAckCount", UintegerValue (1)); + Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (20)); + + LogComponentEnableAll (LOG_PREFIX_FUNC); + LogComponentEnable ("TcpTestCases", LOG_LEVEL_ALL); + if (verbose) + { + //LogComponentEnableAll (LOG_PREFIX_TIME); + LogComponentEnable ("ErrorModel", LOG_LEVEL_DEBUG); + LogComponentEnable ("TcpTestCases", LOG_LEVEL_ALL); + LogComponentEnable ("TcpNewReno", LOG_LEVEL_INFO); + LogComponentEnable ("TcpReno", LOG_LEVEL_INFO); + LogComponentEnable ("TcpTahoe", LOG_LEVEL_INFO); + LogComponentEnable ("TcpSocketBase", (verbose>1)?LOG_LEVEL_ALL:LOG_LEVEL_INFO); + //LogComponentEnable ("TcpTxBuffer", LOG_LEVEL_ALL); + //LogComponentEnable ("TcpRxBuffer", LOG_LEVEL_ALL); + } + + // initialize the tx buffer (fill with lowercase a to z) + for (uint32_t i = 0; i < writeSize; ++i) + { + char m = toascii (97 + i % 26); + data[i] = m; + } + + //////////////////////////////////////////////////////// + // Topology construction + // + + // Create three nodes + NodeContainer n0n1; + n0n1.Create (2); + Names::Add ("n0", n0n1.Get (0)); + Names::Add ("n1", n0n1.Get (1)); + + NodeContainer n1n2; + n1n2.Add (n0n1.Get (1)); + n1n2.Create (1); + Names::Add ("n2", n1n2.Get (1)); + + // Set up TCP/IP stack to all nodes (and create loopback device at device 0) + InternetStackHelper internet; + internet.InstallAll (); + + // Connect the nodes + PointToPointHelper p2p; + p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate (1e6))); + p2p.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (0.1))); + NetDeviceContainer dev0 = p2p.Install (n0n1); + NetDeviceContainer dev1 = p2p.Install (n1n2); + + // Add IP addresses to each network interfaces + Ipv4AddressHelper ipv4; + ipv4.SetBase ("10.1.3.0", "255.255.255.0"); + ipv4.Assign (dev0); + ipv4.SetBase ("10.1.2.0", "255.255.255.0"); + Ipv4InterfaceContainer ipInterfs = ipv4.Assign (dev1); + + // Set up routes to all nodes + Ipv4GlobalRoutingHelper::PopulateRoutingTables (); + + //////////////////////////////////////////////////////// + // A flow from node n0 to node n2 + // + + // Create a packet sink to receive packets on node n2 + uint16_t servPort = 50000; // Destination port number + PacketSinkHelper sink ("ns3::TcpSocketFactory", InetSocketAddress (Ipv4Address::GetAny (), servPort)); + ApplicationContainer apps = sink.Install (n1n2.Get (1)); + apps.Start (Seconds (0.0)); + apps.Stop (Seconds (100.0)); + + // Create a data source to send packets on node n0 + // Instead of full application, here use the socket directly by + // registering callbacks in function StarFlow(). + Ptr localSocket = Socket::CreateSocket (n0n1.Get (0), TcpSocketFactory::GetTypeId ()); + localSocket->Bind (); + Simulator::ScheduleNow (&StartFlow, localSocket, ipInterfs.GetAddress (1), servPort); + + // Trace changes to the congestion window (available in Tahoe and descendents) + Config::ConnectWithoutContext ("/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow", MakeCallback (&CwndTracer)); + + //////////////////////////////////////////////////////// + // Set up different test cases: Lost model at node n1, different file size + // + + std::list dropListN0; + std::list dropListN1; + switch (testcase) + { + case 0: // Verify connection establishment + totalTxBytes = 1000; + break; + case 1: // Verify a bigger (100 pkts) transfer: Sliding window operation, etc. + totalTxBytes = 1e5; + break; + case 2: // Survive a SYN lost + totalTxBytes = 1000; + dropListN0.push_back (0); + break; + case 3: // Survive a SYN+ACK lost + totalTxBytes = 2000; + dropListN1.push_back (0); + break; + case 4: // Survive a ACK (last packet in 3-way handshake) lost + totalTxBytes = 2000; + dropListN0.push_back (1); + break; + case 5: // Immediate FIN upon SYN_RCVD + totalTxBytes = 0; + needToClose = false; + dropListN0.push_back (1); // Hide the ACK in 3WHS + Simulator::Schedule (Seconds(0.002), &Socket::Close, localSocket); + break; + case 6: // Simulated simultaneous close + totalTxBytes = 5000; + dropListN1.push_back (5); // Hide the ACK-to-FIN from n2 + break; + case 7: // FIN check 1: Lost of initiator's FIN. Shall wait until application close. + needToClose = false; + totalTxBytes = 5000; + dropListN0.push_back (7); // Hide the FIN from n0 + Simulator::Schedule (Seconds(0.04), &Socket::Close, localSocket); + break; + case 8: // FIN check 2: Lost of responder's FIN. The FIN will resent after last ack timeout + totalTxBytes = 5000; + dropListN1.push_back (6); // Hide the FIN from n2 + break; + default: + NS_FATAL_ERROR ("Program fatal error: specified test case not supported: " << testcase); + break; + } + + Ptr errN0 = CreateObject (); + errN0->SetList (dropListN0); + dev0.Get (1)->SetAttribute ("ReceiveErrorModel", PointerValue (errN0)); + + Ptr errN1 = CreateObject (); + errN1->SetList (dropListN1); + dev1.Get (0)->SetAttribute ("ReceiveErrorModel", PointerValue (errN1)); + + ///////////////////////////////////////////////////////// + // Set up trace and run the simulation + // + + if (tracing) + { + // Ask for ASCII and pcap traces of network traffic + AsciiTraceHelper ascii; + //Ptr osw = ascii.CreateFileStream ("tcp-loss-response.tr"); + Ptr osw = Create (&std::clog); + *(osw->GetStream ()) << std::setprecision(9) << std::fixed; + p2p.EnableAsciiAll (osw); + } + + // Finally, set up the simulator to run. The 1000 second hard limit is a + // failsafe in case some change above causes the simulation to never end + Simulator::Stop (Seconds (1000)); + Simulator::Run (); + Simulator::Destroy (); +} diff --git a/examples/tcp/wscript b/examples/tcp/wscript index ebe5e1d24..ed0262fc6 100644 --- a/examples/tcp/wscript +++ b/examples/tcp/wscript @@ -21,6 +21,14 @@ def build(bld): ['point-to-point', 'internet-stack']) obj.source = 'star.cc' + obj = bld.create_ns3_program('tcp-loss-response', + ['point-to-point', 'internet-stack']) + obj.source = 'tcp-loss-response.cc' + + obj = bld.create_ns3_program('tcp-testcases', + ['point-to-point', 'internet-stack']) + obj.source = 'tcp-testcases.cc' + obj = bld.create_ns3_program('tcp-bulk-send', ['point-to-point', 'internet-stack']) obj.source = 'tcp-bulk-send.cc' diff --git a/src/common/output-stream-wrapper.cc b/src/common/output-stream-wrapper.cc index f3f3b29ec..9f52faf6c 100644 --- a/src/common/output-stream-wrapper.cc +++ b/src/common/output-stream-wrapper.cc @@ -27,18 +27,27 @@ NS_LOG_COMPONENT_DEFINE ("OutputStreamWrapper"); namespace ns3 { OutputStreamWrapper::OutputStreamWrapper (std::string filename, std::ios::openmode filemode) - : m_ostream (new std::ofstream ()) + : m_destroyable (true) +{ + std::ofstream* os = new std::ofstream (); + os->open (filename.c_str (), filemode); + m_ostream = os; + FatalImpl::RegisterStream (m_ostream); + NS_ABORT_MSG_UNLESS (os->is_open (), "AsciiTraceHelper::CreateFileStream(): " << + "Unable to Open " << filename << " for mode " << filemode); +} + +OutputStreamWrapper::OutputStreamWrapper (std::ostream* os) + : m_ostream (os), m_destroyable (false) { FatalImpl::RegisterStream (m_ostream); - m_ostream->open (filename.c_str (), filemode); - NS_ABORT_MSG_UNLESS (m_ostream->is_open (), "AsciiTraceHelper::CreateFileStream(): " << - "Unable to Open " << filename << " for mode " << filemode); + NS_ABORT_MSG_UNLESS (m_ostream->good (), "Output stream is not vaild for writing."); } OutputStreamWrapper::~OutputStreamWrapper () { FatalImpl::UnregisterStream (m_ostream); - delete m_ostream; + if (m_destroyable) delete m_ostream; m_ostream = 0; } diff --git a/src/common/output-stream-wrapper.h b/src/common/output-stream-wrapper.h index 27a6a7b50..432c70799 100644 --- a/src/common/output-stream-wrapper.h +++ b/src/common/output-stream-wrapper.h @@ -71,6 +71,7 @@ class OutputStreamWrapper : public SimpleRefCount { public: OutputStreamWrapper (std::string filename, std::ios::openmode filemode); + OutputStreamWrapper (std::ostream* os); ~OutputStreamWrapper (); /** @@ -83,7 +84,8 @@ public: std::ostream *GetStream (void); private: - std::ofstream *m_ostream; + std::ostream *m_ostream; + bool m_destroyable; }; } //namespace ns3 diff --git a/src/helper/point-to-point-helper.cc b/src/helper/point-to-point-helper.cc index 654c2ff1c..bc0f9a6d8 100644 --- a/src/helper/point-to-point-helper.cc +++ b/src/helper/point-to-point-helper.cc @@ -171,6 +171,9 @@ PointToPointHelper::EnableAsciiInternal ( asciiTraceHelper.HookDefaultDropSinkWithoutContext (queue, "Drop", theStream); asciiTraceHelper.HookDefaultDequeueSinkWithoutContext (queue, "Dequeue", theStream); + // PhyRxDrop trace source for "d" event + asciiTraceHelper.HookDefaultDropSinkWithoutContext (device, "PhyRxDrop", theStream); + return; } @@ -204,6 +207,10 @@ PointToPointHelper::EnableAsciiInternal ( oss.str (""); oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid << "/$ns3::PointToPointNetDevice/TxQueue/Drop"; Config::Connect (oss.str (), MakeBoundCallback (&AsciiTraceHelper::DefaultDropSinkWithContext, stream)); + + oss.str (""); + oss << "/NodeList/" << nodeid << "/DeviceList/" << deviceid << "/$ns3::PointToPointNetDevice/PhyRxDrop"; + Config::Connect (oss.str (), MakeBoundCallback (&AsciiTraceHelper::DefaultDropSinkWithContext, stream)); } NetDeviceContainer diff --git a/src/internet-stack/nsc-tcp-l4-protocol.cc b/src/internet-stack/nsc-tcp-l4-protocol.cc index 6b5ca448d..15a98a524 100644 --- a/src/internet-stack/nsc-tcp-l4-protocol.cc +++ b/src/internet-stack/nsc-tcp-l4-protocol.cc @@ -37,8 +37,6 @@ #include "nsc-tcp-socket-factory-impl.h" #include "sim_interface.h" -#include "tcp-typedefs.h" - #include #include #include diff --git a/src/internet-stack/nsc-tcp-socket-impl.cc b/src/internet-stack/nsc-tcp-socket-impl.cc index 026361f19..c8eba7da8 100644 --- a/src/internet-stack/nsc-tcp-socket-impl.cc +++ b/src/internet-stack/nsc-tcp-socket-impl.cc @@ -28,7 +28,6 @@ #include "nsc-tcp-l4-protocol.h" #include "nsc-tcp-socket-impl.h" #include "ns3/simulation-singleton.h" -#include "tcp-typedefs.h" #include "ns3/simulator.h" #include "ns3/packet.h" #include "ns3/uinteger.h" @@ -39,8 +38,8 @@ // for ntohs(). #include #include - #include "sim_interface.h" + #include "sim_errno.h" NS_LOG_COMPONENT_DEFINE ("NscTcpSocketImpl"); @@ -63,7 +62,7 @@ NscTcpSocketImpl::GetTypeId () return tid; } - NscTcpSocketImpl::NscTcpSocketImpl () +NscTcpSocketImpl::NscTcpSocketImpl () : m_endPoint (0), m_node (0), m_tcp (0), @@ -795,6 +794,18 @@ NscTcpSocketImpl::GetDelAckMaxCount (void) const return m_delAckMaxCount; } +void +NscTcpSocketImpl::SetPersistTimeout (Time timeout) +{ + m_persistTimeout = timeout; +} + +Time +NscTcpSocketImpl::GetPersistTimeout (void) const +{ + return m_persistTimeout; +} + enum Socket::SocketErrno NscTcpSocketImpl::GetNativeNs3Errno(int error) const { diff --git a/src/internet-stack/nsc-tcp-socket-impl.h b/src/internet-stack/nsc-tcp-socket-impl.h index 3653683aa..fb47b1f4c 100644 --- a/src/internet-stack/nsc-tcp-socket-impl.h +++ b/src/internet-stack/nsc-tcp-socket-impl.h @@ -27,7 +27,6 @@ #include "ns3/ipv4-address.h" #include "ns3/inet-socket-address.h" #include "ns3/event-id.h" -#include "tcp-typedefs.h" #include "pending-data.h" #include "ns3/sequence-number.h" @@ -126,6 +125,8 @@ private: virtual Time GetDelAckTimeout (void) const; virtual void SetDelAckMaxCount (uint32_t count); virtual uint32_t GetDelAckMaxCount (void) const; + virtual void SetPersistTimeout (Time timeout); + virtual Time GetPersistTimeout (void) const; enum Socket::SocketErrno GetNativeNs3Errno(int err) const; uint32_t m_delAckMaxCount; @@ -146,7 +147,7 @@ private: bool m_connected; //manage the state information - States_t m_state; + TracedValue m_state; bool m_closeOnEmpty; //needed to queue data when in SYN_SENT state @@ -167,6 +168,7 @@ private: // Timer-related members Time m_cnTimeout; uint32_t m_cnCount; + Time m_persistTimeout; // Temporary queue for delivering data to application std::queue > m_deliveryQueue; diff --git a/src/internet-stack/tcp-header.cc b/src/internet-stack/tcp-header.cc index 4b3eb3a7d..3c6f287c2 100644 --- a/src/internet-stack/tcp-header.cc +++ b/src/internet-stack/tcp-header.cc @@ -20,7 +20,6 @@ #include #include -#include "tcp-socket-impl.h" #include "tcp-header.h" #include "ns3/buffer.h" #include "ns3/address-utils.h" diff --git a/src/internet-stack/tcp-l4-protocol.cc b/src/internet-stack/tcp-l4-protocol.cc index 2138d1307..260e76b0a 100644 --- a/src/internet-stack/tcp-l4-protocol.cc +++ b/src/internet-stack/tcp-l4-protocol.cc @@ -35,9 +35,8 @@ #include "ipv4-end-point.h" #include "ipv4-l3-protocol.h" #include "tcp-socket-factory-impl.h" -#include "tcp-socket-impl.h" +#include "tcp-newreno.h" #include "rtt-estimator.h" -#include "tcp-typedefs.h" #include #include @@ -49,269 +48,6 @@ namespace ns3 { NS_OBJECT_ENSURE_REGISTERED (TcpL4Protocol); -//State Machine things -------------------------------------------------------- -TcpStateMachine::TcpStateMachine() - : aT (LAST_STATE, StateActionVec_t(LAST_EVENT)), - eV (MAX_FLAGS) -{ - NS_LOG_FUNCTION_NOARGS (); - - // Create the state table - // Closed state - aT[CLOSED][APP_LISTEN] = SA (LISTEN, NO_ACT); - aT[CLOSED][APP_CONNECT] = SA (SYN_SENT, SYN_TX); - aT[CLOSED][APP_SEND] = SA (CLOSED, RST_TX); - aT[CLOSED][SEQ_RECV] = SA (CLOSED, NO_ACT); - aT[CLOSED][APP_CLOSE] = SA (CLOSED, NO_ACT); - aT[CLOSED][TIMEOUT] = SA (CLOSED, RST_TX); - aT[CLOSED][ACK_RX] = SA (CLOSED, RST_TX); - aT[CLOSED][SYN_RX] = SA (CLOSED, RST_TX); - aT[CLOSED][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[CLOSED][FIN_RX] = SA (CLOSED, RST_TX); - aT[CLOSED][FIN_ACK_RX] = SA (CLOSED, RST_TX); - aT[CLOSED][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[CLOSED][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // Listen State - // For the listen state, anything other than CONNECT or SEND - // is simply ignored....this likely indicates the child TCP - // has finished and issued unbind call, but the remote end - // has not yet closed. - aT[LISTEN][APP_LISTEN] = SA (LISTEN, NO_ACT); - aT[LISTEN][APP_CONNECT] = SA (SYN_SENT, SYN_TX); - aT[LISTEN][APP_SEND] = SA (SYN_SENT, SYN_TX); - aT[LISTEN][SEQ_RECV] = SA (LISTEN, NO_ACT); - aT[LISTEN][APP_CLOSE] = SA (CLOSED, NO_ACT); - aT[LISTEN][TIMEOUT] = SA (LISTEN, NO_ACT); - aT[LISTEN][ACK_RX] = SA (LISTEN, NO_ACT); - aT[LISTEN][SYN_RX] = SA (LISTEN, SYN_ACK_TX);//stay in listen and fork - aT[LISTEN][SYN_ACK_RX] = SA (LISTEN, NO_ACT); - aT[LISTEN][FIN_RX] = SA (LISTEN, NO_ACT); - aT[LISTEN][FIN_ACK_RX] = SA (LISTEN, NO_ACT); - aT[LISTEN][RST_RX] = SA (LISTEN, NO_ACT); - aT[LISTEN][BAD_FLAGS] = SA (LISTEN, NO_ACT); - - // Syn Sent State - aT[SYN_SENT][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[SYN_SENT][APP_CONNECT] = SA (SYN_SENT, SYN_TX); - aT[SYN_SENT][APP_SEND] = SA (SYN_SENT, NO_ACT); - aT[SYN_SENT][SEQ_RECV] = SA (ESTABLISHED, NEW_SEQ_RX); - aT[SYN_SENT][APP_CLOSE] = SA (CLOSED, RST_TX); - aT[SYN_SENT][TIMEOUT] = SA (CLOSED, NO_ACT); - aT[SYN_SENT][ACK_RX] = SA (SYN_SENT, NO_ACT); - aT[SYN_SENT][SYN_RX] = SA (SYN_RCVD, SYN_ACK_TX); - aT[SYN_SENT][SYN_ACK_RX] = SA (ESTABLISHED, ACK_TX_1); - aT[SYN_SENT][FIN_RX] = SA (CLOSED, RST_TX); - aT[SYN_SENT][FIN_ACK_RX] = SA (CLOSED, RST_TX); - aT[SYN_SENT][RST_RX] = SA (CLOSED, APP_NOTIFY); - aT[SYN_SENT][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // Syn Recvd State - aT[SYN_RCVD][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][APP_CONNECT] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][APP_SEND] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][SEQ_RECV] = SA (ESTABLISHED, NEW_SEQ_RX); - aT[SYN_RCVD][APP_CLOSE] = SA (FIN_WAIT_1, FIN_TX); - aT[SYN_RCVD][TIMEOUT] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][ACK_RX] = SA (ESTABLISHED, SERV_NOTIFY); - aT[SYN_RCVD][SYN_RX] = SA (SYN_RCVD, SYN_ACK_TX); - aT[SYN_RCVD][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][FIN_RX] = SA (CLOSED, RST_TX); - aT[SYN_RCVD][FIN_ACK_RX] = SA (CLOSE_WAIT, PEER_CLOSE); - aT[SYN_RCVD][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[SYN_RCVD][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // Established State - aT[ESTABLISHED][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[ESTABLISHED][APP_CONNECT]= SA (CLOSED, RST_TX); - aT[ESTABLISHED][APP_SEND] = SA (ESTABLISHED,TX_DATA); - aT[ESTABLISHED][SEQ_RECV] = SA (ESTABLISHED,NEW_SEQ_RX); - aT[ESTABLISHED][APP_CLOSE] = SA (FIN_WAIT_1, FIN_TX); - aT[ESTABLISHED][TIMEOUT] = SA (ESTABLISHED,RETX); - aT[ESTABLISHED][ACK_RX] = SA (ESTABLISHED,NEW_ACK); - aT[ESTABLISHED][SYN_RX] = SA (SYN_RCVD, SYN_ACK_TX); - aT[ESTABLISHED][SYN_ACK_RX] = SA (ESTABLISHED,NO_ACT); - aT[ESTABLISHED][FIN_RX] = SA (CLOSE_WAIT, PEER_CLOSE); - aT[ESTABLISHED][FIN_ACK_RX] = SA (CLOSE_WAIT, PEER_CLOSE); - aT[ESTABLISHED][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[ESTABLISHED][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // Close Wait State - aT[CLOSE_WAIT][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[CLOSE_WAIT][APP_CONNECT] = SA (SYN_SENT, SYN_TX); - aT[CLOSE_WAIT][APP_SEND] = SA (CLOSE_WAIT, TX_DATA); - aT[CLOSE_WAIT][SEQ_RECV] = SA (CLOSE_WAIT, NEW_SEQ_RX); - aT[CLOSE_WAIT][APP_CLOSE] = SA (LAST_ACK, FIN_ACK_TX); - aT[CLOSE_WAIT][TIMEOUT] = SA (CLOSE_WAIT, NO_ACT); - aT[CLOSE_WAIT][ACK_RX] = SA (CLOSE_WAIT, NEW_ACK); - aT[CLOSE_WAIT][SYN_RX] = SA (CLOSED, RST_TX); - aT[CLOSE_WAIT][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[CLOSE_WAIT][FIN_RX] = SA (CLOSE_WAIT, ACK_TX); - aT[CLOSE_WAIT][FIN_ACK_RX] = SA (CLOSE_WAIT, ACK_TX); - aT[CLOSE_WAIT][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[CLOSE_WAIT][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // Close Last Ack State - aT[LAST_ACK][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[LAST_ACK][APP_CONNECT] = SA (SYN_SENT, SYN_TX); - aT[LAST_ACK][APP_SEND] = SA (CLOSED, RST_TX); - aT[LAST_ACK][SEQ_RECV] = SA (LAST_ACK, NEW_SEQ_RX); - aT[LAST_ACK][APP_CLOSE] = SA (CLOSED, NO_ACT); - aT[LAST_ACK][TIMEOUT] = SA (CLOSED, NO_ACT); - aT[LAST_ACK][ACK_RX] = SA (LAST_ACK, NO_ACT); - aT[LAST_ACK][FIN_ACKED] = SA (CLOSED, APP_CLOSED); - aT[LAST_ACK][SYN_RX] = SA (CLOSED, RST_TX); - aT[LAST_ACK][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[LAST_ACK][FIN_RX] = SA (LAST_ACK, FIN_ACK_TX); - aT[LAST_ACK][FIN_ACK_RX] = SA (CLOSED, NO_ACT); - aT[LAST_ACK][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[LAST_ACK][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // FIN_WAIT_1 state - aT[FIN_WAIT_1][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_1][APP_CONNECT] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_1][APP_SEND] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_1][SEQ_RECV] = SA (FIN_WAIT_1, NEW_SEQ_RX); - aT[FIN_WAIT_1][APP_CLOSE] = SA (FIN_WAIT_1, NO_ACT); - aT[FIN_WAIT_1][TIMEOUT] = SA (FIN_WAIT_1, NO_ACT); - aT[FIN_WAIT_1][ACK_RX] = SA (FIN_WAIT_1, NEW_ACK); - aT[FIN_WAIT_1][FIN_ACKED] = SA (FIN_WAIT_2, NEW_ACK); - aT[FIN_WAIT_1][SYN_RX] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_1][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_1][FIN_RX] = SA (CLOSING, ACK_TX); - aT[FIN_WAIT_1][FIN_ACK_RX] = SA (TIMED_WAIT, ACK_TX); - aT[FIN_WAIT_1][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[FIN_WAIT_1][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // FIN_WAIT_2 state - aT[FIN_WAIT_2][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_2][APP_CONNECT] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_2][APP_SEND] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_2][SEQ_RECV] = SA (FIN_WAIT_2, NEW_SEQ_RX); - aT[FIN_WAIT_2][APP_CLOSE] = SA (FIN_WAIT_2, NO_ACT); - aT[FIN_WAIT_2][TIMEOUT] = SA (FIN_WAIT_2, NO_ACT); - aT[FIN_WAIT_2][ACK_RX] = SA (FIN_WAIT_2, NEW_ACK); - aT[FIN_WAIT_2][SYN_RX] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_2][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[FIN_WAIT_2][FIN_RX] = SA (TIMED_WAIT, ACK_TX); - aT[FIN_WAIT_2][FIN_ACK_RX] = SA (TIMED_WAIT, ACK_TX); - aT[FIN_WAIT_2][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[FIN_WAIT_2][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // CLOSING state - aT[CLOSING][APP_LISTEN] = SA (CLOSED, RST_TX); - aT[CLOSING][APP_CONNECT] = SA (CLOSED, RST_TX); - aT[CLOSING][APP_SEND] = SA (CLOSED, RST_TX); - aT[CLOSING][SEQ_RECV] = SA (CLOSED, RST_TX); - aT[CLOSING][APP_CLOSE] = SA (CLOSED, RST_TX); - aT[CLOSING][TIMEOUT] = SA (CLOSING, NO_ACT); - aT[CLOSING][ACK_RX] = SA (CLOSING, NO_ACT); - aT[CLOSING][FIN_ACKED] = SA (TIMED_WAIT, NO_ACT); - aT[CLOSING][SYN_RX] = SA (CLOSED, RST_TX); - aT[CLOSING][SYN_ACK_RX] = SA (CLOSED, RST_TX); - aT[CLOSING][FIN_RX] = SA (CLOSED, ACK_TX); - aT[CLOSING][FIN_ACK_RX] = SA (CLOSED, ACK_TX); - aT[CLOSING][RST_RX] = SA (CLOSED, CANCEL_TM); - aT[CLOSING][BAD_FLAGS] = SA (CLOSED, RST_TX); - - // TIMED_WAIT state - aT[TIMED_WAIT][APP_LISTEN] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][APP_CONNECT] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][APP_SEND] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][SEQ_RECV] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][APP_CLOSE] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][TIMEOUT] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][ACK_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][SYN_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][SYN_ACK_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][FIN_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][FIN_ACK_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][RST_RX] = SA (TIMED_WAIT, NO_ACT); - aT[TIMED_WAIT][BAD_FLAGS] = SA (TIMED_WAIT, NO_ACT); - - // Create the flags lookup table - eV[ 0x00] = SEQ_RECV; // No flags - eV[ 0x01] = FIN_RX; // Fin - eV[ 0x02] = SYN_RX; // Syn - eV[ 0x03] = BAD_FLAGS; // Illegal - eV[ 0x04] = RST_RX; // Rst - eV[ 0x05] = BAD_FLAGS; // Illegal - eV[ 0x06] = BAD_FLAGS; // Illegal - eV[ 0x07] = BAD_FLAGS; // Illegal - eV[ 0x08] = SEQ_RECV; // Psh flag is not used - eV[ 0x09] = FIN_RX; // Fin - eV[ 0x0a] = SYN_RX; // Syn - eV[ 0x0b] = BAD_FLAGS; // Illegal - eV[ 0x0c] = RST_RX; // Rst - eV[ 0x0d] = BAD_FLAGS; // Illegal - eV[ 0x0e] = BAD_FLAGS; // Illegal - eV[ 0x0f] = BAD_FLAGS; // Illegal - eV[ 0x10] = ACK_RX; // Ack - eV[ 0x11] = FIN_ACK_RX;// Fin/Ack - eV[ 0x12] = SYN_ACK_RX;// Syn/Ack - eV[ 0x13] = BAD_FLAGS; // Illegal - eV[ 0x14] = RST_RX; // Rst - eV[ 0x15] = BAD_FLAGS; // Illegal - eV[ 0x16] = BAD_FLAGS; // Illegal - eV[ 0x17] = BAD_FLAGS; // Illegal - eV[ 0x18] = ACK_RX; // Ack - eV[ 0x19] = FIN_ACK_RX;// Fin/Ack - eV[ 0x1a] = SYN_ACK_RX;// Syn/Ack - eV[ 0x1b] = BAD_FLAGS; // Illegal - eV[ 0x1c] = RST_RX; // Rst - eV[ 0x1d] = BAD_FLAGS; // Illegal - eV[ 0x1e] = BAD_FLAGS; // Illegal - eV[ 0x1f] = BAD_FLAGS; // Illegal - eV[ 0x20] = SEQ_RECV; // No flags (Urgent not presently used) - eV[ 0x21] = FIN_RX; // Fin - eV[ 0x22] = SYN_RX; // Syn - eV[ 0x23] = BAD_FLAGS; // Illegal - eV[ 0x24] = RST_RX; // Rst - eV[ 0x25] = BAD_FLAGS; // Illegal - eV[ 0x26] = BAD_FLAGS; // Illegal - eV[ 0x27] = BAD_FLAGS; // Illegal - eV[ 0x28] = SEQ_RECV; // Psh flag is not used - eV[ 0x29] = FIN_RX; // Fin - eV[ 0x2a] = SYN_RX; // Syn - eV[ 0x2b] = BAD_FLAGS; // Illegal - eV[ 0x2c] = RST_RX; // Rst - eV[ 0x2d] = BAD_FLAGS; // Illegal - eV[ 0x2e] = BAD_FLAGS; // Illegal - eV[ 0x2f] = BAD_FLAGS; // Illegal - eV[ 0x30] = ACK_RX; // Ack (Urgent not used) - eV[ 0x31] = FIN_ACK_RX;// Fin/Ack - eV[ 0x32] = SYN_ACK_RX;// Syn/Ack - eV[ 0x33] = BAD_FLAGS; // Illegal - eV[ 0x34] = RST_RX; // Rst - eV[ 0x35] = BAD_FLAGS; // Illegal - eV[ 0x36] = BAD_FLAGS; // Illegal - eV[ 0x37] = BAD_FLAGS; // Illegal - eV[ 0x38] = ACK_RX; // Ack - eV[ 0x39] = FIN_ACK_RX;// Fin/Ack - eV[ 0x3a] = SYN_ACK_RX;// Syn/Ack - eV[ 0x3b] = BAD_FLAGS; // Illegal - eV[ 0x3c] = RST_RX; // Rst - eV[ 0x3d] = BAD_FLAGS; // Illegal - eV[ 0x3e] = BAD_FLAGS; // Illegal - eV[ 0x3f] = BAD_FLAGS; // Illegal -} - -SA TcpStateMachine::Lookup (States_t s, Events_t e) -{ - NS_LOG_FUNCTION (this << s << e); - return aT[s][e]; -} - -Events_t TcpStateMachine::FlagsEvent (uint8_t f) -{ - NS_LOG_FUNCTION (this << f); - // Lookup event from flags - if (f >= MAX_FLAGS) return BAD_FLAGS; - return eV[f]; // Look up flags event -} - -static TcpStateMachine tcpStateMachine; //only instance of a TcpStateMachine - //TcpL4Protocol stuff---------------------------------------------------------- #undef NS_LOG_APPEND_CONTEXT @@ -321,29 +57,26 @@ static TcpStateMachine tcpStateMachine; //only instance of a TcpStateMachine /* see http://www.iana.org/assignments/protocol-numbers */ const uint8_t TcpL4Protocol::PROT_NUMBER = 6; -ObjectFactory -TcpL4Protocol::GetDefaultRttEstimatorFactory (void) -{ - ObjectFactory factory; - factory.SetTypeId (RttMeanDeviation::GetTypeId ()); - return factory; -} - TypeId TcpL4Protocol::GetTypeId (void) { static TypeId tid = TypeId ("ns3::TcpL4Protocol") .SetParent () .AddConstructor () - .AddAttribute ("RttEstimatorFactory", - "How RttEstimator objects are created.", - ObjectFactoryValue (GetDefaultRttEstimatorFactory ()), - MakeObjectFactoryAccessor (&TcpL4Protocol::m_rttFactory), - MakeObjectFactoryChecker ()) + .AddAttribute ("RttEstimatorType", + "Type of RttEstimator objects.", + TypeIdValue (RttMeanDeviation::GetTypeId()), + MakeTypeIdAccessor (&TcpL4Protocol::m_rttTypeId), + MakeTypeIdChecker ()) + .AddAttribute ("SocketType", + "Socket type of TCP objects.", + TypeIdValue (TcpNewReno::GetTypeId()), + MakeTypeIdAccessor (&TcpL4Protocol::m_socketTypeId), + MakeTypeIdChecker ()) .AddAttribute ("SocketList", "The list of sockets associated to this protocol.", ObjectVectorValue (), MakeObjectVectorAccessor (&TcpL4Protocol::m_sockets), - MakeObjectVectorChecker ()) + MakeObjectVectorChecker ()) ; return tid; } @@ -403,7 +136,7 @@ void TcpL4Protocol::DoDispose (void) { NS_LOG_FUNCTION_NOARGS (); - for (std::vector >::iterator i = m_sockets.begin (); i != m_sockets.end (); i++) + for (std::vector >::iterator i = m_sockets.begin (); i != m_sockets.end (); i++) { *i = 0; } @@ -420,11 +153,15 @@ TcpL4Protocol::DoDispose (void) } Ptr -TcpL4Protocol::CreateSocket (void) +TcpL4Protocol::CreateSocket (TypeId socketTypeId) { NS_LOG_FUNCTION_NOARGS (); - Ptr rtt = m_rttFactory.Create (); - Ptr socket = CreateObject (); + ObjectFactory rttFactory; + ObjectFactory socketFactory; + rttFactory.SetTypeId(m_rttTypeId); + socketFactory.SetTypeId(socketTypeId); + Ptr rtt = rttFactory.Create (); + Ptr socket = socketFactory.Create (); socket->SetNode (m_node); socket->SetTcp (this); socket->SetRtt (rtt); @@ -432,6 +169,12 @@ TcpL4Protocol::CreateSocket (void) return socket; } +Ptr +TcpL4Protocol::CreateSocket (void) +{ + return CreateSocket (m_socketTypeId); +} + Ipv4EndPoint * TcpL4Protocol::Allocate (void) { diff --git a/src/internet-stack/tcp-l4-protocol.h b/src/internet-stack/tcp-l4-protocol.h index 2158fc1a1..dc4abd43d 100644 --- a/src/internet-stack/tcp-l4-protocol.h +++ b/src/internet-stack/tcp-l4-protocol.h @@ -37,7 +37,7 @@ class Socket; class TcpHeader; class Ipv4EndPointDemux; class Ipv4Interface; -class TcpSocketImpl; +class TcpSocketBase; class Ipv4EndPoint; /** @@ -69,6 +69,7 @@ public: * of the TCP protocol */ Ptr CreateSocket (void); + Ptr CreateSocket (TypeId socketTypeId); Ipv4EndPoint *Allocate (void); Ipv4EndPoint *Allocate (Ipv4Address address); @@ -115,16 +116,16 @@ protected: private: Ptr m_node; Ipv4EndPointDemux *m_endPoints; - ObjectFactory m_rttFactory; + TypeId m_rttTypeId; + TypeId m_socketTypeId; private: - friend class TcpSocketImpl; + friend class TcpSocketBase; void SendPacket (Ptr, const TcpHeader &, Ipv4Address, Ipv4Address, Ptr oif = 0); - static ObjectFactory GetDefaultRttEstimatorFactory (void); TcpL4Protocol (const TcpL4Protocol &o); TcpL4Protocol &operator = (const TcpL4Protocol &o); - std::vector > m_sockets; + std::vector > m_sockets; }; }; // namespace ns3 diff --git a/src/internet-stack/tcp-newreno.cc b/src/internet-stack/tcp-newreno.cc new file mode 100644 index 000000000..fa6dfa981 --- /dev/null +++ b/src/internet-stack/tcp-newreno.cc @@ -0,0 +1,221 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#define NS_LOG_APPEND_CONTEXT \ + if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; } + +#include "tcp-newreno.h" +#include "ns3/log.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/simulator.h" +#include "ns3/abort.h" +#include "ns3/node.h" + +NS_LOG_COMPONENT_DEFINE ("TcpNewReno"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TcpNewReno); + +TypeId +TcpNewReno::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpNewReno") + .SetParent () + .AddConstructor () + .AddTraceSource ("CongestionWindow", + "The TCP connection's congestion window", + MakeTraceSourceAccessor (&TcpNewReno::m_cWnd)) + ; + return tid; +} + +TcpNewReno::TcpNewReno (void) : m_inFastRec (false) +{ + NS_LOG_FUNCTION (this); +} + +TcpNewReno::TcpNewReno (const TcpNewReno& sock) + : TcpSocketBase (sock), + m_cWnd (sock.m_cWnd), + m_ssThresh (sock.m_ssThresh), + m_initialCWnd (sock.m_initialCWnd), + m_inFastRec (false) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Invoked the copy constructor"); +} + +TcpNewReno::~TcpNewReno (void) +{ +} + +void +TcpNewReno::SetNode (Ptr node) +{ + TcpSocketBase::SetNode (node); + /* + * Initialize congestion window, default to 1 MSS (RFC2001, sec.1) and must + * not be larger than 2 MSS (RFC2581, sec.3.1). Both m_initiaCWnd and + * m_segmentSize are set by the attribute system in ns3::TcpSocket. + */ + m_cWnd = m_initialCWnd * m_segmentSize; +} + +/** Limit the size of in-flight data by cwnd and receiver's rxwin */ +uint32_t +TcpNewReno::Window (void) +{ + NS_LOG_FUNCTION (this); + return std::min (m_rWnd.Get (), m_cWnd.Get ()); +} + +Ptr +TcpNewReno::Fork (void) +{ + return CopyObject (this); +} + +/** New ACK (up to seqnum seq) received. Increase cwnd and call TcpSocketBase::NewAck() */ +void +TcpNewReno::NewAck (const SequenceNumber32& seq) +{ + NS_LOG_FUNCTION (this << seq); + NS_LOG_LOGIC ("TcpNewReno receieved ACK for seq " << seq << + " cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + + // Check for exit condition of fast recovery + if (m_inFastRec && seq < m_recover) + { // Partial ACK, partial window deflation (RFC2582 sec.3 bullet #5 paragraph 3) + m_cWnd += m_segmentSize; // increase cwnd + NS_LOG_INFO ("Partial ACK in fast recovery: cwnd set to " << m_cWnd); + TcpSocketBase::NewAck(seq); // update m_nextTxSequence and send new data if allowed by window + DoRetransmit (); // Assume the next seq is lost. Retransmit lost packet + return; + } + else if (m_inFastRec && seq >= m_recover) + { // Full ACK (RFC2582 sec.3 bullet #5 paragraph 2, option 1) + m_cWnd = std::min (m_ssThresh, BytesInFlight () + m_segmentSize); + m_inFastRec = false; + NS_LOG_INFO ("Received full ACK. Leaving fast recovery with cwnd set to " << m_cWnd); + } + + // Increase of cwnd based on current phase (slow start or congestion avoidance) + if (m_cWnd < m_ssThresh) + { // Slow start mode, add one segSize to cWnd. Default m_ssThresh is 65535. (RFC2001, sec.1) + m_cWnd += m_segmentSize; + NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + else + { // Congestion avoidance mode, increase by (segSize*segSize)/cwnd. (RFC2581, sec.3.1) + // To increase cwnd for one segSize per RTT, it should be (ackBytes*segSize)/cwnd + double adder = static_cast (m_segmentSize * m_segmentSize) / m_cWnd.Get (); + adder = std::max (1.0, adder); + m_cWnd += static_cast (adder); + NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + + // Complete newAck processing + TcpSocketBase::NewAck (seq); +} + +/** Cut cwnd and enter fast recovery mode upon triple dupack */ +void +TcpNewReno::DupAck (const TcpHeader& t, uint32_t count) +{ + if (count == 3 && ! m_inFastRec) + { // triple duplicate ack triggers fast retransmit (RFC2582 sec.3 bullet #1) + m_ssThresh = std::max (2 * m_segmentSize, BytesInFlight () / 2); + m_cWnd = m_ssThresh + 3 * m_segmentSize; + m_recover = m_highTxMark; + m_inFastRec = true; + NS_LOG_INFO ("Triple dupack. Enter fast recovery mode. Reset cwnd to " << m_cWnd << + ", ssthresh to " << m_ssThresh << " at fast recovery seqnum " << m_recover); + DoRetransmit (); + } + else if (m_inFastRec) + { // Increase cwnd for every additional dupack (RFC2582, sec.3 bullet #3) + m_cWnd += m_segmentSize; + NS_LOG_INFO ("Dupack in fast recovery mode. Increase cwnd to " << m_cWnd); + SendPendingData (m_connected); + }; +} + +/** Retransmit timeout */ +void +TcpNewReno::Retransmit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC (this << " ReTxTimeout Expired at time " << Simulator::Now ().GetSeconds ()); + m_inFastRec = false; + + // If erroneous timeout in closed/timed-wait state, just return + if (m_state == CLOSED || m_state == TIME_WAIT) return; + // If all data are received, just return + if (m_txBuffer.HeadSequence () >= m_nextTxSequence) return; + + // According to RFC2581 sec.3.1, upon RTO, ssthresh is set to half of flight + // size and cwnd is set to 1*MSS, then the lost packet is retransmitted and + // TCP back to slow start + m_ssThresh = std::max (2 * m_segmentSize, BytesInFlight () / 2); + m_cWnd = m_segmentSize; + m_nextTxSequence = m_txBuffer.HeadSequence (); // Restart from highest Ack + NS_LOG_INFO ("RTO. Reset cwnd to " << m_cWnd << + ", ssthresh to " << m_ssThresh << ", restart from seqnum " << m_nextTxSequence); + m_rtt->IncreaseMultiplier (); // Double the next RTO + DoRetransmit (); // Retransmit the packet +} + +void +TcpNewReno::SetSegSize (uint32_t size) +{ + NS_ABORT_MSG_UNLESS (m_state == CLOSED, "TcpNewReno::SetSegSize() cannot change segment size after connection started."); + m_segmentSize = size; + m_cWnd = m_initialCWnd * m_segmentSize; +} + +void +TcpNewReno::SetSSThresh (uint32_t threshold) +{ + m_ssThresh = threshold; +} + +uint32_t +TcpNewReno::GetSSThresh (void) const +{ + return m_ssThresh; +} + +void +TcpNewReno::SetInitialCwnd (uint32_t cwnd) +{ + NS_ABORT_MSG_UNLESS (m_state == CLOSED, "TcpNewReno::SetInitialCwnd() cannot change initial cwnd after connection started."); + m_initialCWnd = cwnd; + m_cWnd = m_initialCWnd * m_segmentSize; +} + +uint32_t +TcpNewReno::GetInitialCwnd (void) const +{ + return m_initialCWnd; +} + +} // namespace ns3 diff --git a/src/internet-stack/tcp-newreno.h b/src/internet-stack/tcp-newreno.h new file mode 100644 index 000000000..54091fbb1 --- /dev/null +++ b/src/internet-stack/tcp-newreno.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#ifndef TCP_NEWRENO_H +#define TCP_NEWRENO_H + +#include "tcp-socket-base.h" + +namespace ns3 { + +/** + * \ingroup socket + * \ingroup tcp + * + * \brief An implementation of a stream socket using TCP. + * + * This class contains the NewReno implementation of TCP, as of RFC2582. + */ +class TcpNewReno : public TcpSocketBase +{ +public: + static TypeId GetTypeId (void); + /** + * Create an unbound tcp socket. + */ + TcpNewReno (void); + TcpNewReno (const TcpNewReno& sock); + virtual ~TcpNewReno (void); + + // Set associated Node, TcpL4Protocol, RttEstimator to this socket + virtual void SetNode (Ptr node); + +protected: + virtual uint32_t Window (void); // Return the max possible number of unacked bytes + virtual Ptr Fork (void); // Call CopyObject to clone me + virtual void NewAck (SequenceNumber32 const& seq); // Inc cwnd and call NewAck() of parent + virtual void DupAck (const TcpHeader& t, uint32_t count); // Halving cwnd and reset nextTxSequence + virtual void Retransmit (void); // Exit fast recovery upon retransmit timeout + + // Implementing ns3::TcpSocket -- Attribute get/set + virtual void SetSegSize (uint32_t size); + virtual void SetSSThresh (uint32_t threshold); + virtual uint32_t GetSSThresh (void) const; + virtual void SetInitialCwnd (uint32_t cwnd); + virtual uint32_t GetInitialCwnd (void) const; + +protected: + TracedValue m_cWnd; //< Congestion window + uint32_t m_ssThresh; //< Slow Start Threshold + uint32_t m_initialCWnd; //< Initial cWnd value + SequenceNumber32 m_recover; //< Previous highest Tx seqnum for fast recovery + bool m_inFastRec; //< currently in fast recovery +}; + +} // namespace ns3 + +#endif /* TCP_NEWRENO_H */ diff --git a/src/internet-stack/tcp-reno.cc b/src/internet-stack/tcp-reno.cc new file mode 100644 index 000000000..74a03cc29 --- /dev/null +++ b/src/internet-stack/tcp-reno.cc @@ -0,0 +1,212 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#define NS_LOG_APPEND_CONTEXT \ + if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; } + +#include "tcp-reno.h" +#include "ns3/log.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/simulator.h" +#include "ns3/abort.h" +#include "ns3/node.h" + +NS_LOG_COMPONENT_DEFINE ("TcpReno"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TcpReno); + +TypeId +TcpReno::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpReno") + .SetParent () + .AddConstructor () + .AddTraceSource ("CongestionWindow", + "The TCP connection's congestion window", + MakeTraceSourceAccessor (&TcpReno::m_cWnd)) + ; + return tid; +} + +TcpReno::TcpReno (void) : m_inFastRec (false) +{ + NS_LOG_FUNCTION (this); +} + +TcpReno::TcpReno (const TcpReno& sock) + : TcpSocketBase (sock), + m_cWnd (sock.m_cWnd), + m_ssThresh (sock.m_ssThresh), + m_initialCWnd (sock.m_initialCWnd), + m_inFastRec (false) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Invoked the copy constructor"); +} + +TcpReno::~TcpReno (void) +{ +} + +void +TcpReno::SetNode (Ptr node) +{ + TcpSocketBase::SetNode (node); + /* + * Initialize congestion window, default to 1 MSS (RFC2001, sec.1) and must + * not be larger than 2 MSS (RFC2581, sec.3.1). Both m_initiaCWnd and + * m_segmentSize are set by the attribute system in ns3::TcpSocket. + */ + m_cWnd = m_initialCWnd * m_segmentSize; +} + +/** Limit the size of in-flight data by cwnd and receiver's rxwin */ +uint32_t +TcpReno::Window (void) +{ + NS_LOG_FUNCTION (this); + return std::min (m_rWnd.Get (), m_cWnd.Get ()); +} + +Ptr +TcpReno::Fork (void) +{ + return CopyObject (this); +} + +/** New ACK (up to seqnum seq) received. Increase cwnd and call TcpSocketBase::NewAck() */ +void +TcpReno::NewAck (const SequenceNumber32& seq) +{ + NS_LOG_FUNCTION (this << seq); + NS_LOG_LOGIC ("TcpReno receieved ACK for seq " << seq << + " cwnd " << m_cWnd << + " ssthresh " << m_ssThresh); + + // Check for exit condition of fast recovery + if (m_inFastRec) + { // RFC2001, sec.4; RFC2581, sec.3.2 + // First new ACK after fast recovery: reset cwnd + m_cWnd = m_ssThresh; + m_inFastRec = false; + NS_LOG_INFO ("Reset cwnd to " << m_cWnd); + }; + + // Increase of cwnd based on current phase (slow start or congestion avoidance) + if (m_cWnd < m_ssThresh) + { // Slow start mode, add one segSize to cWnd. Default m_ssThresh is 65535. (RFC2001, sec.1) + m_cWnd += m_segmentSize; + NS_LOG_INFO ("In SlowStart, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + else + { // Congestion avoidance mode, increase by (segSize*segSize)/cwnd. (RFC2581, sec.3.1) + // To increase cwnd for one segSize per RTT, it should be (ackBytes*segSize)/cwnd + double adder = static_cast (m_segmentSize * m_segmentSize) / m_cWnd.Get (); + adder = std::max (1.0, adder); + m_cWnd += static_cast (adder); + NS_LOG_INFO ("In CongAvoid, updated to cwnd " << m_cWnd << " ssthresh " << m_ssThresh); + } + + // Complete newAck processing + TcpSocketBase::NewAck (seq); +} + +// Fast recovery and fast retransmit +void +TcpReno::DupAck (const TcpHeader& t, uint32_t count) +{ + NS_LOG_FUNCTION (this << "t " << count); + if (count == 3 && ! m_inFastRec) + { // triple duplicate ack triggers fast retransmit (RFC2581, sec.3.2) + m_ssThresh = std::max (2 * m_segmentSize, BytesInFlight () / 2); + m_cWnd = m_ssThresh + 3 * m_segmentSize; + m_inFastRec = true; + NS_LOG_INFO ("Triple dupack. Reset cwnd to " << m_cWnd << ", ssthresh to " << m_ssThresh); + DoRetransmit (); + } + else if (m_inFastRec) + { // In fast recovery, inc cwnd for every additional dupack (RFC2581, sec.3.2) + m_cWnd += m_segmentSize; + NS_LOG_INFO ("Increased cwnd to " << m_cWnd); + SendPendingData (m_connected); + }; +} + +// Retransmit timeout +void TcpReno::Retransmit (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC (this << " ReTxTimeout Expired at time " << Simulator::Now ().GetSeconds ()); + m_inFastRec = false; + + // If erroneous timeout in closed/timed-wait state, just return + if (m_state == CLOSED || m_state == TIME_WAIT) return; + // If all data are received, just return + if (m_txBuffer.HeadSequence () >= m_nextTxSequence) return; + + // According to RFC2581 sec.3.1, upon RTO, ssthresh is set to half of flight + // size and cwnd is set to 1*MSS, then the lost packet is retransmitted and + // TCP back to slow start + m_ssThresh = std::max (2 * m_segmentSize, BytesInFlight () / 2); + m_cWnd = m_segmentSize; + m_nextTxSequence = m_txBuffer.HeadSequence (); // Restart from highest Ack + NS_LOG_INFO ("RTO. Reset cwnd to " << m_cWnd << + ", ssthresh to " << m_ssThresh << ", restart from seqnum " << m_nextTxSequence); + m_rtt->IncreaseMultiplier (); // Double the next RTO + DoRetransmit (); // Retransmit the packet +} + +void +TcpReno::SetSegSize (uint32_t size) +{ + NS_ABORT_MSG_UNLESS (m_state == CLOSED, "TcpReno::SetSegSize() cannot change segment size after connection started."); + m_segmentSize = size; + m_cWnd = m_initialCWnd * m_segmentSize; +} + +void +TcpReno::SetSSThresh (uint32_t threshold) +{ + m_ssThresh = threshold; +} + +uint32_t +TcpReno::GetSSThresh (void) const +{ + return m_ssThresh; +} + +void +TcpReno::SetInitialCwnd (uint32_t cwnd) +{ + NS_ABORT_MSG_UNLESS (m_state == CLOSED, "TcpReno::SetInitialCwnd() cannot change initial cwnd after connection started."); + m_initialCWnd = cwnd; + m_cWnd = m_initialCWnd * m_segmentSize; +} + +uint32_t +TcpReno::GetInitialCwnd (void) const +{ + return m_initialCWnd; +} + +} // namespace ns3 diff --git a/src/internet-stack/tcp-reno.h b/src/internet-stack/tcp-reno.h new file mode 100644 index 000000000..87d1f8594 --- /dev/null +++ b/src/internet-stack/tcp-reno.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#ifndef TCP_RENO_H +#define TCP_RENO_H + +#include "tcp-socket-base.h" + +namespace ns3 { + +/** + * \ingroup socket + * \ingroup tcp + * + * \brief An implementation of a stream socket using TCP. + * + * This class contains the Reno implementation of TCP, according to RFC2581, + * except sec.4.1 "re-starting idle connections", which we do not detect for + * idleness and thus no slow start upon resumption. + */ +class TcpReno : public TcpSocketBase +{ +public: + static TypeId GetTypeId (void); + /** + * Create an unbound tcp socket. + */ + TcpReno (void); + TcpReno (const TcpReno& sock); + virtual ~TcpReno (void); + + // Set associated Node, TcpL4Protocol, RttEstimator to this socket + virtual void SetNode (Ptr node); + +protected: + virtual uint32_t Window (void); // Return the max possible number of unacked bytes + virtual Ptr Fork (void); // Call CopyObject to clone me + virtual void NewAck (const SequenceNumber32& seq); // Inc cwnd and call NewAck() of parent + virtual void DupAck (const TcpHeader& t, uint32_t count); // Fast retransmit + virtual void Retransmit (void); // Retransmit timeout + + // Implementing ns3::TcpSocket -- Attribute get/set + virtual void SetSegSize (uint32_t size); + virtual void SetSSThresh (uint32_t threshold); + virtual uint32_t GetSSThresh (void) const; + virtual void SetInitialCwnd (uint32_t cwnd); + virtual uint32_t GetInitialCwnd (void) const; + +protected: + TracedValue m_cWnd; //< Congestion window + uint32_t m_ssThresh; //< Slow Start Threshold + uint32_t m_initialCWnd; //< Initial cWnd value + bool m_inFastRec; //< currently in fast recovery +}; + +} // namespace ns3 + +#endif /* TCP_RENO_H */ diff --git a/src/internet-stack/tcp-rfc793.cc b/src/internet-stack/tcp-rfc793.cc new file mode 100644 index 000000000..4c725f644 --- /dev/null +++ b/src/internet-stack/tcp-rfc793.cc @@ -0,0 +1,91 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#include "tcp-rfc793.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("TcpRfc793"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TcpRfc793); + +TypeId +TcpRfc793::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpRfc793") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TcpRfc793::TcpRfc793 (void) +{ + NS_LOG_FUNCTION (this); + SetDelAckMaxCount(0); // Delayed ACK is not in RFC793 +} + +TcpRfc793::TcpRfc793 (const TcpRfc793& sock) : TcpSocketBase (sock) +{ +} + +TcpRfc793::~TcpRfc793 (void) +{ +} + +Ptr +TcpRfc793::Fork (void) +{ + return CopyObject (this); +} + +void +TcpRfc793::DupAck (const TcpHeader& t, uint32_t count) +{ +} + +void +TcpRfc793::SetSSThresh (uint32_t threshold) +{ + NS_LOG_WARN ("DoD TCP does not perform slow start"); +} + +uint32_t +TcpRfc793::GetSSThresh (void) const +{ + NS_LOG_WARN ("DoD TCP does not perform slow start"); + return 0; +} + +void +TcpRfc793::SetInitialCwnd (uint32_t cwnd) +{ + NS_LOG_WARN ("DoD TCP does not have congestion window"); +} + +uint32_t +TcpRfc793::GetInitialCwnd (void) const +{ + NS_LOG_WARN ("DoD TCP does not have congestion window"); + return 0; +} + +} // namespace ns3 diff --git a/src/internet-stack/tcp-rfc793.h b/src/internet-stack/tcp-rfc793.h new file mode 100644 index 000000000..d734130e7 --- /dev/null +++ b/src/internet-stack/tcp-rfc793.h @@ -0,0 +1,61 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#ifndef TCP_RFC793_H +#define TCP_RFC793_H + +#include "tcp-socket-base.h" + +namespace ns3 { + +/** + * \ingroup socket + * \ingroup tcp + * + * \brief An implementation of a stream socket using TCP. + * + * This class contains an RFC793 implementation of TCP, as well as a sockets + * interface for talking to TCP. This serves as a base for other TCP functions + * where the sliding window mechanism is handled here. This class provides + * connection orientation and sliding window flow control. + */ +class TcpRfc793 : public TcpSocketBase +{ +public: + static TypeId GetTypeId (void); + /** + * Create an unbound tcp socket. + */ + TcpRfc793 (void); + TcpRfc793 (const TcpRfc793& sock); + virtual ~TcpRfc793 (void); + +protected: + virtual Ptr Fork (); // Call CopyObject to clone me + virtual void DupAck (const TcpHeader& t, uint32_t count); + virtual void SetSSThresh (uint32_t threshold); + virtual uint32_t GetSSThresh (void) const; + virtual void SetInitialCwnd (uint32_t cwnd); + virtual uint32_t GetInitialCwnd (void) const; +}; + +} // namespace ns3 + +#endif /* TCP_RFC793_H */ diff --git a/src/internet-stack/tcp-rx-buffer.cc b/src/internet-stack/tcp-rx-buffer.cc new file mode 100644 index 000000000..fdd703abc --- /dev/null +++ b/src/internet-stack/tcp-rx-buffer.cc @@ -0,0 +1,264 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#include "ns3/packet.h" +#include "ns3/fatal-error.h" +#include "ns3/log.h" +#include "tcp-rx-buffer.h" + +NS_LOG_COMPONENT_DEFINE ("TcpRxBuffer"); + +namespace ns3 { + +TypeId +TcpRxBuffer::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpRxBuffer") + .SetParent () + .AddConstructor () + .AddTraceSource ("NextRxSequence", + "Next sequence number expected (RCV.NXT)", + MakeTraceSourceAccessor (&TcpRxBuffer::m_nextRxSeq)) + ; + return tid; +} + +/* A user is supposed to create a TcpSocket through a factory. In TcpSocket, + * there are attributes SndBufSize and RcvBufSize to control the default Tx and + * Rx window sizes respectively, with default of 128 KiByte. The attribute + * RcvBufSize is passed to TcpRxBuffer by TcpSocketBase::SetRcvBufSize() and in + * turn, TcpRxBuffer:SetMaxBufferSize(). Therefore, the m_maxBuffer value + * initialized below is insignificant. + */ +TcpRxBuffer::TcpRxBuffer (uint32_t n) + : m_nextRxSeq(n), m_gotFin(false), m_size(0), m_maxBuffer(32768), m_availBytes(0) +{ +} + +TcpRxBuffer::~TcpRxBuffer () +{ +} + +SequenceNumber32 +TcpRxBuffer::NextRxSequence (void) const +{ + return m_nextRxSeq; +} + +void +TcpRxBuffer::SetNextRxSequence (const SequenceNumber32& s) +{ + m_nextRxSeq = s; +} + +uint32_t +TcpRxBuffer::MaxBufferSize (void) const +{ + return m_maxBuffer; +} + +void +TcpRxBuffer::SetMaxBufferSize (uint32_t s) +{ + m_maxBuffer = s; +} + +uint32_t +TcpRxBuffer::Size (void) const +{ + return m_size; +} + +uint32_t +TcpRxBuffer::Available () const +{ + return m_availBytes; +} + +void +TcpRxBuffer::IncNextRxSequence () +{ + NS_LOG_FUNCTION (this); + // Increment nextRxSeq is valid only if we don't have any data buffered, + // this is supposed to be called only during the three-way handshake + NS_ASSERT (m_size == 0); + m_nextRxSeq++; +} + +// Return the highest sequence number that this TcpRxBuffer can accept +SequenceNumber32 +TcpRxBuffer::MaxRxSequence (void) const +{ + if (m_gotFin) + { // No data allowed beyond FIN + return m_finSeq; + } + else if (m_data.size ()) + { // No data allowed beyond Rx window allowed + return m_data.begin()->first + SequenceNumber32 (m_maxBuffer); + } + return m_nextRxSeq + SequenceNumber32 (m_maxBuffer); +} + +void +TcpRxBuffer::SetFinSequence (const SequenceNumber32& s) +{ + NS_LOG_FUNCTION (this); + + m_gotFin = true; + m_finSeq = s; + if (m_nextRxSeq == m_finSeq) ++m_nextRxSeq; +} + +bool +TcpRxBuffer::Finished (void) +{ + return (m_gotFin && m_finSeq < m_nextRxSeq); +} + +bool +TcpRxBuffer::Add (Ptr p, TcpHeader const& tcph) +{ + NS_LOG_FUNCTION (this << p << tcph); + + uint32_t pktSize = p->GetSize (); + SequenceNumber32 headSeq = tcph.GetSequenceNumber (); + SequenceNumber32 tailSeq = headSeq + SequenceNumber32 (pktSize); + NS_LOG_LOGIC ("Add pkt " << p << " len=" << pktSize << " seq=" << headSeq + << ", when NextRxSeq=" << m_nextRxSeq << ", buffsize=" << m_size); + + // Trim packet to fit Rx window specification + if (headSeq < m_nextRxSeq) headSeq = m_nextRxSeq; + if (m_data.size ()) + { + SequenceNumber32 maxSeq = m_data.begin ()->first + SequenceNumber32 (m_maxBuffer); + if (maxSeq < tailSeq) tailSeq = maxSeq; + if (tailSeq < headSeq) headSeq = tailSeq; + } + // Remove overlapped bytes from packet + BufIterator i = m_data.begin (); + while (i != m_data.end () && i->first <= tailSeq) + { + SequenceNumber32 lastByteSeq = i->first + SequenceNumber32 (i->second->GetSize ()); + if (lastByteSeq > headSeq) + { + if (i->first > headSeq && lastByteSeq < tailSeq) + { // Rare case: Existing packet is embedded fully in the new packet + m_size -= i->second->GetSize(); + m_data.erase (i++); + continue; + } + if (i->first <= headSeq) + { // Incoming head is overlapped + headSeq = lastByteSeq; + } + if (lastByteSeq >= tailSeq) + { // Incoming tail is overlapped + tailSeq = i->first; + } + } + ++i; + } + // We now know how much we are going to store, trim the packet + if (headSeq >= tailSeq) + { + NS_LOG_LOGIC ("Nothing to buffer"); + return false; // Nothing to buffer anyway + } + else + { + uint32_t start = headSeq - tcph.GetSequenceNumber (); + uint32_t length = tailSeq - headSeq; + p = p->CreateFragment (start, length); + NS_ASSERT (length == p->GetSize ()); + } + // Insert packet into buffer + NS_ASSERT (m_data.find (headSeq) == m_data.end ()); // Shouldn't be there yet + m_data [ headSeq ] = p; + NS_LOG_LOGIC ("Buffered packet of seqno=" << headSeq << " len=" << p->GetSize ()); + // Update variables + m_size += p->GetSize (); // Occupancy + for (BufIterator i = m_data.begin (); i != m_data.end (); ++i) + { + if (i->first < m_nextRxSeq) + { + continue; + } + else if (i->first > m_nextRxSeq) + { + break; + }; + m_nextRxSeq = i->first + SequenceNumber32 (i->second->GetSize ()); + m_availBytes += i->second->GetSize (); + } + NS_LOG_LOGIC ("Updated buffer occupancy=" << m_size << " nextRxSeq=" << m_nextRxSeq); + if (m_gotFin && m_nextRxSeq == m_finSeq) + { // Account for the FIN packet + ++m_nextRxSeq; + }; + return true; +} + +Ptr +TcpRxBuffer::Extract (uint32_t maxSize) +{ + NS_LOG_FUNCTION (this << maxSize); + + uint32_t extractSize = std::min (maxSize, m_availBytes); + NS_LOG_LOGIC ("Requested to extract " << extractSize << " bytes from TcpRxBuffer of size=" << m_size); + if (extractSize == 0) return 0; // No contiguous block to return + NS_ASSERT (m_data.size ()); // At least we have something to extract + Ptr outPkt = Create (); // The packet that contains all the data to return + BufIterator i; + while (extractSize) + { // Check the buffered data for delivery + i = m_data.begin (); + NS_ASSERT (i->first <= m_nextRxSeq); // in-sequence data expected + // Check if we send the whole pkt or just a partial + uint32_t pktSize = i->second->GetSize (); + if (pktSize <= extractSize) + { // Whole packet is extracted + outPkt->AddAtEnd (i->second); + m_data.erase (i); + m_size -= pktSize; + m_availBytes -= pktSize; + extractSize -= pktSize; + } + else + { // Partial is extracted and done + outPkt->AddAtEnd (i->second->CreateFragment (0, extractSize)); + m_data[i->first + SequenceNumber32 (extractSize)] = i->second->CreateFragment (extractSize, pktSize - extractSize); + m_data.erase (i); + m_size -= extractSize; + m_availBytes -= extractSize; + extractSize = 0; + } + } + if (outPkt->GetSize () == 0) + { + NS_LOG_LOGIC ("Nothing extracted."); + return 0; + } + NS_LOG_LOGIC ("Extracted " << outPkt->GetSize( ) << " bytes, bufsize=" << m_size + << ", num pkts in buffer=" << m_data.size ()); + return outPkt; +} + +} //namepsace ns3 diff --git a/src/internet-stack/tcp-rx-buffer.h b/src/internet-stack/tcp-rx-buffer.h new file mode 100644 index 000000000..5c6efcd5b --- /dev/null +++ b/src/internet-stack/tcp-rx-buffer.h @@ -0,0 +1,89 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#ifndef __TCP_RX_BUFFER_H__ +#define __TCP_RX_BUFFER_H__ + +#include +#include "ns3/traced-value.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/sequence-number.h" +#include "ns3/ptr.h" +#include "ns3/tcp-header.h" + +namespace ns3 { +class Packet; + +/** + * \ingroup tcp + * + * \brief class for the reordering buffer that keeps the data from lower layer, i.e. + * TcpL4Protocol, sent to the application + */ +class TcpRxBuffer : public Object +{ +public: + static TypeId GetTypeId (void); + TcpRxBuffer (uint32_t n = 0); + virtual ~TcpRxBuffer (); + + // Accessors + SequenceNumber32 NextRxSequence (void) const; + SequenceNumber32 MaxRxSequence (void) const; + void IncNextRxSequence (void); + void SetNextRxSequence (const SequenceNumber32& s); + void SetFinSequence (const SequenceNumber32& s); + uint32_t MaxBufferSize (void) const; + void SetMaxBufferSize (uint32_t s); + uint32_t Size (void) const; + uint32_t Available () const; + bool Finished (void); + + /** + * Insert a packet into the buffer and update the availBytes counter to + * reflect the number of bytes ready to send to the application. This + * function handles overlap by triming the head of the inputted packet and + * removing data from the buffer that overlaps the tail of the inputted + * packet + * + * \return True when success, false otherwise. + */ + bool Add (Ptr p, TcpHeader const& tcph); + + /** + * Extract data from the head of the buffer as indicated by nextRxSeq. + * The extracted data is going to be forwarded to the application. + */ + Ptr Extract (uint32_t maxSize); +public: + typedef std::map >::iterator BufIterator; + TracedValue m_nextRxSeq; //< Seqnum of the first missing byte in data (RCV.NXT) + SequenceNumber32 m_finSeq; //< Seqnum of the FIN packet + bool m_gotFin; //< Did I received FIN packet? + uint32_t m_size; //< Number of total data bytes in the buffer, not necessarily contiguous + uint32_t m_maxBuffer; //< Upper bound of the number of data bytes in buffer (RCV.WND) + uint32_t m_availBytes; //< Number of bytes available to read, i.e. contiguous block at head + std::map > m_data; + //< Corresponding data (may be null) +}; + +}//namepsace ns3 + +#endif /* __TCP_RX_BUFFER_H__ */ diff --git a/src/internet-stack/tcp-socket-base.cc b/src/internet-stack/tcp-socket-base.cc new file mode 100644 index 000000000..f6d2af221 --- /dev/null +++ b/src/internet-stack/tcp-socket-base.cc @@ -0,0 +1,1755 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ + +#define NS_LOG_APPEND_CONTEXT \ + if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << "] "; } + +#include "ns3/abort.h" +#include "ns3/node.h" +#include "ns3/inet-socket-address.h" +#include "ns3/log.h" +#include "ns3/ipv4.h" +#include "ns3/ipv4-interface-address.h" +#include "ns3/ipv4-route.h" +#include "ns3/ipv4-routing-protocol.h" +#include "ns3/simulation-singleton.h" +#include "ns3/simulator.h" +#include "ns3/packet.h" +#include "ns3/uinteger.h" +#include "ns3/trace-source-accessor.h" +#include "tcp-socket-base.h" +#include "tcp-l4-protocol.h" +#include "ipv4-end-point.h" +#include "tcp-header.h" +#include "rtt-estimator.h" + +#include + +NS_LOG_COMPONENT_DEFINE ("TcpSocketBase"); + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TcpSocketBase); + +TypeId +TcpSocketBase::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpSocketBase") + .SetParent () +// .AddAttribute ("TcpState", "State in TCP state machine", +// TypeId::ATTR_GET, +// EnumValue (CLOSED), +// MakeEnumAccessor (&TcpSocketBase::m_state), +// MakeEnumChecker (CLOSED, "Closed")) + .AddTraceSource ("RTO", + "Retransmission timeout", + MakeTraceSourceAccessor (&TcpSocketBase::m_rto)) + .AddTraceSource ("RTT", + "Last RTT sample", + MakeTraceSourceAccessor (&TcpSocketBase::m_lastRtt)) + .AddTraceSource ("NextTxSequence", + "Next sequence number to send (SND.NXT)", + MakeTraceSourceAccessor (&TcpSocketBase::m_nextTxSequence)) + .AddTraceSource ("HighestSequence", + "Highest sequence number ever sent in socket's life time", + MakeTraceSourceAccessor (&TcpSocketBase::m_highTxMark)) + .AddTraceSource ("State", + "TCP state", + MakeTraceSourceAccessor (&TcpSocketBase::m_state)) + .AddTraceSource ("RWND", + "Remote side's flow control window", + MakeTraceSourceAccessor (&TcpSocketBase::m_rWnd)) + ; + return tid; +} + +TcpSocketBase::TcpSocketBase (void) + : m_dupAckCount (0), + m_delAckCount (0), + m_endPoint (0), + m_node (0), + m_tcp (0), + m_rtt (0), + m_nextTxSequence (0), // Change this for non-zero initial sequence number + m_highTxMark (0), + m_rxBuffer (0), + m_txBuffer (0), + m_state (CLOSED), + m_errno (ERROR_NOTERROR), + m_closeNotified (false), + m_closeOnEmpty (false), + m_shutdownSend (false), + m_shutdownRecv (false), + m_connected (false), + m_segmentSize (0), // For attribute initialization consistency (quiet valgrind) + m_rWnd (0) +{ + NS_LOG_FUNCTION (this); +} + +TcpSocketBase::TcpSocketBase (const TcpSocketBase& sock) + : TcpSocket (sock), //copy object::m_tid and socket::callbacks + m_dupAckCount (sock.m_dupAckCount), + m_delAckCount (0), + m_delAckMaxCount (sock.m_delAckMaxCount), + m_cnCount (sock.m_cnCount), + m_delAckTimeout (sock.m_delAckTimeout), + m_persistTimeout (sock.m_persistTimeout), + m_cnTimeout (sock.m_cnTimeout), + m_endPoint (0), + m_node (sock.m_node), + m_tcp (sock.m_tcp), + m_rtt (0), + m_nextTxSequence (sock.m_nextTxSequence), + m_highTxMark (sock.m_highTxMark), + m_rxBuffer (sock.m_rxBuffer), + m_txBuffer (sock.m_txBuffer), + m_state (sock.m_state), + m_errno (sock.m_errno), + m_closeNotified (sock.m_closeNotified), + m_closeOnEmpty (sock.m_closeOnEmpty), + m_shutdownSend (sock.m_shutdownSend), + m_shutdownRecv (sock.m_shutdownRecv), + m_connected (sock.m_connected), + m_segmentSize (sock.m_segmentSize), + m_rWnd (sock.m_rWnd) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Invoked the copy constructor"); + // Copy the rtt estimator if it is set + if (sock.m_rtt) + { + m_rtt = sock.m_rtt->Copy (); + } + // Reset all callbacks to null + Callback > vPS = MakeNullCallback > (); + Callback, const Address &> vPSA = MakeNullCallback, const Address &> (); + Callback, uint32_t> vPSUI = MakeNullCallback, uint32_t> (); + SetConnectCallback (vPS, vPS); + SetDataSentCallback (vPSUI); + SetSendCallback (vPSUI); + SetRecvCallback (vPS); +} + +TcpSocketBase::~TcpSocketBase (void) +{ + NS_LOG_FUNCTION (this); + m_node = 0; + if (m_endPoint != 0) + { + NS_ASSERT (m_tcp != 0); + /* + * Upon Bind, an Ipv4Endpoint is allocated and set to m_endPoint, and + * DestroyCallback is set to TcpSocketBase::Destroy. If we called + * m_tcp->DeAllocate, it wil destroy its Ipv4EndpointDemux::DeAllocate, + * which in turn destroys my m_endPoint, and in turn invokes + * TcpSocketBase::Destroy to nullify m_node, m_endPoint, and m_tcp. + */ + NS_ASSERT (m_endPoint != 0); + m_tcp->DeAllocate (m_endPoint); + NS_ASSERT (m_endPoint == 0); + } + m_tcp = 0; + CancelAllTimers (); +} + +/** Associate a node with this TCP socket */ +void +TcpSocketBase::SetNode (Ptr node) +{ + m_node = node; +} + +/** Associate the L4 protocol (e.g. mux/demux) with this socket */ +void +TcpSocketBase::SetTcp (Ptr tcp) +{ + m_tcp = tcp; +} + +/** Set an RTT estimator with this socket */ +void +TcpSocketBase::SetRtt (Ptr rtt) +{ + m_rtt = rtt; +} + +/** Inherit from Socket class: Returns error code */ +enum Socket::SocketErrno +TcpSocketBase::GetErrno (void) const +{ + return m_errno; +} + +/** Inherit from Socket class: Returns socket type, NS3_SOCK_STREAM */ +enum Socket::SocketType +TcpSocketBase::GetSocketType (void) const +{ + return NS3_SOCK_STREAM; +} + +/** Inherit from Socket class: Returns associated node */ +Ptr +TcpSocketBase::GetNode (void) const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_node; +} + +/** Inherit from Socket class: Bind socket to an end-point in TcpL4Protocol */ +int +TcpSocketBase::Bind (void) +{ + NS_LOG_FUNCTION_NOARGS (); + m_endPoint = m_tcp->Allocate (); + return SetupCallback (); +} + +/** Inherit from Socket class: Bind socket (with specific address) to an end-point in TcpL4Protocol */ +int +TcpSocketBase::Bind (const Address &address) +{ + NS_LOG_FUNCTION (this << address); + if (!InetSocketAddress::IsMatchingType (address)) + { + m_errno = ERROR_INVAL; + return -1; + } + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + Ipv4Address ipv4 = transport.GetIpv4 (); + uint16_t port = transport.GetPort (); + if (ipv4 == Ipv4Address::GetAny () && port == 0) + { + m_endPoint = m_tcp->Allocate (); + } + else if (ipv4 == Ipv4Address::GetAny () && port != 0) + { + m_endPoint = m_tcp->Allocate (port); + } + else if (ipv4 != Ipv4Address::GetAny () && port == 0) + { + m_endPoint = m_tcp->Allocate (ipv4); + } + else if (ipv4 != Ipv4Address::GetAny () && port != 0) + { + m_endPoint = m_tcp->Allocate (ipv4, port); + } + NS_LOG_LOGIC ("TcpSocketBase " << this << " got an endpoint: " << m_endPoint); + + return SetupCallback (); +} + +/** Inherit from Socket class: Initiate connection to a remote address:port */ +int +TcpSocketBase::Connect (const Address & address) +{ + NS_LOG_FUNCTION (this << address); + + // If haven't do so, Bind() this socket first + if (m_endPoint == 0) + { + if (Bind () == -1) + { + NS_ASSERT (m_endPoint == 0); + return -1; // Bind() failed + } + NS_ASSERT (m_endPoint != 0); + } + + InetSocketAddress transport = InetSocketAddress::ConvertFrom (address); + m_endPoint->SetPeer (transport.GetIpv4 (), transport.GetPort ()); + + // Get the appropriate local address and port number from the routing protocol and set up endpoint + if (SetupEndpoint () != 0) + { // Route to destination does not exist + return -1; + } + + // DoConnect() will do state-checking and send a SYN packet + return DoConnect (); +} + +/** Inherit from Socket class: Listen on the endpoint for an incoming connection */ +int +TcpSocketBase::Listen (void) +{ + NS_LOG_FUNCTION (this); + // Linux quits EINVAL if we're not in CLOSED state, so match what they do + if (m_state != CLOSED) + { + m_errno = ERROR_INVAL; + return -1; + } + // In other cases, set the state to LISTEN and done + NS_LOG_INFO ("CLOSED -> LISTEN"); + m_state = LISTEN; + return 0; +} + +/** Inherit from Socket class: Kill this socket and signal the peer (if any) */ +int +TcpSocketBase::Close (void) +{ + NS_LOG_FUNCTION (this); + // First we check to see if there is any unread rx data + // Bug number 426 claims we should send reset in this case. + if (m_rxBuffer.Size () != 0) + { + SendRST (); + return 0; + } + if (m_txBuffer.SizeFromSequence (m_nextTxSequence) > 0) + { // App close with pending data must wait until all data transmitted + if (m_closeOnEmpty == false) + { + m_closeOnEmpty = true; + NS_LOG_INFO ("Socket " << this << " deferring close, state " << TcpStateName[m_state]); + } + return 0; + } + return DoClose (); +} + +/** Inherit from Socket class: Signal a termination of send */ +int +TcpSocketBase::ShutdownSend (void) +{ + NS_LOG_FUNCTION (this); + m_shutdownSend = true; + return 0; +} + +/** Inherit from Socket class: Signal a termination of receive */ +int +TcpSocketBase::ShutdownRecv (void) +{ + NS_LOG_FUNCTION (this); + m_shutdownRecv = true; + return 0; +} + +/** Inherit from Socket class: Send a packet. Parameter flags is not used. + Packet has no TCP header. Invoked by upper-layer application */ +int +TcpSocketBase::Send (Ptr p, uint32_t flags) +{ + NS_LOG_FUNCTION (this << p); + NS_ABORT_MSG_IF (flags, "use of flags is not supported in TcpSocketBase::Send()"); + if (m_state == ESTABLISHED || m_state == SYN_SENT || m_state == CLOSE_WAIT) + { + // Store the packet into Tx buffer + if (!m_txBuffer.Add (p)) + { // TxBuffer overflow, send failed + m_errno = ERROR_MSGSIZE; + return -1; + } + // Submit the data to lower layers + NS_LOG_LOGIC ("txBufSize=" << m_txBuffer.Size () << " state " << TcpStateName[m_state]); + if (m_state == ESTABLISHED || m_state == CLOSE_WAIT) + { // Try to send the data out + SendPendingData (m_connected); + } + return p->GetSize (); + } + else + { // Connection not established yet + m_errno = ERROR_NOTCONN; + return -1; // Send failure + } +} + +/** Inherit from Socket class: In TcpSocketBase, it is same as Send() call */ +int +TcpSocketBase::SendTo (Ptr p, uint32_t flags, const Address &address) +{ + return Send (p, flags); // SendTo() and Send() are the same +} + +/** Inherit from Socket class: Return data to upper-layer application. Parameter flags + is not used. Data is returned as a packet of size no larger than maxSize */ +Ptr +TcpSocketBase::Recv (uint32_t maxSize, uint32_t flags) +{ + NS_LOG_FUNCTION (this); + NS_ABORT_MSG_IF (flags, "use of flags is not supported in TcpSocketBase::Recv()"); + if (m_rxBuffer.Size () == 0 && m_state == CLOSE_WAIT) + { + return Create (); // Send EOF on connection close + } + Ptr outPacket = m_rxBuffer.Extract (maxSize); + if (outPacket != 0 && outPacket->GetSize () != 0) + { + SocketAddressTag tag; + tag.SetAddress (InetSocketAddress (m_endPoint->GetPeerAddress (), m_endPoint->GetPeerPort ())); + outPacket->AddPacketTag (tag); + } + return outPacket; +} + +/** Inherit from Socket class: Recv and return the remote's address */ +Ptr +TcpSocketBase::RecvFrom (uint32_t maxSize, uint32_t flags, Address &fromAddress) +{ + NS_LOG_FUNCTION (this << maxSize << flags); + Ptr packet = Recv (maxSize, flags); + // Null packet means no data to read, and an empty packet indicates EOF + if (packet != 0 && packet->GetSize () != 0) + { + GetSockName (fromAddress); + } + return packet; +} + +/** Inherit from Socket class: Get the max number of bytes an app can send */ +uint32_t +TcpSocketBase::GetTxAvailable (void) const +{ + NS_LOG_FUNCTION (this); + return m_txBuffer.Available (); +} + +/** Inherit from Socket class: Get the max number of bytes an app can read */ +uint32_t +TcpSocketBase::GetRxAvailable (void) const +{ + NS_LOG_FUNCTION (this); + return m_rxBuffer.Available (); +} + +/** Inherit from Socket class: Return local address:port */ +int +TcpSocketBase::GetSockName (Address &address) const +{ + NS_LOG_FUNCTION (this); + if (m_endPoint != 0) + { + address = InetSocketAddress (m_endPoint->GetLocalAddress (), m_endPoint->GetLocalPort ()); + } + else + { // It is possible to call this method on a socket without a name + // in which case, behavior is unspecified + address = InetSocketAddress (Ipv4Address::GetZero (), 0); + } + return 0; +} + +/** Inherit from Socket class: Bind this socket to the specified NetDevice */ +void +TcpSocketBase::BindToNetDevice (Ptr netdevice) +{ + NS_LOG_FUNCTION (netdevice); + Socket::BindToNetDevice (netdevice); // Includes sanity check + if (m_endPoint == 0) + { + if (Bind () == -1) + { + NS_ASSERT (m_endPoint == 0); + return; + } + NS_ASSERT (m_endPoint != 0); + } + m_endPoint->BindToNetDevice (netdevice); + return; +} + +/** Clean up after Bind. Set up callback functions in the end-point. */ +int +TcpSocketBase::SetupCallback (void) +{ + NS_LOG_FUNCTION (this); + if (m_endPoint == 0) + { + return -1; + } + m_endPoint->SetRxCallback (MakeCallback (&TcpSocketBase::ForwardUp, Ptr (this))); + m_endPoint->SetDestroyCallback (MakeCallback (&TcpSocketBase::Destroy, Ptr (this))); + return 0; +} + +/** Perform the real connection tasks: Send SYN if allowed, RST if invalid */ +int +TcpSocketBase::DoConnect (void) +{ + NS_LOG_FUNCTION (this); + + // A new connection is allowed only if this socket does not have a connection + if (m_state == CLOSED || m_state == LISTEN || m_state == SYN_SENT || m_state == LAST_ACK || m_state == CLOSE_WAIT) + { // send a SYN packet and change state into SYN_SENT + SendEmptyPacket (TcpHeader::SYN); + NS_LOG_INFO (TcpStateName[m_state] << " -> SYN_SENT"); + m_state = SYN_SENT; + } + else if (m_state != TIME_WAIT) + { // In states SYN_RCVD, ESTABLISHED, FIN_WAIT_1, FIN_WAIT_2, and CLOSING, an connection + // exists. We send RST, tear down everything, and close this socket. + CloseAndNotify (); + SendRST (); + } + return 0; +} + +/** Do the action to close the socket. Usually send a packet with appropriate + flags depended on the current m_state. */ +int +TcpSocketBase::DoClose (void) +{ + NS_LOG_FUNCTION (this); + switch (m_state) + { + case SYN_RCVD: + case ESTABLISHED: + // send FIN to close the peer + SendEmptyPacket (TcpHeader::FIN); + NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1"); + m_state = FIN_WAIT_1; + break; + case CLOSE_WAIT: + // send FIN+ACK to close the peer + SendEmptyPacket (TcpHeader::FIN | TcpHeader::ACK); + NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK"); + m_state = LAST_ACK; + break; + case SYN_SENT: + case CLOSING: + // Send RST if application closes in SYN_SENT and CLOSING + CloseAndNotify (); + SendRST (); + break; + case LISTEN: + case LAST_ACK: + // In these three states, move to CLOSED and tear down the end point + CloseAndNotify (); + break; + case CLOSED: + case FIN_WAIT_1: + case FIN_WAIT_2: + case TIME_WAIT: + default: /* mute compiler */ + // Do nothing in these four states + break; + } + return 0; +} + +/** Peacefully close the socket by notifying the upper layer and deallocate end point */ +void +TcpSocketBase::CloseAndNotify (void) +{ + NS_LOG_FUNCTION (this); + + if (!m_closeNotified) NotifyNormalClose (); + m_closeNotified = true; + NS_LOG_INFO (TcpStateName[m_state] << " -> CLOSED"); + m_state = CLOSED; + DeallocateEndPoint (); +} + + +/** Tell if a sequence number is out side the range that my rx buffer can + accpet */ +bool +TcpSocketBase::OutOfRange (SequenceNumber32 s) const +{ + if (m_state == LISTEN || m_state == SYN_SENT || m_state == SYN_RCVD) + { // Rx buffer in these states are not initialized. + return false; + } + if (m_state == LAST_ACK || m_state == CLOSING) + { // In LAST_ACK and CLOSING states, it only wait for an ACK and the + // sequence number must equals to m_rxBuffer.NextRxSequence () + return (m_rxBuffer.NextRxSequence () != s); + }; + + // In all other cases, check if the sequence number is in range + return (m_rxBuffer.MaxRxSequence () < s || m_rxBuffer.NextRxSequence () > s); +} + +/** Function called by the L3 protocol when it received a packet to pass on to + the TCP. This function is registered as the "RxCallback" function in + SetupCallback(), which invoked by Bind(), and CompleteFork() */ +void +TcpSocketBase::ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, + Ptr incomingInterface) +{ + NS_LOG_LOGIC ("Socket " << this << " forward up " << + m_endPoint->GetPeerAddress () << + ":" << m_endPoint->GetPeerPort () << + " to " << m_endPoint->GetLocalAddress () << + ":" << m_endPoint->GetLocalPort ()); + Address fromAddress = InetSocketAddress (header.GetSource (), port); + Address toAddress = InetSocketAddress (header.GetDestination (), m_endPoint->GetLocalPort ()); + + // Peel off TCP header and do validity checking + TcpHeader tcpHeader; + packet->RemoveHeader (tcpHeader); + if (tcpHeader.GetFlags () & TcpHeader::ACK) + { + EstimateRtt (tcpHeader); + } + + // Update Rx window size, i.e. the flow control window + if (m_rWnd.Get () == 0 && tcpHeader.GetWindowSize () != 0) + { // persist probes end + NS_LOG_LOGIC (this << " Leaving zerowindow persist state"); + m_persistEvent.Cancel (); + } + m_rWnd = tcpHeader.GetWindowSize (); + + // Discard out of range packets + if (OutOfRange (tcpHeader.GetSequenceNumber ())) + { + NS_LOG_LOGIC ("At state " << TcpStateName[m_state] << + " received packet of seq " << tcpHeader.GetSequenceNumber () << + " out of range [" << m_rxBuffer.NextRxSequence () << ":" << + m_rxBuffer.MaxRxSequence () << "]"); + return; + } + + // TCP state machine code in different process functions + // C.f.: tcp_rcv_state_process() in tcp_input.c in Linux kernel + switch (m_state) + { + case ESTABLISHED: + ProcessEstablished (packet, tcpHeader); + break; + case LISTEN: + ProcessListen (packet, tcpHeader, fromAddress, toAddress); + break; + case TIME_WAIT: + // Do nothing + break; + case CLOSED: + // Send RST if the incoming packet is not a RST + if ((tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG)) != TcpHeader::RST) + { + SendRST (); + } + break; + case SYN_SENT: + ProcessSynSent (packet, tcpHeader); + break; + case SYN_RCVD: + ProcessSynRcvd (packet, tcpHeader, fromAddress, toAddress); + break; + case FIN_WAIT_1: + case FIN_WAIT_2: + case CLOSE_WAIT: + ProcessWait (packet, tcpHeader); + break; + case CLOSING: + ProcessClosing (packet, tcpHeader); + break; + case LAST_ACK: + ProcessLastAck (packet, tcpHeader); + break; + default: // mute compiler + break; + } +} + +/** Received a packet upon ESTABLISHED state. This function is mimicking the + role of tcp_rcv_established() in tcp_input.c in Linux kernel. */ +void +TcpSocketBase::ProcessEstablished (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + // Different flags are different events + if (tcpflags == TcpHeader::ACK) + { + ReceivedAck (packet, tcpHeader); + } + else if (tcpflags == TcpHeader::SYN) + { // Received SYN, old NS-3 behaviour is to set state to SYN_RCVD and + // respond with a SYN+ACK. But it is not a legal state transition as of + // RFC793. Thus this is ignored. + } + else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK)) + { // No action for received SYN+ACK, it is probably a duplicated packet + } + else if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) + { // Received FIN or FIN+ACK, bring down this socket nicely + PeerClose (packet, tcpHeader); + } + else if (tcpflags == 0) + { // No flags means there is only data + ReceivedData (packet, tcpHeader); + if (m_rxBuffer.Finished ()) + { + PeerClose (packet, tcpHeader); + } + } + else + { // Received RST or the TCP flags is invalid, in either case, terminate this socket + CloseAndNotify (); + if (tcpflags != TcpHeader::RST) + { // this must be an invalid flag, send reset + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } + } +} + +/** Process the newly received ACK */ +void +TcpSocketBase::ReceivedAck (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Received ACK. Compare the ACK number against highest unacked seqno + if (0 == (tcpHeader.GetFlags () & TcpHeader::ACK)) + { // Ignore if no ACK flag + } + else if (tcpHeader.GetAckNumber () < m_txBuffer.HeadSequence ()) + { // Case 1: Old ACK, ignored. + NS_LOG_LOGIC ("Ignored ack of " << tcpHeader.GetAckNumber ()); + } + else if (tcpHeader.GetAckNumber () == m_txBuffer.HeadSequence ()) + { // Case 2: Potentially a duplicated ACK + if (tcpHeader.GetAckNumber () < m_nextTxSequence) + { + NS_LOG_LOGIC ("Dupack of " << tcpHeader.GetAckNumber ()); + DupAck (tcpHeader, ++m_dupAckCount); + } + // otherwise, the ACK is precisely equal to the nextTxSequence + NS_ASSERT (tcpHeader.GetAckNumber () <= m_nextTxSequence); + } + else if (tcpHeader.GetAckNumber () > m_txBuffer.HeadSequence ()) + { // Case 3: New ACK, reset m_dupAckCount and update m_txBuffer + NS_LOG_LOGIC ("New ack of " << tcpHeader.GetAckNumber ()); + NewAck (tcpHeader.GetAckNumber ()); + m_dupAckCount = 0; + } + // If there is any data piggybacked, store it into m_rxBuffer + if (packet->GetSize () > 0) + { + ReceivedData (packet, tcpHeader); + } +} + +/** Received a packet upon LISTEN state. */ +void +TcpSocketBase::ProcessListen (Ptr packet, const TcpHeader& tcpHeader, + const Address& fromAddress, const Address& toAddress) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + // Fork a socket if received a SYN. Do nothing otherwise. + // C.f.: the LISTEN part in tcp_v4_do_rcv() in tcp_ipv4.c in Linux kernel + if (tcpflags != TcpHeader::SYN) return; + + // Call socket's notify function to let the server app know we got a SYN + // If the server app refuses the connection, do nothing + if (!NotifyConnectionRequest (fromAddress)) return; + // Clone the socket, simulate fork + Ptr newSock = Fork (); + NS_LOG_LOGIC ("Cloned a TcpSocketBase " << newSock); + Simulator::ScheduleNow (&TcpSocketBase::CompleteFork, newSock, + packet, tcpHeader, fromAddress, toAddress); +} + +/** Received a packet upon SYN_SENT */ +void +TcpSocketBase::ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + if (tcpflags == 0) + { // Bare data, accept it and move to ESTABLISHED state. This is not a normal behaviour. Remove this? + NS_LOG_INFO ("SYN_SENT -> ESTABLISHED"); + m_state = ESTABLISHED; + m_connected = true; + m_retxEvent.Cancel (); + ReceivedData (packet, tcpHeader); + Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this); + } + else if (tcpflags == TcpHeader::ACK) + { // Ignore ACK in SYN_SENT + } + else if (tcpflags == TcpHeader::SYN) + { // Received SYN, move to SYN_RCVD state and respond with SYN+ACK + NS_LOG_INFO ("SYN_SENT -> SYN_RCVD"); + m_state = SYN_RCVD; + m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); + SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); + } + else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK) + && m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ()) + { // Handshake completed + NS_LOG_INFO ("SYN_SENT -> ESTABLISHED"); + m_state = ESTABLISHED; + m_connected = true; + m_retxEvent.Cancel (); + m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); + m_highTxMark = ++m_nextTxSequence; + m_txBuffer.SetHeadSequence (m_nextTxSequence); + SendEmptyPacket (TcpHeader::ACK); + if (GetTxAvailable () > 0) + { + NotifySend (GetTxAvailable ()); + } + SendPendingData (m_connected); + Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this); + } + else + { // Other in-sequence input + CloseAndNotify (); + if (tcpflags != TcpHeader::RST) + { // When (1) rx of FIN+ACK; (2) rx of FIN; (3) rx of bad flags + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } + } +} + +/** Received a packet upon SYN_RCVD */ +void +TcpSocketBase::ProcessSynRcvd (Ptr packet, const TcpHeader& tcpHeader, + const Address& fromAddress, const Address& toAddress) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + if (tcpflags == 0 || + (tcpflags == TcpHeader::ACK + && m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ())) + { // If it is bare data, accept it and move to ESTABLISHED state. This is + // possibly due to ACK lost in 3WHS. If in-sequence ACK is received, the + // handshake is completed nicely. + NS_LOG_INFO ("SYN_RCVD -> ESTABLISHED"); + m_state = ESTABLISHED; + m_connected = true; + m_retxEvent.Cancel (); + m_highTxMark = ++m_nextTxSequence; + m_txBuffer.SetHeadSequence (m_nextTxSequence); + m_endPoint->SetPeer (m_endPoint->GetPeerAddress (), m_endPoint->GetPeerPort ()); + // Always respond to first data packet to speed up the connection. + // Remove to get the behaviour of old NS-3 code. + m_delAckCount = m_delAckMaxCount; + ReceivedAck (packet, tcpHeader); + NotifyNewConnectionCreated (this, fromAddress); + } + else if (tcpflags == TcpHeader::SYN) + { // Probably the peer lost my SYN+ACK + m_rxBuffer.SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); + SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); + } + else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) + { + if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ()) + { // In-sequence FIN before connection complete. Set up connection and close. + m_connected = true; + m_retxEvent.Cancel (); + m_highTxMark = ++m_nextTxSequence; + m_txBuffer.SetHeadSequence (m_nextTxSequence); + m_endPoint->SetPeer (m_endPoint->GetPeerAddress (), m_endPoint->GetPeerPort ()); + PeerClose (packet, tcpHeader); + } + } + else + { // Other in-sequence input + CloseAndNotify (); + if (tcpflags != TcpHeader::RST) + { // When (1) rx of SYN+ACK; (2) rx of FIN; (3) rx of bad flags + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } + } +} + +/** Received a packet upon CLOSE_WAIT, FIN_WAIT_1, or FIN_WAIT_2 states */ +void +TcpSocketBase::ProcessWait (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + if (packet->GetSize () > 0) + { // Bare data, accept it + ReceivedData (packet, tcpHeader); + } + else if (tcpflags == TcpHeader::ACK) + { // Process the ACK, and if in FIN_WAIT_1, conditionally move to FIN_WAIT_2 + ReceivedAck (packet, tcpHeader); + if (m_state == FIN_WAIT_1 && m_txBuffer.Size () == 0 && + tcpHeader.GetAckNumber () == m_highTxMark + SequenceNumber32 (1)) + { // This ACK corresponds to the FIN sent + NS_LOG_INFO ("FIN_WAIT_1 -> FIN_WAIT_2"); + m_state = FIN_WAIT_2; + } + } + else if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) + { // Got FIN, respond with ACK and move to next state + if (tcpflags & TcpHeader::ACK) + { // Process the ACK first + ReceivedAck (packet, tcpHeader); + } + m_rxBuffer.SetFinSequence (tcpHeader.GetSequenceNumber ()); + } + else if (tcpflags == TcpHeader::SYN || tcpflags == (TcpHeader::SYN | TcpHeader::ACK)) + { // Duplicated SYN or SYN+ACK, possibly due to spurious retransmission + return; + } + else + { // This is a RST or bad flags + CloseAndNotify (); + if (tcpflags != TcpHeader::RST) + { + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } + return; + } + + // Check if the close responder sent an in-sequence FIN, if so, respond ACK + if ((m_state == FIN_WAIT_1 || m_state == FIN_WAIT_2) && m_rxBuffer.Finished ()) + { + if (m_state == FIN_WAIT_1) + { + NS_LOG_INFO ("FIN_WAIT_1 -> CLOSING"); + m_state = CLOSING; + if (m_txBuffer.Size () == 0 && + tcpHeader.GetAckNumber () == m_highTxMark + SequenceNumber32 (1)) + { // This ACK corresponds to the FIN sent + NS_LOG_INFO ("CLOSING -> TIME_WAIT"); + m_state = TIME_WAIT; + CancelAllTimers (); + } + } + else if (m_state == FIN_WAIT_2) + { + NS_LOG_INFO ("FIN_WAIT_2 -> TIME_WAIT"); + m_state = TIME_WAIT; + CancelAllTimers (); + // TODO: In TIME_WAIT, we supposed to move to CLOSED after 2*MSL time + // but this move is not yet implemeneted. Is this necessary? + }; + SendEmptyPacket (TcpHeader::ACK); + if (!m_shutdownRecv) NotifyDataRecv (); + } +} + +/** Received a packet upon CLOSING */ +void +TcpSocketBase::ProcessClosing (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + if (tcpflags == TcpHeader::ACK) + { + if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ()) + { // This ACK corresponds to the FIN sent + NS_LOG_INFO ("CLOSING -> TIME_WAIT"); + m_state = TIME_WAIT; + CancelAllTimers (); + // TODO: In TIME_WAIT, we supposed to move to CLOSED after 2*MSL time + // but this move is not yet implemeneted. Is this necessary? + } + } + else + { // CLOSING state means simultaneous close, i.e. no one is sending data to + // anyone. If anything other than ACK is received, respond with a reset. + CloseAndNotify (); + if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) + { // FIN from the peer as well. We can close immediately. + SendEmptyPacket (TcpHeader::ACK); + } + else if (tcpflags != TcpHeader::RST) + { // Receive of SYN or SYN+ACK or bad flags or pure data + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } + } +} + +/** Received a packet upon LAST_ACK */ +void +TcpSocketBase::ProcessLastAck (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); + + if (tcpflags == 0) + { + ReceivedData (packet, tcpHeader); + } + else if (tcpflags == TcpHeader::ACK) + { + if (tcpHeader.GetSequenceNumber () == m_rxBuffer.NextRxSequence ()) + { // This ACK corresponds to the FIN sent. This socket closed peacefully. + CloseAndNotify (); + } + } + else if (tcpflags == TcpHeader::FIN) + { // Received FIN again, the peer probably lost the FIN+ACK + SendEmptyPacket (TcpHeader::FIN | TcpHeader::ACK); + } + else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK) || tcpflags == TcpHeader::RST) + { + CloseAndNotify (); + } + else + { // Received a SYN or SYN+ACK or bad flags + CloseAndNotify (); + NS_LOG_LOGIC ("Illegal flag " << tcpflags << " received. Reset packet is sent."); + SendRST (); + } +} + +/** Peer sent me a FIN. Remember its sequence in rx buffer. */ +void +TcpSocketBase::PeerClose (Ptr p, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + // Ignore all out of range packets + if (tcpHeader.GetSequenceNumber () < m_rxBuffer.NextRxSequence () || + tcpHeader.GetSequenceNumber () > m_rxBuffer.MaxRxSequence ()) + { + return; + }; + // For any case, remember the FIN position in rx buffer first + m_rxBuffer.SetFinSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (p->GetSize ())); + NS_LOG_LOGIC ("Accepted FIN at seq " << tcpHeader.GetSequenceNumber () + SequenceNumber32 (p->GetSize ())); + // If there is any piggybacked data, process it + if (p->GetSize ()) + { + ReceivedData (p, tcpHeader); + } + // Return if FIN is out of sequence, otherwise move to CLOSE_WAIT state by DoPeerClose + if (! m_rxBuffer.Finished ()) + { + return; + }; + + // Simultaneous close: Application invoked Close() when we are processing this FIN packet + if (m_state == FIN_WAIT_1) + { + NS_LOG_INFO ("FIN_WAIT_1 -> CLOSING"); + m_state = CLOSING; + return; + } + + DoPeerClose (); // Change state, respond with ACK +} + +/** Received a in-sequence FIN. Close down this socket. */ +void +TcpSocketBase::DoPeerClose (void) +{ + NS_ASSERT (m_state == ESTABLISHED || m_state == SYN_RCVD); + + // Move the state to CLOSE_WAIT + NS_LOG_INFO (TcpStateName[m_state] << " -> CLOSE_WAIT"); + m_state = CLOSE_WAIT; + + if (!m_closeNotified) + { + // The normal behaviour for an application is that, when the peer sent a in-sequence + // FIN, the app should prepare to close. The app has two choices at this point: either + // respond with ShutdownSend() call to declare that it has nothing more to send and + // the socket can be closed immediately; or remember the peer's close request, wait + // until all its existing data are pushed into the TCP socket, then call Close() + // explicitly. + NS_LOG_LOGIC ("TCP " << this << " calling NotifyNormalClose"); + if (!m_closeNotified) NotifyNormalClose (); + m_closeNotified = true; + } + if (m_shutdownSend) + { // The application declares that it would not sent any more, close this socket + Close(); + } + else + { // Need to ack, the application will close later + SendEmptyPacket (TcpHeader::ACK); + } + if (m_state == LAST_ACK) + { + NS_LOG_LOGIC ("TcpSocketBase " << this << " scheduling LATO1"); + m_lastAckEvent = Simulator::Schedule (m_rtt->RetransmitTimeout (), + &TcpSocketBase::LastAckTimeout, this); + } +} + +/** Kill this socket. This is a callback function configured to m_endpoint in + SetupCallback(), invoked when the endpoint is destroyed. */ +void +TcpSocketBase::Destroy (void) +{ + NS_LOG_FUNCTION (this); + m_node = 0; + m_endPoint = 0; + m_tcp = 0; + NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " << + (Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ()); + CancelAllTimers (); +} + +/** Send an empty packet with specified TCP flags */ +void +TcpSocketBase::SendEmptyPacket (uint8_t flags) +{ + NS_LOG_FUNCTION (this << (uint32_t)flags); + Ptr p = Create (); + TcpHeader header; + SequenceNumber32 s = m_nextTxSequence; + + if (m_endPoint == 0) + { + NS_LOG_WARN ("Failed to send empty packet due to null endpoint"); + return; + } + if (flags & TcpHeader::FIN) + { + flags |= TcpHeader::ACK; + } + else if (m_state == FIN_WAIT_1 || m_state == LAST_ACK || m_state == CLOSING) + { + ++s; + } + + header.SetFlags (flags); + header.SetSequenceNumber (s); + header.SetAckNumber (m_rxBuffer.NextRxSequence ()); + header.SetSourcePort (m_endPoint->GetLocalPort ()); + header.SetDestinationPort (m_endPoint->GetPeerPort ()); + header.SetWindowSize (AdvertisedWindowSize ()); + m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_endPoint->GetPeerAddress (), m_boundnetdevice); + m_rto = m_rtt->RetransmitTimeout (); + bool hasSyn = flags & TcpHeader::SYN; + bool hasFin = flags & TcpHeader::FIN; + bool isAck = flags == TcpHeader::ACK; + if (hasSyn) + { // Exponential backoff of connection time out + m_rto = m_cnTimeout; + m_cnTimeout = m_cnTimeout + m_cnTimeout; + m_cnCount--; + } + if (flags & TcpHeader::ACK) + { // If sending an ACK, cancel the delay ACK as well + m_delAckEvent.Cancel (); + m_delAckCount = 0; + } + if (m_retxEvent.IsExpired () && (hasSyn || hasFin) && !isAck ) + { // Retransmit SYN / SYN+ACK / FIN / FIN+ACK to guard against lost + NS_LOG_LOGIC ("Schedule retransmission timeout at time " + << Simulator::Now ().GetSeconds () << " to expire at time " + << (Simulator::Now () + m_rto.Get ()).GetSeconds ()); + m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::SendEmptyPacket, this, flags); + } +} + +/** This function closes the endpoint completely. Called upon RST_TX action. */ +void +TcpSocketBase::SendRST (void) +{ + NS_LOG_FUNCTION (this); + SendEmptyPacket (TcpHeader::RST); + NotifyErrorClose (); + DeallocateEndPoint (); +} + +/** Deallocate the end point the cancel all the timers */ +void +TcpSocketBase::DeallocateEndPoint (void) +{ + if (m_endPoint != 0) + { + m_endPoint->SetDestroyCallback (MakeNullCallback ()); + m_tcp->DeAllocate (m_endPoint); + m_endPoint = 0; + CancelAllTimers (); + } +} + +/** Configure the endpoint to a local address. Called by Connect() if Bind() didn't specify one. */ +int +TcpSocketBase::SetupEndpoint () +{ + NS_LOG_FUNCTION (this); + Ptr ipv4 = m_node->GetObject (); + NS_ASSERT (ipv4 != 0); + if (ipv4->GetRoutingProtocol () == 0) + { + NS_FATAL_ERROR ("No Ipv4RoutingProtocol in the node"); + } + // Create a dummy packet, then ask the routing function for the best output + // interface's address + Ipv4Header header; + header.SetDestination (m_endPoint->GetPeerAddress ()); + Socket::SocketErrno errno_; + Ptr route; + Ptr oif = m_boundnetdevice; + route = ipv4->GetRoutingProtocol ()->RouteOutput (Ptr (), header, oif, errno_); + if (route == 0) + { + NS_LOG_LOGIC ("Route to " << m_endPoint->GetPeerAddress () << " does not exist"); + NS_LOG_ERROR (errno_); + m_errno = errno_; + return -1; + } + NS_LOG_LOGIC ("Route exists"); + m_endPoint->SetLocalAddress (route->GetSource ()); + return 0; +} + +/** This function is called only if a SYN received in LISTEN state. After + TcpSocketBase cloned, allocate a new end point to handle the incoming + connection and send a SYN+ACK to complete the handshake. */ +void +TcpSocketBase::CompleteFork (Ptr p, const TcpHeader& h, + const Address& fromAddress, const Address& toAddress) +{ + // Get port and address from peer (connecting host) + m_endPoint = m_tcp->Allocate (InetSocketAddress::ConvertFrom (toAddress).GetIpv4 (), + InetSocketAddress::ConvertFrom (toAddress).GetPort (), + InetSocketAddress::ConvertFrom (fromAddress).GetIpv4 (), + InetSocketAddress::ConvertFrom (fromAddress).GetPort ()); + + // Change the cloned socket from LISTEN state to SYN_RCVD + NS_LOG_INFO ("LISTEN -> SYN_RCVD"); + m_state = SYN_RCVD; + SetupCallback (); + // Set the sequence number and send SYN+ACK + m_rxBuffer.SetNextRxSequence (h.GetSequenceNumber () + SequenceNumber32 (1)); + SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); +} + +void +TcpSocketBase::ConnectionSucceeded () +{ // Wrapper to protected function NotifyConnectionSucceeded() so that it can + // be called as a scheduled event + NotifyConnectionSucceeded (); +} + +// Send as much pending data as possible according to the Tx window. Note that +// this function did not implement the PSH flag +bool +TcpSocketBase::SendPendingData (bool withAck) +{ + NS_LOG_FUNCTION (this << withAck); + if (m_txBuffer.Size () == 0) return false; // Nothing to send + if (m_endPoint == 0) + { + NS_LOG_INFO ("TcpSocketImpl::SendPendingData: No endpoint; m_shutdownSend=" << m_shutdownSend); + return false; // Is this the right way to handle this condition? + } + uint32_t nPacketsSent = 0; + while (m_txBuffer.SizeFromSequence (m_nextTxSequence)) + { + uint32_t w = AvailableWindow (); // Get available window size + NS_LOG_LOGIC ("TcpSocketBase " << this << " SendPendingData" << + " w " << w << + " rxwin " << m_rWnd << + " segsize " << m_segmentSize << + " nextTxSeq " << m_nextTxSequence << + " highestRxAck " << m_txBuffer.HeadSequence () << + " pd->Size " << m_txBuffer.Size () << + " pd->SFS " << m_txBuffer.SizeFromSequence (m_nextTxSequence)); + // Quit if send disallowed + if (m_shutdownSend) + { + m_errno = ERROR_SHUTDOWN; + return false; + } + // Stop sending if we need to wait for a larger Tx window + if (w < m_segmentSize && m_txBuffer.SizeFromSequence (m_nextTxSequence) > w) + { + break; // No more + } + uint32_t s = std::min (w, m_segmentSize); // Send no more than window + Ptr p = m_txBuffer.CopyFromSequence (s, m_nextTxSequence); + NS_LOG_LOGIC ("TcpSocketBase " << this << " SendPendingData" << + " txseq " << m_nextTxSequence << + " s " << s << " datasize " << p->GetSize ()); + uint8_t flags = 0; + uint32_t sz = p->GetSize (); // Size of packet + uint32_t remainingData = m_txBuffer.SizeFromSequence (m_nextTxSequence + SequenceNumber32 (sz)); + if (m_closeOnEmpty && (remainingData == 0)) + { + flags = TcpHeader::FIN; + if (m_state == ESTABLISHED) + { // On active close: I am the first one to send FIN + NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1"); + m_state = FIN_WAIT_1; + } + else + { // On passive close: Peer sent me FIN already + NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK"); + m_state = LAST_ACK; + } + } + if (withAck) + { + flags |= TcpHeader::ACK; + } + TcpHeader header; + header.SetFlags (flags); + header.SetSequenceNumber (m_nextTxSequence); + header.SetAckNumber (m_rxBuffer.NextRxSequence ()); + header.SetSourcePort (m_endPoint->GetLocalPort ()); + header.SetDestinationPort (m_endPoint->GetPeerPort ()); + header.SetWindowSize (AdvertisedWindowSize ()); + if (m_retxEvent.IsExpired () ) + { // Schedule retransmit + m_rto = m_rtt->RetransmitTimeout (); + NS_LOG_LOGIC (this << " SendPendingData Schedule ReTxTimeout at time " << + Simulator::Now ().GetSeconds () << " to expire at time " << + (Simulator::Now () + m_rto.Get ()).GetSeconds () ); + m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::ReTxTimeout, this); + } + NS_LOG_LOGIC ("Send packet via TcpL4Protocol with flags 0x" << std::hex << static_cast (flags) << std::dec); + m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), + m_endPoint->GetPeerAddress (), m_boundnetdevice); + m_rtt->SentSeq (m_nextTxSequence, sz); // notify the RTT + // Notify the application of the data being sent + Simulator::ScheduleNow (&TcpSocketBase::NotifyDataSent, this, sz); + nPacketsSent++; // Count sent this loop + m_nextTxSequence += sz; // Advance next tx sequence + // Update highTxMark + m_highTxMark = std::max (m_nextTxSequence, m_highTxMark); + } + NS_LOG_LOGIC ("SendPendingData sent " << nPacketsSent << " packets"); + return (nPacketsSent > 0); +} + +uint32_t +TcpSocketBase::UnAckDataCount () +{ + NS_LOG_FUNCTION (this); + return m_nextTxSequence.Get () - m_txBuffer.HeadSequence (); +} + +uint32_t +TcpSocketBase::BytesInFlight () +{ + NS_LOG_FUNCTION (this); + return m_highTxMark.Get () - m_txBuffer.HeadSequence (); +} + +uint32_t +TcpSocketBase::Window () +{ + NS_LOG_FUNCTION (this); + return m_rWnd; +} + +uint32_t +TcpSocketBase::AvailableWindow () +{ + NS_LOG_FUNCTION_NOARGS (); + uint32_t unack = UnAckDataCount (); // Number of outstanding bytes + uint32_t win = Window (); // Number of bytes allowed to be outstanding + NS_LOG_LOGIC ("UnAckCount=" << unack << ", Win=" << win); + return (win < unack) ? 0 : (win - unack); +} + +uint16_t +TcpSocketBase::AdvertisedWindowSize () +{ + uint32_t max = 0xffff; + return std::min (m_rxBuffer.MaxBufferSize () - m_rxBuffer.Size (), max); +} + +// Receipt of new packet, put into Rx buffer +void +TcpSocketBase::ReceivedData (Ptr p, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + NS_LOG_LOGIC ("seq " << tcpHeader.GetSequenceNumber () << + " ack " << tcpHeader.GetAckNumber () << + " pkt size " << p->GetSize () ); + + // Put into Rx buffer + SequenceNumber32 expectedSeq = m_rxBuffer.NextRxSequence (); + if (!m_rxBuffer.Add (p, tcpHeader)) + { // Insert failed: No data or RX buffer full + SendEmptyPacket (TcpHeader::ACK); + return; + } + // Now send a new ACK packet acknowledging all received and delivered data + if (tcpHeader.GetSequenceNumber () > expectedSeq) + { // Out of sequence packet: Always ACK + SendEmptyPacket (TcpHeader::ACK); + } + else + { // In-sequence packet: ACK if delayed ack count allows + if (++m_delAckCount >= m_delAckMaxCount) + { + m_delAckEvent.Cancel (); + m_delAckCount = 0; + SendEmptyPacket (TcpHeader::ACK); + } + else if (m_delAckEvent.IsExpired ()) + { + m_delAckEvent = Simulator::Schedule (m_delAckTimeout, + &TcpSocketBase::DelAckTimeout, this); + } + } + // Notify app to receive if necessary + if (expectedSeq < m_rxBuffer.NextRxSequence ()) + { // NextRxSeq advanced, we have something to send to the app + if (!m_shutdownRecv) NotifyDataRecv (); + // Handle exceptions + if (m_closeNotified) + { + NS_LOG_WARN ("Why TCP " << this << " got data after close notification?"); + } + // If we received FIN before and now completed all "holes" in rx buffer, + // invoke peer close procedure + if (m_rxBuffer.Finished () && (tcpHeader.GetFlags() & TcpHeader::FIN) == 0) + { + DoPeerClose (); + } + } +} + +/** Called by ForwardUp() to estimate RTT */ +void +TcpSocketBase::EstimateRtt (const TcpHeader& tcpHeader) +{ + // Use m_rtt for the estimation. Note, RTT of duplicated acknowledgement + // (which should be ignored) is handled by m_rtt. Once timestamp option + // is implemented, this function would be more elaborated. + m_rtt->AckSeq (tcpHeader.GetAckNumber () ); +}; + +// Called by the ReceivedAck() when new ACK received and by ProcessSynRcvd() +// when the three-way handshake completed. This cancels retransmission timer +// and advances Tx window +void +TcpSocketBase::NewAck (SequenceNumber32 const& ack) +{ + NS_LOG_FUNCTION (this << ack); + + if (m_state != SYN_RCVD) + { // Set RTO unless the ACK is received in SYN_RCVD state + NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " << + (Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ()); + m_retxEvent.Cancel (); + // On recieving a "New" ack we restart retransmission timer .. RFC 2988 + m_rto = m_rtt->RetransmitTimeout (); + NS_LOG_LOGIC (this << " Schedule ReTxTimeout at time " << + Simulator::Now ().GetSeconds () << " to expire at time " << + (Simulator::Now () + m_rto.Get ()).GetSeconds ()); + m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::ReTxTimeout, this); + } + if (m_rWnd.Get () == 0 && m_persistEvent.IsExpired ()) + { // Zero window: Enter persist state to send 1 byte to probe + NS_LOG_LOGIC (this << "Enter zerowindow persist state"); + NS_LOG_LOGIC (this << "Cancelled ReTxTimeout event which was set to expire at " << + (Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ()); + m_retxEvent.Cancel (); + NS_LOG_LOGIC ("Schedule persist timeout at time " << + Simulator::Now ().GetSeconds () << " to expire at time " << + (Simulator::Now () + m_persistTimeout).GetSeconds ()); + m_persistEvent = Simulator::Schedule (m_persistTimeout, &TcpSocketBase::PersistTimeout, this); + NS_ASSERT (m_persistTimeout == Simulator::GetDelayLeft (m_persistEvent)); + } + // Note the highest ACK and tell app to send more + NS_LOG_LOGIC ("TCP " << this << " NewAck " << ack << + " numberAck " << (ack - m_txBuffer.HeadSequence ())); // Number bytes ack'ed + m_txBuffer.DiscardUpTo (ack); + if (GetTxAvailable () > 0) + { + NotifySend (GetTxAvailable ()); + } + if (ack > m_nextTxSequence) + { + m_nextTxSequence = ack; // If advanced + } + if (m_txBuffer.Size () == 0 && m_state != FIN_WAIT_1 && m_state != CLOSING) + { // No retransmit timer if no data to retransmit + NS_LOG_LOGIC (this << " Cancelled ReTxTimeout event which was set to expire at " << + (Simulator::Now () + Simulator::GetDelayLeft (m_retxEvent)).GetSeconds ()); + m_retxEvent.Cancel (); + } + // Try to send more data + SendPendingData (m_connected); +} + +// Retransmit timeout +void +TcpSocketBase::ReTxTimeout () +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC (this << " ReTxTimeout Expired at time " << Simulator::Now ().GetSeconds ()); + // If erroneous timeout in closed/timed-wait state, just return + if (m_state == CLOSED || m_state == TIME_WAIT) return; + // If all data are received, just return + if (m_state <= ESTABLISHED && m_txBuffer.HeadSequence () >= m_nextTxSequence) return; + + Retransmit (); +} + +void +TcpSocketBase::DelAckTimeout (void) +{ + m_delAckCount = 0; + SendEmptyPacket (TcpHeader::ACK); +} + +void +TcpSocketBase::LastAckTimeout (void) +{ + NS_LOG_FUNCTION (this); + + m_lastAckEvent.Cancel (); + if (m_state == LAST_ACK) + { + CloseAndNotify (); + } + if (!m_closeNotified) + { + m_closeNotified = true; + } +} + +// Send 1-byte data to probe for the window size at the receiver when +// the local knowledge tells that the receiver has zero window size +// C.f.: RFC793 p.42, RFC1112 sec.4.2.2.17 +void +TcpSocketBase::PersistTimeout () +{ + NS_LOG_LOGIC ("PersistTimeout expired at " << Simulator::Now ().GetSeconds ()); + m_persistTimeout = std::min (Seconds (60), Scalar (2) * m_persistTimeout); // max persist timeout = 60s + Ptr p = m_txBuffer.CopyFromSequence (1, m_nextTxSequence); + TcpHeader tcpHeader; + tcpHeader.SetSequenceNumber (m_nextTxSequence); + tcpHeader.SetAckNumber (m_rxBuffer.NextRxSequence ()); + tcpHeader.SetSourcePort (m_endPoint->GetLocalPort ()); + tcpHeader.SetDestinationPort (m_endPoint->GetPeerPort ()); + tcpHeader.SetWindowSize (AdvertisedWindowSize ()); + + m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (), + m_endPoint->GetPeerAddress (), m_boundnetdevice); + NS_LOG_LOGIC ("Schedule persist timeout at time " + << Simulator::Now ().GetSeconds () << " to expire at time " + << (Simulator::Now () + m_persistTimeout).GetSeconds ()); + m_persistEvent = Simulator::Schedule (m_persistTimeout, &TcpSocketBase::PersistTimeout, this); +} + +void +TcpSocketBase::Retransmit () +{ + m_nextTxSequence = m_txBuffer.HeadSequence (); // Start from highest Ack + m_rtt->IncreaseMultiplier (); // Double the timeout value for next retx timer + m_dupAckCount = 0; + DoRetransmit (); // Retransmit the packet +} + +void +TcpSocketBase::DoRetransmit () +{ + NS_LOG_FUNCTION (this); + uint8_t flags = TcpHeader::ACK; + // Retransmit SYN packet + if (m_state == SYN_SENT) + { + if (m_cnCount > 0) + { + SendEmptyPacket (TcpHeader::SYN); + } + else + { + NotifyConnectionFailed (); + } + return; + } + // Retransmit non-data packet: Only if in FIN_WAIT_1 or CLOSING state + if (m_txBuffer.Size () == 0) + { + if (m_state == FIN_WAIT_1 || m_state == CLOSING) + { // Must have lost FIN, re-send + SendEmptyPacket (TcpHeader::FIN); + } + return; + } + // Retransmit a data packet: Extract data + Ptr p = m_txBuffer.CopyFromSequence (m_segmentSize, m_txBuffer.HeadSequence ()); + // Close-on-Empty check + if (m_closeOnEmpty && m_txBuffer.Size () == p->GetSize ()) + { + flags |= TcpHeader::FIN; + } + // Reset transmission timeout + NS_LOG_LOGIC ("TcpSocketBase " << this << " retxing seq " << m_txBuffer.HeadSequence ()); + if (m_retxEvent.IsExpired ()) + { + m_rto = m_rtt->RetransmitTimeout (); + NS_LOG_LOGIC (this << " Schedule ReTxTimeout at time " << + Simulator::Now ().GetSeconds () << " to expire at time " << + (Simulator::Now () + m_rto.Get ()).GetSeconds ()); + m_retxEvent = Simulator::Schedule (m_rto, &TcpSocketBase::ReTxTimeout, this); + } + m_rtt->SentSeq (m_txBuffer.HeadSequence (), p->GetSize ()); + // And send the packet + TcpHeader tcpHeader; + tcpHeader.SetSequenceNumber (m_txBuffer.HeadSequence ()); + tcpHeader.SetAckNumber (m_rxBuffer.NextRxSequence ()); + tcpHeader.SetSourcePort (m_endPoint->GetLocalPort ()); + tcpHeader.SetDestinationPort (m_endPoint->GetPeerPort ()); + tcpHeader.SetFlags (flags); + tcpHeader.SetWindowSize (AdvertisedWindowSize ()); + + m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (), + m_endPoint->GetPeerAddress (), m_boundnetdevice); +} + +void +TcpSocketBase::CancelAllTimers () +{ + m_retxEvent.Cancel (); + m_persistEvent.Cancel (); + m_delAckEvent.Cancel (); + m_lastAckEvent.Cancel (); +} + +/** Below are the attribute get/set functions */ + +void +TcpSocketBase::SetSndBufSize (uint32_t size) +{ + m_txBuffer.SetMaxBufferSize (size); +} + +uint32_t +TcpSocketBase::GetSndBufSize (void) const +{ + return m_txBuffer.MaxBufferSize (); +} + +void +TcpSocketBase::SetRcvBufSize (uint32_t size) +{ + m_rxBuffer.SetMaxBufferSize (size); +} + +uint32_t +TcpSocketBase::GetRcvBufSize (void) const +{ + return m_rxBuffer.MaxBufferSize (); +} + +void +TcpSocketBase::SetSegSize (uint32_t size) +{ + m_segmentSize = size; + NS_ABORT_MSG_UNLESS (m_state == CLOSED, "Cannot change segment size dynamically."); +} + +uint32_t +TcpSocketBase::GetSegSize (void) const +{ + return m_segmentSize; +} + +void +TcpSocketBase::SetConnTimeout (Time timeout) +{ + m_cnTimeout = timeout; +} + +Time +TcpSocketBase::GetConnTimeout (void) const +{ + return m_cnTimeout; +} + +void +TcpSocketBase::SetConnCount (uint32_t count) +{ + m_cnCount = count; +} + +uint32_t +TcpSocketBase::GetConnCount (void) const +{ + return m_cnCount; +} + +void +TcpSocketBase::SetDelAckTimeout (Time timeout) +{ + m_delAckTimeout = timeout; +} + +Time +TcpSocketBase::GetDelAckTimeout (void) const +{ + return m_delAckTimeout; +} + +void +TcpSocketBase::SetDelAckMaxCount (uint32_t count) +{ + m_delAckMaxCount = count; +} + +uint32_t +TcpSocketBase::GetDelAckMaxCount (void) const +{ + return m_delAckMaxCount; +} + +void +TcpSocketBase::SetPersistTimeout (Time timeout) +{ + m_persistTimeout = timeout; +} + +Time +TcpSocketBase::GetPersistTimeout (void) const +{ + return m_persistTimeout; +} + +bool +TcpSocketBase::SetAllowBroadcast (bool allowBroadcast) +{ + // Broadcast is not implemented. Return true only if allowBroadcast==false + return (! allowBroadcast); +} + +bool +TcpSocketBase::GetAllowBroadcast () const +{ + return false; +} + +} // namespace ns3 diff --git a/src/internet-stack/tcp-socket-base.h b/src/internet-stack/tcp-socket-base.h new file mode 100644 index 000000000..e9fb2a2ea --- /dev/null +++ b/src/internet-stack/tcp-socket-base.h @@ -0,0 +1,222 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Adrian Sai-wah Tam + */ +#ifndef TCP_SOCKET_BASE_H +#define TCP_SOCKET_BASE_H + +#include +#include +#include "ns3/callback.h" +#include "ns3/traced-value.h" +#include "ns3/tcp-socket.h" +#include "ns3/ptr.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4-header.h" +#include "ns3/ipv4-interface.h" +#include "ns3/event-id.h" +#include "tcp-tx-buffer.h" +#include "tcp-rx-buffer.h" +#include "rtt-estimator.h" + +namespace ns3 { + +class Ipv4EndPoint; +class Node; +class Packet; +class TcpL4Protocol; +class TcpHeader; + +/** + * \ingroup socket + * \ingroup tcp + * + * \brief A base class for implementation of a stream socket using TCP. + * + * This class contains the essential components of TCP, as well as a sockets + * interface for upper layers to call. This serves as a base for other TCP + * functions where the sliding window mechanism is handled here. This class + * provides connection orientation and sliding window flow control. Part of + * this class is modified from the original NS-3 TCP socket implementation + * (TcpSocketImpl) by Raj Bhattacharjea. + */ +class TcpSocketBase : public TcpSocket +{ +public: + static TypeId GetTypeId (void); + /** + * Create an unbound TCP socket + */ + TcpSocketBase (void); + + /** + * Clone a TCP socket, for use upon receiving a connection request in LISTEN state + */ + TcpSocketBase (const TcpSocketBase& sock); + virtual ~TcpSocketBase (void); + + // Set associated Node, TcpL4Protocol, RttEstimator to this socket + virtual void SetNode (Ptr node); + virtual void SetTcp (Ptr tcp); + virtual void SetRtt (Ptr rtt); + + // Necessary implementations of null functions from ns3::Socket + virtual enum SocketErrno GetErrno (void) const; // returns m_errno + virtual enum SocketType GetSocketType (void) const;// returns socket type + virtual Ptr GetNode (void) const; // returns m_node + virtual int Bind (void); // Bind a socket by setting up endpoint in TcpL4Protocol + virtual int Bind (const Address &address); // ... endpoint of specific addr or port + virtual int Connect (const Address &address); // Setup endpoint and call ProcessAction() to connect + virtual int Listen (void); // Verify the socket is in a correct state and call ProcessAction() to listen + virtual int Close (void); // Close by app: Kill socket upon tx buffer emptied + virtual int ShutdownSend (void); // Assert the m_shutdownSend flag to prevent send to network + virtual int ShutdownRecv (void); // Assert the m_shutdownRecv flag to prevent forward to app + virtual int Send (Ptr p, uint32_t flags); // Call by app to send data to network + virtual int SendTo (Ptr p, uint32_t flags, const Address &toAddress); // Same as Send(), toAddress is insignificant + virtual Ptr Recv (uint32_t maxSize, uint32_t flags); // Return a packet to be forwarded to app + virtual Ptr RecvFrom (uint32_t maxSize, uint32_t flags, Address &fromAddress); // ... and write the remote address at fromAddress + virtual uint32_t GetTxAvailable (void) const; // Available Tx buffer size + virtual uint32_t GetRxAvailable (void) const; // Available-to-read data size, i.e. value of m_rxAvailable + virtual int GetSockName (Address &address) const; // Return local addr:port in address + virtual void BindToNetDevice (Ptr netdevice); // NetDevice with my m_endPoint + +protected: + // Implementing ns3::TcpSocket -- Attribute get/set + virtual void SetSndBufSize (uint32_t size); + virtual uint32_t GetSndBufSize (void) const; + virtual void SetRcvBufSize (uint32_t size); + virtual uint32_t GetRcvBufSize (void) const; + virtual void SetSegSize (uint32_t size); + virtual uint32_t GetSegSize (void) const; + virtual void SetSSThresh (uint32_t threshold) = 0; + virtual uint32_t GetSSThresh (void) const = 0; + virtual void SetInitialCwnd (uint32_t cwnd) = 0; + virtual uint32_t GetInitialCwnd (void) const = 0; + virtual void SetConnTimeout (Time timeout); + virtual Time GetConnTimeout (void) const; + virtual void SetConnCount (uint32_t count); + virtual uint32_t GetConnCount (void) const; + virtual void SetDelAckTimeout (Time timeout); + virtual Time GetDelAckTimeout (void) const; + virtual void SetDelAckMaxCount (uint32_t count); + virtual uint32_t GetDelAckMaxCount (void) const; + virtual void SetPersistTimeout (Time timeout); + virtual Time GetPersistTimeout (void) const; + virtual bool SetAllowBroadcast (bool allowBroadcast); + virtual bool GetAllowBroadcast () const; + + // Helper functions: Connection set up + int SetupCallback (void); // Common part of the two Bind(), i.e. set callback and remembering local addr:port + int DoConnect (void); // Sending a SYN packet to make a connection if the state allows + void ConnectionSucceeded (void); // Schedule-friendly wrapper for Socket::NotifyConnectionSucceeded() + int SetupEndpoint (void); // Configure m_endpoint for local addr for given remote addr + void CompleteFork (Ptr, const TcpHeader&, const Address& fromAddress, const Address& toAdress); + + // Helper functions: Transfer operation + void ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface); //Get a pkt from L3 + bool SendPendingData (bool withAck = false); // Send as much as the window allows + void SendEmptyPacket (uint8_t flags); // Send a empty packet that carries a flag, e.g. ACK + void SendRST (void); // Send reset and tear down this socket + bool OutOfRange (SequenceNumber32 s) const; // Check if a sequence number is within rx window + + // Helper functions: Connection close + int DoClose (void); // Close a socket by sending RST, FIN, or FIN+ACK, depend on the current state + void CloseAndNotify (void); // To CLOSED state, notify upper layer, and deallocate end point + void Destroy (void); // Kill this socket by zeroing its attributes + void DeallocateEndPoint (void); // Deallocate m_endPoint + void PeerClose (Ptr, const TcpHeader&); // Received a FIN from peer, notify rx buffer + void DoPeerClose (void); // FIN is in sequence, notify app and respond with a FIN + void CancelAllTimers (void); // Cancel all timer when endpoint is deleted + + // State transition functions + void ProcessEstablished (Ptr, const TcpHeader&); // Received a packet upon ESTABLISHED state + void ProcessListen (Ptr, const TcpHeader&, const Address&, const Address&); // Process the newly received ACK + void ProcessSynSent (Ptr, const TcpHeader&); // Received a packet upon SYN_SENT + void ProcessSynRcvd (Ptr, const TcpHeader&, const Address&, const Address&); // Received a packet upon SYN_RCVD + void ProcessWait (Ptr, const TcpHeader&); // Received a packet upon CLOSE_WAIT, FIN_WAIT_1, FIN_WAIT_2 + void ProcessClosing (Ptr, const TcpHeader&); // Received a packet upon CLOSING + void ProcessLastAck (Ptr, const TcpHeader&); // Received a packet upon LAST_ACK + + // Window management + virtual uint32_t UnAckDataCount (void); // Return count of number of unacked bytes + virtual uint32_t BytesInFlight (void); // Return total bytes in flight + virtual uint32_t Window (void); // Return the max possible number of unacked bytes + virtual uint32_t AvailableWindow (void); // Return unfilled portion of window + virtual uint16_t AdvertisedWindowSize (void); // The amount of Rx window announced to the peer + + // Manage data tx/rx + virtual Ptr Fork (void) = 0; // Call CopyObject<> to clone me + virtual void ReceivedAck (Ptr, const TcpHeader&); // Received an ACK packet + virtual void ReceivedData (Ptr, const TcpHeader&); // Recv of a data, put into buffer, call L7 to get it if necessary + virtual void EstimateRtt (const TcpHeader&); // RTT accounting + virtual void NewAck (SequenceNumber32 const& seq); // Update buffers w.r.t. ACK + virtual void DupAck (const TcpHeader& t, uint32_t count) = 0; // Received dupack + virtual void ReTxTimeout (void); // Call Retransmit() upon RTO event + virtual void Retransmit (void); // Halving cwnd and call DoRetransmit() + virtual void DelAckTimeout (void); // Action upon delay ACK timeout, i.e. send an ACK + virtual void LastAckTimeout (void); // Timeout at LAST_ACK, close the connection + virtual void PersistTimeout (void); // Send 1 byte probe to get an updated window size + virtual void DoRetransmit (void); // Retransmit the oldest packet + +protected: + // Counters and events + EventId m_retxEvent; //< Retransmission event + EventId m_lastAckEvent; //< Last ACK timeout event + EventId m_delAckEvent; //< Delayed ACK timeout event + EventId m_persistEvent; //< Persist event: Send 1 byte to probe for a non-zero Rx window + uint32_t m_dupAckCount; //< Dupack counter + uint32_t m_delAckCount; //< Delayed ACK counter + uint32_t m_delAckMaxCount; //< Number of packet to fire an ACK before delay timeout + uint32_t m_cnCount; //< Count of remaining connection retries + TracedValue