A Discrete-Event Network Simulator
API
tcp-rx-buffer.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2010 Adrian Sai-wah Tam
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
18 */
19
20#include "tcp-rx-buffer.h"
21
22#include "ns3/log.h"
23#include "ns3/packet.h"
24
25namespace ns3
26{
27
28NS_LOG_COMPONENT_DEFINE("TcpRxBuffer");
29
31
32TypeId
34{
35 static TypeId tid = TypeId("ns3::TcpRxBuffer")
37 .SetGroupName("Internet")
38 .AddConstructor<TcpRxBuffer>()
39 .AddTraceSource("NextRxSequence",
40 "Next sequence number expected (RCV.NXT)",
42 "ns3::SequenceNumber32TracedValueCallback");
43 return tid;
44}
45
46/* A user is supposed to create a TcpSocket through a factory. In TcpSocket,
47 * there are attributes SndBufSize and RcvBufSize to control the default Tx and
48 * Rx window sizes respectively, with default of 128 KiByte. The attribute
49 * RcvBufSize is passed to TcpRxBuffer by TcpSocketBase::SetRcvBufSize() and in
50 * turn, TcpRxBuffer:SetMaxBufferSize(). Therefore, the m_maxBuffer value
51 * initialized below is insignificant.
52 */
54 : m_nextRxSeq(n),
55 m_gotFin(false),
56 m_size(0),
57 m_maxBuffer(32768),
58 m_availBytes(0)
59{
60}
61
63{
64}
65
68{
69 return m_nextRxSeq;
70}
71
72void
74{
75 m_nextRxSeq = s;
76}
77
80{
81 return m_maxBuffer;
82}
83
84void
86{
87 m_maxBuffer = s;
88}
89
92{
93 return m_size;
94}
95
98{
99 return m_availBytes;
100}
101
102void
104{
105 NS_LOG_FUNCTION(this);
106 // Increment nextRxSeq is valid only if we don't have any data buffered,
107 // this is supposed to be called only during the three-way handshake
108 NS_ASSERT(m_size == 0);
109 m_nextRxSeq++;
110}
111
112// Return the lowest sequence number that this TcpRxBuffer cannot accept
115{
116 if (m_gotFin)
117 { // No data allowed beyond FIN
118 return m_finSeq;
119 }
120 else if (m_data.size() && m_nextRxSeq > m_data.begin()->first)
121 { // No data allowed beyond Rx window allowed
122 return m_data.begin()->first + SequenceNumber32(m_maxBuffer);
123 }
125}
126
127void
129{
130 NS_LOG_FUNCTION(this);
131
132 m_gotFin = true;
133 m_finSeq = s;
134 if (m_nextRxSeq == m_finSeq)
135 {
136 ++m_nextRxSeq;
137 }
138}
139
140bool
142{
143 return (m_gotFin && m_finSeq < m_nextRxSeq);
144}
145
146bool
148{
149 NS_LOG_FUNCTION(this << p << tcph);
150
151 uint32_t pktSize = p->GetSize();
152 SequenceNumber32 headSeq = tcph.GetSequenceNumber();
153 SequenceNumber32 tailSeq = headSeq + SequenceNumber32(pktSize);
154 NS_LOG_LOGIC("Add pkt " << p << " len=" << pktSize << " seq=" << headSeq
155 << ", when NextRxSeq=" << m_nextRxSeq << ", buffsize=" << m_size);
156
157 // Trim packet to fit Rx window specification
158 if (headSeq < m_nextRxSeq)
159 {
160 headSeq = m_nextRxSeq;
161 }
162 if (m_data.size())
163 {
164 SequenceNumber32 maxSeq = m_data.begin()->first + SequenceNumber32(m_maxBuffer);
165 if (maxSeq < tailSeq)
166 {
167 tailSeq = maxSeq;
168 }
169 if (tailSeq < headSeq)
170 {
171 headSeq = tailSeq;
172 }
173 }
174 // Remove overlapped bytes from packet
175 BufIterator i = m_data.begin();
176 while (i != m_data.end() && i->first <= tailSeq)
177 {
178 SequenceNumber32 lastByteSeq = i->first + SequenceNumber32(i->second->GetSize());
179 if (lastByteSeq > headSeq)
180 {
181 if (i->first > headSeq && lastByteSeq < tailSeq)
182 { // Rare case: Existing packet is embedded fully in the new packet
183 m_size -= i->second->GetSize();
184 m_data.erase(i++);
185 continue;
186 }
187 if (i->first <= headSeq)
188 { // Incoming head is overlapped
189 headSeq = lastByteSeq;
190 }
191 if (lastByteSeq >= tailSeq)
192 { // Incoming tail is overlapped
193 tailSeq = i->first;
194 }
195 }
196 ++i;
197 }
198 // We now know how much we are going to store, trim the packet
199 if (headSeq >= tailSeq)
200 {
201 NS_LOG_LOGIC("Nothing to buffer");
202 return false; // Nothing to buffer anyway
203 }
204 else
205 {
206 uint32_t start = static_cast<uint32_t>(headSeq - tcph.GetSequenceNumber());
207 uint32_t length = static_cast<uint32_t>(tailSeq - headSeq);
208 p = p->CreateFragment(start, length);
209 NS_ASSERT(length == p->GetSize());
210 }
211 // Insert packet into buffer
212 NS_ASSERT(m_data.find(headSeq) == m_data.end()); // Shouldn't be there yet
213 m_data[headSeq] = p;
214
215 if (headSeq > m_nextRxSeq)
216 {
217 // Generate a new SACK block
218 UpdateSackList(headSeq, tailSeq);
219 }
220
221 NS_LOG_LOGIC("Buffered packet of seqno=" << headSeq << " len=" << p->GetSize());
222 // Update variables
223 m_size += p->GetSize(); // Occupancy
224 for (i = m_data.begin(); i != m_data.end(); ++i)
225 {
226 if (i->first < m_nextRxSeq)
227 {
228 continue;
229 }
230 else if (i->first > m_nextRxSeq)
231 {
232 break;
233 };
234 m_nextRxSeq = i->first + SequenceNumber32(i->second->GetSize());
235 m_availBytes += i->second->GetSize();
237 }
238 NS_LOG_LOGIC("Updated buffer occupancy=" << m_size << " nextRxSeq=" << m_nextRxSeq);
239 if (m_gotFin && m_nextRxSeq == m_finSeq)
240 { // Account for the FIN packet
241 ++m_nextRxSeq;
242 };
243 return true;
244}
245
248{
249 NS_LOG_FUNCTION(this);
250
251 return static_cast<uint32_t>(m_sackList.size());
252}
253
254void
256{
257 NS_LOG_FUNCTION(this << head << tail);
258 NS_ASSERT(head > m_nextRxSeq);
259
261 current.first = head;
262 current.second = tail;
263
264 // The block "current" has been safely stored. Now we need to build the SACK
265 // list, to be advertised. From RFC 2018:
266 // (a) The first SACK block (i.e., the one immediately following the
267 // kind and length fields in the option) MUST specify the contiguous
268 // block of data containing the segment which triggered this ACK,
269 // unless that segment advanced the Acknowledgment Number field in
270 // the header. This assures that the ACK with the SACK option
271 // reflects the most recent change in the data receiver's buffer
272 // queue.
273 //
274 // (b) The data receiver SHOULD include as many distinct SACK blocks as
275 // possible in the SACK option. Note that the maximum available
276 // option space may not be sufficient to report all blocks present in
277 // the receiver's queue.
278 //
279 // (c) The SACK option SHOULD be filled out by repeating the most
280 // recently reported SACK blocks (based on first SACK blocks in
281 // previous SACK options) that are not subsets of a SACK block
282 // already included in the SACK option being constructed. This
283 // assures that in normal operation, any segment remaining part of a
284 // non-contiguous block of data held by the data receiver is reported
285 // in at least three successive SACK options, even for large-window
286 // TCP implementations [RFC1323]). After the first SACK block, the
287 // following SACK blocks in the SACK option may be listed in
288 // arbitrary order.
289
290 m_sackList.push_front(current);
291
292 // We have inserted the block at the beginning of the list. Now, we should
293 // check if any existing blocks overlap with that.
294 bool updated = false;
295 TcpOptionSack::SackList::iterator it = m_sackList.begin();
296 TcpOptionSack::SackBlock begin = *it;
298 ++it;
299
300 // Iterates until we examined all blocks in the list (maximum 4)
301 while (it != m_sackList.end())
302 {
303 current = *it;
304
305 // This is a left merge:
306 // [current_first; current_second] [beg_first; beg_second]
307 if (begin.first == current.second)
308 {
309 NS_ASSERT(current.first < begin.second);
310 merged = TcpOptionSack::SackBlock(current.first, begin.second);
311 updated = true;
312 }
313 // while this is a right merge
314 // [begin_first; begin_second] [current_first; current_second]
315 else if (begin.second == current.first)
316 {
317 NS_ASSERT(begin.first < current.second);
318 merged = TcpOptionSack::SackBlock(begin.first, current.second);
319 updated = true;
320 }
321
322 // If we have merged the blocks (and the result is in merged) we should
323 // delete the current block (it), the first block, and insert the merged
324 // one at the beginning.
325 if (updated)
326 {
327 m_sackList.erase(it);
328 m_sackList.pop_front();
329 m_sackList.push_front(merged);
330 it = m_sackList.begin();
331 begin = *it;
332 updated = false;
333 }
334
335 ++it;
336 }
337
338 // Since the maximum blocks that fits into a TCP header are 4, there's no
339 // point on maintaining the others.
340 if (m_sackList.size() > 4)
341 {
342 m_sackList.pop_back();
343 }
344
345 // Please note that, if a block b is discarded and then a block contiguous
346 // to b is received, only that new block (without the b part) is reported.
347 // This is perfectly fine for the RFC point (a), given that we do not report any
348 // overlapping blocks shortly after.
349}
350
351void
353{
354 NS_LOG_FUNCTION(this << seq);
355
356 TcpOptionSack::SackList::iterator it;
357 for (it = m_sackList.begin(); it != m_sackList.end();)
358 {
359 TcpOptionSack::SackBlock block = *it;
360 NS_ASSERT(block.first < block.second);
361
362 if (block.second <= seq)
363 {
364 it = m_sackList.erase(it);
365 }
366 else
367 {
368 it++;
369 }
370 }
371}
372
375{
376 return m_sackList;
377}
378
381{
382 NS_LOG_FUNCTION(this << maxSize);
383
384 uint32_t extractSize = std::min(maxSize, m_availBytes);
385 NS_LOG_LOGIC("Requested to extract " << extractSize
386 << " bytes from TcpRxBuffer of size=" << m_size);
387 if (extractSize == 0)
388 {
389 return nullptr; // No contiguous block to return
390 }
391 NS_ASSERT(m_data.size()); // At least we have something to extract
392 Ptr<Packet> outPkt = Create<Packet>(); // The packet that contains all the data to return
393 BufIterator i;
394 while (extractSize)
395 { // Check the buffered data for delivery
396 i = m_data.begin();
397 NS_ASSERT(i->first <= m_nextRxSeq); // in-sequence data expected
398 // Check if we send the whole pkt or just a partial
399 uint32_t pktSize = i->second->GetSize();
400 if (pktSize <= extractSize)
401 { // Whole packet is extracted
402 outPkt->AddAtEnd(i->second);
403 m_data.erase(i);
404 m_size -= pktSize;
406 extractSize -= pktSize;
407 }
408 else
409 { // Partial is extracted and done
410 outPkt->AddAtEnd(i->second->CreateFragment(0, extractSize));
411 m_data[i->first + SequenceNumber32(extractSize)] =
412 i->second->CreateFragment(extractSize, pktSize - extractSize);
413 m_data.erase(i);
414 m_size -= extractSize;
415 m_availBytes -= extractSize;
416 extractSize = 0;
417 }
418 }
419 if (outPkt->GetSize() == 0)
420 {
421 NS_LOG_LOGIC("Nothing extracted.");
422 return nullptr;
423 }
424 NS_LOG_LOGIC("Extracted " << outPkt->GetSize() << " bytes, bufsize=" << m_size
425 << ", num pkts in buffer=" << m_data.size());
426 return outPkt;
427}
428
429} // namespace ns3
#define min(a, b)
Definition: 80211b.c:42
A base class which provides memory management and object aggregation.
Definition: object.h:89
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:354
uint32_t GetSize() const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:863
Ptr< Packet > CreateFragment(uint32_t start, uint32_t length) const
Create a new packet which contains a fragment of the original packet.
Definition: packet.cc:238
Header for the Transmission Control Protocol.
Definition: tcp-header.h:46
SequenceNumber32 GetSequenceNumber() const
Get the sequence number.
Definition: tcp-header.cc:137
std::list< SackBlock > SackList
SACK list definition.
std::pair< SequenceNumber32, SequenceNumber32 > SackBlock
SACK block definition.
Rx reordering buffer for TCP.
Definition: tcp-rx-buffer.h:75
~TcpRxBuffer() override
uint32_t GetSackListSize() const
Get the size of Sack list.
Ptr< Packet > Extract(uint32_t maxSize)
Extract data from the head of the buffer as indicated by nextRxSeq.
bool Finished()
Check if the buffer did receive all the data (and the connection is closed)
static TypeId GetTypeId()
Get the type ID.
void SetFinSequence(const SequenceNumber32 &s)
Set the FIN Sequence number (i.e., the one closing the connection)
void SetMaxBufferSize(uint32_t s)
Set the Maximum buffer size.
SequenceNumber32 NextRxSequence() const
Get Next Rx Sequence number.
std::map< SequenceNumber32, Ptr< Packet > > m_data
Corresponding data (may be null)
void SetNextRxSequence(const SequenceNumber32 &s)
Set the Next Sequence number.
SequenceNumber32 m_finSeq
Seqnum of the FIN packet.
uint32_t m_availBytes
Number of bytes available to read, i.e.
void IncNextRxSequence()
Increment the Next Sequence number.
uint32_t MaxBufferSize() const
Get the Maximum buffer size.
uint32_t Size() const
Get the actual buffer occupancy.
bool Add(Ptr< Packet > p, const TcpHeader &tcph)
Insert a packet into the buffer and update the availBytes counter to reflect the number of bytes read...
uint32_t m_maxBuffer
Upper bound of the number of data bytes in buffer (RCV.WND)
std::map< SequenceNumber32, Ptr< Packet > >::iterator BufIterator
container for data stored in the buffer
SequenceNumber32 MaxRxSequence() const
Get the lowest sequence number that this TcpRxBuffer cannot accept.
TracedValue< SequenceNumber32 > m_nextRxSeq
Seqnum of the first missing byte in data (RCV.NXT)
TcpRxBuffer(uint32_t n=0)
Constructor.
void UpdateSackList(const SequenceNumber32 &head, const SequenceNumber32 &tail)
Update the sack list, with the block seq starting at the beginning.
uint32_t Available() const
Get the actual number of bytes available to be read.
TcpOptionSack::SackList GetSackList() const
Get the sack list.
uint32_t m_size
Number of total data bytes in the buffer, not necessarily contiguous.
void ClearSackList(const SequenceNumber32 &seq)
Remove old blocks from the sack list.
bool m_gotFin
Did I received FIN packet?
TcpOptionSack::SackList m_sackList
Sack list (updated constantly)
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
SequenceNumber< uint32_t, int32_t > SequenceNumber32
32 bit Sequence number.
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
def start()
Definition: core.py:1861
uint32_t pktSize
packet size used for the simulation (in bytes)