diff --git a/src/internet/test/tcp-general-test.cc b/src/internet/test/tcp-general-test.cc index 1c9beb444..3f1c4cbab 100644 --- a/src/internet/test/tcp-general-test.cc +++ b/src/internet/test/tcp-general-test.cc @@ -164,6 +164,7 @@ TcpGeneralTest::DoRun (void) m_receiverSocket->SetProcessedAckCb (MakeCallback (&TcpGeneralTest::ProcessedAckCb, this)); m_receiverSocket->SetRetransmitCb (MakeCallback (&TcpGeneralTest::RtoExpiredCb, this)); m_receiverSocket->SetForkCb (MakeCallback (&TcpGeneralTest::ForkCb, this)); + m_receiverSocket->SetUpdateRttHistoryCb (MakeCallback (&TcpGeneralTest::UpdateRttHistoryCb, this)); m_receiverSocket->TraceConnectWithoutContext ("Tx", MakeCallback (&TcpGeneralTest::TxPacketCb, this)); m_receiverSocket->TraceConnectWithoutContext ("Rx", @@ -181,6 +182,7 @@ TcpGeneralTest::DoRun (void) m_senderSocket->SetProcessedAckCb (MakeCallback (&TcpGeneralTest::ProcessedAckCb, this)); m_senderSocket->SetRetransmitCb (MakeCallback (&TcpGeneralTest::RtoExpiredCb, this)); m_senderSocket->SetDataSentCallback (MakeCallback (&TcpGeneralTest::DataSentCb, this)); + m_senderSocket->SetUpdateRttHistoryCb (MakeCallback (&TcpGeneralTest::UpdateRttHistoryCb, this)); m_senderSocket->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&TcpGeneralTest::CWndTrace, this)); m_senderSocket->TraceConnectWithoutContext ("CongState", @@ -334,6 +336,25 @@ TcpGeneralTest::NormalCloseCb (Ptr socket) } } +void +TcpGeneralTest::UpdateRttHistoryCb (Ptr tcp, + const SequenceNumber32 & seq, uint32_t sz, + bool isRetransmission) +{ + if (tcp->GetNode () == m_receiverSocket->GetNode ()) + { + UpdatedRttHistory (seq, sz, isRetransmission, RECEIVER); + } + else if (tcp->GetNode () == m_senderSocket->GetNode ()) + { + UpdatedRttHistory (seq, sz, isRetransmission, SENDER); + } + else + { + NS_FATAL_ERROR ("Closed socket, but not recognized"); + } +} + void TcpGeneralTest::RtoExpiredCb (const Ptr tcb, const Ptr tcp) @@ -775,21 +796,21 @@ TcpSocketMsgBase::Fork (void) } void -TcpSocketMsgBase::SetRcvAckCb (AckManagementCallback cb) +TcpSocketMsgBase::SetRcvAckCb (AckManagementCb cb) { NS_ASSERT (!cb.IsNull ()); m_rcvAckCb = cb; } void -TcpSocketMsgBase::SetProcessedAckCb (AckManagementCallback cb) +TcpSocketMsgBase::SetProcessedAckCb (AckManagementCb cb) { NS_ASSERT (!cb.IsNull ()); m_processedAckCb = cb; } void -TcpSocketMsgBase::SetRetransmitCb (RetrCallback cb) +TcpSocketMsgBase::SetRetransmitCb (RetrCb cb) { NS_ASSERT (!cb.IsNull ()); m_retrCallback = cb; @@ -821,6 +842,24 @@ TcpSocketMsgBase::SetForkCb (Callback > cb) m_forkCb = cb; } +void +TcpSocketMsgBase::SetUpdateRttHistoryCb (UpdateRttCallback cb) +{ + NS_ASSERT (!cb.IsNull ()); + m_updateRttCb = cb; +} + +void +TcpSocketMsgBase::UpdateRttHistory (const SequenceNumber32 &seq, uint32_t sz, + bool isRetransmission) +{ + TcpSocketBase::UpdateRttHistory (seq, sz, isRetransmission); + if (!m_updateRttCb.IsNull ()) + { + m_updateRttCb (this, seq, sz, isRetransmission); + } +} + void TcpSocketMsgBase::CompleteFork (Ptr p, const TcpHeader &tcpHeader, const Address &fromAddress, const Address &toAddress) diff --git a/src/internet/test/tcp-general-test.h b/src/internet/test/tcp-general-test.h index b2b1bf1e1..ac32d4e07 100644 --- a/src/internet/test/tcp-general-test.h +++ b/src/internet/test/tcp-general-test.h @@ -60,9 +60,11 @@ public: } typedef Callback, const TcpHeader&, - Ptr > AckManagementCallback; + Ptr > AckManagementCb; typedef Callback, - Ptr > RetrCallback; + Ptr > RetrCb; + typedef Callback, const SequenceNumber32&, + uint32_t, bool> UpdateRttCallback; /** * \brief Set the callback invoked when an ACK is received (at the beginning @@ -70,7 +72,7 @@ public: * * \param cb callback */ - void SetRcvAckCb (AckManagementCallback cb); + void SetRcvAckCb (AckManagementCb cb); /** * \brief Set the callback invoked when an ACK is received and processed @@ -78,14 +80,14 @@ public: * * \param cb callback */ - void SetProcessedAckCb (AckManagementCallback cb); + void SetProcessedAckCb (AckManagementCb cb); /** * \brief Set the callback invoked after the processing of a retransmit timeout * * \param cb callback */ - void SetRetransmitCb (RetrCallback cb); + void SetRetransmitCb (RetrCb cb); /** * \brief Set the callback invoked after the forking @@ -93,18 +95,28 @@ public: */ void SetForkCb (Callback > cb); + /** + * \brief Set the callback invoked when we update rtt history + * + * \param cb callback + */ + void SetUpdateRttHistoryCb (UpdateRttCallback cb); + protected: virtual void ReceivedAck (Ptr packet, const TcpHeader& tcpHeader); virtual void Retransmit (void); virtual Ptr Fork (void); virtual void CompleteFork (Ptr p, const TcpHeader& tcpHeader, const Address& fromAddress, const Address& toAddress); + virtual void UpdateRttHistory (const SequenceNumber32 &seq, uint32_t sz, + bool isRetransmission); private: - AckManagementCallback m_rcvAckCb; - AckManagementCallback m_processedAckCb; - RetrCallback m_retrCallback; + AckManagementCb m_rcvAckCb; + AckManagementCb m_processedAckCb; + RetrCb m_retrCallback; Callback > m_forkCb; + UpdateRttCallback m_updateRttCb; }; @@ -129,16 +141,17 @@ public: TcpSocketSmallAcks () : TcpSocketMsgBase (), - m_bytesToAck (125), - m_bytesLeftToBeAcked (0), - m_lastAckedSeq (1) + 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) + TcpSocketSmallAcks (const TcpSocketSmallAcks &other) + : TcpSocketMsgBase (other), + m_bytesToAck (other.m_bytesToAck), + m_bytesLeftToBeAcked (other.m_bytesLeftToBeAcked), + m_lastAckedSeq (other.m_lastAckedSeq) { } @@ -464,7 +477,7 @@ protected: * \param newValue new value */ virtual void CongStateTrace (const TcpSocketState::TcpCongState_t oldValue, - const TcpSocketState::TcpCongState_t newValue) + const TcpSocketState::TcpCongState_t newValue) { } @@ -580,6 +593,18 @@ protected: { } + /** + * \brief Updated the Rtt history + * \param seq Sequence inserted + * \param sz size + * \param isRetransmission self-explanatory + * \param who where the rtt history was updated + */ + virtual void UpdatedRttHistory (const SequenceNumber32 & seq, uint32_t sz, + bool isRetransmission, SocketWho who) + { + } + /** * \brief Notifying application for sent data * @@ -709,6 +734,8 @@ private: const Ptr tcp); void RtoExpiredCb (const Ptr tcb, const Ptr tcp); + void UpdateRttHistoryCb (Ptr tcp, const SequenceNumber32&seq, + uint32_t sz, bool isRetransmission); void DataSentCb (Ptr socket, uint32_t size); void ForkCb (Ptr tcp); void HandleAccept (Ptr socket, const Address& from); diff --git a/src/internet/test/tcp-rtt-estimation.cc b/src/internet/test/tcp-rtt-estimation.cc new file mode 100644 index 000000000..52e149ee4 --- /dev/null +++ b/src/internet/test/tcp-rtt-estimation.cc @@ -0,0 +1,167 @@ +/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2016 Natale Patriciello + * + * 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 "tcp-general-test.h" +#include "ns3/node.h" +#include "ns3/log.h" +#include "tcp-error-model.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("TcpRttEstimationTestSuite"); + +/** + * \brief Check Rtt calculations + * + * First check is that, for each ACK, we have a valid estimation of the RTT. + * The second check is that, when updating RTT history, we should consider + * retransmission only segments which sequence number is lower than the highest + * already transmitted. + */ +class TcpRttEstimationTest : public TcpGeneralTest +{ +public: + TcpRttEstimationTest (const std::string &desc, bool enableTs, uint32_t dataPkt); + +protected: + virtual Ptr CreateReceiverSocket (Ptr node); + virtual Ptr CreateSenderSocket (Ptr node); + + virtual void Rx (const Ptr p, const TcpHeader&h, SocketWho who); + virtual void Tx (const Ptr p, const TcpHeader&h, SocketWho who); + virtual void UpdatedRttHistory (const SequenceNumber32 & seq, uint32_t sz, + bool isRetransmission, SocketWho who); + virtual void RttTrace (Time oldTime, Time newTime); + void FinalChecks (); + +private: + bool m_enableTs; + bool m_rttChanged; + SequenceNumber32 m_highestTxSeq; +}; + +TcpRttEstimationTest::TcpRttEstimationTest (const std::string &desc, bool enableTs, uint32_t dataPkt) + : TcpGeneralTest (desc, 500, dataPkt, Seconds (0.01), Seconds (0.05), Seconds (2.0), + 0xffffffff,1, 500), + m_enableTs (enableTs), + m_rttChanged (false), + m_highestTxSeq (0) +{ +} + +Ptr +TcpRttEstimationTest::CreateReceiverSocket (Ptr node) +{ + Ptr s = TcpGeneralTest::CreateReceiverSocket (node); + if (!m_enableTs) + { + s->SetAttribute ("Timestamp", BooleanValue (false)); + } + + return s; +} + +Ptr +TcpRttEstimationTest::CreateSenderSocket (Ptr node) +{ + Ptr s = TcpGeneralTest::CreateSenderSocket (node); + if (!m_enableTs) + { + s->SetAttribute ("Timestamp", BooleanValue (false)); + } + + return s; +} + +void +TcpRttEstimationTest::Tx (const Ptr p, const TcpHeader &h, SocketWho who) +{ + if (who == SENDER && h.GetFlags () != TcpHeader::SYN) + { + if (m_highestTxSeq < h.GetSequenceNumber ()) + { + m_highestTxSeq = h.GetSequenceNumber (); + } + + Ptr rttEstimator = GetRttEstimator (SENDER); + NS_ASSERT (rttEstimator != 0); + NS_LOG_DEBUG ("S Rx: seq=" << h.GetSequenceNumber () << " ack=" << h.GetAckNumber ()); + NS_TEST_ASSERT_MSG_NE (rttEstimator->GetEstimate (), Seconds (1), + "Default Estimate for the RTT"); + } +} + +void +TcpRttEstimationTest::Rx (const Ptr p, const TcpHeader &h, SocketWho who) +{ + if (who == RECEIVER) + { + NS_LOG_DEBUG ("R Rx: seq=" << h.GetSequenceNumber () << " ack=" << h.GetAckNumber ()); + } +} + +void +TcpRttEstimationTest::UpdatedRttHistory (const SequenceNumber32 & seq, uint32_t sz, + bool isRetransmission, SocketWho who) +{ + if (seq < m_highestTxSeq) + { + NS_TEST_ASSERT_MSG_EQ (isRetransmission, true, + "A retransmission is not flagged as such"); + } + else + { + NS_TEST_ASSERT_MSG_EQ (isRetransmission, false, + "Incorrectly flagging seq as retransmission"); + } + +} + +void +TcpRttEstimationTest::RttTrace (Time oldTime, Time newTime) +{ + NS_LOG_DEBUG ("Rtt changed to " << newTime); + m_rttChanged = true; +} + +void +TcpRttEstimationTest::FinalChecks () +{ + NS_TEST_ASSERT_MSG_EQ (m_rttChanged, true, "Rtt was not updated"); +} + +//----------------------------------------------------------------------------- + +static class TcpRttEstimationTestSuite : public TestSuite +{ +public: + TcpRttEstimationTestSuite () : TestSuite ("tcp-rtt-estimation-test", UNIT) + { + AddTestCase (new TcpRttEstimationTest ("RTT estimation, ts, no data", true, 0), + TestCase::QUICK); + AddTestCase (new TcpRttEstimationTest ("RTT estimation, no ts, no data", false, 0), + TestCase::QUICK); + AddTestCase (new TcpRttEstimationTest ("RTT estimation, ts, some data", true, 10), + TestCase::QUICK); + AddTestCase (new TcpRttEstimationTest ("RTT estimation, no ts, some data", false, 10), + TestCase::QUICK); + } +} g_tcpRttEstimationTestSuite; + +} // namespace ns3 diff --git a/src/internet/wscript b/src/internet/wscript index 7f1e1d7c5..35d5cb982 100644 --- a/src/internet/wscript +++ b/src/internet/wscript @@ -243,6 +243,7 @@ def build(bld): 'test/tcp-hybla-test.cc', 'test/tcp-zero-window-test.cc', 'test/tcp-pkts-acked-test.cc', + 'test/tcp-rtt-estimation.cc', 'test/udp-test.cc', 'test/ipv6-address-generator-test-suite.cc', 'test/ipv6-dual-stack-test-suite.cc',