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 void
587 {
588  NS_ASSERT (t1 != nullptr && t2 != nullptr);
589  NS_LOG_FUNCTION (this << *t1 << *t2);
590  NS_LOG_INFO ("Merging " << *t2 << " into " << *t1);
591 
592  NS_ASSERT_MSG (t1->m_sacked == t2->m_sacked,
593  "Merging one sacked and another not sacked. Impossible");
594  NS_ASSERT_MSG (t1->m_lost == t2->m_lost,
595  "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  TcpTxBuffer *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  TcpTxBuffer *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, "Trying to remove " << size <<
644  " 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  const Callback<void, TcpTxItem *> &beforeDelCb)
668 {
669  NS_LOG_FUNCTION (this << seq);
670 
671  // Cases do not need to scan the buffer
672  if (m_firstByteSeq >= seq)
673  {
674  NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
675  return;
676  }
677  NS_LOG_DEBUG ("Remove up to " << seq << " lost: " << m_lostOut <<
678  " retrans: " << m_retrans << " sacked: " << m_sackedOut);
679 
680  // Scan the buffer and discard packets
681  uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
682  uint32_t pktSize;
683  PacketList::iterator i = m_sentList.begin ();
684  while (m_size > 0 && offset > 0)
685  {
686  if (i == m_sentList.end ())
687  {
688  // Move data from app list to sent list, so we can delete the item
690  NS_ASSERT (p != nullptr);
691  NS_UNUSED (p);
692  i = m_sentList.begin ();
693  NS_ASSERT (i != m_sentList.end ());
694  }
695  TcpTxItem *item = *i;
696  Ptr<Packet> p = item->m_packet;
697  pktSize = p->GetSize ();
699  "Item starts at " << item->m_startSeq <<
700  " while SND.UNA is " << m_firstByteSeq << " from " << *this);
701 
702  if (offset >= pktSize)
703  { // This packet is behind the seqnum. Remove this packet from the buffer
704  m_size -= pktSize;
705  m_sentSize -= pktSize;
706  offset -= pktSize;
708 
709  RemoveFromCounts (item, pktSize);
710 
711  i = m_sentList.erase (i);
712  NS_LOG_INFO ("Removed " << *item << " lost: " << m_lostOut <<
713  " retrans: " << m_retrans << " sacked: " << m_sackedOut <<
714  ". Remaining data " << m_size);
715 
716  if (!beforeDelCb.IsNull ())
717  {
718  // Inform Rate algorithms only when a full packet is ACKed
719  beforeDelCb (item);
720  }
721 
722  delete item;
723  }
724  else if (offset > 0)
725  { // Part of the packet is behind the seqnum. Fragment
726  pktSize -= offset;
727  NS_LOG_INFO (*item);
728  // PacketTags are preserved when fragmenting
729  item->m_packet = item->m_packet->CreateFragment (offset, pktSize);
730  item->m_startSeq += offset;
731  m_size -= offset;
732  m_sentSize -= offset;
733  m_firstByteSeq += offset;
734 
735  RemoveFromCounts (item, offset);
736 
737  NS_LOG_INFO ("Fragmented one packet by size " << offset <<
738  ", new size=" << pktSize << " resulting item is " <<
739  *item << " status: " << *this);
740  break;
741  }
742  }
743  // Catching the case of ACKing a FIN
744  if (m_size == 0)
745  {
746  m_firstByteSeq = seq;
747  }
748 
749  if (!m_sentList.empty ())
750  {
751  TcpTxItem *head = m_sentList.front ();
752  if (head->m_sacked)
753  {
754  NS_ASSERT (!head->m_lost);
755  // It is not possible to have the UNA sacked; otherwise, it would
756  // have been ACKed. This is, most likely, our wrong guessing
757  // when adding Reno dupacks in the count.
758  head->m_sacked = false;
759  m_sackedOut -= head->m_packet->GetSize ();
760  NS_LOG_INFO ("Moving the SACK flag from the HEAD to another segment");
761  AddRenoSack ();
762  MarkHeadAsLost ();
763  }
764 
765  NS_ASSERT_MSG (head->m_startSeq == seq,
766  "While removing up to " << seq << " we get SND.UNA to " <<
767  m_firstByteSeq << " this is the result: " << *this);
768  }
769 
770  if (m_highestSack.second <= m_firstByteSeq)
771  {
772  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
773  }
774 
775  NS_LOG_DEBUG ("Discarded up to " << seq << " lost: " << m_lostOut <<
776  " retrans: " << m_retrans << " sacked: " << m_sackedOut);
777  NS_LOG_LOGIC ("Buffer status after discarding data " << *this);
778  NS_ASSERT (m_firstByteSeq >= seq);
780  ConsistencyCheck ();
781 }
782 
783 uint32_t
785  const Callback<void, TcpTxItem *> &sackedCb)
786 {
787  NS_LOG_FUNCTION (this);
788  NS_LOG_INFO ("Updating scoreboard, got " << list.size () << " blocks to analyze");
789 
790  uint32_t bytesSacked = 0;
791 
792  for (auto option_it = list.begin (); option_it != list.end (); ++option_it)
793  {
794  PacketList::iterator item_it = m_sentList.begin ();
795  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
796 
797  if (m_firstByteSeq + m_sentSize < (*option_it).first)
798  {
799  NS_LOG_INFO ("Not updating scoreboard, the option block is outside the sent list");
800  return bytesSacked;
801  }
802 
803  while (item_it != m_sentList.end ())
804  {
805  uint32_t pktSize = (*item_it)->m_packet->GetSize ();
806 
807  // Check the boundary of this packet ... only mark as sacked if
808  // it is precisely mapped over the option. It means that if the receiver
809  // is reporting as sacked single range bytes that are not mapped 1:1
810  // in what we have, the option is discarded. There's room for improvement
811  // here.
812  if (beginOfCurrentPacket >= (*option_it).first
813  && beginOfCurrentPacket + pktSize <= (*option_it).second)
814  {
815  if ((*item_it)->m_sacked)
816  {
817  NS_ASSERT (!(*item_it)->m_lost);
818  NS_LOG_INFO ("Received block " << *option_it <<
819  ", checking sentList for block " << *(*item_it) <<
820  ", found in the sackboard already sacked");
821  }
822  else
823  {
824  if ((*item_it)->m_lost)
825  {
826  (*item_it)->m_lost = false;
827  m_lostOut -= (*item_it)->m_packet->GetSize ();
828  }
829 
830  (*item_it)->m_sacked = true;
831  m_sackedOut += (*item_it)->m_packet->GetSize ();
832  bytesSacked += (*item_it)->m_packet->GetSize ();
833 
834  if (m_highestSack.first == m_sentList.end()
835  || m_highestSack.second <= beginOfCurrentPacket + pktSize)
836  {
837  m_highestSack = std::make_pair (item_it, beginOfCurrentPacket);
838  }
839 
840  NS_LOG_INFO ("Received block " << *option_it <<
841  ", checking sentList for block " << *(*item_it) <<
842  ", found in the sackboard, sacking, current highSack: " <<
843  m_highestSack.second);
844 
845  if (!sackedCb.IsNull ())
846  {
847  sackedCb (*item_it);
848  }
849  }
850  }
851  else if (beginOfCurrentPacket + pktSize > (*option_it).second)
852  {
853  // We already passed the received block end. Exit from the loop
854  NS_LOG_INFO ("Received block [" << *option_it <<
855  ", checking sentList for block " << *(*item_it) <<
856  "], not found, breaking loop");
857  break;
858  }
859 
860  beginOfCurrentPacket += pktSize;
861  ++item_it;
862  }
863  }
864 
865  if (bytesSacked > 0)
866  {
867  NS_ASSERT_MSG (m_highestSack.first != m_sentList.end(), "Buffer status: " << *this);
868  UpdateLostCount ();
869  }
870 
871  NS_ASSERT ((*(m_sentList.begin ()))->m_sacked == false);
873  //NS_ASSERT (list.size () == 0 || modified); // Assert for duplicated SACK or
874  // impossiblity to map the option into the sent blocks
875  ConsistencyCheck ();
876  return bytesSacked;
877 }
878 
879 void
881 {
882  NS_LOG_FUNCTION (this);
883  uint32_t sacked = 0;
884  SequenceNumber32 beginOfCurrentPacket = m_highestSack.second;
885  if (m_highestSack.first == m_sentList.end ())
886  {
887  NS_LOG_INFO ("Status before the update: " << *this <<
888  ", will start from the latest sent item");
889  }
890  else
891  {
892  NS_LOG_INFO ("Status before the update: " << *this <<
893  ", will start from item " << *(*m_highestSack.first));
894  }
895 
896  for (auto it = m_highestSack.first; it != m_sentList.begin(); --it)
897  {
898  TcpTxItem *item = *it;
899  if (item->m_sacked)
900  {
901  sacked++;
902  }
903 
904  if (sacked >= m_dupAckThresh)
905  {
906  if (!item->m_sacked && !item->m_lost)
907  {
908  item->m_lost = true;
909  m_lostOut += item->m_packet->GetSize ();
910  }
911  }
912  beginOfCurrentPacket -= item->m_packet->GetSize ();
913  }
914 
915  if (sacked >= m_dupAckThresh)
916  {
917  TcpTxItem *item = *m_sentList.begin ();
918  if (!item->m_lost)
919  {
920  item->m_lost = true;
921  m_lostOut += item->m_packet->GetSize ();
922  }
923  }
924  NS_LOG_INFO ("Status after the update: " << *this);
925  ConsistencyCheck ();
926 }
927 
928 bool
930 {
931  NS_LOG_FUNCTION (this << seq);
932 
933  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
934  PacketList::const_iterator it;
935 
936  if (seq >= m_highestSack.second)
937  {
938  return false;
939  }
940 
941  // In theory, using a map and hints when inserting elements can improve
942  // performance
943  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
944  {
945  // Search for the right iterator before calling IsLost()
946  if (beginOfCurrentPacket >= seq)
947  {
948  if ((*it)->m_lost == true)
949  {
950  NS_LOG_INFO ("seq=" << seq << " is lost because of lost flag");
951  return true;
952  }
953 
954  if ((*it)->m_sacked == true)
955  {
956  NS_LOG_INFO ("seq=" << seq << " is not lost because of sacked flag");
957  return false;
958  }
959  }
960 
961  beginOfCurrentPacket += (*it)->m_packet->GetSize ();
962  }
963 
964  return false;
965 }
966 
967 bool
968 TcpTxBuffer::NextSeg (SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
969 {
970  NS_LOG_FUNCTION (this << isRecovery);
971  /* RFC 6675, NextSeg definition.
972  *
973  * (1) If there exists a smallest unSACKed sequence number 'S2' that
974  * meets the following three criteria for determining loss, the
975  * sequence range of one segment of up to SMSS octets starting
976  * with S2 MUST be returned.
977  *
978  * (1.a) S2 is greater than HighRxt.
979  *
980  * (1.b) S2 is less than the highest octet covered by any
981  * received SACK.
982  *
983  * (1.c) IsLost (S2) returns true.
984  */
985  PacketList::const_iterator it;
986  TcpTxItem *item;
987  SequenceNumber32 seqPerRule3;
988  bool isSeqPerRule3Valid = false;
989  SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
990 
991  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
992  {
993  item = *it;
994 
995  // Condition 1.a , 1.b , and 1.c
996  if (item->m_retrans == false && item->m_sacked == false)
997  {
998  if (item->m_lost)
999  {
1000  NS_LOG_INFO("IsLost, returning" << beginOfCurrentPkt);
1001  *seq = beginOfCurrentPkt;
1002  *seqHigh = *seq + m_segmentSize;
1003  return true;
1004  }
1005  else if (seqPerRule3.GetValue () == 0 && isRecovery)
1006  {
1007  NS_LOG_INFO ("Saving for rule 3 the seq " << beginOfCurrentPkt);
1008  isSeqPerRule3Valid = true;
1009  seqPerRule3 = beginOfCurrentPkt;
1010  }
1011  }
1012 
1013  // Nothing found, iterate
1014  beginOfCurrentPkt += item->m_packet->GetSize ();
1015  }
1016 
1017  /* (2) If no sequence number 'S2' per rule (1) exists but there
1018  * exists available unsent data and the receiver's advertised
1019  * window allows, the sequence range of one segment of up to SMSS
1020  * octets of previously unsent data starting with sequence number
1021  * HighData+1 MUST be returned.
1022  */
1024  {
1025  if (m_sentSize <= m_rWndCallback ())
1026  {
1027  NS_LOG_INFO ("There is unsent data. Send it");
1028  *seq = m_firstByteSeq + m_sentSize;
1029  *seqHigh = *seq + std::min<uint32_t> (m_segmentSize, (m_rWndCallback () - m_sentSize));
1030  return true;
1031  }
1032  else
1033  {
1034  NS_LOG_INFO ("There is no available receiver window to send");
1035  return false;
1036  }
1037  }
1038  else
1039  {
1040  NS_LOG_INFO ("There isn't unsent data.");
1041  }
1042 
1043  /* (3) If the conditions for rules (1) and (2) fail, but there exists
1044  * an unSACKed sequence number 'S3' that meets the criteria for
1045  * detecting loss given in steps (1.a) and (1.b) above
1046  * (specifically excluding step (1.c)), then one segment of up to
1047  * SMSS octets starting with S3 SHOULD be returned.
1048  */
1049  if (isSeqPerRule3Valid)
1050  {
1051  NS_LOG_INFO ("Rule3 valid. " << seqPerRule3);
1052  *seq = seqPerRule3;
1053  *seqHigh = *seq + m_segmentSize;
1054  return true;
1055  }
1056 
1057  /* (4) If the conditions for (1), (2), and (3) fail, but there exists
1058  * outstanding unSACKed data, we provide the opportunity for a
1059  * single "rescue" retransmission per entry into loss recovery.
1060  * If HighACK is greater than RescueRxt (or RescueRxt is
1061  * undefined), then one segment of up to SMSS octets that MUST
1062  * include the highest outstanding unSACKed sequence number
1063  * SHOULD be returned, and RescueRxt set to RecoveryPoint.
1064  * HighRxt MUST NOT be updated.
1065  *
1066  * This point require too much interaction between us and TcpSocketBase.
1067  * We choose to not respect the SHOULD (allowed from RFC MUST/SHOULD definition)
1068  */
1069  NS_LOG_INFO ("Can't return anything");
1070  return false;
1071 }
1072 
1073 uint32_t
1075 {
1077  "Count of sacked " << m_sackedOut << " and lost " << m_lostOut <<
1078  " is out of sync with sent list size " << m_sentSize <<
1079  " " << *this);
1080  uint32_t leftOut = m_sackedOut + m_lostOut;
1081  uint32_t retrans = m_retrans;
1082 
1083  NS_LOG_INFO ("Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1084  " retrans: " << retrans);
1085  uint32_t in_flight = m_sentSize - leftOut + retrans;
1086 
1087  //uint32_t rfc_in_flight = BytesInFlightRFC (3, segmentSize);
1088  //NS_ASSERT_MSG(in_flight == rfc_in_flight,
1089  // "Calculated: " << in_flight << " RFC: " << rfc_in_flight <<
1090  // "Sent size: " << m_sentSize << " leftOut: " << leftOut <<
1091  // " retrans: " << retrans);
1092  return in_flight;
1093 }
1094 
1095 uint32_t
1097 {
1098  PacketList::const_iterator it;
1099  TcpTxItem *item;
1100  uint32_t size = 0; // "pipe" in RFC
1101  SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
1102  uint32_t sackedOut = 0;
1103  uint32_t lostOut = 0;
1104  uint32_t retrans = 0;
1105  uint32_t totalSize = 0;
1106 
1107  // After initializing pipe to zero, the following steps are taken for each
1108  // octet 'S1' in the sequence space between HighACK and HighData that has not
1109  // been SACKed:
1110  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
1111  {
1112  item = *it;
1113  totalSize += item->m_packet->GetSize();
1114  if (!item->m_sacked)
1115  {
1116  bool isLost = IsLostRFC (beginOfCurrentPkt, it);
1117  // (a) If IsLost (S1) returns false: Pipe is incremented by 1 octet.
1118  if (!isLost)
1119  {
1120  size += item->m_packet->GetSize ();
1121  }
1122  // (b) If S1 <= HighRxt: Pipe is incremented by 1 octet.
1123  // (NOTE: we use the m_retrans flag instead of keeping and updating
1124  // another variable). Only if the item is not marked as lost
1125  else if (item->m_retrans)
1126  {
1127  size += item->m_packet->GetSize ();
1128  }
1129 
1130  if (isLost)
1131  {
1132  lostOut += item->m_packet->GetSize ();
1133  }
1134  }
1135  else
1136  {
1137  sackedOut += item->m_packet->GetSize ();
1138  }
1139 
1140  if (item->m_retrans)
1141  {
1142  retrans += item->m_packet->GetSize ();
1143  }
1144  beginOfCurrentPkt += item->m_packet->GetSize ();
1145  }
1146 
1147  NS_ASSERT_MSG(lostOut == m_lostOut, "Lost counted: " << lostOut << " " <<
1148  m_lostOut << "\n" << *this);
1149  NS_ASSERT_MSG(retrans == m_retrans, "Retrans Counted: " << retrans << " " <<
1150  m_retrans << "\n" << *this);
1151  NS_ASSERT_MSG(sackedOut == m_sackedOut, "Sacked counted: " << sackedOut <<
1152  " " << m_sackedOut << *this);
1153  NS_ASSERT_MSG(totalSize == m_sentSize,
1154  "Sent size counted: " << totalSize << " " << m_sentSize << *this);
1155 
1156  return size;
1157 }
1158 
1159 bool
1160 TcpTxBuffer::IsLostRFC (const SequenceNumber32 &seq, const PacketList::const_iterator &segment) const
1161 {
1162  NS_LOG_FUNCTION (this << seq);
1163  uint32_t count = 0;
1164  uint32_t bytes = 0;
1165  PacketList::const_iterator it;
1166  TcpTxItem *item;
1167  Ptr<const Packet> current;
1168  SequenceNumber32 beginOfCurrentPacket = seq;
1169 
1170  if ((*segment)->m_sacked == true)
1171  {
1172  return false;
1173  }
1174 
1175  // From RFC 6675:
1176  // > The routine returns true when either dupThresh discontiguous SACKed
1177  // > sequences have arrived above 'seq' or more than (dupThresh - 1) * SMSS bytes
1178  // > with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the
1179  // > routine returns false.
1180  for (it = segment; it != m_sentList.end (); ++it)
1181  {
1182  item = *it;
1183  current = item->m_packet;
1184 
1185  if (item->m_sacked)
1186  {
1187  NS_LOG_INFO ("Segment " << *item <<
1188  " found to be SACKed while checking for " << seq);
1189  ++count;
1190  bytes += current->GetSize ();
1191  if ((count >= m_dupAckThresh) || (bytes > (m_dupAckThresh-1) * m_segmentSize))
1192  {
1193  NS_LOG_INFO ("seq=" << seq << " is lost because of 3 sacked blocks ahead");
1194  return true;
1195  }
1196  }
1197 
1198  if (beginOfCurrentPacket >= m_highestSack.second)
1199  {
1200  if (item->m_lost && !item->m_retrans)
1201  return true;
1202 
1203  NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead");
1204  return false;
1205  }
1206 
1207  beginOfCurrentPacket += current->GetSize ();
1208  }
1209  if (it == m_highestSack.first)
1210  {
1211  NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead " << m_highestSack.second);
1212  }
1213  return false;
1214 }
1215 
1216 void
1218 {
1219  NS_LOG_FUNCTION (this);
1220 
1221  m_sackedOut = 0;
1222  for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1223  {
1224  (*it)->m_sacked = false;
1225  }
1226 
1227  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1228 }
1229 
1230 void
1232 {
1233  NS_LOG_FUNCTION (this);
1234  m_rWndCallback = rWndCallback;
1235 }
1236 
1237 void
1239 {
1240  NS_LOG_FUNCTION (this);
1241  TcpTxItem *item;
1242 
1243  // Keep the head items; they will then marked as lost
1244  while (m_sentList.size () > 0)
1245  {
1246  item = m_sentList.back ();
1247  item->m_retrans = item->m_sacked = item->m_lost = false;
1248  m_appList.push_front (item);
1249  m_sentList.pop_back ();
1250  }
1251 
1252  m_sentSize = 0;
1253  m_lostOut = 0;
1254  m_retrans = 0;
1255  m_sackedOut = 0;
1256  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1257 }
1258 
1259 void
1261 {
1262  NS_LOG_FUNCTION (this);
1263  if (!m_sentList.empty ())
1264  {
1265  TcpTxItem *item = m_sentList.back ();
1266 
1267  m_sentList.pop_back ();
1268  m_sentSize -= item->m_packet->GetSize ();
1269  if (item->m_retrans)
1270  {
1271  m_retrans -= item->m_packet->GetSize ();
1272  }
1273  m_appList.insert (m_appList.begin (), item);
1274  }
1275  ConsistencyCheck ();
1276 }
1277 
1278 void
1280 {
1281  NS_LOG_FUNCTION (this);
1282  m_retrans = 0;
1283 
1284  if (resetSack)
1285  {
1286  m_sackedOut = 0;
1288  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1289  }
1290  else
1291  {
1292  m_lostOut = 0;
1293  }
1294 
1295  for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1296  {
1297  if (resetSack)
1298  {
1299  (*it)->m_sacked = false;
1300  (*it)->m_lost = true;
1301  }
1302  else
1303  {
1304  if ((*it)->m_lost)
1305  {
1306  // Have to increment it because we set it to 0 at line 1133
1307  m_lostOut += (*it)->m_packet->GetSize ();
1308  }
1309  else if (!(*it)->m_sacked)
1310  {
1311  // Packet is not marked lost, nor is sacked. Then it becomes lost.
1312  (*it)->m_lost = true;
1313  m_lostOut += (*it)->m_packet->GetSize ();
1314  }
1315  }
1316 
1317  (*it)->m_retrans = false;
1318  }
1319 
1320  NS_LOG_INFO ("Set sent list lost, status: " << *this);
1322  ConsistencyCheck ();
1323 }
1324 
1325 bool
1327 {
1328  NS_LOG_FUNCTION (this);
1329 
1330  if (m_sentSize == 0)
1331  {
1332  return false;
1333  }
1334 
1335  return m_sentList.front ()->m_retrans;
1336 }
1337 
1338 void
1340 {
1341  NS_LOG_FUNCTION (this);
1342 
1343  if (m_sentSize == 0)
1344  {
1345  return;
1346  }
1347 
1348  if (m_sentList.front ()->m_retrans)
1349  {
1350  m_sentList.front ()->m_retrans = false;
1351  m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1352  }
1353  ConsistencyCheck ();
1354 }
1355 
1356 void
1358 {
1359  if (m_sentList.size () > 0)
1360  {
1361  // If the head is sacked (reneging by the receiver the previously sent
1362  // information) we revert the sacked flag.
1363  // A sacked head means that we should advance SND.UNA.. so it's an error.
1364  if (m_sentList.front ()->m_sacked)
1365  {
1366  m_sentList.front ()->m_sacked = false;
1367  m_sackedOut -= m_sentList.front ()->m_packet->GetSize ();
1368  }
1369 
1370  if (m_sentList.front ()->m_retrans)
1371  {
1372  m_sentList.front ()->m_retrans = false;
1373  m_retrans -= m_sentList.front ()->m_packet->GetSize ();
1374  }
1375 
1376  if (! m_sentList.front()->m_lost)
1377  {
1378  m_sentList.front()->m_lost = true;
1379  m_lostOut += m_sentList.front ()->m_packet->GetSize ();
1380  }
1381  }
1382  ConsistencyCheck ();
1383 }
1384 
1385 void
1387 {
1388  NS_LOG_FUNCTION (this);
1389 
1390  if (m_sackEnabled)
1391  {
1392  NS_ASSERT (m_sentList.size () > 1);
1393  }
1394  else
1395  {
1396  NS_ASSERT (m_sentList.size () > 0);
1397  }
1398 
1399  m_renoSack = true;
1400 
1401  // We can _never_ SACK the head, so start from the second segment sent
1402  auto it = ++m_sentList.begin ();
1403 
1404  // Find the "highest sacked" point, that is SND.UNA + m_sackedOut
1405  while (it != m_sentList.end () && (*it)->m_sacked)
1406  {
1407  ++it;
1408  }
1409 
1410  // Add to the sacked size the size of the first "not sacked" segment
1411  if (it != m_sentList.end ())
1412  {
1413  (*it)->m_sacked = true;
1414  m_sackedOut += (*it)->m_packet->GetSize ();
1415  m_highestSack = std::make_pair (it, (*it)->m_startSeq);
1416  NS_LOG_INFO ("Added a Reno SACK, status: " << *this);
1417  }
1418  else
1419  {
1420  NS_LOG_INFO ("Can't add a Reno SACK because we miss segments. This dupack"
1421  " should be arrived from spurious retransmissions");
1422  }
1423 
1424  ConsistencyCheck ();
1425 }
1426 
1427 void
1429 {
1430  static const bool enable = false;
1431 
1432  if (!enable)
1433  {
1434  return;
1435  }
1436 
1437  uint32_t sacked = 0;
1438  uint32_t lost = 0;
1439  uint32_t retrans = 0;
1440 
1441  for (auto it = m_sentList.begin (); it != m_sentList.end (); ++it)
1442  {
1443  if ((*it)->m_sacked)
1444  {
1445  sacked += (*it)->m_packet->GetSize ();
1446  }
1447  if ((*it)->m_lost)
1448  {
1449  lost += (*it)->m_packet->GetSize ();
1450  }
1451  if ((*it)->m_retrans)
1452  {
1453  retrans += (*it)->m_packet->GetSize ();
1454  }
1455  }
1456 
1457  NS_ASSERT_MSG (sacked == m_sackedOut, "Counted SACK: " << sacked <<
1458  " stored SACK: " << m_sackedOut);
1459  NS_ASSERT_MSG (lost == m_lostOut, " Counted lost: " << lost <<
1460  " stored lost: " << m_lostOut);
1461  NS_ASSERT_MSG (retrans == m_retrans, " Counted retrans: " << retrans <<
1462  " stored retrans: " << m_retrans);
1463 }
1464 
1465 std::ostream &
1466 operator<< (std::ostream & os, TcpTxItem const & item)
1467 {
1468  item.Print (os);
1469  return os;
1470 }
1471 
1472 std::ostream &
1473 operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf)
1474 {
1475  TcpTxBuffer::PacketList::const_iterator it;
1476  std::stringstream ss;
1477  SequenceNumber32 beginOfCurrentPacket = tcpTxBuf.m_firstByteSeq;
1478  uint32_t sentSize = 0, appSize = 0;
1479 
1481  for (it = tcpTxBuf.m_sentList.begin (); it != tcpTxBuf.m_sentList.end (); ++it)
1482  {
1483  p = (*it)->GetPacket ();
1484  ss << "{";
1485  (*it)->Print (ss);
1486  ss << "}";
1487  sentSize += p->GetSize ();
1488  beginOfCurrentPacket += p->GetSize ();
1489  }
1490 
1491  for (it = tcpTxBuf.m_appList.begin (); it != tcpTxBuf.m_appList.end (); ++it)
1492  {
1493  appSize += (*it)->GetPacket ()->GetSize ();
1494  }
1495 
1496  os << "Sent list: " << ss.str () << ", size = " << tcpTxBuf.m_sentList.size () <<
1497  " Total size: " << tcpTxBuf.m_size <<
1498  " m_firstByteSeq = " << tcpTxBuf.m_firstByteSeq <<
1499  " m_sentSize = " << tcpTxBuf.m_sentSize <<
1500  " m_retransOut = " << tcpTxBuf.m_retrans <<
1501  " m_lostOut = " << tcpTxBuf.m_lostOut <<
1502  " m_sackedOut = " << tcpTxBuf.m_sackedOut;
1503 
1504  NS_ASSERT (sentSize == tcpTxBuf.m_sentSize);
1505  NS_ASSERT (tcpTxBuf.m_size - tcpTxBuf.m_sentSize == appSize);
1506  return os;
1507 }
1508 
1509 } // 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
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.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:137
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
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.
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
uint32_t pktSize
packet size used for the simulation (in bytes)
Definition: wifi-bianchi.cc:86
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.