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