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}
584
585void
587{
588 NS_ASSERT (t1 != nullptr && t2 != nullptr);
589 NS_LOG_FUNCTION (this << *t1 << *t2);
590 NS_LOG_INFO ("Merging " << *t2 << " into " << *t1);
591
592 NS_ASSERT_MSG (t1->m_sacked == t2->m_sacked,
593 "Merging one sacked and another not sacked. Impossible");
594 NS_ASSERT_MSG (t1->m_lost == t2->m_lost,
595 "Merging one lost and another not lost. Impossible");
596
597 // If one is retrans and the other is not, cancel the retransmitted flag.
598 // We are merging this segment for the retransmit, so the count will
599 // be updated in MarkTransmittedSegment.
600 if (t1->m_retrans != t2->m_retrans)
601 {
602 if (t1->m_retrans)
603 {
604 TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
605 self->m_retrans -= t1->m_packet->GetSize ();
606 t1->m_retrans = false;
607 }
608 else
609 {
610 NS_ASSERT (t2->m_retrans);
611 TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
612 self->m_retrans -= t2->m_packet->GetSize ();
613 t2->m_retrans = false;
614 }
615 }
616
617 if (t1->m_lastSent < t2->m_lastSent)
618 {
619 t1->m_lastSent = t2->m_lastSent;
620 }
621
622 t1->m_packet->AddAtEnd (t2->m_packet);
623
624 NS_LOG_INFO ("Situation after the merge: " << *t1);
625}
626
627void
629{
630 NS_LOG_FUNCTION (this << *item << size);
631 if (item->m_sacked)
632 {
633 NS_ASSERT (m_sackedOut >= size);
634 m_sackedOut -= size;
635 }
636 if (item->m_retrans)
637 {
638 NS_ASSERT (m_retrans >= size);
639 m_retrans -= size;
640 }
641 if (item->m_lost)
642 {
643 NS_ASSERT_MSG (m_lostOut >= size, "Trying to remove " << size <<
644 " bytes from " << m_lostOut);
645 m_lostOut -= size;
646 }
647}
648
649bool
651{
652 NS_LOG_FUNCTION (this);
653 for (const auto &it : m_sentList)
654 {
655 TcpTxItem *item = it;
656 Ptr<Packet> p = item->m_packet;
657 if (item->m_startSeq + p->GetSize () == ack && !item->m_sacked && item->m_retrans)
658 {
659 return true;
660 }
661 }
662 return false;
663}
664
665void
667 const Callback<void, TcpTxItem *> &beforeDelCb)
668{
669 NS_LOG_FUNCTION (this << seq);
670
671 // Cases do not need to scan the buffer
672 if (m_firstByteSeq >= seq)
673 {
674 NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
675 return;
676 }
677 NS_LOG_DEBUG ("Remove up to " << seq << " lost: " << m_lostOut <<
678 " retrans: " << m_retrans << " sacked: " << m_sackedOut);
679
680 // Scan the buffer and discard packets
681 uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
683 PacketList::iterator i = m_sentList.begin ();
684 while (m_size > 0 && offset > 0)
685 {
686 if (i == m_sentList.end ())
687 {
688 // Move data from app list to sent list, so we can delete the item
689 [[maybe_unused]] Ptr<Packet> p = CopyFromSequence (offset, m_firstByteSeq)->GetPacketCopy ();
690 NS_ASSERT (p != nullptr);
691 i = m_sentList.begin ();
692 NS_ASSERT (i != m_sentList.end ());
693 }
694 TcpTxItem *item = *i;
695 Ptr<Packet> p = item->m_packet;
696 pktSize = p->GetSize ();
698 "Item starts at " << item->m_startSeq <<
699 " while SND.UNA is " << m_firstByteSeq << " from " << *this);
700
701 if (offset >= pktSize)
702 { // This packet is behind the seqnum. Remove this packet from the buffer
703 m_size -= pktSize;
705 offset -= pktSize;
707
709
710 i = m_sentList.erase (i);
711 NS_LOG_INFO ("Removed " << *item << " lost: " << m_lostOut <<
712 " retrans: " << m_retrans << " sacked: " << m_sackedOut <<
713 ". Remaining data " << m_size);
714
715 if (!beforeDelCb.IsNull ())
716 {
717 // Inform Rate algorithms only when a full packet is ACKed
718 beforeDelCb (item);
719 }
720
721 delete item;
722 }
723 else if (offset > 0)
724 { // Part of the packet is behind the seqnum. Fragment
725 pktSize -= offset;
726 NS_LOG_INFO (*item);
727 // PacketTags are preserved when fragmenting
728 item->m_packet = item->m_packet->CreateFragment (offset, pktSize);
729 item->m_startSeq += offset;
730 m_size -= offset;
731 m_sentSize -= offset;
732 m_firstByteSeq += offset;
733
734 RemoveFromCounts (item, offset);
735
736 NS_LOG_INFO ("Fragmented one packet by size " << offset <<
737 ", new size=" << pktSize << " resulting item is " <<
738 *item << " status: " << *this);
739 break;
740 }
741 }
742 // Catching the case of ACKing a FIN
743 if (m_size == 0)
744 {
745 m_firstByteSeq = seq;
746 }
747
748 if (!m_sentList.empty ())
749 {
750 TcpTxItem *head = m_sentList.front ();
751 if (head->m_sacked)
752 {
753 NS_ASSERT (!head->m_lost);
754 // It is not possible to have the UNA sacked; otherwise, it would
755 // have been ACKed. This is, most likely, our wrong guessing
756 // when adding Reno dupacks in the count.
757 head->m_sacked = false;
758 m_sackedOut -= head->m_packet->GetSize ();
759 NS_LOG_INFO ("Moving the SACK flag from the HEAD to another segment");
760 AddRenoSack ();
762 }
763
764 NS_ASSERT_MSG (head->m_startSeq == seq,
765 "While removing up to " << seq << " we get SND.UNA to " <<
766 m_firstByteSeq << " this is the result: " << *this);
767 }
768
769 if (m_highestSack.second <= m_firstByteSeq)
770 {
771 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
772 }
773
774 NS_LOG_DEBUG ("Discarded up to " << seq << " lost: " << m_lostOut <<
775 " retrans: " << m_retrans << " sacked: " << m_sackedOut);
776 NS_LOG_LOGIC ("Buffer status after discarding data " << *this);
777 NS_ASSERT (m_firstByteSeq >= seq);
780}
781
784 const Callback<void, TcpTxItem *> &sackedCb)
785{
786 NS_LOG_FUNCTION (this);
787 NS_LOG_INFO ("Updating scoreboard, got " << list.size () << " blocks to analyze");
788
789 uint32_t bytesSacked = 0;
790
791 for (auto option_it = list.begin (); option_it != list.end (); ++option_it)
792 {
793 PacketList::iterator item_it = m_sentList.begin ();
794 SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
795
796 if (m_firstByteSeq + m_sentSize < (*option_it).first)
797 {
798 NS_LOG_INFO ("Not updating scoreboard, the option block is outside the sent list");
799 return bytesSacked;
800 }
801
802 while (item_it != m_sentList.end ())
803 {
804 uint32_t pktSize = (*item_it)->m_packet->GetSize ();
805
806 // Check the boundary of this packet ... only mark as sacked if
807 // it is precisely mapped over the option. It means that if the receiver
808 // is reporting as sacked single range bytes that are not mapped 1:1
809 // in what we have, the option is discarded. There's room for improvement
810 // here.
811 if (beginOfCurrentPacket >= (*option_it).first
812 && beginOfCurrentPacket + pktSize <= (*option_it).second)
813 {
814 if ((*item_it)->m_sacked)
815 {
816 NS_ASSERT (!(*item_it)->m_lost);
817 NS_LOG_INFO ("Received block " << *option_it <<
818 ", checking sentList for block " << *(*item_it) <<
819 ", found in the sackboard already sacked");
820 }
821 else
822 {
823 if ((*item_it)->m_lost)
824 {
825 (*item_it)->m_lost = false;
826 m_lostOut -= (*item_it)->m_packet->GetSize ();
827 }
828
829 (*item_it)->m_sacked = true;
830 m_sackedOut += (*item_it)->m_packet->GetSize ();
831 bytesSacked += (*item_it)->m_packet->GetSize ();
832
833 if (m_highestSack.first == m_sentList.end()
834 || m_highestSack.second <= beginOfCurrentPacket + pktSize)
835 {
836 m_highestSack = std::make_pair (item_it, beginOfCurrentPacket);
837 }
838
839 NS_LOG_INFO ("Received block " << *option_it <<
840 ", checking sentList for block " << *(*item_it) <<
841 ", found in the sackboard, sacking, current highSack: " <<
842 m_highestSack.second);
843
844 if (!sackedCb.IsNull ())
845 {
846 sackedCb (*item_it);
847 }
848 }
849 }
850 else if (beginOfCurrentPacket + pktSize > (*option_it).second)
851 {
852 // We already passed the received block end. Exit from the loop
853 NS_LOG_INFO ("Received block [" << *option_it <<
854 ", checking sentList for block " << *(*item_it) <<
855 "], not found, breaking loop");
856 break;
857 }
858
859 beginOfCurrentPacket += pktSize;
860 ++item_it;
861 }
862 }
863
864 if (bytesSacked > 0)
865 {
866 NS_ASSERT_MSG (m_highestSack.first != m_sentList.end(), "Buffer status: " << *this);
868 }
869
870 NS_ASSERT ((*(m_sentList.begin ()))->m_sacked == false);
872 //NS_ASSERT (list.size () == 0 || modified); // Assert for duplicated SACK or
873 // impossiblity to map the option into the sent blocks
875 return bytesSacked;
876}
877
878void
880{
881 NS_LOG_FUNCTION (this);
882 uint32_t sacked = 0;
883 SequenceNumber32 beginOfCurrentPacket = m_highestSack.second;
884 if (m_highestSack.first == m_sentList.end ())
885 {
886 NS_LOG_INFO ("Status before the update: " << *this <<
887 ", will start from the latest sent item");
888 }
889 else
890 {
891 NS_LOG_INFO ("Status before the update: " << *this <<
892 ", will start from item " << *(*m_highestSack.first));
893 }
894
895 for (auto it = m_highestSack.first; it != m_sentList.begin(); --it)
896 {
897 TcpTxItem *item = *it;
898 if (item->m_sacked)
899 {
900 sacked++;
901 }
902
903 if (sacked >= m_dupAckThresh)
904 {
905 if (!item->m_sacked && !item->m_lost)
906 {
907 item->m_lost = true;
908 m_lostOut += item->m_packet->GetSize ();
909 }
910 }
911 beginOfCurrentPacket -= item->m_packet->GetSize ();
912 }
913
914 if (sacked >= m_dupAckThresh)
915 {
916 TcpTxItem *item = *m_sentList.begin ();
917 if (!item->m_lost)
918 {
919 item->m_lost = true;
920 m_lostOut += item->m_packet->GetSize ();
921 }
922 }
923 NS_LOG_INFO ("Status after the update: " << *this);
925}
926
927bool
929{
930 NS_LOG_FUNCTION (this << seq);
931
932 SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
933 PacketList::const_iterator it;
934
935 if (seq >= m_highestSack.second)
936 {
937 return false;
938 }
939
940 // In theory, using a map and hints when inserting elements can improve
941 // performance
942 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
943 {
944 // Search for the right iterator before calling IsLost()
945 if (beginOfCurrentPacket >= seq)
946 {
947 if ((*it)->m_lost == true)
948 {
949 NS_LOG_INFO ("seq=" << seq << " is lost because of lost flag");
950 return true;
951 }
952
953 if ((*it)->m_sacked == true)
954 {
955 NS_LOG_INFO ("seq=" << seq << " is not lost because of sacked flag");
956 return false;
957 }
958 }
959
960 beginOfCurrentPacket += (*it)->m_packet->GetSize ();
961 }
962
963 return false;
964}
965
966bool
967TcpTxBuffer::NextSeg (SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
968{
969 NS_LOG_FUNCTION (this << isRecovery);
970 /* RFC 6675, NextSeg definition.
971 *
972 * (1) If there exists a smallest unSACKed sequence number 'S2' that
973 * meets the following three criteria for determining loss, the
974 * sequence range of one segment of up to SMSS octets starting
975 * with S2 MUST be returned.
976 *
977 * (1.a) S2 is greater than HighRxt.
978 *
979 * (1.b) S2 is less than the highest octet covered by any
980 * received SACK.
981 *
982 * (1.c) IsLost (S2) returns true.
983 */
984 PacketList::const_iterator it;
985 TcpTxItem *item;
986 SequenceNumber32 seqPerRule3;
987 bool isSeqPerRule3Valid = false;
988 SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
989
990 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
991 {
992 item = *it;
993
994 // Condition 1.a , 1.b , and 1.c
995 if (item->m_retrans == false && item->m_sacked == false)
996 {
997 if (item->m_lost)
998 {
999 NS_LOG_INFO("IsLost, returning" << beginOfCurrentPkt);
1000 *seq = beginOfCurrentPkt;
1001 *seqHigh = *seq + m_segmentSize;
1002 return true;
1003 }
1004 else if (seqPerRule3.GetValue () == 0 && isRecovery)
1005 {
1006 NS_LOG_INFO ("Saving for rule 3 the seq " << beginOfCurrentPkt);
1007 isSeqPerRule3Valid = true;
1008 seqPerRule3 = beginOfCurrentPkt;
1009 }
1010 }
1011
1012 // Nothing found, iterate
1013 beginOfCurrentPkt += item->m_packet->GetSize ();
1014 }
1015
1016 /* (2) If no sequence number 'S2' per rule (1) exists but there
1017 * exists available unsent data and the receiver's advertised
1018 * window allows, the sequence range of one segment of up to SMSS
1019 * octets of previously unsent data starting with sequence number
1020 * HighData+1 MUST be returned.
1021 */
1023 {
1024 if (m_sentSize <= m_rWndCallback ())
1025 {
1026 NS_LOG_INFO ("There is unsent data. Send it");
1027 *seq = m_firstByteSeq + m_sentSize;
1028 *seqHigh = *seq + std::min<uint32_t> (m_segmentSize, (m_rWndCallback () - m_sentSize));
1029 return true;
1030 }
1031 else
1032 {
1033 NS_LOG_INFO ("There is no available receiver window to send");
1034 return false;
1035 }
1036 }
1037 else
1038 {
1039 NS_LOG_INFO ("There isn't unsent data.");
1040 }
1041
1042 /* (3) If the conditions for rules (1) and (2) fail, but there exists
1043 * an unSACKed sequence number 'S3' that meets the criteria for
1044 * detecting loss given in steps (1.a) and (1.b) above
1045 * (specifically excluding step (1.c)), then one segment of up to
1046 * SMSS octets starting with S3 SHOULD be returned.
1047 */
1048 if (isSeqPerRule3Valid)
1049 {
1050 NS_LOG_INFO ("Rule3 valid. " << seqPerRule3);
1051 *seq = seqPerRule3;
1052 *seqHigh = *seq + m_segmentSize;
1053 return true;
1054 }
1055
1056 /* (4) If the conditions for (1), (2), and (3) fail, but there exists
1057 * outstanding unSACKed data, we provide the opportunity for a
1058 * single "rescue" retransmission per entry into loss recovery.
1059 * If HighACK is greater than RescueRxt (or RescueRxt is
1060 * undefined), then one segment of up to SMSS octets that MUST
1061 * include the highest outstanding unSACKed sequence number
1062 * SHOULD be returned, and RescueRxt set to RecoveryPoint.
1063 * HighRxt MUST NOT be updated.
1064 *
1065 * This point require too much interaction between us and TcpSocketBase.
1066 * We choose to not respect the SHOULD (allowed from RFC MUST/SHOULD definition)
1067 */
1068 NS_LOG_INFO ("Can't return anything");
1069 return false;
1070}
1071
1074{
1076 "Count of sacked " << m_sackedOut << " and lost " << m_lostOut <<
1077 " is out of sync with sent list size " << m_sentSize <<
1078 " " << *this);
1079 uint32_t leftOut = m_sackedOut + m_lostOut;
1080 uint32_t retrans = m_retrans;
1081
1082 NS_LOG_INFO ("Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1083 " retrans: " << retrans);
1084 uint32_t in_flight = m_sentSize - leftOut + retrans;
1085
1086 //uint32_t rfc_in_flight = BytesInFlightRFC (3, segmentSize);
1087 //NS_ASSERT_MSG(in_flight == rfc_in_flight,
1088 // "Calculated: " << in_flight << " RFC: " << rfc_in_flight <<
1089 // "Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1090 // " retrans: " << retrans);
1091 return in_flight;
1092}
1093
1096{
1097 PacketList::const_iterator it;
1098 TcpTxItem *item;
1099 uint32_t size = 0; // "pipe" in RFC
1100 SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
1101 uint32_t sackedOut = 0;
1102 uint32_t lostOut = 0;
1103 uint32_t retrans = 0;
1104 uint32_t totalSize = 0;
1105
1106 // After initializing pipe to zero, the following steps are taken for each
1107 // octet 'S1' in the sequence space between HighACK and HighData that has not
1108 // been SACKed:
1109 for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
1110 {
1111 item = *it;
1112 totalSize += item->m_packet->GetSize();
1113 if (!item->m_sacked)
1114 {
1115 bool isLost = IsLostRFC (beginOfCurrentPkt, it);
1116 // (a) If IsLost (S1) returns false: Pipe is incremented by 1 octet.
1117 if (!isLost)
1118 {
1119 size += item->m_packet->GetSize ();
1120 }
1121 // (b) If S1 <= HighRxt: Pipe is incremented by 1 octet.
1122 // (NOTE: we use the m_retrans flag instead of keeping and updating
1123 // another variable). Only if the item is not marked as lost
1124 else if (item->m_retrans)
1125 {
1126 size += item->m_packet->GetSize ();
1127 }
1128
1129 if (isLost)
1130 {
1131 lostOut += item->m_packet->GetSize ();
1132 }
1133 }
1134 else
1135 {
1136 sackedOut += item->m_packet->GetSize ();
1137 }
1138
1139 if (item->m_retrans)
1140 {
1141 retrans += item->m_packet->GetSize ();
1142 }
1143 beginOfCurrentPkt += item->m_packet->GetSize ();
1144 }
1145
1146 NS_ASSERT_MSG(lostOut == m_lostOut, "Lost counted: " << lostOut << " " <<
1147 m_lostOut << "\n" << *this);
1148 NS_ASSERT_MSG(retrans == m_retrans, "Retrans Counted: " << retrans << " " <<
1149 m_retrans << "\n" << *this);
1150 NS_ASSERT_MSG(sackedOut == m_sackedOut, "Sacked counted: " << sackedOut <<
1151 " " << m_sackedOut << *this);
1152 NS_ASSERT_MSG(totalSize == m_sentSize,
1153 "Sent size counted: " << totalSize << " " << m_sentSize << *this);
1154
1155 return size;
1156}
1157
1158bool
1159TcpTxBuffer::IsLostRFC (const SequenceNumber32 &seq, const PacketList::const_iterator &segment) const
1160{
1161 NS_LOG_FUNCTION (this << seq);
1162 uint32_t count = 0;
1163 uint32_t bytes = 0;
1164 PacketList::const_iterator it;
1165 TcpTxItem *item;
1166 Ptr<const Packet> current;
1167 SequenceNumber32 beginOfCurrentPacket = seq;
1168
1169 if ((*segment)->m_sacked == true)
1170 {
1171 return false;
1172 }
1173
1174 // From RFC 6675:
1175 // > The routine returns true when either dupThresh discontiguous SACKed
1176 // > sequences have arrived above 'seq' or more than (dupThresh - 1) * SMSS bytes
1177 // > with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the
1178 // > routine returns false.
1179 for (it = segment; it != m_sentList.end (); ++it)
1180 {
1181 item = *it;
1182 current = item->m_packet;
1183
1184 if (item->m_sacked)
1185 {
1186 NS_LOG_INFO ("Segment " << *item <<
1187 " found to be SACKed while checking for " << seq);
1188 ++count;
1189 bytes += current->GetSize ();
1190 if ((count >= m_dupAckThresh) || (bytes > (m_dupAckThresh-1) * m_segmentSize))
1191 {
1192 NS_LOG_INFO ("seq=" << seq << " is lost because of 3 sacked blocks ahead");
1193 return true;
1194 }
1195 }
1196
1197 if (beginOfCurrentPacket >= m_highestSack.second)
1198 {
1199 if (item->m_lost && !item->m_retrans)
1200 return true;
1201
1202 NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead");
1203 return false;
1204 }
1205
1206 beginOfCurrentPacket += current->GetSize ();
1207 }
1208 if (it == m_highestSack.first)
1209 {
1210 NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead " << m_highestSack.second);
1211 }
1212 return false;
1213}
1214
1215void
1217{
1218 NS_LOG_FUNCTION (this);
1219
1220 m_sackedOut = 0;
1221 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1222 {
1223 (*it)->m_sacked = false;
1224 }
1225
1226 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1227}
1228
1229void
1231{
1232 NS_LOG_FUNCTION (this);
1233 m_rWndCallback = rWndCallback;
1234}
1235
1236void
1238{
1239 NS_LOG_FUNCTION (this);
1240 TcpTxItem *item;
1241
1242 // Keep the head items; they will then marked as lost
1243 while (m_sentList.size () > 0)
1244 {
1245 item = m_sentList.back ();
1246 item->m_retrans = item->m_sacked = item->m_lost = false;
1247 m_appList.push_front (item);
1248 m_sentList.pop_back ();
1249 }
1250
1251 m_sentSize = 0;
1252 m_lostOut = 0;
1253 m_retrans = 0;
1254 m_sackedOut = 0;
1255 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1256}
1257
1258void
1260{
1261 NS_LOG_FUNCTION (this);
1262 if (!m_sentList.empty ())
1263 {
1264 TcpTxItem *item = m_sentList.back ();
1265
1266 m_sentList.pop_back ();
1267 m_sentSize -= item->m_packet->GetSize ();
1268 if (item->m_retrans)
1269 {
1270 m_retrans -= item->m_packet->GetSize ();
1271 }
1272 m_appList.insert (m_appList.begin (), item);
1273 }
1275}
1276
1277void
1279{
1280 NS_LOG_FUNCTION (this);
1281 m_retrans = 0;
1282
1283 if (resetSack)
1284 {
1285 m_sackedOut = 0;
1287 m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1288 }
1289 else
1290 {
1291 m_lostOut = 0;
1292 }
1293
1294 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1295 {
1296 if (resetSack)
1297 {
1298 (*it)->m_sacked = false;
1299 (*it)->m_lost = true;
1300 }
1301 else
1302 {
1303 if ((*it)->m_lost)
1304 {
1305 // Have to increment it because we set it to 0 at line 1133
1306 m_lostOut += (*it)->m_packet->GetSize ();
1307 }
1308 else if (!(*it)->m_sacked)
1309 {
1310 // Packet is not marked lost, nor is sacked. Then it becomes lost.
1311 (*it)->m_lost = true;
1312 m_lostOut += (*it)->m_packet->GetSize ();
1313 }
1314 }
1315
1316 (*it)->m_retrans = false;
1317 }
1318
1319 NS_LOG_INFO ("Set sent list lost, status: " << *this);
1322}
1323
1324bool
1326{
1327 NS_LOG_FUNCTION (this);
1328
1329 if (m_sentSize == 0)
1330 {
1331 return false;
1332 }
1333
1334 return m_sentList.front ()->m_retrans;
1335}
1336
1337void
1339{
1340 NS_LOG_FUNCTION (this);
1341
1342 if (m_sentSize == 0)
1343 {
1344 return;
1345 }
1346
1347 if (m_sentList.front ()->m_retrans)
1348 {
1349 m_sentList.front ()->m_retrans = false;
1350 m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1351 }
1353}
1354
1355void
1357{
1358 if (m_sentList.size () > 0)
1359 {
1360 // If the head is sacked (reneging by the receiver the previously sent
1361 // information) we revert the sacked flag.
1362 // A sacked head means that we should advance SND.UNA.. so it's an error.
1363 if (m_sentList.front ()->m_sacked)
1364 {
1365 m_sentList.front ()->m_sacked = false;
1366 m_sackedOut -= m_sentList.front ()->m_packet->GetSize ();
1367 }
1368
1369 if (m_sentList.front ()->m_retrans)
1370 {
1371 m_sentList.front ()->m_retrans = false;
1372 m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1373 }
1374
1375 if (! m_sentList.front()->m_lost)
1376 {
1377 m_sentList.front()->m_lost = true;
1378 m_lostOut += m_sentList.front ()->m_packet->GetSize ();
1379 }
1380 }
1382}
1383
1384void
1386{
1387 NS_LOG_FUNCTION (this);
1388
1389 if (m_sackEnabled)
1390 {
1391 NS_ASSERT (m_sentList.size () > 1);
1392 }
1393 else
1394 {
1395 NS_ASSERT (m_sentList.size () > 0);
1396 }
1397
1398 m_renoSack = true;
1399
1400 // We can _never_ SACK the head, so start from the second segment sent
1401 auto it = ++m_sentList.begin ();
1402
1403 // Find the "highest sacked" point, that is SND.UNA + m_sackedOut
1404 while (it != m_sentList.end () && (*it)->m_sacked)
1405 {
1406 ++it;
1407 }
1408
1409 // Add to the sacked size the size of the first "not sacked" segment
1410 if (it != m_sentList.end ())
1411 {
1412 (*it)->m_sacked = true;
1413 m_sackedOut += (*it)->m_packet->GetSize ();
1414 m_highestSack = std::make_pair (it, (*it)->m_startSeq);
1415 NS_LOG_INFO ("Added a Reno SACK, status: " << *this);
1416 }
1417 else
1418 {
1419 NS_LOG_INFO ("Can't add a Reno SACK because we miss segments. This dupack"
1420 " should be arrived from spurious retransmissions");
1421 }
1422
1424}
1425
1426void
1428{
1429 static const bool enable = false;
1430
1431 if (!enable)
1432 {
1433 return;
1434 }
1435
1436 uint32_t sacked = 0;
1437 uint32_t lost = 0;
1438 uint32_t retrans = 0;
1439
1440 for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1441 {
1442 if ((*it)->m_sacked)
1443 {
1444 sacked += (*it)->m_packet->GetSize ();
1445 }
1446 if ((*it)->m_lost)
1447 {
1448 lost += (*it)->m_packet->GetSize ();
1449 }
1450 if ((*it)->m_retrans)
1451 {
1452 retrans += (*it)->m_packet->GetSize ();
1453 }
1454 }
1455
1456 NS_ASSERT_MSG (sacked == m_sackedOut, "Counted SACK: " << sacked <<
1457 " stored SACK: " << m_sackedOut);
1458 NS_ASSERT_MSG (lost == m_lostOut, " Counted lost: " << lost <<
1459 " stored lost: " << m_lostOut);
1460 NS_ASSERT_MSG (retrans == m_retrans, " Counted retrans: " << retrans <<
1461 " stored retrans: " << m_retrans);
1462}
1463
1464std::ostream &
1465operator<< (std::ostream & os, TcpTxItem const & item)
1466{
1467 item.Print (os);
1468 return os;
1469}
1470
1471std::ostream &
1472operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf)
1473{
1474 TcpTxBuffer::PacketList::const_iterator it;
1475 std::stringstream ss;
1476 SequenceNumber32 beginOfCurrentPacket = tcpTxBuf.m_firstByteSeq;
1477 uint32_t sentSize = 0, appSize = 0;
1478
1480 for (it = tcpTxBuf.m_sentList.begin (); it != tcpTxBuf.m_sentList.end (); ++it)
1481 {
1482 p = (*it)->GetPacket ();
1483 ss << "{";
1484 (*it)->Print (ss);
1485 ss << "}";
1486 sentSize += p->GetSize ();
1487 beginOfCurrentPacket += p->GetSize ();
1488 }
1489
1490 for (it = tcpTxBuf.m_appList.begin (); it != tcpTxBuf.m_appList.end (); ++it)
1491 {
1492 appSize += (*it)->GetPacket ()->GetSize ();
1493 }
1494
1495 os << "Sent list: " << ss.str () << ", size = " << tcpTxBuf.m_sentList.size () <<
1496 " Total size: " << tcpTxBuf.m_size <<
1497 " m_firstByteSeq = " << tcpTxBuf.m_firstByteSeq <<
1498 " m_sentSize = " << tcpTxBuf.m_sentSize <<
1499 " m_retransOut = " << tcpTxBuf.m_retrans <<
1500 " m_lostOut = " << tcpTxBuf.m_lostOut <<
1501 " m_sackedOut = " << tcpTxBuf.m_sackedOut;
1502
1503 NS_ASSERT (sentSize == tcpTxBuf.m_sentSize);
1504 NS_ASSERT (tcpTxBuf.m_size - tcpTxBuf.m_sentSize == appSize);
1505 return os;
1506}
1507
1508} // 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