A Discrete-Event Network Simulator
API
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/tcp-option-ts.h"
27 
28 #include <algorithm>
29 #include <iostream>
30 
31 namespace ns3
32 {
33 
34 NS_LOG_COMPONENT_DEFINE("TcpTxBuffer");
35 NS_OBJECT_ENSURE_REGISTERED(TcpTxBuffer);
36 
37 Callback<void, TcpTxItem*> TcpTxBuffer::m_nullCb = MakeNullCallback<void, TcpTxItem*>();
38 
39 TypeId
41 {
42  static TypeId tid = TypeId("ns3::TcpTxBuffer")
43  .SetParent<Object>()
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 
100 uint32_t
102 {
103  return m_size;
104 }
105 
106 uint32_t
108 {
109  return m_maxBuffer;
110 }
111 
112 void
114 {
115  m_maxBuffer = n;
116 }
117 
118 bool
120 {
121  return m_sackEnabled;
122 }
123 
124 void
126 {
127  m_sackEnabled = enabled;
128 }
129 
130 uint32_t
132 {
133  return m_maxBuffer - m_size;
134 }
135 
136 void
137 TcpTxBuffer::SetDupAckThresh(uint32_t dupAckThresh)
138 {
139  m_dupAckThresh = dupAckThresh;
140 }
141 
142 void
144 {
146 }
147 
148 uint32_t
150 {
151  return m_retrans;
152 }
153 
154 uint32_t
156 {
157  return m_lostOut;
158 }
159 
160 uint32_t
162 {
163  return m_sackedOut;
164 }
165 
166 void
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 
182 bool
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 
206 uint32_t
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 
223 TcpTxItem*
224 TcpTxBuffer::CopyFromSequence(uint32_t numBytes, const SequenceNumber32& seq)
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() &&
264  m_firstByteSeq.Get().GetValue() + m_sentSize < seq.GetValue() + s)
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 
282 TcpTxItem*
283 TcpTxBuffer::GetNewSegment(uint32_t numBytes)
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 
306 TcpTxItem*
308 {
309  NS_LOG_FUNCTION(this << numBytes << seq);
310  NS_ASSERT(seq >= m_firstByteSeq);
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 
359 std::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 
381 void
382 TcpTxBuffer::SplitItems(TcpTxItem* t1, TcpTxItem* t2, uint32_t size) const
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 
401 TcpTxItem*
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;
447  NS_ASSERT_MSG(list != m_sentList || currentItem->m_startSeq >= m_firstByteSeq,
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 
588 void
590 {
591  NS_ASSERT(t1 != nullptr && t2 != nullptr);
592  NS_LOG_FUNCTION(this << *t1 << *t2);
593  NS_LOG_INFO("Merging " << *t2 << " into " << *t1);
594 
595  NS_ASSERT_MSG(t1->m_sacked == t2->m_sacked,
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 
629 void
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 
651 bool
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 
667 void
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
683  uint32_t pktSize;
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;
706  m_sentSize -= pktSize;
707  offset -= pktSize;
709 
710  RemoveFromCounts(item, pktSize);
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();
762  MarkHeadAsLost();
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);
778  NS_ASSERT(m_firstByteSeq >= seq);
781 }
782 
783 uint32_t
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);
866  UpdateLostCount();
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 
877 void
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 
926 bool
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 == true)
947  {
948  NS_LOG_INFO("seq=" << seq << " is lost because of lost flag");
949  return true;
950  }
951 
952  if ((*it)->m_sacked == true)
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 
965 bool
966 TcpTxBuffer::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 == false && item->m_sacked == false)
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 
1071 uint32_t
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 
1092 uint32_t
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 
1158 bool
1159 TcpTxBuffer::IsLostRFC(const SequenceNumber32& seq, const PacketList::const_iterator& segment) const
1160 {
1161  NS_LOG_FUNCTION(this << seq);
1162  uint32_t count = 0;
1163  uint32_t bytes = 0;
1164  PacketList::const_iterator it;
1165  TcpTxItem* item;
1166  Ptr<const Packet> current;
1167  SequenceNumber32 beginOfCurrentPacket = seq;
1168 
1169  if ((*segment)->m_sacked == true)
1170  {
1171  return false;
1172  }
1173 
1174  // From RFC 6675:
1175  // > The routine returns true when either dupThresh discontiguous SACKed
1176  // > sequences have arrived above 'seq' or more than (dupThresh - 1) * SMSS bytes
1177  // > with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the
1178  // > routine returns false.
1179  for (it = segment; it != m_sentList.end(); ++it)
1180  {
1181  item = *it;
1182  current = item->m_packet;
1183 
1184  if (item->m_sacked)
1185  {
1186  NS_LOG_INFO("Segment " << *item << " 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 
1217 void
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 
1231 void
1233 {
1234  NS_LOG_FUNCTION(this);
1235  m_rWndCallback = rWndCallback;
1236 }
1237 
1238 void
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 
1260 void
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  }
1276  ConsistencyCheck();
1277 }
1278 
1279 void
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);
1323  ConsistencyCheck();
1324 }
1325 
1326 bool
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 
1339 void
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  }
1354  ConsistencyCheck();
1355 }
1356 
1357 void
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  }
1383  ConsistencyCheck();
1384 }
1385 
1386 void
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 
1425  ConsistencyCheck();
1426 }
1427 
1428 void
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 
1465 std::ostream&
1466 operator<<(std::ostream& os, const TcpTxItem& item)
1467 {
1468  item.Print(os);
1469  return os;
1470 }
1471 
1472 std::ostream&
1473 operator<<(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
#define min(a, b)
Definition: 80211b.c:42
Callback template class.
Definition: callback.h:443
bool IsNull() const
Check for null implementation.
Definition: callback.h:572
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:863
void RemoveAtStart(uint32_t size)
Remove size bytes from the start of the current packet.
Definition: packet.cc:384
Ptr< Packet > Copy() const
performs a COW copy of the packet.
Definition: packet.cc:131
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
void Print(std::ostream &os) const
Print the packet contents.
Definition: packet.cc:456
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:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
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)