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