A Discrete-Event Network Simulator
API
tcp-tx-buffer.cc
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2010-2015 Adrian Sai-wah Tam
4 * Copyright (c) 2016 Natale Patriciello <natale.patriciello@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation;
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 * Original author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
20 */
21
22#include <algorithm>
23#include <iostream>
24
25#include "ns3/packet.h"
26#include "ns3/log.h"
27#include "ns3/abort.h"
28#include "ns3/tcp-option-ts.h"
29
30#include "tcp-tx-buffer.h"
31
32namespace ns3 {
33
34NS_LOG_COMPONENT_DEFINE ("TcpTxBuffer");
35NS_OBJECT_ENSURE_REGISTERED (TcpTxBuffer);
36
37Callback<void, TcpTxItem *> TcpTxBuffer::m_nullCb = MakeNullCallback<void, TcpTxItem*> ();
38TypeId
40{
41 static TypeId tid = TypeId ("ns3::TcpTxBuffer")
42 .SetParent<Object> ()
43 .SetGroupName ("Internet")
44 .AddConstructor<TcpTxBuffer> ()
45 .AddTraceSource ("UnackSequence",
46 "First unacknowledged sequence number (SND.UNA)",
48 "ns3::SequenceNumber32TracedValueCallback")
49 ;
50 return tid;
51}
52
53/* A user is supposed to create a TcpSocket through a factory. In TcpSocket,
54 * there are attributes SndBufSize and RcvBufSize to control the default Tx and
55 * Rx window sizes respectively, with default of 128 KiByte. The attribute
56 * SndBufSize is passed to TcpTxBuffer by TcpSocketBase::SetSndBufSize() and in
57 * turn, TcpTxBuffer:SetMaxBufferSize(). Therefore, the m_maxBuffer value
58 * initialized below is insignificant.
59 */
61 : m_maxBuffer (32768), m_size (0), m_sentSize (0), m_firstByteSeq (n)
62{
63 m_rWndCallback = MakeNullCallback<uint32_t> ();
64}
65
67{
68 PacketList::iterator it;
69
70 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
71 {
72 TcpTxItem *item = *it;
73 m_sentSize -= item->m_packet->GetSize ();
74 delete item;
75 }
76
77 for (it = m_appList.begin (); it != m_appList.end (); ++it)
78 {
79 TcpTxItem *item = *it;
80 m_size -= item->m_packet->GetSize ();
81 delete item;
82 }
83}
84
87{
88 return m_firstByteSeq;
89}
90
93{
95}
96
99{
100 return m_size;
101}
102
105{
106 return m_maxBuffer;
107}
108
109void
111{
112 m_maxBuffer = n;
113}
114
115bool
117{
118 return m_sackEnabled;
119}
120
121void
123{
124 m_sackEnabled = enabled;
125}
126
129{
130 return m_maxBuffer - m_size;
131}
132
133void
135{
136 m_dupAckThresh = dupAckThresh;
137}
138
139void
141{
143}
144
147{
148 return m_retrans;
149}
150
153{
154 return m_lostOut;
155}
156
159{
160 return m_sackedOut;
161}
162
163void
165{
166 NS_LOG_FUNCTION (this << seq);
167 m_firstByteSeq = seq;
168
169 if (m_sentList.size () > 0)
170 {
171 m_sentList.front ()->m_startSeq = seq;
172 }
173
174 // if you change the head with data already sent, something bad will happen
175 NS_ASSERT (m_sentList.size () == 0);
176 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
177}
178
179bool
181{
182 NS_LOG_FUNCTION (this << p);
183 NS_LOG_LOGIC ("Try to append " << p->GetSize () << " bytes to window starting at "
184 << m_firstByteSeq << ", availSize=" << Available ());
185 if (p->GetSize () <= Available ())
186 {
187 if (p->GetSize () > 0)
188 {
189 TcpTxItem *item = new TcpTxItem ();
190 item->m_packet = p->Copy ();
191 m_appList.insert (m_appList.end (), item);
192 m_size += p->GetSize ();
193
194 NS_LOG_LOGIC ("Updated size=" << m_size << ", lastSeq=" <<
196 }
197 return true;
198 }
199 NS_LOG_LOGIC ("Rejected. Not enough room to buffer packet.");
200 return false;
201}
202
205{
206 NS_LOG_FUNCTION (this << seq);
207 // Sequence of last byte in buffer
208 SequenceNumber32 lastSeq = TailSequence ();
209
210 if (lastSeq >= seq)
211 {
212 return static_cast<uint32_t> (lastSeq - seq);
213 }
214
215 NS_LOG_ERROR ("Requested a sequence beyond our space (" << seq << " > " << lastSeq <<
216 "). Returning 0 for convenience.");
217 return 0;
218}
219
220TcpTxItem *
222{
223 NS_LOG_FUNCTION (this << numBytes << seq);
224
226 "Requested a sequence number which is not in the buffer anymore");
228
229 // Real size to extract. Insure not beyond end of data
230 uint32_t s = std::min (numBytes, SizeFromSequence (seq));
231
232 if (s == 0)
233 {
234 return nullptr;
235 }
236
237 TcpTxItem *outItem = nullptr;
238
239 if (m_firstByteSeq + m_sentSize >= seq + s)
240 {
241 // already sent this block completely
242 outItem = GetTransmittedSegment (s, seq);
243 NS_ASSERT (outItem != nullptr);
244 NS_ASSERT (!outItem->m_sacked);
245
246 NS_LOG_DEBUG ("Returning already sent item " << *outItem << " from " << *this);
247 }
248 else if (m_firstByteSeq + m_sentSize <= seq)
249 {
251 "Requesting a piece of new data with an hole");
252
253 // this is the first time we transmit this block
254 outItem = GetNewSegment (s);
255 NS_ASSERT (outItem != nullptr);
256 NS_ASSERT (outItem->m_retrans == false);
257
258 NS_LOG_DEBUG ("Returning new item " << *outItem << " from " << *this);
259 }
260 else if (m_firstByteSeq.Get ().GetValue () + m_sentSize > seq.GetValue ()
261 && m_firstByteSeq.Get ().GetValue () + m_sentSize < seq.GetValue () + s)
262 {
263 // Partial: a part is retransmission, the remaining data is new
264 // Just return the old segment, without taking new data. Hopefully
265 // TcpSocketBase will request new data
266
267 uint32_t amount = (m_firstByteSeq.Get ().GetValue () + m_sentSize) - seq.GetValue ();
268
269 return CopyFromSequence (amount, seq);
270 }
271
272 outItem->m_lastSent = Simulator::Now ();
274 "Returning an item " << *outItem << " with SND.UNA as " <<
277 return outItem;
278}
279
282{
283 NS_LOG_FUNCTION (this << numBytes);
284
285 SequenceNumber32 startOfAppList = m_firstByteSeq + m_sentSize;
286
287 NS_LOG_INFO ("AppList start at " << startOfAppList << ", sentSize = " <<
288 m_sentSize << " firstByte: " << m_firstByteSeq);
289
290 TcpTxItem *item = GetPacketFromList (m_appList, startOfAppList,
291 numBytes, startOfAppList);
292 item->m_startSeq = startOfAppList;
293
294 // Move item from AppList to SentList (should be the first, not too complex)
295 auto it = std::find (m_appList.begin (), m_appList.end (), item);
296 NS_ASSERT (it != m_appList.end ());
297
298 m_appList.erase (it);
299 m_sentList.insert (m_sentList.end (), item);
300 m_sentSize += item->m_packet->GetSize ();
301
302 return item;
303}
304
307{
308 NS_LOG_FUNCTION (this << numBytes << seq);
309 NS_ASSERT (seq >= m_firstByteSeq);
310 NS_ASSERT (numBytes <= m_sentSize);
311 NS_ASSERT (m_sentList.size () >= 1);
312
313 auto it = m_sentList.begin ();
314 bool listEdited = false;
315 uint32_t s = numBytes;
316
317 // Avoid to merge different packet for this retransmission if flags are
318 // different.
319 for (; it != m_sentList.end(); ++it)
320 {
321 if ((*it)->m_startSeq == seq)
322 {
323 auto next = it;
324 next++;
325 if (next != m_sentList.end ())
326 {
327 // Next is not sacked and have the same value for m_lost ... there is the possibility to merge
328 if ((! (*next)->m_sacked) && ((*it)->m_lost == (*next)->m_lost))
329 {
330 s = std::min(s, (*it)->m_packet->GetSize () + (*next)->m_packet->GetSize ());
331 }
332 else
333 {
334 // Next is sacked... better to retransmit only the first segment
335 s = std::min(s, (*it)->m_packet->GetSize ());
336 }
337 }
338 else
339 {
340 s = std::min(s, (*it)->m_packet->GetSize ());
341 }
342 break;
343 }
344 }
345
346 TcpTxItem *item = GetPacketFromList (m_sentList, m_firstByteSeq, s, seq, &listEdited);
347
348 if (! item->m_retrans)
349 {
350 m_retrans += item->m_packet->GetSize ();
351 item->m_retrans = true;
352 }
353
354 return item;
355}
356
357std::pair <TcpTxBuffer::PacketList::const_iterator, SequenceNumber32>
359{
360 NS_LOG_FUNCTION (this);
361
362 SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
363
364 auto ret = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
365
366 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
367 {
368 const TcpTxItem *item = *it;
369 if (item->m_sacked)
370 {
371 ret = std::make_pair (it, beginOfCurrentPacket);
372 }
373 beginOfCurrentPacket += item->m_packet->GetSize ();
374 }
375
376 return ret;
377}
378
379
380void
382{
383 NS_ASSERT (t1 != nullptr && t2 != nullptr);
384 NS_LOG_FUNCTION (this << *t2 << size);
385
386 t1->m_packet = t2->m_packet->CreateFragment (0, size);
387 t2->m_packet->RemoveAtStart (size);
388
389 t1->m_startSeq = t2->m_startSeq;
390 t1->m_sacked = t2->m_sacked;
391 t1->m_lastSent = t2->m_lastSent;
392 t1->m_retrans = t2->m_retrans;
393 t1->m_lost = t2->m_lost;
394
395 t2->m_startSeq += size;
396
397 NS_LOG_INFO ("Split of size " << size << " result: t1 " << *t1 << " t2 " << *t2);
398}
399
402 uint32_t numBytes, const SequenceNumber32 &seq,
403 bool *listEdited) const
404{
405 NS_LOG_FUNCTION (this << numBytes << seq);
406
407 /*
408 * Our possibilities are sketched out in the following:
409 *
410 * |------| |----| |----|
411 * GetList (m_data) = | | --> | | --> | |
412 * |------| |----| |----|
413 *
414 * ^ ^ ^ ^
415 * | | | | (1)
416 * seq | | numBytes
417 * | |
418 * | |
419 * seq numBytes (2)
420 *
421 * (1) seq and numBytes are the boundary of some packet
422 * (2) seq and numBytes are not the boundary of some packet
423 *
424 * We can have mixed case (e.g. seq over the boundary while numBytes not).
425 *
426 * If we discover that we are in (2) or in a mixed case, we split
427 * packets accordingly to the requested bounds and re-run the function.
428 *
429 * In (1), things are pretty easy, it's just a matter of walking the list and
430 * defragment packets, if needed (e.g. seq is the beginning of the first packet
431 * while maxBytes is the end of some packet next in the list).
432 */
433
434 Ptr<Packet> currentPacket = nullptr;
435 TcpTxItem *currentItem = nullptr;
436 TcpTxItem *outItem = nullptr;
437 PacketList::iterator it = list.begin ();
438 SequenceNumber32 beginOfCurrentPacket = listStartFrom;
439
440 while (it != list.end ())
441 {
442 currentItem = *it;
443 currentPacket = currentItem->m_packet;
445 "start: " << m_firstByteSeq << " currentItem start: " <<
446 currentItem->m_startSeq);
447
448 // The objective of this snippet is to find (or to create) the packet
449 // that begin with the sequence seq
450
451 if (seq < beginOfCurrentPacket + currentPacket->GetSize ())
452 {
453 // seq is inside the current packet
454 if (seq == beginOfCurrentPacket)
455 {
456 // seq is the beginning of the current packet. Hurray!
457 outItem = currentItem;
458 NS_LOG_INFO ("Current packet starts at seq " << seq <<
459 " ends at " << seq + currentPacket->GetSize ());
460 }
461 else if (seq > beginOfCurrentPacket)
462 {
463 // seq is inside the current packet but seq is not the beginning,
464 // it's somewhere in the middle. Just fragment the beginning and
465 // start again.
466 NS_LOG_INFO ("we are at " << beginOfCurrentPacket <<
467 " searching for " << seq <<
468 " and now we recurse because packet ends at "
469 << beginOfCurrentPacket + currentPacket->GetSize ());
470 TcpTxItem *firstPart = new TcpTxItem ();
471 SplitItems (firstPart, currentItem, seq - beginOfCurrentPacket);
472
473 // insert firstPart before currentItem
474 list.insert (it, firstPart);
475 if (listEdited)
476 {
477 *listEdited = true;
478 }
479
480 return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
481 }
482 else
483 {
484 NS_FATAL_ERROR ("seq < beginOfCurrentPacket: our data is before");
485 }
486 }
487 else
488 {
489 // Walk the list, the current packet does not contain seq
490 beginOfCurrentPacket += currentPacket->GetSize ();
491 it++;
492 continue;
493 }
494
495 NS_ASSERT (outItem != nullptr);
496
497 // The objective of this snippet is to find (or to create) the packet
498 // that ends after numBytes bytes. We are sure that outPacket starts
499 // at seq.
500
501 if (seq + numBytes <= beginOfCurrentPacket + currentPacket->GetSize ())
502 {
503 // the end boundary is inside the current packet
504 if (numBytes == currentPacket->GetSize ())
505 {
506 // the end boundary is exactly the end of the current packet. Hurray!
507 if (currentItem->m_packet == outItem->m_packet)
508 {
509 // A perfect match!
510 return outItem;
511 }
512 else
513 {
514 // the end is exactly the end of current packet, but
515 // current > outPacket in the list. Merge current with the
516 // previous, and recurse.
517 NS_ASSERT (it != list.begin ());
518 TcpTxItem *previous = *(--it);
519
520 list.erase (it);
521
522 MergeItems (previous, currentItem);
523 delete currentItem;
524 if (listEdited)
525 {
526 *listEdited = true;
527 }
528
529 return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
530 }
531 }
532 else if (numBytes < currentPacket->GetSize ())
533 {
534 // the end is inside the current packet, but it isn't exactly
535 // the packet end. Just fragment, fix the list, and return.
536 TcpTxItem *firstPart = new TcpTxItem ();
537 SplitItems (firstPart, currentItem, numBytes);
538
539 // insert firstPart before currentItem
540 list.insert (it, firstPart);
541 if (listEdited)
542 {
543 *listEdited = true;
544 }
545
546 return firstPart;
547 }
548 }
549 else
550 {
551 // The end isn't inside current packet, but there is an exception for
552 // the merge and recurse strategy...
553 if (++it == list.end ())
554 {
555 // ...current is the last packet we sent. We have not more data;
556 // Go for this one.
557 NS_LOG_WARN ("Cannot reach the end, but this case is covered "
558 "with conditional statements inside CopyFromSequence."
559 "Something has gone wrong, report a bug");
560 return outItem;
561 }
562
563 // The current packet does not contain the requested end. Merge current
564 // with the packet that follows, and recurse
565 TcpTxItem *next = (*it); // Please remember we have incremented it
566 // in the previous if
567
568 MergeItems (currentItem, next);
569 list.erase (it);
570
571 delete next;
572
573 if (listEdited)
574 {
575 *listEdited = true;
576 }
577
578 return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
579 }
580 }
581
582 NS_FATAL_ERROR ("This point is not reachable");
583 return nullptr; // Silence compiler warning about lack of return value
584}
585
586void
588{
589 NS_ASSERT (t1 != nullptr && t2 != nullptr);
590 NS_LOG_FUNCTION (this << *t1 << *t2);
591 NS_LOG_INFO ("Merging " << *t2 << " into " << *t1);
592
593 NS_ASSERT_MSG (t1->m_sacked == t2->m_sacked,
594 "Merging one sacked and another not sacked. Impossible");
595 NS_ASSERT_MSG (t1->m_lost == t2->m_lost,
596 "Merging one lost and another not lost. Impossible");
597
598 // If one is retrans and the other is not, cancel the retransmitted flag.
599 // We are merging this segment for the retransmit, so the count will
600 // be updated in MarkTransmittedSegment.
601 if (t1->m_retrans != t2->m_retrans)
602 {
603 if (t1->m_retrans)
604 {
605 TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
606 self->m_retrans -= t1->m_packet->GetSize ();
607 t1->m_retrans = false;
608 }
609 else
610 {
611 NS_ASSERT (t2->m_retrans);
612 TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
613 self->m_retrans -= t2->m_packet->GetSize ();
614 t2->m_retrans = false;
615 }
616 }
617
618 if (t1->m_lastSent < t2->m_lastSent)
619 {
620 t1->m_lastSent = t2->m_lastSent;
621 }
622
623 t1->m_packet->AddAtEnd (t2->m_packet);
624
625 NS_LOG_INFO ("Situation after the merge: " << *t1);
626}
627
628void
630{
631 NS_LOG_FUNCTION (this << *item << size);
632 if (item->m_sacked)
633 {
634 NS_ASSERT (m_sackedOut >= size);
635 m_sackedOut -= size;
636 }
637 if (item->m_retrans)
638 {
639 NS_ASSERT (m_retrans >= size);
640 m_retrans -= size;
641 }
642 if (item->m_lost)
643 {
644 NS_ASSERT_MSG (m_lostOut >= size, "Trying to remove " << size <<
645 " bytes from " << m_lostOut);
646 m_lostOut -= size;
647 }
648}
649
650bool
652{
653 NS_LOG_FUNCTION (this);
654 for (const auto &it : m_sentList)
655 {
656 TcpTxItem *item = it;
657 Ptr<Packet> p = item->m_packet;
658 if (item->m_startSeq + p->GetSize () == ack && !item->m_sacked && item->m_retrans)
659 {
660 return true;
661 }
662 }
663 return false;
664}
665
666void
668 const Callback<void, TcpTxItem *> &beforeDelCb)
669{
670 NS_LOG_FUNCTION (this << seq);
671
672 // Cases do not need to scan the buffer
673 if (m_firstByteSeq >= seq)
674 {
675 NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
676 return;
677 }
678 NS_LOG_DEBUG ("Remove up to " << seq << " lost: " << m_lostOut <<
679 " retrans: " << m_retrans << " sacked: " << m_sackedOut);
680
681 // Scan the buffer and discard packets
682 uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
684 PacketList::iterator i = m_sentList.begin ();
685 while (m_size > 0 && offset > 0)
686 {
687 if (i == m_sentList.end ())
688 {
689 // Move data from app list to sent list, so we can delete the item
690 [[maybe_unused]] Ptr<Packet> p = CopyFromSequence (offset, m_firstByteSeq)->GetPacketCopy ();
691 NS_ASSERT (p != nullptr);
692 i = m_sentList.begin ();
693 NS_ASSERT (i != m_sentList.end ());
694 }
695 TcpTxItem *item = *i;
696 Ptr<Packet> p = item->m_packet;
697 pktSize = p->GetSize ();
699 "Item starts at " << item->m_startSeq <<
700 " while SND.UNA is " << m_firstByteSeq << " from " << *this);
701
702 if (offset >= pktSize)
703 { // This packet is behind the seqnum. Remove this packet from the buffer
704 m_size -= pktSize;
706 offset -= pktSize;
708
710
711 i = m_sentList.erase (i);
712 NS_LOG_INFO ("Removed " << *item << " lost: " << m_lostOut <<
713 " retrans: " << m_retrans << " sacked: " << m_sackedOut <<
714 ". Remaining data " << m_size);
715
716 if (!beforeDelCb.IsNull ())
717 {
718 // Inform Rate algorithms only when a full packet is ACKed
719 beforeDelCb (item);
720 }
721
722 delete item;
723 }
724 else if (offset > 0)
725 { // Part of the packet is behind the seqnum. Fragment
726 pktSize -= offset;
727 NS_LOG_INFO (*item);
728 // PacketTags are preserved when fragmenting
729 item->m_packet = item->m_packet->CreateFragment (offset, pktSize);
730 item->m_startSeq += offset;
731 m_size -= offset;
732 m_sentSize -= offset;
733 m_firstByteSeq += offset;
734
735 RemoveFromCounts (item, offset);
736
737 NS_LOG_INFO ("Fragmented one packet by size " << offset <<
738 ", new size=" << pktSize << " resulting item is " <<
739 *item << " status: " << *this);
740 break;
741 }
742 }
743 // Catching the case of ACKing a FIN
744 if (m_size == 0)
745 {
746 m_firstByteSeq = seq;
747 }
748
749 if (!m_sentList.empty ())
750 {
751 TcpTxItem *head = m_sentList.front ();
752 if (head->m_sacked)
753 {
754 NS_ASSERT (!head->m_lost);
755 // It is not possible to have the UNA sacked; otherwise, it would
756 // have been ACKed. This is, most likely, our wrong guessing
757 // when adding Reno dupacks in the count.
758 head->m_sacked = false;
759 m_sackedOut -= head->m_packet->GetSize ();
760 NS_LOG_INFO ("Moving the SACK flag from the HEAD to another segment");
761 AddRenoSack ();
763 }
764
765 NS_ASSERT_MSG (head->m_startSeq == seq,
766 "While removing up to " << seq << " we get SND.UNA to " <<
767 m_firstByteSeq << " this is the result: " << *this);
768 }
769
770 if (m_highestSack.second <= m_firstByteSeq)
771 {
772 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
773 }
774
775 NS_LOG_DEBUG ("Discarded up to " << seq << " lost: " << m_lostOut <<
776 " retrans: " << m_retrans << " sacked: " << m_sackedOut);
777 NS_LOG_LOGIC ("Buffer status after discarding data " << *this);
778 NS_ASSERT (m_firstByteSeq >= seq);
781}
782
785 const Callback<void, TcpTxItem *> &sackedCb)
786{
787 NS_LOG_FUNCTION (this);
788 NS_LOG_INFO ("Updating scoreboard, got " << list.size () << " blocks to analyze");
789
790 uint32_t bytesSacked = 0;
791
792 for (auto option_it = list.begin (); option_it != list.end (); ++option_it)
793 {
794 PacketList::iterator item_it = m_sentList.begin ();
795 SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
796
797 if (m_firstByteSeq + m_sentSize < (*option_it).first)
798 {
799 NS_LOG_INFO ("Not updating scoreboard, the option block is outside the sent list");
800 return bytesSacked;
801 }
802
803 while (item_it != m_sentList.end ())
804 {
805 uint32_t pktSize = (*item_it)->m_packet->GetSize ();
806
807 // Check the boundary of this packet ... only mark as sacked if
808 // it is precisely mapped over the option. It means that if the receiver
809 // is reporting as sacked single range bytes that are not mapped 1:1
810 // in what we have, the option is discarded. There's room for improvement
811 // here.
812 if (beginOfCurrentPacket >= (*option_it).first
813 && beginOfCurrentPacket + pktSize <= (*option_it).second)
814 {
815 if ((*item_it)->m_sacked)
816 {
817 NS_ASSERT (!(*item_it)->m_lost);
818 NS_LOG_INFO ("Received block " << *option_it <<
819 ", checking sentList for block " << *(*item_it) <<
820 ", found in the sackboard already sacked");
821 }
822 else
823 {
824 if ((*item_it)->m_lost)
825 {
826 (*item_it)->m_lost = false;
827 m_lostOut -= (*item_it)->m_packet->GetSize ();
828 }
829
830 (*item_it)->m_sacked = true;
831 m_sackedOut += (*item_it)->m_packet->GetSize ();
832 bytesSacked += (*item_it)->m_packet->GetSize ();
833
834 if (m_highestSack.first == m_sentList.end()
835 || m_highestSack.second <= beginOfCurrentPacket + pktSize)
836 {
837 m_highestSack = std::make_pair (item_it, beginOfCurrentPacket);
838 }
839
840 NS_LOG_INFO ("Received block " << *option_it <<
841 ", checking sentList for block " << *(*item_it) <<
842 ", found in the sackboard, sacking, current highSack: " <<
843 m_highestSack.second);
844
845 if (!sackedCb.IsNull ())
846 {
847 sackedCb (*item_it);
848 }
849 }
850 }
851 else if (beginOfCurrentPacket + pktSize > (*option_it).second)
852 {
853 // We already passed the received block end. Exit from the loop
854 NS_LOG_INFO ("Received block [" << *option_it <<
855 ", checking sentList for block " << *(*item_it) <<
856 "], not found, breaking loop");
857 break;
858 }
859
860 beginOfCurrentPacket += pktSize;
861 ++item_it;
862 }
863 }
864
865 if (bytesSacked > 0)
866 {
867 NS_ASSERT_MSG (m_highestSack.first != m_sentList.end(), "Buffer status: " << *this);
869 }
870
871 NS_ASSERT ((*(m_sentList.begin ()))->m_sacked == false);
873 //NS_ASSERT (list.size () == 0 || modified); // Assert for duplicated SACK or
874 // impossiblity to map the option into the sent blocks
876 return bytesSacked;
877}
878
879void
881{
882 NS_LOG_FUNCTION (this);
883 uint32_t sacked = 0;
884 SequenceNumber32 beginOfCurrentPacket = m_highestSack.second;
885 if (m_highestSack.first == m_sentList.end ())
886 {
887 NS_LOG_INFO ("Status before the update: " << *this <<
888 ", will start from the latest sent item");
889 }
890 else
891 {
892 NS_LOG_INFO ("Status before the update: " << *this <<
893 ", will start from item " << *(*m_highestSack.first));
894 }
895
896 for (auto it = m_highestSack.first; it != m_sentList.begin(); --it)
897 {
898 TcpTxItem *item = *it;
899 if (item->m_sacked)
900 {
901 sacked++;
902 }
903
904 if (sacked >= m_dupAckThresh)
905 {
906 if (!item->m_sacked && !item->m_lost)
907 {
908 item->m_lost = true;
909 m_lostOut += item->m_packet->GetSize ();
910 }
911 }
912 beginOfCurrentPacket -= item->m_packet->GetSize ();
913 }
914
915 if (sacked >= m_dupAckThresh)
916 {
917 TcpTxItem *item = *m_sentList.begin ();
918 if (!item->m_lost)
919 {
920 item->m_lost = true;
921 m_lostOut += item->m_packet->GetSize ();
922 }
923 }
924 NS_LOG_INFO ("Status after the update: " << *this);
926}
927
928bool
930{
931 NS_LOG_FUNCTION (this << seq);
932
933 SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
934 PacketList::const_iterator it;
935
936 if (seq >= m_highestSack.second)
937 {
938 return false;
939 }
940
941 // In theory, using a map and hints when inserting elements can improve
942 // performance
943 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
944 {
945 // Search for the right iterator before calling IsLost()
946 if (beginOfCurrentPacket >= seq)
947 {
948 if ((*it)->m_lost == true)
949 {
950 NS_LOG_INFO ("seq=" << seq << " is lost because of lost flag");
951 return true;
952 }
953
954 if ((*it)->m_sacked == true)
955 {
956 NS_LOG_INFO ("seq=" << seq << " is not lost because of sacked flag");
957 return false;
958 }
959 }
960
961 beginOfCurrentPacket += (*it)->m_packet->GetSize ();
962 }
963
964 return false;
965}
966
967bool
968TcpTxBuffer::NextSeg (SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
969{
970 NS_LOG_FUNCTION (this << isRecovery);
971 /* RFC 6675, NextSeg definition.
972 *
973 * (1) If there exists a smallest unSACKed sequence number 'S2' that
974 * meets the following three criteria for determining loss, the
975 * sequence range of one segment of up to SMSS octets starting
976 * with S2 MUST be returned.
977 *
978 * (1.a) S2 is greater than HighRxt.
979 *
980 * (1.b) S2 is less than the highest octet covered by any
981 * received SACK.
982 *
983 * (1.c) IsLost (S2) returns true.
984 */
985 PacketList::const_iterator it;
986 TcpTxItem *item;
987 SequenceNumber32 seqPerRule3;
988 bool isSeqPerRule3Valid = false;
989 SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
990
991 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
992 {
993 item = *it;
994
995 // Condition 1.a , 1.b , and 1.c
996 if (item->m_retrans == false && item->m_sacked == false)
997 {
998 if (item->m_lost)
999 {
1000 NS_LOG_INFO("IsLost, returning" << beginOfCurrentPkt);
1001 *seq = beginOfCurrentPkt;
1002 *seqHigh = *seq + m_segmentSize;
1003 return true;
1004 }
1005 else if (seqPerRule3.GetValue () == 0 && isRecovery)
1006 {
1007 NS_LOG_INFO ("Saving for rule 3 the seq " << beginOfCurrentPkt);
1008 isSeqPerRule3Valid = true;
1009 seqPerRule3 = beginOfCurrentPkt;
1010 }
1011 }
1012
1013 // Nothing found, iterate
1014 beginOfCurrentPkt += item->m_packet->GetSize ();
1015 }
1016
1017 /* (2) If no sequence number 'S2' per rule (1) exists but there
1018 * exists available unsent data and the receiver's advertised
1019 * window allows, the sequence range of one segment of up to SMSS
1020 * octets of previously unsent data starting with sequence number
1021 * HighData+1 MUST be returned.
1022 */
1024 {
1025 if (m_sentSize <= m_rWndCallback ())
1026 {
1027 NS_LOG_INFO ("There is unsent data. Send it");
1028 *seq = m_firstByteSeq + m_sentSize;
1029 *seqHigh = *seq + std::min<uint32_t> (m_segmentSize, (m_rWndCallback () - m_sentSize));
1030 return true;
1031 }
1032 else
1033 {
1034 NS_LOG_INFO ("There is no available receiver window to send");
1035 return false;
1036 }
1037 }
1038 else
1039 {
1040 NS_LOG_INFO ("There isn't unsent data.");
1041 }
1042
1043 /* (3) If the conditions for rules (1) and (2) fail, but there exists
1044 * an unSACKed sequence number 'S3' that meets the criteria for
1045 * detecting loss given in steps (1.a) and (1.b) above
1046 * (specifically excluding step (1.c)), then one segment of up to
1047 * SMSS octets starting with S3 SHOULD be returned.
1048 */
1049 if (isSeqPerRule3Valid)
1050 {
1051 NS_LOG_INFO ("Rule3 valid. " << seqPerRule3);
1052 *seq = seqPerRule3;
1053 *seqHigh = *seq + m_segmentSize;
1054 return true;
1055 }
1056
1057 /* (4) If the conditions for (1), (2), and (3) fail, but there exists
1058 * outstanding unSACKed data, we provide the opportunity for a
1059 * single "rescue" retransmission per entry into loss recovery.
1060 * If HighACK is greater than RescueRxt (or RescueRxt is
1061 * undefined), then one segment of up to SMSS octets that MUST
1062 * include the highest outstanding unSACKed sequence number
1063 * SHOULD be returned, and RescueRxt set to RecoveryPoint.
1064 * HighRxt MUST NOT be updated.
1065 *
1066 * This point require too much interaction between us and TcpSocketBase.
1067 * We choose to not respect the SHOULD (allowed from RFC MUST/SHOULD definition)
1068 */
1069 NS_LOG_INFO ("Can't return anything");
1070 return false;
1071}
1072
1075{
1077 "Count of sacked " << m_sackedOut << " and lost " << m_lostOut <<
1078 " is out of sync with sent list size " << m_sentSize <<
1079 " " << *this);
1080 uint32_t leftOut = m_sackedOut + m_lostOut;
1081 uint32_t retrans = m_retrans;
1082
1083 NS_LOG_INFO ("Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1084 " retrans: " << retrans);
1085 uint32_t in_flight = m_sentSize - leftOut + retrans;
1086
1087 //uint32_t rfc_in_flight = BytesInFlightRFC (3, segmentSize);
1088 //NS_ASSERT_MSG(in_flight == rfc_in_flight,
1089 // "Calculated: " << in_flight << " RFC: " << rfc_in_flight <<
1090 // "Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1091 // " retrans: " << retrans);
1092 return in_flight;
1093}
1094
1097{
1098 PacketList::const_iterator it;
1099 TcpTxItem *item;
1100 uint32_t size = 0; // "pipe" in RFC
1101 SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
1102 uint32_t sackedOut = 0;
1103 uint32_t lostOut = 0;
1104 uint32_t retrans = 0;
1105 uint32_t totalSize = 0;
1106
1107 // After initializing pipe to zero, the following steps are taken for each
1108 // octet 'S1' in the sequence space between HighACK and HighData that has not
1109 // been SACKed:
1110 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
1111 {
1112 item = *it;
1113 totalSize += item->m_packet->GetSize();
1114 if (!item->m_sacked)
1115 {
1116 bool isLost = IsLostRFC (beginOfCurrentPkt, it);
1117 // (a) If IsLost (S1) returns false: Pipe is incremented by 1 octet.
1118 if (!isLost)
1119 {
1120 size += item->m_packet->GetSize ();
1121 }
1122 // (b) If S1 <= HighRxt: Pipe is incremented by 1 octet.
1123 // (NOTE: we use the m_retrans flag instead of keeping and updating
1124 // another variable). Only if the item is not marked as lost
1125 else if (item->m_retrans)
1126 {
1127 size += item->m_packet->GetSize ();
1128 }
1129
1130 if (isLost)
1131 {
1132 lostOut += item->m_packet->GetSize ();
1133 }
1134 }
1135 else
1136 {
1137 sackedOut += item->m_packet->GetSize ();
1138 }
1139
1140 if (item->m_retrans)
1141 {
1142 retrans += item->m_packet->GetSize ();
1143 }
1144 beginOfCurrentPkt += item->m_packet->GetSize ();
1145 }
1146
1147 NS_ASSERT_MSG(lostOut == m_lostOut, "Lost counted: " << lostOut << " " <<
1148 m_lostOut << "\n" << *this);
1149 NS_ASSERT_MSG(retrans == m_retrans, "Retrans Counted: " << retrans << " " <<
1150 m_retrans << "\n" << *this);
1151 NS_ASSERT_MSG(sackedOut == m_sackedOut, "Sacked counted: " << sackedOut <<
1152 " " << m_sackedOut << *this);
1153 NS_ASSERT_MSG(totalSize == m_sentSize,
1154 "Sent size counted: " << totalSize << " " << m_sentSize << *this);
1155
1156 return size;
1157}
1158
1159bool
1160TcpTxBuffer::IsLostRFC (const SequenceNumber32 &seq, const PacketList::const_iterator &segment) const
1161{
1162 NS_LOG_FUNCTION (this << seq);
1163 uint32_t count = 0;
1164 uint32_t bytes = 0;
1165 PacketList::const_iterator it;
1166 TcpTxItem *item;
1167 Ptr<const Packet> current;
1168 SequenceNumber32 beginOfCurrentPacket = seq;
1169
1170 if ((*segment)->m_sacked == true)
1171 {
1172 return false;
1173 }
1174
1175 // From RFC 6675:
1176 // > The routine returns true when either dupThresh discontiguous SACKed
1177 // > sequences have arrived above 'seq' or more than (dupThresh - 1) * SMSS bytes
1178 // > with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the
1179 // > routine returns false.
1180 for (it = segment; it != m_sentList.end (); ++it)
1181 {
1182 item = *it;
1183 current = item->m_packet;
1184
1185 if (item->m_sacked)
1186 {
1187 NS_LOG_INFO ("Segment " << *item <<
1188 " found to be SACKed while checking for " << seq);
1189 ++count;
1190 bytes += current->GetSize ();
1191 if ((count >= m_dupAckThresh) || (bytes > (m_dupAckThresh-1) * m_segmentSize))
1192 {
1193 NS_LOG_INFO ("seq=" << seq << " is lost because of 3 sacked blocks ahead");
1194 return true;
1195 }
1196 }
1197
1198 if (beginOfCurrentPacket >= m_highestSack.second)
1199 {
1200 if (item->m_lost && !item->m_retrans)
1201 return true;
1202
1203 NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead");
1204 return false;
1205 }
1206
1207 beginOfCurrentPacket += current->GetSize ();
1208 }
1209 if (it == m_highestSack.first)
1210 {
1211 NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead " << m_highestSack.second);
1212 }
1213 return false;
1214}
1215
1216void
1218{
1219 NS_LOG_FUNCTION (this);
1220
1221 m_sackedOut = 0;
1222 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1223 {
1224 (*it)->m_sacked = false;
1225 }
1226
1227 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1228}
1229
1230void
1232{
1233 NS_LOG_FUNCTION (this);
1234 m_rWndCallback = rWndCallback;
1235}
1236
1237void
1239{
1240 NS_LOG_FUNCTION (this);
1241 TcpTxItem *item;
1242
1243 // Keep the head items; they will then marked as lost
1244 while (m_sentList.size () > 0)
1245 {
1246 item = m_sentList.back ();
1247 item->m_retrans = item->m_sacked = item->m_lost = false;
1248 m_appList.push_front (item);
1249 m_sentList.pop_back ();
1250 }
1251
1252 m_sentSize = 0;
1253 m_lostOut = 0;
1254 m_retrans = 0;
1255 m_sackedOut = 0;
1256 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1257}
1258
1259void
1261{
1262 NS_LOG_FUNCTION (this);
1263 if (!m_sentList.empty ())
1264 {
1265 TcpTxItem *item = m_sentList.back ();
1266
1267 m_sentList.pop_back ();
1268 m_sentSize -= item->m_packet->GetSize ();
1269 if (item->m_retrans)
1270 {
1271 m_retrans -= item->m_packet->GetSize ();
1272 }
1273 m_appList.insert (m_appList.begin (), item);
1274 }
1276}
1277
1278void
1280{
1281 NS_LOG_FUNCTION (this);
1282 m_retrans = 0;
1283
1284 if (resetSack)
1285 {
1286 m_sackedOut = 0;
1288 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1289 }
1290 else
1291 {
1292 m_lostOut = 0;
1293 }
1294
1295 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1296 {
1297 if (resetSack)
1298 {
1299 (*it)->m_sacked = false;
1300 (*it)->m_lost = true;
1301 }
1302 else
1303 {
1304 if ((*it)->m_lost)
1305 {
1306 // Have to increment it because we set it to 0 at line 1133
1307 m_lostOut += (*it)->m_packet->GetSize ();
1308 }
1309 else if (!(*it)->m_sacked)
1310 {
1311 // Packet is not marked lost, nor is sacked. Then it becomes lost.
1312 (*it)->m_lost = true;
1313 m_lostOut += (*it)->m_packet->GetSize ();
1314 }
1315 }
1316
1317 (*it)->m_retrans = false;
1318 }
1319
1320 NS_LOG_INFO ("Set sent list lost, status: " << *this);
1323}
1324
1325bool
1327{
1328 NS_LOG_FUNCTION (this);
1329
1330 if (m_sentSize == 0)
1331 {
1332 return false;
1333 }
1334
1335 return m_sentList.front ()->m_retrans;
1336}
1337
1338void
1340{
1341 NS_LOG_FUNCTION (this);
1342
1343 if (m_sentSize == 0)
1344 {
1345 return;
1346 }
1347
1348 if (m_sentList.front ()->m_retrans)
1349 {
1350 m_sentList.front ()->m_retrans = false;
1351 m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1352 }
1354}
1355
1356void
1358{
1359 if (m_sentList.size () > 0)
1360 {
1361 // If the head is sacked (reneging by the receiver the previously sent
1362 // information) we revert the sacked flag.
1363 // A sacked head means that we should advance SND.UNA.. so it's an error.
1364 if (m_sentList.front ()->m_sacked)
1365 {
1366 m_sentList.front ()->m_sacked = false;
1367 m_sackedOut -= m_sentList.front ()->m_packet->GetSize ();
1368 }
1369
1370 if (m_sentList.front ()->m_retrans)
1371 {
1372 m_sentList.front ()->m_retrans = false;
1373 m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1374 }
1375
1376 if (! m_sentList.front()->m_lost)
1377 {
1378 m_sentList.front()->m_lost = true;
1379 m_lostOut += m_sentList.front ()->m_packet->GetSize ();
1380 }
1381 }
1383}
1384
1385void
1387{
1388 NS_LOG_FUNCTION (this);
1389
1390 if (m_sackEnabled)
1391 {
1392 NS_ASSERT (m_sentList.size () > 1);
1393 }
1394 else
1395 {
1396 NS_ASSERT (m_sentList.size () > 0);
1397 }
1398
1399 m_renoSack = true;
1400
1401 // We can _never_ SACK the head, so start from the second segment sent
1402 auto it = ++m_sentList.begin ();
1403
1404 // Find the "highest sacked" point, that is SND.UNA + m_sackedOut
1405 while (it != m_sentList.end () && (*it)->m_sacked)
1406 {
1407 ++it;
1408 }
1409
1410 // Add to the sacked size the size of the first "not sacked" segment
1411 if (it != m_sentList.end ())
1412 {
1413 (*it)->m_sacked = true;
1414 m_sackedOut += (*it)->m_packet->GetSize ();
1415 m_highestSack = std::make_pair (it, (*it)->m_startSeq);
1416 NS_LOG_INFO ("Added a Reno SACK, status: " << *this);
1417 }
1418 else
1419 {
1420 NS_LOG_INFO ("Can't add a Reno SACK because we miss segments. This dupack"
1421 " should be arrived from spurious retransmissions");
1422 }
1423
1425}
1426
1427void
1429{
1430 static const bool enable = false;
1431
1432 if (!enable)
1433 {
1434 return;
1435 }
1436
1437 uint32_t sacked = 0;
1438 uint32_t lost = 0;
1439 uint32_t retrans = 0;
1440
1441 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1442 {
1443 if ((*it)->m_sacked)
1444 {
1445 sacked += (*it)->m_packet->GetSize ();
1446 }
1447 if ((*it)->m_lost)
1448 {
1449 lost += (*it)->m_packet->GetSize ();
1450 }
1451 if ((*it)->m_retrans)
1452 {
1453 retrans += (*it)->m_packet->GetSize ();
1454 }
1455 }
1456
1457 NS_ASSERT_MSG (sacked == m_sackedOut, "Counted SACK: " << sacked <<
1458 " stored SACK: " << m_sackedOut);
1459 NS_ASSERT_MSG (lost == m_lostOut, " Counted lost: " << lost <<
1460 " stored lost: " << m_lostOut);
1461 NS_ASSERT_MSG (retrans == m_retrans, " Counted retrans: " << retrans <<
1462 " stored retrans: " << m_retrans);
1463}
1464
1465std::ostream &
1466operator<< (std::ostream & os, TcpTxItem const & item)
1467{
1468 item.Print (os);
1469 return os;
1470}
1471
1472std::ostream &
1473operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf)
1474{
1475 TcpTxBuffer::PacketList::const_iterator it;
1476 std::stringstream ss;
1477 SequenceNumber32 beginOfCurrentPacket = tcpTxBuf.m_firstByteSeq;
1478 uint32_t sentSize = 0, appSize = 0;
1479
1481 for (it = tcpTxBuf.m_sentList.begin (); it != tcpTxBuf.m_sentList.end (); ++it)
1482 {
1483 p = (*it)->GetPacket ();
1484 ss << "{";
1485 (*it)->Print (ss);
1486 ss << "}";
1487 sentSize += p->GetSize ();
1488 beginOfCurrentPacket += p->GetSize ();
1489 }
1490
1491 for (it = tcpTxBuf.m_appList.begin (); it != tcpTxBuf.m_appList.end (); ++it)
1492 {
1493 appSize += (*it)->GetPacket ()->GetSize ();
1494 }
1495
1496 os << "Sent list: " << ss.str () << ", size = " << tcpTxBuf.m_sentList.size () <<
1497 " Total size: " << tcpTxBuf.m_size <<
1498 " m_firstByteSeq = " << tcpTxBuf.m_firstByteSeq <<
1499 " m_sentSize = " << tcpTxBuf.m_sentSize <<
1500 " m_retransOut = " << tcpTxBuf.m_retrans <<
1501 " m_lostOut = " << tcpTxBuf.m_lostOut <<
1502 " m_sackedOut = " << tcpTxBuf.m_sackedOut;
1503
1504 NS_ASSERT (sentSize == tcpTxBuf.m_sentSize);
1505 NS_ASSERT (tcpTxBuf.m_size - tcpTxBuf.m_sentSize == appSize);
1506 return os;
1507}
1508
1509} // namespace ns3
#define min(a, b)
Definition: 80211b.c:42
Callback template class.
Definition: callback.h:1279
bool IsNull(void) const
Check for null implementation.
Definition: callback.h:1386
A base class which provides memory management and object aggregation.
Definition: object.h:88
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:335
void RemoveAtStart(uint32_t size)
Remove size bytes from the start of the current packet.
Definition: packet.cc:362
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:227
void Print(std::ostream &os) const
Print the packet contents.
Definition: packet.cc:434
Ptr< Packet > Copy(void) const
performs a COW copy of the packet.
Definition: packet.cc:121
uint32_t GetSize(void) const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:856
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:195
std::list< SackBlock > SackList
SACK list definition.
Tcp sender buffer.
uint32_t m_retrans
Number of retransmitted bytes.
void ResetRenoSack()
Reset the SACKs.
uint32_t GetSacked(void) const
Get the number of segments that have been explicitly sacked by the receiver.
void ConsistencyCheck() const
Check if the values of sacked, lost, retrans, are in sync with the sent list.
static TypeId GetTypeId(void)
Get the type ID.
bool m_sackEnabled
Indicates if SACK is enabled on this connection.
uint32_t m_lostOut
Number of lost bytes.
bool IsLostRFC(const SequenceNumber32 &seq, const PacketList::const_iterator &segment) const
Decide if a segment is lost based on RFC 6675 algorithm.
std::pair< TcpTxBuffer::PacketList::const_iterator, SequenceNumber32 > FindHighestSacked() const
Find the highest SACK byte.
std::pair< PacketList::const_iterator, SequenceNumber32 > m_highestSack
Highest SACK byte.
bool IsHeadRetransmitted() const
Check if the head is retransmitted.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
void SetSegmentSize(uint32_t segmentSize)
Set the segment size.
uint32_t Available(void) const
Returns the available capacity of this buffer.
bool IsRetransmittedDataAcked(const SequenceNumber32 &ack) const
Checks whether the ack corresponds to retransmitted data.
TcpTxItem * GetPacketFromList(PacketList &list, const SequenceNumber32 &startingSeq, uint32_t numBytes, const SequenceNumber32 &requestedSeq, bool *listEdited=nullptr) const
Get a block (which is returned as Packet) from a list.
uint32_t SizeFromSequence(const SequenceNumber32 &seq) const
Returns the number of bytes from the buffer in the range [seq, tailSequence)
TracedValue< SequenceNumber32 > m_firstByteSeq
Sequence number of the first byte in data (SND.UNA)
std::list< TcpTxItem * > PacketList
container for data stored in the buffer
TcpTxItem * GetTransmittedSegment(uint32_t numBytes, const SequenceNumber32 &seq)
Get a block of data previously transmitted.
uint32_t m_maxBuffer
Max number of data bytes in buffer (SND.WND)
bool m_renoSack
Indicates if AddRenoSack was called.
uint32_t m_dupAckThresh
Duplicate Ack threshold from TcpSocketBase.
void SetSackEnabled(bool enabled)
tell tx-buffer whether SACK is used on this TCP socket
static Callback< void, TcpTxItem * > m_nullCb
Null callback for an item.
virtual ~TcpTxBuffer(void)
uint32_t m_sackedOut
Number of sacked bytes.
void ResetLastSegmentSent()
Take the last segment sent and put it back into the un-sent list (at the beginning)
bool IsLost(const SequenceNumber32 &seq) const
Check if a segment is lost.
bool NextSeg(SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
Get the next sequence number to transmit, according to RFC 6675.
uint32_t MaxBufferSize(void) const
Get the maximum buffer size.
uint32_t GetLost(void) const
Get the number of segments that we believe are lost in the network.
void SetDupAckThresh(uint32_t dupAckThresh)
Set the DupAckThresh.
TcpTxItem * CopyFromSequence(uint32_t numBytes, const SequenceNumber32 &seq)
Copy data from the range [seq, seq+numBytes) into a packet.
TcpTxItem * GetNewSegment(uint32_t numBytes)
Get a block of data not transmitted yet and move it into SentList.
uint32_t m_segmentSize
Segment size from TcpSocketBase.
uint32_t Update(const TcpOptionSack::SackList &list, const Callback< void, TcpTxItem * > &sackedCb=m_nullCb)
Update the scoreboard.
void AddRenoSack()
Emulate SACKs for SACKless connection: account for a new dupack.
void MarkHeadAsLost()
Mark the head of the sent list as lost.
void UpdateLostCount()
Update the lost count.
SequenceNumber32 TailSequence(void) const
Get the sequence number of the buffer tail (plus one)
Callback< uint32_t > m_rWndCallback
Callback to obtain RCV.WND value.
void SetRWndCallback(Callback< uint32_t > rWndCallback)
Set callback to obtain receiver window value.
uint32_t BytesInFlight() const
Return total bytes in flight.
uint32_t Size(void) const
Returns total number of bytes in this buffer.
SequenceNumber32 HeadSequence(void) const
Get the sequence number of the buffer head.
void SetSentListLost(bool resetSack=false)
Set the entire sent list as lost (typically after an RTO)
void SplitItems(TcpTxItem *t1, TcpTxItem *t2, uint32_t size) const
Split one TcpTxItem.
void DiscardUpTo(const SequenceNumber32 &seq, const Callback< void, TcpTxItem * > &beforeDelCb=m_nullCb)
Discard data up to but not including this sequence number.
uint32_t BytesInFlightRFC() const
Calculate the number of bytes in flight per RFC 6675.
PacketList m_appList
Buffer for application data.
void MergeItems(TcpTxItem *t1, TcpTxItem *t2) const
Merge two TcpTxItem.
uint32_t m_size
Size of all data in this buffer.
void ResetSentList()
Reset the sent list.
void DeleteRetransmittedFlagFromHead()
DeleteRetransmittedFlagFromHead.
void SetMaxBufferSize(uint32_t n)
Set the maximum buffer size.
bool IsSackEnabled(void) const
check whether SACK is used on the corresponding TCP socket
PacketList m_sentList
Buffer for sent (but not acked) data.
uint32_t m_sentSize
Size of sent (and not discarded) segments.
void RemoveFromCounts(TcpTxItem *item, uint32_t size)
Remove the size specified from the lostOut, retrans, sacked count.
void SetHeadSequence(const SequenceNumber32 &seq)
Set the head sequence of the buffer.
uint32_t GetRetransmitsCount(void) const
Return the number of segments in the sent list that have been transmitted more than once,...
TcpTxBuffer(uint32_t n=0)
Constructor.
Item that encloses the application packet and some flags for it.
Definition: tcp-tx-item.h:33
bool m_sacked
Indicates if the segment has been SACKed.
Definition: tcp-tx-item.h:113
bool m_retrans
Indicates if the segment is retransmitted.
Definition: tcp-tx-item.h:102
SequenceNumber32 m_startSeq
Sequence number of the item (if transmitted)
Definition: tcp-tx-item.h:109
Time m_lastSent
Timestamp of the time at which the segment has been sent last time.
Definition: tcp-tx-item.h:112
Ptr< Packet > GetPacketCopy(void) const
Get a copy of the Packet underlying this item.
Definition: tcp-tx-item.cc:80
bool m_lost
Indicates if the segment has been lost (RTO)
Definition: tcp-tx-item.h:111
Ptr< Packet > m_packet
Application packet (can be null)
Definition: tcp-tx-item.h:110
void Print(std::ostream &os, Time::Unit unit=Time::S) const
Print the time.
Definition: tcp-tx-item.cc:24
T Get(void) const
Get the underlying value.
Definition: traced-value.h:232
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:922
uint32_t segmentSize
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:67
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:88
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition: abort.h:144
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:257
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:265
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#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.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:139
uint32_t GetSize(Ptr< const Packet > packet, const WifiMacHeader *hdr, bool isAmpdu)
Return the total size of the packet after WifiMacHeader and FCS trailer have been added.
Definition: wifi-utils.cc:129
#define list
uint32_t pktSize
packet size used for the simulation (in bytes)
Definition: wifi-bianchi.cc:89