From 48ac0501a071000a31287b86037b7660f4af526e Mon Sep 17 00:00:00 2001 From: Raj Bhattacharjea Date: Sun, 7 Sep 2008 19:38:26 -0400 Subject: [PATCH 1/4] Fix TCP closedown FINs, and remove broken tcp-erros example (bug 242) --- examples/tcp-errors.cc | 150 -------------------------- examples/tcp-large-transfer.cc | 1 + examples/wscript | 4 - src/internet-stack/tcp-socket-impl.cc | 43 +++++--- src/internet-stack/tcp-typedefs.h | 30 +++--- 5 files changed, 43 insertions(+), 185 deletions(-) delete mode 100644 examples/tcp-errors.cc diff --git a/examples/tcp-errors.cc b/examples/tcp-errors.cc deleted file mode 100644 index 8197accfe..000000000 --- a/examples/tcp-errors.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- 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, 10ms 10Mb/s, 10ms -// n0-----------------n1-----------------n2 -// -// -// - CBR traffic for 1000 seconds -// - Tracing of queues and packet receptions to file -// "tcp-large-transfer.tr" -// - pcap traces also generated in the following files -// "tcp-large-transfer-$n-$i.pcap" where n and i represent node and interface -// numbers respectively -// Usage (e.g.): ./waf --run tcp-large-transfer - - -#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/global-route-manager.h" -#include "ns3/simulator-module.h" - -using namespace ns3; - -NS_LOG_COMPONENT_DEFINE ("TcpErrors"); - -int main (int argc, char *argv[]) -{ - - // Users may find it convenient to turn on explicit debugging - // for selected modules; the below lines suggest how to do this - // LogComponentEnable("TcpL4Protocol", LOG_LEVEL_ALL); - // LogComponentEnable("TcpSocketImpl", LOG_LEVEL_ALL); - // LogComponentEnable("PacketSink", LOG_LEVEL_ALL); - // LogComponentEnable("TcpErrors", LOG_LEVEL_ALL); - - // - // Make the random number generators generate reproducible results. - // - RandomVariable::UseGlobalSeed (1, 1, 2, 3, 5, 8); - - // Allow the user to override any of the defaults and the above - // Bind()s at run-time, via command-line arguments - CommandLine cmd; - cmd.Parse (argc, argv); - - // Here, we will explicitly create three nodes. The first container contains - // nodes 0 and 1 from the diagram above, and the second one contains nodes - // 1 and 2. This reflects the channel connectivity, and will be used to - // install the network interfaces and connect them with a channel. - NodeContainer n0n1; - n0n1.Create (2); - - NodeContainer n1n2; - n1n2.Add (n0n1.Get (1)); - n1n2.Create (1); - - // We create the channels first without any IP addressing information - // First make and configure the helper, so that it will put the appropriate - // parameters on the network interfaces and channels we are about to install. - PointToPointHelper p2p; - p2p.SetDeviceAttribute ("DataRate", DataRateValue (DataRate(10000000))); - p2p.SetChannelAttribute ("Delay", TimeValue (MilliSeconds(10))); - - // And then install devices and channels connecting our topology. - NetDeviceContainer d0d1 = p2p.Install (n0n1); - NetDeviceContainer d1d2 = p2p.Install (n1n2); - - // Now add ip/tcp stack to all nodes. - NodeContainer allNodes = NodeContainer (n0n1, n1n2.Get (1)); - InternetStackHelper internet; - internet.Install (allNodes); - - // Later, we add IP addresses. - Ipv4AddressHelper ipv4; - ipv4.SetBase ("10.1.3.0", "255.255.255.0"); - Ipv4InterfaceContainer i0i1 = ipv4.Assign (d0d1); - ipv4.SetBase ("10.1.2.0", "255.255.255.0"); - Ipv4InterfaceContainer i1i2 = ipv4.Assign (d1d2); - - // and setup ip routing tables to get total ip-level connectivity. - GlobalRouteManager::PopulateRoutingTables (); - - // Set up the sending CBR application - uint16_t servPort = 50000; - Address remoteAddress(InetSocketAddress(i1i2.GetAddress (1), servPort)); - OnOffHelper clientHelper ("ns3::TcpSocketFactory", remoteAddress); - clientHelper.SetAttribute - ("OnTime", RandomVariableValue (ConstantVariable (1))); - clientHelper.SetAttribute - ("OffTime", RandomVariableValue (ConstantVariable (0))); - ApplicationContainer clientApp = clientHelper.Install(n0n1.Get(0)); - clientApp.Start (Seconds (1.0)); - clientApp.Stop (Seconds (10.0)); - - // Create a packet sink to receive at n2 - PacketSinkHelper sinkHelper ("ns3::TcpSocketFactory", - InetSocketAddress (Ipv4Address::GetAny (), servPort)); - ApplicationContainer sinkApp = sinkHelper.Install (n1n2.Get(1)); - sinkApp.Start (Seconds (1.0)); - sinkApp.Stop (Seconds (10.0)); - - // We're going to model a lossy channel - RandomVariableValue u01(UniformVariable (0.0, 1.0)); - DoubleValue rate(0.001); - Ptr em1 = - CreateObject ("RanVar", u01, "ErrorRate", rate); - Ptr em2 = - CreateObject ("RanVar", u01, "ErrorRate", rate); - //put error models on both netdevices of the router nodes so that there is - //loss of both data and acks - d0d1.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue (em1)); - d1d2.Get(0)->SetAttribute("ReceiveErrorModel", PointerValue (em2)); - - //Ask for ASCII and pcap traces of network traffic - std::ofstream ascii; - ascii.open ("tcp-errors.tr"); - PointToPointHelper::EnableAsciiAll (ascii); - - PointToPointHelper::EnablePcapAll ("tcp-errors"); - - // Finally, set up the simulator to run for 1000 seconds. - Simulator::Stop (Seconds(1000)); - Simulator::Run (); - Simulator::Destroy (); -} diff --git a/examples/tcp-large-transfer.cc b/examples/tcp-large-transfer.cc index d4afeab08..4a4d294f3 100644 --- a/examples/tcp-large-transfer.cc +++ b/examples/tcp-large-transfer.cc @@ -132,6 +132,7 @@ int main (int argc, char *argv[]) ApplicationContainer apps = sink.Install (n1n2.Get (1)); apps.Start (Seconds (0.0)); + apps.Stop (Seconds (3.0)); // Create a source to send packets from n0. Instead of a full Application // and the helper APIs you might see in other example files, this example diff --git a/examples/wscript b/examples/wscript index c4a8c7c81..34673bb1c 100644 --- a/examples/wscript +++ b/examples/wscript @@ -60,10 +60,6 @@ def build(bld): ['point-to-point', 'internet-stack']) obj.source = 'tcp-large-transfer.cc' - obj = bld.create_ns3_program('tcp-errors', - ['point-to-point', 'internet-stack']) - obj.source = 'tcp-errors.cc' - obj = bld.create_ns3_program('tcp-nsc-lfn', ['point-to-point', 'internet-stack']) obj.source = 'tcp-nsc-lfn.cc' diff --git a/src/internet-stack/tcp-socket-impl.cc b/src/internet-stack/tcp-socket-impl.cc index 7902dd259..bcd698dea 100644 --- a/src/internet-stack/tcp-socket-impl.cc +++ b/src/internet-stack/tcp-socket-impl.cc @@ -645,13 +645,15 @@ void TcpSocketImpl::SendEmptyPacket (uint8_t flags) m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_remoteAddress); Time rto = m_rtt->RetransmitTimeout (); - if (flags & TcpHeader::SYN) + bool hasSyn = flags & TcpHeader::SYN; + bool hasFin = flags & TcpHeader::FIN; + if (hasSyn) { rto = m_cnTimeout; m_cnTimeout = m_cnTimeout + m_cnTimeout; m_cnCount--; } - if (m_retxEvent.IsExpired () ) //no outstanding timer + if (m_retxEvent.IsExpired () && (hasSyn || hasFin) ) //no outstanding timer { NS_LOG_LOGIC ("Schedule retransmission timeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " @@ -745,6 +747,13 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, Ptr ipv4 = m_node->GetObject (); switch (a) { + case ACK_TX: + if(tcpHeader.GetFlags() & TcpHeader::FIN) + { + ++m_nextRxSequence; //bump this to account for the FIN + } + SendEmptyPacket (TcpHeader::ACK); + break; case SYN_ACK_TX: NS_LOG_LOGIC ("TcpSocketImpl " << this <<" Action SYN_ACK_TX"); // m_remotePort = InetSocketAddress::ConvertFrom (fromAddress).GetPort (); @@ -767,19 +776,20 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, p, tcpHeader,fromAddress); return true; } - // This is the cloned endpoint - m_endPoint->SetPeer (m_remoteAddress, m_remotePort); - if (ipv4->GetIfIndexForDestination (m_remoteAddress, localIfIndex)) - { - m_localAddress = ipv4->GetAddress (localIfIndex); - m_endPoint->SetLocalAddress (m_localAddress); - // Leave local addr in the portmap to any, as the path from - // remote can change and packets can arrive on different interfaces - //m_endPoint->SetLocalAddress (Ipv4Address::GetAny()); - } - // TCP SYN consumes one byte - m_nextRxSequence = tcpHeader.GetSequenceNumber() + SequenceNumber(1); - SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); + // This is the cloned endpoint + NS_ASSERT (m_state == SYN_RCVD); + m_endPoint->SetPeer (m_remoteAddress, m_remotePort); + if (ipv4->GetIfIndexForDestination (m_remoteAddress, localIfIndex)) + { + m_localAddress = ipv4->GetAddress (localIfIndex); + m_endPoint->SetLocalAddress (m_localAddress); + // Leave local addr in the portmap to any, as the path from + // remote can change and packets can arrive on different interfaces + //m_endPoint->SetLocalAddress (Ipv4Address::GetAny()); + } + // TCP SYN consumes one byte + m_nextRxSequence = tcpHeader.GetSequenceNumber() + SequenceNumber(1); + SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK); break; case ACK_TX_1: NS_LOG_LOGIC ("TcpSocketImpl " << this <<" Action ACK_TX_1"); @@ -844,6 +854,7 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, { NewRx (p, tcpHeader, fromAddress); } + ++m_nextRxSequence; //bump this to account for the FIN States_t saveState = m_state; // Used to see if app responds NS_LOG_LOGIC ("TcpSocketImpl " << this << " peer close, state " << m_state); @@ -878,7 +889,7 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, NotifyNewConnectionCreated (this, fromAddress); break; default: - break; + return ProcessAction (a); } return true; } diff --git a/src/internet-stack/tcp-typedefs.h b/src/internet-stack/tcp-typedefs.h index 277511c26..ff538710e 100644 --- a/src/internet-stack/tcp-typedefs.h +++ b/src/internet-stack/tcp-typedefs.h @@ -63,21 +63,21 @@ typedef enum { typedef enum { NO_ACT, // 0 ACK_TX, // 1 - ACK_TX_1, // ACK response to syn - RST_TX, // 2 - SYN_TX, // 3 - SYN_ACK_TX, // 4 - FIN_TX, // 5 - FIN_ACK_TX, // 6 - NEW_ACK, // 7 - NEW_SEQ_RX, // 8 - RETX, // 9 - TX_DATA, // 10 - PEER_CLOSE, // 11 - APP_CLOSED, // 12 - CANCEL_TM, // 13 - APP_NOTIFY, // 14 - Notify app that connection failed - SERV_NOTIFY, // 15 - Notify server tcp that connection completed + ACK_TX_1, // 2 - ACK response to syn + RST_TX, // 3 + SYN_TX, // 4 + SYN_ACK_TX, // 5 + FIN_TX, // 6 + FIN_ACK_TX, // 7 + NEW_ACK, // 8 + NEW_SEQ_RX, // 9 + RETX, // 10 + TX_DATA, // 11 + PEER_CLOSE, // 12 + APP_CLOSED, // 13 + CANCEL_TM, // 14 + APP_NOTIFY, // 15 - Notify app that connection failed + SERV_NOTIFY, // 16 - Notify server tcp that connection completed LAST_ACTION } Actions_t; class SA // State/Action pair From 1b605147ce39e9708d23f0b39c592e8506ec20e4 Mon Sep 17 00:00:00 2001 From: Raj Bhattacharjea Date: Sun, 7 Sep 2008 20:23:24 -0400 Subject: [PATCH 2/4] Implement TCP finite rx buffer (bug 239) --- src/internet-stack/pending-data.cc | 2 +- src/internet-stack/tcp-socket-impl.cc | 424 +++++++++++++++++--------- src/internet-stack/tcp-socket-impl.h | 34 ++- src/node/tcp-socket.cc | 6 - src/node/tcp-socket.h | 2 - 5 files changed, 299 insertions(+), 169 deletions(-) diff --git a/src/internet-stack/pending-data.cc b/src/internet-stack/pending-data.cc index bbfde0d77..c97c85968 100644 --- a/src/internet-stack/pending-data.cc +++ b/src/internet-stack/pending-data.cc @@ -131,7 +131,7 @@ Ptr PendingData::CopyFromOffset (uint32_t s, uint32_t o) uint32_t s1 = std::min (s, SizeFromOffset (o)); // Insure not beyond end of data if (s1 == 0) { - return 0; // No data requested + return Create (); // No data requested } if (data.size() != 0) { // Actual data exists, make copy and return it diff --git a/src/internet-stack/tcp-socket-impl.cc b/src/internet-stack/tcp-socket-impl.cc index bcd698dea..d9deccc1b 100644 --- a/src/internet-stack/tcp-socket-impl.cc +++ b/src/internet-stack/tcp-socket-impl.cc @@ -76,10 +76,13 @@ TcpSocketImpl::GetTypeId () m_highestRxAck (0), m_lastRxAck (0), m_nextRxSequence (0), + m_rxAvailable (0), + m_rxBufSize (0), m_pendingData (0), + m_rxWindowSize (0), + m_persistTime (Seconds(6)), //XXX hook this into attributes? m_rtt (0), - m_lastMeasuredRtt (Seconds(0.0)), - m_rxAvailable (0) + m_lastMeasuredRtt (Seconds(0.0)) { NS_LOG_FUNCTION (this); } @@ -112,20 +115,21 @@ TcpSocketImpl::TcpSocketImpl(const TcpSocketImpl& sock) m_highestRxAck (sock.m_highestRxAck), m_lastRxAck (sock.m_lastRxAck), m_nextRxSequence (sock.m_nextRxSequence), + m_rxAvailable (0), + m_rxBufSize (0), m_pendingData (0), m_segmentSize (sock.m_segmentSize), m_rxWindowSize (sock.m_rxWindowSize), - m_advertisedWindowSize (sock.m_advertisedWindowSize), m_cWnd (sock.m_cWnd), m_ssThresh (sock.m_ssThresh), m_initialCWnd (sock.m_initialCWnd), + m_persistTime (sock.m_persistTime), m_rtt (0), m_lastMeasuredRtt (Seconds(0.0)), m_cnTimeout (sock.m_cnTimeout), m_cnCount (sock.m_cnCount), - m_rxAvailable (0), m_sndBufSize (sock.m_sndBufSize), - m_rcvBufSize(sock.m_rcvBufSize) + m_rxBufMaxSize(sock.m_rxBufMaxSize) { NS_LOG_FUNCTION_NOARGS (); NS_LOG_LOGIC("Invoked the copy constructor"); @@ -176,7 +180,6 @@ TcpSocketImpl::SetNode (Ptr node) m_node = node; // Initialize some variables m_cWnd = m_initialCWnd * m_segmentSize; - m_rxWindowSize = m_advertisedWindowSize; } void @@ -212,6 +215,9 @@ TcpSocketImpl::Destroy (void) 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()); m_retxEvent.Cancel (); } int @@ -492,6 +498,7 @@ TcpSocketImpl::Recv (uint32_t maxSize, uint32_t flags) out[i->first] = i->second; } m_rxAvailable -= i->second->GetSize (); + m_rxBufSize -= i->second->GetSize (); m_bufferedData.erase (i); // Remove from list } if (out.size() == 0) @@ -514,6 +521,7 @@ TcpSocketImpl::Recv (uint32_t maxSize, uint32_t flags) m_bufferedData[i->first+SequenceNumber(avail)] = i->second->CreateFragment(avail,i->second->GetSize()-avail); m_rxAvailable += i->second->GetSize()-avail; + m_rxBufSize += i->second->GetSize()-avail; } } return outPacket; @@ -571,6 +579,13 @@ TcpSocketImpl::ForwardUp (Ptr packet, Ipv4Address ipv4, uint16_t port) } } + if (m_rxWindowSize == 0 && tcpHeader.GetWindowSize () != 0) + { //persist probes end + NS_LOG_LOGIC (this<<" Leaving zerowindow persist state"); + m_persistEvent.Cancel (); + } + m_rxWindowSize = tcpHeader.GetWindowSize (); //update the flow control window + Events_t event = SimulationSingleton::Get ()->FlagsEvent (tcpHeader.GetFlags () ); Actions_t action = ProcessEvent (event); //updates the state Address address = InetSocketAddress (ipv4, port); @@ -632,7 +647,7 @@ Actions_t TcpSocketImpl::ProcessEvent (Events_t e) void TcpSocketImpl::SendEmptyPacket (uint8_t flags) { - NS_LOG_FUNCTION (this << flags); + NS_LOG_FUNCTION (this << (uint32_t)flags); Ptr p = Create (); TcpHeader header; @@ -641,19 +656,21 @@ void TcpSocketImpl::SendEmptyPacket (uint8_t flags) header.SetAckNumber (m_nextRxSequence); header.SetSourcePort (m_endPoint->GetLocalPort ()); header.SetDestinationPort (m_remotePort); - header.SetWindowSize (m_advertisedWindowSize); + header.SetWindowSize (AdvertisedWindowSize()); m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_remoteAddress); Time rto = m_rtt->RetransmitTimeout (); bool hasSyn = flags & TcpHeader::SYN; bool hasFin = flags & TcpHeader::FIN; + bool isAck = flags == TcpHeader::ACK; if (hasSyn) { rto = m_cnTimeout; m_cnTimeout = m_cnTimeout + m_cnTimeout; m_cnCount--; } - if (m_retxEvent.IsExpired () && (hasSyn || hasFin) ) //no outstanding timer + if (m_retxEvent.IsExpired () && (hasSyn || hasFin) && !isAck ) + //no outstanding timer { NS_LOG_LOGIC ("Schedule retransmission timeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " @@ -800,7 +817,6 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, NS_LOG_DEBUG ("TcpSocketImpl " << this << " ACK_TX_1" << " nextRxSeq " << m_nextRxSequence); SendEmptyPacket (TcpHeader::ACK); - m_rxWindowSize = tcpHeader.GetWindowSize (); if (tcpHeader.GetAckNumber () > m_highestRxAck) { m_highestRxAck = tcpHeader.GetAckNumber (); @@ -818,13 +834,17 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr p, { break; } - if (tcpHeader.GetAckNumber () == m_highestRxAck && - tcpHeader.GetAckNumber () < m_nextTxSequence) + if (tcpHeader.GetAckNumber () == m_highestRxAck) { - DupAck (tcpHeader, ++m_dupAckCount); + if (tcpHeader.GetAckNumber () < m_nextTxSequence) + { + DupAck (tcpHeader, ++m_dupAckCount); + } + NS_ASSERT(tcpHeader.GetAckNumber () <= m_nextTxSequence); + //if the ack is precisely equal to the nextTxSequence break; } - if (tcpHeader.GetAckNumber () > m_highestRxAck) + if (tcpHeader.GetAckNumber () > m_highestRxAck) { m_dupAckCount = 0; } @@ -939,7 +959,7 @@ bool TcpSocketImpl::SendPendingData (bool withAck) << " highestRxAck " << m_highestRxAck << " pd->Size " << m_pendingData->Size () << " pd->SFS " << m_pendingData->SizeFromSeq (m_firstPendingSequence, m_nextTxSequence)); - +//XXX pd->Size is probably a bug, should be SizeFromSeq(...) if (w < m_segmentSize && m_pendingData->Size () > w) { break; // No more @@ -972,6 +992,7 @@ bool TcpSocketImpl::SendPendingData (bool withAck) header.SetAckNumber (m_nextRxSequence); header.SetSourcePort (m_endPoint->GetLocalPort()); header.SetDestinationPort (m_remotePort); + header.SetWindowSize (AdvertisedWindowSize()); if (m_shutdownSend) { m_errno = ERROR_SHUTDOWN; @@ -982,7 +1003,7 @@ bool TcpSocketImpl::SendPendingData (bool withAck) if (m_retxEvent.IsExpired () ) //go ahead and schedule the retransmit { Time rto = m_rtt->RetransmitTimeout (); - NS_LOG_LOGIC ("SendPendingData Schedule retransmission timeout at time " << + NS_LOG_LOGIC (this<<" SendPendingData Schedule ReTxTimeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " << (Simulator::Now () + rto).GetSeconds () ); m_retxEvent = Simulator::Schedule (rto,&TcpSocketImpl::ReTxTimeout,this); @@ -1035,6 +1056,17 @@ uint32_t TcpSocketImpl::AvailableWindow () return (win - unack); // Amount of window space available } +uint32_t TcpSocketImpl::RxBufferFreeSpace() +{ + return m_rxBufMaxSize - m_rxBufSize; +} + +uint16_t TcpSocketImpl::AdvertisedWindowSize() +{ + uint32_t max = 0xffff; + return std::min(RxBufferFreeSpace(), max); +} + void TcpSocketImpl::NewRx (Ptr p, const TcpHeader& tcpHeader, const Address& fromAddress) @@ -1050,9 +1082,20 @@ void TcpSocketImpl::NewRx (Ptr p, " ack " << tcpHeader.GetAckNumber() << " p.size is " << p->GetSize()); States_t origState = m_state; + if (RxBufferFreeSpace() < p->GetSize()) + { //if not enough room, fragment + p = p->CreateFragment(0, RxBufferFreeSpace()); + } + //XXX + //fragmenting here MIGHT not be the right thing to do, since possibly we trim + //the front and back off the packet below if it isn't all new data, so the + //check against RxBufferFreeSpace and fragmentation should ideally occur + //just before insertion into m_bufferedData, but this strategy is more + //agressive in rejecting oversized packets and still gives acceptable TCP uint32_t s = p->GetSize (); // Size of associated data if (s == 0) - {// Nothing to do if no associated data + {//if there is no data or no rx buffer space, just ack anyway + SendEmptyPacket (TcpHeader::ACK); return; } // Log sequence received if enabled @@ -1085,14 +1128,12 @@ void TcpSocketImpl::NewRx (Ptr p, //buffer this, it'll be read by call to Recv UnAckData_t::iterator i = m_bufferedData.find (tcpHeader.GetSequenceNumber () ); - if (i != m_bufferedData.end () ) //we found it already in the buffer - { - i->second = 0; // relase reference to already buffered - } - // Save for later delivery - m_bufferedData[tcpHeader.GetSequenceNumber () ] = p; + NS_ASSERT(i == m_bufferedData.end ()); //no way it should have been found + // Save for later delivery if there is room + m_bufferedData[tcpHeader.GetSequenceNumber () ] = p; m_rxAvailable += p->GetSize (); RxBufFinishInsert (tcpHeader.GetSequenceNumber ()); + m_rxBufSize += p->GetSize (); NotifyDataRecv (); if (m_closeNotified) { @@ -1192,6 +1233,7 @@ void TcpSocketImpl::NewRx (Ptr p, // Save for later delivery m_bufferedData[start] = p; m_rxAvailable += p->GetSize (); + m_rxBufSize += p->GetSize(); RxBufFinishInsert(start); NotifyDataRecv (); } @@ -1259,13 +1301,31 @@ void TcpSocketImpl::CommonNewAck (SequenceNumber ack, bool skipTimer) //DEBUG(1,(cout << "TCP " << this << "Cancelling retx timer " << endl)); if (!skipTimer) { - m_retxEvent.Cancel (); + 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 Time rto = m_rtt->RetransmitTimeout (); - NS_LOG_LOGIC ("Schedule retransmission timeout at time " + NS_LOG_LOGIC (this<<" Schedule ReTxTimeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " << (Simulator::Now () + rto).GetSeconds ()); - m_retxEvent = Simulator::Schedule (rto, &TcpSocketImpl::ReTxTimeout, this); + m_retxEvent = + Simulator::Schedule (rto, &TcpSocketImpl::ReTxTimeout, this); + } + if (m_rxWindowSize == 0 && m_persistEvent.IsExpired ()) //zerowindow + { + 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 " + < p = + m_pendingData->CopyFromSeq(1,m_firstPendingSequence,m_nextTxSequence); + TcpHeader tcpHeader; + tcpHeader.SetSequenceNumber (m_nextTxSequence); + tcpHeader.SetAckNumber (m_nextRxSequence); + tcpHeader.SetSourcePort (m_endPoint->GetLocalPort()); + tcpHeader.SetDestinationPort (m_remotePort); + tcpHeader.SetWindowSize (AdvertisedWindowSize()); + + m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (), + m_remoteAddress); + NS_LOG_LOGIC ("Schedule persist timeout at time " + < p = m_pendingData->CopyFromSeq (m_segmentSize, m_firstPendingSequence, - m_highestRxAck); + m_nextTxSequence); // Calculate remaining data for COE check uint32_t remainingData = m_pendingData->SizeFromSeq ( m_firstPendingSequence, @@ -1417,7 +1507,7 @@ void TcpSocketImpl::Retransmit () if (m_retxEvent.IsExpired () ) { Time rto = m_rtt->RetransmitTimeout (); - NS_LOG_LOGIC ("Schedule retransmission timeout at time " + NS_LOG_LOGIC (this<<" Schedule ReTxTimeout at time " << Simulator::Now ().GetSeconds () << " to expire at time " << (Simulator::Now () + rto).GetSeconds ()); m_retxEvent = Simulator::Schedule (rto,&TcpSocketImpl::ReTxTimeout,this); @@ -1430,7 +1520,7 @@ void TcpSocketImpl::Retransmit () tcpHeader.SetSourcePort (m_endPoint->GetLocalPort()); tcpHeader.SetDestinationPort (m_remotePort); tcpHeader.SetFlags (flags); - tcpHeader.SetWindowSize (m_advertisedWindowSize); + tcpHeader.SetWindowSize (AdvertisedWindowSize()); m_tcp->SendPacket (p, tcpHeader, m_endPoint->GetLocalAddress (), m_remoteAddress); @@ -1451,13 +1541,13 @@ TcpSocketImpl::GetSndBufSize (void) const void TcpSocketImpl::SetRcvBufSize (uint32_t size) { - m_rcvBufSize = size; + m_rxBufMaxSize = size; } uint32_t TcpSocketImpl::GetRcvBufSize (void) const { - return m_rcvBufSize; + return m_rxBufMaxSize; } void @@ -1472,18 +1562,6 @@ TcpSocketImpl::GetSegSize (void) const return m_segmentSize; } -void -TcpSocketImpl::SetAdvWin (uint32_t window) -{ - m_advertisedWindowSize = window; -} - -uint32_t -TcpSocketImpl::GetAdvWin (void) const -{ - return m_advertisedWindowSize; -} - void TcpSocketImpl::SetSSThresh (uint32_t threshold) { @@ -1567,6 +1645,7 @@ TcpSocketImpl::GetDelAckMaxCount (void) const #include "ns3/simple-channel.h" #include "ns3/simple-net-device.h" #include "ns3/drop-tail-queue.h" +#include "ns3/config.h" #include "internet-stack.h" #include @@ -1578,20 +1657,34 @@ class TcpSocketImplTest: public Test TcpSocketImplTest (); virtual bool RunTests (void); private: - void Test1 (void); //send string "Hello world" server->client - void Test2 (uint32_t payloadSize); - Ptr CreateInternetNode (); - Ptr AddSimpleNetDevice (Ptr,const char*,const char*); - + //test 1, which sends string "Hello world" server->client + void Test1 (void); void Test1_HandleConnectionCreated (Ptr, const Address &); void Test1_HandleRecv (Ptr sock); + //test 2, which sends a number of bytes server->client + void Test2 (uint32_t payloadSize); void Test2_HandleConnectionCreated (Ptr, const Address &); void Test2_HandleRecv (Ptr sock); uint32_t test2_payloadSize; + //test 3, which makes sure the rx buffer is finite + void Test3 (uint32_t payloadSize); + void Test3_HandleConnectionCreated (Ptr, const Address &); + void Test3_HandleRecv (Ptr sock); + uint32_t test3_payloadSize; + + //helpers to make topology construction easier + Ptr CreateInternetNode (); + Ptr AddSimpleNetDevice (Ptr,const char*,const char*); + void SetupDefaultSim (); + + //reset all of the below state for another run void Reset (); - + + //all of the state this class needs; basically both ends of the connection, + //and this test kind of acts as an single application running on both nodes + //simultaneously Ptr node0; Ptr node1; Ptr dev0; @@ -1602,9 +1695,9 @@ class TcpSocketImplTest: public Test Ptr sock1; uint32_t rxBytes0; uint32_t rxBytes1; - + uint8_t* rxPayload; - + bool result; }; @@ -1621,118 +1714,34 @@ bool TcpSocketImplTest::RunTests (void) { Test1(); + if (!result) return false; Test2(600); + if (!result) return false; + Test3(20000); return result; } +//----------------------------------------------------------------------------- +//test 1----------------------------------------------------------------------- +//----------------------------------------------------------------------------- void TcpSocketImplTest::Test1 () { - const char* netmask = "255.255.255.0"; - const char* ipaddr0 = "192.168.1.1"; - const char* ipaddr1 = "192.168.1.2"; - node0 = CreateInternetNode (); - node1 = CreateInternetNode (); - dev0 = AddSimpleNetDevice (node0, ipaddr0, netmask); - dev1 = AddSimpleNetDevice (node1, ipaddr1, netmask); - - channel = CreateObject (); - dev0->SetChannel (channel); - dev1->SetChannel (channel); - - Ptr sockFactory0 = node0->GetObject (); - Ptr sockFactory1 = node1->GetObject (); - - listeningSock = sockFactory0->CreateSocket(); - sock1 = sockFactory1->CreateSocket(); - - uint16_t port = 50000; - InetSocketAddress serverlocaladdr (Ipv4Address::GetAny(), port); - InetSocketAddress serverremoteaddr (Ipv4Address(ipaddr0), port); - - listeningSock->Bind(serverlocaladdr); - listeningSock->Listen (0); + SetupDefaultSim (); listeningSock->SetAcceptCallback - (MakeNullCallback, const Address &> (), - MakeCallback(&TcpSocketImplTest::Test1_HandleConnectionCreated,this)); - - sock1->Connect(serverremoteaddr); + (MakeNullCallback, const Address &> (), + MakeCallback(&TcpSocketImplTest::Test1_HandleConnectionCreated,this)); sock1->SetRecvCallback (MakeCallback(&TcpSocketImplTest::Test1_HandleRecv, this)); - + Simulator::Run (); Simulator::Destroy (); - + result = result && (rxBytes1 == 13); result = result && (strcmp((const char*) rxPayload,"Hello World!") == 0); - + Reset (); } -void -TcpSocketImplTest::Test2 (uint32_t payloadSize) -{ - test2_payloadSize = payloadSize; - const char* netmask = "255.255.255.0"; - const char* ipaddr0 = "192.168.1.1"; - const char* ipaddr1 = "192.168.1.2"; - node0 = CreateInternetNode (); - node1 = CreateInternetNode (); - dev0 = AddSimpleNetDevice (node0, ipaddr0, netmask); - dev1 = AddSimpleNetDevice (node1, ipaddr1, netmask); - - channel = CreateObject (); - dev0->SetChannel (channel); - dev1->SetChannel (channel); - - Ptr sockFactory0 = node0->GetObject (); - Ptr sockFactory1 = node1->GetObject (); - - listeningSock = sockFactory0->CreateSocket(); - sock1 = sockFactory1->CreateSocket(); - - uint16_t port = 50000; - InetSocketAddress serverlocaladdr (Ipv4Address::GetAny(), port); - InetSocketAddress serverremoteaddr (Ipv4Address(ipaddr0), port); - - listeningSock->Bind(serverlocaladdr); - listeningSock->Listen (0); - listeningSock->SetAcceptCallback - (MakeNullCallback, const Address &> (), - MakeCallback(&TcpSocketImplTest::Test2_HandleConnectionCreated,this)); - - sock1->Connect(serverremoteaddr); - sock1->SetRecvCallback (MakeCallback(&TcpSocketImplTest::Test2_HandleRecv, this)); - - Simulator::Run (); - Simulator::Destroy (); - - result = result && (rxBytes1 == test2_payloadSize); - - Reset (); -} - -Ptr -TcpSocketImplTest::CreateInternetNode () -{ - Ptr node = CreateObject (); - AddInternetStack (node); - return node; -} - -Ptr -TcpSocketImplTest::AddSimpleNetDevice (Ptr node, const char* ipaddr, const char* netmask) -{ - Ptr dev = CreateObject (); - dev->SetAddress (Mac48Address::Allocate ()); - node->AddDevice (dev); - Ptr ipv4 = node->GetObject (); - uint32_t ndid = ipv4->AddInterface (dev); - ipv4->SetAddress (ndid, Ipv4Address (ipaddr)); - ipv4->SetNetworkMask (ndid, Ipv4Mask (netmask)); - ipv4->SetUp (ndid); - return dev; -} - void TcpSocketImplTest::Test1_HandleConnectionCreated (Ptr s, const Address & addr) { @@ -1764,6 +1773,27 @@ TcpSocketImplTest::Test1_HandleRecv (Ptr sock) } } +//----------------------------------------------------------------------------- +//test 2----------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void +TcpSocketImplTest::Test2 (uint32_t payloadSize) +{ + test2_payloadSize = payloadSize; + SetupDefaultSim (); + listeningSock->SetAcceptCallback + (MakeNullCallback, const Address &> (), + MakeCallback(&TcpSocketImplTest::Test2_HandleConnectionCreated,this)); + sock1->SetRecvCallback (MakeCallback(&TcpSocketImplTest::Test2_HandleRecv, this)); + + Simulator::Run (); + Simulator::Destroy (); + + result = result && (rxBytes1 == test2_payloadSize); + + Reset (); +} + void TcpSocketImplTest::Test2_HandleConnectionCreated (Ptr s, const Address & addr) { @@ -1788,10 +1818,108 @@ TcpSocketImplTest::Test2_HandleRecv (Ptr sock) } else { - NS_FATAL_ERROR ("Recv from unknown socket "<SetAcceptCallback + (MakeNullCallback, const Address &> (), + MakeCallback(&TcpSocketImplTest::Test3_HandleConnectionCreated,this)); + sock1->SetRecvCallback (MakeCallback(&TcpSocketImplTest::Test3_HandleRecv, this)); + + Simulator::Run (); + Simulator::Destroy (); + + result = result && (rxBytes1 == test3_payloadSize); + + Reset(); +} +void +TcpSocketImplTest::Test3_HandleConnectionCreated (Ptr s, const Address &) +{ + NS_ASSERT(s != listeningSock); + NS_ASSERT(sock0 == 0); + sock0 = s; + Ptr p = Create (test3_payloadSize); + sock0->Send(p); +} +void +TcpSocketImplTest::Test3_HandleRecv (Ptr sock) +{ + NS_ASSERT_MSG (sock == sock1, "Not supposed to be back traffic in test 3... "); + if(sock->GetRxAvailable() >= 10000 ) //perform batch reads every 10000 bytes + { + Ptr p = sock->Recv(); + uint32_t sz = p->GetSize(); + rxBytes1 += sz; + } +} + +//----------------------------------------------------------------------------- +//helpers---------------------------------------------------------------------- +//----------------------------------------------------------------------------- +Ptr +TcpSocketImplTest::CreateInternetNode () +{ + Ptr node = CreateObject (); + AddInternetStack (node); + return node; +} + +Ptr +TcpSocketImplTest::AddSimpleNetDevice (Ptr node, const char* ipaddr, const char* netmask) +{ + Ptr dev = CreateObject (); + dev->SetAddress (Mac48Address::Allocate ()); + node->AddDevice (dev); + Ptr ipv4 = node->GetObject (); + uint32_t ndid = ipv4->AddInterface (dev); + ipv4->SetAddress (ndid, Ipv4Address (ipaddr)); + ipv4->SetNetworkMask (ndid, Ipv4Mask (netmask)); + ipv4->SetUp (ndid); + return dev; +} + +void +TcpSocketImplTest::SetupDefaultSim () +{ + const char* netmask = "255.255.255.0"; + const char* ipaddr0 = "192.168.1.1"; + const char* ipaddr1 = "192.168.1.2"; + node0 = CreateInternetNode (); + node1 = CreateInternetNode (); + dev0 = AddSimpleNetDevice (node0, ipaddr0, netmask); + dev1 = AddSimpleNetDevice (node1, ipaddr1, netmask); + + channel = CreateObject (); + dev0->SetChannel (channel); + dev1->SetChannel (channel); + + Ptr sockFactory0 = node0->GetObject (); + Ptr sockFactory1 = node1->GetObject (); + + listeningSock = sockFactory0->CreateSocket(); + sock1 = sockFactory1->CreateSocket(); + + uint16_t port = 50000; + InetSocketAddress serverlocaladdr (Ipv4Address::GetAny(), port); + InetSocketAddress serverremoteaddr (Ipv4Address(ipaddr0), port); + + listeningSock->Bind(serverlocaladdr); + listeningSock->Listen (0); + + sock1->Connect(serverremoteaddr); +} + void TcpSocketImplTest::Reset () { diff --git a/src/internet-stack/tcp-socket-impl.h b/src/internet-stack/tcp-socket-impl.h index 8f2597124..915c0ef5c 100644 --- a/src/internet-stack/tcp-socket-impl.h +++ b/src/internet-stack/tcp-socket-impl.h @@ -121,6 +121,10 @@ private: virtual uint32_t Window(); // Return window size (integer) virtual uint32_t AvailableWindow();// Return unfilled portion of window + //methods for Rx buffer management + uint32_t RxBufferFreeSpace(); + uint16_t AdvertisedWindowSize(); + // Manage data tx/rx void NewRx (Ptr, const TcpHeader&, const Address&); void RxBufFinishInsert (SequenceNumber); @@ -132,6 +136,7 @@ private: void ReTxTimeout (); void DelAckTimeout (); void LastAckTimeout (); + void PersistTimeout (); void Retransmit (); void CommonNewAck (SequenceNumber seq, bool skipTimer = false); @@ -142,8 +147,6 @@ private: virtual uint32_t GetRcvBufSize (void) const; virtual void SetSegSize (uint32_t size); virtual uint32_t GetSegSize (void) const; - virtual void SetAdvWin (uint32_t window); - virtual uint32_t GetAdvWin (void) const; virtual void SetSSThresh (uint32_t threshold); virtual uint32_t GetSSThresh (void) const; virtual void SetInitialCwnd (uint32_t cwnd); @@ -195,23 +198,33 @@ private: SequenceNumber m_lastRxAck; //sequence info, reciever side - SequenceNumber m_nextRxSequence; + SequenceNumber m_nextRxSequence; //next expected sequence + + //Rx buffer + UnAckData_t m_bufferedData; //buffer which sorts out of sequence data + //Rx buffer state + uint32_t m_rxAvailable; // amount of data available for reading through Recv + uint32_t m_rxBufSize; //size in bytes of the data in the rx buf + //note that these two are not the same: rxAvailbale is the number of + //contiguous sequenced bytes that can be read, rxBufSize is the TOTAL size + //including out of sequence data, such that m_rxAvailable <= m_rxBufSize - //history data - //this is the incoming data buffer which sorts out of sequence data - UnAckData_t m_bufferedData; //this is kind of the tx buffer PendingData* m_pendingData; SequenceNumber m_firstPendingSequence; // Window management uint32_t m_segmentSize; //SegmentSize - uint32_t m_rxWindowSize; - uint32_t m_advertisedWindowSize; //Window to advertise + uint32_t m_rxWindowSize; //Flow control window TracedValue m_cWnd; //Congestion window uint32_t m_ssThresh; //Slow Start Threshold uint32_t m_initialCWnd; //Initial cWnd value + //persist timer management + Time m_persistTime; + EventId m_persistEvent; + + // Round trip time estimation Ptr m_rtt; Time m_lastMeasuredRtt; @@ -220,12 +233,9 @@ private: Time m_cnTimeout; uint32_t m_cnCount; - // Temporary queue for delivering data to application - uint32_t m_rxAvailable; - // Attributes uint32_t m_sndBufSize; // buffer limit for the outgoing queue - uint32_t m_rcvBufSize; // maximum receive socket buffer size + uint32_t m_rxBufMaxSize; // maximum receive socket buffer size }; }//namespace ns3 diff --git a/src/node/tcp-socket.cc b/src/node/tcp-socket.cc index 1dd3d4057..2ba8195ba 100644 --- a/src/node/tcp-socket.cc +++ b/src/node/tcp-socket.cc @@ -55,12 +55,6 @@ TcpSocket::GetTypeId (void) MakeUintegerAccessor (&TcpSocket::GetSegSize, &TcpSocket::SetSegSize), MakeUintegerChecker ()) - .AddAttribute ("AdvertisedWindowSize", - "TCP advertised window size (bytes)", - UintegerValue (0xffff), - MakeUintegerAccessor (&TcpSocket::GetAdvWin, - &TcpSocket::SetAdvWin), - MakeUintegerChecker ()) .AddAttribute ("SlowStartThreshold", "TCP slow start threshold (bytes)", UintegerValue (0xffff), diff --git a/src/node/tcp-socket.h b/src/node/tcp-socket.h index 413d87f62..5c443f442 100644 --- a/src/node/tcp-socket.h +++ b/src/node/tcp-socket.h @@ -59,8 +59,6 @@ private: virtual uint32_t GetRcvBufSize (void) const = 0; virtual void SetSegSize (uint32_t size) = 0; virtual uint32_t GetSegSize (void) const = 0; - virtual void SetAdvWin (uint32_t window) = 0; - virtual uint32_t GetAdvWin (void) const = 0; virtual void SetSSThresh (uint32_t threshold) = 0; virtual uint32_t GetSSThresh (void) const = 0; virtual void SetInitialCwnd (uint32_t count) = 0; From 61947e25a7c0f64b91b5032fbd5d3b932b47968e Mon Sep 17 00:00:00 2001 From: Raj Bhattacharjea Date: Sun, 7 Sep 2008 21:58:50 -0400 Subject: [PATCH 3/4] Update CHANGES.html to reflect the last two changesets --- CHANGES.html | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/CHANGES.html b/CHANGES.html index 974f6de38..d4d549e71 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -217,6 +217,63 @@ Rename all instances method names using "Set..Parameter" to "Set..Attribute"

