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  return nullptr; // Silence compiler warning about lack of return value
584 }
585 
586 void
588 {
589  NS_ASSERT (t1 != nullptr && t2 != nullptr);
590  NS_LOG_FUNCTION (this << *t1 << *t2);
591  NS_LOG_INFO ("Merging " << *t2 << " into " << *t1);
592 
593  NS_ASSERT_MSG (t1->m_sacked == t2->m_sacked,
594  "Merging one sacked and another not sacked. Impossible");
595  NS_ASSERT_MSG (t1->m_lost == t2->m_lost,
596  "Merging one lost and another not lost. Impossible");
597 
598  // If one is retrans and the other is not, cancel the retransmitted flag.
599  // We are merging this segment for the retransmit, so the count will
600  // be updated in MarkTransmittedSegment.
601  if (t1->m_retrans != t2->m_retrans)
602  {
603  if (t1->m_retrans)
604  {
605  TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
606  self->m_retrans -= t1->m_packet->GetSize ();
607  t1->m_retrans = false;
608  }
609  else
610  {
611  NS_ASSERT (t2->m_retrans);
612  TcpTxBuffer *self = const_cast<TcpTxBuffer*> (this);
613  self->m_retrans -= t2->m_packet->GetSize ();
614  t2->m_retrans = false;
615  }
616  }
617 
618  if (t1->m_lastSent < t2->m_lastSent)
619  {
620  t1->m_lastSent = t2->m_lastSent;
621  }
622 
623  t1->m_packet->AddAtEnd (t2->m_packet);
624 
625  NS_LOG_INFO ("Situation after the merge: " << *t1);
626 }
627 
628 void
630 {
631  NS_LOG_FUNCTION (this << *item << size);
632  if (item->m_sacked)
633  {
634  NS_ASSERT (m_sackedOut >= size);
635  m_sackedOut -= size;
636  }
637  if (item->m_retrans)
638  {
639  NS_ASSERT (m_retrans >= size);
640  m_retrans -= size;
641  }
642  if (item->m_lost)
643  {
644  NS_ASSERT_MSG (m_lostOut >= size, "Trying to remove " << size <<
645  " bytes from " << m_lostOut);
646  m_lostOut -= size;
647  }
648 }
649 
650 bool
652 {
653  NS_LOG_FUNCTION (this);
654  for (const auto &it : m_sentList)
655  {
656  TcpTxItem *item = it;
657  Ptr<Packet> p = item->m_packet;
658  if (item->m_startSeq + p->GetSize () == ack && !item->m_sacked && item->m_retrans)
659  {
660  return true;
661  }
662  }
663  return false;
664 }
665 
666 void
668  const Callback<void, TcpTxItem *> &beforeDelCb)
669 {
670  NS_LOG_FUNCTION (this << seq);
671 
672  // Cases do not need to scan the buffer
673  if (m_firstByteSeq >= seq)
674  {
675  NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
676  return;
677  }
678  NS_LOG_DEBUG ("Remove up to " << seq << " lost: " << m_lostOut <<
679  " retrans: " << m_retrans << " sacked: " << m_sackedOut);
680 
681  // Scan the buffer and discard packets
682  uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
683  uint32_t pktSize;
684  PacketList::iterator i = m_sentList.begin ();
685  while (m_size > 0 && offset > 0)
686  {
687  if (i == m_sentList.end ())
688  {
689  // Move data from app list to sent list, so we can delete the item
690  [[maybe_unused]] Ptr<Packet> p = CopyFromSequence (offset, m_firstByteSeq)->GetPacketCopy ();
691  NS_ASSERT (p != nullptr);
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
#define min(a, b)
Definition: 80211b.c:42
Callback template class.
Definition: callback.h:1279
bool IsNull(void) const
Check for null implementation.
Definition: callback.h:1386
A base class which provides memory management and object aggregation.
Definition: object.h:88
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:335
void RemoveAtStart(uint32_t size)
Remove size bytes from the start of the current packet.
Definition: packet.cc:362
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
void Print(std::ostream &os) const
Print the packet contents.
Definition: packet.cc:434
Ptr< Packet > Copy(void) const
performs a COW copy of the packet.
Definition: packet.cc:121
uint32_t GetSize(void) const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:856
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:195
std::list< SackBlock > SackList
SACK list definition.
Tcp sender buffer.
uint32_t m_retrans
Number of retransmitted bytes.
void ResetRenoSack()
Reset the SACKs.
uint32_t GetSacked(void) const
Get the number of segments that have been explicitly sacked by the receiver.
void ConsistencyCheck() const
Check if the values of sacked, lost, retrans, are in sync with the sent list.
static TypeId GetTypeId(void)
Get the type ID.
bool m_sackEnabled
Indicates if SACK is enabled on this connection.
uint32_t m_lostOut
Number of lost bytes.
bool IsLostRFC(const SequenceNumber32 &seq, const PacketList::const_iterator &segment) const
Decide if a segment is lost based on RFC 6675 algorithm.
std::pair< TcpTxBuffer::PacketList::const_iterator, SequenceNumber32 > FindHighestSacked() const
Find the highest SACK byte.
std::pair< PacketList::const_iterator, SequenceNumber32 > m_highestSack
Highest SACK byte.
bool IsHeadRetransmitted() const
Check if the head is retransmitted.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
void SetSegmentSize(uint32_t segmentSize)
Set the segment size.
uint32_t Available(void) const
Returns the available capacity of this buffer.
bool IsRetransmittedDataAcked(const SequenceNumber32 &ack) const
Checks whether the ack corresponds to retransmitted data.
TcpTxItem * GetPacketFromList(PacketList &list, const SequenceNumber32 &startingSeq, uint32_t numBytes, const SequenceNumber32 &requestedSeq, bool *listEdited=nullptr) const
Get a block (which is returned as Packet) from a list.
uint32_t SizeFromSequence(const SequenceNumber32 &seq) const
Returns the number of bytes from the buffer in the range [seq, tailSequence)
TracedValue< SequenceNumber32 > m_firstByteSeq
Sequence number of the first byte in data (SND.UNA)
std::list< TcpTxItem * > PacketList
container for data stored in the buffer
TcpTxItem * GetTransmittedSegment(uint32_t numBytes, const SequenceNumber32 &seq)
Get a block of data previously transmitted.
uint32_t m_maxBuffer
Max number of data bytes in buffer (SND.WND)
bool m_renoSack
Indicates if AddRenoSack was called.
uint32_t m_dupAckThresh
Duplicate Ack threshold from TcpSocketBase.
void SetSackEnabled(bool enabled)
tell tx-buffer whether SACK is used on this TCP socket
static Callback< void, TcpTxItem * > m_nullCb
Null callback for an item.
virtual ~TcpTxBuffer(void)
uint32_t m_sackedOut
Number of sacked bytes.
void ResetLastSegmentSent()
Take the last segment sent and put it back into the un-sent list (at the beginning)
bool IsLost(const SequenceNumber32 &seq) const
Check if a segment is lost.
bool NextSeg(SequenceNumber32 *seq, SequenceNumber32 *seqHigh, bool isRecovery) const
Get the next sequence number to transmit, according to RFC 6675.
uint32_t MaxBufferSize(void) const
Get the maximum buffer size.
uint32_t GetLost(void) const
Get the number of segments that we believe are lost in the network.
void SetDupAckThresh(uint32_t dupAckThresh)
Set the DupAckThresh.
TcpTxItem * CopyFromSequence(uint32_t numBytes, const SequenceNumber32 &seq)
Copy data from the range [seq, seq+numBytes) into a packet.
TcpTxItem * GetNewSegment(uint32_t numBytes)
Get a block of data not transmitted yet and move it into SentList.
uint32_t m_segmentSize
Segment size from TcpSocketBase.
uint32_t Update(const TcpOptionSack::SackList &list, const Callback< void, TcpTxItem * > &sackedCb=m_nullCb)
Update the scoreboard.
void AddRenoSack()
Emulate SACKs for SACKless connection: account for a new dupack.
void MarkHeadAsLost()
Mark the head of the sent list as lost.
void UpdateLostCount()
Update the lost count.
SequenceNumber32 TailSequence(void) const
Get the sequence number of the buffer tail (plus one)
Callback< uint32_t > m_rWndCallback
Callback to obtain RCV.WND value.
void SetRWndCallback(Callback< uint32_t > rWndCallback)
Set callback to obtain receiver window value.
uint32_t BytesInFlight() const
Return total bytes in flight.
uint32_t Size(void) const
Returns total number of bytes in this buffer.
SequenceNumber32 HeadSequence(void) const
Get the sequence number of the buffer head.
void SetSentListLost(bool resetSack=false)
Set the entire sent list as lost (typically after an RTO)
void SplitItems(TcpTxItem *t1, TcpTxItem *t2, uint32_t size) const
Split one TcpTxItem.
void DiscardUpTo(const SequenceNumber32 &seq, const Callback< void, TcpTxItem * > &beforeDelCb=m_nullCb)
Discard data up to but not including this sequence number.
uint32_t BytesInFlightRFC() const
Calculate the number of bytes in flight per RFC 6675.
PacketList m_appList
Buffer for application data.
void MergeItems(TcpTxItem *t1, TcpTxItem *t2) const
Merge two TcpTxItem.
uint32_t m_size
Size of all data in this buffer.
void ResetSentList()
Reset the sent list.
void DeleteRetransmittedFlagFromHead()
DeleteRetransmittedFlagFromHead.
void SetMaxBufferSize(uint32_t n)
Set the maximum buffer size.
bool IsSackEnabled(void) const
check whether SACK is used on the corresponding TCP socket
PacketList m_sentList
Buffer for sent (but not acked) data.
uint32_t m_sentSize
Size of sent (and not discarded) segments.
void RemoveFromCounts(TcpTxItem *item, uint32_t size)
Remove the size specified from the lostOut, retrans, sacked count.
void SetHeadSequence(const SequenceNumber32 &seq)
Set the head sequence of the buffer.
uint32_t GetRetransmitsCount(void) const
Return the number of segments in the sent list that have been transmitted more than once,...
TcpTxBuffer(uint32_t n=0)
Constructor.
Item that encloses the application packet and some flags for it.
Definition: tcp-tx-item.h:33
bool m_sacked
Indicates if the segment has been SACKed.
Definition: tcp-tx-item.h:113
bool m_retrans
Indicates if the segment is retransmitted.
Definition: tcp-tx-item.h:102
SequenceNumber32 m_startSeq
Sequence number of the item (if transmitted)
Definition: tcp-tx-item.h:109
Time m_lastSent
Timestamp of the time at which the segment has been sent last time.
Definition: tcp-tx-item.h:112
Ptr< Packet > GetPacketCopy(void) const
Get a copy of the Packet underlying this item.
Definition: tcp-tx-item.cc:80
bool m_lost
Indicates if the segment has been lost (RTO)
Definition: tcp-tx-item.h:111
Ptr< Packet > m_packet
Application packet (can be null)
Definition: tcp-tx-item.h:110
void Print(std::ostream &os, Time::Unit unit=Time::S) const
Print the time.
Definition: tcp-tx-item.cc:24
T Get(void) const
Get the underlying value.
Definition: traced-value.h:232
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:922
uint32_t segmentSize
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:67
#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
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition: abort.h:144
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:257
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:265
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
SequenceNumber< uint32_t, int32_t > SequenceNumber32
32 bit Sequence number.
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition: angles.cc:139
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:129
#define list
uint32_t pktSize
packet size used for the simulation (in bytes)
Definition: wifi-bianchi.cc:89