This commit is contained in:
Gustavo J. A. M. Carneiro
2008-09-08 12:35:08 +01:00
10 changed files with 398 additions and 353 deletions

View File

@@ -217,6 +217,63 @@ Rename all instances method names using "Set..Parameter" to "Set..Attribute"
<h2>changed behavior:</h2>
<ul>
<li>07-09-2008; changeset
<a href="http://code.nsnam.org/ns-3-dev/rev/5d836ab1523b">5d836ab1523b</a></li>
<ul>
<li>
Implement a finite receive buffer for TCP<br>
The native TCP model in TcpSocketImpl did not support a finite receive buffer.
This changeset adds the following functionality in this regard:
<ul>
<li>
Being able to set the receiver buffer size through the attributes system.
</li>
<li>
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
</li>
<li>
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.
</li>
<li>
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.
</li>
</ul>
See
<a href="http://www.nsnam.org/bugzilla/show_bug.cgi?id=239"> bug 239 </a> for
more.
</li>
</ul>
</li>
<li>07-09-2008; changeset
<a href="http://code.nsnam.org/ns-3-dev/rev/7afa66c2b291">7afa66c2b291</a></li>
<ul>
<li>
Add correct FIN exchange behavior during TCP closedown<br>
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
<a href="http://www.nsnam.org/bugzilla/show_bug.cgi?id=242"> bug 242 </a> for
more.
</li>
</ul>
</li>
<li> 28-07-2008; changeset
<a href="http://code.nsnam.org/ns-3-dev/rev/6f68f1044df1">6f68f1044df1</a>
<ul>

View File

@@ -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 <ctype.h>
#include <iostream>
#include <fstream>
#include <string>
#include <cassert>
#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<RateErrorModel> em1 =
CreateObject<RateErrorModel> ("RanVar", u01, "ErrorRate", rate);
Ptr<RateErrorModel> em2 =
CreateObject<RateErrorModel> ("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 ();
}

View File

@@ -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

View File

@@ -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'

View File

