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