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