tcp: Generate Sack blocks in TcpRxBuffer

This commit is contained in:
Natale Patriciello
2017-02-03 14:02:32 +01:00
parent e8df39f824
commit cc400e8eb1
2 changed files with 224 additions and 2 deletions

View File

@@ -196,6 +196,13 @@ TcpRxBuffer::Add (Ptr<Packet> 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<Packet> 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<Packet> 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<Packet>
TcpRxBuffer::Extract (uint32_t maxSize)
{

View File

@@ -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<Packet> 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<SequenceNumber32, Ptr<Packet> >::iterator BufIterator;
TracedValue<SequenceNumber32> m_nextRxSeq; //!< Seqnum of the first missing byte in data (RCV.NXT)