251 lines
7.9 KiB
C++
251 lines
7.9 KiB
C++
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
|
/*
|
|
* 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: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
#include "ns3/packet.h"
|
|
#include "ns3/fatal-error.h"
|
|
#include "ns3/log.h"
|
|
#include "tcp-tx-buffer.h"
|
|
|
|
NS_LOG_COMPONENT_DEFINE ("TcpTxBuffer");
|
|
|
|
namespace ns3 {
|
|
|
|
TypeId
|
|
TcpTxBuffer::GetTypeId (void)
|
|
{
|
|
static TypeId tid = TypeId ("ns3::TcpTxBuffer")
|
|
.SetParent<Object> ()
|
|
.AddConstructor<TcpTxBuffer> ()
|
|
.AddTraceSource ("UnackSequence",
|
|
"First unacknowledged sequence number (SND.UNA)",
|
|
MakeTraceSourceAccessor (&TcpTxBuffer::m_firstByteSeq))
|
|
;
|
|
return tid;
|
|
}
|
|
|
|
/* A user is supposed to create a TcpSocket through a factory. In TcpSocket,
|
|
* there are attributes SndBufSize and RcvBufSize to control the default Tx and
|
|
* Rx window sizes respectively, with default of 128 KiByte. The attribute
|
|
* SndBufSize is passed to TcpTxBuffer by TcpSocketBase::SetSndBufSize() and in
|
|
* turn, TcpTxBuffer:SetMaxBufferSize(). Therefore, the m_maxBuffer value
|
|
* initialized below is insignificant.
|
|
*/
|
|
TcpTxBuffer::TcpTxBuffer (uint32_t n)
|
|
: m_firstByteSeq(n), m_size (0), m_maxBuffer(32768), m_data (0)
|
|
{
|
|
}
|
|
|
|
TcpTxBuffer::~TcpTxBuffer (void)
|
|
{
|
|
}
|
|
|
|
SequenceNumber32
|
|
TcpTxBuffer::HeadSequence (void) const
|
|
{
|
|
return m_firstByteSeq;
|
|
}
|
|
|
|
SequenceNumber32
|
|
TcpTxBuffer::TailSequence (void) const
|
|
{
|
|
return m_firstByteSeq + SequenceNumber32 (m_size);
|
|
}
|
|
|
|
uint32_t
|
|
TcpTxBuffer::Size (void) const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
uint32_t
|
|
TcpTxBuffer::MaxBufferSize (void) const
|
|
{
|
|
return m_maxBuffer;
|
|
}
|
|
|
|
void
|
|
TcpTxBuffer::SetMaxBufferSize (uint32_t n)
|
|
{
|
|
m_maxBuffer = n;
|
|
}
|
|
|
|
uint32_t
|
|
TcpTxBuffer::Available (void) const
|
|
{
|
|
return m_maxBuffer - m_size;
|
|
}
|
|
|
|
bool
|
|
TcpTxBuffer::Add (Ptr<Packet> p)
|
|
{
|
|
NS_LOG_FUNCTION (this << p);
|
|
NS_LOG_LOGIC ("Packet of size " << p->GetSize () << " appending to window starting at "
|
|
<< m_firstByteSeq << ", availSize="<< Available ());
|
|
if (p->GetSize () <= Available ())
|
|
{
|
|
if (p->GetSize () > 0)
|
|
{
|
|
m_data.push_back (p);
|
|
m_size += p->GetSize ();
|
|
NS_LOG_LOGIC ("Updated size=" << m_size << ", lastSeq=" << m_firstByteSeq + SequenceNumber32 (m_size));
|
|
}
|
|
return true;
|
|
}
|
|
NS_LOG_LOGIC ("Rejected. Not enough room to buffer packet.");
|
|
return false;
|
|
}
|
|
|
|
uint32_t
|
|
TcpTxBuffer::SizeFromSequence (const SequenceNumber32& seq) const
|
|
{
|
|
NS_LOG_FUNCTION (this << seq);
|
|
// Sequence of last byte in buffer
|
|
SequenceNumber32 lastSeq = m_firstByteSeq + SequenceNumber32 (m_size);
|
|
// Non-negative size
|
|
NS_LOG_LOGIC ("HeadSeq=" << m_firstByteSeq << ", lastSeq=" << lastSeq << ", size=" << m_size <<
|
|
", returns " << lastSeq - seq);
|
|
return lastSeq - seq;
|
|
}
|
|
|
|
Ptr<Packet>
|
|
TcpTxBuffer::CopyFromSequence (uint32_t numBytes, const SequenceNumber32& seq)
|
|
{
|
|
NS_LOG_FUNCTION (this << numBytes << seq);
|
|
uint32_t s = std::min (numBytes, SizeFromSequence (seq)); // Real size to extract. Insure not beyond end of data
|
|
if (s == 0)
|
|
{
|
|
return Create<Packet> (); // Empty packet returned
|
|
}
|
|
if (m_data.size () == 0)
|
|
{ // No actual data, just return dummy-data packet of correct size
|
|
return Create<Packet> (s);
|
|
}
|
|
|
|
// Extract data from the buffer and return
|
|
uint32_t offset = seq - m_firstByteSeq.Get ();
|
|
uint32_t count = 0; // Offset of the first byte of a packet in the buffer
|
|
uint32_t pktSize = 0;
|
|
bool beginFound = false;
|
|
int pktCount = 0;
|
|
Ptr<Packet> outPacket;
|
|
NS_LOG_LOGIC ("There are " << m_data.size () << " number of packets in buffer");
|
|
for (BufIterator i = m_data.begin (); i != m_data.end (); ++i)
|
|
{
|
|
pktCount++;
|
|
pktSize = (*i)->GetSize ();
|
|
if (!beginFound)
|
|
{ // Look for first fragment
|
|
if (count + pktSize > offset)
|
|
{
|
|
NS_LOG_LOGIC ("First byte found in packet #" << pktCount << " at buffer offset " << count
|
|
<< ", packet len=" << pktSize);
|
|
beginFound = true;
|
|
uint32_t packetOffset = offset - count;
|
|
uint32_t fragmentLength = count + pktSize - offset;
|
|
if (fragmentLength >= s)
|
|
{ // Data to be copied falls entirely in this packet
|
|
return (*i)->CreateFragment (packetOffset, s);
|
|
}
|
|
else
|
|
{ // This packet only fulfills part of the request
|
|
outPacket = (*i)->CreateFragment (packetOffset, fragmentLength);
|
|
}
|
|
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
|
}
|
|
}
|
|
else if (count + pktSize >= offset + s)
|
|
{ // Last packet fragment found
|
|
NS_LOG_LOGIC ("Last byte found in packet #" << pktCount << " at buffer offset " << count
|
|
<< ", packet len=" << pktSize);
|
|
uint32_t fragmentLength = offset + s - count;
|
|
Ptr<Packet> endFragment = (*i)->CreateFragment (0, fragmentLength);
|
|
outPacket->AddAtEnd (endFragment);
|
|
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
NS_LOG_LOGIC ("Appending to output the packet #" << pktCount << " of offset " << count << " len=" << pktSize);
|
|
outPacket->AddAtEnd (*i);
|
|
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
|
}
|
|
count += pktSize;
|
|
}
|
|
NS_ASSERT (outPacket->GetSize () == s);
|
|
return outPacket;
|
|
}
|
|
|
|
void
|
|
TcpTxBuffer::SetHeadSequence (const SequenceNumber32& seq)
|
|
{
|
|
NS_LOG_FUNCTION (this << seq);
|
|
m_firstByteSeq = seq;
|
|
}
|
|
|
|
void
|
|
TcpTxBuffer::DiscardUpTo (const SequenceNumber32& seq)
|
|
{
|
|
NS_LOG_FUNCTION (this << seq);
|
|
NS_LOG_LOGIC ("current data size=" << m_size << ", headSeq=" << m_firstByteSeq << ", maxBuffer=" << m_maxBuffer
|
|
<< ", numPkts=" << m_data.size ());
|
|
// Cases do not need to scan the buffer
|
|
if (m_firstByteSeq >= seq) return;
|
|
|
|
// Scan the buffer and discard packets
|
|
uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
|
|
uint32_t pktSize;
|
|
NS_LOG_LOGIC ("Offset=" << offset);
|
|
BufIterator i = m_data.begin ();
|
|
while (i != m_data.end ())
|
|
{
|
|
if (offset > (*i)->GetSize ())
|
|
{ // This packet is behind the seqnum. Remove this packet from the buffer
|
|
pktSize = (*i)->GetSize ();
|
|
m_size -= pktSize;
|
|
offset -= pktSize;
|
|
m_firstByteSeq += pktSize;
|
|
i = m_data.erase (i);
|
|
NS_LOG_LOGIC ("Removed one packet of size " << pktSize << ", offset=" << offset);
|
|
}
|
|
else if (offset > 0)
|
|
{ // Part of the packet is behind the seqnum. Fragment
|
|
pktSize = (*i)->GetSize () - offset;
|
|
*i = (*i)->CreateFragment (offset, pktSize);
|
|
m_size -= offset;
|
|
m_firstByteSeq += offset;
|
|
NS_LOG_LOGIC ("Fragmented one packet by size " << offset << ", new size=" << pktSize);
|
|
break;
|
|
}
|
|
}
|
|
// Catching the case of ACKing a FIN
|
|
if (m_size == 0)
|
|
{
|
|
m_firstByteSeq = seq;
|
|
}
|
|
NS_LOG_LOGIC ("size=" << m_size << " headSeq=" << m_firstByteSeq << " maxBuffer=" << m_maxBuffer
|
|
<<" numPkts="<< m_data.size ());
|
|
NS_ASSERT (m_firstByteSeq == seq);
|
|
}
|
|
|
|
} // namepsace ns3
|