Added test suite for Fast Retransmit

This commit is contained in:
Natale Patriciello
2015-10-16 10:43:58 -07:00
parent cb30195586
commit 8a3ce829f6
3 changed files with 453 additions and 0 deletions

View File

@@ -0,0 +1,371 @@
/* -*- 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/log.h"
#include "tcp-fast-retr-test.h"
#include "ns3/tcp-westwood.h"
#include "ns3/node.h"
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("TcpFastRetrTest");
TcpFastRetrTest::TcpFastRetrTest (TypeId typeId, uint32_t seqToKill,
const std::string &msg)
: TcpGeneralTest (msg, 500, 100, Seconds (0.01), Seconds (0.5), Seconds (10),
0, 1, 500, typeId, 1500),
m_pktDropped (false),
m_pktWasDropped (false),
m_seqToKill (seqToKill),
m_dupAckReceived (0),
m_sndNextExpSeq (0),
m_rcvNextExpAck (1),
m_countRetr (0),
m_bytesRcvButNotAcked (0)
{
}
Ptr<ErrorModel>
TcpFastRetrTest::CreateSenderErrorModel ()
{
return 0;
}
Ptr<ErrorModel>
TcpFastRetrTest::CreateReceiverErrorModel ()
{
m_errorModel = CreateObject<TcpSeqErrorModel> ();
m_errorModel->AddSeqToKill (SequenceNumber32 (m_seqToKill));
m_errorModel->SetDropCallback (MakeCallback (&TcpFastRetrTest::PktDropped, this));
return m_errorModel;
}
Ptr<TcpSocketMsgBase>
TcpFastRetrTest::CreateSenderSocket (Ptr<Node> node)
{
Ptr<TcpSocketMsgBase> socket = TcpGeneralTest::CreateSenderSocket (node);
socket->SetAttribute ("MinRto", TimeValue (Seconds (10.0)));
return socket;
}
void
TcpFastRetrTest::Rx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who)
{
if (who == SENDER)
{
// Nothing to check
NS_LOG_INFO ("\tSENDER Rx " << h);
}
else if (who == RECEIVER)
{
NS_LOG_INFO ("\tRECEIVER Rx " << h);
// Receiver has received the missing segment
if (h.GetSequenceNumber ().GetValue () == m_seqToKill)
{
m_pktDropped = false;
if (m_bytesRcvButNotAcked > 0)
{
m_rcvNextExpAck += m_bytesRcvButNotAcked + GetSegSize (SENDER);
m_bytesRcvButNotAcked = 0;
}
}
// Count all the received bytes not acked
if (m_pktDropped)
{
m_bytesRcvButNotAcked += GetSegSize (SENDER);
}
}
}
void
TcpFastRetrTest::Tx (const Ptr<const Packet> p, const TcpHeader &h, SocketWho who)
{
if (who == SENDER)
{
NS_LOG_INFO ("\tSENDER Tx " << h << " size=" << p->GetSize ());
if (h.GetSequenceNumber ().GetValue () == m_seqToKill && m_pktDropped)
{
// Spotted the retransmission!
m_countRetr++;
NS_TEST_ASSERT_MSG_EQ (m_countRetr, 1,
"Segment retransmitted too many times");
}
else
{
// No delayed ACK involved here.
while (h.GetSequenceNumber () < m_sndNextExpSeq)
{
m_sndNextExpSeq -= GetSegSize (SENDER);
}
if (h.GetSequenceNumber ().GetValue () != 50002)
{
NS_TEST_ASSERT_MSG_EQ (m_sndNextExpSeq, h.GetSequenceNumber (),
"Sequence number expected differs");
}
}
if (m_sndNextExpSeq.GetValue () == 0)
{
// SYN
m_sndNextExpSeq = SequenceNumber32 (1);
}
else if (m_sndNextExpSeq.GetValue () == 1 && p->GetSize () == 32)
{
// Pure ACK in three-way handshake, then we expect data
m_sndNextExpSeq = SequenceNumber32 (1);
}
else
{
// Data segments
m_sndNextExpSeq += GetSegSize (SENDER);
}
}
else if (who == RECEIVER)
{
NS_LOG_INFO ("\tRECEIVER Tx, " << h << " size=" << p->GetSize ());
if (h.GetFlags () == (TcpHeader::SYN | TcpHeader::ACK))
{
NS_TEST_ASSERT_MSG_EQ (h.GetSequenceNumber ().GetValue (), 0,
"SYN pkt has not 0 as initial sequence number."
"Probably, random sqn number has been implemented."
"Check this test");
}
else
{
NS_TEST_ASSERT_MSG_EQ (h.GetSequenceNumber ().GetValue (), 1,
"ACK pkt has not 1 as sequence number."
"Probably, random sqn number has been implemented."
"Check this test");
}
// Accounted for delayed ACK, but not received.
while (h.GetAckNumber () < m_rcvNextExpAck)
{
m_rcvNextExpAck -= GetSegSize (SENDER);
}
if (m_rcvNextExpAck.GetValue () == 50001)
{
m_rcvNextExpAck += 1;
}
NS_TEST_ASSERT_MSG_EQ (h.GetAckNumber (), m_rcvNextExpAck,
"ACKing something not considered");
if (m_pktDropped)
{
m_rcvNextExpAck = SequenceNumber32 (m_seqToKill);
}
else
{
switch (m_rcvNextExpAck.GetValue ())
{
case 0:
m_rcvNextExpAck = SequenceNumber32 (1);
break;
case 1:
m_rcvNextExpAck += GetSegSize (SENDER);
break;
default:
m_rcvNextExpAck += GetSegSize (SENDER) * GetDelAckCount (SENDER);
// FIN seq
if (m_rcvNextExpAck.GetValue () == 50001)
{
m_rcvNextExpAck += 1;
}
else if (m_rcvNextExpAck.GetValue () > 50002)
{
m_rcvNextExpAck = 50002;
}
}
}
}
}
void
TcpFastRetrTest::RcvAck (const Ptr<const TcpSocketState> tcb, const TcpHeader &h,
SocketWho who)
{
NS_LOG_FUNCTION (this << tcb << h << who);
if (who == SENDER)
{
if (h.GetAckNumber ().GetValue () < m_seqToKill)
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::OPEN,
"Not in OPEN state to respond to a loss");
NS_TEST_ASSERT_MSG_EQ (GetDupAckCount (SENDER), 0,
"Dupack different than 0 but no loss detected");
}
else if (h.GetAckNumber ().GetValue () == m_seqToKill)
{
NS_TEST_ASSERT_MSG_EQ (GetDupAckCount (SENDER), m_dupAckReceived,
"Dupack count differs");
if (GetDupAckCount(SENDER) == 0 &&
GetDupAckCount (SENDER) < GetReTxThreshold (SENDER))
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::OPEN,
"Not in OPEN state for processing dupack");
}
else if (GetDupAckCount (SENDER) > 0 &&
GetDupAckCount (SENDER) < GetReTxThreshold (SENDER))
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::DISORDER,
"Not in DISORDER state after receiving dupacks");
}
else if (GetDupAckCount (SENDER) >= GetReTxThreshold (SENDER))
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::RECOVERY,
"Not in RECOVERY state after reaching retxthresh");
}
}
}
else if (who == RECEIVER)
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::OPEN,
"Receiver not in OPEN state");
}
}
void
TcpFastRetrTest::ProcessedAck (const Ptr<const TcpSocketState> tcb, const TcpHeader &h,
SocketWho who)
{
NS_LOG_FUNCTION (this << tcb << h << who);
if (who == SENDER)
{
if (m_previousAck == h.GetAckNumber () && h.GetAckNumber ().GetValue () < 50002)
{
m_dupAckReceived++;
NS_TEST_ASSERT_MSG_GT_OR_EQ (m_dupAckReceived, GetDupAckCount (SENDER),
"Count of dupAck differs");
if (GetDupAckCount (SENDER) < GetReTxThreshold (SENDER))
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::DISORDER,
"DupAck less than ReTxThreshold but not "
"in DISORDER state");
}
else
{
NS_TEST_ASSERT_MSG_GT_OR_EQ (GetAckStateFrom (tcb), TcpSocketState::RECOVERY,
"DupAck greater than ReTxThreshold but not "
"in RECOVERY or LOSS state");
m_pktWasDropped = true;
}
}
else if (m_previousAck < h.GetAckNumber ())
{
m_dupAckReceived = 0;
}
m_previousAck = h.GetAckNumber ();
}
else if (who == RECEIVER)
{
NS_TEST_ASSERT_MSG_EQ (GetAckStateFrom (tcb), TcpSocketState::OPEN,
"Different state than OPEN in the receiver");
}
}
void
TcpFastRetrTest::RTOExpired(const Ptr<const TcpSocketState> tcb, SocketWho who)
{
NS_ASSERT_MSG (true == false, "RTO isn't expected here");
}
void
TcpFastRetrTest::AckStateTrace (const TcpSocketState::TcpAckState_t oldValue,
const TcpSocketState::TcpAckState_t newValue)
{
NS_LOG_FUNCTION (this << oldValue << newValue);
if (oldValue == TcpSocketState::OPEN && newValue == TcpSocketState::DISORDER)
{
}
else if (oldValue == TcpSocketState::OPEN
&& newValue == TcpSocketState::RECOVERY
&& GetReTxThreshold (SENDER) > 1)
{
NS_TEST_ASSERT_MSG_EQ (true, false,
"Invalid OPEN to RECOVERY state change");
}
else if (oldValue == TcpSocketState::DISORDER
&& newValue == TcpSocketState::RECOVERY)
{
NS_TEST_ASSERT_MSG_EQ (GetReTxThreshold (SENDER), GetDupAckCount (SENDER),
"DISORDER to RECOVERY state change but not reached "
"the ReTxThreshold");
}
}
void
TcpFastRetrTest::PktDropped (const Ipv4Header &ipH, const TcpHeader& tcpH)
{
NS_LOG_FUNCTION (this << ipH << tcpH);
m_pktDropped = true;
m_rcvNextExpAck = tcpH.GetSequenceNumber ();
NS_TEST_ASSERT_MSG_EQ (tcpH.GetSequenceNumber (), SequenceNumber32 (m_seqToKill),
"Packet dropped but sequence number differs");
}
void
TcpFastRetrTest::FinalChecks ()
{
NS_TEST_ASSERT_MSG_EQ (m_pktWasDropped, true,
"Packet was not dropped at all");
NS_TEST_ASSERT_MSG_EQ (m_countRetr, 1,
"Segment was not retransmitted at all");
NS_TEST_ASSERT_MSG_EQ (m_rcvNextExpAck.GetValue (), 50002,
"Not all data have been transmitted");
}
//-----------------------------------------------------------------------------
static class TcpFastRetrTestSuite : public TestSuite
{
public:
TcpFastRetrTestSuite () : TestSuite ("tcp-fast-retr-test", UNIT)
{
std::list<TypeId> types;
types.insert (types.begin (), TcpWestwood::GetTypeId ());
types.insert (types.begin (), TcpNewReno::GetTypeId ());
for (std::list<TypeId>::iterator it = types.begin (); it != types.end (); ++it)
{
AddTestCase (new TcpFastRetrTest ((*it), 5001, "Fast Retransmit testing"), TestCase::QUICK);
}
}
} g_TcpFastRetrTestSuite;
} // namespace ns3

View File

@@ -0,0 +1,81 @@
/* -*- 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 TCPFASTRETRTEST_H
#define TCPFASTRETRTEST_H
#include "tcp-general-test.h"
#include "ns3/simple-channel.h"
#include "tcp-error-model.h"
namespace ns3 {
/**
* \brief Test the fast retransmission
*
* Checking what is happening is not so easy, so there are a lot of variables
* which helps to keep track on what is happening.
* The idea is following sequence and ack numbers which are exchanged,
* testing if they are the same as the implementation transmits.
*/
class TcpFastRetrTest : public TcpGeneralTest
{
public:
TcpFastRetrTest (TypeId congControl, uint32_t seqToKill, const std::string &msg);
virtual Ptr<ErrorModel> CreateSenderErrorModel ();
virtual Ptr<ErrorModel> CreateReceiverErrorModel ();
virtual Ptr<TcpSocketMsgBase> CreateSenderSocket (Ptr<Node> node);
protected:
virtual void RcvAck (const Ptr<const TcpSocketState> tcb,
const TcpHeader& h, SocketWho who);
virtual void ProcessedAck (const Ptr<const TcpSocketState> tcb,
const TcpHeader& h, SocketWho who);
virtual void AckStateTrace (const TcpSocketState::TcpAckState_t oldValue,
const TcpSocketState::TcpAckState_t newValue);
virtual void Tx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
virtual void Rx (const Ptr<const Packet> p, const TcpHeader&h, SocketWho who);
virtual void RTOExpired (const Ptr<const TcpSocketState> tcb, SocketWho who);
void PktDropped (const Ipv4Header &ipH, const TcpHeader& tcpH);
void FinalChecks ();
bool m_pktDropped;
bool m_pktWasDropped;
uint32_t m_seqToKill;
uint32_t m_dupAckReceived;
SequenceNumber32 m_previousAck;
SequenceNumber32 m_sndNextExpSeq;
SequenceNumber32 m_rcvNextExpAck;
uint32_t m_countRetr;
uint32_t m_bytesRcvButNotAcked;
Ptr<TcpSeqErrorModel> m_errorModel;
};
} // namespace ns3
#endif // TCPFASTRETRTEST_H

View File

@@ -235,6 +235,7 @@ def build(bld):
'test/tcp-error-model.cc',
'test/tcp-slow-start-test.cc',
'test/tcp-cong-avoid-test.cc',
'test/tcp-fast-retr-test.cc',
'test/udp-test.cc',
'test/ipv6-address-generator-test-suite.cc',
'test/ipv6-dual-stack-test-suite.cc',