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