changed behavior:

    + +
  • 07-09-2008; changeset +5d836ab1523b
  • +
      +
    • +Implement a finite receive buffer for TCP
      +The native TCP model in TcpSocketImpl did not support a finite receive buffer. +This changeset adds the following functionality in this regard: +
        +
      • +Being able to set the receiver buffer size through the attributes system. +
      • +
      • +This receiver buffer size is now correctly exported in the TCP header as the +advertised window. Prior to this changeset, the TCP header advertised window +was set to the maximum size of 2^16 bytes. +window +
      • +
      • +The aforementioned window size is correctly used for flow control, i.e. the +sending TCP will not send more data than available space in the receiver's +buffer. +
      • +
      • +In the case of a receiver window collapse, when a advertised zero-window +packet is received, the sender enters the persist probing state in which +it sends probe packets with one payload byte at exponentially backed-off +intervals up to 60s. The reciever will continue to send advertised +zero-window ACKs of the old data so long as the receiver buffer remains full. +When the receiver window clears up due to an application read, the TCP +will finally ACK the probe byte, and update its advertised window appropriately. +
      • +
      + +See + bug 239 for +more. +
    • +
    + + +
  • 07-09-2008; changeset +7afa66c2b291
  • +
      +
    • +Add correct FIN exchange behavior during TCP closedown
      +The behavior of the native TcpSocketImpl TCP model was such that the final +FIN exchange was not correct, i.e. calling Socket::Close didn't send a FIN +packet, and even if it had, the ACK never came back, and even if it had, the +ACK would have incorrect sequence number. All these various problems have been +addressed by this changeset. See + bug 242 for +more. +
    • +
    + +
  • 28-07-2008; changeset 6f68f1044df1
      From ae217b0eaa182247325eb7114bd47e35142e6662 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 7 Sep 2008 20:13:51 -0700 Subject: [PATCH 4/4] Don't build realtime simulator if required core components aren't built --- src/simulator/wscript | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/simulator/wscript b/src/simulator/wscript index a30e30bb3..bd57d4397 100644 --- a/src/simulator/wscript +++ b/src/simulator/wscript @@ -44,8 +44,6 @@ def configure(conf): conf.write_config_header('ns3/simulator-config.h') - - def build(bld): sim = bld.create_ns3_module('simulator', ['core']) sim.source = [ @@ -59,11 +57,8 @@ def build(bld): 'event-impl.cc', 'simulator.cc', 'default-simulator-impl.cc', - 'realtime-simulator-impl.cc', 'timer.cc', 'watchdog.cc', - 'synchronizer.cc', - 'wall-clock-synchronizer.cc', ] headers = bld.create_obj('ns3header') @@ -89,6 +84,13 @@ def build(bld): 'wall-clock-synchronizer.h', ] + if sys.platform != 'win32': + sim.source.extend([ + 'realtime-simulator-impl.cc', + 'synchronizer.cc', + 'wall-clock-synchronizer.cc', + ]) + env = bld.env_of_name('default') if env['USE_HIGH_PRECISION_DOUBLE']: sim.source.extend([