A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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.empty() && 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.empty())
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 auto 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 auto 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 auto 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 for (auto it = m_sackList.begin(); it != m_sackList.end();)
357 {
358 TcpOptionSack::SackBlock block = *it;
359 NS_ASSERT(block.first < block.second);
360
361 if (block.second <= seq)
362 {
363 it = m_sackList.erase(it);
364 }
365 else
366 {
367 it++;
368 }
369 }
370}
371
374{
375 return m_sackList;
376}
377
380{
381 NS_LOG_FUNCTION(this << maxSize);
382
383 uint32_t extractSize = std::min(maxSize, m_availBytes);
384 NS_LOG_LOGIC("Requested to extract " << extractSize
385 << " bytes from TcpRxBuffer of size=" << m_size);
386 if (extractSize == 0)
387 {
388 return nullptr; // No contiguous block to return
389 }
390 NS_ASSERT(!m_data.empty()); // At least we have something to extract
391 Ptr<Packet> outPkt = Create<Packet>(); // The packet that contains all the data to return
392 BufIterator i;
393 while (extractSize)
394 { // Check the buffered data for delivery
395 i = m_data.begin();
396 NS_ASSERT(i->first <= m_nextRxSeq); // in-sequence data expected
397 // Check if we send the whole pkt or just a partial
398 uint32_t pktSize = i->second->GetSize();
399 if (pktSize <= extractSize)
400 { // Whole packet is extracted
401 outPkt->AddAtEnd(i->second);
402 m_data.erase(i);
403 m_size -= pktSize;
405 extractSize -= pktSize;
406 }
407 else
408 { // Partial is extracted and done
409 outPkt->AddAtEnd(i->second->CreateFragment(0, extractSize));
410 m_data[i->first + SequenceNumber32(extractSize)] =
411 i->second->CreateFragment(extractSize, pktSize - extractSize);
412 m_data.erase(i);
413 m_size -= extractSize;
414 m_availBytes -= extractSize;
415 extractSize = 0;
416 }
417 }
418 if (outPkt->GetSize() == 0)
419 {
420 NS_LOG_LOGIC("Nothing extracted.");
421 return nullptr;
422 }
423 NS_LOG_LOGIC("Extracted " << outPkt->GetSize() << " bytes, bufsize=" << m_size
424 << ", num pkts in buffer=" << m_data.size());
425 return outPkt;
426}
427
428} // namespace ns3
A base class which provides memory management and object aggregation.
Definition: object.h:89
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
Header for the Transmission Control Protocol.
Definition: tcp-header.h:47
SequenceNumber32 GetSequenceNumber() const
Get the sequence number.
Definition: tcp-header.cc:118
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:76
~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:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
#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:46
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.
uint32_t pktSize
packet size used for the simulation (in bytes)