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