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