Added a GeneralTest class for TCP

The class provides a general infrastructure for a TCP-related test,
setting up the environment (two sockets connected through
SimpleNetDevice over SimpleChannel) and provides the basic management of
parameters (like segment size).
This commit is contained in:
Natale Patriciello
2015-10-16 10:43:36 -07:00
parent 8350087b48
commit 157fec86d9
4 changed files with 1557 additions and 1 deletions

View File

@@ -245,6 +245,9 @@ public:
* \return the object TypeId
*/
static TypeId GetTypeId (void);
friend class TcpGeneralTest;
/**
* Create an unbound TCP socket
*/
@@ -559,7 +562,7 @@ protected:
*
* \param flags the packet's flags
*/
void SendEmptyPacket (uint8_t flags);
virtual void SendEmptyPacket (uint8_t flags);
/**
* \brief Send reset and tear down this socket

View File

@@ -0,0 +1,884 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
*
* 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
*
*/
#include "ns3/test.h"
#include "ns3/node-container.h"
#include "ns3/tcp-socket-base.h"
#include "ns3/simple-net-device-helper.h"
#include "ns3/ipv4-address-helper.h"
#include "ns3/internet-stack-helper.h"
#include "ns3/log.h"
#include "tcp-general-test.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpGeneralTest");
TcpGeneralTest::TcpGeneralTest (const std::string &desc,
uint32_t pktSize, uint32_t pktCount,
const Time& pktInterval,
const Time& propagationDelay, const Time& startTime,
uint32_t initialSlowStartThresh, uint32_t initialCwnd,
uint32_t segmentSize,
TypeId congestionControl,
uint32_t mtu)
: TestCase (desc),
m_congControlTypeId (congestionControl),
m_propagationDelay (propagationDelay),
m_startTime (startTime),
m_mtu (mtu),
m_pktSize (pktSize),
m_pktCount (pktCount),
m_interPacketInterval (pktInterval),
m_initialSlowStartThresh (initialSlowStartThresh),
m_initialCwnd (initialCwnd),
m_segmentSize (segmentSize)
{
NS_LOG_FUNCTION (this << desc);
}
TcpGeneralTest::~TcpGeneralTest ()
{
NS_LOG_FUNCTION_NOARGS ();
}
void
TcpGeneralTest::ReceivePacket (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet;
Address from;
while ((packet = socket->RecvFrom (from)))
{
if (packet->GetSize () == 0)
{ //EOF
break;
}
}
}
void
TcpGeneralTest::SendPacket (Ptr<Socket> socket, uint32_t pktSize,
uint32_t pktCount, Time pktInterval )
{
NS_LOG_FUNCTION (this << " " << pktSize << " " << pktCount << " " <<
pktInterval.GetSeconds ());
if (pktCount > 0)
{
socket->Send (Create<Packet> (pktSize));
Simulator::Schedule (pktInterval, &TcpGeneralTest::SendPacket, this,
socket, pktSize, pktCount - 1, pktInterval);
}
else
{
socket->Close ();
}
}
void
TcpGeneralTest::DoTeardown (void)
{
FinalChecks ();
Simulator::Destroy ();
NS_LOG_INFO ("Done.");
}
void
TcpGeneralTest::DoRun (void)
{
NS_LOG_INFO ("Create nodes.");
NodeContainer nodes;
nodes.Create (2);
InternetStackHelper internet;
internet.Install (nodes);
Packet::EnablePrinting ();
Ptr<SimpleChannel> channel = CreateChannel ();
SimpleNetDeviceHelper helperChannel;
helperChannel.SetNetDevicePointToPointMode (true);
NetDeviceContainer net = helperChannel.Install (nodes, channel);
Ptr<ErrorModel> receiverEM = CreateReceiverErrorModel ();
Ptr<ErrorModel> senderEM = CreateSenderErrorModel ();
Ptr<SimpleNetDevice> senderDev = DynamicCast<SimpleNetDevice> (net.Get (0));
Ptr<SimpleNetDevice> receiverDev = DynamicCast<SimpleNetDevice> (net.Get (1));
senderDev->SetMtu (m_mtu);
senderDev->GetQueue ()->TraceConnect ("Drop", "SENDER",
MakeCallback (&TcpGeneralTest::QueueDropCb, this));
senderDev->TraceConnect ("PhyRxDrop", "sender",
MakeCallback (&TcpGeneralTest::PhyDropCb, this));
receiverDev->SetMtu (m_mtu);
receiverDev->GetQueue ()->TraceConnect ("Drop", "RECEIVER",
MakeCallback (&TcpGeneralTest::QueueDropCb, this));
receiverDev->TraceConnect ("PhyRxDrop", "RECEIVER",
MakeCallback (&TcpGeneralTest::PhyDropCb, this));
senderDev->SetReceiveErrorModel (senderEM);
receiverDev->SetReceiveErrorModel (receiverEM);
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer i = ipv4.Assign (net);
Ipv4Address serverAddress = i.GetAddress (1);
//Ipv4Address clientAddress = i.GetAddress (0);
NS_LOG_INFO ("Create sockets.");
//Receiver socket on n1
m_receiverSocket = CreateReceiverSocket (nodes.Get (1));
m_receiverSocket->SetRecvCallback (MakeCallback (&TcpGeneralTest::ReceivePacket, this));
m_receiverSocket->SetAcceptCallback (
MakeNullCallback<bool, Ptr<Socket>, const Address &> (),
MakeCallback (&TcpGeneralTest::HandleAccept, this));
m_receiverSocket->SetCloseCallbacks (MakeCallback (&TcpGeneralTest::NormalCloseCb, this),
MakeCallback (&TcpGeneralTest::ErrorCloseCb, this));
m_receiverSocket->SetRcvAckCb (MakeCallback (&TcpGeneralTest::RcvAckCb, this));
m_receiverSocket->SetProcessedAckCb (MakeCallback (&TcpGeneralTest::ProcessedAckCb, this));
m_receiverSocket->SetRetransmitCb (MakeCallback (&TcpGeneralTest::RtoExpiredCb, this));
m_receiverSocket->SetForkCb (MakeCallback (&TcpGeneralTest::ForkCb, this));
m_receiverSocket->TraceConnectWithoutContext ("Tx",
MakeCallback (&TcpGeneralTest::TxPacketCb, this));
m_receiverSocket->TraceConnectWithoutContext ("Rx",
MakeCallback (&TcpGeneralTest::RxPacketCb, this));
InetSocketAddress local = InetSocketAddress (Ipv4Address::GetAny (), 4477);
m_receiverSocket->Bind (local);
m_receiverSocket->Listen ();
m_receiverSocket->ShutdownSend ();
m_senderSocket = CreateSenderSocket (nodes.Get (0));
m_senderSocket->SetCloseCallbacks (MakeCallback (&TcpGeneralTest::NormalCloseCb, this),
MakeCallback (&TcpGeneralTest::ErrorCloseCb, this));
m_senderSocket->SetRcvAckCb (MakeCallback (&TcpGeneralTest::RcvAckCb, this));
m_senderSocket->SetProcessedAckCb (MakeCallback (&TcpGeneralTest::ProcessedAckCb, this));
m_senderSocket->SetRetransmitCb (MakeCallback (&TcpGeneralTest::RtoExpiredCb, this));
m_senderSocket->TraceConnectWithoutContext ("CongestionWindow",
MakeCallback (&TcpGeneralTest::CWndTrace, this));
m_senderSocket->TraceConnectWithoutContext ("AckState",
MakeCallback (&TcpGeneralTest::AckStateTrace, this));
m_senderSocket->TraceConnectWithoutContext ("Tx",
MakeCallback (&TcpGeneralTest::TxPacketCb, this));
m_senderSocket->TraceConnectWithoutContext ("Rx",
MakeCallback (&TcpGeneralTest::RxPacketCb, this));
InetSocketAddress remoteAddr = InetSocketAddress (serverAddress, 4477);
m_senderSocket->Connect (remoteAddr);
Simulator::ScheduleWithContext (nodes.Get (0)->GetId (),
m_startTime, &TcpGeneralTest::SendPacket, this,
m_senderSocket, m_pktSize, m_pktCount, m_interPacketInterval);
NS_LOG_INFO ("Run Simulation.");
Simulator::Run ();
}
void
TcpGeneralTest::HandleAccept (Ptr<Socket> socket, const Address& from)
{
(void) from;
socket->SetRecvCallback (MakeCallback (&TcpGeneralTest::ReceivePacket, this));
socket->SetCloseCallbacks (MakeCallback (&TcpGeneralTest::NormalCloseCb, this),
MakeCallback (&TcpGeneralTest::ErrorCloseCb, this));
}
Ptr<SimpleChannel>
TcpGeneralTest::CreateChannel ()
{
Ptr<SimpleChannel> ch = CreateObject <SimpleChannel> ();
ch->SetAttribute ("Delay", TimeValue (m_propagationDelay));
return ch;
}
Ptr<TcpSocketMsgBase>
TcpGeneralTest::CreateSocket (Ptr<Node> node, TypeId socketType,
TypeId congControl)
{
ObjectFactory rttFactory;
ObjectFactory congestionAlgorithmFactory;
ObjectFactory socketFactory;
rttFactory.SetTypeId (RttMeanDeviation::GetTypeId ());
congestionAlgorithmFactory.SetTypeId (congControl);
socketFactory.SetTypeId (socketType);
Ptr<RttEstimator> rtt = rttFactory.Create<RttEstimator> ();
Ptr<TcpSocketMsgBase> socket = socketFactory.Create <TcpSocketMsgBase> ();
Ptr<TcpCongestionOps> algo = congestionAlgorithmFactory.Create<TcpCongestionOps> ();
socket->SetNode (node);
socket->SetTcp (node->GetObject<TcpL4Protocol> ());
socket->SetRtt (rtt);
socket->SetCongestionControlAlgorithm (algo);
socket->SetAttribute ("InitialSlowStartThreshold", UintegerValue (m_initialSlowStartThresh));
socket->SetAttribute ("SegmentSize", UintegerValue (m_segmentSize));
socket->SetAttribute ("InitialCwnd", UintegerValue (m_initialCwnd));
return socket;
}
Ptr<ErrorModel>
TcpGeneralTest::CreateSenderErrorModel ()
{
return 0;
}
Ptr<ErrorModel>
TcpGeneralTest::CreateReceiverErrorModel ()
{
return 0;
}
Ptr<TcpSocketMsgBase>
TcpGeneralTest::CreateSenderSocket (Ptr<Node> node)
{
return CreateSocket (node, TcpSocketMsgBase::GetTypeId (), m_congControlTypeId);
}
Ptr<TcpSocketMsgBase>
TcpGeneralTest::CreateReceiverSocket (Ptr<Node> node)
{
return CreateSocket (node, TcpSocketMsgBase::GetTypeId (), m_congControlTypeId);
}
void
TcpGeneralTest::QueueDropCb ( std::string context, Ptr<const Packet> p)
{
if (context.compare ("SENDER") == 0)
{
QueueDrop (SENDER);
}
else if (context.compare ("RECEIVER") == 0)
{
QueueDrop (RECEIVER);
}
else
{
NS_FATAL_ERROR ("Packet dropped in a queue, but queue not recognized");
}
}
void
TcpGeneralTest::PhyDropCb (std::string context, Ptr<const Packet> p)
{
if (context.compare ("SENDER") == 0)
{
PhyDrop (SENDER);
}
else if (context.compare ("RECEIVER") == 0)
{
PhyDrop (RECEIVER);
}
else
{
NS_FATAL_ERROR ("Packet dropped in a queue, but queue not recognized");
}
}
void
TcpGeneralTest::NormalCloseCb (Ptr<Socket> socket)
{
if (socket->GetNode () == m_receiverSocket->GetNode ())
{
NormalClose (RECEIVER);
}
else if (socket->GetNode () == m_senderSocket->GetNode ())
{
NormalClose (SENDER);
}
else
{
NS_FATAL_ERROR ("Closed socket, but not recognized");
}
}
void
TcpGeneralTest::RtoExpiredCb (const Ptr<const TcpSocketState> tcb,
const Ptr<const TcpSocketBase> tcp)
{
if (tcp->GetNode () == m_receiverSocket->GetNode ())
{
RTOExpired (tcb, RECEIVER);
}
else if (tcp->GetNode () == m_senderSocket->GetNode ())
{
RTOExpired (tcb, SENDER);
}
else
{
NS_FATAL_ERROR ("Closed socket, but not recognized");
}
}
void
TcpGeneralTest::ErrorCloseCb (Ptr<Socket> socket)
{
if (socket->GetNode () == m_receiverSocket->GetNode ())
{
ErrorClose (RECEIVER);
}
else if (socket->GetNode () == m_senderSocket->GetNode ())
{
ErrorClose (SENDER);
}
else
{
NS_FATAL_ERROR ("Closed socket, but not recognized");
}
}
void
TcpGeneralTest::RcvAckCb (const Ptr<const Packet> p, const TcpHeader& h,
const Ptr<const TcpSocketBase> tcp)
{
if (tcp->GetNode () == m_receiverSocket->GetNode ())
{
RcvAck (tcp->m_tcb, h, RECEIVER);
}
else if (tcp->GetNode () == m_senderSocket->GetNode ())
{
RcvAck (tcp->m_tcb, h, SENDER);
}
else
{
NS_FATAL_ERROR ("Received ACK but socket not recognized");
}
}
void
TcpGeneralTest::TxPacketCb (const Ptr<const Packet> p,
const TcpHeader &h, const Ptr<const TcpSocketBase> tcp)
{
if (tcp->GetNode () == m_receiverSocket->GetNode ())
{
Tx (p, h, RECEIVER);
}
else if (tcp->GetNode () == m_senderSocket->GetNode ())
{
Tx (p, h, SENDER);
}
else
{
NS_FATAL_ERROR ("Received ACK but socket not recognized");
}
}
void
TcpGeneralTest::RxPacketCb (const Ptr<const Packet> p, const TcpHeader &h,
const Ptr<const TcpSocketBase> tcp)
{
if (tcp->GetNode () == m_receiverSocket->GetNode ())
{
Rx (p, h, RECEIVER);
}
else if (tcp->GetNode () == m_senderSocket->GetNode ())
{
Rx (p, h, SENDER);
}
else
{
NS_FATAL_ERROR ("Received ACK but socket not recognized");
}
}
void
TcpGeneralTest::ProcessedAckCb (Ptr<const Packet> p, const TcpHeader& h,
Ptr<const TcpSocketBase> tcp)
{
if (tcp->GetNode () == m_receiverSocket->GetNode ())
{
ProcessedAck (tcp->m_tcb, h, RECEIVER);
}
else if (tcp->GetNode () == m_senderSocket->GetNode ())
{
ProcessedAck (tcp->m_tcb, h, SENDER);
}
else
{
NS_FATAL_ERROR ("Received ACK but socket not recognized");
}
}
void
TcpGeneralTest::ForkCb (Ptr<TcpSocketMsgBase> tcp)
{
NS_LOG_FUNCTION (this << tcp);
m_receiverSocket = tcp;
}
uint32_t
TcpGeneralTest::GetReTxThreshold (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_retxThresh;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_retxThresh;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
uint32_t
TcpGeneralTest::GetDupAckCount (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_dupAckCount;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_dupAckCount;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
uint32_t
TcpGeneralTest::GetDelAckCount (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_delAckMaxCount;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_delAckMaxCount;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Time
TcpGeneralTest::GetDelAckTimeout(SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->GetDelAckTimeout ();
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->GetDelAckTimeout ();
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
uint32_t
TcpGeneralTest::GetSegSize (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->GetSegSize ();
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->GetSegSize ();
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Time
TcpGeneralTest::GetRto (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_rto.Get ();
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_rto.Get ();
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Time
TcpGeneralTest::GetMinRto (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_minRto;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_minRto;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Time
TcpGeneralTest::GetConnTimeout (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_cnTimeout;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_cnTimeout;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Ptr<RttEstimator>
TcpGeneralTest::GetRttEstimator (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_rtt;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_rtt;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
Time
TcpGeneralTest::GetClockGranularity (SocketWho who)
{
if (who == SENDER)
{
return DynamicCast<TcpSocketMsgBase> (m_senderSocket)->m_clockGranularity;
}
else if (who == RECEIVER)
{
return DynamicCast<TcpSocketMsgBase> (m_receiverSocket)->m_clockGranularity;
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
void
TcpGeneralTest::SetRcvBufSize (SocketWho who, uint32_t size)
{
if (who == SENDER)
{
m_senderSocket->SetRcvBufSize (size);
}
else if (who == RECEIVER)
{
m_receiverSocket->SetRcvBufSize (size);
}
else
{
NS_FATAL_ERROR ("Not defined");
}
}
NS_OBJECT_ENSURE_REGISTERED (TcpSocketMsgBase);
TypeId
TcpSocketMsgBase::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpSocketMsgBase")
.SetParent<TcpSocketBase> ()
.SetGroupName ("Internet")
.AddConstructor<TcpSocketMsgBase> ()
;
return tid;
}
Ptr<TcpSocketBase>
TcpSocketMsgBase::Fork (void)
{
return CopyObject<TcpSocketMsgBase> (this);
}
void
TcpSocketMsgBase::SetRcvAckCb (AckManagementCallback cb)
{
NS_ASSERT (!cb.IsNull ());
m_rcvAckCb = cb;
}
void
TcpSocketMsgBase::SetProcessedAckCb (AckManagementCallback cb)
{
NS_ASSERT (!cb.IsNull ());
m_processedAckCb = cb;
}
void
TcpSocketMsgBase::SetRetransmitCb (RetrCallback cb)
{
NS_ASSERT (!cb.IsNull ());
m_retrCallback = cb;
}
void
TcpSocketMsgBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
{
NS_ASSERT (!(m_rcvAckCb.IsNull () || m_processedAckCb.IsNull ()));
m_rcvAckCb (packet, tcpHeader, this);
TcpSocketBase::ReceivedAck (packet, tcpHeader);
m_processedAckCb (packet, tcpHeader, this);
}
void
TcpSocketMsgBase::Retransmit ()
{
TcpSocketBase::Retransmit ();
m_retrCallback (m_tcb, this);
}
void
TcpSocketMsgBase::SetForkCb (Callback<void, Ptr<TcpSocketMsgBase> > cb)
{
NS_ASSERT (!cb.IsNull ());
m_forkCb = cb;
}
void
TcpSocketMsgBase::CompleteFork (Ptr<Packet> p, const TcpHeader &tcpHeader,
const Address &fromAddress, const Address &toAddress)
{
TcpSocketBase::CompleteFork (p, tcpHeader, fromAddress, toAddress);
if (!m_forkCb.IsNull ())
{
m_forkCb (this);
}
}
NS_OBJECT_ENSURE_REGISTERED (TcpSocketSmallAcks);
TypeId
TcpSocketSmallAcks::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::TcpSocketSmallAcks")
.SetParent<TcpSocketMsgBase> ()
.SetGroupName ("Internet")
.AddConstructor<TcpSocketSmallAcks> ()
;
return tid;
}
/**
* \brief Send empty packet, copied/pasted from TcpSocketBase
*
* The rationale for copying/pasting is that we need to edit a little the
* code inside. Since there isn't a well-defined division of duties,
* we are forced to do this.
*/
void
TcpSocketSmallAcks::SendEmptyPacket (uint8_t flags)
{
Ptr<Packet> p = Create<Packet> ();
TcpHeader header;
SequenceNumber32 s = m_nextTxSequence;
/*
* Add tags for each socket option.
* Note that currently the socket adds both IPv4 tag and IPv6 tag
* if both options are set. Once the packet got to layer three, only
* the corresponding tags will be read.
*/
if (IsManualIpTos ())
{
SocketIpTosTag ipTosTag;
ipTosTag.SetTos (GetIpTos ());
p->AddPacketTag (ipTosTag);
}
if (IsManualIpv6Tclass ())
{
SocketIpv6TclassTag ipTclassTag;
ipTclassTag.SetTclass (GetIpv6Tclass ());
p->AddPacketTag (ipTclassTag);
}
if (IsManualIpTtl ())
{
SocketIpTtlTag ipTtlTag;
ipTtlTag.SetTtl (GetIpTtl ());
p->AddPacketTag (ipTtlTag);
}
if (IsManualIpv6HopLimit ())
{
SocketIpv6HopLimitTag ipHopLimitTag;
ipHopLimitTag.SetHopLimit (GetIpv6HopLimit ());
p->AddPacketTag (ipHopLimitTag);
}
if (m_endPoint == 0 && m_endPoint6 == 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;
}
bool hasSyn = flags & TcpHeader::SYN;
bool hasFin = flags & TcpHeader::FIN;
bool isAck = flags == TcpHeader::ACK;
header.SetFlags (flags);
header.SetSequenceNumber (s);
// Actual division in small acks.
if (hasSyn || hasFin)
{
header.SetAckNumber (m_rxBuffer->NextRxSequence ());
}
else
{
SequenceNumber32 ackSeq;
ackSeq = m_lastAckedSeq + m_bytesToAck;
if (m_bytesLeftToBeAcked == 0 && m_rxBuffer->NextRxSequence () > m_lastAckedSeq)
{
m_bytesLeftToBeAcked = m_rxBuffer->NextRxSequence ().GetValue () - 1 - m_bytesToAck;
}
else if (m_bytesLeftToBeAcked > 0 && m_rxBuffer->NextRxSequence () > m_lastAckedSeq)
{
m_bytesLeftToBeAcked -= m_bytesToAck;
}
NS_LOG_LOGIC ("Acking up to " << ackSeq << " remaining bytes: " << m_bytesLeftToBeAcked);
header.SetAckNumber (ackSeq);
m_lastAckedSeq = ackSeq;
}
// end of division in small acks
if (m_endPoint != 0)
{
header.SetSourcePort (m_endPoint->GetLocalPort ());
header.SetDestinationPort (m_endPoint->GetPeerPort ());
}
else
{
header.SetSourcePort (m_endPoint6->GetLocalPort ());
header.SetDestinationPort (m_endPoint6->GetPeerPort ());
}
AddOptions (header);
header.SetWindowSize (AdvertisedWindowSize ());
// RFC 6298, clause 2.4
m_rto = Max (m_rtt->GetEstimate () + Max (m_clockGranularity, m_rtt->GetVariation () * 4), m_minRto);
if (hasSyn)
{
if (m_synCount == 0)
{ // No more connection retries, give up
NS_LOG_LOGIC ("Connection failed.");
m_rtt->Reset (); //According to recommendation -> RFC 6298
CloseAndNotify ();
return;
}
else
{ // Exponential backoff of connection time out
int backoffCount = 0x1 << (m_synRetries - m_synCount);
m_rto = m_cnTimeout * backoffCount;
m_synCount--;
}
}
if (m_endPoint != 0)
{
m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (),
m_endPoint->GetPeerAddress (), m_boundnetdevice);
}
else
{
m_tcp->SendPacket (p, header, m_endPoint6->GetLocalAddress (),
m_endPoint6->GetPeerAddress (), m_boundnetdevice);
}
m_txTrace (p, header, this);
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, &TcpSocketSmallAcks::SendEmptyPacket, this, flags);
}
// send another ACK if bytes remain
if (m_bytesLeftToBeAcked > 0 && m_rxBuffer->NextRxSequence () > m_lastAckedSeq)
{
SendEmptyPacket (flags);
}
}
Ptr<TcpSocketBase>
TcpSocketSmallAcks::Fork (void)
{
return CopyObject<TcpSocketSmallAcks> (this);
}
} // namespace ns3

View File

@@ -0,0 +1,668 @@
/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
*
* 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
*
*/
#ifndef TCPGENERALTEST_H
#define TCPGENERALTEST_H
#include "ns3/simple-net-device.h"
#include "ns3/error-model.h"
#include "ns3/tcp-socket-base.h"
#include "ns3/test.h"
namespace ns3 {
/**
* \brief Class for inserting callbacks special points of the flow of TCP sockets
*
* This subclass is born to extend TcpSocketBase, inserting callbacks in certain
* points of the flow, to be used in testing to check certain values or flow
* directions.
*
* There isn't the necessity to fill TcpSocketBase of TracedCallbacks; the rationale
* is to maintain the base class as clean as possible.
*
* To be fair with testing, this class should NOT modify the behavior of TcpSocketBase.
*
* \see SetRcvAckCb
* \see SetProcessedAckCb
* \see SetRetransmitCb
*/
class TcpSocketMsgBase : public TcpSocketBase
{
public:
static TypeId GetTypeId (void);
TcpSocketMsgBase () : TcpSocketBase ()
{
}
TcpSocketMsgBase (const TcpSocketMsgBase &other) : TcpSocketBase (other)
{
m_rcvAckCb = other.m_rcvAckCb;
m_processedAckCb = other.m_processedAckCb;
m_retrCallback = other.m_retrCallback;
m_forkCb = other.m_forkCb;
}
typedef Callback<void, Ptr<const Packet>, const TcpHeader&,
Ptr<const TcpSocketBase> > AckManagementCallback;
typedef Callback<void, Ptr<const TcpSocketState>,
Ptr<const TcpSocketBase> > RetrCallback;
/**
* \brief Set the callback invoked when an ACK is received (at the beginning
* of the processing)
*
* \param cb callback
*/
void SetRcvAckCb (AckManagementCallback cb);
/**
* \brief Set the callback invoked when an ACK is received and processed
* (at the end of the processing)
*
* \param cb callback
*/
void SetProcessedAckCb (AckManagementCallback cb);
/**
* \brief Set the callback invoked after the processing of a retransmit timeout
*
* \param cb callback
*/
void SetRetransmitCb (RetrCallback cb);
/**
* \brief Set the callback invoked after the forking
* \param cb callback
*/
void SetForkCb (Callback<void, Ptr<TcpSocketMsgBase> > cb);
protected:
virtual void ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader);
virtual void Retransmit (void);
virtual Ptr<TcpSocketBase> Fork (void);
virtual void CompleteFork (Ptr<Packet> p, const TcpHeader& tcpHeader,
const Address& fromAddress, const Address& toAddress);
private:
AckManagementCallback m_rcvAckCb;
AckManagementCallback m_processedAckCb;
RetrCallback m_retrCallback;
Callback<void, Ptr<TcpSocketMsgBase> > m_forkCb;
};
/**
* \brief A TCP socket which sends ACKs smaller than the segment received.
*
* Usually, a TCP socket which receives the sequence number "x" replies with
* an ACK to "x+1". What happen if a malicious socket sends smaller ACKs
* (e.g. two ACKs, one for "x/2", and the other for "x+1") ? A TCP implementation
* should avoid to artificially increase the congestion window, thinking of
* having ACKed 2 segments instead of 1.
*
* Set the number of bytes that should be acked in each ACK packet with
* SetBytesToAck.
*
* \see TcpSlowStartAttackerTest
*/
class TcpSocketSmallAcks : public TcpSocketMsgBase
{
public:
static TypeId GetTypeId (void);
TcpSocketSmallAcks ()
: TcpSocketMsgBase (),
m_bytesToAck (125),
m_bytesLeftToBeAcked (0),
m_lastAckedSeq (1)
{
}
TcpSocketSmallAcks (const TcpSocketSmallAcks &other) : TcpSocketMsgBase (other),
m_bytesToAck (other.m_bytesToAck),
m_bytesLeftToBeAcked (other.m_bytesLeftToBeAcked),
m_lastAckedSeq (other.m_lastAckedSeq)
{
}
void SetBytesToAck (uint32_t bytes)
{
m_bytesToAck = bytes;
}
protected:
virtual void SendEmptyPacket (uint8_t flags);
Ptr<TcpSocketBase> Fork (void);
uint32_t m_bytesToAck;
uint32_t m_bytesLeftToBeAcked;
SequenceNumber32 m_lastAckedSeq;
};
/**
* \brief General infrastructure for TCP testing
*
* The class provides a simple setup for a connection testing. Implement
* or modify the virtual methods in order to install a specified
* channel, a specified socket and a specified error model on this simulation.
* Default values are a null error model, and as a channel a SimpleChannel with
* the propagation delay set through the constructor.
*
* Check DoRun documentation for more information on the environment setup.
*
* Apart from setting up the environment for testing, subclassing permits also
* to track and check what is happening inside the two connected sockets. Thanks
* to TcpSocketMsgBase, there are many information provided to children:
*
* - Tracing of states inside the state machines (TCP and ACK ones, through
* functions AckStateTrace and TcpStateTrace)
* - cWnd tracing (through CWndTrace)
* - Socket closing: error state, or normal state (NormalClose and ErrorClose)
* - Packet drop, inside queue or over the link (QueueDrop, PhyDrop)
* - Ack received (RcvAck) and Ack processed (ProcessedAck). The first is used to
* signal that an ACK has been received; after the processing of it, the Second
* is called
* - A packet is transmitted to IP layer or received from IP layer (Tx and Rx)
* - The RTO expires (RTOExpired)
*
* The default version of such methods is empty; implement their behavior differently,
* based on what you want to test. Default is empty to avoid the need to implement
* useless pure virtual function.
*
* If you need values from TcpSocketBase, thanks to the friend relationship between
* this class and TcpSocketBase itself you can get it. Remember that friendship
* isn't passed by inheritance, so the way to go is to write a Getters (like
* GetSegSize) and call it in the subclass.
*
* \see DoRun
* \see TcpSocketMsgBase
*/
class TcpGeneralTest : public TestCase
{
public:
/**
* \brief TcpGeneralTest constructor
*
* \param pktSize application packet size
* \param pktCount count of application packet to generate
* \param pktInterval (application) interval between each generated packet
* \param propagationDelay propagation delay of the channel
* \param startTime time of the first application-generated packet
* \param mtu MTU of the environment
* \param desc description of the test
*/
TcpGeneralTest (const std::string &desc,
uint32_t pktSize = 500,
uint32_t pktCount = 10,
const Time& pktInterval = Seconds (0.01),
const Time& propagationDelay = Seconds (0.5),
const Time& startTime = Seconds (10),
uint32_t initialSlowStartThresh = 0xffff,
uint32_t initialCwnd = 1,
uint32_t segmentSize = 500,
TypeId congestionControl = TcpNewReno::GetTypeId (),
uint32_t mtu = 1500);
~TcpGeneralTest ();
/**
* \brief Used as parameter of methods, specifies on what node
* the caller is interested (e.g. GetSegSize).
*/
enum SocketWho
{
SENDER, //!< Sender node
RECEIVER //!< Receiver node
};
protected:
/**
* \brief Create and return the channel installed between the two socket
*
* \return A SimpleChannel subclass
*/
virtual Ptr<SimpleChannel> CreateChannel ();
/**
* \brief Create and return the error model to install in the sender node
*
* \return sender error model
*/
virtual Ptr<ErrorModel> CreateSenderErrorModel ();
/**
* \brief Create and return the error model to install in the receiver node
*
* \return receiver error model
*/
virtual Ptr<ErrorModel> CreateReceiverErrorModel ();
/**
* \brief Create and install the socket to install on the receiver
* \param node receiver node pointer
* \return the socket to be installed in the receiver
*/
virtual Ptr<TcpSocketMsgBase> CreateReceiverSocket (Ptr<Node> node);
/**
* \brief Create and install the socket to install on the sender
* \param node sender node pointer
* \return the socket to be installed in the sender
*/
virtual Ptr<TcpSocketMsgBase> CreateSenderSocket (Ptr<Node> node);
/**
* \brief Create a socket
*
* \param node associated node
* \param congControl congestion control
* \return a pointer to the newer created socket
*/
virtual Ptr<TcpSocketMsgBase> CreateSocket (Ptr<Node> node, TypeId socketType,
TypeId congControl);
/**
* \brief Get the pointer to a previously created sender socket
* \return ptr to sender socket or 0
*/
Ptr<TcpSocketMsgBase> GetSenderSocket ()
{
return m_senderSocket;
}
/**
* \brief Get the pointer to a previously created receiver socket
* \return ptr to receiver socket or 0
*/
Ptr<TcpSocketMsgBase> GetReceiverSocket ()
{
return m_receiverSocket;
}
/**
* \brief Execute the tcp test
*
* As environment, two socket are connected through a SimpleChannel. Each device
* has an MTU of 1500 bytes, and the application starts to send packet at
* 10s of simulated time, through SendPacket.
*/
virtual void DoRun (void);
/**
* \brief Teardown the TCP test
*/
virtual void DoTeardown (void);
/**
* \brief Packet received
*
* The method processes the packet (application-layer)
* \param socket socket which has received the packet
*/
virtual void ReceivePacket (Ptr<Socket> socket);
/**
* \brief Send packets to other endpoint
*
* \param socket Socket
* \param pktSize size of the packet
* \param pktCount number of packets to send
* \param pktInterval interval between packet (application-level)
*/
void SendPacket (Ptr<Socket> socket, uint32_t pktSize,
uint32_t pktCount, Time pktInterval);
/**
* \brief Get the segment size of the node specified
*
* \param who node to get the parameter from
*
* \return segment size of the specified node
*/
uint32_t GetSegSize (SocketWho who);
/**
* \brief Get the retransmission threshold
* \param who node to get the parameter from
* \return retransmission threshold
*/
uint32_t GetReTxThreshold (SocketWho who);
/**
* \brief Get the number of dupack received
* \param who node to get the parameter from
* \return number of dupack
*/
uint32_t GetDupAckCount (SocketWho who);
/**
* \brief Get the number of delayed ack (if present)
* \param who node to get the parameter from
* \return number of ack we will wait before sending an ACK
*/
uint32_t GetDelAckCount (SocketWho who);
/**
* \brief Get the timeout of delayed ack (if present)
* \param who node to get the parameter from
* \return time we will wait before sending an ACK
*/
Time GetDelAckTimeout (SocketWho who);
/**
* \brief Get the retransmission time
*
* \param who node to get the parameter from
* \return calculated RTO time
*/
Time GetRto (SocketWho who);
/**
* \brief Get the minimun RTO attribute
*
* \param who node to get the parameter from
* \return minimum RTO time
*/
Time GetMinRto (SocketWho who);
/**
* \brief Get the retransmission time for the SYN segments
*
* \param who node to get the parameter from
* \return SYN segments RTO time
*/
Time GetConnTimeout (SocketWho who);
/**
* \brief Get the Rtt estimator of the socket
*
* \param who node to get the parameter from
* \return Rtt estimator
*/
Ptr<RttEstimator> GetRttEstimator (SocketWho who);
/**
* \brief Get the clock granularity attribute
*
* \param who node to get the parameter from
* \return clock granularity
*/
Time GetClockGranularity (SocketWho who);
/**
* \brief Forcefully set a defined size for rx buffer
*
* \param who socket to force
* \param size size of the rx buffer
*/
void SetRcvBufSize (SocketWho who, uint32_t size);
/**
* \brief State on Ack state machine changes
* \param oldValue old value
* \param newValue new value
*/
virtual void AckStateTrace (const TcpSocketState::TcpAckState_t oldValue,
const TcpSocketState::TcpAckState_t newValue)
{
}
/**
* \brief Congestion window changes
*
* \param oldValue old value
* \param newValue new value
*/
virtual void CWndTrace (uint32_t oldValue, uint32_t newValue)
{
}
/**
* \brief Socket closed normally
* \param who the socket closed (SENDER or RECEIVER)
*/
virtual void NormalClose (SocketWho who)
{
}
/**
* \brief Socket closed with an error
*
* \param who the socket closed (SENDER or RECEIVER)
*/
virtual void ErrorClose (SocketWho who)
{
/** \todo indicate the error */
}
/**
* \brief Drop on the queue
* \param who where the drop occurred (SENDER or RECEIVER)
*/
virtual void QueueDrop (SocketWho who)
{
}
/**
* \brief Link drop
* \param who where the drop occurred (SENDER or RECEIVER)
*/
virtual void PhyDrop (SocketWho who)
{
}
/**
* \brief Received ack
*
* Invoked when an ACK is received (no processing is done yet)
*
* \param tcb Transmission Control Block
* \param h the header of segment
* \param who the socket which has received the ACK (SENDER or RECEIVER)
*/
virtual void RcvAck (const Ptr<const TcpSocketState> tcb,
const TcpHeader& h, SocketWho who)
{
}
/**
* \brief Processed ack
*
* Invoked after the processing of the ACK
*
* \param tcb Transmission Control Block
* \param h the header of segment
* \param who the socket which has processed the ACK (SENDER or RECEIVER)
*/
virtual void ProcessedAck (const Ptr<const TcpSocketState> tcb,
const TcpHeader& h, SocketWho who)
{
}
/**
* \brief Packet transmitted down to IP layer
*
* \param p packet
* \param h header
* \param who the socket which has received the packet (SENDER or RECEIVER)
*/
virtual void Tx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who)
{
}
/**
* \brief Packet received from IP layer
*
* \param p packet
* \param h header
* \param who the socket which has received the packet (SENDER or RECEIVER)
*/
virtual void Rx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who)
{
}
/**
* \brief Rto has expired
*
* \param tcb Transmission control block
* \param who where the RTO has expired (SENDER or RECEIVER)
*/
virtual void RTOExpired (const Ptr<const TcpSocketState> tcb, SocketWho who)
{
}
/**
* \brief Performs the (eventual) final checks through test asserts
*
*/
virtual void FinalChecks ()
{
}
/**
* \brief Get the channel Propagation Delay
* \return propagation delay of the channel
*/
Time GetPropagationDelay () const
{
return m_propagationDelay;
}
/**
* \brief Get the data start time
* \return start time of data packets
*/
Time GetStartTime () const
{
return m_startTime;
}
/**
* \brief Get the MTU of the environment
* \return MTU of the environment
*/
uint32_t GetMtu () const
{
return m_mtu;
}
/**
* \brief Get the application packet size
* \return application packet size
*/
uint32_t GetPktSize () const
{
return m_pktSize;
}
/**
* \brief Get the number of application packets
* \return count of application packets to be generated
*/
uint32_t GetPktCount () const
{
return m_pktCount;
}
/**
* \brief Get the interval to wait for each packet sent down from application
* to TCP
* \return interval between packet
*/
Time GetPktInterval () const
{
return m_interPacketInterval;
}
/**
* \brief Get the initial slow start threshold
* \return initial slow start threshold
*/
uint32_t GetInitialSsThresh () const
{
return m_initialSlowStartThresh;
}
/**
* \brief Get the initial congestion window
* \return initial cwnd
*/
uint32_t GetInitialCwnd () const
{
return m_initialCwnd;
}
TypeId m_congControlTypeId; //!< Congestion control
private:
// Member variables, accessible through getters
// giving write access to subclass can be dangerous
Time m_propagationDelay; //!< Propagation delay of the channel
Time m_startTime; //!< Data transmission time
uint32_t m_mtu; //!< MTU of the environment
uint32_t m_pktSize; //!< Size of the application packet
uint32_t m_pktCount; //!< Count of the application packet
Time m_interPacketInterval; //!< Time between sending application packet
// down to tcp socket
uint32_t m_initialSlowStartThresh; //!< Initial slow start threshold
uint32_t m_initialCwnd; //!< Initial congestion window
uint32_t m_segmentSize; //!< Segment size
Ptr<TcpSocketMsgBase> m_senderSocket; //!< Pointer to sender socket
Ptr<TcpSocketMsgBase> m_receiverSocket; //!< Pointer to receiver socket
private:
// De-multiplexing callbacks.
void NormalCloseCb (Ptr<Socket> socket);
void ErrorCloseCb (Ptr<Socket> socket);
void QueueDropCb (std::string context, Ptr<const Packet> p);
void PhyDropCb (std::string context, Ptr<const Packet> p);
void RcvAckCb (Ptr<const Packet> p, const TcpHeader& h,
Ptr<const TcpSocketBase> tcp);
void ProcessedAckCb (Ptr<const Packet> p, const TcpHeader& h,
Ptr<const TcpSocketBase> tcp);
void TxPacketCb (const Ptr<const Packet> p, const TcpHeader& h,
const Ptr<const TcpSocketBase> tcp);
void RxPacketCb (const Ptr<const Packet> p, const TcpHeader& h,
const Ptr<const TcpSocketBase> tcp);
void RtoExpiredCb (const Ptr<const TcpSocketState> tcb,
const Ptr<const TcpSocketBase> tcp);
void ForkCb (Ptr<TcpSocketMsgBase> tcp);
void HandleAccept (Ptr<Socket> socket, const Address& from);
};
/**
* \brief Convenience function to retrieve the ACK state from a TCB
*
* \param tcb Transmission control block
* \return the state of the ACK state machine
*/
static inline TcpSocketState::TcpAckState_t
GetAckStateFrom (Ptr<const TcpSocketState> tcb)
{
return tcb->m_ackState.Get ();
}
} // namespace ns3
#endif // TCPGENERALTEST_H

View File

@@ -231,6 +231,7 @@ def build(bld):
'test/tcp-wscaling-test.cc',
'test/tcp-option-test.cc',
'test/tcp-header-test.cc',
'test/tcp-general-test.cc',
'test/udp-test.cc',
'test/ipv6-address-generator-test-suite.cc',
'test/ipv6-dual-stack-test-suite.cc',