@@ -131,7 +131,7 @@ Ptr<Packet> 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<Packet> (); // No data requested
}
if (data.size() != 0)
{ // Actual data exists, make copy and return it

View File

@@ -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> 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> 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<TcpStateMachine>::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<Packet> p = Create<Packet> ();
TcpHeader header;
@@ -641,17 +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 ();
if (flags & TcpHeader::SYN)
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 () ) //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 "
@@ -745,6 +764,13 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> p,
Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
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 +793,20 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> 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");
@@ -790,7 +817,6 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> 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 ();
@@ -808,13 +834,17 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> 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;
}
@@ -844,6 +874,7 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> 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 +909,7 @@ bool TcpSocketImpl::ProcessPacketAction (Actions_t a, Ptr<Packet> p,
NotifyNewConnectionCreated (this, fromAddress);
break;
default:
break;
return ProcessAction (a);
}
return true;
}
@@ -928,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
@@ -961,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;
@@ -971,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);
@@ -1024,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<Packet> p,
const TcpHeader& tcpHeader,
const Address& fromAddress)
@@ -1039,9 +1082,20 @@ void TcpSocketImpl::NewRx (Ptr<Packet> 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
@@ -1074,14 +1128,12 @@ void TcpSocketImpl::NewRx (Ptr<Packet> 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)
{
@@ -1181,6 +1233,7 @@ void TcpSocketImpl::NewRx (Ptr<Packet> p,
// Save for later delivery
m_bufferedData[start] = p;
m_rxAvailable += p->GetSize ();
m_rxBufSize += p->GetSize();
RxBufFinishInsert(start);
NotifyDataRecv ();
}
@@ -1248,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 "
<<Simulator::Now ().GetSeconds () << " to expire at time "
<< (Simulator::Now () + m_persistTime).GetSeconds());
m_persistEvent =
Simulator::Schedule (m_persistTime, &TcpSocketImpl::PersistTimeout, this);
NS_ASSERT (m_persistTime == Simulator::GetDelayLeft (m_persistEvent));
}
NS_LOG_LOGIC ("TCP " << this << " NewAck " << ack
<< " numberAck " << (ack - m_highestRxAck)); // Number bytes ack'ed
@@ -1276,6 +1347,9 @@ void TcpSocketImpl::CommonNewAck (SequenceNumber ack, bool skipTimer)
delete m_pendingData;
m_pendingData = 0;
// Insure no re-tx timer
NS_LOG_LOGIC (this<<" Cancelled ReTxTimeout event which was set to expire at "
<< (Simulator::Now () +
Simulator::GetDelayLeft (m_retxEvent)).GetSeconds());
m_retxEvent.Cancel ();
}
}
@@ -1341,6 +1415,7 @@ void TcpSocketImpl::DupAck (const TcpHeader& t, uint32_t count)
void TcpSocketImpl::ReTxTimeout ()
{ // Retransmit timeout
NS_LOG_FUNCTION (this);
NS_LOG_LOGIC (this<<" ReTxTimeout Expired at time "<<Simulator::Now ().GetSeconds());
m_ssThresh = Window () / 2; // Per RFC2581
m_ssThresh = std::max (m_ssThresh, 2 * m_segmentSize);
// Set cWnd to segSize on timeout, per rfc2581
@@ -1365,6 +1440,31 @@ void TcpSocketImpl::LastAckTimeout ()
}
}
void TcpSocketImpl::PersistTimeout ()
{
NS_LOG_LOGIC ("PersistTimeout expired at "<<Simulator::Now ().GetSeconds ());
m_persistTime = Scalar(2)*m_persistTime;
m_persistTime = std::min(Seconds(60),m_persistTime); //maxes out at 60
//the persist timeout sends exactly one byte probes
//this is explicit in stevens, and kind of in rfc793 p42, rfc1122 sec4.2.2.17
Ptr<Packet> 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 "
<<Simulator::Now ().GetSeconds () << " to expire at time "
<< (Simulator::Now () + m_persistTime).GetSeconds());
m_persistEvent =
Simulator::Schedule (m_persistTime, &TcpSocketImpl::PersistTimeout, this);
}
void TcpSocketImpl::Retransmit ()
{
NS_LOG_FUNCTION (this);
@@ -1390,9 +1490,10 @@ void TcpSocketImpl::Retransmit ()
}
return;
}
NS_ASSERT(m_nextTxSequence == m_highestRxAck);
Ptr<Packet> 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,
@@ -1406,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);
@@ -1419,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);
@@ -1440,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
@@ -1461,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)
{
@@ -1556,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 <string>
@@ -1567,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<Node> CreateInternetNode ();
Ptr<SimpleNetDevice> AddSimpleNetDevice (Ptr<Node>,const char*,const char*);
//test 1, which sends string "Hello world" server->client
void Test1 (void);
void Test1_HandleConnectionCreated (Ptr<Socket>, const Address &);
void Test1_HandleRecv (Ptr<Socket> sock);
//test 2, which sends a number of bytes server->client
void Test2 (uint32_t payloadSize);
void Test2_HandleConnectionCreated (Ptr<Socket>, const Address &);
void Test2_HandleRecv (Ptr<Socket> sock);
uint32_t test2_payloadSize;
//test 3, which makes sure the rx buffer is finite
void Test3 (uint32_t payloadSize);
void Test3_HandleConnectionCreated (Ptr<Socket>, const Address &);
void Test3_HandleRecv (Ptr<Socket> sock);
uint32_t test3_payloadSize;
//helpers to make topology construction easier
Ptr<Node> CreateInternetNode ();
Ptr<SimpleNetDevice> AddSimpleNetDevice (Ptr<Node>,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<Node> node0;
Ptr<Node> node1;
Ptr<SimpleNetDevice> dev0;
@@ -1591,9 +1695,9 @@ class TcpSocketImplTest: public Test
Ptr<Socket> sock1;
uint32_t rxBytes0;
uint32_t rxBytes1;
uint8_t* rxPayload;
bool result;
};
@@ -1610,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<SimpleChannel> ();
dev0->SetChannel (channel);
dev1->SetChannel (channel);
Ptr<SocketFactory> sockFactory0 = node0->GetObject<TcpSocketFactory> ();
Ptr<SocketFactory> sockFactory1 = node1->GetObject<TcpSocketFactory> ();
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<bool, Ptr< Socket >, const Address &> (),
MakeCallback(&TcpSocketImplTest::Test1_HandleConnectionCreated,this));
sock1->Connect(serverremoteaddr);
(MakeNullCallback<bool, Ptr< Socket >, 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<SimpleChannel> ();
dev0->SetChannel (channel);
dev1->SetChannel (channel);
Ptr<SocketFactory> sockFactory0 = node0->GetObject<TcpSocketFactory> ();
Ptr<SocketFactory> sockFactory1 = node1->GetObject<TcpSocketFactory> ();
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<bool, Ptr< Socket >, 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<Node>
TcpSocketImplTest::CreateInternetNode ()
{
Ptr<Node> node = CreateObject<Node> ();
AddInternetStack (node);
return node;
}
Ptr<SimpleNetDevice>
TcpSocketImplTest::AddSimpleNetDevice (Ptr<Node> node, const char* ipaddr, const char* netmask)
{
Ptr<SimpleNetDevice> dev = CreateObject<SimpleNetDevice> ();
dev->SetAddress (Mac48Address::Allocate ());
node->AddDevice (dev);
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
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<Socket> s, const Address & addr)
{
@@ -1753,6 +1773,27 @@ TcpSocketImplTest::Test1_HandleRecv (Ptr<Socket> sock)
}
}
//-----------------------------------------------------------------------------
//test 2-----------------------------------------------------------------------
//-----------------------------------------------------------------------------
void
TcpSocketImplTest::Test2 (uint32_t payloadSize)
{
test2_payloadSize = payloadSize;
SetupDefaultSim ();
listeningSock->SetAcceptCallback
(MakeNullCallback<bool, Ptr< Socket >, 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<Socket> s, const Address & addr)
{
@@ -1777,10 +1818,108 @@ TcpSocketImplTest::Test2_HandleRecv (Ptr<Socket> sock)
}
else
{
NS_FATAL_ERROR ("Recv from unknown socket "<<sock);
NS_FATAL_ERROR ("Not supposed to be back traffic in test 2..."<<sock);
}
}
//-----------------------------------------------------------------------------
//test 3-----------------------------------------------------------------------
//-----------------------------------------------------------------------------
void
TcpSocketImplTest::Test3 (uint32_t payloadSize)
{
Config::SetDefault ("ns3::TcpSocket::RcvBufSize", UintegerValue (10000));
test3_payloadSize = payloadSize;
SetupDefaultSim ();
listeningSock->SetAcceptCallback
(MakeNullCallback<bool, Ptr< Socket >, 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<Socket> s, const Address &)
{
NS_ASSERT(s != listeningSock);
NS_ASSERT(sock0 == 0);
sock0 = s;
Ptr<Packet> p = Create<Packet> (test3_payloadSize);
sock0->Send(p);
}
void
TcpSocketImplTest::Test3_HandleRecv (Ptr<Socket> 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<Packet> p = sock->Recv();
uint32_t sz = p->GetSize();
rxBytes1 += sz;
}
}
//-----------------------------------------------------------------------------
//helpers----------------------------------------------------------------------
//-----------------------------------------------------------------------------
Ptr<Node>
TcpSocketImplTest::CreateInternetNode ()
{
Ptr<Node> node = CreateObject<Node> ();
AddInternetStack (node);
return node;
}
Ptr<SimpleNetDevice>
TcpSocketImplTest::AddSimpleNetDevice (Ptr<Node> node, const char* ipaddr, const char* netmask)
{
Ptr<SimpleNetDevice> dev = CreateObject<SimpleNetDevice> ();
dev->SetAddress (Mac48Address::Allocate ());
node->AddDevice (dev);
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
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<SimpleChannel> ();
dev0->SetChannel (channel);
dev1->SetChannel (channel);
Ptr<SocketFactory> sockFactory0 = node0->GetObject<TcpSocketFactory> ();
Ptr<SocketFactory> sockFactory1 = node1->GetObject<TcpSocketFactory> ();
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 ()
{

View File

@@ -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<Packet>, 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<uint32_t> 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<RttEstimator> 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

View File

@@ -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

View File

@@ -55,12 +55,6 @@ TcpSocket::GetTypeId (void)
MakeUintegerAccessor (&TcpSocket::GetSegSize,
&TcpSocket::SetSegSize),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("AdvertisedWindowSize",
"TCP advertised window size (bytes)",
UintegerValue (0xffff),
MakeUintegerAccessor (&TcpSocket::GetAdvWin,
&TcpSocket::SetAdvWin),
MakeUintegerChecker<uint32_t> ())
.AddAttribute ("SlowStartThreshold",
"TCP slow start threshold (bytes)",
UintegerValue (0xffff),

View File

@@ -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;