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