Index: src/internet/model/mptcp-crypto.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-crypto.cc @@ -0,0 +1,75 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + */ + +#include +#include "ns3/mptcp-crypto.h" +#include "ns3/log.h" +#include "ns3/node.h" +#include "ns3/buffer.h" +#include "ns3/assert.h" +#include + +#ifdef HAVE_CRYPTO + #include +#else + #include + #include + #include +#endif + +NS_LOG_COMPONENT_DEFINE ("MpTcpCrypto"); + +namespace ns3 { + +/* https://www.gnupg.org/documentation/manuals/gcrypt/Working-with-hash-algorithms.html#Working-with-hash-algorithms */ +void +GenerateTokenForKey (mptcp_crypto_alg_t ns_alg, uint64_t key, uint32_t& token, uint64_t& idsn) +{ + NS_LOG_LOGIC ("Generating token/key from key=" << key); + #ifdef HAVE_CRYPTO + gcry_md_algos gcry_algo = GCRY_MD_SHA1; + static const int KEY_SIZE_IN_BYTES = sizeof (key); + + // converts the key into a buffer + Buffer keyBuff; + keyBuff.AddAtStart (KEY_SIZE_IN_BYTES); + Buffer::Iterator it = keyBuff.Begin (); + it.WriteHtonU64 (key); + int hash_length = gcry_md_get_algo_dlen (gcry_algo ); + unsigned char digest[20]; + Buffer digestBuf; + digestBuf.AddAtStart (hash_length); + + gcry_md_hash_buffer (GCRY_MD_SHA1, digest, keyBuff.PeekData (), KEY_SIZE_IN_BYTES); + Buffer::Iterator it_digest = digestBuf.Begin (); + it_digest.Write (digest , hash_length); + it_digest = digestBuf.Begin (); + token = it_digest.ReadNtohU32 (); + it_digest.Next (8); + idsn = it_digest.ReadNtohU64 (); + #else + + idsn = key; + token = (uint32_t) key; + #endif // HAVE_CRYPTO +} + +} // end of 'ns3' Index: src/internet/model/mptcp-crypto.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-crypto.h @@ -0,0 +1,72 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + */ + +#ifndef MPTCP_CRYPTO_H +#define MPTCP_CRYPTO_H + + /** + * Token: A locally unique identifier given to a multipath connection + * by a host. May also be referred to as a "Connection ID". + * + * In this specification, with only the SHA-1 algorithm + * (bit "H") specified and selected, the token MUST be a truncated (most + * significant 32 bits) SHA-1 hash ([4], [15]) of the key. A different, + * 64-bit truncation (the least significant 64 bits) of the SHA-1 hash + * of the key MUST be used as the initial data sequence number. Note + * that the key MUST be hashed in network byte order. Also note that + * the "least significant" bits MUST be the rightmost bits of the SHA-1 + * digest, as per [4]. Future specifications of the use of the crypto + * bits may choose to specify different algorithms for token and IDSN + * generation. + */ +namespace ns3 +{ + /** + * \brief Only SHA1 is defined in the RFC up to now. + */ + enum mptcp_crypto_alg_t + { + HMAC_SHA1 = 1 /**< Default choice */ + /* more may come in the future depending on the standardization */ + }; + + /** + * \brief This function generates the token and idsn based on the passed key + * + * \note This function operates in different modes depending on if the library libgcrypt + * was available when running ./waf config . The result conforms to the standard when libgcrypt + * is present, otherwise it relies on a simpler incompatible ns3 implementation. + * + * In the case of sha1 (only one standardized), the token MUST be a truncated (most + * significant 32 bits) SHA-1 hash according to \rfc{6824}. + * The least significant 64 bits of the SHA-1 hash + * of the key MUST be used as the initial data sequence number. + * + * \param alg The hmac algorith m to use to generate the hash + * \param key Given key for a connection + * \param token Resulting token generated from the key + * \param idsn Resulting initial data sequence number generated from the key + */ + void + GenerateTokenForKey (mptcp_crypto_alg_t alg, uint64_t key, uint32_t& token, uint64_t& idsn); +} + +#endif Index: src/internet/model/mptcp-fullmesh.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-fullmesh.cc @@ -0,0 +1,76 @@ + +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * 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 + * + * Author: Kashif Nadeem + */ + +#include "ns3/mptcp-fullmesh.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/tcp-l4-protocol.h" +#include "ns3/ipv4-end-point.h" +#include "ns3/ipv4-header.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("MpTcpFullMesh"); + +NS_OBJECT_ENSURE_REGISTERED (MpTcpFullMesh); + +TypeId +MpTcpFullMesh::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns::MpTcpFullMesh") + .SetParent () + .AddConstructor () + .SetGroupName ("Internet") + + ; + return tid; +} + +MpTcpFullMesh::MpTcpFullMesh (void) + : Object() +{ + NS_LOG_FUNCTION (this); +} + +MpTcpFullMesh::~MpTcpFullMesh (void) +{ + NS_LOG_FUNCTION (this); +} + +TypeId +MpTcpFullMesh::GetInstanceTypeId () const +{ + return MpTcpFullMesh::GetTypeId (); +} + +void +MpTcpFullMesh::CreateMesh (Ptr meta) +{ + NS_LOG_FUNCTION (this<AddLocalAddresses (); // Add local addresses to vector LocalAddressInfo; + meta->CreateSubflowsForMesh (); +} + +} //namespace ns3 Index: src/internet/model/mptcp-fullmesh.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-fullmesh.h @@ -0,0 +1,71 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * 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 + * + * Author: Kashif Nadeem + */ +#ifndef MPTCP_FULLMESH_H +#define MPTCP_FULLMESH_H + +#include "ns3/mptcp-socket-base.h" +#include "ns3/ipv4-header.h" +#include "ns3/object.h" + +namespace ns3 { + +/** + * \ingroup socket + * \ingroup mptcp + * + * \brief A base class for implementation of a MPTCP Fullmesh Path Manager. + * + * This class contains the functionality to create mesh of subflows between + * sender and receiver. This class makes calls to add addresses of the local + * host to the container. Makes call to the function CreateSubflowsForMesh + * from MPTCP socket base to create subflows + */ +class MpTcpFullMesh : public Object +{ +public: + /** + * Get the type ID. + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * \brief Get the instance TypeId + * \return the instance TypeId + */ + virtual TypeId GetInstanceTypeId () const; + + MpTcpFullMesh (void); + + virtual ~MpTcpFullMesh (void); + /** + * Creates the mesh of subflows between the sender and receiver + * based on the available IP addresses + * \param meta the pointer to the MpTcpSocketBase + */ + + virtual void CreateMesh (Ptr meta); +}; + +} //namespace ns3 + +#endif //MPTCP_FULLMESH_H Index: src/internet/model/mptcp-mapping.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-mapping.cc @@ -0,0 +1,262 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#include +#include +#include +#include +#include "ns3/mptcp-mapping.h" +#include "ns3/simulator.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("MpTcpMapping"); + +namespace ns3 +{ + +// brief sorted on DSNs +typedef std::set MappingList; + +MpTcpMapping::MpTcpMapping () + : m_dataSequenceNumber (0), + m_subflowSequenceNumber (0), + m_dataLevelLength (0) +{ + NS_LOG_FUNCTION (this); +} + +MpTcpMapping::~MpTcpMapping (void) +{ + NS_LOG_FUNCTION (this); +}; + +void +MpTcpMapping::SetMappingSize (uint16_t const& length) +{ + NS_LOG_DEBUG (this << " length=" << length); + m_dataLevelLength = length; +} + +bool +MpTcpMapping::TranslateSSNToDSN (const SequenceNumber32& ssn, SequenceNumber64& dsn) const +{ + if (IsSSNInRange (ssn)) + { + dsn = SequenceNumber64 (ssn - HeadSSN ()) + HeadDSN (); + return true; + } + return false; +} + +std::ostream& +operator<< (std::ostream& os, const MpTcpMapping& mapping) +{ + os << "DSN [" << mapping.HeadDSN () << "-" << mapping.TailDSN () + << "] mapped to SSN [" << mapping.HeadSSN () << "-" << mapping.TailSSN () << "]"; + return os; +} + +void +MpTcpMapping::SetHeadDSN (SequenceNumber64 const& dsn) +{ + m_dataSequenceNumber = dsn; +} + +void +MpTcpMapping::MapToSSN (SequenceNumber32 const& seq) +{ + m_subflowSequenceNumber = seq; +} + +bool +MpTcpMapping::operator == (const MpTcpMapping& mapping) const +{ + return (GetLength() == mapping.GetLength() + && HeadDSN() == mapping.HeadDSN() + && HeadSSN() == mapping.HeadSSN()); +} + +bool +MpTcpMapping::operator != (const MpTcpMapping& mapping) const +{ + return !(*this == mapping); +} + +SequenceNumber64 +MpTcpMapping::HeadDSN () const +{ + return m_dataSequenceNumber; +} + +SequenceNumber32 +MpTcpMapping::HeadSSN () const +{ + return m_subflowSequenceNumber; +} + +uint16_t +MpTcpMapping::GetLength () const +{ + return m_dataLevelLength; +} + +SequenceNumber64 +MpTcpMapping::TailDSN (void) const +{ + return (HeadDSN ()+GetLength () -1); +} + +SequenceNumber32 +MpTcpMapping::TailSSN (void) const +{ + return (HeadSSN ()+GetLength () -1); +} + +bool +MpTcpMapping::operator < (MpTcpMapping const& m) const +{ + return (HeadSSN () < m.HeadSSN ()); +} + +bool +MpTcpMapping::IsSSNInRange (SequenceNumber32 const& ssn) const +{ + return ((HeadSSN () <= ssn) && (TailSSN () >= ssn) ); +} + +bool +MpTcpMapping::IsDSNInRange (SequenceNumber64 const& dsn) const +{ + return ((HeadDSN () <= dsn) && (TailDSN () >= dsn) ); +} + +bool +MpTcpMapping::OverlapRangeSSN (const SequenceNumber32& headSSN, const uint16_t& len) const +{ + SequenceNumber32 tailSSN = headSSN + len-1; + + if (HeadSSN() > tailSSN || TailSSN () < headSSN) + { + return false; + } + NS_LOG_DEBUG ("SSN overlap"); + return true; +} + +bool +MpTcpMapping::OverlapRangeDSN (const SequenceNumber64& headDSN, const uint16_t& len) const +{ + SequenceNumber64 tailDSN = headDSN + len-1; + + if ( HeadDSN () > tailDSN || TailDSN () < headDSN) + { + return false; + } + NS_LOG_DEBUG ("DSN overlap"); + return true; +} + +///// MpTcpMappingContainer ///////////////// +MpTcpMappingContainer::MpTcpMappingContainer (void) +{ + NS_LOG_LOGIC (this); +} + +MpTcpMappingContainer::~MpTcpMappingContainer (void) +{ + NS_LOG_LOGIC (this); +} + +void +MpTcpMappingContainer::Dump () const +{ + NS_LOG_UNCOND ("\n==== Dumping list of mappings ===="); + for (MappingList::const_iterator it = m_mappings.begin (); it != m_mappings.end (); it++) + { + NS_LOG_UNCOND (*it ); + } + NS_LOG_UNCOND ("==== End of dump ====\n"); +} + +//! should return a boolean +bool +MpTcpMappingContainer::AddMapping (const MpTcpMapping& mapping) +{ + NS_LOG_LOGIC ("Adding mapping " << mapping); + + NS_ASSERT (mapping.GetLength() != 0); + std::pair res = m_mappings.insert (mapping); + + return res.second; +} + +bool +MpTcpMappingContainer::FirstUnmappedSSN (SequenceNumber32& ssn) const +{ + NS_LOG_FUNCTION_NOARGS (); + if (m_mappings.empty()) + { + return false; + } + ssn = m_mappings.rbegin ()->TailSSN () + 1; + return true; +} + +bool +MpTcpMappingContainer::DiscardMapping (const MpTcpMapping& mapping) +{ + NS_LOG_LOGIC ("discard mapping "<< mapping); + return m_mappings.erase (mapping); +} + +bool +MpTcpMappingContainer::GetMappingsStartingFromSSN (SequenceNumber32 ssn, std::set& missing) +{ + NS_LOG_FUNCTION (this << ssn ); + missing.clear (); + //http://www.cplusplus.com/reference/algorithm/equal_range/ + MpTcpMapping temp; + temp.MapToSSN (ssn); + MappingList::const_iterator it = std::lower_bound (m_mappings.begin(), m_mappings.end(), temp); + + std::copy (it, m_mappings.end (), std::inserter (missing, missing.begin ())); + return false; +} + +bool +MpTcpMappingContainer::GetMappingForSSN (const SequenceNumber32& ssn, MpTcpMapping& mapping) const +{ + NS_LOG_FUNCTION (this << ssn); + if (m_mappings.empty ()) + return false; + MpTcpMapping temp; + temp.MapToSSN (ssn); + + // Returns the first that is not less + // upper_bound returns the greater + MappingList::const_iterator it = std::upper_bound (m_mappings.begin(), m_mappings.end(), temp); + it--; + mapping = *it; + return mapping.IsSSNInRange (ssn ); // IsSSNInRange() return ( (HeadSSN() <= ssn) && (TailSSN() >= ssn) ); +} + +} // namespace ns3 Index: src/internet/model/mptcp-mapping.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-mapping.h @@ -0,0 +1,221 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#ifndef MPTCP_MAPPING_H +#define MPTCP_MAPPING_H + +#include +#include +#include +#include +#include +#include +#include "ns3/object.h" +#include "ns3/uinteger.h" +#include "ns3/traced-value.h" +#include "ns3/trace-source-accessor.h" +#include "ns3/sequence-number.h" +#include "ns3/rtt-estimator.h" +#include "ns3/event-id.h" +#include "ns3/packet.h" +#include "ns3/tcp-socket.h" +#include "ns3/ipv4-address.h" +#include "ns3/tcp-tx-buffer.h" +#include "ns3/tcp-rx-buffer.h" + +namespace ns3 +{ +/** + * \brief A base class for implementation of mapping for mptcp. + * + * This class contains the functionality for sequence number mapping. + * + * This class maps TCP sequence numbers to subflow level sequnce number (SSN) + * and connection level sequence numbers (DSN) + * DSN=Data Sequence Number (mptcp option level) + * SSN=Subflow Sequence Number (TCP legacy seq nb) + */ + +class MpTcpMapping +{ +public: + MpTcpMapping (void); + virtual ~MpTcpMapping (void); + + /** + * \brief Set subflow sequence number + * \param headSSN + */ + void MapToSSN (SequenceNumber32 const& headSSN); + + /** + * \return True if mappings share DSN space + * \param headSSN head SSN + * \param len + */ + virtual bool + OverlapRangeSSN (const SequenceNumber32& headSSN, const uint16_t& len) const; + + virtual bool + OverlapRangeDSN (const SequenceNumber64& headDSN, const uint16_t& len) const; + void SetHeadDSN (SequenceNumber64 const&); + + /** + * \brief Set mapping length + */ + virtual void SetMappingSize (uint16_t const&); + + /** + * \param ssn Data seqNb + */ + bool IsSSNInRange (SequenceNumber32 const& ssn) const; + + /** + * \param dsn Data seqNb + */ + bool IsDSNInRange (SequenceNumber64 const& dsn) const; + + /** + * \param ssn Subflow sequence number + * \param dsn Data Sequence Number + * \return True if ssn belonged to this mapping, then a dsn would have been computed + */ + bool + TranslateSSNToDSN (const SequenceNumber32& ssn, SequenceNumber64& dsn) const; + + /** + * \return The last MPTCP sequence number included in the mapping + */ + SequenceNumber64 TailDSN (void) const; + + /** + * \return The last subflow sequence number included in the mapping + */ + SequenceNumber32 TailSSN (void) const; + + /** + * \brief Necessary for + * \brief std::set to sort mappings + * Compares ssn + * \brief Compares mapping based on their DSN number. It is required when inserting into a set + */ + bool operator<(MpTcpMapping const&) const; + + /** + * \return MPTCP sequence number for the first mapped byte + */ + virtual SequenceNumber64 HeadDSN () const; + + /** + * \return subflow sequence number for the first mapped byte + */ + virtual SequenceNumber32 HeadSSN () const; + + /** + * \return mapping length + */ + virtual uint16_t GetLength() const ; + + /** + * \brief Mapping are equal if everything concord, SSN/DSN and length + */ + virtual bool operator == (const MpTcpMapping&) const; + + /** + * \return Not == + */ + virtual bool operator != (const MpTcpMapping& mapping) const; + +protected: + SequenceNumber64 m_dataSequenceNumber; //!< MPTCP sequence number + SequenceNumber32 m_subflowSequenceNumber; //!< subflow sequence number + uint16_t m_dataLevelLength; //!< mapping length / size +}; + +/** + * \brief Depending on modifications allowed in upstream ns3, it may some day inherit from TcpTxbuffer etc ... + * \brief Meanwhile we have a pointer towards the buffers. + * + * \class MpTcpMappingContainer + * Mapping handling + * Once a mapping has been advertised on a subflow, it must be honored. If the remote host already received the data + * (because it was sent in parallel over another subflow), then the received data must be discarded. + * Could be fun implemented as an interval tree + * http://www.geeksforgeeks.org/interval-tree/ + */ + +class MpTcpMappingContainer +{ +public: + MpTcpMappingContainer (void); + virtual ~MpTcpMappingContainer (void); + + /** + * \brief Discard mappings which TailDSN() < maxDsn and TailSSN() < maxSSN + * \brief When Buffers work in non renegotiable mode, + * This can be called only when dsn is in the meta socket Rx buffer and in order + * (since it may renegate some data when out of order). + * The mapping should also have been thoroughly fulfilled at the subflow level. + * it should be possible to remove them one by one + * \param mapping to be discarded + * \return Number of mappings discarded. >= 0 + */ + bool DiscardMapping (const MpTcpMapping& mapping); + + /** + * \param firstUnmappedSsn last mapped SSN. + * \return true if non empty + */ + bool FirstUnmappedSSN (SequenceNumber32& firstUnmappedSsn) const; + + /** + * \brief For debug purpose. Dump all registered mappings + */ + virtual void Dump() const; + + /** + * \brief + * Should do no check + * The mapping + * \note Check for overlap. + * \return False if the dsn range overlaps with a registered mapping, true otherwise + */ + bool AddMapping(const MpTcpMapping& mapping); + + /** + * \param l list + * \param m pass on the mapping you want to retrieve + */ + bool GetMappingForSSN(const SequenceNumber32& ssn, MpTcpMapping& m) const; + + virtual bool GetMappingsStartingFromSSN(SequenceNumber32 ssn, std::set& mappings); + +protected: + + std::set m_mappings; //!< it is a set ordered by SSN +}; + +std::ostream& operator<<(std::ostream &os, const MpTcpMapping& mapping); + +} //namespace ns3 + +#endif //MP_TCP_TYPEDEFS_H Index: src/internet/model/mptcp-ndiffports.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-ndiffports.cc @@ -0,0 +1,90 @@ + +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * 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 + * + * Author: Kashif Nadeem + */ + +#include "ns3/mptcp-ndiffports.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/tcp-l4-protocol.h" +#include "ns3/ipv4-end-point.h" +#include "ns3/ipv4-header.h" +#include "ns3/log.h" + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("MpTcpNdiffPorts"); + +NS_OBJECT_ENSURE_REGISTERED (MpTcpNdiffPorts); + +TypeId +MpTcpNdiffPorts::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns::MpTcpNdiffPorts") + .SetParent () + .SetGroupName ("Internet") + .AddConstructor () + .AddAttribute ("MaxSubflows", "Maximum number of sub-flows per each mptcp connection", + UintegerValue (4), + MakeUintegerAccessor (&MpTcpNdiffPorts::m_maxSubflows), + MakeUintegerChecker ()) + + ; + return tid; +} + +MpTcpNdiffPorts::MpTcpNdiffPorts (void) + : Object() +{ + NS_LOG_FUNCTION (this); +} + +MpTcpNdiffPorts::~MpTcpNdiffPorts (void) +{ + NS_LOG_FUNCTION (this); +} + +TypeId +MpTcpNdiffPorts::GetInstanceTypeId () const +{ + return MpTcpNdiffPorts::GetTypeId (); +} + +void +MpTcpNdiffPorts::CreateSubflows (Ptr meta, uint16_t localport, uint16_t remoteport) +{ + NS_LOG_FUNCTION (this<< meta << localport<< remoteport); + + for (int i = 0; i < m_maxSubflows; i++) + { + uint16_t randomSourcePort = rand () % 65000; + uint16_t randomDestinationPort = rand () % 65000; + + if (randomSourcePort == localport) + { + NS_LOG_UNCOND ("Generated random port is the same as meta subflow's local port, increment by +1"); + randomSourcePort++; + } + meta->CreateSingleSubflow (randomSourcePort, randomDestinationPort); + } +} + +} //namespace ns3 Index: src/internet/model/mptcp-ndiffports.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-ndiffports.h @@ -0,0 +1,78 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2007 Georgia Tech Research Corporation + * Copyright (c) 2010 Adrian Sai-wah Tam + * + * 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 + * + * Author: Kashif Nadeem + */ + +#ifndef MPTCP_NDIFFPORTS_H +#define MPTCP_NDIFFPORTS_H + +#include "ns3/mptcp-socket-base.h" +#include "ns3/ipv4-header.h" +#include "ns3/object.h" + +namespace ns3 { + +class RttEstimator; +class MpTcpSocketBase; +/** + * \ingroup socket + * \ingroup mptcp + * + * \brief A base class for implementation of a MPTCP ndiffports Path Manager. + * + * This class contains the functionality to create subflows between the same + * pair of IP between sender and receiver. This class makes to CreateSingSubglows + * from MpTcpSocketBase to create subflows with different source and destination + * port pairs + */ +class MpTcpNdiffPorts : public Object +{ +public: + /** + * Get the type ID. + * \brief Get the type ID. + * \return the object TypeId + */ + static TypeId GetTypeId (void); + + /** + * \brief Get the instance TypeId + * \return the instance TypeId + */ + virtual TypeId GetInstanceTypeId () const; + + MpTcpNdiffPorts (void); + + virtual ~MpTcpNdiffPorts (void); + /** + * \brief create subflows between source and destination IP + * \param meta MpTcpSocketBase pointer + * \param localport meta subflow source port + * \param remoteport meta subflow destination port + * \param + */ + + void CreateSubflows (Ptr meta, uint16_t localport, uint16_t remoteport); + + uint8_t m_maxSubflows {3} ; //!< Maximum number of subflows per mptcp connection +}; + +} //namespace ns3 + +#endif //MPTCP_NDIFFPORTS_H Index: src/internet/model/mptcp-scheduler-fastest-rtt.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-scheduler-fastest-rtt.cc @@ -0,0 +1,143 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#include "ns3/mptcp-scheduler-fastest-rtt.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("MpTcpSchedulerFastestRTT"); + +namespace ns3 { + +static inline +SequenceNumber64 SEQ32TO64 (SequenceNumber32 seq) +{ + return SequenceNumber64 (seq.GetValue()); +} + +TypeId +MpTcpSchedulerFastestRTT::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MpTcpSchedulerFastestRTT") + .SetParent () + .AddConstructor () + ; + + return tid; +} + +MpTcpSchedulerFastestRTT::MpTcpSchedulerFastestRTT () + : MpTcpScheduler (), + m_metaSock (0) +{ + NS_LOG_FUNCTION (this); +} + +MpTcpSchedulerFastestRTT::~MpTcpSchedulerFastestRTT (void) +{ + NS_LOG_FUNCTION (this); +} + +void +MpTcpSchedulerFastestRTT::SetMeta (Ptr metaSock) +{ + NS_ASSERT (metaSock); + NS_ASSERT_MSG (m_metaSock == 0, "SetMeta already called"); + m_metaSock = metaSock; +} + +int +MpTcpSchedulerFastestRTT::FindFastestSubflowWithFreeWindow () const +{ + NS_LOG_FUNCTION_NOARGS (); + int id = -1; + + Time lowestEstimate = Time::Max (); + for (int i = 0; i < (int) m_metaSock->GetNActiveSubflows () ; i++) + { + Ptr sf = m_metaSock->GetSubflow (i); + if (sf->AvailableWindow() <= 0) + { + continue; + } + if (sf->m_rtt->GetEstimate () < lowestEstimate) + { + lowestEstimate = sf->m_rtt->GetEstimate (); + id = i; + } + } + return id; +} + +Ptr +MpTcpSchedulerFastestRTT::GetSubflowToUseForEmptyPacket () +{ + NS_ASSERT (m_metaSock->GetNActiveSubflows() > 0 ); + return m_metaSock->GetSubflow (0); +} + +bool +MpTcpSchedulerFastestRTT::GenerateMapping (int& activeSubflowArrayId, SequenceNumber64& dsn, uint16_t& length) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (m_metaSock); + + uint32_t amountOfDataToSend = 0; + //! Tx data not sent to subflows yet + SequenceNumber32 metaNextTxSeq = m_metaSock->Getvalue_nextTxSeq (); + amountOfDataToSend = m_metaSock->m_txBuffer->SizeFromSequence (metaNextTxSeq); + uint32_t metaWindow = m_metaSock->AvailableWindow (); + if (amountOfDataToSend <= 0) + { + NS_LOG_DEBUG ("Nothing to send from meta"); + return false; + } + if (metaWindow <= 0) + { + return false; + } + + int id = FindFastestSubflowWithFreeWindow (); + if (id < 0) + { + NS_LOG_DEBUG ("No valid subflow"); + return false; + } + Ptr subflow = m_metaSock->GetSubflow (id); + uint32_t subflowWindow = subflow->AvailableWindow (); + uint32_t canSend = std::min (subflowWindow, metaWindow); + + //! Can't send more than SegSize + //metaWindow en fait on s'en fout du SegSize ? + if (canSend > 0) + { + dsn = SEQ32TO64 (metaNextTxSeq); + canSend = std::min (canSend, amountOfDataToSend); + // For now we limit ourselves to a per packet basis + length = std::min (canSend, subflow->GetSegSize ()); + return true; + } + return false; +} + +} // namespace ns3 Index: src/internet/model/mptcp-scheduler-fastest-rtt.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-scheduler-fastest-rtt.h @@ -0,0 +1,78 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Author: Matthieu Coudron + * Morteza Kheirkhah + */ +#ifndef MPTCP_SCHEDULER_FASTEST_RTT_H +#define MPTCP_SCHEDULER_FASTEST_RTT_H + +#include "ns3/mptcp-scheduler.h" +#include "ns3/object.h" +#include "ns3/ptr.h" +#include "ns3/mptcp-scheduler-fastest-rtt.h" +#include +#include + +namespace ns3 { + +class MpTcpSocketBase; +class MpTcpSubflow; + +class MpTcpSchedulerFastestRTT : public MpTcpScheduler +{ +public: + static TypeId + GetTypeId (void); + + MpTcpSchedulerFastestRTT (); + virtual ~MpTcpSchedulerFastestRTT (); + void SetMeta (Ptr metaSock); + + /** + * \brief This function is responsible for generating a list of packets to send + * and to specify on which subflow to send. + * + * These *mappings* will be passed on to the meta socket that will send them without altering the + * mappings. + * It is of utmost importance to generate a perfect mapping !!! Any deviation + * from the foreseen mapping will trigger an error and crash the simulator + * + * \warn This function MUST NOT fiddle with metasockInternal + * subflowId: pair(start,size) + */ + virtual bool GenerateMapping (int& activeSubflowArrayId, SequenceNumber64& dsn, uint16_t& length); + + /** + * \return -1 if could not find a valid subflow, the subflow id otherwise + */ + virtual int FindFastestSubflowWithFreeWindow () const; + + /** + * \return Index of subflow to use + */ + virtual Ptr GetSubflowToUseForEmptyPacket (); + +protected: + Ptr m_metaSock; //!< +}; + +} // end of 'ns3' + +#endif /* MPTCP_SCHEDULER_ROUND_ROBIN_H */ Index: src/internet/model/mptcp-scheduler-round-robin.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-scheduler-round-robin.cc @@ -0,0 +1,125 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ + +#include "ns3/mptcp-scheduler-round-robin.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/log.h" + +NS_LOG_COMPONENT_DEFINE ("MpTcpSchedulerRoundRobin"); + +namespace ns3 { + +static inline +SequenceNumber64 SEQ32TO64 (SequenceNumber32 seq) +{ + return SequenceNumber64 (seq.GetValue()); +} + +TypeId +MpTcpSchedulerRoundRobin::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MpTcpSchedulerRoundRobin") + .SetParent () + .AddConstructor () + ; + return tid; +} + +MpTcpSchedulerRoundRobin::MpTcpSchedulerRoundRobin () + : MpTcpScheduler (), + m_lastUsedFlowId (0), + m_metaSock (0) +{ + NS_LOG_FUNCTION (this); +} + +MpTcpSchedulerRoundRobin::~MpTcpSchedulerRoundRobin (void) +{ + NS_LOG_FUNCTION (this); +} + +void +MpTcpSchedulerRoundRobin::SetMeta (Ptr metaSock) +{ + NS_ASSERT (metaSock); + NS_ASSERT_MSG (m_metaSock == 0, "SetMeta already called"); + m_metaSock = metaSock; +} + +Ptr +MpTcpSchedulerRoundRobin::GetSubflowToUseForEmptyPacket () +{ + NS_ASSERT (m_metaSock->GetNActiveSubflows () > 0 ); + return m_metaSock->GetSubflow (0); +} + +/* We assume scheduler can't send data on subflows, so it can just generate mappings */ +bool +MpTcpSchedulerRoundRobin::GenerateMapping (int& activeSubflowArrayId, SequenceNumber64& dsn, uint16_t& length) +{ + NS_LOG_FUNCTION (this); + NS_ASSERT (m_metaSock); + + int nbOfSubflows = m_metaSock->GetNActiveSubflows (); + int attempt = 0; + uint32_t amountOfDataToSend = 0; + + //! Tx data not sent to subflows yet + SequenceNumber32 metaNextTxSeq = m_metaSock->Getvalue_nextTxSeq (); + amountOfDataToSend = m_metaSock->m_txBuffer->SizeFromSequence (metaNextTxSeq); + uint32_t metaWindow = m_metaSock->AvailableWindow (); + + if (amountOfDataToSend <= 0) + { + NS_LOG_DEBUG ("Nothing to send from meta"); + return false; + } + if (metaWindow <= 0) + { + NS_LOG_DEBUG ("No meta window available"); + return false; + } + while (attempt < nbOfSubflows) + { + attempt++; + m_lastUsedFlowId = (m_lastUsedFlowId + 1) % nbOfSubflows; + Ptr subflow = m_metaSock->GetSubflow (m_lastUsedFlowId); + uint32_t subflowWindow = subflow->AvailableWindow (); + uint32_t canSend = std::min (subflowWindow, metaWindow); + + //! Can't send more than SegSize + if (canSend > 0) + { + activeSubflowArrayId = m_lastUsedFlowId; + dsn = SEQ32TO64 (metaNextTxSeq); + canSend = std::min (canSend, amountOfDataToSend); + // For now we limit ourselves to a per packet basis + length = std::min (canSend, subflow->GetSegSize ()); + return true; + } + } + return false; +} + +} // end of 'ns3' Index: src/internet/model/mptcp-scheduler-round-robin.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-scheduler-round-robin.h @@ -0,0 +1,75 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#ifndef MPTCP_SCHEDULER_ROUND_ROBIN_H +#define MPTCP_SCHEDULER_ROUND_ROBIN_H + +#include "ns3/mptcp-scheduler.h" +#include "ns3/object.h" +#include "ns3/ptr.h" +#include "ns3/mptcp-scheduler-round-robin.h" +#include +#include + +namespace ns3 { + +class MpTcpSocketBase; +class MpTcpSubflow; + +class MpTcpSchedulerRoundRobin : public MpTcpScheduler +{ +public: + static TypeId + GetTypeId (void); + + MpTcpSchedulerRoundRobin (); + virtual ~MpTcpSchedulerRoundRobin (); + void SetMeta (Ptr metaSock); + + /** + * \brief This function is responsible for generating a list of packets to send + * and to specify on which subflow to send. + * + * These *mappings* will be passed on to the meta socket that will send them without altering the + * mappings. + * It is of utmost importance to generate a perfect mapping !!! Any deviation + * from the foreseen mapping will trigger an error and crash the simulator + * + * \warn This function MUST NOT fiddle with metasockInternal + * subflowId: pair(start,size) + */ + virtual bool GenerateMapping (int& activeSubflowArrayId, SequenceNumber64& dsn, uint16_t& length); + + /** + * \brief chooseSubflowForRetransmit + * \return Index of subflow to use + */ + virtual Ptr GetSubflowToUseForEmptyPacket (); + +protected: + uint8_t m_lastUsedFlowId; //!< keep track of last used subflow + Ptr m_metaSock; //!< +}; + +} // end of 'ns3' + +#endif /* MPTCP_SCHEDULER_ROUND_ROBIN_H */ Index: src/internet/model/mptcp-scheduler.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-scheduler.h @@ -0,0 +1,68 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Matthieu Coudron + */ +#ifndef MPTCP_SCHEDULER_H +#define MPTCP_SCHEDULER_H + +#include +#include "ns3/ptr.h" +#include "ns3/object.h" +#include "ns3/sequence-number.h" + +namespace ns3 { + +class MpTcpSocketBase; + +/** + * This class is responsible for + * + * The mptcp linux 0.90 scheduler is composed of mainly 2 functions: + * -get_available_subflow + * -get_next_segment + * + * Here we wanted to let a maximum number of possibilities for testing, for instance: + * - you can generate DSS for data that is not in the meta Tx buffer yet (in order to minimize the number of DSS sent). + * - you can send the + * + * The scheduler maps a dsn range to a subflow. It does not map the dsn to an ssn: this is done + * by the subflow when it actually receives the Tx data. + * + * \warn The decoupling between dsn & ssn mapping may prove hard to debug. There are + * some checks but you should be especially careful when writing a new scheduler. + */ +class MpTcpScheduler : public Object +{ + +public: + virtual ~MpTcpScheduler() {} + + /** + * \param activeSubflowArrayId + * \param uint16_t + * \return true if could generate a mapping + * \see MpTcpSocketBase::SendPendingData + */ + virtual bool GenerateMapping (int& activeSubflowArrayId, SequenceNumber64& dsn, uint16_t& length) = 0; + + virtual void SetMeta (Ptr metaSock) = 0; +}; + +} + +#endif /* MPTCP_SCHEDULER_H */ Index: src/internet/model/mptcp-socket-base.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-socket-base.cc @@ -0,0 +1,1546 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ + +#include +#include +#include +#include +#include +#include "ns3/abort.h" +#include "ns3/log.h" +#include "ns3/enum.h" +#include "ns3/string.h" +#include "ns3/tcp-l4-protocol.h" +#include "ns3/ipv4-l3-protocol.h" +#include "ns3/error-model.h" +#include "ns3/point-to-point-channel.h" +#include "ns3/point-to-point-net-device.h" +#include "ns3/pointer.h" +#include "ns3/drop-tail-queue.h" +#include "ns3/object-vector.h" +#include "ns3/mptcp-scheduler-round-robin.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/tcp-option-mptcp.h" +#include "ns3/callback.h" +#include "ns3/trace-helper.h" +#include "ns3/mptcp-ndiffports.h" +#include "ns3/mptcp-fullmesh.h" + +using namespace std; + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("MpTcpSocketBase"); + +NS_OBJECT_ENSURE_REGISTERED (MpTcpSocketBase); + +static inline +SequenceNumber32 SEQ64TO32 (SequenceNumber64 seq) +{ + return SequenceNumber32 (seq.GetValue()); +} + +static inline +SequenceNumber64 SEQ32TO64 (SequenceNumber32 seq) +{ + return SequenceNumber64 (seq.GetValue()); +} + +//! wrapper function +static inline +MpTcpMapping +GetMapping (Ptr dss) +{ + MpTcpMapping mapping; + uint64_t dsn; + uint32_t ssn; + uint16_t length; + + dss->GetMapping (dsn, ssn, length); + mapping.SetHeadDSN (SequenceNumber64(dsn)); + mapping.SetMappingSize (length); + mapping.MapToSSN (SequenceNumber32(ssn)); + return mapping; +} + +TypeId +MpTcpSocketBase::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::MpTcpSocketBase") + .SetParent () + .SetGroupName ("Internet") + .AddConstructor () + .AddAttribute ("SocketType", + "Socket type of TCP objects.", + TypeIdValue (MpTcpSubflow::GetTypeId ()), + MakeTypeIdAccessor (&MpTcpSocketBase::m_subflowTypeId), + MakeTypeIdChecker ()) + .AddAttribute ("Scheduler", + "How to generate the mappings", + TypeIdValue (MpTcpScheduler::GetTypeId ()), + MakeTypeIdAccessor (&MpTcpSocketBase::m_schedulerTypeId), + MakeTypeIdChecker ()) + .AddAttribute("PathManagerMode", + "Mechanism for establishing new sub-flows", + EnumValue (MpTcpSocketBase::FullMesh), + MakeEnumAccessor (&MpTcpSocketBase::m_pathManager), + MakeEnumChecker (MpTcpSocketBase::Default,"Default", + MpTcpSocketBase::FullMesh, "FullMesh", + MpTcpSocketBase::nDiffPorts, "nDiffPorts")) + + ; + return tid; +} + +static const std::string containerNames[MpTcpSocketBase::Maximum] = { + "Established", + "Others", + "Closing" +}; + +MpTcpSocketBase::MpTcpSocketBase (const TcpSocketBase& sock) + : TcpSocketBase (sock), + m_peerToken (0), + m_peerKey (0), + m_doChecksum (false), + m_receivedDSS (false), + m_multipleSubflows (false) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Copying from TcpSocketBase"); + CreateScheduler (m_schedulerTypeId); +} + +MpTcpSocketBase::MpTcpSocketBase (const MpTcpSocketBase& sock) + : TcpSocketBase (sock), + m_peerToken (sock.m_peerToken), + m_peerKey (sock.m_peerKey), + m_doChecksum (sock.m_doChecksum), + m_receivedDSS (sock.m_receivedDSS), + m_multipleSubflows (sock.m_multipleSubflows), + m_subflowConnectionSucceeded (sock.m_subflowConnectionSucceeded), + m_subflowConnectionFailure (sock.m_subflowConnectionFailure), + m_joinRequest (sock.m_joinRequest), + m_subflowCreated (sock.m_subflowCreated), + m_subflowTypeId (sock.m_subflowTypeId), + m_schedulerTypeId (sock.m_schedulerTypeId) +{ + NS_LOG_FUNCTION (this); + NS_LOG_LOGIC ("Invoked the copy constructor"); + //! Scheduler may have some states, thus generate a new one + CreateScheduler (m_schedulerTypeId); +} + +MpTcpSocketBase::MpTcpSocketBase () + : TcpSocketBase (), + m_peerToken (0), + m_peerKey (0), + m_doChecksum (false), + m_receivedDSS (false), + m_multipleSubflows (false), + m_subflowTypeId (MpTcpSubflow::GetTypeId ()), + m_schedulerTypeId (MpTcpSchedulerRoundRobin::GetTypeId ()) +{ + NS_LOG_FUNCTION (this); + + //not considered as an Object + CreateScheduler (m_schedulerTypeId); + m_subflowConnectionSucceeded = MakeNullCallback > (); + m_subflowConnectionFailure = MakeNullCallback > (); +} + +MpTcpSocketBase::~MpTcpSocketBase (void) +{ + NS_LOG_FUNCTION (this); + m_node = 0; + + if (m_scheduler ) + { + } + m_subflowConnectionSucceeded = MakeNullCallback > (); + m_subflowCreated = MakeNullCallback > (); + m_subflowConnectionSucceeded = MakeNullCallback > (); +} + +void +MpTcpSocketBase::CreateScheduler (TypeId schedulerTypeId) +{ + NS_LOG_FUNCTION (this); + NS_LOG_WARN ("Overriding scheduler choice"); + ObjectFactory schedulerFactory; + schedulerTypeId = MpTcpSchedulerRoundRobin::GetTypeId (); + schedulerFactory.SetTypeId (schedulerTypeId); + m_scheduler = schedulerFactory.Create (); + m_scheduler->SetMeta (this); +} + +void +MpTcpSocketBase::SetPathManager (PathManagerMode pathManager) +{ + m_pathManager = pathManager; +} + +int +MpTcpSocketBase::ConnectNewSubflow (const Address &local, const Address &remote) +{ + NS_ASSERT_MSG (InetSocketAddress::IsMatchingType (local) && InetSocketAddress::IsMatchingType (remote), "only support ipv4"); + + NS_LOG_LOGIC ("Trying to add a new subflow " << InetSocketAddress::ConvertFrom(local).GetIpv4 () + << "->" << InetSocketAddress::ConvertFrom(remote).GetIpv4()); + // not constructed properly since MpTcpSocketBase creation is hackish + // and does not call CompleteConstruct + m_subflowTypeId = MpTcpSubflow::GetTypeId (); + Ptr socket = m_tcp->CreateSocket (m_congestionControl, m_subflowTypeId); + NS_ASSERT (socket); + Ptr sf = DynamicCast (socket); + NS_ASSERT (sf); + AddSubflow (sf); + NS_ASSERT (sf->Bind (local) == 0); + int ret = sf->Connect( remote); + + return ret; +} + +uint64_t +MpTcpSocketBase::GetLocalKey () const +{ + return m_mptcpLocalKey; +} + +uint32_t +MpTcpSocketBase::GetLocalToken () const +{ + return m_mptcpLocalToken; +} + +uint32_t +MpTcpSocketBase::GetPeerToken () const +{ + return m_peerToken; +} + +uint64_t +MpTcpSocketBase::GetPeerKey () const +{ + return m_peerKey; +} + +void +MpTcpSocketBase::NotifyRemoteAddAddr (Address address) +{ + if (!m_onRemoteAddAddr.IsNull ()) + { + m_onRemoteAddAddr (this, address, 0); + } +} + +bool +MpTcpSocketBase::DoChecksum () const +{ + return false; +} + +MpTcpSocketBase::SubflowList::size_type +MpTcpSocketBase::GetNActiveSubflows () const +{ + return m_subflows[Established].size (); +} + +Ptr +MpTcpSocketBase::GetSubflow (uint8_t id) const +{ + NS_ASSERT_MSG (id < m_subflows[Established].size (), "Trying to get an unexisting subflow"); + return m_subflows[Established][id]; +} + +void +MpTcpSocketBase::SetPeerKey (uint64_t remoteKey) +{ + uint64_t idsn = 0; + m_peerKey = remoteKey; + // use the one from mptcp-crypo.h + GenerateTokenForKey (HMAC_SHA1, m_peerKey, m_peerToken, idsn); +} + +// in fact it just calls SendPendingData() +int +MpTcpSocketBase::Send (Ptr p, uint32_t flags) +{ + NS_LOG_FUNCTION (this); + //! This will check for established state + return TcpSocketBase::Send (p,flags); +} + +bool +MpTcpSocketBase::UpdateWindowSize (const TcpHeader& header) +{ + NS_LOG_FUNCTION (this); + m_rWnd = header.GetWindowSize (); + return true; +} + +/* Inherit from Socket class: Get the max number of bytes an app can read */ +uint32_t +MpTcpSocketBase::GetRxAvailable (void) const +{ + NS_LOG_FUNCTION (this); + return m_rxBuffer->Available (); +} + +void +MpTcpSocketBase::OnSubflowClosed (Ptr subflow, bool reset) +{ + NS_LOG_LOGIC ("Subflow " << subflow << " definitely closed"); + if (reset) + { + NS_FATAL_ERROR ("Case not handled yet."); + } + SubflowList::iterator it = std::remove (m_subflows[Closing].begin (), m_subflows[Closing].end (), subflow); +} + +/* May return a bool to let subflow know if it should return a ack ? + it would leave the possibility for meta to send ack on another subflow. + We have to extract data from subflows on a per mapping basis because mappings + may not necessarily be contiguous */ +void +MpTcpSocketBase::OnSubflowRecv (Ptr sf) +{ + NS_LOG_FUNCTION (this << "Received data from subflow=" << sf); + SequenceNumber32 expectedDSN = m_rxBuffer->NextRxSequence (); + + /* Extract one by one mappings from subflow */ + while (true) + { + Ptr p; + SequenceNumber64 dsn; + TcpHeader tcpHeader; + uint32_t canRead = m_rxBuffer->MaxBufferSize () - m_rxBuffer->Size (); + + if (canRead <= 0) + { + NS_LOG_LOGIC ("No free space in meta Rx Buffer"); + break; + } + /* Todo tell if we stop to extract only between mapping boundaries or if Extract */ + p = sf->ExtractAtMostOneMapping (canRead, true, dsn); + if (p->GetSize () == 0) + { + NS_LOG_DEBUG ("packet extracted empty."); + break; + } + // THIS MUST WORK. else we removed the data from subflow buffer so it would be lost + // Pb here, htis will be extracted but will not be saved into the main buffer + // Notify app to receive if necessary + tcpHeader.SetSequenceNumber (SEQ64TO32(dsn)); + if (!m_rxBuffer->Add(p, tcpHeader)) + { + NS_FATAL_ERROR("Data might have been lost"); + } + } + if (expectedDSN < m_rxBuffer->NextRxSequence ()) + { + NS_LOG_LOGIC ("The Rxbuffer advanced"); + + // NextRxSeq advanced, we have something to send to the app + if (!m_shutdownRecv) + { + //<< m_receivedData + NS_LOG_LOGIC ("Notify data Rcvd" ); + NotifyDataRecv (); + } + // Handle exceptions + if (m_closeNotified) + { + NS_LOG_WARN ("Why TCP " << this << " got data after close notification?"); + } + } +} + +void +MpTcpSocketBase::OnSubflowNewCwnd (std::string context, uint32_t oldCwnd, uint32_t newCwnd) +{ + NS_LOG_LOGIC ("Subflow updated window from " << oldCwnd << " to " << newCwnd ); + // maybe ComputeTotalCWND should be left + m_tcb->m_cWnd = ComputeTotalCWND (); +} + +/* add a MakeBoundCallback that accepts a member function as first input */ +static void +onSubflowNewState (Ptr meta, Ptr sf, + TcpSocket::TcpStates_t oldState, TcpSocket::TcpStates_t newState) +{ + NS_LOG_UNCOND ("onSubflowNewState wrapper"); + meta->OnSubflowNewState ("context", sf, oldState, newState); +} + +/* use it to Notify user of, we need a MakeBoundCallback */ +void +MpTcpSocketBase::OnSubflowNewState (std::string context, Ptr sf, + TcpSocket::TcpStates_t oldState, TcpSocket::TcpStates_t newState) +{ + NS_LOG_LOGIC ("subflow " << sf << " state changed from " << TcpStateName[oldState] << " to " << TcpStateName[newState]); + NS_LOG_LOGIC ("Current rWnd=" << m_rWnd); + ComputeTotalCWND (); + + if (sf->IsMaster () && newState == SYN_RCVD) + { + NS_LOG_LOGIC("moving meta to SYN_RCVD"); + m_state = SYN_RCVD; + m_rWnd = sf->m_rWnd; + } + // Only react when it gets established + if (newState == ESTABLISHED) + { + MoveSubflow (sf, Others, Established) ; + + // subflow did SYN_RCVD -> ESTABLISHED + if (oldState == SYN_RCVD) + { + NS_LOG_LOGIC ("Subflow created"); + OnSubflowEstablished (sf); + } + else if (oldState == SYN_SENT) + { + OnSubflowEstablishment (sf); + } + else + { + NS_FATAL_ERROR ("Unhandled case"); + } + } +} + +void +MpTcpSocketBase::OnSubflowCreated (Ptr socket, const Address &from) +{ + NS_LOG_LOGIC(this); + Ptr sf = DynamicCast (socket); + NotifySubflowCreated (sf); +} + +void +MpTcpSocketBase::OnSubflowConnectionSuccess (Ptr socket) +{ + NS_LOG_LOGIC (this); + Ptr sf = DynamicCast (socket); + NotifySubflowConnected (sf); +} + +void +MpTcpSocketBase::OnSubflowConnectionFailure (Ptr socket) +{ + NS_LOG_LOGIC (this); + Ptr sf = DynamicCast (socket); + if (sf->IsMaster ()) + { + TcpSocketBase::NotifyConnectionFailed (); + } + else + { + // use a specific callback + NS_FATAL_ERROR ("TODO"); + } +} + +void +MpTcpSocketBase::AddSubflow (Ptr sflow) +{ + NS_LOG_FUNCTION (sflow); + Ptr sf = sflow; + bool ok; + ok = sf->TraceConnect ("CongestionWindow", "CongestionWindow", MakeCallback (&MpTcpSocketBase::OnSubflowNewCwnd, this)); + NS_ASSERT_MSG (ok, "Tracing mandatory to update the MPTCP global congestion window"); + + //! We need to act on certain subflow state transitions according to doc "There is not a version with bound arguments." + ok = sf->TraceConnectWithoutContext ("State", MakeBoundCallback (&onSubflowNewState, this, sf)); + NS_ASSERT_MSG (ok, "Tracing mandatory to update the MPTCP socket state"); + + if (sf->IsMaster()) + { + //! then we update + m_state = sf->GetState(); + m_mptcpLocalKey = sf->m_mptcpLocalKey; + m_mptcpLocalToken = sf->m_mptcpLocalToken; + NS_LOG_DEBUG("Set master key/token to "<< m_mptcpLocalKey << "/" << m_mptcpLocalToken); + + // Those may be overriden later + m_endPoint = sf->m_endPoint; + m_endPoint6 = sf->m_endPoint6; + } + sf->SetMeta (this); + + /** We override here callbacks so that subflows + * don't communicate with the applications directly. The meta socket will + */ + sf->SetConnectCallback (MakeCallback (&MpTcpSocketBase::OnSubflowConnectionSuccess,this), + MakeCallback (&MpTcpSocketBase::OnSubflowConnectionFailure,this)); // Ok + sf->SetAcceptCallback ( + MakeNullCallback, const Address &> (), + MakeCallback (&MpTcpSocketBase::OnSubflowCreated,this)); + sf->SetCongestionControlAlgorithm (this->m_congestionControl); + m_subflows[Others].push_back (sf); +} + +/* I ended up duplicating this code to update the meta r_Wnd, + which would have been hackish otherwise */ +void +MpTcpSocketBase::ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface) +{ + NS_LOG_DEBUG (this << " called with endpoint " << m_endPoint); + NS_FATAL_ERROR ("This socket should never receive any packet"); +} + +bool +MpTcpSocketBase::NotifyJoinRequest (const Address &from, const Address & toAddress) +{ + NS_LOG_FUNCTION (this << &from); + if (!m_joinRequest.IsNull ()) + { + return m_joinRequest (this, from, toAddress); + } + else + { + // accept all incoming connections by default. + // this way people writing code don't have to do anything + // special like register a callback that returns true + // just to get incoming connections + return true; + } +} + +bool +MpTcpSocketBase::OwnIP (const Address& address) const +{ + NS_LOG_FUNCTION (this); + NS_ASSERT_MSG (Ipv4Address::IsMatchingType(address), "only ipv4 supported for now"); + Ipv4Address ip = Ipv4Address::ConvertFrom (address); + Ptr node = GetNode (); + Ptr ipv4 = node->GetObject (); + return (ipv4->GetInterfaceForAddress (ip) >= 0); + return false; +} + +Ipv4EndPoint* +MpTcpSocketBase::NewSubflowRequest (Ptr< Packet> p, const TcpHeader & tcpHeader, const Address & fromAddress, + const Address & toAddress, Ptr join) +{ + NS_LOG_LOGIC ("Received request for a new subflow while in state " << TcpStateName[m_state]); + NS_ASSERT_MSG (InetSocketAddress::IsMatchingType (fromAddress) && InetSocketAddress::IsMatchingType (toAddress), + "Source and destination addresses should be of the same type"); + NS_ASSERT (join); + NS_ASSERT (join->GetPeerToken () == GetLocalToken ()); + + // check we can accept the creation of a new subflow (did we receive a DSS already ?) + if (!FullyEstablished() ) + { + NS_LOG_WARN ("Received an MP_JOIN while meta not fully established yet."); + return 0; + } + Ipv4Address ip = InetSocketAddress::ConvertFrom (toAddress).GetIpv4 (); + if (!OwnIP (ip)) + { + NS_LOG_WARN ("This host does not own the ip " << ip); + return 0; + } + // Similar to NotifyConnectionRequest + bool accept_connection = NotifyJoinRequest (fromAddress, toAddress); + if (!accept_connection) + { + NS_LOG_LOGIC("Refusing establishement of a new subflow"); + return 0; + } + Ptr subflow ; + m_subflowTypeId = MpTcpSubflow::GetTypeId (); + Ptr sock = m_tcp->CreateSocket (m_congestionControl, m_subflowTypeId); + subflow = DynamicCast (sock); + AddSubflow (subflow); + // Call it now so that endpoint gets allocated + subflow->CompleteFork (p, tcpHeader, fromAddress, toAddress); + return subflow->m_endPoint; +} + +/* Create a new subflow with different source and destination port pairs */ +bool +MpTcpSocketBase::CreateSingleSubflow (uint16_t randomSourcePort, uint16_t randomDestinationPort) +{ + NS_LOG_FUNCTION (this << randomSourcePort << randomDestinationPort); + Ptr subflow; + Ptr sock = m_tcp->CreateSocket (m_congestionControl, MpTcpSubflow::GetTypeId ()); + subflow = DynamicCast (sock); + AddSubflow (subflow); + + // Set up subflow based on different source ports + Ipv4Address sAddr = m_endPoint->GetLocalAddress (); + uint16_t sPort = randomSourcePort ; + Ipv4Address dAddr = m_endPoint->GetPeerAddress (); + uint16_t dPort = randomDestinationPort; + NS_LOG_INFO ("Creating Subflow with: sAddr "<< sAddr<<" sPort "<m_tcb->m_cWnd = m_tcb->m_segmentSize; + subflow->m_state = SYN_SENT; + subflow->m_synCount = m_synCount; + subflow->m_synCount = m_synRetries; + subflow->m_dataRetrCount = m_dataRetries; + subflow->m_endPoint = m_tcp->Allocate (GetBoundNetDevice (), sAddr, sPort, dAddr, dPort); + NS_LOG_INFO ("subflow endPoiont "<< subflow->m_endPoint); + m_endPoint = subflow->m_endPoint; + subflow->SetupCallback (); + bool result = m_tcp->AddSocket (this); + if (!result) + { + NS_LOG_INFO("InitiateSinglesubflow/ Can't add socket: already registered ? Can be because of mptcp"); + } + subflow->SendEmptyPacket (TcpHeader::SYN); + return true; +} + +/* Create a new subflow with different source and destination IP pairs */ +bool +MpTcpSocketBase::CreateSubflowsForMesh () +{ + Ipv4Address sAddr = m_endPoint->GetLocalAddress (); + Ipv4Address dAddr = m_endPoint->GetPeerAddress (); + uint16_t sPort = m_endPoint->GetLocalPort (); + uint16_t dPort = m_endPoint->GetPeerPort (); + + for (auto it = LocalAddressInfo.begin (); it != LocalAddressInfo.end (); it++) + { + // To get hold of the class pointers: + Ipv4Address localAddress = it->first; + for (auto it = RemoteAddressInfo.begin (); it != RemoteAddressInfo.end (); it++) + { + // To get hold of the class pointers: + Ipv4Address remoteAddress = it->first; + uint16_t localPort = rand () % 65000; + uint16_t remotePort = rand () % 65000; + if (localPort == sPort) + { + NS_LOG_UNCOND ("Generated random port is the same as meta subflow's local port, increment by +1"); + localPort++; + } + if (remotePort == dPort) + { + NS_LOG_UNCOND ("Generated random port is the same as meta subflow's peer port, increment by +1"); + remotePort++; + } + if ( (sAddr == localAddress) && (dAddr == remoteAddress) ) + { + continue; + } + // Set up subflow based on different source ports + Ptr subflow; + Ptr sock = m_tcp->CreateSocket (m_congestionControl, MpTcpSubflow::GetTypeId ()); + subflow = DynamicCast (sock); + AddSubflow (subflow); + subflow->m_tcb->m_cWnd = m_tcb->m_segmentSize; + subflow->m_state = SYN_SENT; + subflow->m_synCount = m_synCount; + subflow->m_synCount = m_synRetries; + subflow->m_dataRetrCount = m_dataRetries; + subflow->m_endPoint = m_tcp->Allocate (GetBoundNetDevice (), localAddress, localPort, remoteAddress, remotePort); + NS_LOG_INFO ("subflow endPoiont "<< subflow->m_endPoint); + m_endPoint = subflow->m_endPoint; + subflow->SetupCallback (); + bool result = m_tcp->AddSocket (this); + if (!result) + { + NS_LOG_INFO ("InitiateSinglesubflow/ Can't add socket: already registered ? Can be because of mptcp"); + } + subflow->SendEmptyPacket(TcpHeader::SYN); + } + } + return true; +} + +void +MpTcpSocketBase::SendFastClose (Ptr sf) +{ + NS_LOG_LOGIC ("Sending MP_FASTCLOSE"); + TcpHeader header; + Ptr opt = Create (); + + opt->SetPeerKey (GetPeerKey() ); + sf->SendEmptyPacket (TcpHeader::RST); + TimeWait (); +} + +void +MpTcpSocketBase::ConnectionSucceeded (void) +{ + NS_LOG_FUNCTION (this); + m_connected = true; + TcpSocketBase::ConnectionSucceeded (); +} + +bool +MpTcpSocketBase::IsConnected () const +{ + return m_connected; +} + +/* Inherited from Socket class: Bind socket to an end-point in MpTcpL4Protocol */ +int +MpTcpSocketBase::Bind () +{ + NS_LOG_FUNCTION (this); + m_endPoint = m_tcp->Allocate (); // Create endPoint with ephemeralPort + if (0 == m_endPoint) + { + m_errno = ERROR_ADDRNOTAVAIL; + return -1; + } + return SetupCallback (); +} + +/* Clean up after Bind operation. Set up callback function in the end-point */ +int +MpTcpSocketBase::SetupCallback () +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::SetupCallback (); +} + +/* Inherit from socket class: Bind socket (with specific address) to an end-point in TcpL4Protocol +cconvert to noop +*/ +int +MpTcpSocketBase::Bind (const Address &address) +{ + NS_LOG_FUNCTION (this< sf) +{ + NS_LOG_LOGIC ("Datafin with seq=" << dsn); + if (dsn < m_rxBuffer->NextRxSequence () || m_rxBuffer->MaxRxSequence () < dsn) + { + NS_LOG_INFO ("dsn " << dsn << " out of expected range [ " << m_rxBuffer->NextRxSequence () << " - " << m_rxBuffer->MaxRxSequence () << " ]" ); + return ; + } + // For any case, remember the FIN position in rx buffer first + //! +1 because the datafin doesn't count as payload + m_rxBuffer->SetFinSequence (dsn); + NS_LOG_LOGIC ("Accepted MPTCP FIN at seq " << dsn); + + // Return if FIN is out of sequence, otherwise move to CLOSE_WAIT state by DoPeerClose + if (!m_rxBuffer->Finished()) + { + NS_LOG_WARN ("Out of range"); + return; + } + + // Simultaneous close: Application invoked Close() when we are processing this FIN packet + TcpStates_t old_state = m_state; + switch (m_state) + { + case FIN_WAIT_1: + m_state = CLOSING; + break; + case FIN_WAIT_2: + // will go into timewait later + TimeWait(); + break; + case ESTABLISHED: + m_state = CLOSE_WAIT; + break; + default: + NS_FATAL_ERROR ("Should not be here"); + break; + }; + NS_LOG_INFO (TcpStateName[old_state] << " -> " << TcpStateName[m_state]); + TcpHeader header; + sf->AppendDSSAck (); + sf->SendEmptyPacket (TcpHeader::ACK); +} + +//This should take care of ReTxTimeout +void +MpTcpSocketBase::NewAck (SequenceNumber32 const& dsn, bool resetRTO) +{ + NS_LOG_FUNCTION (this << " new dataack=[" << dsn << "]"); + TcpSocketBase::NewAck (dsn, resetRTO); +} + +bool +MpTcpSocketBase::IsInfiniteMappingEnabled () const +{ + return false; +} + +// Send 1-byte data to probe for the window size at the receiver when +// the local knowledge tells that the receiver has zero window size +// C.f.: RFC793 p.42, RFC1112 sec.4.2.2.17 +void +MpTcpSocketBase::PersistTimeout () +{ + NS_LOG_LOGIC ("PersistTimeout expired at " << Simulator::Now ().GetSeconds ()); + NS_FATAL_ERROR ("TODO"); +} + +void +MpTcpSocketBase::BecomeFullyEstablished () +{ + NS_LOG_FUNCTION (this); + m_receivedDSS = true; + + // should be called only on client side + ConnectionSucceeded (); +} + +bool +MpTcpSocketBase::FullyEstablished () const +{ + NS_LOG_FUNCTION_NOARGS (); + return m_receivedDSS; +} + +/* Sending data via subflows with available window size. + Todo somehow rename to dispatch + we should not care about IsInfiniteMapping () */ +uint32_t +MpTcpSocketBase::SendPendingData (bool withAck) +{ + NS_LOG_FUNCTION (this << "Sending data" << TcpStateName[m_state]); + + // To initiate path managers after first DSS + if (FullyEstablished() ) + { + if (!m_multipleSubflows) + { + NS_LOG_INFO(" Path manager called"); + //selecting path manager + if (m_pathManager == MpTcpSocketBase::Default) + { + //Default path mananger + } + else if (m_pathManager == MpTcpSocketBase::FullMesh) + { + //fullmesh path manager + Ptr m_fullMesh = Create(); + m_fullMesh->CreateMesh(this); + } + else if (m_pathManager == MpTcpSocketBase::nDiffPorts) + { + //ndiffports path manager + Ptr m_ndiffPorts = Create (); + uint16_t localport = m_endPoint->GetLocalPort (); + uint16_t remoteport = m_endPoint->GetPeerPort (); + m_ndiffPorts->CreateSubflows(this, localport, remoteport); + } + else + { + NS_LOG_WARN(" Wrong selection of Path Manger"); + } + m_multipleSubflows = true; + } + } + // MappingList mappings; + if (m_txBuffer->Size () == 0) + { + return false; // Nothing to send + } + //start/size + uint32_t nbMappingsDispatched = 0; // mimic nbPackets in TcpSocketBase::SendPendingData + + /* Generate DSS mappings + * This could go into a specific function + * MappingVector mappings; + */ + SequenceNumber64 dsnHead; + SequenceNumber32 ssn; + int subflowArrayId; + uint16_t length; + + while (m_scheduler->GenerateMapping (subflowArrayId, dsnHead, length)) + { + NS_LOG_DEBUG ("Generated mapping for sf=" << subflowArrayId << " dsn=" << dsnHead + << " of len=" << length); + Ptr subflow = GetSubflow (subflowArrayId); + + // For now we limit the mapping to a per packet basis + bool ok = subflow->AddLooseMapping (dsnHead, length); + NS_ASSERT (ok); + // see next #if 0 to see how it should be + SequenceNumber32 dsnTail = SEQ64TO32 (dsnHead) + length; + Ptr p = m_txBuffer->CopyFromSequence (length, SEQ64TO32 (dsnHead)); + NS_ASSERT (p->GetSize () <= length); + int ret = subflow->Send (p, 0); + // Flush to update cwnd and stuff + NS_LOG_DEBUG ("Send result=" << ret); + + /* Ideally we should be able to send data out of order so that it arrives in order at the + * receiver but to do that we need SACK support (IMO). Once SACK is implemented it should + * be reasonably easy to add + */ + NS_ASSERT (dsnHead == SEQ32TO64 (m_tcb->m_nextTxSequence)); + SequenceNumber32 nextTxSeq = m_tcb->m_nextTxSequence; + if (dsnHead <= SEQ32TO64(nextTxSeq) + && (dsnTail) >= nextTxSeq ) + { + m_tcb-> m_nextTxSequence = dsnTail; + } + m_tcb->m_highTxMark = std::max ( m_tcb->m_highTxMark.Get (), dsnTail); + NS_LOG_LOGIC ("m_nextTxSequence=" << m_tcb->m_nextTxSequence << " m_highTxMark=" << m_tcb->m_highTxMark); + } + + uint32_t remainingData = m_txBuffer->SizeFromSequence (m_tcb->m_nextTxSequence ); + if (m_closeOnEmpty && (remainingData == 0)) + { + TcpHeader header; + ClosingOnEmpty (header); + } + return nbMappingsDispatched > 0; +} + +void +MpTcpSocketBase::OnSubflowDupAck (Ptr sf) +{ + NS_LOG_DEBUG ("Dup ack signaled by subflow " << sf ); +} + + /** + * Retransmit timeout + * This function should be very interesting because one may + * adopt different strategies here, like reinjecting on other subflows etc... + * Maybe allow for a callback to be set here. + */ +void +MpTcpSocketBase::Retransmit () +{ + NS_LOG_LOGIC (this); + m_tcb->m_nextTxSequence = m_txBuffer->HeadSequence (); // Start from highest Ack + m_dupAckCount = 0; + DoRetransmit (); // Retransmit the packet +} + +void +MpTcpSocketBase::DoRetransmit () +{ + NS_LOG_FUNCTION (this); + // Retransmit SYN packet + if (m_state == SYN_SENT) + { + if (m_synCount > 0) + { + NS_FATAL_ERROR ("TODO, first syn didn't reach it should be resent. Maybe this shoudl be let to the subflow"); + } + else + { + NotifyConnectionFailed (); + } + return; + } + + // Retransmit non-data packet: Only if in FIN_WAIT_1 or CLOSING state + if (m_txBuffer->Size () == 0) + { + if (m_state == FIN_WAIT_1 || m_state == CLOSING) + { + // Must have lost FIN, re-send + TcpHeader header; + SendFin (); + } + return; + } + // Retransmit a data packet: Call SendDataPacket + NS_LOG_LOGIC ("TcpSocketBase " << this << " retxing seq " << m_txBuffer->HeadSequence ()); + NS_FATAL_ERROR ("TODO later, but for the tests only, it should not be necesssary ?! Check for anything suspicious"); +} + +void +MpTcpSocketBase::SendFin () +{ + Ptr subflow = GetSubflow (0); + subflow->AppendDSSFin (); + subflow->SendEmptyPacket (TcpHeader::ACK); +} + +void +MpTcpSocketBase::ReTxTimeout () +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::ReTxTimeout (); +} + +// advertise addresses +void +MpTcpSocketBase::AdvertiseAddresses () +{ + NS_LOG_INFO ("AdvertiseAvailableAddresses-> "); + Ptr node = TcpSocketBase::GetNode (); + Ptr p = Create (); + TcpHeader header; + SequenceNumber32 s = m_tcb->m_nextTxSequence; + uint8_t flags = TcpHeader::ACK; + uint8_t j = 100; + + if (flags & TcpHeader::FIN) + { + flags |= TcpHeader::ACK; + } + else if (m_state == FIN_WAIT_1 || m_state == LAST_ACK || m_state == CLOSING) + { + ++s; + } + AddSocketTags (p); + header.SetFlags (flags); + header.SetSequenceNumber (s); + header.SetAckNumber (m_rxBuffer->NextRxSequence ()); + header.SetSourcePort (m_endPoint->GetLocalPort ()); + header.SetDestinationPort (m_endPoint->GetPeerPort ()); + // Object from L3 to access to routing protocol, Interfaces and NetDevices and so on. + Ptr ipv4 = node->GetObject (); + for (uint32_t i = 1; i < ipv4->GetNInterfaces (); i++) + { + Ptr interface = ipv4->GetInterface (i); + Ipv4InterfaceAddress interfaceAddr = interface->GetAddress (0); + if (interfaceAddr.GetLocal () == Ipv4Address::GetLoopback ()) + continue; + Address address = InetSocketAddress(interfaceAddr.GetLocal (), m_endPoint->GetLocalPort ()); + Ptr addaddr = CreateObject (); + uint8_t addrId = j; + std::cout<<" advertising "<GetLocalPort ()<SetAddress (address, addrId); + NS_LOG_INFO ("Appended option" << addaddr); + header.AppendOption ( addaddr ); + j = j + 10 ; + } + m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), + m_endPoint->GetPeerAddress (), m_boundnetdevice); +} + +// add local addresses to the container +void +MpTcpSocketBase::AddLocalAddresses () +{ + NS_LOG_FUNCTION (this); + Ptr node = TcpSocketBase::GetNode (); + Ptr ipv4 = node->GetObject (); + for (uint32_t i = 0; i < ipv4->GetNInterfaces (); i++) + { + Ptr interface = ipv4->GetInterface (i); + Ipv4InterfaceAddress interfaceAddr = interface->GetAddress (0); + if (interfaceAddr.GetLocal() == Ipv4Address::GetLoopback ()) + continue; + NS_LOG_INFO (" Adding local addresses "<GetLocalPort ()); + LocalAddressInfo.push_back(std::make_pair (interfaceAddr.GetLocal (), m_endPoint->GetLocalPort ())); + } +} + +void +MpTcpSocketBase::SetNewAddrCallback (Callback, Address, uint8_t> remoteAddAddrCb, + Callback remoteRemAddrCb) +{ + m_onRemoteAddAddr = remoteAddAddrCb; + m_onAddrDeletion = remoteRemAddrCb; +} + +void +MpTcpSocketBase::MoveSubflow (Ptr subflow, mptcp_container_t from, mptcp_container_t to) +{ + NS_ASSERT (from != to); + SubflowList::iterator it = std::find (m_subflows[from].begin (), m_subflows[from].end (), subflow); + + if (it == m_subflows [from].end ()) + { + NS_LOG_ERROR ("Could not find subflow in *from* container. It may have already been moved by another callback "); + DumpSubflows (); + return; + } + m_subflows[to].push_back (*it); + m_subflows[from].erase (it); +} + +void +MpTcpSocketBase::DumpSubflows () const +{ + NS_LOG_FUNCTION (this << "\n"); + for (int i = 0; i < Maximum; ++i) + { + NS_LOG_UNCOND ("===== container [" << containerNames[i] << "]"); + for (SubflowList::const_iterator it = m_subflows[i].begin (); it != m_subflows[i].end (); it++ ) + { + NS_LOG_UNCOND ("- subflow [" << *it << "]"); + } + } +} + +void +MpTcpSocketBase::MoveSubflow (Ptr subflow, mptcp_container_t to) +{ + for (int i = 0; i < Maximum; ++i) + { + SubflowList::iterator it = std::find (m_subflows[i].begin (), m_subflows[i].end (), subflow); + if (it != m_subflows[i].end ()) + { + MoveSubflow (subflow, static_cast (i), to); + return; + } + } + NS_FATAL_ERROR ("Subflow not found in any container"); +} + +void +MpTcpSocketBase::OnSubflowEstablished (Ptr subflow) +{ + if (subflow->IsMaster()) + { + NS_LOG_LOGIC ("Master subflow created, copying its endpoint"); + m_endPoint = subflow->m_endPoint; + SetTcp (subflow->m_tcp); + SetNode (subflow->GetNode ()); + + if (m_state == SYN_SENT || m_state == SYN_RCVD) + { + NS_LOG_LOGIC ("Meta " << TcpStateName[m_state] << " -> ESTABLISHED"); + m_state = ESTABLISHED; + } + else + { + NS_FATAL_ERROR ("Unhandled case where subflow got established while meta in " << TcpStateName[m_state] ); + } + InetSocketAddress addr (subflow->m_endPoint->GetPeerAddress (), subflow->m_endPoint->GetPeerPort ()); + NotifyNewConnectionCreated (this, addr); + } + ComputeTotalCWND (); +} + +void +MpTcpSocketBase::OnSubflowEstablishment (Ptr subflow) +{ + NS_LOG_LOGIC (this << " (=meta) New subflow " << subflow << " established"); + NS_ASSERT_MSG (subflow, "Contact ns3 team"); + ComputeTotalCWND (); + + if (subflow->IsMaster ()) + { + NS_LOG_LOGIC ("Master subflow established, moving meta from " << TcpStateName[m_state] << " to ESTABLISHED state"); + if (m_state == SYN_SENT || m_state == SYN_RCVD) + { + NS_LOG_LOGIC ("Meta " << TcpStateName[m_state] << " -> ESTABLISHED"); + m_state = ESTABLISHED; + } + else + { + NS_LOG_WARN ("Unhandled case where subflow got established while meta in " << TcpStateName[m_state] ); + } + + Simulator::ScheduleNow (&MpTcpSocketBase::ConnectionSucceeded, this); + } +} + +void +MpTcpSocketBase::NotifySubflowCreated (Ptr sf) +{ + NS_LOG_FUNCTION (this << sf); + if (!m_subflowCreated.IsNull ()) + { + m_subflowCreated (sf); + } +} + +void +MpTcpSocketBase::NotifySubflowConnected (Ptr sf) +{ + NS_LOG_FUNCTION (this << sf); + if (!m_subflowConnectionSucceeded.IsNull ()) + { + m_subflowConnectionSucceeded (sf); + } +} + +void +MpTcpSocketBase::SetSubflowAcceptCallback ( + Callback, const Address &, const Address & > joinRequest, + Callback > connectionCreated +) +{ + NS_LOG_FUNCTION (this << &joinRequest << " " << &connectionCreated); + m_joinRequest = joinRequest; + m_subflowCreated = connectionCreated; +} + +void +MpTcpSocketBase::SetSubflowConnectCallback ( + Callback > connectionSucceeded, + Callback > connectionFailure + ) +{ + NS_LOG_FUNCTION (this << &connectionSucceeded); + m_subflowConnectionSucceeded = connectionSucceeded; + m_subflowConnectionFailure = connectionFailure; +} + +TypeId +MpTcpSocketBase::GetInstanceTypeId (void) const +{ + return MpTcpSocketBase::GetTypeId (); +} + +void +MpTcpSocketBase::OnSubflowClosing (Ptr sf) +{ + NS_LOG_LOGIC ("Subflow has gone into state ["); + MoveSubflow (sf, Established, Closing); +} + +void +MpTcpSocketBase::OnSubflowDupack (Ptr sf, MpTcpMapping mapping) +{ + NS_LOG_LOGIC ("Subflow Dupack TODO.Nothing done by meta"); +} + +void +MpTcpSocketBase::OnSubflowRetransmit (Ptr sf) +{ + NS_LOG_INFO ("Subflow retransmit. Nothing done by meta"); +} + +// if bytes may not really be in flight but rather in subflows buffer +uint32_t +MpTcpSocketBase::BytesInFlight () const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::BytesInFlight (); +} + +/* This function is added to access m_nextTxSequence in + MpTcpSchedulerRoundRobin class */ +SequenceNumber32 +MpTcpSocketBase::Getvalue_nextTxSeq () +{ + return m_tcb->m_nextTxSequence; +} +uint16_t +MpTcpSocketBase::AdvertisedWindowSize (bool scale) const +{ + NS_LOG_FUNCTION (this << scale); + return TcpSocketBase::AdvertisedWindowSize (); +} + +uint32_t +MpTcpSocketBase::Window () const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::Window (); +} + +uint32_t +MpTcpSocketBase::AvailableWindow () const +{ + NS_LOG_FUNCTION (this); + uint32_t unack = UnAckDataCount (); // Number of outstanding bytes + uint32_t win = Window (); // Number of bytes allowed to be outstanding + NS_LOG_LOGIC ("UnAckCount=" << unack << ", Win=" << win); + return (win < unack) ? 0 : (win - unack); +} + +void +MpTcpSocketBase::ClosingOnEmpty (TcpHeader& header) +{ + NS_LOG_INFO("closing on empty called"); +} + +/* Inherit from Socket class: Kill this socket and signal the peer (if any) */ +int +MpTcpSocketBase::Close (void) +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::Close (); +} + +void +MpTcpSocketBase::CloseAllSubflows () +{ + NS_LOG_FUNCTION (this << "Closing all subflows"); + NS_ASSERT ( m_state == FIN_WAIT_2 || m_state == CLOSING || m_state == CLOSE_WAIT || m_state == LAST_ACK); + + for (int i = 0; i < Closing; ++i) + { + // Established + NS_LOG_INFO ("Closing all subflows in state [" << containerNames[i] << "]"); + for ( SubflowList::const_iterator it = m_subflows[i].begin (); it != m_subflows[i].end (); it++ ) + { + Ptr sf = *it; + NS_LOG_LOGIC ("Closing sf " << sf); + int ret = sf->Close (); + NS_ASSERT_MSG (ret == 0, "Can't close subflow"); + } + m_subflows[Closing].insert (m_subflows[Closing].end (), m_subflows[i].begin (), m_subflows[i].end ()); + m_subflows[i].clear (); + } +} + +void +MpTcpSocketBase::ReceivedAck (SequenceNumber32 dack , Ptr sf , + bool count_dupacks) +{ + NS_LOG_FUNCTION ("Received DACK " << dack << "from subflow" << sf << "(Enable dupacks:" << count_dupacks << " )"); + + if (dack < m_txBuffer->HeadSequence ()) + { // Case 1: Old ACK, ignored. + NS_LOG_LOGIC ("Old ack Ignored " << dack ); + } + else if (dack == m_txBuffer->HeadSequence ()) + { // Case 2: Potentially a duplicated ACK + if (dack < m_tcb->m_nextTxSequence && count_dupacks) + { + /* dupackcount shall only be increased if there is only a DSS option ! */ + } + // otherwise, the ACK is precisely equal to the nextTxSequence + NS_ASSERT( dack <= m_tcb->m_nextTxSequence); + } + else if (dack > m_txBuffer->HeadSequence ()) + { // Case 3: New ACK, reset m_dupAckCount and update m_txBuffer + NS_LOG_LOGIC ("New DataAck [" << dack << "]"); + m_txBuffer->DiscardUpTo( dack ); + bool resetRTO = true; + NewAck( dack, resetRTO ); + m_dupAckCount = 0; + } +} + +/* Move TCP to Time_Wait state and schedule a transition to Closed state */ +void +MpTcpSocketBase::TimeWait () +{ + Time timewait_duration = Seconds (2 * m_msl); + NS_LOG_INFO (TcpStateName[m_state] << " -> TIME_WAIT " + << "with duration of " << timewait_duration + << "; m_msl=" << m_msl); + CloseAllSubflows (); + m_state = TIME_WAIT; + CancelAllTimers (); + m_timewaitEvent = Simulator::Schedule (timewait_duration, &MpTcpSocketBase::OnTimeWaitTimeOut, this); +} + +void +MpTcpSocketBase::OnTimeWaitTimeOut (void) +{ + // Would normally call CloseAndNotify + NS_LOG_LOGIC ("Timewait timeout expired"); + NS_LOG_UNCOND ("after timewait timeout, there are still " << m_subflows [Closing].size () << " subflows pending"); + + CloseAndNotify (); +} + +/* Peacefully close the socket by notifying the upper layer and deallocate end point */ +void +MpTcpSocketBase::CloseAndNotify (void) +{ + NS_LOG_FUNCTION (this); + if (!m_closeNotified) + { + NotifyNormalClose(); + } + if (m_state != TIME_WAIT) + { + DeallocateEndPoint(); + } + m_closeNotified = true; + NS_LOG_INFO (TcpStateName [m_state] << " -> CLOSED"); + CancelAllTimers (); + m_state = CLOSED; +} + +/* Peer sent me a DATA FIN. Remember its sequence in rx buffer. + It means there won't be any mapping above that dataseq */ +void +MpTcpSocketBase::PeerClose (Ptr p, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << " PEEER CLOSE CALLED !" << tcpHeader); + NS_FATAL_ERROR ("TO REMOVE. Function overriden by PeerClose(subflow)"); +} + +/* Received a in-sequence FIN. Close down this socket. + FIN is in sequence, notify app and respond with a FIN */ +void +MpTcpSocketBase::DoPeerClose (void) +{ + // Move the state to CLOSE_WAIT + NS_LOG_INFO (TcpStateName[m_state] << " -> CLOSE_WAIT"); + m_state = CLOSE_WAIT; + + if (!m_closeNotified) + { + /* The normal behaviour for an application is that, when the peer sent a in-sequence + * FIN, the app should prepare to close. The app has two choices at this point: either + * respond with ShutdownSend() call to declare that it has nothing more to send and + * the socket can be closed immediately; or remember the peer's close request, wait + * until all its existing data are pushed into the TCP socket, then call Close() + * explicitly. + */ + NS_LOG_LOGIC ("TCP " << this << " calling NotifyNormalClose"); + NotifyNormalClose(); + m_closeNotified = true; + } + if (m_shutdownSend) + { // The application declares that it would not sent any more, close this socket + Close (); + } + else + { // Need to ack, the application will close later + TcpHeader header; + Ptr sf = GetSubflow (0); + sf->AppendDSSAck (); + sf->SendEmptyPacket (TcpHeader::ACK); + } + if (m_state == LAST_ACK) + { + NS_LOG_LOGIC ("TcpSocketBase " << this << " scheduling Last Ack timeout 01 (LATO1)"); + NS_FATAL_ERROR ("TODO"); + } +} + + /* Do the action to close the socket. Usually send a packet with appropriate + * flags depended on the current m_state. + * use a closeAndNotify in more situations + */ +int +MpTcpSocketBase::DoClose (void) +{ + NS_LOG_FUNCTION (this << " in state " << TcpStateName[m_state]); + + /* + * close all subflows + * send a data fin + * ideally we should be abl e to work without any subflows and + * retransmit as soon as we get a subflow up ! + * we should ask the scheduler on what subflow to send the messages + */ + TcpHeader header; + switch (m_state) + { + case SYN_RCVD: + case ESTABLISHED: + // send FIN to close the peer + { + NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1"); + m_state = FIN_WAIT_1; + Ptr subflow = GetSubflow (0); + subflow->AppendDSSFin (); + subflow->SendEmptyPacket (TcpHeader::ACK); + } + break; + case CLOSE_WAIT: + { + // send ACK to close the peer + NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK"); + Ptr subflow = GetSubflow (0); + m_state = LAST_ACK; + subflow->AppendDSSAck (); + subflow->SendEmptyPacket (TcpHeader::ACK); //changed from header to TcpHeader::ACK + } + break; + case SYN_SENT: + case CLOSING: + // Send RST if application closes in SYN_SENT and CLOSING + NS_LOG_WARN ("trying to close while closing.."); + break; + case LISTEN: + CloseAndNotify (); + break; + case LAST_ACK: + TimeWait (); + break; + case CLOSED: + case FIN_WAIT_1: + case FIN_WAIT_2: + break; + case TIME_WAIT: + break; + default: /* mute compiler */ + // Do nothing in these four states + break; + } + return 0; +} + +Ptr +MpTcpSocketBase::Recv (uint32_t maxSize, uint32_t flags) +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::Recv (maxSize,flags); +} + +uint32_t +MpTcpSocketBase::GetTxAvailable (void) const +{ + NS_LOG_FUNCTION (this); + uint32_t value = m_txBuffer->Available (); + return value; +} + +/* This function should be overridable since it may depend on the CC, cf RFC: + To compute cwnd_total, it is an easy mistake to sum up cwnd_i across + all subflows: when a flow is in fast retransmit, its cwnd is + typically inflated and no longer represents the real congestion + window. The correct behavior is to use the ssthresh (slow start + threshold) value for flows in fast retransmit when computing + cwnd_total. To cater to connections that are app limited, the + computation should consider the minimum between flight_size_i and + cwnd_i, and flight_size_i and ssthresh_i, where appropriate. */ +uint32_t +MpTcpSocketBase::ComputeTotalCWND () +{ + NS_LOG_DEBUG ("Cwnd before update=" << Window ()); + uint32_t totalCwnd = 0; + + for (uint32_t i = 0; i < Maximum; i++) + { + for (SubflowList::const_iterator it = m_subflows[i].begin (); it != m_subflows[i].end (); it++ ) + { + Ptr sf = *it; + { + totalCwnd += sf->Window(); // Should be this all the time ?! + } + } + } + m_tcb->m_cWnd = totalCwnd; + NS_LOG_DEBUG ("Cwnd after computation=" << m_tcb->m_cWnd.Get ()); + return totalCwnd; +} + +/* Kill this socket. This is a callback function configured to m_endpoint in + SetupCallback(), invoked when the endpoint is destroyed. */ +void +MpTcpSocketBase::Destroy (void) +{ + NS_LOG_FUNCTION (this); + NS_LOG_INFO ("Enter Destroy(" << this << ") m_sockets: )"); + + NS_LOG_ERROR ("Before unsetting endpoint, check it's not used by subflow ?"); + m_endPoint = 0; +} + +} //namespace ns3 Index: src/internet/model/mptcp-socket-base.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-socket-base.h @@ -0,0 +1,519 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#ifndef MPTCP_SOCKET_BASE_H +#define MPTCP_SOCKET_BASE_H + +#include "ns3/callback.h" +#include "ns3/mptcp-mapping.h" +#include "ns3/tcp-socket.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/inet-socket-address.h" +#include "ns3/mptcp-scheduler-round-robin.h" +#include "ns3/mptcp-fullmesh.h" +#include "ns3/mptcp-ndiffports.h" + +namespace ns3 { + +class Ipv4EndPoint; +class Ipv6EndPoint; +class Node; +class Packet; +class TcpL4Protocol; +class MpTcpSubflow; +class TcpOptionMpTcpDSS; +class TcpOptionMpTcpJoin; +class OutputStreamWrapper; +class TcpSocketBase; + +/** + * \ingroup socket + * \class MpTcpSocketBase + * + * \brief A base class for implementation of a stream socket using MPTCP. + * This is the MPTCP meta socket the application talks with + * this socket. New subflows, as well as the first one (the master + * socket) are linked to this meta socket. + * Every data transfer happens on a subflow. + * Following the linux kernel from UCL (http://multipath-tcp.org) convention, + * the first established subflow is called the "master" subflow. + * + * This inherits TcpSocketBase so that it can be used as any other TCP variant: + * this is the backward compability feature that is required in RFC. + * Also doing so allows to run TCP tests with MPTCP via for instance the command + * Config::SetDefault ("ns3::TcpL4Protocol::SocketType", "ns3::MpTcpOlia"); + * + * But to make sure some inherited functions are not improperly used, we need to redefine them so that they + * launch an assert. You can notice those via the comments "//! Disabled" + * + * As such many inherited (protected) functions are overriden & left empty. + * + * As in linux, the meta should return the m_endPoint information of the master, + * even if that subflow got closed during the MpTcpConnection. + * + * ConnectionSucceeded may be called twice; once when it goes to established + * and the second time when it sees + * Simulator::ScheduleNow(&MpTcpSocketBase::ConnectionSucceeded, this); + */ + +class MpTcpSocketBase : public TcpSocketBase +{ +public: + + typedef std::vector< Ptr > SubflowList; + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + /** + * \brief Path managers to initiate subslows + * \param Default default path manager + * \param FullMesh fullmesh path manager + * \param nDiffPorts ndiffports path manager + */ + enum PathManagerMode + { + Default, + FullMesh, + nDiffPorts + }; + + MpTcpSocketBase (); + + MpTcpSocketBase (const TcpSocketBase& sock); + MpTcpSocketBase (const MpTcpSocketBase&); + virtual ~MpTcpSocketBase (); + + /** + * \brief Should be called only by subflows when they update their receiver window + */ + virtual bool UpdateWindowSize (const TcpHeader& header); + +protected: + /** + * This is callback called by subflow NotifyNewConnectionCreated. If + * the calling subflow is the master, then the call is forwarded through meta's + * NotifyNewConnectionCreated, else it is forward to the JoinCreatedCallback + * + * \see Socket::NotifyNewConnectionCreated + */ + virtual void OnSubflowCreated (Ptr socket, const Address &from); + virtual void OnSubflowConnectionFailure (Ptr socket); + virtual void OnSubflowConnectionSuccess (Ptr socket); + +public: + + virtual void SetPathManager (PathManagerMode); + /** + * Create a subflow for ndiffports path manager + * Initiate a single new subflow between given IP addresses + * \param randomSourcePort random source port + * \param randomDestinationPort random destination port + */ + bool CreateSingleSubflow (uint16_t randomSourcePort, uint16_t randomDestinationPort); + + /** + * \brief Create a subflow for fullmesh path manager + * \brief Initiate s subflow between the received address/port + * \param destinationIp address of the destination + * \param destinationPort destination port + * \param id + */ + bool CreateSubflowsForMesh (); + + /** + * \brief these callbacks will be passed on to + * \see Socket::Set + */ + virtual void + SetSubflowAcceptCallback (Callback, const Address &, const Address & > connectionRequest, + Callback > connectionCreated + ); + + virtual void + SetSubflowConnectCallback (Callback > connectionSucceeded, + Callback > connectionFailure + ); + + /** + * Triggers callback registered by SetSubflowAcceptCallback + */ + void NotifySubflowCreated (Ptr sf); + + /** + * Triggers callback registered by SetSubflowConnectCallback + */ + void NotifySubflowConnected (Ptr sf); + + /** + * Called when a subflow TCP state is updated. + * It detects such events by tracing its subflow m_state. + * + */ + virtual void OnSubflowNewCwnd (std::string context, uint32_t oldCwnd, uint32_t newCwnd); + + /** + * Initiates a new subflow with MP_JOIN + * + * Wrapper that just creates a subflow, bind it to a specific address + * and then establishes the connection + */ + virtual int ConnectNewSubflow (const Address &local, const Address &remote); + virtual void DoRetransmit (void); + + /** + * Called when a subflow TCP state is updated. + * It detects such events by tracing its subflow m_state. + * + * \param context + * \param sf Subflow that changed state + * \param oldState previous TCP state of the subflow + * \param newState new TCP state of the subflow + */ + virtual void OnSubflowNewState (std::string context, Ptr sf, + TcpStates_t oldState, TcpStates_t newState); + + // Window Management + virtual uint32_t BytesInFlight (void) const; // Return total bytes in flight of a subflow + + /* + * This function is added to access m_nextTxSequence in + * MpTcpSchedulerRoundRobin class + * directly accessing value of m_nextTxSequence in MpTcpSechulerRoundRobin + * it gives zero value + */ + SequenceNumber32 Getvalue_nextTxSeq (); + + /* Sum congestio nwindows across subflows to compute global cwin + * It does not take flows that are closing yet so that may be a weakness depending on the scenario + * to update + */ + virtual uint32_t ComputeTotalCWND (); + virtual uint16_t AdvertisedWindowSize (bool scale = true) const; + virtual uint32_t AvailableWindow (void) const; + virtual void CloseAndNotify (void); + + /* Limit the size of in-flight data by cwnd and receiver's rxwin */ + + virtual uint32_t Window (void) const; + virtual void PersistTimeout (void); + + /* \brief equivalent to PeerClose + * \param finalDsn + * OnDataFin + */ + virtual void PeerClose (SequenceNumber32 fin_seq, Ptr sf); + + /* equivalent to TCP Rst */ + virtual void SendFastClose (Ptr sf); + + /** + * \return return true if connected + */ + bool IsConnected () const; + + // Public interface for MPTCP + virtual int Bind (void); + virtual int Bind (const Address &address); + + /** + * \brief Same as MpTcpSocketBase::Close + * + * The default behavior is to do nothing until all the data is transmitted. + * Only then are + * RFC 6824: + * - When an application calls close() on a socket, this indicates that it + * has no more data to send; for regular TCP, this would result in a FIN + * on the connection. For MPTCP, an equivalent mechanism is needed, and + * this is referred to as the DATA_FIN. + * + * - A DATA_FIN has the semantics and behavior as a regular TCP FIN, but + * at the connection level. Notably, it is only DATA_ACKed once all + * data has been successfully received at the connection level + */ + // socket function member + virtual int Close (void); + + /** \brief called by subflow when it sees a DSS with the DATAFIN flag + * \Params are ignored + * \param packet Ignored + */ + virtual void PeerClose (Ptr p, const TcpHeader& tcpHeader); // Received a FIN from peer, notify rx buffer + virtual void DoPeerClose (void); // FIN is in sequence, notify app and respond with a FIN + virtual int DoClose(void); + virtual uint32_t GetTxAvailable () const; + virtual uint32_t GetRxAvailable (void) const; + virtual void ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface); + + /** + * \return Number of connected subflows (that is that ran the 3whs) + */ + SubflowList::size_type GetNActiveSubflows () const; + + /** + * + * \return an established subflow + */ + virtual Ptr GetSubflow (uint8_t) const; + virtual void ClosingOnEmpty (TcpHeader& header); + + /** + * The connection is considered fully established + * when it can create new subflows, i.e., when it received + * a first dss ack + */ + virtual bool FullyEstablished () const; + + /** + * This retriggers Connection success callback + * You have to check in the callback if it fully estalbished or not + */ + virtual void BecomeFullyEstablished (); + + /* \brief Superseed into TcpSocketBase + * Here it sends MP_FIN + */ + virtual void SendFin (); + + /** + * \brief Initiate + * \param sf subflow to setup + */ + virtual void AddSubflow (Ptr sf); + + virtual void Destroy (void); + + /** + * \return 0 In case of success + */ + uint64_t GetPeerKey () const; + + /** + * \brief Generated during the initial 3 WHS + */ + uint64_t GetLocalKey () const; + + /** + * \return Hash of the local key + */ + uint32_t GetLocalToken () const; + + /** + * \return Hash of the peer key + */ + uint32_t GetPeerToken () const; + + /** + * \brief For now it looks there is no way to know that an ip interface went up so we will assume until + * \brief further notice that IPs of the client don't change. + * \param -1st callback called on receiving an ADD_ADDR + * -param 2nd callback called on receiving REM_ADDR + */ + void SetNewAddrCallback (Callback, Address, uint8_t> remoteAddAddrCb, + Callback remoteRemAddrCb); + /* + * \brief Advertise addresses to the peer + * \brief to create new subflows + */ + void AdvertiseAddresses (); + + /* + * \breif Add local addresses to the container for future + * \brief to create new subflows when asked by application + */ + void AddLocalAddresses (); + +public: + typedef enum { + Established = 0, //!< contains ESTABLISHED/CLOSE_WAIT */ + Others = 1, //!< Composed of SYN_RCVD, SYN_SENT*/ + Closing, //!< CLOSE_WAIT, FIN_WAIT */ + Maximum //!< keep it last, used to decalre array */ + } mptcp_container_t; + +protected: + friend class Tcp; + friend class MpTcpSubflow; + /** + * \brief Expects InetXSocketAddress + */ + virtual bool NotifyJoinRequest (const Address &from, const Address & toAddress); + /** + * \brief Expects Ipv4 (6 not supported yet) + */ + bool OwnIP (const Address& address) const; + + /** + * Should be called after having sent a dataFIN + * Should send a RST on all subflows in state Other + * and a FIN for Established subflows + */ + virtual void CloseAllSubflows (); + + // MPTCP connection and subflow set up + virtual int SetupCallback (void); // Setup SetRxCallback & SetRxCallback call back for a host + + /** + * \param sf + * \param reset True if closing due to reset + */ + void OnSubflowClosed (Ptr sf, bool reset); + void OnSubflowDupAck (Ptr sf); + + /** + * \brief Currently used as callback for subflows + * \param dataSeq Used to reconstruct the mapping + */ + virtual void OnSubflowRecv (Ptr sf); + + /* + * \brief close all subflows + */ + virtual void TimeWait (void); + + /** + * \brief Called when a subflow that initiated the connection + * \brief gets established + */ + virtual void OnSubflowEstablishment (Ptr); + + /** + * \brief Called when a subflow that received a connection33 + * request gets established + */ + virtual void OnSubflowEstablished (Ptr subflow); + + /** + * \brief Should be called when subflows enters FIN_WAIT or LAST_ACK + */ + virtual void OnSubflowClosing (Ptr); + + void NotifyRemoteAddAddr (Address address); + void NotifyRemoteRemAddr (uint8_t addrId); + + /** + * /brief note Setting a remote key has the sideeffect of enabling MPTCP on the socket + */ + void SetPeerKey (uint64_t); + + virtual void ConnectionSucceeded (void); // Schedule-friendly wrapper for Socket::NotifyConnectionSucceeded() + + /** Inherit from Socket class: Return data to upper-layer application. Parameter flags + is not used. Data is returned as a packet of size no larger than maxSize */ + virtual Ptr Recv (uint32_t maxSize, uint32_t flags); + virtual int Send (Ptr p, uint32_t flags); + + /** + * Sending data via subflows with available window size. It sends data only to ESTABLISHED subflows. + * It sends data by calling SendDataPacket() function. + * This one is really different from parent class + * + * Called by functions: ReceveidAck, NewAck + * send as much as possible + * \return true if it send mappings + */ + virtual uint32_t SendPendingData (bool withAck = false); + virtual void ReTxTimeout (void); + virtual void Retransmit (); + // MPTCP specfic version + virtual void ReceivedAck (SequenceNumber32 dack, Ptr sf, bool count_dupacks); + + /** + * \brief Part of the logic was implemented but this is non-working. + * \return Always false + */ + virtual bool IsInfiniteMappingEnabled () const; + + virtual bool DoChecksum () const; + virtual void OnSubflowDupack (Ptr sf, MpTcpMapping mapping); + virtual void OnSubflowRetransmit (Ptr sf) ; + + /** + * inherited from parent: update buffers + * @brief Called from subflows when they receive DATA-ACK. For now calls parent fct + */ + virtual void NewAck (SequenceNumber32 const& dataLevelSeq, bool resetRTO); + + virtual void OnTimeWaitTimeOut (); + std::vector< std::pair > RemoteAddressInfo; //!< Ipv4/v6 address and its port + std::vector< std::pair > LocalAddressInfo; //!< Ipv4/v6 address and its port + +protected: + friend class TcpL4Protocol; + friend class MpTcpSchedulerRoundRobin; + friend class MpTcpSchedulerFastestRTT; + friend class MpTcpNdiffPorts; + /** + * \brief called by TcpL4protocol when receiving an MP_JOIN taht does not fit + * \brief to any Ipv4endpoint. Thus twe create one. + */ + virtual Ipv4EndPoint* NewSubflowRequest (Ptr p, const TcpHeader & header, const Address & fromAddress, + const Address & toAddress, Ptr join); + + /** + * \brief should accept a stream + */ + virtual void DumpSubflows () const; + + SubflowList m_subflows [Maximum]; + Callback, Address, uint8_t > m_onRemoteAddAddr; //!< return true to create a subflow + Callback m_onAddrDeletion; //!< return true to create a subflow +protected: + virtual void CreateScheduler (TypeId schedulerTypeId); + + /** + * \brief the scheduler is so closely + */ + Ptr m_scheduler; //!< + uint32_t m_peerToken; + PathManagerMode m_pathManager {FullMesh}; //!< path manager + +private: + uint64_t m_peerKey; //!< Store remote host token + bool m_doChecksum; //!< Compute the checksum. Negociated during 3WHS. Unused + bool m_receivedDSS; //!< True if we received at least one DSS + bool m_multipleSubflows; //!< true if required number of subflows have been created [KN] + + /* \brief Utility function used when a subflow changes state + * \brief Research of the subflow is done + * \warn This function does not check if the destination container is + */ + void MoveSubflow (Ptr sf, mptcp_container_t to); + + /** + * \brief Asserts if from == to + */ + void MoveSubflow(Ptr sf, mptcp_container_t from, mptcp_container_t to); + + Callback > m_subflowConnectionSucceeded; //!< connection succeeded callback + Callback > m_subflowConnectionFailure; //!< connection failed callback + Callback, const Address &, const Address & > m_joinRequest; //!< connection request callback + Callback > m_subflowCreated; //!< connection created callback + + //! + TypeId m_subflowTypeId; + TypeId m_schedulerTypeId; +}; + +} //namespace ns3 + +#endif /* MP_TCP_SOCKET_BASE_H */ Index: src/internet/model/mptcp-socket.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-socket.h @@ -0,0 +1,57 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2006 Georgia Tech Research Corporation + * 2007 INRIA + * + * 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 + * + * Authors: Matthieu Coudron + */ + +#ifndef TCP_SOCKET_H +#define TCP_SOCKET_H + +#include "ns3/socket.h" +#include "ns3/traced-callback.h" +#include "ns3/callback.h" +#include "ns3/ptr.h" +#include "ns3/object.h" +#include "ns3/nstime.h" + +namespace ns3 { + +class Node; +class Packet; + +/** + * \brief Names of the MPTCP states + */ +typedef enum { + M_CLOSED, // 0 + M_LISTEN, // 1 + M_SYN_SENT, // 2 + M_SYN_RCVD, // 3 + M_ESTA_WAIT, // 4 + M_ESTA_SP, // 4 + M_ESTA_MP, + M_CLOSE_WAIT, // 5 + M_LAST_ACK, // 6 + M_FIN_WAIT_1, // 7 + M_FIN_WAIT_2, // 8 + M_CLOSING, // 9 + TIME_WAIT, // 10 + LAST_STATE +} MpTcpStates_t; + +} // end of namespace Index: src/internet/model/mptcp-subflow.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-subflow.cc @@ -0,0 +1,1012 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#undef NS_LOG_APPEND_CONTEXT +#define NS_LOG_APPEND_CONTEXT \ + if (m_node) { std::clog << Simulator::Now ().GetSeconds () << " [node " << m_node->GetId () << ": ] "; } + +#include +#include +#include "ns3/mptcp-mapping.h" +#include "ns3/simulator.h" +#include "ns3/log.h" +#include "ns3/abort.h" +#include "ns3/mptcp-subflow.h" +#include "ns3/mptcp-socket-base.h" +#include "ns3/tcp-l4-protocol.h" +#include "ns3/ipv4-address.h" +#include "ns3/ipv4-end-point.h" +#include "ns3/node.h" +#include "ns3/ptr.h" +#include "ns3/tcp-option-mptcp.h" +#include "ns3/ipv4-address.h" +#include "ns3/trace-helper.h" +#include +#include + +namespace ns3 { + +NS_LOG_COMPONENT_DEFINE ("MpTcpSubflow"); + +NS_OBJECT_ENSURE_REGISTERED (MpTcpSubflow); + +static inline +SequenceNumber32 SEQ64TO32 (SequenceNumber64 seq) +{ + return SequenceNumber32 (seq.GetValue()); +} + +static inline +SequenceNumber64 SEQ32TO64 (SequenceNumber32 seq) +{ + return SequenceNumber64 (seq.GetValue()); +} + +TypeId +MpTcpSubflow::GetTypeId (void) +{ + static TypeId tid = TypeId("ns3::MpTcpSubflow") + .SetParent() + .SetGroupName ("Internet") + .AddConstructor() + ; + return tid; +} + +//! wrapper function +static inline +MpTcpMapping +GetMapping (const Ptr dss) +{ + MpTcpMapping mapping; + uint64_t dsn; + uint32_t ssn; + uint16_t length; + dss->GetMapping (dsn, ssn, length); + mapping.SetHeadDSN( SequenceNumber64(dsn)); + mapping.SetMappingSize(length); + mapping.MapToSSN( SequenceNumber32(ssn)); + return mapping; +} + +TypeId +MpTcpSubflow::GetInstanceTypeId (void) const +{ + return MpTcpSubflow::GetTypeId (); +} + +void +MpTcpSubflow::SetMeta (Ptr metaSocket) +{ + NS_ASSERT (metaSocket); + NS_LOG_FUNCTION (this); + m_metaSocket = metaSocket; +} + +void +MpTcpSubflow::DumpInfo () const +{ + NS_LOG_LOGIC ("MpTcpSubflow " << this << " SendPendingData" << + " rxwin " << m_rWnd << + " nextTxSeq " << m_tcb->m_nextTxSequence << + " highestRxAck " << m_txBuffer->HeadSequence ()<< + " pd->SFS " << m_txBuffer->SizeFromSequence (m_tcb->m_nextTxSequence) + ); +} + +void +MpTcpSubflow::CancelAllTimers () +{ + NS_LOG_FUNCTION (this); + m_retxEvent.Cancel (); + m_lastAckEvent.Cancel (); + m_timewaitEvent.Cancel (); + NS_LOG_LOGIC ("CancelAllTimers"); +} + +int +MpTcpSubflow::DoConnect () +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::DoConnect (); + +} + +/* Inherit from Socket class: Kill this socket and signal the peer (if any) */ +int +MpTcpSubflow::Close (void) +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::Close (); +} + +/* If copied from a legacy socket, then it's a master socket */ +MpTcpSubflow::MpTcpSubflow (const TcpSocketBase& sock) + : TcpSocketBase(sock), + m_dssFlags(0), + m_masterSocket(true) +{ + NS_LOG_FUNCTION (this << &sock); + NS_LOG_LOGIC ("Copying from TcpSocketBase/check2. endPoint=" << sock.m_endPoint); + // We need to update the endpoint callbnacks so that packets come to this socket + // instead of the abstract meta + // this is necessary for the client socket + NS_LOG_UNCOND("Cb=" << m_sendCb.IsNull () << " endPoint=" << m_endPoint); + m_endPoint = (sock.m_endPoint); + m_endPoint6 = (sock.m_endPoint6); + SetupCallback(); +} + +// Does this constructor even make sense ? no ? to remove ? +MpTcpSubflow::MpTcpSubflow (const MpTcpSubflow& sock) + : TcpSocketBase(sock), + m_dssFlags(0), + m_masterSocket(sock.m_masterSocket), + m_localNonce(sock.m_localNonce) +{ + NS_LOG_FUNCTION (this << &sock); + NS_LOG_LOGIC ("Invoked the copy constructor/check2"); +} + +MpTcpSubflow::MpTcpSubflow () + : TcpSocketBase(), + m_metaSocket(0), + m_masterSocket(false), + m_backupSubflow(false), + m_localNonce(0) +{ + NS_LOG_FUNCTION (this); +} + +MpTcpSubflow::~MpTcpSubflow () +{ + NS_LOG_FUNCTION (this); +} + +void +MpTcpSubflow::CloseAndNotify (void) +{ + NS_LOG_FUNCTION_NOARGS (); + TcpSocketBase::CloseAndNotify (); + GetMeta()->OnSubflowClosed (this, false ); +} + +/* Mapping should already exist when sending the packet */ +int +MpTcpSubflow::Send (Ptr p, uint32_t flags) +{ + NS_LOG_FUNCTION (this); + int ret = TcpSocketBase::Send (p, flags); + if (ret > 0) + { + MpTcpMapping temp; + SequenceNumber32 ssnHead = m_txBuffer->TailSequence() - p->GetSize(); + NS_ASSERT (m_TxMappings.GetMappingForSSN(ssnHead, temp)); + } + return ret; +} + +void +MpTcpSubflow::SendEmptyPacket (uint8_t flags) +{ + NS_LOG_FUNCTION_NOARGS (); + TcpSocketBase::SendEmptyPacket (flags); +} + +bool +MpTcpSubflow::AddLooseMapping (SequenceNumber64 dsnHead, uint16_t length) +{ + NS_LOG_LOGIC ("Adding mapping with dsn=" << dsnHead << " len=" << length); + MpTcpMapping mapping; + + mapping.MapToSSN (FirstUnmappedSSN()); + mapping.SetMappingSize (length); + mapping.SetHeadDSN (dsnHead); + bool ok = m_TxMappings.AddMapping (mapping); + NS_ASSERT_MSG (ok, "Can't add mapping: 2 mappings overlap"); + return ok; +} + +SequenceNumber32 +MpTcpSubflow::FirstUnmappedSSN () +{ + NS_LOG_FUNCTION (this); + SequenceNumber32 ssn; + if (!m_TxMappings.FirstUnmappedSSN (ssn)) + { + ssn = m_txBuffer->TailSequence (); + } + return ssn; +} + +void +MpTcpSubflow::GetMappedButMissingData (std::set< MpTcpMapping >& missing) +{ + NS_LOG_FUNCTION (this); + SequenceNumber32 startingSsn = m_txBuffer->TailSequence (); + m_TxMappings.GetMappingsStartingFromSSN (startingSsn, missing); +} + +void +MpTcpSubflow::SendPacket (TcpHeader header, Ptr p) +{ + NS_LOG_FUNCTION (this << header << p); + Ptr join; + if (p->GetSize() && !IsInfiniteMappingEnabled() && !GetTcpOption(header, join)) + { + // we must decide to send a mapping or not + // For now we always append the mapping but we could have mappings spanning over several packets. + // and thus not add the mapping for several packets + + SequenceNumber32 ssnHead = header.GetSequenceNumber(); + MpTcpMapping mapping; + + bool result = m_TxMappings.GetMappingForSSN (ssnHead, mapping); + if (!result) + { + m_TxMappings.Dump (); + NS_FATAL_ERROR ("Could not find mapping associated to ssn"); + } + NS_ASSERT_MSG (mapping.TailSSN() >= ssnHead +p->GetSize() -1, "mapping should cover the whole packet" ); + AppendDSSMapping (mapping); + } + // we append hte ack everytime + TcpSocketBase::SendPacket(header, p); + m_dssFlags = 0; // reset for next packet +} + +uint32_t +MpTcpSubflow::SendDataPacket (SequenceNumber32 ssnHead, uint32_t maxSize, bool withAck) +{ + NS_LOG_FUNCTION (this << "Sending packet starting at SSN [" << ssnHead.GetValue() << "] with len=" << maxSize<< withAck); + MpTcpMapping mapping; + bool result = m_TxMappings.GetMappingForSSN (ssnHead, mapping); + if (!result) + { + m_TxMappings.Dump (); + NS_FATAL_ERROR ("Could not find mapping associated to ssn"); + } + AppendDSSMapping (mapping); + // Here we set the maxsize to the size of the mapping + return TcpSocketBase::SendDataPacket (ssnHead, std::min( (int)maxSize,mapping.TailSSN()-ssnHead+1), withAck); +} + +bool +MpTcpSubflow::IsInfiniteMappingEnabled () const +{ + return GetMeta()->IsInfiniteMappingEnabled (); +} + +void +MpTcpSubflow::Retransmit (void) +{ + NS_LOG_FUNCTION (this); +} + +void +MpTcpSubflow::DoRetransmit () +{ + NS_LOG_FUNCTION (this); + + GetMeta()->OnSubflowRetransmit (this); + TcpSocketBase::DoRetransmit (); +} + +/* Received a packet upon LISTEN state. */ +void +MpTcpSubflow::ProcessListen (Ptr packet, const TcpHeader& tcpHeader, const Address& fromAddress, const Address& toAddress) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + NS_FATAL_ERROR ("This function should never be called, shoud it ?!"); + // Extract the flags. PSH and URG are not honoured. + uint8_t tcpflags = tcpHeader.GetFlags() & ~(TcpHeader::PSH | TcpHeader::URG); + + // Fork a socket if received a SYN. Do nothing otherwise. + // C.f.: the LISTEN part in tcp_v4_do_rcv() in tcp_ipv4.c in Linux kernel + if (tcpflags != TcpHeader::SYN) + { + return; + } + if (!NotifyConnectionRequest(fromAddress)) + { + return; + } + NS_LOG_LOGIC("Updating receive window" << tcpHeader.GetWindowSize()); + + // Clone the socket, simulate fork + Ptr newSock = DynamicCast(Fork()); + NS_LOG_LOGIC ("Cloned a TcpSocketBase " << newSock); + Simulator::ScheduleNow (&MpTcpSubflow::CompleteFork, newSock, packet, + tcpHeader, fromAddress, toAddress); +} + +Ptr +MpTcpSubflow::GetMeta () const +{ + NS_ASSERT (m_metaSocket); + return m_metaSocket; +} + +/* It is also encouraged to + reduce the timeouts (Maximum Segment Life) on subflows at end hosts. + Move TCP to Time_Wait state and schedule a transition to Closed state */ +void +MpTcpSubflow::TimeWait () +{ + NS_LOG_INFO (TcpStateName[m_state] << " -> TIME_WAIT"); + m_state = TIME_WAIT; + CancelAllTimers(); + // Move from TIME_WAIT to CLOSED after 2*MSL. Max segment lifetime is 2 min + // according to RFC793, p.28 + m_timewaitEvent = Simulator::Schedule (Seconds( m_msl), &MpTcpSubflow::CloseAndNotify, this); +} + +void +MpTcpSubflow::AddMpTcpOptionDSS (TcpHeader& header) +{ + NS_LOG_FUNCTION (this); + Ptr dss = Create(); + const bool sendDataFin = m_dssFlags & TcpOptionMpTcpDSS::DataFin; + const bool sendDataAck = m_dssFlags & TcpOptionMpTcpDSS::DataAckPresent; + + if (sendDataAck) + { + uint32_t dack = GetMeta()->GetRxBuffer()->NextRxSequence().GetValue(); + dss->SetDataAck( dack ); + } + + // If no mapping set but datafin set , we have to create the mapping from scratch + if (sendDataFin) + { + m_dssMapping.MapToSSN (SequenceNumber32(0)); + m_dssMapping.SetHeadDSN (SEQ32TO64(GetMeta()->m_txBuffer->TailSequence() )); + m_dssMapping.SetMappingSize(1); + m_dssFlags |= TcpOptionMpTcpDSS::DSNMappingPresent; + } + + // if there is a mapping to send + if (m_dssFlags & TcpOptionMpTcpDSS::DSNMappingPresent) + { + dss->SetMapping (m_dssMapping.HeadDSN().GetValue(), m_dssMapping.HeadSSN().GetValue(), + m_dssMapping.GetLength(), sendDataFin); + } + header.AppendOption (dss); +} + +void +MpTcpSubflow::AddMpTcpOptions (TcpHeader& header) +{ + NS_LOG_FUNCTION(this); + + Ptr join; + if ((header.GetFlags () & TcpHeader::SYN)) + { + AddOptionMpTcp3WHS(header); + } + // as long as we've not received an ack from the peer we + // send an MP_CAPABLE with both keys + else if (!GetMeta()->FullyEstablished()) + { + AddOptionMpTcp3WHS (header); + } + // Constructs DSS if necessary + + if (m_dssFlags && !GetTcpOption (header, join) ) + { + AddMpTcpOptionDSS (header); + } +} + +void +MpTcpSubflow::ProcessClosing (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + return TcpSocketBase::ProcessClosing (packet,tcpHeader); +} + +/* Received a packet upon CLOSE_WAIT, FIN_WAIT_1, or FIN_WAIT_2 states */ +void +MpTcpSubflow::ProcessWait (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + TcpSocketBase::ProcessWait (packet,tcpHeader); +} + +/* Deallocate the end point and cancel all the timers */ +void +MpTcpSubflow::DeallocateEndPoint (void) +{ + NS_LOG_FUNCTION (this); + TcpSocketBase::DeallocateEndPoint (); +} + +void +MpTcpSubflow::CompleteFork (Ptr p, const TcpHeader& h, const Address& fromAddress, const Address& toAddress) +{ + NS_LOG_INFO (this << "Completing fork of MPTCP subflow"); + + if (IsMaster()) + { + GetMeta()->GenerateUniqueMpTcpKey (); + } + TcpSocketBase::CompleteFork (p, h, fromAddress, toAddress); + + if (IsMaster ()) + { + NS_LOG_LOGIC ("Setting meta endpoint to " << m_endPoint + << " (old endpoint=" << GetMeta()->m_endPoint << " )"); + GetMeta ()->m_endPoint = m_endPoint; + } + NS_LOG_LOGIC ("Setting subflow endpoint to " << m_endPoint); +} + +/* Clean up after Bind. Set up callback functions in the end-point. + This function is added to call subflow endpoint to handle receive packet. */ +int +MpTcpSubflow::SetupCallback (void) +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::SetupCallback (); +} + +void +MpTcpSubflow::ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, + Ptr incomingInterface) +{ + NS_LOG_LOGIC ("Socket " << this << " forward up " << + m_endPoint->GetPeerAddress () << + ":" << m_endPoint->GetPeerPort () << + " to " << m_endPoint->GetLocalAddress () << + ":" << m_endPoint->GetLocalPort ()); + TcpSocketBase::ForwardUp (packet, header, port, incomingInterface); +} + +/* Apparently this function is never called for now */ +void +MpTcpSubflow::ConnectionSucceeded (void) +{ + NS_LOG_LOGIC (this << "Connection succeeded"); + m_connected = true; +} + +/* Received a packet upon SYN_SENT */ +void +MpTcpSubflow::ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + + NS_ASSERT (m_state == SYN_SENT); + TcpSocketBase::ProcessSynSent (packet, tcpHeader); +} + +int +MpTcpSubflow::ProcessOptionMpTcpCapable (const Ptr option) +{ + NS_LOG_LOGIC (this << option); + NS_ASSERT_MSG (IsMaster(), "You can receive MP_CAPABLE only on the master subflow"); + + // Expect an MP_CAPABLE option + Ptr mpcRcvd = DynamicCast (option); + NS_ASSERT_MSG(mpcRcvd, "There must be a MP_CAPABLE option"); + GetMeta()->SetPeerKey (mpcRcvd->GetSenderKey() ); + return 0; +} + +int +MpTcpSubflow::ProcessOptionMpTcpJoin (const Ptr option) +{ + NS_LOG_FUNCTION (this << option); + + Ptr join = DynamicCast (option); + NS_ASSERT_MSG (join, "There must be an MP_JOIN option in the SYN Packet" ); + NS_ASSERT_MSG (join && join->GetMode() == TcpOptionMpTcpJoin::SynAck, "the MPTCP join option received is not of the expected 1 out of 3 MP_JOIN types." ); + return 0; +} + +//This functions process MP_ADD_ADDR mptcp options +int +MpTcpSubflow::ProcessOptionMpTcpAddAddress (const Ptr addaddr) +{ + NS_LOG_FUNCTION (this << addaddr << " MP_ADD_ADDR "); + InetSocketAddress address= InetSocketAddress::ConvertFrom (addaddr->GetAddress ()); + + Ipv4Address destinationIp = address.GetIpv4 (); + uint16_t destinationPort = address.GetPort (); + GetMeta()->RemoteAddressInfo.push_back (std::make_pair (destinationIp, destinationPort)); + return 0; +} + +int +MpTcpSubflow::ProcessOptionMpTcp (const Ptr option) +{ + //! adds the header + NS_LOG_FUNCTION (option); + Ptr main = DynamicCast(option); + switch (main->GetSubType()) + { + case TcpOptionMpTcpMain::MP_CAPABLE: + return ProcessOptionMpTcpCapable(main); + case TcpOptionMpTcpMain::MP_JOIN: + return ProcessOptionMpTcpJoin(main); + case TcpOptionMpTcpMain::MP_DSS: + { + Ptr dss = DynamicCast(option); + NS_ASSERT(dss); + // Update later on + ProcessOptionMpTcpDSSEstablished (dss); + } + break; + case TcpOptionMpTcpMain::MP_ADD_ADDR: + { + Ptr addaddr = DynamicCast(option); + NS_ASSERT (addaddr); + ProcessOptionMpTcpAddAddress (addaddr); + } + break; + case TcpOptionMpTcpMain::MP_FASTCLOSE: + case TcpOptionMpTcpMain::MP_FAIL: + default: + NS_FATAL_ERROR ("Unsupported yet"); + break; + }; + return 0; +} + +void +MpTcpSubflow::AddOptionMpTcp3WHS (TcpHeader& hdr) const +{ + NS_LOG_FUNCTION (this << hdr << hdr.FlagsToString(hdr.GetFlags())); + + if (IsMaster() ) + { + //! Use an MP_CAPABLE option + Ptr mpc = CreateObject (); + switch(hdr.GetFlags()) + { + case TcpHeader::SYN: + case (TcpHeader::SYN | TcpHeader::ACK): + mpc->SetSenderKey( GetMeta ()->GetLocalKey () ); + break; + case TcpHeader::ACK: + mpc->SetSenderKey (GetMeta ()->GetLocalKey () ); + mpc->SetPeerKey (GetMeta ()->GetPeerKey () ); + break; + default: + NS_FATAL_ERROR ("Should never happen"); + break; + }; + NS_LOG_INFO ("Appended option" << mpc); + hdr.AppendOption (mpc ); + } + else + { + Ptr join = CreateObject (); + switch (hdr.GetFlags()) + { + case TcpHeader::SYN: + { + join->SetMode (TcpOptionMpTcpJoin::Syn); + join->SetPeerToken (GetMeta ()->GetPeerToken ()); + join->SetNonce (0); + } + break; + case TcpHeader::ACK: + { + uint8_t hmac[20]; + + join->SetMode (TcpOptionMpTcpJoin::Ack); + join->SetHmac (hmac ); + } + break; + case (TcpHeader::SYN | TcpHeader::ACK): + { + join->SetMode (TcpOptionMpTcpJoin::SynAck); + static uint8_t id = 0; + NS_LOG_WARN ("IDs are incremental, there is no real logic behind it yet"); + join->SetAddressId (id++ ); + join->SetTruncatedHmac (424242); // who cares + join->SetNonce (4242); //! truly random :) + } + break; + default: + NS_FATAL_ERROR ("Should never happen"); + break; + } + NS_LOG_INFO ("Appended option" << join); + hdr.AppendOption ( join ); + } +} + +// This function to pass peer token to tcpsocketbase +uint32_t +MpTcpSubflow::PeerToken () +{ + uint32_t dummy = GetMeta ()->GetPeerToken (); + return dummy; +} + +int +MpTcpSubflow::Listen (void) +{ + NS_FATAL_ERROR ("This should never be called. The meta will make the subflow pass from LISTEN to ESTABLISHED."); +} + +void +MpTcpSubflow::NotifySend (uint32_t spaceAvailable) +{ + GetMeta ()->NotifySend (spaceAvailable); +} + +/* if one looks at the linux kernel, tcp_synack_options */ +void +MpTcpSubflow::ProcessSynRcvd (Ptr packet, const TcpHeader& tcpHeader, const Address& fromAddress, + const Address& toAddress) +{ + NS_LOG_FUNCTION (this << tcpHeader); + TcpSocketBase::ProcessSynRcvd (packet, tcpHeader, fromAddress, toAddress); + + //receiver node advertises addresses to the peer after complete fork + GetMeta ()->AdvertiseAddresses (); +} + +uint32_t +MpTcpSubflow::SendPendingData (bool withAck) +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::SendPendingData (withAck); +} + +bool +MpTcpSubflow::IsMaster () const +{ + return m_masterSocket; +} + +bool +MpTcpSubflow::BackupSubflow () const +{ + return m_backupSubflow; +} + +/* should be able to advertise several in one packet if enough space + It is possible + http://tools.ietf.org/html/rfc6824#section-3.4.1 + A host can send an ADD_ADDR message with an already assigned Address + ID, but the Address MUST be the same as previously assigned to this + Address ID, and the Port MUST be different from one already in use + for this Address ID. If these conditions are not met, the receiver + SHOULD silently ignore the ADD_ADDR. A host wishing to replace an + existing Address ID MUST first remove the existing one + (Section 3.4.2). + + A host that receives an ADD_ADDR but finds a conn + ection set up to + that IP address and port number is unsuccessful SHOULD NOT perform + further connection attempts to this address/port combination for this + connection. A sender that wants to trigger a new incoming connection + attempt on a previously advertised address/port combination can + therefore refresh ADD_ADDR information by sending the option again. */ +void +MpTcpSubflow::AdvertiseAddress (Ipv4Address addr, uint16_t port) +{ + NS_LOG_FUNCTION ("Started advertising address"); +} + +bool +MpTcpSubflow::StopAdvertisingAddress (Ipv4Address address) +{ + return true; +} + +void +MpTcpSubflow::ReTxTimeout () +{ + NS_LOG_LOGIC ("MpTcpSubflow ReTxTimeout expired !"); + TcpSocketBase::ReTxTimeout (); +} + +bool +MpTcpSubflow::UpdateWindowSize (const TcpHeader& header) +{ + bool updated = TcpSocketBase::UpdateWindowSize (header); + if (updated) + { + GetMeta ()->UpdateWindowSize (header); + } + return updated; +} + +uint32_t +MpTcpSubflow::GetTxAvailable () const +{ + return TcpSocketBase::GetTxAvailable (); +} + +/* Receipt of new packet, put into Rx buffer + SlowStart and fast recovery remains untouched in MPTCP. + The reaction should be different depending on if we handle NR-SACK or not */ +void +MpTcpSubflow::NewAck (SequenceNumber32 const& ack, bool resetRTO) +{ + NS_LOG_FUNCTION (this << resetRTO << ack); + TcpSocketBase::NewAck (ack, resetRTO); +} + +/* this is private */ +Ptr +MpTcpSubflow::ExtractAtMostOneMapping (uint32_t maxSize, bool only_full_mapping, SequenceNumber64& headDSN) +{ + MpTcpMapping mapping; + Ptr p = Create (); + uint32_t rxAvailable = GetRxAvailable (); + if (rxAvailable == 0) + { + NS_LOG_LOGIC ("Nothing to extract"); + return p; + } + else + { + NS_LOG_LOGIC (rxAvailable << " Rx available"); + } + + // as in linux, we extract in order NextRxSequence + SequenceNumber32 headSSN = m_rxBuffer->HeadSequence (); + if (!m_RxMappings.GetMappingForSSN (headSSN, mapping)) + { + m_RxMappings.Dump (); + NS_FATAL_ERROR ("Could not associate a mapping to ssn [" << headSSN << "]. Should be impossible"); + } + headDSN = mapping.HeadDSN (); + + if (only_full_mapping) + { + + if (mapping.GetLength () > maxSize) + { + NS_LOG_DEBUG ("Not enough space available to extract the full mapping"); + return p; + } + if (m_rxBuffer->Available () < mapping.GetLength ()) + { + NS_LOG_DEBUG ("Mapping not fully received yet"); + return p; + } + } + + // Extract at most one mapping + maxSize = std::min(maxSize, (uint32_t)mapping.GetLength()); + NS_LOG_DEBUG ("Extracting at most " << maxSize << " bytes "); + p = m_rxBuffer->Extract( maxSize ); + SequenceNumber32 extractedTail = headSSN + p->GetSize() - 1; + NS_ASSERT_MSG ( extractedTail <= mapping.TailSSN(), "Can not extract more than the size of the mapping"); + + if (extractedTail < mapping.TailSSN () ) + { + NS_ASSERT_MSG (!only_full_mapping, "The only extracted size possible should be the one of the mapping"); + // only if data extracted covers full mapping we can remove the mapping + } + else + { + m_RxMappings.DiscardMapping (mapping); + } + return p; +} + +void +MpTcpSubflow::AppendDSSMapping (const MpTcpMapping& mapping) +{ + NS_LOG_FUNCTION (this << mapping); + m_dssFlags |= TcpOptionMpTcpDSS::DSNMappingPresent; + m_dssMapping = mapping; +} + +void +MpTcpSubflow::AppendDSSAck () +{ + NS_LOG_FUNCTION(this); + m_dssFlags |= TcpOptionMpTcpDSS::DataAckPresent; +} + +void +MpTcpSubflow::AppendDSSFin () +{ + NS_LOG_FUNCTION (this); + m_dssFlags |= TcpOptionMpTcpDSS::DataFin; +} + +void +MpTcpSubflow::ReceivedData (Ptr p, const TcpHeader& tcpHeader) +{ + NS_LOG_FUNCTION (this << tcpHeader); + MpTcpMapping mapping; + bool sendAck = false; + + // OutOfRange + // If cannot find an adequate mapping, then it should [check RFC] + if (!m_RxMappings.GetMappingForSSN (tcpHeader.GetSequenceNumber (), mapping) ) + { + m_RxMappings.Dump (); + NS_FATAL_ERROR ("Could not find mapping associated "); + return; + } + // Put into Rx buffer + SequenceNumber32 expectedSSN = m_rxBuffer->NextRxSequence (); + if (!m_rxBuffer->Add (p, tcpHeader)) + { // Insert failed: No data or RX buffer full + NS_LOG_WARN ("Insert failed, No data (" << p->GetSize() << ") ?"); + AppendDSSAck (); + SendEmptyPacket (TcpHeader::ACK); + return; + } + + // Size() = Get the actual buffer occupancy + if (m_rxBuffer->Size() > m_rxBuffer->Available () /* Out of order packets exist in buffer */ + || m_rxBuffer->NextRxSequence () > expectedSSN + p->GetSize () /* or we filled a gap */ + ) + { // A gap exists in the buffer, or we filled a gap: Always ACK + sendAck = true; + } + else + { + sendAck = true; + } + // Notify app to receive if necessary + if (expectedSSN < m_rxBuffer->NextRxSequence ()) + { // NextRxSeq advanced, we have something to send to the app + if (!m_shutdownRecv) + { + GetMeta()->OnSubflowRecv ( this ); + sendAck = true; + } + // Handle exceptions + if (m_closeNotified) + { + NS_LOG_WARN ("Why TCP " << this << " got data after close notification?"); + } + // If we received FIN before and now completed all "holes" in rx buffer, + // invoke peer close procedure + if (m_rxBuffer->Finished() && (tcpHeader.GetFlags() & TcpHeader::FIN) == 0) + { + DoPeerClose(); + } + } + // For now we always sent an ack + // should be always true hack to allow compilation + if (sendAck) + { + SendEmptyPacket (TcpHeader::ACK); + } +} + +uint32_t +MpTcpSubflow::UnAckDataCount () const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::UnAckDataCount (); +} + +uint32_t +MpTcpSubflow::BytesInFlight () const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::BytesInFlight (); +} + +uint32_t +MpTcpSubflow::AvailableWindow () const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::AvailableWindow (); +} + +/* this should be ok */ +uint32_t +MpTcpSubflow::Window (void) const +{ + NS_LOG_FUNCTION (this); + return TcpSocketBase::Window (); +} + +uint16_t +MpTcpSubflow::AdvertisedWindowSize( bool scale) const +{ + NS_LOG_DEBUG (this<AdvertisedWindowSize (); +} + +void +MpTcpSubflow::ClosingOnEmpty (TcpHeader& header) +{ + NS_LOG_FUNCTION (this << "mattator"); + header.SetFlags ( header.GetFlags() | TcpHeader::FIN); + if (m_state == ESTABLISHED) + { // On active close: I am the first one to send FIN + NS_LOG_INFO ("ESTABLISHED -> FIN_WAIT_1"); + m_state = FIN_WAIT_1; + } + else if (m_state == CLOSE_WAIT) + { + // On passive close: Peer sent me FIN already + NS_LOG_INFO ("CLOSE_WAIT -> LAST_ACK"); + m_state = LAST_ACK; + } + GetMeta()->OnSubflowClosing (this); +} + +int +MpTcpSubflow::ProcessOptionMpTcpDSSEstablished (const Ptr dss) +{ + NS_LOG_FUNCTION (this << dss << " from subflow "); + + if (!GetMeta()->FullyEstablished () ) + { + NS_LOG_LOGIC ("First DSS received !"); + GetMeta ()->BecomeFullyEstablished (); + } + + //! datafin case handled at the start of the function + if ( (dss->GetFlags () & TcpOptionMpTcpDSS::DSNMappingPresent) && !dss->DataFinMappingOnly () ) + { + MpTcpMapping m; + //Get mapping n'est utilisé qu'une fois, copier le code ici + m = GetMapping (dss); + // Add peer mapping + bool ok = m_RxMappings.AddMapping (m ); + if (!ok) + { + NS_LOG_WARN ("Could not insert mapping: already received ?"); + NS_LOG_UNCOND ("Dumping Rx mappings..."); + m_RxMappings.Dump (); + } + } + if ( dss->GetFlags() & TcpOptionMpTcpDSS::DataFin) + { + NS_LOG_LOGIC ("DFIN detected " << dss->GetDataFinDSN ()); + GetMeta()->PeerClose (SequenceNumber32 (dss->GetDataFinDSN ()), this); + } + + if ( dss->GetFlags() & TcpOptionMpTcpDSS::DataAckPresent) + { + GetMeta()->ReceivedAck (SequenceNumber32 (dss->GetDataAck()), this, false); + } + return 0; +} + +/* Upon ack receival we need to act depending on if it's new or not + if it's new it may allow us to discard a mapping + otherwise notify meta of duplicate + this is called */ +void +MpTcpSubflow::ReceivedAck (Ptr p, const TcpHeader& header) +{ + NS_LOG_FUNCTION (this << header); + + // if packet size > 0 then it will call ReceivedData + TcpSocketBase::ReceivedAck (p, header ); + // By default we always append a DACK + // We should consider more advanced schemes + AppendDSSAck (); +} + +} // end of ns3 Index: src/internet/model/mptcp-subflow.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/mptcp-subflow.h @@ -0,0 +1,317 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 University of Sussex + * Copyright (c) 2015 Université Pierre et Marie Curie (UPMC) + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + * Morteza Kheirkhah + */ +#ifndef MPTCP_SUBFLOW_H +#define MPTCP_SUBFLOW_H + +#include "ns3/trace-source-accessor.h" +#include "ns3/sequence-number.h" +#include "ns3/rtt-estimator.h" +#include "ns3/event-id.h" +#include "ns3/packet.h" +#include "ns3/tcp-socket.h" +#include "ns3/ipv4-end-point.h" +#include "ns3/ipv4-address.h" +#include "ns3/tcp-socket-base.h" +#include "ns3/tcp-header.h" +#include "ns3/mptcp-mapping.h" + +using namespace std; + +namespace ns3 { + +class MpTcpSocketBase; +class TcpOptionMpTcpDSS; +class TcpOptionMpTcpMain; +class TcpSocketBase; +class TcpOptionMpTcpAddAddress; + +/** + * \class MpTcpSubflow + */ +class MpTcpSubflow : public TcpSocketBase +{ +public: + + static TypeId + GetTypeId (void); + + virtual TypeId GetInstanceTypeId (void) const; + virtual uint32_t GetTxAvailable (void) const; + + /* + * This function to pass peer token to tcpsocketbase + */ + uint32_t PeerToken(); + + /** + * \bfief the metasocket is the socket the application is talking to. + * \brief Every subflow is linked to that socket. + * \param The metasocket it is linked to + */ + MpTcpSubflow (); + + MpTcpSubflow (const TcpSocketBase& sock); + MpTcpSubflow (const MpTcpSubflow&); + virtual ~MpTcpSubflow (); + virtual uint32_t UnAckDataCount (void) const; + virtual uint32_t BytesInFlight (void) const; + virtual uint32_t AvailableWindow (void) const; + virtual uint32_t Window (void) const; // Return the max possible number of unacked bytes + + /** + * \brief will update the meta rwnd. Called by subflows whose + * \return true + */ + virtual bool UpdateWindowSize (const TcpHeader& header); + + /** + * \return Value advertised by the meta socket + */ + virtual uint16_t AdvertisedWindowSize (bool scale = true) const; + + /** + * \param metaSocket + */ + virtual void SetMeta (Ptr metaSocket); + + /** + * \warning for prototyping purposes, we let the user free to advertise an IP that doesn't belong to the node + * (in reference to MPTCP connection agility). + * \note Maybe we should change this behavior + */ + virtual void AdvertiseAddress (Ipv4Address , uint16_t port); + + /** + * \brief Send a REM_ADDR for the specific address. + * \see AdvertiseAddress + * \return false if no id associated with the address which likely means it was never advertised in the first place + */ + virtual bool StopAdvertisingAddress(Ipv4Address); + + /** + * \brief This is important. This should first request data from the meta + */ + virtual void NotifySend (uint32_t spaceAvailable); + + /** + * \brief for debug + */ + void DumpInfo () const; + + /** + * \brief + * \note A Master socket is the first to initiate the connection, thus it will use the option MP_CAPABLE + * during the 3WHS while any additionnal subflow must resort to the MP_JOIN option + * \return True if this subflow is the first (should be unique) subflow attempting to connect + */ + virtual bool IsMaster () const; + + /** + * \return True if this subflow shall be used only when all the regular ones failed + */ + virtual bool BackupSubflow () const; + virtual uint32_t SendPendingData (bool withAck = false); + + /** + * \brief Disabled for now. + * SendMapping should be used instead. + */ + virtual int Send (Ptr p, uint32_t flags); // Call by app to send data to network + + /** + * \param dsn will set the dsn of the beginning of the data + * \param only_full_mappings Set to true if you want to extract only packets that match a whole mapping + * \param dsn returns the head DSN of the returned packet + * + * \return this can return an EmptyPacket if on close + */ + virtual Ptr + ExtractAtMostOneMapping (uint32_t maxSize, bool only_full_mappings, SequenceNumber64& dsn); + + /** + * \brief used in previous implementation to notify meta of subflow closing. + * \brief See it that's worth keeping + */ + virtual void ClosingOnEmpty (TcpHeader& header); + virtual void DeallocateEndPoint (void); + virtual void NewAck (SequenceNumber32 const& ack, bool resetRTO); + virtual void TimeWait (void); + virtual void DoRetransmit (void); + virtual int Listen (void); + virtual void ProcessListen (Ptr packet, const TcpHeader& tcpHeader, + const Address& fromAddress, const Address& toAddress); + /** + * \brief Will send MP_JOIN or MP_CAPABLE depending on if it is master or not + * \brief Updates the meta endpoint + * \see TcpSocketBase::CompleteFork + */ + virtual void CompleteFork (Ptr p, const TcpHeader& tcpHeader, + const Address& fromAddress, const Address& toAddress); + virtual void ProcessSynRcvd (Ptr packet, const TcpHeader& tcpHeader, + const Address& fromAddress, const Address& toAddress); + virtual void ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader); + virtual void ProcessWait (Ptr packet, const TcpHeader& tcpHeader); + virtual void Retransmit (void); + + /** + * \bfief Parse DSS essentially + */ + virtual int ProcessOptionMpTcpDSSEstablished (const Ptr option); + virtual int ProcessOptionMpTcpJoin (const Ptr option); + virtual int ProcessOptionMpTcpCapable (const Ptr option); + + /* + * \brief Process TcpOptionTcpAddAddress + * \briefto add advertised address and create new subflows + * \param option MpTcpOptions + */ + virtual int ProcessOptionMpTcpAddAddress (const Ptr option); + + /** + * \brief Helper functions: Connection set up + * \brief Common part of the two Bind(), i.e. set callback and remembering local addr:port + * \returns 0 on success, -1 on failure + */ + virtual int SetupCallback (void); + + /** + * \brief Called by the L3 protocol when it received a packet to pass on to TCP. + * + * \param packet the incoming packet + * \param header the packet's IPv4 header + * \param port the remote port + * \param incomingInterface the incoming interface + */ + virtual void ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface); + +protected: + friend class MpTcpSocketBase; + /** + * \brief This is a public function in TcpSocketBase but it shouldn't be public here ! + */ + virtual int Close (void); + virtual void CloseAndNotify (void); + virtual void GetMappedButMissingData (std::set< MpTcpMapping >& missing); + + /** + * \brief Depending on if this subflow is master or not, we want to + * \brief trigger + * \brief Callbacks being private members + * \brief Overrides parent in order to warn meta + */ + virtual void ConnectionSucceeded (void); + int DoConnect (); + + /** + * \brief In fact, instead of relying on IsMaster etc... + * \brief this should depend on meta's state , if it is wait or not + * \brief and thus it should be pushed in meta (would also remove the need for crypto accessors) + * \param header + */ + virtual void AddOptionMpTcp3WHS (TcpHeader& hdr) const; + + virtual void ProcessClosing (Ptr packet, const TcpHeader& tcpHeader); + virtual int ProcessOptionMpTcp (const Ptr option); + Ptr m_metaSocket; //!< Meta + virtual void SendPacket (TcpHeader header, Ptr p); + +public: + /** + * \return pointer to MpTcpSocketBase + */ + virtual Ptr GetMeta () const; + + /** + * \brief Not implemented + * \return false + */ + virtual bool IsInfiniteMappingEnabled () const; + + virtual void SendEmptyPacket (uint8_t flags); // Send a empty packet that carries a flag, e.g. ACK + +protected: + + ///////////////////////////////////////////// + //// DSS Mapping handling + ///////////////////////////////////////////// + + /** + * \brief Mapping is said "loose" because it is not tied to an SSN yet, this is the job + * \brief of this function: it will look for the FirstUnmappedSSN() and map the DSN to it. + * + * Thus you should call it with increased dsn. + * \param dsnHead + */ + bool AddLooseMapping (SequenceNumber64 dsnHead, uint16_t length); + + /** + * \brief If no mappings set yet, then it returns the tail ssn of the Tx buffer. + * \return Otherwise returns the last registered mapping TailSequence + */ + SequenceNumber32 FirstUnmappedSSN (); + + /** + * \brief Creates a DSS option if does not exist and configures it to have a dataack + */ + virtual void AppendDSSAck (); + + /** + * \brief Corresponds to mptcp_write_dss_mapping and mptcp_write_dss_ack + */ + virtual void AddMpTcpOptionDSS (TcpHeader& header); + + virtual void AppendDSSFin (); + virtual void AppendDSSMapping (const MpTcpMapping& mapping); + virtual void ReceivedAck (Ptr, const TcpHeader&); // Received an ACK packet + virtual void ReceivedData (Ptr packet, const TcpHeader& tcpHeader); + virtual uint32_t SendDataPacket (SequenceNumber32 seq, uint32_t maxSize, bool withAck); + virtual void ReTxTimeout (); + /** + * \brief Overrides the TcpSocketBase that just handles the MP_CAPABLE option. + * \param header + */ + virtual void AddMpTcpOptions (TcpHeader& header); + + /** + * \brief should be able to use parent's one little by little + */ + virtual void CancelAllTimers (void); // Cancel all timer when endpoint is deleted + + // Use Ptr here so that we don't have to unallocate memory manually + // MappingList + MpTcpMappingContainer m_TxMappings; //!< List of mappings to send + MpTcpMappingContainer m_RxMappings; //!< List of mappings to receive + +private: + // Delayed values to + uint8_t m_dssFlags; //!< used to know if AddMpTcpOptions should send a flag + bool m_masterSocket; //!< True if this is the first subflow established (with MP_CAPABLE) + MpTcpMapping m_dssMapping; //!< Pending ds configuration to be sent in next packet + bool m_backupSubflow; //!< Priority + uint32_t m_localNonce; //!< Store local host token, generated during the 3-way handshake + int m_prefixCounter; //!< Temporary variable to help with prefix generation . To remove later + +}; + +} +#endif /* MP_TCP_SUBFLOW */ Index: src/internet/model/tcp-header.cc =================================================================== --- a/src/internet/model/tcp-header.cc +++ b/src/internet/model/tcp-header.cc @@ -22,6 +22,7 @@ #include #include "tcp-header.h" #include "tcp-option.h" +#include "tcp-option-mptcp.h" #include "ns3/buffer.h" #include "ns3/address-utils.h" #include "ns3/log.h" @@ -260,6 +261,12 @@ return ~(it.CalculateIpChecksum (hdrSize)); } +void +TcpHeader::GetOptions (TcpHeader::TcpOptionList& l) const +{ + l = m_options; +} + bool TcpHeader::IsChecksumOk (void) const { @@ -366,7 +373,7 @@ m_sequenceNumber = i.ReadNtohU32 (); m_ackNumber = i.ReadNtohU32 (); uint16_t field = i.ReadNtohU16 (); - m_flags = field & 0xFF; + m_flags = field & 0xFF; // changed from 0x3F to 0xFF by kashif m_length = field >> 12; m_windowSize = i.ReadNtohU16 (); i.Next (2); @@ -385,7 +392,15 @@ uint8_t kind = i.PeekU8 (); Ptr op; uint32_t optionSize; - if (TcpOption::IsKindKnown (kind)) + + if ( kind == TcpOption::MPTCP) + { + i.ReadU16(); // skip TCP kind & length + uint8_t subtype = i.ReadU8() >> 4; // read MPTCP subtype + i.Prev(3); // revert the iterator back to where it should be + op = TcpOptionMpTcpMain::CreateMpTcpOption(subtype); + } + else if (TcpOption::IsKindKnown (kind)) { op = TcpOption::CreateOption (kind); } Index: src/internet/model/tcp-header.h =================================================================== --- a/src/internet/model/tcp-header.h +++ b/src/internet/model/tcp-header.h @@ -188,6 +188,13 @@ Ptr GetOption (uint8_t kind) const; /** + * \brief Copy all options in a list + * \note The list should be empty + * \param options Return a copy of the options + */ + void GetOptions (TcpHeader::TcpOptionList& options) const; + + /** * \brief Get the list of option in this header * \return a const reference to the option list */ @@ -353,6 +360,28 @@ uint8_t m_optionsLen; //!< Tcp options length. }; + /** + * \brief Helper function to find an MPTCP option + * \param ret save found option in ret, otherwise + * \return true if matching option type found. If false, ret should be considered invalid + */ +template +bool +GetTcpOption (const TcpHeader& header, Ptr& ret) +{ + TcpHeader::TcpOptionList l; + header.GetOptions (l); + for (auto it = l.begin(); it != l.end(); it++ ) + { ; + if ((*it)->GetInstanceTypeId () == T::GetTypeId()) + { + ret = DynamicCast (*it ); + return (ret != 0); + } + } + return false; +} + } // namespace ns3 #endif /* TCP_HEADER */ Index: src/internet/model/tcp-l4-protocol.cc =================================================================== --- a/src/internet/model/tcp-l4-protocol.cc +++ b/src/internet/model/tcp-l4-protocol.cc @@ -41,6 +41,9 @@ #include "ipv6-routing-protocol.h" #include "tcp-socket-factory-impl.h" #include "tcp-socket-base.h" +#include "mptcp-socket-base.h" +#include "mptcp-subflow.h" +#include "tcp-option-mptcp.h" #include "tcp-congestion-ops.h" #include "tcp-recovery-ops.h" #include "rtt-estimator.h" @@ -218,6 +221,36 @@ return CreateSocket (m_congestionTypeId, m_recoveryTypeId); } +Ptr +TcpL4Protocol::CreateSocket (TypeId congestionTypeId, TypeId socketTypeId, bool) +{ + NS_LOG_FUNCTION (this << congestionTypeId << socketTypeId); + ObjectFactory congestionAlgorithmFactory; + congestionAlgorithmFactory.SetTypeId (congestionTypeId); + Ptr algo = congestionAlgorithmFactory.Create (); + return CreateSocket(algo, socketTypeId); +} + +Ptr +TcpL4Protocol::CreateSocket (Ptr algo, TypeId socketTypeId) +{ + NS_LOG_FUNCTION (this << algo << socketTypeId); + NS_LOG_FUNCTION_NOARGS (); + ObjectFactory rttFactory; + ObjectFactory socketFactory; + rttFactory.SetTypeId (m_rttTypeId); + socketFactory.SetTypeId(socketTypeId); + + Ptr rtt = rttFactory.Create (); + Ptr socket = socketFactory.Create (); + socket->SetNode (m_node); + socket->SetTcp (this); + socket->SetRtt (rtt); + socket->SetCongestionControlAlgorithm (algo); + m_sockets.push_back (socket); + return socket; +} + Ipv4EndPoint * TcpL4Protocol::Allocate (void) { @@ -430,6 +463,32 @@ } } +Ptr +TcpL4Protocol::LookupMpTcpToken (uint32_t token) +{ + //! We should find the token + /* We go through all the metas to find one with the correct token */ + for (std::vector >::iterator it = m_sockets.begin (), last (m_sockets.end()); + it != last; it++) + { + Ptr sock = *it; + Ptr meta = DynamicCast (sock ); + Address addr; + (*it)->GetSockName (addr); + if (!meta) + { + NS_LOG_DEBUG ("Conversion failed: " << sock << " is not an mptcp socket"); + continue; + } + if (meta->GetLocalToken () == token) + { + NS_LOG_DEBUG ("Found match " << &meta); + return meta; + } + } + return 0; +} + enum IpL4Protocol::RxStatus TcpL4Protocol::Receive (Ptr packet, Ipv4Header const &incomingIpHeader, @@ -456,6 +515,38 @@ incomingTcpHeader.GetSourcePort (), incomingInterface); + if (endPoints.empty()) + { + NS_LOG_LOGIC ("No Ipv4 endpoints matched on TcpL4Protocol, " + "checking if packet is a MP_JOIN request:" << incomingIpHeader); + + // Extract MPTCP options if there is any + // If it is a SYN packet with an MP_JOIN option + Ptr join; + Ptr meta; + + if ( (incomingTcpHeader.GetFlags() & TcpHeader::SYN) + && GetTcpOption(incomingTcpHeader, join) + && join->GetMode () == TcpOptionMpTcpJoin::Syn + ) + { + meta = DynamicCast (LookupMpTcpToken (join->GetPeerToken ())); + if (meta) + { + NS_LOG_LOGIC ("Found meta " << meta << " matching MP_JOIN token=" << join->GetPeerToken ()); + Ipv4EndPoint *endP = meta->NewSubflowRequest (packet, + incomingTcpHeader, + InetSocketAddress (incomingIpHeader.GetSource (), incomingTcpHeader.GetSourcePort ()), + InetSocketAddress (incomingIpHeader.GetDestination (), incomingTcpHeader.GetDestinationPort ()), + join); + if (endP) + { + endPoints.push_back (endP); + } + } + } + } + if (endPoints.empty ()) { if (this->GetObject () != 0) @@ -702,7 +793,7 @@ NS_FATAL_ERROR ("Trying to send a packet without IP addresses"); } -void +bool TcpL4Protocol::AddSocket (Ptr socket) { NS_LOG_FUNCTION (this << socket); @@ -712,13 +803,14 @@ { if (*it == socket) { - return; + return false; } ++it; } m_sockets.push_back (socket); + return true; } bool Index: src/internet/model/tcp-l4-protocol.h =================================================================== --- a/src/internet/model/tcp-l4-protocol.h +++ b/src/internet/model/tcp-l4-protocol.h @@ -27,6 +27,7 @@ #include "ns3/ipv6-address.h" #include "ns3/sequence-number.h" #include "ip-l4-protocol.h" +#include "tcp-congestion-ops.h" namespace ns3 { @@ -37,11 +38,13 @@ class Ipv4EndPointDemux; class Ipv6EndPointDemux; class Ipv4Interface; +class TcpSocket; class TcpSocketBase; class Ipv4EndPoint; class Ipv6EndPoint; +class MpTcpSubflow; class NetDevice; - +class TcpCongestionOps; /** * \ingroup internet @@ -119,14 +122,15 @@ Ptr CreateSocket (TypeId congestionTypeId, TypeId recoveryTypeId); /** - * \brief Create a TCP socket using the specified congestion control algorithm - * \return A smart Socket pointer to a TcpSocket allocated by this instance - * of the TCP protocol - * - * \param congestionTypeId the congestion control algorithm TypeId - * - */ + * \brief Create a TCP socket using the specified congestion control algorithm + * \return A smart Socket pointer to a TcpSocket allocated by this instance + * of the TCP protocol + * + * \param congestionTypeId the congestion control algorithm TypeId + */ Ptr CreateSocket (TypeId congestionTypeId); + Ptr CreateSocket (TypeId congestionTypeId, TypeId socketTypeId, bool); // For MPTCP + Ptr CreateSocket (Ptr algo, TypeId socketTypeId); // For MPTCP /** * \brief Allocate an IPv4 Endpoint @@ -206,6 +210,12 @@ Ipv6Address peerAddress, uint16_t peerPort); /** + * \brief finds if peer have valid token + * \token the key exchanged during 3WHS + */ + Ptr LookupMpTcpToken (uint32_t token); + + /** * \brief Send a packet via TCP (IP-agnostic) * * \param pkt The packet to send @@ -225,7 +235,7 @@ * * \param socket Socket to be added */ - void AddSocket (Ptr socket); + bool AddSocket (Ptr socket); /** * \brief Remove a socket from the internal list Index: src/internet/model/tcp-option-mptcp.cc =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/tcp-option-mptcp.cc @@ -0,0 +1,1409 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 Matthieu Coudron + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + */ + +#include "tcp-option-mptcp.h" +#include "ns3/log.h" + +static inline +uint64_t TRUNC_TO_32(uint64_t seq) { + return static_cast (seq); +} + +namespace ns3 { + +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpCapable); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpAddAddress); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpRemoveAddress); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpJoin); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpChangePriority); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpDSS); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpFail); +NS_OBJECT_ENSURE_REGISTERED (TcpOptionMpTcpFastClose); + +/* note This is a global MPTCP option logger */ +NS_LOG_COMPONENT_DEFINE ("TcpOptionMpTcp"); + +//////// Base for MPTCP options /////////// +TcpOptionMpTcpMain::TcpOptionMpTcpMain () + : TcpOption () +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpMain::~TcpOptionMpTcpMain () +{ + NS_LOG_FUNCTION (this); +} + +TypeId +TcpOptionMpTcpMain::GetInstanceTypeId (void) const +{ + return GetTypeId (); +} + +uint8_t +TcpOptionMpTcpMain::GetKind (void) const +{ + return TcpOption::MPTCP; +} + +TypeId +TcpOptionMpTcpMain::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpMain") + .SetParent () + ; + return tid; +} + +void +TcpOptionMpTcpMain::Print (std::ostream &os) const +{ + NS_ASSERT_MSG (false, " You should override TcpOptionMpTcp::Print function"); +} + +std::string +TcpOptionMpTcpMain::SubTypeToString (const uint8_t& flags, const std::string& delimiter) +{ + static const char* flagNames[8] = { + "CAPABLE", + "JOIN", + "DSS", + "ADD_ADDR", + "REM_ADDR", + "CHANGE_PRIORITY", + "MP_FAIL", + "MP_FASTCLOSE" + }; + + std::string flagsDescription = ""; + + for (int i = 0; i < 8; ++i) + { + if (flags & (1 << i) ) + { + if (flagsDescription.length () > 0) + { + flagsDescription += delimiter; + } + flagsDescription.append (flagNames[i] ); + + } + } + return flagsDescription; +} + +Ptr +TcpOptionMpTcpMain::CreateMpTcpOption (const uint8_t& subtype) +{ + NS_LOG_FUNCTION_NOARGS(); + switch (subtype) + { + case MP_CAPABLE: + return CreateObject(); + case MP_JOIN: + return CreateObject(); + case MP_DSS: + return CreateObject(); + case MP_FAIL: + return CreateObject(); + case MP_FASTCLOSE: + return CreateObject(); + case MP_PRIO: + return CreateObject(); + case MP_REMOVE_ADDR: + return CreateObject(); + case MP_ADD_ADDR: + return CreateObject(); + default: + break; + } + + NS_FATAL_ERROR ("Unsupported MPTCP suboption" << subtype); + return 0; +} + +void +TcpOptionMpTcpMain::SerializeRef (Buffer::Iterator& i) const +{ + i.WriteU8 (GetKind ()); + i.WriteU8 (GetSerializedSize ()); +} + +uint32_t +TcpOptionMpTcpMain::DeserializeRef (Buffer::Iterator& i) const +{ + uint8_t kind = i.ReadU8 (); + uint32_t length = 0; + + NS_ASSERT (kind == GetKind ()); + + length = static_cast(i.ReadU8 ()); + return length; +} + +//////// MP_CAPABLE //////////////// +TcpOptionMpTcpCapable::TcpOptionMpTcpCapable () + : TcpOptionMpTcp (), + m_version (0), + m_flags ( HMAC_SHA1 ), + m_senderKey (0), + m_remoteKey (0), + m_length (12) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpCapable::~TcpOptionMpTcpCapable () +{ + NS_LOG_FUNCTION_NOARGS (); +} + +TypeId +TcpOptionMpTcpCapable::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpCapable") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpCapable::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpCapable::GetTypeId (); +} + +bool +TcpOptionMpTcpCapable::operator== (const TcpOptionMpTcpCapable& opt) const +{ + return (GetPeerKey () == opt.GetPeerKey () && GetSenderKey () == opt.GetSenderKey () ); +} + +void +TcpOptionMpTcpCapable::SetSenderKey (const uint64_t& senderKey) +{ + NS_LOG_FUNCTION (this); + m_senderKey = senderKey; +} + +void +TcpOptionMpTcpCapable::SetPeerKey (const uint64_t& remoteKey) +{ + NS_LOG_FUNCTION (this); + m_length = 20; + m_remoteKey = remoteKey; +} + +void +TcpOptionMpTcpCapable::Print (std::ostream &os) const +{ + os << "MP_CAPABLE:" + << " flags=" << (int)m_flags << "]" + << " Sender's Key :[" << GetSenderKey () << "]"; + if (HasReceiverKey () ) + { + os << " Peer's Key [" << GetPeerKey () << "]"; + } + +} + +bool +TcpOptionMpTcpCapable::IsChecksumRequired () const +{ + return (m_flags >> 7); +} + +void +TcpOptionMpTcpCapable::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + i.WriteU8 ((GetSubType () << 4) + (0x0f & GetVersion ())); // Kind + i.WriteU8 (m_flags); // + i.WriteHtonU64 (GetSenderKey ()); + if (HasReceiverKey ()) + { + i.WriteHtonU64 (GetPeerKey ()); + } +} + +uint32_t +TcpOptionMpTcpCapable::Deserialize (Buffer::Iterator i) +{ + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT (length == 12 || length == 20); + + uint8_t subtype_and_version = i.ReadU8 (); + NS_ASSERT (subtype_and_version >> 4 == GetSubType ()); + m_flags = i.ReadU8 (); + + SetSenderKey (i.ReadNtohU64 ()); + + if (length == 20) + { + SetPeerKey (i.ReadNtohU64 ()); + } + return length; +} + +uint32_t +TcpOptionMpTcpCapable::GetSerializedSize (void) const +{ + return (m_length); +} + +uint8_t +TcpOptionMpTcpCapable::GetVersion (void) const +{ + return 0; +} + +uint64_t +TcpOptionMpTcpCapable::GetSenderKey (void) const +{ + return m_senderKey; +} + +uint64_t +TcpOptionMpTcpCapable::GetPeerKey (void) const +{ + return m_remoteKey; +} + +bool +TcpOptionMpTcpCapable::HasReceiverKey (void) const +{ + return GetSerializedSize () == 20; +} + +//////// MP_JOIN Initial SYN ///////////// +TypeId +TcpOptionMpTcpJoin::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpJoin") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpJoin::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpJoin::GetTypeId (); +} + +TcpOptionMpTcpJoin::TcpOptionMpTcpJoin () + : TcpOptionMpTcp (), + m_mode (Uninitialized), + m_addressId (0), + m_flags (0) +{ + NS_LOG_FUNCTION (this); + memset(&m_buffer[0], 0, sizeof(uint32_t) * 5); +} + +TcpOptionMpTcpJoin::~TcpOptionMpTcpJoin () +{ + NS_LOG_FUNCTION (this); +} + +void +TcpOptionMpTcpJoin::SetPeerToken (const uint32_t& token) +{ + NS_ASSERT ( m_mode & Syn); + m_buffer[0] = token; +} + +void +TcpOptionMpTcpJoin::Print (std::ostream &os) const +{ + os << "MP_JOIN: "; + switch (m_mode) + { + case Uninitialized: + os << "Uninitialized"; + return; + case Syn: + os << "[Syn] with token=" << GetPeerToken () << ", nonce=" << GetNonce (); + return; + case SynAck: + os << "[SynAck] with nonce=" << GetNonce ()<< ", HMAC-B=" << GetTruncatedHmac (); + return; + case Ack: + os << "[Ack] with hash"; + return; + } +} + +void +TcpOptionMpTcpJoin::SetAddressId (const uint8_t& addrId) +{ + NS_ASSERT (m_mode & (SynAck | Ack) ); + m_addressId = addrId; +} + +uint32_t +TcpOptionMpTcpJoin::GetPeerToken () const +{ + NS_ASSERT (m_mode & Syn ); + return m_buffer[0]; +} + +void +TcpOptionMpTcpJoin::SetMode (Mode s) +{ + NS_ASSERT_MSG (m_mode == Uninitialized, "Can't change state once initialized."); + m_mode = s; +} + +uint8_t +TcpOptionMpTcpJoin::GetAddressId () const +{ + NS_ASSERT_MSG (m_mode & (SynAck | Ack), "AddressId only available in states SynAck and Ack"); + return m_addressId; +} + +bool +TcpOptionMpTcpJoin::operator== (const TcpOptionMpTcpJoin& opt) const +{ + + if ( m_mode != opt.m_mode) + { + return false; + } + + /* depending on mode, operator does not check same fields */ + switch (m_mode) + { + case Uninitialized: + return true; + case Syn: + return (GetPeerToken () == opt.GetPeerToken () + && GetNonce () == opt.GetNonce () + && GetAddressId () == opt.GetAddressId ()); + case SynAck: + return (GetNonce () == opt.GetNonce () + && GetAddressId () == opt.GetAddressId ()); + case Ack: + return true; + } + + NS_FATAL_ERROR ( "This should never trigger. Contact ns3 team"); + return false; +} + +TcpOptionMpTcpJoin::Mode +TcpOptionMpTcpJoin::GetMode (void) const +{ + return m_mode; +} + +uint32_t +TcpOptionMpTcpJoin::GetNonce () const +{ + NS_ASSERT_MSG (m_mode & (Syn | SynAck), "Nonce only available in Syn and SynAck modes"); + return m_buffer[0]; +} + +void +TcpOptionMpTcpJoin::SetNonce (const uint32_t& nonce) +{ + if (m_mode == Syn) + { + m_buffer[1] = nonce; + } + else if (m_mode == SynAck) + { + m_buffer[2] = nonce; + } + else + { + NS_FATAL_ERROR ("Unavailable command in this mode"); + } +} + +void +TcpOptionMpTcpJoin::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + i.WriteU8 ( GetSubType () << 4 ); + if (m_mode & (Syn | SynAck)) + { + i.WriteU8 (GetAddressId ()); + } + else + { + i.WriteU8 (0); + } + + switch (m_mode) + { + case Uninitialized: + NS_FATAL_ERROR ("Uninitialized option"); + + case Syn: + i.WriteHtonU32 (GetPeerToken ()); + i.WriteHtonU32 (GetNonce ()); + break; + case SynAck: + { + uint64_t hmac = GetTruncatedHmac (); + i.WriteHtonU64 (hmac); + } + i.WriteHtonU32 (GetNonce ()); + break; + case Ack: + // +=4 cos' amount of bytes we write + for (int j = 0; j < m_mode / 4 - 1; j++) + { + i.WriteHtonU32 (m_buffer[j]); + } + break; + default: + NS_FATAL_ERROR ("Unhandled case"); + } +} + +const uint8_t* +TcpOptionMpTcpJoin::GetHmac () const +{ + NS_ASSERT_MSG (m_mode == Ack, "Only available in Ack mode"); + return 0; +} + +uint32_t +TcpOptionMpTcpJoin::Deserialize (Buffer::Iterator i) +{ + NS_ASSERT (m_mode == Uninitialized); + + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + uint8_t subtype_and_flags = i.ReadU8 (); + NS_ASSERT ((subtype_and_flags >> 4) == GetSubType ()); + m_mode = static_cast ( length ); + m_addressId = i.ReadU8 (); + + switch ( m_mode ) + { + case Uninitialized: + NS_FATAL_ERROR ("Unitialized option, this case should not happen." ); + case Syn: + SetPeerToken (i.ReadNtohU32 ()); + SetNonce (i.ReadNtohU32 ()); + break; + case SynAck: + SetTruncatedHmac (i.ReadNtohU64 ()); + SetNonce (i.ReadNtohU32 ()); // read nonce + break; + case Ack: + i.Read ( (uint8_t*)&m_buffer, 20); + break; + } + + return m_mode; +} + +void +TcpOptionMpTcpJoin::SetHmac (uint8_t hmac[20]) +{ + NS_LOG_ERROR ("Not implemented"); +} + +uint32_t +TcpOptionMpTcpJoin::GetSerializedSize (void) const +{ + NS_ASSERT (m_mode != Uninitialized); + return (m_mode); +} + +void +TcpOptionMpTcpJoin::SetTruncatedHmac (const uint64_t& hmac) +{ + NS_ASSERT_MSG (m_mode == SynAck, "Wrong mode"); + m_buffer[0] = hmac >> 32; + m_buffer[1] = (hmac); +} + +uint64_t +TcpOptionMpTcpJoin::GetTruncatedHmac () const +{ + NS_ASSERT_MSG (m_mode == SynAck, "Wrong mode"); + uint64_t temp = 0; + temp = m_buffer[0]; + temp = temp << 32; + temp |= m_buffer[1]; + + return temp; +} + +//// MP_DSS /////////////// +TcpOptionMpTcpDSS::TcpOptionMpTcpDSS () + : TcpOptionMpTcp (), + m_hasChecksum (false), + m_checksum (0), + m_flags (0), + m_dataAck (0), + m_dsn (0), + m_ssn (0), + m_dataLevelLength (0) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpDSS::~TcpOptionMpTcpDSS () +{ + NS_LOG_FUNCTION (this); +} + +TypeId +TcpOptionMpTcpDSS::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpDSS") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpDSS::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpDSS::GetTypeId (); +} + +void +TcpOptionMpTcpDSS::TruncateDSS(bool truncate) +{ + NS_ASSERT_MSG (m_flags & DSNMappingPresent, "Call it only after setting the mapping"); + + if (truncate) + { + m_flags &= ~(0xff & DSNOfEightBytes); + } + else + { + m_flags |= DSNOfEightBytes; + } +} + +void +TcpOptionMpTcpDSS::SetMapping (uint64_t headDsn, uint32_t headSsn, uint16_t length, bool enable_dfin) +{ + NS_ASSERT_MSG (!(m_flags & DataFin), "For now you can't set mapping after enabling datafin"); + m_dsn = headDsn; + m_ssn = headSsn; + // += in case there is a datafin + m_dataLevelLength = length; + m_flags |= DSNMappingPresent; + if (enable_dfin) + { + m_flags |= DataFin; + } +} + +void +TcpOptionMpTcpDSS::GetMapping (uint64_t& dsn, uint32_t& ssn, uint16_t& length) const +{ + NS_ASSERT ((m_flags & DSNMappingPresent) && !IsInfiniteMapping ()); + ssn = m_ssn; + dsn = m_dsn; + length = m_dataLevelLength; + if (GetFlags () & DataFin) + { + length--; + } +} + +uint32_t +TcpOptionMpTcpDSS::GetSerializedSize (void) const +{ + uint32_t len = GetSizeFromFlags (m_flags) + ((m_hasChecksum) ? 2 : 0); + return len; +} + +uint64_t +TcpOptionMpTcpDSS::GetDataAck (void) const +{ + NS_ASSERT_MSG (m_flags & DataAckPresent, "Can't request DataAck value when DataAck flag was not set. Check for its presence first" ); + return m_dataAck; +} + +void +TcpOptionMpTcpDSS::SetChecksum (const uint16_t& checksum) +{ + m_hasChecksum = checksum; +} + +uint16_t +TcpOptionMpTcpDSS::GetChecksum (void) const +{ + NS_ASSERT (m_hasChecksum); + return m_checksum; +} + +void +TcpOptionMpTcpDSS::Print (std::ostream& os) const +{ + os << " MP_DSS: "; + if (GetFlags () & DataAckPresent) + { + os << "Acknowledges [" << GetDataAck () << "] "; + if (GetFlags () & DataAckOf8Bytes) + { + os << "(8bytes DACK)"; + } + } + + if (GetFlags () & DSNMappingPresent) + { + + if (IsInfiniteMapping ()) + { + os << " Infinite Mapping"; + } + else if (GetFlags () & DataFin) + { + os << "Has datafin for seq [" << GetDataFinDSN () << "]"; + } + os << " DSN:" << m_dsn << " length=" << m_dataLevelLength; + if (GetFlags () & DSNOfEightBytes) + { + os << "(8bytes mapping)"; + } + } +} + +void +TcpOptionMpTcpDSS::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + i.WriteU8 (GetSubType () << 4); + i.WriteU8 (m_flags); + + if (m_flags & DataAckPresent) + { + if (m_flags & DataAckOf8Bytes) + { + i.WriteHtonU64 (m_dataAck ); + } + else + { + i.WriteHtonU32 (static_cast(m_dataAck) ); + } + } + + if (m_flags & DSNMappingPresent) + { + + if (m_flags & DSNOfEightBytes) + { + i.WriteHtonU64 (m_dsn ); + } + else + { + i.WriteHtonU32 (m_dsn ); + } + + // Write relative SSN + i.WriteHtonU32 (m_ssn ); + i.WriteHtonU16 (m_dataLevelLength ); + } + if (m_hasChecksum) + { + i.WriteHtonU16 (m_checksum ); + } +} + +uint32_t +TcpOptionMpTcpDSS::GetSizeFromFlags (uint16_t flags) +{ + uint32_t length = 4; + + if (flags & DataAckPresent) + { + length += 4; + if (flags & DataAckOf8Bytes) + { + length += 4; + } + } + if (flags & DSNMappingPresent) + { + length += 10; /* data length (2) + ssn (4) + DSN min size (4) */ + if (flags & DSNOfEightBytes) + { + length += 4; + } + } + return length; +} + +uint32_t +TcpOptionMpTcpDSS::Deserialize (Buffer::Iterator i) +{ + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + uint8_t subtype_and_reserved = i.ReadU8 (); + + NS_ASSERT ( (subtype_and_reserved >> 4) == GetSubType () ); + m_flags = i.ReadU8 (); + uint32_t shouldBeLength = GetSizeFromFlags (m_flags); + NS_ASSERT (shouldBeLength == length || shouldBeLength + 2 == length); + + if (shouldBeLength + 2 == length) + { + m_hasChecksum = true; + } + if (m_flags & DataAckPresent) + { + if (m_flags & DataAckOf8Bytes) + { + m_dataAck = i.ReadNtohU64 (); + } + else + { + m_dataAck = i.ReadNtohU32 (); + } + } + // Read mapping + if (m_flags & DSNMappingPresent) + { + + if (m_flags & DSNOfEightBytes) + { + m_dsn = i.ReadNtohU64 (); + } + else + { + m_dsn = i.ReadNtohU32 (); + } + m_ssn = i.ReadNtohU32 (); + m_dataLevelLength = i.ReadNtohU16 (); + } + + if (m_hasChecksum) + { + m_checksum = i.ReadNtohU16 (); + } + + return length; +} + +uint8_t +TcpOptionMpTcpDSS::GetFlags (void) const +{ + return m_flags; +} + +/* +Note that when the DATA_FIN is not attached to a TCP segment +containing data, the Data Sequence Signal MUST have a subflow +sequence number of 0, a Data-Level Length of 1, and the data sequence +number that corresponds with the DATA_FIN itself +*/ +bool +TcpOptionMpTcpDSS::DataFinMappingOnly () const +{ + return (m_flags & DataFin) && m_dataLevelLength == 1 && m_ssn == 0; +} + +bool +TcpOptionMpTcpDSS::IsInfiniteMapping () const +{ + // The checksum, in such a case, will also be set to zero + return (GetFlags () & DSNMappingPresent) && m_dataLevelLength == 0; +} + +uint64_t +TcpOptionMpTcpDSS::GetDataFinDSN () const +{ + NS_ASSERT (GetFlags () & DataFin); + + if (DataFinMappingOnly ()) + { + return m_dsn; + } + else + { + return m_dsn + m_dataLevelLength; + } +} + +void +TcpOptionMpTcpDSS::SetDataAck (const uint64_t& dack, const bool& send_as_32bits) +{ + NS_LOG_LOGIC (this << dack); + + m_dataAck = dack; + m_flags |= DataAckPresent; + + if (send_as_32bits) + { + m_dataAck = TRUNC_TO_32 (m_dataAck); + } + else + { + m_flags |= DataAckOf8Bytes; + } +} + +bool +TcpOptionMpTcpDSS::operator == (const TcpOptionMpTcpDSS& opt) const +{ + bool ret = m_flags == opt.m_flags; + ret &= opt.m_checksum == m_checksum; + ret &= opt.m_dsn == m_dsn; + ret &= opt.m_ssn == m_ssn; + ret &= opt.m_dataAck == m_dataAck; + return ( ret ); +} + +//// ADD_ADDR ///////////////////////////////// +TcpOptionMpTcpAddAddress::TcpOptionMpTcpAddAddress () + : TcpOptionMpTcp (), + m_addressVersion (0), + m_addrId (0) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpAddAddress::~TcpOptionMpTcpAddAddress () +{ + NS_LOG_FUNCTION (this); + +} + +TypeId +TcpOptionMpTcpAddAddress::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpAddAddress") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpAddAddress::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpAddAddress::GetTypeId (); +} + +void +TcpOptionMpTcpAddAddress::SetAddress (const Address& _address, uint8_t addrId) +{ + if (InetSocketAddress::IsMatchingType (_address) ) + { + m_addressVersion = 4; + InetSocketAddress address = InetSocketAddress::ConvertFrom (_address); + m_address = address.GetIpv4 (); + m_port = address.GetPort (); + } + else + { + NS_ASSERT_MSG (Inet6SocketAddress::IsMatchingType (_address), "Address of unsupported type"); + m_addressVersion = 6; + Inet6SocketAddress address6 = Inet6SocketAddress::ConvertFrom (_address); + m_address6 = address6.GetIpv6 (); + m_port = address6.GetPort (); + } + + m_addrId = addrId; +} + +void +TcpOptionMpTcpAddAddress::Print (std::ostream &os) const +{ + os << "ADD_ADDR: address id=" << GetAddressId () + << " associated to IP:port ["; + if (m_addressVersion == 4) + { + os << m_address<<":"; + os << m_port; + } + else + { + os << m_address6; + } + os << "]"; +} + +InetSocketAddress +TcpOptionMpTcpAddAddress::GetAddress () const +{ + NS_ASSERT (m_addressVersion == 4); + return InetSocketAddress (m_address, m_port); +} + +Inet6SocketAddress +TcpOptionMpTcpAddAddress::GetAddress6 () const +{ + NS_ASSERT (m_addressVersion == 6); + return Inet6SocketAddress (m_address6,m_port); +} + +uint8_t +TcpOptionMpTcpAddAddress::GetAddressId () const +{ + return m_addrId; +} + +void +TcpOptionMpTcpAddAddress::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + NS_ASSERT_MSG (m_addressVersion == 4 || m_addressVersion == 6, "Set an IP before serializing"); + + i.WriteU8 ((GetSubType () << 4) + (uint8_t) m_addressVersion); + i.WriteU8 (GetAddressId () ); + + if (m_addressVersion == 4) + { + i.WriteHtonU32 (m_address.Get ()); + } + else + { + NS_ASSERT_MSG (m_addressVersion == 6, "You should set an IP address before serializing MPTCP option ADD_ADDR"); + + uint8_t buf[16]; + m_address6.GetBytes (buf); + for (int j = 0; j < 16; ++j) + { + i.WriteU8 (buf[j]); + } + } + + i.WriteHtonU16 (m_port); +} + +uint32_t +TcpOptionMpTcpAddAddress::Deserialize (Buffer::Iterator i) +{ + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT (length == 10 || length == 22); + + uint8_t subtype_and_ipversion = i.ReadU8 (); + NS_ASSERT (subtype_and_ipversion >> 4 == GetSubType ()); + + m_addressVersion = subtype_and_ipversion & 0x0f; + NS_ASSERT_MSG (m_addressVersion == 4 || m_addressVersion == 6, "Unsupported address version"); + + m_addrId = i.ReadU8 (); + + if (m_addressVersion == 4) + { + m_address.Set (i.ReadNtohU32 ()); + m_port = i.ReadNtohU16 (); + } + else + { + NS_FATAL_ERROR ("IPv6 not supported yet"); + } + return length; +} + +uint8_t +TcpOptionMpTcpAddAddress::GetAddressVersion (void) const +{ + return m_addressVersion; +} + +uint32_t +TcpOptionMpTcpAddAddress::GetSerializedSize (void) const +{ + if (GetAddressVersion () == 4) + { + return 10; + } + NS_ASSERT_MSG (GetAddressVersion () == 6,"Wrong IP version. Maybe you didn't set an address to the MPTCP ADD_ADDR option ?"); + return 22; +} + +bool +TcpOptionMpTcpAddAddress::operator== (const TcpOptionMpTcpAddAddress& opt) const +{ + return (GetAddressId () == opt.GetAddressId () + && m_address == opt.m_address + && m_address6 == opt.m_address6); +} + +////// DEL_ADDR Remove address ///////////////////////// +TcpOptionMpTcpRemoveAddress::TcpOptionMpTcpRemoveAddress () + : TcpOptionMpTcp () +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpRemoveAddress::~TcpOptionMpTcpRemoveAddress () +{ + NS_LOG_FUNCTION (this); +} + +TypeId +TcpOptionMpTcpRemoveAddress::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpRemoveAddress") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpRemoveAddress::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpRemoveAddress::GetTypeId (); +} + +void +TcpOptionMpTcpRemoveAddress::GetAddresses (std::vector& addresses) +{ + addresses = m_addressesId; +} + +void +TcpOptionMpTcpRemoveAddress::AddAddressId (uint8_t addrId) +{ + NS_ASSERT_MSG (m_addressesId.size () < 5, "5 is a random limit but it \ + should be weird that you remove more than 5 addresses at once. \ + Maybe increase it"); + + m_addressesId.push_back (addrId); +} + +void +TcpOptionMpTcpRemoveAddress::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + i.WriteU8 ((GetSubType () << 4) ); + for (std::vector::const_iterator it = m_addressesId.begin (); + it != m_addressesId.end (); it++) + { + i.WriteU8 (*it); + } +} + +uint32_t +TcpOptionMpTcpRemoveAddress::Deserialize (Buffer::Iterator i) +{ + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT_MSG (length > 3, "You probably forgot to add AddrId to the MPTCP Remove option"); + uint8_t subtype_and_resvd = i.ReadU8 (); + NS_ASSERT (subtype_and_resvd >> 4 == GetSubType () ); + m_addressesId.clear (); + + for (uint32_t j = 3; j < length; ++j) + { + m_addressesId.push_back ( i.ReadU8 () ); + } + + return length; +} + +uint32_t +TcpOptionMpTcpRemoveAddress::GetSerializedSize (void) const +{ + return ( 3 + m_addressesId.size ()); +} + +void +TcpOptionMpTcpRemoveAddress::Print (std::ostream &os) const +{ + os << "REMOVE_ADDR. Removing addresses:"; + for (std::vector::const_iterator it = m_addressesId.begin (); + it != m_addressesId.end (); it++) + { + os << *it << "/"; + } +} + +bool +TcpOptionMpTcpRemoveAddress::operator== (const TcpOptionMpTcpRemoveAddress& opt) const +{ + return (m_addressesId == opt.m_addressesId); + +} + +//// MP_PRIO change priority //////////////////// +TcpOptionMpTcpChangePriority::TcpOptionMpTcpChangePriority () + : TcpOptionMpTcp (), + m_length (3), + m_addrId (0), + m_flags (false) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpChangePriority::~TcpOptionMpTcpChangePriority (void) +{ + NS_LOG_FUNCTION (this); +} + +TypeId +TcpOptionMpTcpChangePriority::GetTypeId (void) +{ + static TypeId tid = TypeId ("ns3::TcpOptionMpTcpChangePriority") + .SetParent () + .AddConstructor () + ; + return tid; +} + +TypeId +TcpOptionMpTcpChangePriority::GetInstanceTypeId (void) const +{ + return TcpOptionMpTcpChangePriority::GetTypeId (); +} + +void +TcpOptionMpTcpChangePriority::Print (std::ostream &os) const +{ + os << "MP_Prio: address with id ["; + + if (EmbeddedAddressId ()) + { + os << m_addrId; + } + else + { + os << "Not set"; + } + os << "] to flags [" << static_cast(GetFlags ()) << "]"; +} + +void +TcpOptionMpTcpChangePriority::SetAddressId (const uint8_t& addrId) +{ + m_addrId = addrId; + m_length = 4; +} + +uint8_t +TcpOptionMpTcpChangePriority::GetAddressId () const +{ + NS_ASSERT_MSG (EmbeddedAddressId (),"Use EmbeddedAddressId to check beforehand if this option carries an id"); + return m_addrId; +} + +void +TcpOptionMpTcpChangePriority::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + i.WriteU8 ((GetSubType () << 4) + (uint8_t)m_flags); + if (EmbeddedAddressId ()) + { + i.WriteU8 (m_addrId); + } +} + +uint32_t +TcpOptionMpTcpChangePriority::Deserialize (Buffer::Iterator i) +{ + + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT (length == 3 || length == 4); + + uint8_t subtype_and_flags = i.ReadU8 (); + NS_ASSERT (subtype_and_flags >> 4 == GetSubType ()); + SetFlags (subtype_and_flags); + + if (length == 4) + { + SetAddressId (i.ReadU8 ()); + } + + return m_length; +} + +uint32_t +TcpOptionMpTcpChangePriority::GetSerializedSize (void) const +{ + return m_length; +} + +void +TcpOptionMpTcpChangePriority::SetFlags (const uint8_t& flags) +{ + NS_LOG_FUNCTION (flags); + + /* only save the LSB bits */ + m_flags = 0x0f & flags; +} + +uint8_t +TcpOptionMpTcpChangePriority::GetFlags (void) const +{ + return m_flags & 0x0f; +} + +bool +TcpOptionMpTcpChangePriority::EmbeddedAddressId () const +{ + return (GetSerializedSize () == 4); +} + +bool +TcpOptionMpTcpChangePriority::operator== (const TcpOptionMpTcpChangePriority& opt) const +{ + return ( + GetFlags () == opt.GetFlags () + && m_addrId == opt.m_addrId + ); +} + +//// MP_FASTCLOSE to totally stop a flow of data ////// +TcpOptionMpTcpFastClose::TcpOptionMpTcpFastClose () + : TcpOptionMpTcp (), + m_peerKey (0) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpFastClose::~TcpOptionMpTcpFastClose (void) +{ + NS_LOG_FUNCTION (this); +} + +void +TcpOptionMpTcpFastClose::SetPeerKey (const uint64_t& remoteKey) +{ + m_peerKey = remoteKey; +} + +uint64_t +TcpOptionMpTcpFastClose::GetPeerKey (void) const +{ + return m_peerKey; +} + +void +TcpOptionMpTcpFastClose::Print (std::ostream &os) const +{ + os << "MP_FastClose: Receiver key set to [" + << GetPeerKey () << "]"; +} + +bool +TcpOptionMpTcpFastClose::operator== (const TcpOptionMpTcpFastClose& opt) const +{ + + return ( GetPeerKey () == opt.GetPeerKey () ); +} + +void +TcpOptionMpTcpFastClose::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + i.WriteU8 ( (GetSubType () << 4) + (uint8_t)0 ); + i.WriteHtonU64 ( GetPeerKey () ); +} + +uint32_t +TcpOptionMpTcpFastClose::Deserialize (Buffer::Iterator i) +{ + + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT ( length == GetSerializedSize() ); + uint8_t subtype_and_flags = i.ReadU8 (); + NS_ASSERT ( subtype_and_flags >> 4 == GetSubType () ); + + SetPeerKey ( i.ReadNtohU64 () ); + return GetSerializedSize(); +} + +uint32_t +TcpOptionMpTcpFastClose::GetSerializedSize (void) const +{ + return 12; +} + +//// MP_FAIL to totally stop a flow of data //// +TcpOptionMpTcpFail::TcpOptionMpTcpFail () + : TcpOptionMpTcp (), + m_dsn (0) +{ + NS_LOG_FUNCTION (this); +} + +TcpOptionMpTcpFail::~TcpOptionMpTcpFail (void) +{ + NS_LOG_FUNCTION (this); +} + +void +TcpOptionMpTcpFail::SetDSN (const uint64_t& dsn) +{ + NS_LOG_FUNCTION (dsn); + m_dsn = dsn; +} + +uint64_t +TcpOptionMpTcpFail::GetDSN (void) const +{ + return m_dsn; +} + +void +TcpOptionMpTcpFail::Print (std::ostream &os) const +{ + os << "MP_FAIL for DSN=" << GetDSN (); +} + +bool +TcpOptionMpTcpFail::operator== (const TcpOptionMpTcpFail& opt) const +{ + return (GetDSN () == opt.GetDSN ()); +} + +void +TcpOptionMpTcpFail::Serialize (Buffer::Iterator i) const +{ + TcpOptionMpTcp::SerializeRef (i); + + i.WriteU8 ((GetSubType () << 4) + (uint8_t)0); + i.WriteHtonU64 (GetDSN ()); +} + +uint32_t +TcpOptionMpTcpFail::Deserialize (Buffer::Iterator i) +{ + uint32_t length = TcpOptionMpTcpMain::DeserializeRef (i); + NS_ASSERT (length == 12); + + uint8_t subtype_and_flags = i.ReadU8 (); + NS_ASSERT (subtype_and_flags >> 4 == GetSubType ()); + SetDSN (i.ReadNtohU64 ()); + + return 12; +} + +uint32_t +TcpOptionMpTcpFail::GetSerializedSize (void) const +{ + return 12; +} + +} // namespace ns3 Index: src/internet/model/tcp-option-mptcp.h =================================================================== new file mode 100644 --- /dev/null +++ b/src/internet/model/tcp-option-mptcp.h @@ -0,0 +1,1000 @@ +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ +/* + * Copyright (c) 2015 Matthieu Coudron + * + * 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 + * + * Author: Kashif Nadeem + * Matthieu Coudron + */ +#ifndef TCP_OPTION_MPTCP_H +#define TCP_OPTION_MPTCP_H + +#include "tcp-option.h" +#include "tcp-header.h" +#include "ns3/log.h" +#include "ns3/address.h" +#include "ns3/inet-socket-address.h" +#include "ns3/inet6-socket-address.h" +#include "ns3/mptcp-crypto.h" +#include "ns3/sequence-number.h" +#include + +/** + * \ingroup mptcp + * + * Here are a few notations/acronyms used in the multipath tcp group + * - 3WHS = three way handshake (SYN/SYN+ACK/ACK) + * - DSN = Data Sequence Number + * - DSS = Data Sequence Signaling + */ + +namespace ns3 { + +/** + * \brief Defines the TCP option of kind 30 (Multipath TCP) as in \RFC{6824} + * + * MPTCP signaling messages are all encoded under the same TCP option number 30. + * MPTCP then uses a subtype + */ +class TcpOptionMpTcpMain : public TcpOption +{ +public: + /** + * List the different subtypes of MPTCP options + */ + enum SubType + { + MP_CAPABLE, + MP_JOIN, + MP_DSS, + MP_ADD_ADDR, + MP_REMOVE_ADDR, + MP_PRIO, + MP_FAIL, + MP_FASTCLOSE + }; + + TcpOptionMpTcpMain (void); + virtual ~TcpOptionMpTcpMain (void); + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + virtual void Print (std::ostream &os) const; + + /** + * \brief Converts a list of mptcp subtypes + * \return Human readable string of subtypes + */ + static std::string + SubTypeToString (const uint8_t& flags, const std::string& delimiter); + + /** + * \brief Calls CreateObject with the template parameter with the class matching the given subtype + * \param Subtype of the MPTCP option to create + * \return An allocated but unconfigured option + */ + static Ptr CreateMpTcpOption (const uint8_t& kind); + + virtual uint32_t GetSerializedSize (void) const = 0; + + /** Get the subtype number assigned to this MPTCP option */ + virtual uint8_t GetKind (void) const; + + virtual void Serialize (Buffer::Iterator start) const = 0; + + /** + * \return the MPTCP subtype of this class + */ + virtual TcpOptionMpTcpMain::SubType GetSubType (void) const = 0; + +protected: + /** + * \brief Serialize TCP option type & length of the option + * + * Let children write the subtype since Buffer iterators + * can't write less than 1 byte + * Should be called at the start of every subclass Serialize call + */ + virtual void SerializeRef (Buffer::Iterator& i) const; + + /** + * \brief Factorizes ation reading/subtype check that each subclass should do. + * Should be called at the start of every subclass Deserialize + * \return length of the option + */ + uint32_t DeserializeRef (Buffer::Iterator& i) const; +}; + + /** + * \tparam SUBTYPE should be an integer + */ +template +class TcpOptionMpTcp : public TcpOptionMpTcpMain +{ +public: + TcpOptionMpTcp () : TcpOptionMpTcpMain () + { + } + + virtual ~TcpOptionMpTcp (void) + { + } + + /** + * \return MPTCP option type + */ + virtual TcpOptionMpTcpMain::SubType + GetSubType (void) const + { + return SUBTYPE; + } +}; + +/** + * \brief The MP_CAPABLE option is carried on the SYN, SYN/ACK, and ACK packets of the first TCP connection + * established by a MPTCP connection. + * + * This first subflow is sometimes called the *master* subflow. It embeds a key that will be used by later TCP connections (subflows) + * to authenticate themselves as they want to join the MPTCP connection. + * + * Here is how the initial 3WHS must look like: + * + * \verbatim +Host A Host B +------ ------ +MP_CAPABLE -> +[A's key, flags] + <- MP_CAPABLE + [B's key, flags] +ACK + MP_CAPABLE -> +[A's key, B's key, flags] + +\endverbatim + +Here is the format as defined in \RFC{6824}, flags C to H refer to the crypto algorithm. +Only sha1 is defined and supported in the standard (same for ns3). +\verbatim + + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-------+---------------+ +| Kind | Length |Subtype|Version|A|B|C|D|E|F|G|H| ++---------------+---------------+-------+-------+---------------+ +| Option Sender's Key (64 bits) | +| | +| | ++---------------------------------------------------------------+ +| Option Receiver's Key (64 bits) | +| (if option Length == 20) | +| | ++---------------------------------------------------------------+ +\endverbatim + */ +class TcpOptionMpTcpCapable : public TcpOptionMpTcp +{ +public: + TcpOptionMpTcpCapable (void); + virtual ~TcpOptionMpTcpCapable (void); + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + bool operator== (const TcpOptionMpTcpCapable&) const; + + /** + * \brief Only version 0 is standardized so far + * \return 0 + */ + virtual uint8_t GetVersion (void) const; + + /** + * \note MPTCP Checksums are not used in ns3 + * \return True if checksum is required + */ + virtual bool IsChecksumRequired (void) const; + + /** + * Set sender key. Useful for each step of the 3WHS + * + * \param senderKey sender key + */ + virtual void SetSenderKey (const uint64_t& senderKey); + + /** + * Set remote key. Useful only for the last ACK of the 3WHS. + * + * \param remoteKey remote key that was received in the SYN/ACK + */ + virtual void SetPeerKey (const uint64_t& remoteKey); + + /** + * \brief Can tell if the option contain the peer key based on the option length + * \return True if peer key available + */ + virtual bool HasReceiverKey (void) const; + + /** + * \return Sender's key + */ + virtual uint64_t GetSenderKey (void) const; + + /** + * \warn + * \return Sender's key + */ + virtual uint64_t GetPeerKey (void) const; + + //! Inherited + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + +protected: + uint8_t m_version; //!< MPTCP version (4 bytes) + uint8_t m_flags; //!< 8 bits bitfield (unused in the standard for now) + uint64_t m_senderKey; //!< Sender key + uint64_t m_remoteKey; //!< Peer key + uint32_t m_length; //!< Stores the length of the option + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpCapable (const TcpOptionMpTcpCapable&); + TcpOptionMpTcpCapable& operator= (const TcpOptionMpTcpCapable&); +}; + +/** + * \brief The MP_JOIN option is used to add new subflows to an existing MPTCP connection + * + * Once cryptographic keys have been exchanged and the first MPTCP Data Ack (in a DSS option) was received from the server (i.e. 2 RTTs), + * an MPTCP socket can decide to open/close subflows. + * The opening of new subflows is similar to the MP_CAPABLE 3WHS but it uses MP_JOIN option instead. + * The MP_JOIN embeds cryptographic data generated from a nonce and the keys sent through the initial MP_CAPABLE in + * order to be recognized by the remote host as valid connections (to prevent connection hijacking). + * + * An MP_JOIN is exchanged during the 3 modes of the 3WHS: Syn/SynAck/Ack and depending on the mode, + * the structure of the option is very different as can be seen in the following: + * + * MP_JOIN Option for Initial SYN: + * \verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-----+-+---------------+ +| Kind | Length = 12 |Subtype| |B| Address ID | ++---------------+---------------+-------+-----+-+---------------+ +| Receiver's Token (32 bits) | ++---------------------------------------------------------------+ +| Sender's Random Number (32 bits) | ++---------------------------------------------------------------+ +\endverbatim + +MP_JOIN for responding SYN/ACK +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-----+-+---------------+ +| Kind | Length = 16 |Subtype| |B| Address ID | ++---------------+---------------+-------+-----+-+---------------+ +| | +| Sender's Truncated HMAC (64 bits) | +| | ++---------------------------------------------------------------+ +| Sender's Random Number (32 bits) | ++---------------------------------------------------------------+ + +\endverbatim +MP_JOIN Option for third ACK: +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-----------------------+ +| Kind | Length = 24 |Subtype| (reserved) | ++---------------+---------------+-------+-----------------------+ +| | +| | +| Sender's HMAC (160 bits) | +| | +| | ++---------------------------------------------------------------+ +\endverbatim + +To sum up a subflow 3WHS : +\verbatim +Host A Host B + | SYN + MP_JOIN(Token-B, R-A) | + |------------------------------->| + |<-------------------------------| + | SYN/ACK + MP_JOIN(HMAC-B, R-B) | + | | + | ACK + MP_JOIN(HMAC-A) | + |------------------------------->| + |<-------------------------------| + | ACK | + +HMAC-A = HMAC(Key=(Key-A+Key-B), Msg=(R-A+R-B)) +HMAC-B = HMAC(Key=(Key-B+Key-A), Msg=(R-B+R-A)) +\endverbatim +or +\verbatim + Host A Host B + ------ ------ + MP_JOIN -> + [B's token, A's nonce, + A's Address ID, flags] + <- MP_JOIN + [B's HMAC, B's nonce, + B's Address ID, flags] + ACK + MP_JOIN -> + [A's HMAC] + + <- ACK +\endverbatim +**/ +class TcpOptionMpTcpJoin : public TcpOptionMpTcp +{ + +public: + /** + * \enum State + * \brief The MPTCP standard assigns only one MP_JOIN subtype but depending on its rank + * during the 3 WHS, it can have very different meanings. In order to prevent misuse by the user, + * this option is very defensive and won't accept setter or getter calls for a rank that is + * not its current rank + * The enum values match the size (in bytes) of the option. + */ + enum Mode + { + Uninitialized = 0, + Syn = 12, /**< peer token + nonce */ + SynAck = 16, /**< peer truncated hmac + nonce */ + Ack = 24 /**< Send sender full hmac */ + }; + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + TcpOptionMpTcpJoin (void); + virtual ~TcpOptionMpTcpJoin (void); + + virtual bool operator== (const TcpOptionMpTcpJoin&) const; + + /** + * \return Mode (internal value) in which the option is configured + */ + virtual Mode GetMode (void) const; + + /** + * \brief Available in Syn and SynAck modes + * \return nonce + */ + virtual uint32_t GetNonce (void) const; + + /** + * \param nonce Random number used to prevent malicious subflow establishement from + * malicious nodes + */ + virtual void SetNonce (const uint32_t& nonce); + + /** + * \note Available in SynAck mode only + */ + virtual void SetTruncatedHmac (const uint64_t& ); + + /** + * \note Available in SynAck mode + */ + virtual uint64_t GetTruncatedHmac (void) const; + + /** + * \brief Returns hmac generated by the peer. + * \warning Available in Ack mode only + * \return null for now (not implemented) + */ + virtual const uint8_t* GetHmac (void) const; + + /** + * \brief Available only in Ack mode. Sets Hmac computed from the nonce, tokens previously exchanged + * \warning Available in Ack mode only + * \param hmac + * \todo not implemented + */ + virtual void SetHmac (uint8_t hmac[20]); + + /** + * \brief Set token computed from peer's key + * Used in the SYN message of the MP_JOIN 3WHS + */ + virtual void SetPeerToken (const uint32_t& token); + + /** + * \return Peer token (the token being a hash of the key) + */ + virtual uint32_t GetPeerToken (void) const; + + /** + * \brief Unique id assigned by the remote host to this subflow combination (ip,port). + * \return Identifier of the subflow + */ + virtual uint8_t GetAddressId (void) const; + + /** + * \brief When a subflow joins a connection, it should advertise a unique identifier + * \param addrId unique identifier associated with this very subflow + */ + virtual void SetAddressId (const uint8_t& addrId); + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + + /** + * \brief When creating from scratch a, MP_CAPABLE option, to call first. Once a mode has been set, + * it is not possible to change. Depending on the chosen mode, some functions will trigger a fatal error + * to prevent ns3 from doing meaningless actions. + * \param mode Mode in which we want to configure the option: Syn or SynAck or Ack + */ + virtual void SetMode (Mode s); + +protected: + Mode m_mode; //!< 3 typs of MP_JOIN. Members will throw an exception if used in wrong mode + uint8_t m_addressId; //!< Unique identifier assigend to this subflow + uint8_t m_flags; //!< Unused so far + uint32_t m_buffer[5]; //!< Memory buffer to register the data of the different modes + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpJoin (const TcpOptionMpTcpJoin&); + TcpOptionMpTcpJoin& operator= (const TcpOptionMpTcpJoin&); +}; + +/** + * \brief DSS option (Data Sequence Signaling) + * + * This option can transport 3 different optional semantic information. + * First it is important to understand that MPTCP uses an additional sequence number space + * on top of TCP sequence numbers in order to reorder the bytes at the receiver. + * The DSS option is in charge of carrying MPTCP sequence number related information between + * the hosts. + * MPTCP sequence numbers are mapped to TCP sequence numbers through this option and + * are acked through this option as well. It it also this option that signals the end of + * the MPTCP connection (similar to the TCP FIN flag). + * + * \warn Note that members asserts that flag is enabled before returning any value. + * + * \note MPTCP sequence numbers are 8 bytes long but when conveyed through a TCP option, + * they can be trimmed to 4 bytes in order to save TCP option space. + * + * \warn ns3 should allow to carry both 4 and 8 bytes but the initial implementation relied on 4 bytes + * so you may find bugs + * + * \warn checksum are not supported since they make little sense in ns3 case + * + * Data Sequence Signal (DSS) Option +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +---------------+---------------+-------+----------------------+ + | Kind | Length |Subtype| (reserved) |F|m|M|a|A| + +---------------+---------------+-------+----------------------+ + | Data ACK (4 or 8 octets, depending on flags) | + +--------------------------------------------------------------+ + | Data sequence number (4 or 8 octets, depending on flags) | + +--------------------------------------------------------------+ + | Subflow Sequence Number (4 octets) | + +-------------------------------+------------------------------+ + | Data-Level Length (2 octets) | Checksum (2 octets) | + +-------------------------------+------------------------------+ + +\endverbatim +*/ +class TcpOptionMpTcpDSS : public TcpOptionMpTcp +{ + +public: + /** + * \brief Each value represents the offset of a flag in LSB order + * \see TcpOptionMpTcpDSS + */ + enum FLAG + { + DataAckPresent = 1, //!< matches the "A" in previous packet format + DataAckOf8Bytes = 2, //!< a + DSNMappingPresent = 4, //!< M bit + DSNOfEightBytes = 8, //!< m bit + DataFin = 16, //!< F . set to indicate end of communication + CheckSumPresent = 32 //!< Not computed for now + + }; + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + TcpOptionMpTcpDSS (void); + virtual ~TcpOptionMpTcpDSS (void); + + /** + * \brief Upon detecting an error, an MPTCP connection can fallback to legacy TCP. + * \return False (not implemented) + */ + virtual bool IsInfiniteMapping () const; + + /** + * \brief when DataFin is set, the data level length is increased by one. + * \return True if the mapping present is just because of the datafin + */ + virtual bool DataFinMappingOnly () const; + + virtual void TruncateDSS(bool truncate); + + /** + * \brief This returns a copy + * \warning Asserts if flags + * \ + */ + virtual void GetMapping (uint64_t& dsn, uint32_t& ssn, uint16_t& length) const; + + /** + * \brief + * \param trunc_to_32bits Set to true to send a 32bit DSN + * \warn Mapping can be set only once, otherwise it will crash ns3 + */ + virtual void SetMapping (uint64_t headDsn, uint32_t headSsn, uint16_t length, bool enable_dfin); + + /** + * \brief A DSS length depends on what content it embeds. This is defined by the flags. + * \return All flags + */ + virtual uint8_t GetFlags (void) const; + + virtual bool operator== (const TcpOptionMpTcpDSS&) const; + + /** + * \brief Set seq nb of acked data at MPTP level + * \param dack Sequence number of the dataack + * \param send_as_32bits Decides if the DACK should be sent as a 32 bits number + */ + virtual void SetDataAck (const uint64_t& dack, const bool& send_as_32bits = true); + + /** + * \brief Get data ack value + * \warning check the flags to know if the returned value is a 32 or 64 bits DSN + */ + virtual uint64_t GetDataAck (void) const; + + /** + * \brief Unimplemented + */ + virtual void SetChecksum (const uint16_t&); + + /** + * \brief Unimplemented + */ + virtual uint16_t GetChecksum (void) const; + + /** + * \return If DFIN is set, returns its associated DSN + * + * \warning check the flags to know if it returns a 32 or 64 bits DSN + */ + virtual uint64_t GetDataFinDSN () const; + + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator ) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + + /** + * \brief the DSS option size can change a lot + * The DSS size depends if it embeds a DataAck, a mapping, in 32 bits + * or in 64 bits. This can compute the size depending on the flags + * \param flags flags of the DSS option + */ + static uint32_t GetSizeFromFlags (uint16_t flags); + +protected: + + bool m_hasChecksum; //!< true if checksums enabled + uint16_t m_checksum; //!< valeu of the checksum + uint8_t m_flags; //!< bitfield + + // In fact for now we use only 32 LSB + uint64_t m_dataAck; //!< Can be On 32 bits dependings on the flags + uint64_t m_dsn; //!< Data Sequence Number (Can be On 32 bits dependings on the flags) + uint32_t m_ssn; //!< Subflow Sequence Number, always 32bits + uint16_t m_dataLevelLength; //!< Length of the mapping and/or +1 if DFIN + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpDSS (const TcpOptionMpTcpDSS&); + TcpOptionMpTcpDSS& operator= (const TcpOptionMpTcpDSS&); +}; + +/** + * \brief Used to advertise new subflow possibilities + * + * This option signals to the peer an IP or a couple (IP, port) to which the peer could + * open a new subflow. + * Every combination (IP, port) must have a unique ID ("Address ID"), whose allocation is not specified + * in the standard. + * + * \see TcpOptionMpTcpRemoveAddress + * + * \note Only supports IPv4 version for now + * + * \note Though the port is optional in the RFC, ns3 implementation always include it, even if + * it's 0 for the sake of simplicity. + * + * Add Address (ADD_ADDR) option: +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-------+---------------+ +| Kind | Length |Subtype| IPVer | Address ID | ++---------------+---------------+-------+-------+---------------+ +| Address (IPv4 - 4 octets / IPv6 - 16 octets) | ++-------------------------------+-------------------------------+ +| Port (2 octets, optional) | ++-------------------------------+ +\endverbatim + */ +class TcpOptionMpTcpAddAddress : public TcpOptionMpTcp +{ + +public: + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + TcpOptionMpTcpAddAddress (void); + virtual ~TcpOptionMpTcpAddAddress (void); + + /** + * \brief we always send the port, even if it's 0 ? + * \brief Expects InetXSocketAddress + * "port is specified, MPTCP SHOULD attempt to connect to the specified + * address on the same port as is already in use by the subflow on which + * the MP_ADD_ADDR signal was sent" + */ + virtual void SetAddress (const Address& address, uint8_t addrId); + + virtual bool operator== (const TcpOptionMpTcpAddAddress&) const; + + /** + * \note Only IPv4 is supported so far + * \return IP version (i.e., 4 or 6) + */ + virtual uint8_t GetAddressVersion (void) const; + + /** + * \brief Return advertised InetSocketAddress. + * If port unset, ns3 sets it to 0 + * \return + */ + virtual InetSocketAddress GetAddress (void) const; + + /** + * \see GetAddress + */ + virtual Inet6SocketAddress GetAddress6 (void) const; + + /** + * \return Address id assigned to the combination + */ + virtual uint8_t GetAddressId (void) const; + + //! Inherited + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + +protected: + uint8_t m_addressVersion; //!< IPversion (4 or 6) + uint8_t m_addrId; //!< Address ID + uint16_t m_port; //!< Optional value // changed from uint8_t to uint16_t + Ipv4Address m_address; //!< Advertised IPv4 address + Ipv6Address m_address6; //!< unused + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpAddAddress (const TcpOptionMpTcpAddAddress&); + TcpOptionMpTcpAddAddress& operator= (const TcpOptionMpTcpAddAddress&); +}; + +/** + * \see TcpOptionMpTcpAddAddress + * + * \brief Allow to remove joinable addresses + * + * In case ADD_ADDR was used to advertise an IP that is not valid anymore (e.g., because of roaming), + * this option allows to remove it from peer memory (and can remove a batch of them). + * The subflow should not exist yet (else you must use TCP FIN to close it). + * + * REMOVE_ADDR option: + * \verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-------+---------------+ +| Kind | Length = 3+n |Subtype|(resvd)| Address ID | ... ++---------------+---------------+-------+-------+---------------+ + (followed by n-1 Address IDs, if required) + +\endverbatim + */ +class TcpOptionMpTcpRemoveAddress : public TcpOptionMpTcp +{ + +public: + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + TcpOptionMpTcpRemoveAddress (void); + virtual ~TcpOptionMpTcpRemoveAddress (void); + + /** + * As we do not know in advance the number of records, we pass a vector + * \param Returns the addresses into a vector. Empty it before use. + */ + void GetAddresses (std::vector& addresses); + + /** + * Append an association id to remove from peer memory + * \param addressId The association (IP,port) id + */ + void AddAddressId (uint8_t addressId); + + virtual bool operator== (const TcpOptionMpTcpRemoveAddress&) const; + + //! Inherited + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + +protected: + std::vector m_addressesId; //!< List of removed association ids + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpRemoveAddress (const TcpOptionMpTcpRemoveAddress&); + TcpOptionMpTcpRemoveAddress& operator= (const TcpOptionMpTcpRemoveAddress&); +}; + +/** + * \brief Allow to (un)block emission from peer on this subflow + * + * MPTCP allows a host to advertise their preference about the links they would prefer to use. + * It is advertised as a boolean (B) that means "Please don't send data on this connection, except if + * there is a problem". + * The peer is free to ignore this preference. + * This option is unidirectional, i.e, an emitter may ask not to receive data + * on a subflow while transmitting on it. + * + * \warn This option is not taken into account by ns3 yet. + * MP_PRIO option: +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-----+-+--------------+ +| Kind | Length |Subtype| |B| AddrID (opt) | ++---------------+---------------+-------+-----+-+--------------+ + +\endverbatim + * + */ +class TcpOptionMpTcpChangePriority : public TcpOptionMpTcp +{ + +public: + /** + * Only one flag standardized + */ + enum Flags + { + Backup = 0 /**< Set this flag if you prefer not to receive data on path addressId*/ + }; + + static TypeId GetTypeId (void); + virtual TypeId GetInstanceTypeId (void) const; + + TcpOptionMpTcpChangePriority (void); + virtual ~TcpOptionMpTcpChangePriority (void); + + /** + * \brief All flags should be set at once. + * + * You can append flags by using SetFlags( option.GetFlags() | NewFlag) + * + * \example SetFlags(TcpOptionMpTcpChangePriority::Backup); + */ + virtual void SetFlags (const uint8_t& flags); + + /** + * \brief Optional. If you don't set addrId, the receiver considers the subflow on which the + * option was received + */ + virtual void SetAddressId (const uint8_t& addrId); + + /** + * \return True if an address id was set in the packet. + * \note Result depends on length of the option + */ + virtual bool EmbeddedAddressId (void) const; + + /** + * \return association id + * \warning will assert if addressId was not set. check it with \see EmbeddedAddressId + */ + virtual uint8_t GetAddressId () const; + + /** + * \return flags, i.e., the 4 LSB + */ + virtual uint8_t GetFlags (void) const; + + virtual bool operator== (const TcpOptionMpTcpChangePriority& ) const; + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + + /** + * \brief Length may be 3 or 4 (if addrId present) + */ + virtual uint32_t GetSerializedSize (void) const; + +private: + /** + * \brief Copy constructor + * Defined and unimplemented to avoid misuse + */ + TcpOptionMpTcpChangePriority (const TcpOptionMpTcpChangePriority&); + TcpOptionMpTcpChangePriority& operator= (const TcpOptionMpTcpChangePriority&); + + uint8_t m_length; //!< Length of this option + uint8_t m_addrId; //!< May be unset + uint8_t m_flags; //!< On 4 bits +}; + +/** + * \brief MP_FASTCLOSE is the equivalent of TCP RST at the MPTCP level + * + * For example, if the operating system is running out of resources, MPTCP could send an + * MP_FASTCLOSE. + * This should not be very useful in ns3 scenarii. + * + * MP_FASTCLOSE option: +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+-----------------------+ +| Kind | Length |Subtype| (reserved) | ++---------------+---------------+-------+-----------------------+ +| Option Receiver's Key | +| (64 bits) | +| | ++---------------------------------------------------------------+ + +\endverbatim +**/ +class TcpOptionMpTcpFastClose : public TcpOptionMpTcp +{ + +public: + TcpOptionMpTcpFastClose (void); + virtual ~TcpOptionMpTcpFastClose (void); + virtual bool operator== (const TcpOptionMpTcpFastClose&) const; + + /** + * \brief Set peer key to prevent spoofing a MP_FastClose. + * \param remoteKey key exchanged during the 3WHS. + */ + virtual void SetPeerKey (const uint64_t& remoteKey); + + /** + * \return peer's key + */ + virtual uint64_t GetPeerKey (void) const; + + //! Inherited + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + +private: + uint64_t m_peerKey; //!< Key of the remote host so that it accepts the RST + + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpFastClose (const TcpOptionMpTcpFastClose&); + TcpOptionMpTcpFastClose& operator= (const TcpOptionMpTcpFastClose&); +}; + +/** + * \brief Option used when a problem is noticed at any point during a connection (payload changed etc). + * A subflow would sends RST and MP_FAIL option + * + * Note that the MP_FAIL option requires the use of the full 64-bit sequence number, even if 32-bit sequence numbers are + * normally in use in the DSS signals on the path. + * + * Such a situation is likely to never happen in ns3 and thus this option is implemented as a reference only. + * + MP_FAIL option: +\verbatim + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++---------------+---------------+-------+----------------------+ +| Kind | Length=12 |Subtype| (reserved) | ++---------------+---------------+-------+----------------------+ +| | +| Data Sequence Number (8 octets) | +| | ++--------------------------------------------------------------+ + +\endverbatim +*/ +class TcpOptionMpTcpFail : public TcpOptionMpTcp +{ + +public: + TcpOptionMpTcpFail (void); + virtual ~TcpOptionMpTcpFail (void); + + virtual bool operator== (const TcpOptionMpTcpFail& ) const; + + /** + * \brief Set Data Sequence Number (DSN) until which the communication was fine + * \param dsn Last in-order ACK-1 received + */ + virtual void SetDSN (const uint64_t& dsn); + + /** + * \return DSN for which error was detected + */ + virtual uint64_t GetDSN (void) const; + + //! Inherited + virtual void Print (std::ostream &os) const; + virtual void Serialize (Buffer::Iterator start) const; + virtual uint32_t Deserialize (Buffer::Iterator start); + virtual uint32_t GetSerializedSize (void) const; + +private: + //! Defined and unimplemented to avoid misuse + TcpOptionMpTcpFail (const TcpOptionMpTcpFail&); + TcpOptionMpTcpFail& operator= (const TcpOptionMpTcpFail&); + + uint64_t m_dsn; //!< Last acked dsn +}; + +/** + * \brief Like GetMpTcpOption but if does not find the option then it creates one + * \brief and append it to the the header + * \see GetMpTcpOption + * \return false if it had to create the option + */ +template +bool +GetOrCreateMpTcpOption (TcpHeader& header, Ptr& ret) +{ + if (!GetTcpOption (header,ret)) + { + ret = Create (); + header.AppendOption (ret); + return false; + } + return true; +} + +} // namespace ns3 + +#endif /* TCP_OPTION_MPTCP */ Index: src/internet/model/tcp-option.cc =================================================================== --- a/src/internet/model/tcp-option.cc +++ b/src/internet/model/tcp-option.cc @@ -107,7 +107,8 @@ case SACKPERMITTED: case SACK: case TS: - // Do not add UNKNOWN here + case MPTCP: + // Do not add UNKNOWN here return true; } Index: src/internet/model/tcp-option.h =================================================================== --- a/src/internet/model/tcp-option.h +++ b/src/internet/model/tcp-option.h @@ -61,6 +61,7 @@ SACKPERMITTED = 4, //!< SACKPERMITTED SACK = 5, //!< SACK TS = 8, //!< TS + MPTCP = 30, //!< Multipath TCP options share the same Kind UNKNOWN = 255 //!< not a standardized value; for unknown recv'd options }; Index: src/internet/model/tcp-rx-buffer.cc =================================================================== --- a/src/internet/model/tcp-rx-buffer.cc +++ b/src/internet/model/tcp-rx-buffer.cc @@ -60,6 +60,12 @@ } SequenceNumber32 +TcpRxBuffer::HeadSequence(void) const +{ + return NextRxSequence()-Available(); +} + +SequenceNumber32 TcpRxBuffer::NextRxSequence (void) const { return m_nextRxSeq; @@ -223,7 +229,7 @@ if (m_gotFin && m_nextRxSeq == m_finSeq) { // Account for the FIN packet ++m_nextRxSeq; - }; + } return true; } Index: src/internet/model/tcp-rx-buffer.h =================================================================== --- a/src/internet/model/tcp-rx-buffer.h +++ b/src/internet/model/tcp-rx-buffer.h @@ -85,6 +85,12 @@ TcpRxBuffer (uint32_t n = 0); virtual ~TcpRxBuffer (); + /** + * \return First in order sequence sumber + * + **/ + SequenceNumber32 HeadSequence(void) const; + // Accessors /** * \brief Get Next Rx Sequence number Index: src/internet/model/tcp-socket-base.cc =================================================================== --- a/src/internet/model/tcp-socket-base.cc +++ b/src/internet/model/tcp-socket-base.cc @@ -58,6 +58,10 @@ #include "tcp-option-sack.h" #include "tcp-congestion-ops.h" #include "tcp-recovery-ops.h" +#include "mptcp-crypto.h" +#include "mptcp-subflow.h" +#include "mptcp-socket-base.h" +#include "tcp-option-mptcp.h" #include #include @@ -89,6 +93,11 @@ UintegerValue (65535), MakeUintegerAccessor (&TcpSocketBase::m_maxWinSize), MakeUintegerChecker ()) + .AddAttribute ("EnableMpTcp", + "Enable or disable MPTCP support", + BooleanValue (false), + MakeBooleanAccessor (&TcpSocketBase::m_mptcpEnabled), + MakeBooleanChecker ()) .AddAttribute ("IcmpCallback", "Callback invoked whenever an icmp error is received on this socket.", CallbackValue (), MakeCallbackAccessor (&TcpSocketBase::m_icmpCallback), @@ -102,11 +111,11 @@ MakeBooleanAccessor (&TcpSocketBase::m_winScalingEnabled), MakeBooleanChecker ()) .AddAttribute ("Sack", "Enable or disable Sack option", - BooleanValue (true), + BooleanValue (false), MakeBooleanAccessor (&TcpSocketBase::m_sackEnabled), MakeBooleanChecker ()) .AddAttribute ("Timestamp", "Enable or disable Timestamp option", - BooleanValue (true), + BooleanValue (false), MakeBooleanAccessor (&TcpSocketBase::m_timestampEnabled), MakeBooleanChecker ()) .AddAttribute ("MinRto", @@ -320,6 +329,10 @@ m_rWnd (sock.m_rWnd), m_highRxMark (sock.m_highRxMark), m_highRxAckMark (sock.m_highRxAckMark), + m_mptcpEnabled (sock.m_mptcpEnabled), + m_mptcpLocalKey (sock.m_mptcpLocalKey), + m_mptcpLocalToken (sock.m_mptcpLocalToken), + m_mptcpPeerToken (sock.m_mptcpPeerToken), m_sackEnabled (sock.m_sackEnabled), m_winScalingEnabled (sock.m_winScalingEnabled), m_rcvWindShift (sock.m_rcvWindShift), @@ -459,6 +472,12 @@ m_rtt = rtt; } +TcpSocket::TcpStates_t +TcpSocketBase::GetState () const +{ + return m_state; +} + /* Inherit from Socket class: Returns error code */ enum Socket::SocketErrno TcpSocketBase::GetErrno (void) const @@ -1369,9 +1388,19 @@ h.SetSourcePort (tcpHeader.GetDestinationPort ()); h.SetDestinationPort (tcpHeader.GetSourcePort ()); h.SetWindowSize (AdvertisedWindowSize ()); - AddOptions (h); + if (!m_mptcpEnabled) + { + AddOptions (h); + } m_txTrace (p, h, this); - m_tcp->SendPacket (p, h, toAddress, fromAddress, m_boundnetdevice); + if (m_mptcpEnabled) + { + SendPacket (h,p); + } + else + { + m_tcp->SendPacket (p, h, toAddress, fromAddress, m_boundnetdevice); + } } break; case SYN_SENT: @@ -1418,6 +1447,7 @@ // Different flags are different events if (tcpflags == TcpHeader::ACK) { + ProcessTcpOptions (tcpHeader); if (tcpHeader.GetAckNumber () < m_txBuffer->HeadSequence ()) { // Case 1: If the ACK is a duplicate (SEG.ACK < SND.UNA), it can be ignored. @@ -1461,6 +1491,14 @@ } else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK)) { // No action for received SYN+ACK, it is probably a duplicated packet + + // If it is a SYN packet with an MP_JOIN option + Ptr join; + if ( GetTcpOption(tcpHeader, join)) + { + ProcessSynSent (packet, tcpHeader); + return; + } } else if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) { // Received FIN or FIN+ACK, bring down this socket nicely @@ -1468,6 +1506,7 @@ } else if (tcpflags == 0) { // No flags means there is only data + ProcessTcpOptions (tcpHeader); ReceivedData (packet, tcpHeader); if (m_rxBuffer->Finished ()) { @@ -1499,6 +1538,8 @@ case TcpOption::SACKPERMITTED: case TcpOption::SACK: return m_sackEnabled; + case TcpOption::MPTCP: + return m_mptcpEnabled; default: break; } @@ -2004,18 +2045,121 @@ { return; } - // Clone the socket, simulate fork + + // If it is a SYN packet with an MP_JOIN option + Ptr join; + if ((tcpHeader.GetFlags() & TcpHeader::SYN) + && GetTcpOption(tcpHeader, join) + && join->GetMode() == TcpOptionMpTcpJoin::Syn + ) + { + return; + } + // we first forked here + if (ProcessTcpOptions(tcpHeader) == 1) + { + NS_LOG_LOGIC ("Fork & Upgrade to meta " << this); + Ptr master = this->UpgradeToMeta (); + Simulator::ScheduleNow (&MpTcpSubflow::CompleteFork, master, + packet, tcpHeader, fromAddress, toAddress); + return; + } + Ptr newSock = Fork (); NS_LOG_LOGIC ("Cloned a TcpSocketBase " << newSock); Simulator::ScheduleNow (&TcpSocketBase::CompleteFork, newSock, packet, tcpHeader, fromAddress, toAddress); } +Ptr +TcpSocketBase::UpgradeToMeta () +{ + NS_LOG_FUNCTION("Upgrading to meta " << this); + + MpTcpSubflow *subflow = new MpTcpSubflow (*this); + Ptr master (subflow, true); + + // the master is always a new socket, hence we should register it + bool result = m_tcp->AddSocket (master); + NS_ASSERT_MSG (result, "Could not register master"); + + // set callbacks + Callback, uint32_t > cbSend = this->m_sendCb; + Callback > cbRcv = this->m_receivedData; + Callback, uint32_t> cbDataSent = this->m_dataSent; + Callback > cbConnectFail = this->m_connectionFailed; + Callback > cbConnectSuccess = this->m_connectionSucceeded; + Callback, const Address &> connectionRequest = this->m_connectionRequest; + Callback, const Address&> newConnectionCreated = this->m_newConnectionCreated; + //// all callbacks are disabled + // Otherwise timers + this->CancelAllTimers (); + + // I don't want the destructor to be called in that moment + MpTcpSocketBase* meta = new (this) MpTcpSocketBase (*master); + meta->SetTcp (master->m_tcp); + meta->SetNode (master->GetNode()); + // we add it to tcp so that it can be freed and used for token lookup + meta->AddSubflow (master); + meta->SetSendCallback (cbSend); + meta->SetConnectCallback (cbConnectSuccess, cbConnectFail); + meta->SetDataSentCallback (cbDataSent); + meta->SetRecvCallback (cbRcv); + meta->SetAcceptCallback (connectionRequest, newConnectionCreated); + return master; +} + +int +TcpSocketBase::ProcessTcpOptions (const TcpHeader& header) +{ + NS_LOG_FUNCTION (this << header); + + TcpHeader::TcpOptionList options; + header.GetOptions (options); + for (TcpHeader::TcpOptionList::const_iterator it (options.begin ()); it != options.end (); ++it) + { + Ptr option = *it; + switch(option->GetKind()) + { + case TcpOption::WINSCALE: + if ((header.GetFlags () & TcpHeader::SYN) && m_winScalingEnabled && m_state < ESTABLISHED) + { + ProcessOptionWScale (option); + } + break; + case TcpOption::MPTCP: + //! this will interrupt option processing but this function will be scheduled again + //! thus some options may be processed twice, it should not trigger errors + if (ProcessOptionMpTcp(option) != 0) + { + return 1; + } + break; + case TcpOption::TS: + if (m_timestampEnabled) + { + ProcessOptionTimestamp (header.GetOption (TcpOption::TS), + header.GetSequenceNumber ()); + } + break; + // Ignore those + case TcpOption::NOP: + case TcpOption::END: + break; + default: + NS_LOG_WARN ("Unsupported option [" << (int)option->GetKind () << "]"); + break; + } + } + return 0; +} + /* Received a packet upon SYN_SENT */ void TcpSocketBase::ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader) { NS_LOG_FUNCTION (this << tcpHeader); + Ptr join; // Extract the flags. PSH and URG are disregarded. uint8_t tcpflags = tcpHeader.GetFlags () & ~(TcpHeader::PSH | TcpHeader::URG); @@ -2059,6 +2203,13 @@ else if (tcpflags & (TcpHeader::SYN | TcpHeader::ACK) && m_tcb->m_nextTxSequence + SequenceNumber32 (1) == tcpHeader.GetAckNumber ()) { // Handshake completed + if (ProcessTcpOptions (tcpHeader) == 1) + { + // upgrade to mptcp socket + Ptr master = UpgradeToMeta (); + Simulator::ScheduleNow (&MpTcpSubflow::ProcessSynSent, master, packet, tcpHeader); + return; + } NS_LOG_DEBUG ("SYN_SENT -> ESTABLISHED"); m_congestionControl->CongestionStateSet (m_tcb, TcpSocketState::CA_OPEN); m_state = ESTABLISHED; @@ -2088,6 +2239,28 @@ // Remove to get the behaviour of old NS-3 code. m_delAckCount = m_delAckMaxCount; } + else if (tcpflags == (TcpHeader::SYN | TcpHeader::ACK) + && GetTcpOption (tcpHeader, join)) + { + if (ProcessTcpOptions (tcpHeader) == 1) + { + return; + } + NS_LOG_DEBUG ("SYN_SENT -> ESTABLISHED"); + m_congestionControl->CongestionStateSet (m_tcb, TcpSocketState::CA_OPEN); + m_state = ESTABLISHED; + m_connected = true; + m_retxEvent.Cancel (); + m_rxBuffer->SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); + m_tcb->m_highTxMark = ++m_tcb->m_nextTxSequence; + m_txBuffer->SetHeadSequence (m_tcb->m_nextTxSequence); + SendEmptyPacket (TcpHeader::ACK); + SendPendingData (m_connected); + Simulator::ScheduleNow (&TcpSocketBase::ConnectionSucceeded, this); + // Always respond to first data packet to speed up the connection. + // Remove to get the behaviour of old NS-3 code. + m_delAckCount = m_delAckMaxCount; + } else { // Other in-sequence input if (!(tcpflags & TcpHeader::RST)) @@ -2137,6 +2310,7 @@ // Always respond to first data packet to speed up the connection. // Remove to get the behaviour of old NS-3 code. m_delAckCount = m_delAckMaxCount; + ProcessTcpOptions (tcpHeader); NotifyNewConnectionCreated (this, fromAddress); ReceivedAck (packet, tcpHeader); // As this connection is established, the socket is available to send data now @@ -2148,16 +2322,22 @@ else if (tcpflags == TcpHeader::SYN) { // Probably the peer lost my SYN+ACK m_rxBuffer->SetNextRxSequence (tcpHeader.GetSequenceNumber () + SequenceNumber32 (1)); - /* Check if we received an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if sender has sent an ECN SYN - * packet and the traffic is ECN Capable - */ - if (m_ecnMode == EcnMode_t::ClassicEcn && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE)) + // Check if we received an ECN SYN packet. Change the ECN state of receiver to ECN_IDLE if sender has sent an ECN SYN + // packet and the traffic is ECN Capable + + Ptr join; + //If it is a SYN packet with an MP_JOIN option then don't send SYN|ACK, because it Already sent by MpTcpSublow::completefork + if (GetTcpOption(tcpHeader, join)) + { + return; + } + else if (m_ecnMode == EcnMode_t::ClassicEcn && (tcpHeader.GetFlags () & (TcpHeader::CWR | TcpHeader::ECE)) == (TcpHeader::CWR | TcpHeader::ECE)) { NS_LOG_INFO ("Received ECN SYN packet"); SendEmptyPacket (TcpHeader::SYN | TcpHeader::ACK |TcpHeader::ECE); NS_LOG_DEBUG (TcpSocketState::EcnStateName[m_tcb->m_ecnState] << " -> ECN_IDLE"); m_tcb->m_ecnState = TcpSocketState::ECN_IDLE; - } + } else { m_tcb->m_ecnState = TcpSocketState::ECN_DISABLED; @@ -2293,6 +2473,7 @@ { if (tcpHeader.GetSequenceNumber () == m_rxBuffer->NextRxSequence ()) { // This ACK corresponds to the FIN sent + ProcessTcpOptions(tcpHeader); TimeWait (); } } @@ -2301,6 +2482,7 @@ // anyone. If anything other than ACK is received, respond with a reset. if (tcpflags == TcpHeader::FIN || tcpflags == (TcpHeader::FIN | TcpHeader::ACK)) { // FIN from the peer as well. We can close immediately. + ProcessTcpOptions (tcpHeader); SendEmptyPacket (TcpHeader::ACK); } else if (tcpflags != TcpHeader::RST) @@ -2323,21 +2505,25 @@ if (tcpflags == 0) { + ProcessTcpOptions (tcpHeader); ReceivedData (packet, tcpHeader); } else if (tcpflags == TcpHeader::ACK) { if (tcpHeader.GetSequenceNumber () == m_rxBuffer->NextRxSequence ()) { // This ACK corresponds to the FIN sent. This socket closed peacefully. + ProcessTcpOptions (tcpHeader); CloseAndNotify (); } } else if (tcpflags == TcpHeader::FIN) { // Received FIN again, the peer probably lost the FIN+ACK + ProcessTcpOptions (tcpHeader); SendEmptyPacket (TcpHeader::FIN | TcpHeader::ACK); } else if (tcpflags == (TcpHeader::FIN | TcpHeader::ACK) || tcpflags == TcpHeader::RST) { + ProcessTcpOptions (tcpHeader); CloseAndNotify (); } else @@ -2496,7 +2682,10 @@ header.SetSourcePort (m_endPoint6->GetLocalPort ()); header.SetDestinationPort (m_endPoint6->GetPeerPort ()); } - AddOptions (header); + if (!m_mptcpEnabled) + { + AddOptions (header); + } // RFC 6298, clause 2.4 m_rto = Max (m_rtt->GetEstimate () + Max (m_clockGranularity, m_rtt->GetVariation () * 4), m_minRto); @@ -2561,7 +2750,11 @@ m_txTrace (p, header, this); - if (m_endPoint != nullptr) + if (m_mptcpEnabled) + { + SendPacket (header, p); + } + else if (m_endPoint != nullptr) { m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_endPoint->GetPeerAddress (), m_boundnetdevice); @@ -2917,7 +3110,10 @@ header.SetDestinationPort (m_endPoint6->GetPeerPort ()); } header.SetWindowSize (AdvertisedWindowSize ()); - AddOptions (header); + if (!m_mptcpEnabled) + { + AddOptions (header); + } if (m_retxEvent.IsExpired ()) { @@ -2931,7 +3127,11 @@ m_txTrace (p, header, this); - if (m_endPoint) + if (m_mptcpEnabled) + { + SendPacket (header, p); + } + else if (m_endPoint) { m_tcp->SendPacket (p, header, m_endPoint->GetLocalAddress (), m_endPoint->GetPeerAddress (), m_boundnetdevice); @@ -2968,6 +3168,30 @@ } void +TcpSocketBase::SendPacket (TcpHeader header, Ptr p) +{ + NS_LOG_LOGIC ("Send packet via TcpL4Protocol with flags"); + + AddOptions (header); + m_txTrace (p, header, this); + if (m_endPoint != nullptr) + { + 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); + } + if (header.GetFlags () & TcpHeader::ACK) + { // If sending an ACK, cancel the delay ACK as well + m_delAckEvent.Cancel (); + m_delAckCount = 0; + } +} + +void TcpSocketBase::UpdateRttHistory (const SequenceNumber32 &seq, uint32_t sz, bool isRetransmission) { @@ -3863,10 +4087,51 @@ return false; } +uint64_t +TcpSocketBase::GenerateUniqueMpTcpKey () +{ + NS_LOG_FUNCTION ("Generating key"); + NS_ASSERT (m_tcp); + uint64_t localKey, idsn; + uint32_t localToken; + + do + { + localKey = (rand () % 1000 + 1); + GenerateTokenForKey ( HMAC_SHA1, localKey, localToken, idsn ); + } + while (m_tcp->LookupMpTcpToken (localToken)); + + m_mptcpLocalToken = localToken; + m_mptcpLocalKey = localKey; + m_mptcpPeerToken = localToken; + return localKey; +} + +void +TcpSocketBase::AddMpTcpOptions (TcpHeader& header) +{ + NS_LOG_FUNCTION (this); + // If key not genereated yet + if (m_mptcpLocalKey == 0) + { + // for the sake of simplicity, we generate a key even if unused + GenerateUniqueMpTcpKey (); + } + if ((header.GetFlags () == TcpHeader::SYN)) + { + // Append the MPTCP capable option + Ptr mpc = CreateObject (); + mpc->SetSenderKey (m_mptcpLocalKey); + header.AppendOption (mpc); + } +} + void TcpSocketBase::AddOptions (TcpHeader& header) { NS_LOG_FUNCTION (this << header); + AddMpTcpOptions (header); if (m_timestampEnabled) { @@ -3874,6 +4139,19 @@ } } +int +TcpSocketBase::ProcessOptionMpTcp (const Ptr option) +{ + Ptr mpc = DynamicCast (option); + + if (!mpc) + { + NS_LOG_WARN ("Invalid option " << option); + return 0; + } + return 1; +} + void TcpSocketBase::ProcessOptionWScale (const Ptr option) { @@ -4043,7 +4321,8 @@ option->GetTimestamp () << " echo=" << m_timestampToEcho); } -void TcpSocketBase::UpdateWindowSize (const TcpHeader &header) +bool + TcpSocketBase::UpdateWindowSize (const TcpHeader &header) { NS_LOG_FUNCTION (this << header); // If the connection is not established, the window size is always @@ -4055,7 +4334,7 @@ { m_rWnd = receivedWindow; NS_LOG_LOGIC ("State less than ESTABLISHED; updating rWnd to " << m_rWnd); - return; + return true; } // Test for conditions that allow updating of the window @@ -4085,6 +4364,7 @@ m_rWnd = receivedWindow; NS_LOG_LOGIC ("updating rWnd to " << m_rWnd); } + return update; } void Index: src/internet/model/tcp-socket-base.h =================================================================== --- a/src/internet/model/tcp-socket-base.h +++ b/src/internet/model/tcp-socket-base.h @@ -32,6 +32,8 @@ #include "ns3/data-rate.h" #include "ns3/node.h" #include "ns3/tcp-socket-state.h" +#include "ns3/ipv4-end-point.h" +#include "ns3/tcp-tx-buffer.h" namespace ns3 { @@ -41,6 +43,9 @@ class Packet; class TcpL4Protocol; class TcpHeader; +class MpTcpSubflow; +class MpTcpSocketBase; +class TcpOptionMpTcpMain; class TcpCongestionOps; class TcpRecoveryOps; class RttEstimator; @@ -267,6 +272,7 @@ * \param rtt the RTT estimator */ virtual void SetRtt (Ptr rtt); + virtual TcpSocket::TcpStates_t GetState() const; /** * \brief Sets the Minimum RTO. @@ -557,7 +563,7 @@ virtual int GetSockName (Address &address) const; // Return local addr:port in address virtual int GetPeerName (Address &address) const; virtual void BindToNetDevice (Ptr netdevice); // NetDevice with my m_endPoint - + virtual uint32_t GetSegSize (void) const; /** * TracedCallback signature for tcp packet transmission or reception events. * @@ -577,7 +583,6 @@ virtual void SetRcvBufSize (uint32_t size); virtual uint32_t GetRcvBufSize (void) const; virtual void SetSegSize (uint32_t size); - virtual uint32_t GetSegSize (void) const; virtual void SetInitialSSThresh (uint32_t threshold); virtual uint32_t GetInitialSSThresh (void) const; virtual void SetInitialCwnd (uint32_t cwnd); @@ -608,19 +613,19 @@ * * \returns 0 on success, -1 on failure */ - int SetupCallback (void); + virtual int SetupCallback (void); /** * \brief Perform the real connection tasks: Send SYN if allowed, RST if invalid * * \returns 0 on success */ - int DoConnect (void); + virtual int DoConnect (void); /** * \brief Schedule-friendly wrapper for Socket::NotifyConnectionSucceeded() */ - void ConnectionSucceeded (void); + virtual void ConnectionSucceeded (void); /** * \brief Configure the endpoint to a local address. Called by Connect() if Bind() didn't specify one. @@ -673,7 +678,7 @@ * \param port the remote port * \param incomingInterface the incoming interface */ - void ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface); + virtual void ForwardUp (Ptr packet, Ipv4Header header, uint16_t port, Ptr incomingInterface); /** * \brief Called by the L3 protocol when it received a packet to pass on to TCP. @@ -730,7 +735,7 @@ * \param withAck forces an ACK to be sent * \returns the number of packets sent */ - uint32_t SendPendingData (bool withAck = false); + virtual uint32_t SendPendingData (bool withAck = false); /** * \brief Extract at most maxSize bytes from the TxBuffer at sequence seq, add the @@ -749,11 +754,17 @@ * \param flags the packet's flags */ virtual void SendEmptyPacket (uint8_t flags); + /** + * \brief + * \param header A valid TCP header + * \param p Packet to send. May be empty. + */ + virtual void SendPacket(TcpHeader header, Ptr p); /** * \brief Send reset and tear down this socket */ - void SendRST (void); + virtual void SendRST (void); /** * \brief Check if a sequence number range is within the rx window @@ -762,7 +773,7 @@ * \param tail end of the Sequence window * \returns true if it is in range */ - bool OutOfRange (SequenceNumber32 head, SequenceNumber32 tail) const; + virtual bool OutOfRange (SequenceNumber32 head, SequenceNumber32 tail) const; // Helper functions: Connection close @@ -772,12 +783,12 @@ * * \returns 0 on success */ - int DoClose (void); + virtual int DoClose (void); /** * \brief Peacefully close the socket by notifying the upper layer and deallocate end point */ - void CloseAndNotify (void); + virtual void CloseAndNotify (void); /** * \brief Kill this socket by zeroing its attributes (IPv4) @@ -785,7 +796,7 @@ * This is a callback function configured to m_endpoint in * SetupCallback(), invoked when the endpoint is destroyed. */ - void Destroy (void); + virtual void Destroy (void); /** * \brief Kill this socket by zeroing its attributes (IPv6) @@ -793,12 +804,12 @@ * This is a callback function configured to m_endpoint in * SetupCallback(), invoked when the endpoint is destroyed. */ - void Destroy6 (void); + virtual void Destroy6 (void); /** * \brief Deallocate m_endPoint and m_endPoint6 */ - void DeallocateEndPoint (void); + virtual void DeallocateEndPoint (void); /** * \brief Received a FIN from peer, notify rx buffer @@ -806,22 +817,22 @@ * \param p the packet * \param tcpHeader the packet's TCP header */ - void PeerClose (Ptr p, const TcpHeader& tcpHeader); + virtual void PeerClose (Ptr p, const TcpHeader& tcpHeader); /** * \brief FIN is in sequence, notify app and respond with a FIN */ - void DoPeerClose (void); + virtual void DoPeerClose (void); /** * \brief Cancel all timer when endpoint is deleted */ - void CancelAllTimers (void); + virtual void CancelAllTimers (void); /** * \brief Move from CLOSING or FIN_WAIT_2 to TIME_WAIT state */ - void TimeWait (void); + virtual void TimeWait (void); // State transition functions @@ -833,7 +844,7 @@ * \param packet the packet * \param tcpHeader the packet's TCP header */ - void ProcessEstablished (Ptr packet, const TcpHeader& tcpHeader); // Received a packet upon ESTABLISHED state + virtual void ProcessEstablished (Ptr packet, const TcpHeader& tcpHeader); // Received a packet upon ESTABLISHED state /** * \brief Received a packet upon LISTEN state. @@ -843,7 +854,7 @@ * \param fromAddress the source address * \param toAddress the destination address */ - void ProcessListen (Ptr packet, const TcpHeader& tcpHeader, + virtual void ProcessListen (Ptr packet, const TcpHeader& tcpHeader, const Address& fromAddress, const Address& toAddress); /** @@ -852,7 +863,7 @@ * \param packet the packet * \param tcpHeader the packet's TCP header */ - void ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader); + virtual void ProcessSynSent (Ptr packet, const TcpHeader& tcpHeader); /** * \brief Received a packet upon SYN_RCVD. @@ -862,7 +873,7 @@ * \param fromAddress the source address * \param toAddress the destination address */ - void ProcessSynRcvd (Ptr packet, const TcpHeader& tcpHeader, + virtual void ProcessSynRcvd (Ptr packet, const TcpHeader& tcpHeader, const Address& fromAddress, const Address& toAddress); /** @@ -871,7 +882,7 @@ * \param packet the packet * \param tcpHeader the packet's TCP header */ - void ProcessWait (Ptr packet, const TcpHeader& tcpHeader); + virtual void ProcessWait (Ptr packet, const TcpHeader& tcpHeader); /** * \brief Received a packet upon CLOSING @@ -879,7 +890,7 @@ * \param packet the packet * \param tcpHeader the packet's TCP header */ - void ProcessClosing (Ptr packet, const TcpHeader& tcpHeader); + virtual void ProcessClosing (Ptr packet, const TcpHeader& tcpHeader); /** * \brief Received a packet upon LAST_ACK @@ -887,7 +898,7 @@ * \param packet the packet * \param tcpHeader the packet's TCP header */ - void ProcessLastAck (Ptr packet, const TcpHeader& tcpHeader); + virtual void ProcessLastAck (Ptr packet, const TcpHeader& tcpHeader); // Window management @@ -941,7 +952,7 @@ * * \param header TcpHeader from which to extract the new window value */ - void UpdateWindowSize (const TcpHeader& header); + virtual bool UpdateWindowSize (const TcpHeader& header); // Manage data tx/rx @@ -1034,7 +1045,7 @@ * \brief Retransmit the first segment marked as lost, without considering * available window nor pacing. */ - void DoRetransmit (void); + virtual void DoRetransmit (void); /** \brief Add options to TcpHeader * @@ -1043,7 +1054,45 @@ * * \param tcpHeader TcpHeader to add options to */ - void AddOptions (TcpHeader& tcpHeader); + virtual void AddOptions (TcpHeader& tcpHeader); + + /** + * This function first generates a copy of the current socket as an MpTcpSubflow. + * Then it upgrades the current socket to an MpTcpSocketBase via the use of + * "placement new", i.e. it does not allocate new memory but reuse the memory at "this" + * address to instantiate MpTcpSocketBase. + * Finally the master socket is associated to the meta. + * + * It is critical that enough memory was allocated beforehand to contain MpTcpSocketBase + * (see how it's done for now in TcpL4Protocol). + * Ideally MpTcoSocketBase would take less memory than TcpSocketBase, so one of the goal should be to let + * MpTcpSocketBase inherit directly from TcpSocket rather than TcpSocketBase. + * + * The function does not register the new subflow in m_tcp->AddSocket, this should be taken care + * of afterwards. + * + * \param master + * \return master subflow. It is not associated to the meta at this point + */ + virtual Ptr UpgradeToMeta (); + + /** + * \return if it returns 1, we need to upgrade the meta socket + * if negative then it should discard the packet ? + */ + virtual int ProcessTcpOptions (const TcpHeader& header); + + /** + * \brief In this baseclass, this only deals with MpTcpCapable options in order to know if the socket + * should be converted to an MPTCP meta socket. + */ + virtual int ProcessOptionMpTcp (const Ptr option); + + /** + * \brief Generate a unique key for this host + * \see mptcp_set_key_sk + */ + virtual uint64_t GenerateUniqueMpTcpKey () ; /** * \brief Read TCP options before Ack processing @@ -1054,7 +1103,7 @@ * \param scoreboardUpdated indicates if the scoreboard was updated due to a * SACK option */ - void ReadOptions (const TcpHeader &tcpHeader, bool &scoreboardUpdated); + virtual void ReadOptions (const TcpHeader &tcpHeader, bool &scoreboardUpdated); /** * \brief Return true if the specified option is enabled @@ -1062,7 +1111,7 @@ * \param kind kind of TCP option * \return true if the option is enabled */ - bool IsTcpOptionEnabled (uint8_t kind) const; + virtual bool IsTcpOptionEnabled (uint8_t kind) const; /** * \brief Read and parse the Window scale option @@ -1072,7 +1121,7 @@ * * \param option Window scale option read from the header */ - void ProcessOptionWScale (const Ptr option); + virtual void ProcessOptionWScale (const Ptr option); /** * \brief Add the window scale option to the header * @@ -1081,7 +1130,13 @@ * * \param header TcpHeader where the method should add the window scale option */ - void AddOptionWScale (TcpHeader& header); + virtual void AddOptionWScale (TcpHeader& header); + + /** + * \brief Add the mptcp option to the header + * \param header TcpHeader where the method should add mptcp option + */ + virtual void AddMpTcpOptions (TcpHeader& header); /** * \brief Calculate window scale value based on receive buffer space @@ -1169,6 +1224,12 @@ void AddSocketTags (const Ptr &p) const; protected: + //mptcp classes declared as friends + friend class MpTcpSocketBase; + friend class MpTcpSubflow; + friend class MpTcpSchedulerRoundRobin; + friend class MpTcpSchedulerFastestRTT; + // Counters and events EventId m_retxEvent {}; //!< Retransmission event EventId m_lastAckEvent {}; //!< Last ACK timeout event @@ -1234,6 +1295,12 @@ TracedValue m_highRxMark {0}; //!< Highest seqno received TracedValue m_highRxAckMark {0}; //!< Highest ack received + // MPTCP variables + bool m_mptcpEnabled {true}; //!< Window Scale option enabled + uint64_t m_mptcpLocalKey {0}; //!< MPTCP key + uint32_t m_mptcpLocalToken {0}; //!< Hash of the key + uint32_t m_mptcpPeerToken {0}; //!< Hash of the key + // Options bool m_sackEnabled {true}; //!< RFC SACK option enabled bool m_winScalingEnabled {true}; //!< Window Scale option enabled (RFC 7323) Index: src/internet/wscript =================================================================== --- a/src/internet/wscript +++ b/src/internet/wscript @@ -166,6 +166,15 @@ 'model/tcp-option-rfc793.cc', 'model/tcp-option-winscale.cc', 'model/tcp-option-ts.cc', + 'model/tcp-option-mptcp.cc', + 'model/mptcp-crypto.cc', + 'model/mptcp-socket-base.cc', + 'model/mptcp-subflow.cc', + 'model/mptcp-scheduler-round-robin.cc', + 'model/mptcp-scheduler-fastest-rtt.cc', + 'model/mptcp-mapping.cc', + 'model/mptcp-ndiffports.cc', + 'model/mptcp-fullmesh.cc', 'model/tcp-option-sack-permitted.cc', 'model/tcp-option-sack.cc', 'model/ipv4-packet-info-tag.cc', @@ -304,6 +313,7 @@ 'model/udp-header.h', 'model/tcp-header.h', 'model/tcp-option.h', + 'model/tcp-option-mptcp.h', 'model/tcp-option-winscale.h', 'model/tcp-option-ts.h', 'model/tcp-option-sack-permitted.h', @@ -313,6 +323,7 @@ 'model/icmpv6-header.h', # used by routing 'model/ipv4-interface.h', + 'model/ipv4-end-point.h', 'model/ipv4-l3-protocol.h', 'model/ipv6-l3-protocol.h', 'model/ipv6-extension.h', @@ -398,6 +409,15 @@ 'model/tcp-socket-state.h', 'model/tcp-tx-buffer.h', 'model/tcp-rx-buffer.h', + 'model/mptcp-crypto.h', + 'model/mptcp-mapping.h', + 'model/mptcp-subflow.h', + 'model/mptcp-socket-base.h', + 'model/mptcp-scheduler.h', + 'model/mptcp-scheduler-round-robin.h', + 'model/mptcp-scheduler-fastest-rtt.h', + 'model/mptcp-ndiffports.h', + 'model/mptcp-fullmesh.h', 'model/tcp-recovery-ops.h', 'model/tcp-prr-recovery.h', 'model/rtt-estimator.h', Index: src/network/model/socket.h =================================================================== --- a/src/network/model/socket.h +++ b/src/network/model/socket.h @@ -985,6 +985,16 @@ virtual void Ipv6LeaveGroup (void); protected: + Callback > m_connectionSucceeded; //!< connection succeeded callback + Callback > m_connectionFailed; //!< connection failed callback + Callback > m_normalClose; //!< connection closed callback + Callback > m_errorClose; //!< connection closed due to errors callback + Callback, const Address &> m_connectionRequest; //!< connection request callback + Callback, const Address&> m_newConnectionCreated; //!< connection created callback + Callback, uint32_t> m_dataSent; //!< data sent callback + Callback, uint32_t > m_sendCb; //!< packet sent callback + Callback > m_receivedData; //!< data received callback + /** * \brief Notify through the callback (if set) that the connection has been * established. @@ -1078,15 +1088,6 @@ Ipv6Address m_ipv6MulticastGroupAddress; //!< IPv6 multicast group address. private: - Callback > m_connectionSucceeded; //!< connection succeeded callback - Callback > m_connectionFailed; //!< connection failed callback - Callback > m_normalClose; //!< connection closed callback - Callback > m_errorClose; //!< connection closed due to errors callback - Callback, const Address &> m_connectionRequest; //!< connection request callback - Callback, const Address&> m_newConnectionCreated; //!< connection created callback - Callback, uint32_t> m_dataSent; //!< data sent callback - Callback, uint32_t > m_sendCb; //!< packet sent callback - Callback > m_receivedData; //!< data received callback uint8_t m_priority; //!< the socket priority Index: src/network/utils/sequence-number.h =================================================================== --- a/src/network/utils/sequence-number.h +++ b/src/network/utils/sequence-number.h @@ -468,6 +468,11 @@ /** * \ingroup network + * 64 bit Sequence number. + */ +typedef SequenceNumber SequenceNumber64; // For MPTCP DSN +/** + * \ingroup network * 32 bit Sequence number. */ typedef SequenceNumber SequenceNumber32;