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