diff --git a/src/internet/model/tcp-rx-buffer.cc b/src/internet/model/tcp-rx-buffer.cc index dcc5ad95e..a9b007a56 100644 --- a/src/internet/model/tcp-rx-buffer.cc +++ b/src/internet/model/tcp-rx-buffer.cc @@ -196,6 +196,13 @@ TcpRxBuffer::Add (Ptr p, TcpHeader const& tcph) // Insert packet into buffer NS_ASSERT (m_data.find (headSeq) == m_data.end ()); // Shouldn't be there yet m_data [ headSeq ] = p; + + if (headSeq > m_nextRxSeq) + { + // Generate a new SACK block + UpdateSackList (headSeq, tailSeq); + } + NS_LOG_LOGIC ("Buffered packet of seqno=" << headSeq << " len=" << p->GetSize ()); // Update variables m_size += p->GetSize (); // Occupancy @@ -211,6 +218,7 @@ TcpRxBuffer::Add (Ptr p, TcpHeader const& tcph) }; m_nextRxSeq = i->first + SequenceNumber32 (i->second->GetSize ()); m_availBytes += i->second->GetSize (); + ClearSackList (m_nextRxSeq); } NS_LOG_LOGIC ("Updated buffer occupancy=" << m_size << " nextRxSeq=" << m_nextRxSeq); if (m_gotFin && m_nextRxSeq == m_finSeq) @@ -220,6 +228,135 @@ TcpRxBuffer::Add (Ptr p, TcpHeader const& tcph) return true; } +uint32_t +TcpRxBuffer::GetSackListSize () const +{ + NS_LOG_FUNCTION (this); + + return m_sackList.size (); +} + +void +TcpRxBuffer::UpdateSackList (const SequenceNumber32 &head, const SequenceNumber32 &tail) +{ + NS_LOG_FUNCTION (this << head << tail); + NS_ASSERT (head > m_nextRxSeq); + + TcpOptionSack::SackBlock current; + current.first = head; + current.second = tail; + + // The block "current" has been safely stored. Now we need to build the SACK + // list, to be advertised. From RFC 2018: + // (a) The first SACK block (i.e., the one immediately following the + // kind and length fields in the option) MUST specify the contiguous + // block of data containing the segment which triggered this ACK, + // unless that segment advanced the Acknowledgment Number field in + // the header. This assures that the ACK with the SACK option + // reflects the most recent change in the data receiver's buffer + // queue. + // + // (b) The data receiver SHOULD include as many distinct SACK blocks as + // possible in the SACK option. Note that the maximum available + // option space may not be sufficient to report all blocks present in + // the receiver's queue. + // + // (c) The SACK option SHOULD be filled out by repeating the most + // recently reported SACK blocks (based on first SACK blocks in + // previous SACK options) that are not subsets of a SACK block + // already included in the SACK option being constructed. This + // assures that in normal operation, any segment remaining part of a + // non-contiguous block of data held by the data receiver is reported + // in at least three successive SACK options, even for large-window + // TCP implementations [RFC1323]). After the first SACK block, the + // following SACK blocks in the SACK option may be listed in + // arbitrary order. + + m_sackList.push_front (current); + + // We have inserted the block at the beginning of the list. Now, we should + // check if any existing blocks overlap with that. + bool updated = false; + TcpOptionSack::SackList::iterator it = m_sackList.begin (); + TcpOptionSack::SackBlock begin = *it; + TcpOptionSack::SackBlock merged; + ++it; + + // Iterates until we examined all blocks in the list (maximum 4) + while (it != m_sackList.end ()) + { + TcpOptionSack::SackBlock current = *it; + + // This is a left merge: + // [current_first; current_second] [beg_first; beg_second] + if (begin.first == current.second) + { + NS_ASSERT (current.first < begin.second); + merged = TcpOptionSack::SackBlock (current.first, begin.second); + updated = true; + } + // while this is a right merge + // [begin_first; begin_second] [current_first; current_second] + else if (begin.second == current.first) + { + NS_ASSERT (begin.first < current.second); + merged = TcpOptionSack::SackBlock (begin.first, current.second); + updated = true; + } + + // If we have merged the blocks (and the result is in merged) we should + // delete the current block (it), the first block, and insert the merged + // one at the beginning. + if (updated) + { + m_sackList.erase (it); + m_sackList.pop_front (); + m_sackList.push_front (merged); + it = m_sackList.begin (); + begin = *it; + updated = false; + } + + ++it; + } + + // Since the maximum blocks that fits into a TCP header are 4, there's no + // point on maintaining the others. + if (m_sackList.size () > 4) + { + m_sackList.pop_back (); + } + + // Please note that, if a block b is discarded and then a block contiguos + // to b is received, only that new block (without the b part) is reported. + // This is perfectly fine for the RFC point (a), given that we do not report any + // overlapping blocks shortly after. +} + +void +TcpRxBuffer::ClearSackList (const SequenceNumber32 &seq) +{ + NS_LOG_FUNCTION (this << seq); + + TcpOptionSack::SackList::iterator it; + for (it = m_sackList.begin (); it != m_sackList.end (); ++it) + { + TcpOptionSack::SackBlock block = *it; + NS_ASSERT (block.first < block.second); + + if (block.second <= seq) + { + it = m_sackList.erase (it); + } + } +} + +TcpOptionSack::SackList +TcpRxBuffer::GetSackList () const +{ + return m_sackList; +} + Ptr TcpRxBuffer::Extract (uint32_t maxSize) { diff --git a/src/internet/model/tcp-rx-buffer.h b/src/internet/model/tcp-rx-buffer.h index c54fcdf24..56654ac68 100644 --- a/src/internet/model/tcp-rx-buffer.h +++ b/src/internet/model/tcp-rx-buffer.h @@ -27,6 +27,7 @@ #include "ns3/sequence-number.h" #include "ns3/ptr.h" #include "ns3/tcp-header.h" +#include "ns3/tcp-option-sack.h" namespace ns3 { class Packet; @@ -34,8 +35,40 @@ class Packet; /** * \ingroup tcp * - * \brief class for the reordering buffer that keeps the data from lower layer, i.e. - * TcpL4Protocol, sent to the application + * \brief Rx reordering buffer for TCP + * + * The class is responsible to safely store the segments, and then + * returning them in-order to the application, where "in-order" does not means + * "network-order", but "sender-order" : the bytes should be returned in the + * same order that the sender application used to push them down on wire. + * + * The first useful sequence that this class is waiting is returned by the method + * NextRxSequence, and could be set at the beginning through MaxRxSequence. + * + * The max. size of this buffer is managed through SetMaxBufferSize, and could be + * retrieved using MaxBufferSize. The current size instead is returned by + * Size, while the amount of in-order data that could be extracted is returned + * by the method Available. + * + * To store data, use Add; for retrieving a certain amount of ordered data, use + * the method Extract. + * + * SACK list + * --------- + * + * An interesting feature of this class is the ability to mantain an ordered + * SACK list, under the definition of RFC 2018. When a out-of-order segment + * reaches this buffer, an ACK will be sent out, and the SACK list is + * generated or updated. From RFC 2018: + * + * > If sent at all, SACK options SHOULD be included in all ACKs which do + * > not ACK the highest sequence number in the data receiver's queue. + * + * For more information about the SACK list, please check the documentation of + * the method GetSackList. + * + * \see GetSackList + * \see UpdateSackList */ class TcpRxBuffer : public Object { @@ -125,7 +158,59 @@ public: */ Ptr Extract (uint32_t maxSize); + /** + * \brief Get the sack list + * + * The sack list can be empty, and it is updated each time Add or Extract + * are called through the private method UpdateSackList. + * + * \return a list of isolated blocks + */ + TcpOptionSack::SackList GetSackList () const; + + /** + * \brief Get the size of Sack list + * + * \return the size of the sack block list; can be empty + */ + uint32_t GetSackListSize () const; + private: + /** + * \brief Update the sack list, with the block seq starting at the beginning + * + * Note: the maximum size of the block list is 4. Caller is free to + * drop blocks at the end to accomodate header size; from RFC 2018: + * + * > The data receiver SHOULD include as many distinct SACK blocks as + * > possible in the SACK option. Note that the maximum available + * > option space may not be sufficient to report all blocks present in + * > the receiver's queue. + * + * In fact, the maximum amount of blocks is 4, and if we consider the timestamp + * (or other) options, it is even less. For more detail about this function, + * please see the source code and in-line comments. + * + * \param head sequence number of the block at the beginning + * \param tail sequence number of the block at the end + */ + void UpdateSackList (const SequenceNumber32 &head, const SequenceNumber32 &tail); + + /** + * \brief Remove old blocks from the sack list + * + * Used to remove blocks already delivered to the application. + * + * After this call, in the SACK list there will be only blocks with + * sequence numbers greater than seq; it is perfectly safe to call this + * function with an empty sack list. + * + * \param seq Last sequence to remove + */ + void ClearSackList (const SequenceNumber32 &seq); + + TcpOptionSack::SackList m_sackList; //!< Sack list (updated constantly) + /// container for data stored in the buffer typedef std::map >::iterator BufIterator; TracedValue m_nextRxSeq; //!< Seqnum of the first missing byte in data (RCV.NXT)