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 
37  : m_packet (0),
38  m_lost (false),
39  m_retrans (false),
40  m_lastSent (Time::Min ()),
41  m_sacked (false)
42 {
43 }
44 
46  : m_packet (other.m_packet),
47  m_lost (other.m_lost),
48  m_retrans (other.m_retrans),
49  m_lastSent (other.m_lastSent),
50  m_sacked (other.m_sacked)
51 {
52 }
53 
54 void
55 TcpTxItem::Print (std::ostream &os) const
56 {
57  bool comma = false;
58 
59  if (m_lost)
60  {
61  os << "[lost]";
62  comma = true;
63  }
64  if (m_retrans)
65  {
66  if (comma)
67  {
68  os << ",";
69  }
70 
71  os << "[retrans]";
72  comma = true;
73  }
74  if (m_sacked)
75  {
76  if (comma)
77  {
78  os << ",";
79  }
80  os << "[sacked]";
81  comma = true;
82  }
83  if (comma)
84  {
85  os << ",";
86  }
87  os << "last sent: " << m_lastSent;
88 }
89 
91 
92 TypeId
94 {
95  static TypeId tid = TypeId ("ns3::TcpTxBuffer")
96  .SetParent<Object> ()
97  .SetGroupName ("Internet")
98  .AddConstructor<TcpTxBuffer> ()
99  .AddTraceSource ("UnackSequence",
100  "First unacknowledged sequence number (SND.UNA)",
102  "ns3::SequenceNumber32TracedValueCallback")
103  ;
104  return tid;
105 }
106 
107 /* A user is supposed to create a TcpSocket through a factory. In TcpSocket,
108  * there are attributes SndBufSize and RcvBufSize to control the default Tx and
109  * Rx window sizes respectively, with default of 128 KiByte. The attribute
110  * SndBufSize is passed to TcpTxBuffer by TcpSocketBase::SetSndBufSize() and in
111  * turn, TcpTxBuffer:SetMaxBufferSize(). Therefore, the m_maxBuffer value
112  * initialized below is insignificant.
113  */
115  : m_maxBuffer (32768), m_size (0), m_sentSize (0), m_firstByteSeq (n)
116 {
117 }
118 
120 {
121  PacketList::iterator it;
122 
123  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
124  {
125  TcpTxItem *item = *it;
126  m_sentSize -= item->m_packet->GetSize ();
127  delete item;
128  }
129 
130  for (it = m_appList.begin (); it != m_appList.end (); ++it)
131  {
132  TcpTxItem *item = *it;
133  m_size -= item->m_packet->GetSize ();
134  delete item;
135  }
136 }
137 
140 {
141  return m_firstByteSeq;
142 }
143 
146 {
148 }
149 
150 uint32_t
151 TcpTxBuffer::Size (void) const
152 {
153  return m_size;
154 }
155 
156 uint32_t
158 {
159  return m_maxBuffer;
160 }
161 
162 void
164 {
165  m_maxBuffer = n;
166 }
167 
168 uint32_t
170 {
171  return m_maxBuffer - m_size;
172 }
173 
174 void
176 {
177  NS_LOG_FUNCTION (this << seq);
178  m_firstByteSeq = seq;
179 
180  // if you change the head with data already sent, something bad will happen
181  NS_ASSERT (m_sentList.size () == 0);
182  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
183 }
184 
185 bool
187 {
188  NS_LOG_FUNCTION (this << p);
189  NS_LOG_INFO ("Try to append " << p->GetSize () << " bytes to window starting at "
190  << m_firstByteSeq << ", availSize=" << Available ());
191  if (p->GetSize () <= Available ())
192  {
193  if (p->GetSize () > 0)
194  {
195  TcpTxItem *item = new TcpTxItem ();
196  item->m_packet = p->Copy ();
197  m_appList.insert (m_appList.end (), item);
198  m_size += p->GetSize ();
199 
200  NS_LOG_INFO ("Updated size=" << m_size << ", lastSeq=" <<
202  }
203  return true;
204  }
205  NS_LOG_WARN ("Rejected. Not enough room to buffer packet.");
206  return false;
207 }
208 
209 uint32_t
211 {
212  NS_LOG_FUNCTION (this << seq);
213  // Sequence of last byte in buffer
214  SequenceNumber32 lastSeq = TailSequence ();
215 
216  if (lastSeq >= seq)
217  {
218  return lastSeq - seq;
219  }
220 
221  NS_LOG_ERROR ("Requested a sequence beyond our space (" << seq << " > " << lastSeq <<
222  "). Returning 0 for convenience.");
223  return 0;
224 }
225 
227 TcpTxBuffer::CopyFromSequence (uint32_t numBytes, const SequenceNumber32& seq)
228 {
229  NS_LOG_FUNCTION (this << numBytes << seq);
230 
231  if (m_firstByteSeq > seq)
232  {
233  NS_LOG_ERROR ("Requested a sequence number which is not in the buffer anymore");
234  return Create<Packet> ();
235  }
236 
237  // Real size to extract. Insure not beyond end of data
238  uint32_t s = std::min (numBytes, SizeFromSequence (seq));
239 
240  if (s == 0)
241  {
242  return Create<Packet> ();
243  }
244 
245  TcpTxItem *outItem = 0;
246 
247  if (m_firstByteSeq + m_sentSize >= seq + s)
248  {
249  // already sent this block completely
250  outItem = GetTransmittedSegment (s, seq);
251  NS_ASSERT (outItem != 0);
252  outItem->m_retrans = true;
253 
254  NS_LOG_DEBUG ("Retransmitting [" << seq << ";" << seq + s << "|" << s <<
255  "] from " << *this);
256  }
257  else if (m_firstByteSeq + m_sentSize <= seq)
258  {
260  "Requesting a piece of new data with an hole");
261 
262  // this is the first time we transmit this block
263  outItem = GetNewSegment (s);
264  NS_ASSERT (outItem != 0);
265  NS_ASSERT (outItem->m_retrans == false);
266 
267  NS_LOG_DEBUG ("New segment [" << seq << ";" << seq + s << "|" << s <<
268  "] from " << *this);
269  }
270  else if (m_firstByteSeq + m_sentSize > seq && m_firstByteSeq + m_sentSize < seq + s)
271  {
272  // Partial: a part is retransmission, the remaining data is new
273 
274  // Take the new data and move it into sent list
275  uint32_t amount = seq + s - m_firstByteSeq.Get () - m_sentSize;
276  NS_LOG_DEBUG ("Moving segment [" << m_firstByteSeq + m_sentSize << ";" <<
277  m_firstByteSeq + m_sentSize + amount <<"|" << amount <<
278  "] from " << *this);
279 
280  outItem = GetNewSegment (amount);
281  NS_ASSERT (outItem != 0);
282 
283  // Now get outItem from the sent list (there will be a merge)
284  return CopyFromSequence (numBytes, seq);
285  }
286 
287  outItem->m_lost = false;
288  outItem->m_lastSent = Simulator::Now ();
289  Ptr<Packet> toRet = outItem->m_packet->Copy ();
290 
291  NS_ASSERT (toRet->GetSize () == s);
292 
293  return toRet;
294 }
295 
296 TcpTxItem*
297 TcpTxBuffer::GetNewSegment (uint32_t numBytes)
298 {
299  NS_LOG_FUNCTION (this << numBytes);
300 
301  SequenceNumber32 startOfAppList = m_firstByteSeq + m_sentSize;
302 
303  bool listEdited = false;
304  TcpTxItem *item = GetPacketFromList (m_appList, startOfAppList,
305  numBytes, startOfAppList, &listEdited);
306 
307  (void) listEdited;
308 
309  // Move item from AppList to SentList (should be the first, not too complex)
310  PacketList::iterator it = std::find (m_appList.begin (), m_appList.end (), item);
311  NS_ASSERT (it != m_appList.end ());
312 
313  m_appList.erase (it);
314  m_sentList.insert (m_sentList.end (), item);
315  m_sentSize += item->m_packet->GetSize ();
316 
317  return item;
318 }
319 
320 TcpTxItem*
322 {
323  NS_LOG_FUNCTION (this << numBytes << seq);
324  NS_ASSERT (seq >= m_firstByteSeq);
325  NS_ASSERT (numBytes <= m_sentSize);
326 
327  bool listEdited = false;
328 
329  TcpTxItem *item = GetPacketFromList (m_sentList, m_firstByteSeq, numBytes, seq, &listEdited);
330 
331  if (listEdited && m_highestSack.second >= m_firstByteSeq)
332  {
334  }
335 
336  return item;
337 }
338 
339 std::pair <TcpTxBuffer::PacketList::const_iterator, SequenceNumber32>
341 {
342  NS_LOG_FUNCTION (this);
343 
344  PacketList::const_iterator it;
345  std::pair <TcpTxBuffer::PacketList::const_iterator, SequenceNumber32> ret;
346  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
347 
348  ret = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
349 
350  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
351  {
352  const TcpTxItem *item = *it;
353  if (item->m_sacked)
354  {
355  ret = std::make_pair (it, beginOfCurrentPacket);
356  }
357  beginOfCurrentPacket += item->m_packet->GetSize ();
358  }
359 
360  return ret;
361 }
362 
363 
364 void
365 TcpTxBuffer::SplitItems (TcpTxItem &t1, TcpTxItem &t2, uint32_t size) const
366 {
367  NS_LOG_FUNCTION (this << size);
368 
369  t1.m_packet = t2.m_packet->CreateFragment (0, size);
370  t2.m_packet->RemoveAtStart (size);
371 
372  t1.m_sacked = t2.m_sacked;
373  t1.m_lastSent = t2.m_lastSent;
374  t1.m_retrans = t2.m_retrans;
375  t1.m_lost = t2.m_lost;
376 }
377 
378 TcpTxItem*
380  uint32_t numBytes, const SequenceNumber32 &seq,
381  bool *listEdited) const
382 {
383  NS_LOG_FUNCTION (this << numBytes << seq);
384 
385  /*
386  * Our possibilites are sketched out in the following:
387  *
388  * |------| |----| |----|
389  * GetList (m_data) = | | --> | | --> | |
390  * |------| |----| |----|
391  *
392  * ^ ^ ^ ^
393  * | | | | (1)
394  * seq | | numBytes
395  * | |
396  * | |
397  * seq numBytes (2)
398  *
399  * (1) seq and numBytes are the boundary of some packet
400  * (2) seq and numBytes are not the boundary of some packet
401  *
402  * We can have mixed case (e.g. seq over the boundary while numBytes not).
403  *
404  * If we discover that we are in (2) or in a mixed case, we split
405  * packets accordingly to the requested bounds and re-run the function.
406  *
407  * In (1), things are pretty easy, it's just a matter of walking the list and
408  * defragment packets, if needed (e.g. seq is the beginning of the first packet
409  * while maxBytes is the end of some packet next in the list).
410  */
411 
412  Ptr<Packet> currentPacket = 0;
413  TcpTxItem *currentItem = 0;
414  TcpTxItem *outItem = 0;
415  PacketList::iterator it = list.begin ();
416  SequenceNumber32 beginOfCurrentPacket = listStartFrom;
417 
418  while (it != list.end ())
419  {
420  currentItem = *it;
421  currentPacket = currentItem->m_packet;
422 
423  // The objective of this snippet is to find (or to create) the packet
424  // that begin with the sequence seq
425 
426  if (seq < beginOfCurrentPacket + currentPacket->GetSize ())
427  {
428  // seq is inside the current packet
429  if (seq == beginOfCurrentPacket)
430  {
431  // seq is the beginning of the current packet. Hurray!
432  outItem = currentItem;
433  NS_LOG_INFO ("Current packet starts at seq " << seq <<
434  " ends at " << seq + currentPacket->GetSize ());
435  }
436  else if (seq > beginOfCurrentPacket)
437  {
438  // seq is inside the current packet but seq is not the beginning,
439  // it's somewhere in the middle. Just fragment the beginning and
440  // start again.
441  NS_LOG_INFO ("we are at " << beginOfCurrentPacket <<
442  " searching for " << seq <<
443  " and now we recurse because packet ends at "
444  << beginOfCurrentPacket + currentPacket->GetSize ());
445  TcpTxItem *firstPart = new TcpTxItem ();
446  SplitItems (*firstPart, *currentItem, seq - beginOfCurrentPacket);
447 
448  // insert firstPart before currentItem
449  list.insert (it, firstPart);
450  *listEdited = true;
451 
452  return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
453  }
454  else
455  {
456  NS_FATAL_ERROR ("seq < beginOfCurrentPacket: our data is before");
457  }
458  }
459  else
460  {
461  // Walk the list, the current packet does not contain seq
462  beginOfCurrentPacket += currentPacket->GetSize ();
463  it++;
464  continue;
465  }
466 
467  NS_ASSERT (outItem != 0);
468 
469  // The objective of this snippet is to find (or to create) the packet
470  // that ends after numBytes bytes. We are sure that outPacket starts
471  // at seq.
472 
473  if (seq + numBytes <= beginOfCurrentPacket + currentPacket->GetSize ())
474  {
475  // the end boundary is inside the current packet
476  if (numBytes == currentPacket->GetSize ())
477  {
478  // the end boundary is exactly the end of the current packet. Hurray!
479  if (currentItem->m_packet == outItem->m_packet)
480  {
481  // A perfect match!
482  return outItem;
483  }
484  else
485  {
486  // the end is exactly the end of current packet, but
487  // current > outPacket in the list. Merge current with the
488  // previous, and recurse.
489  NS_ASSERT (it != list.begin ());
490  TcpTxItem *previous = *(--it);
491 
492  list.erase (it);
493 
494  MergeItems (*previous, *currentItem);
495  delete currentItem;
496  *listEdited = true;
497 
498  return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
499  }
500  }
501  else if (numBytes < currentPacket->GetSize ())
502  {
503  // the end is inside the current packet, but it isn't exactly
504  // the packet end. Just fragment, fix the list, and return.
505  TcpTxItem *firstPart = new TcpTxItem ();
506  SplitItems (*firstPart, *currentItem, numBytes);
507 
508  // insert firstPart before currentItem
509  list.insert (it, firstPart);
510  *listEdited = true;
511 
512  return firstPart;
513  }
514  }
515  else
516  {
517  // The end isn't inside current packet, but there is an exception for
518  // the merge and recurse strategy...
519  if (++it == list.end ())
520  {
521  // ...current is the last packet we sent. We have not more data;
522  // Go for this one.
523  NS_LOG_WARN ("Cannot reach the end, but this case is covered "
524  "with conditional statements inside CopyFromSequence."
525  "Something has gone wrong, report a bug");
526  return outItem;
527  }
528 
529  // The current packet does not contain the requested end. Merge current
530  // with the packet that follows, and recurse
531  TcpTxItem *next = (*it); // Please remember we have incremented it
532  // in the previous if
533 
534  MergeItems (*currentItem, *next);
535  list.erase (it);
536 
537  delete next;
538 
539  *listEdited = true;
540 
541  return GetPacketFromList (list, listStartFrom, numBytes, seq, listEdited);
542  }
543  }
544 
545  NS_FATAL_ERROR ("This point is not reachable");
546 }
547 
548 void
550 {
551  NS_LOG_FUNCTION (this);
552  if (t1.m_sacked == true && t2.m_sacked == true)
553  {
554  t1.m_sacked = true;
555  }
556  else
557  {
558  t1.m_sacked = false;
559  }
560 
561  if (t2.m_retrans == true && t1.m_retrans == false)
562  {
563  t1.m_retrans = true;
564  }
565  if (t1.m_lastSent < t2.m_lastSent)
566  {
567  t1.m_lastSent = t2.m_lastSent;
568  }
569  if (t2.m_lost)
570  {
571  t1.m_lost = true;
572  }
573 
574  t1.m_packet->AddAtEnd (t2.m_packet);
575 }
576 
577 void
579 {
580  NS_LOG_FUNCTION (this << seq);
581 
582  // Cases do not need to scan the buffer
583  if (m_firstByteSeq >= seq)
584  {
585  NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
586  return;
587  }
588 
589  // Scan the buffer and discard packets
590  uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
591  uint32_t pktSize;
592  PacketList::iterator i = m_sentList.begin ();
593  while (m_size > 0 && offset > 0)
594  {
595  if (i == m_sentList.end ())
596  {
598  NS_ASSERT (p != 0);
599  i = m_sentList.begin ();
600  NS_ASSERT (i != m_sentList.end ());
601  }
602  TcpTxItem *item = *i;
603  Ptr<Packet> p = item->m_packet;
604  pktSize = p->GetSize ();
605 
606  if (offset >= pktSize)
607  { // This packet is behind the seqnum. Remove this packet from the buffer
608  m_size -= pktSize;
609  m_sentSize -= pktSize;
610  offset -= pktSize;
611  m_firstByteSeq += pktSize;
612  i = m_sentList.erase (i);
613  delete item;
614  NS_LOG_INFO ("While removing up to " << seq <<
615  ".Removed one packet of size " << pktSize <<
616  " starting from " << m_firstByteSeq - pktSize <<
617  ". Remaining data " << m_size);
618  }
619  else if (offset > 0)
620  { // Part of the packet is behind the seqnum. Fragment
621  pktSize -= offset;
622  // PacketTags are preserved when fragmenting
623  item->m_packet = item->m_packet->CreateFragment (offset, pktSize);
624  m_size -= offset;
625  m_sentSize -= offset;
626  m_firstByteSeq += offset;
627  NS_LOG_INFO ("Fragmented one packet by size " << offset <<
628  ", new size=" << pktSize);
629  break;
630  }
631  }
632  // Catching the case of ACKing a FIN
633  if (m_size == 0)
634  {
635  m_firstByteSeq = seq;
636  }
637 
638  if (!m_sentList.empty ())
639  {
640  TcpTxItem *head = m_sentList.front ();
641  if (head->m_sacked)
642  {
643  // It is not possible to have the UNA sacked; otherwise, it would
644  // have been ACKed. This is, most likely, our wrong guessing
645  // when crafting the SACK option for a non-SACK receiver.
646  head->m_sacked = false;
647  }
648  }
649 
650  if (m_highestSack.second <= m_firstByteSeq)
651  {
652  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
653  }
654 
655  NS_LOG_DEBUG ("Discarded up to " << seq);
656  NS_LOG_LOGIC ("Buffer status after discarding data " << *this);
657  NS_ASSERT (m_firstByteSeq >= seq);
658 }
659 
660 bool
662 {
663  NS_LOG_FUNCTION (this);
664 
665  bool modified = false;
666  TcpOptionSack::SackList::const_iterator option_it;
667  NS_LOG_INFO ("Updating scoreboard, got " << list.size () << " blocks to analyze");
668  for (option_it = list.begin (); option_it != list.end (); ++option_it)
669  {
670  Ptr<Packet> current;
671  TcpTxItem *item;
672  const TcpOptionSack::SackBlock b = (*option_it);
673 
674  PacketList::iterator item_it = m_sentList.begin ();
675  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
676 
677  while (item_it != m_sentList.end ())
678  {
679  item = *item_it;
680  current = item->m_packet;
681 
682  // Check the boundary of this packet ... only mark as sacked if
683  // it is precisely mapped over the option
684  if (beginOfCurrentPacket >= b.first
685  && beginOfCurrentPacket + current->GetSize () <= b.second)
686  {
687  if (item->m_sacked)
688  {
689  NS_LOG_INFO ("Received block [" << b.first << ";" << b.second <<
690  ", checking sentList for block " << beginOfCurrentPacket <<
691  ";" << beginOfCurrentPacket + current->GetSize () <<
692  "], found in the sackboard already sacked");
693  }
694  else
695  {
696  item->m_sacked = true;
697  NS_LOG_INFO ("Received block [" << b.first << ";" << b.second <<
698  ", checking sentList for block " << beginOfCurrentPacket <<
699  ";" << beginOfCurrentPacket + current->GetSize () <<
700  "], found in the sackboard, sacking");
701  if (m_highestSack.second <= beginOfCurrentPacket + current->GetSize ())
702  {
703  PacketList::iterator new_it = item_it;
704  m_highestSack = std::make_pair (++new_it, beginOfCurrentPacket+current->GetSize ());
705  }
706  }
707  modified = true;
708  }
709  else if (beginOfCurrentPacket + current->GetSize () > b.second)
710  {
711  // we missed the block. It's useless to iterate again; Say "ciao"
712  // to the loop for optimization purposes
713  NS_LOG_INFO ("Received block [" << b.first << ";" << b.second <<
714  ", checking sentList for block " << beginOfCurrentPacket <<
715  ";" << beginOfCurrentPacket + current->GetSize () <<
716  "], not found, breaking loop");
717  break;
718  }
719 
720  beginOfCurrentPacket += current->GetSize ();
721  ++item_it;
722  }
723  }
724 
725  NS_ASSERT ((*(m_sentList.begin ()))->m_sacked == false);
726 
727  return modified;
728 }
729 
730 bool
731 TcpTxBuffer::IsLost (const SequenceNumber32 &seq, const PacketList::const_iterator &segment,
732  uint32_t dupThresh, uint32_t segmentSize) const
733 {
734  NS_LOG_FUNCTION (this << seq << dupThresh << segmentSize);
735  uint32_t count = 0;
736  uint32_t bytes = 0;
737  PacketList::const_iterator it;
738  TcpTxItem *item;
739  Ptr<const Packet> current;
740  SequenceNumber32 beginOfCurrentPacket = seq;
741 
742  NS_LOG_INFO ("Checking if seq=" << seq << " is lost from the buffer ");
743 
744  if ((*segment)->m_lost == true)
745  {
746  NS_LOG_INFO ("seq=" << seq << " is lost because of lost flag");
747  return true;
748  }
749 
750  if ((*segment)->m_sacked == true)
751  {
752  NS_LOG_INFO ("seq=" << seq << " is not lost because of sacked flag");
753  return false;
754  }
755 
756  // From RFC 6675:
757  // > The routine returns true when either dupThresh discontiguous SACKed
758  // > sequences have arrived above 'seq' or more than (dupThresh - 1) * SMSS bytes
759  // > with sequence numbers greater than 'SeqNum' have been SACKed. Otherwise, the
760  // > routine returns false.
761  for (it = segment; it != m_highestSack.first; ++it)
762  {
763  if (beginOfCurrentPacket >= m_highestSack.second)
764  {
765  NS_LOG_INFO ("seq=" << seq << " is not lost because there are no sacked segment ahead");
766  return false;
767  }
768 
769  item = *it;
770  current = item->m_packet;
771 
772  if (item->m_sacked)
773  {
774  NS_LOG_INFO ("Segment [" << beginOfCurrentPacket << ", " <<
775  beginOfCurrentPacket+item->m_packet->GetSize () <<
776  "] found to be SACKed");
777  ++count;
778  bytes += current->GetSize ();
779  if ((count >= dupThresh) || (bytes > (dupThresh-1) * segmentSize))
780  {
781  NS_LOG_INFO ("seq=" << seq << " is lost because of 3 sacked blocks ahead");
782  return true;
783  }
784  }
785 
786  beginOfCurrentPacket += current->GetSize ();
787  }
788 
789  return false;
790 }
791 
792 bool
793 TcpTxBuffer::IsLost (const SequenceNumber32 &seq, uint32_t dupThresh,
794  uint32_t segmentSize) const
795 {
796  NS_LOG_FUNCTION (this << seq << dupThresh);
797 
798  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
799  PacketList::const_iterator it;
800 
801  if (seq >= m_highestSack.second)
802  {
803  return false;
804  }
805 
806  // This O(n) method is called only once, and outside this class.
807  // It should not harm the performance
808  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
809  {
810  // Search for the right iterator before calling IsLost()
811  if (beginOfCurrentPacket >= seq)
812  {
813  return IsLost (beginOfCurrentPacket, it, dupThresh, segmentSize);
814  }
815 
816  beginOfCurrentPacket += (*it)->m_packet->GetSize ();
817  }
818 
819  return false;
820 }
821 
822 bool
823 TcpTxBuffer::NextSeg (SequenceNumber32 *seq, uint32_t dupThresh,
824  uint32_t segmentSize, bool isRecovery) const
825 {
826  NS_LOG_FUNCTION (this);
827 
828  /* RFC 6675, NextSeg definition.
829  *
830  * (1) If there exists a smallest unSACKed sequence number 'S2' that
831  * meets the following three criteria for determining loss, the
832  * sequence range of one segment of up to SMSS octets starting
833  * with S2 MUST be returned.
834  *
835  * (1.a) S2 is greater than HighRxt.
836  *
837  * (1.b) S2 is less than the highest octet covered by any
838  * received SACK.
839  *
840  * (1.c) IsLost (S2) returns true.
841  */
842  PacketList::const_iterator it;
843  TcpTxItem *item;
844  SequenceNumber32 seqPerRule3;
845  bool isSeqPerRule3Valid = false;
846  SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
847 
848  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
849  {
850  item = *it;
851 
852  // Condition 1.a , 1.b , and 1.c
853  if (item->m_retrans == false && item->m_sacked == false)
854  {
855  if (IsLost (beginOfCurrentPkt, it, dupThresh, segmentSize))
856  {
857  *seq = beginOfCurrentPkt;
858  return true;
859  }
860  else if (seqPerRule3.GetValue () == 0 && isRecovery)
861  {
862  isSeqPerRule3Valid = true;
863  seqPerRule3 = beginOfCurrentPkt;
864  }
865  }
866 
867  // Nothing found, iterate
868  beginOfCurrentPkt += item->m_packet->GetSize ();
869  }
870 
871  /* (2) If no sequence number 'S2' per rule (1) exists but there
872  * exists available unsent data and the receiver's advertised
873  * window allows, the sequence range of one segment of up to SMSS
874  * octets of previously unsent data starting with sequence number
875  * HighData+1 MUST be returned.
876  */
878  {
879  *seq = m_firstByteSeq + m_sentSize;
880  return true;
881  }
882 
883  /* (3) If the conditions for rules (1) and (2) fail, but there exists
884  * an unSACKed sequence number 'S3' that meets the criteria for
885  * detecting loss given in steps (1.a) and (1.b) above
886  * (specifically excluding step (1.c)), then one segment of up to
887  * SMSS octets starting with S3 SHOULD be returned.
888  */
889  if (isSeqPerRule3Valid)
890  {
891  *seq = seqPerRule3;
892  return true;
893  }
894 
895  /* (4) If the conditions for (1), (2), and (3) fail, but there exists
896  * outstanding unSACKed data, we provide the opportunity for a
897  * single "rescue" retransmission per entry into loss recovery.
898  * If HighACK is greater than RescueRxt (or RescueRxt is
899  * undefined), then one segment of up to SMSS octets that MUST
900  * include the highest outstanding unSACKed sequence number
901  * SHOULD be returned, and RescueRxt set to RecoveryPoint.
902  * HighRxt MUST NOT be updated.
903  *
904  * This point require too much interaction between us and TcpSocketBase.
905  * We choose to not respect the SHOULD (allowed from RFC MUST/SHOULD definition)
906  */
907  return false;
908 }
909 
910 uint32_t
912 {
913  NS_LOG_FUNCTION (this);
914  PacketList::const_iterator it;
915  TcpTxItem *item;
916  uint32_t count = 0;
917  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
918  {
919  item = *it;
920  if (item->m_retrans)
921  {
922  count++;
923  }
924  }
925  return count;
926 }
927 
928 uint32_t
929 TcpTxBuffer::BytesInFlight (uint32_t dupThresh, uint32_t segmentSize) const
930 {
931  PacketList::const_iterator it;
932  TcpTxItem *item;
933  uint32_t size = 0; // "pipe" in RFC
934  SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
935 
936  // After initializing pipe to zero, the following steps are taken for each
937  // octet 'S1' in the sequence space between HighACK and HighData that has not
938  // been SACKed:
939  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
940  {
941  item = *it;
942  if (!item->m_sacked)
943  {
944  // (a) If IsLost (S1) returns false: Pipe is incremented by 1 octet.
945  if (!IsLost (beginOfCurrentPkt, it, dupThresh, segmentSize))
946  {
947  size += item->m_packet->GetSize ();
948  }
949  // (b) If S1 <= HighRxt: Pipe is incremented by 1 octet.
950  // (NOTE: we use the m_retrans flag instead of keeping and updating
951  // another variable). Only if the item is not marked as lost
952  else if (item->m_retrans && !item->m_lost)
953  {
954  size += item->m_packet->GetSize ();
955  }
956  }
957  beginOfCurrentPkt += item->m_packet->GetSize ();
958  }
959 
960  return size;
961 }
962 
963 void
965 {
966  NS_LOG_FUNCTION (this);
967 
968  PacketList::iterator it;
969  SequenceNumber32 beginOfCurrentPkt = m_firstByteSeq;
970 
971  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
972  {
973  (*it)->m_sacked = false;
974  beginOfCurrentPkt += (*it)->m_packet->GetSize ();
975  }
976 
977  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
978 }
979 
980 void
981 TcpTxBuffer::ResetSentList (uint32_t keepItems)
982 {
983  NS_LOG_FUNCTION (this);
984  TcpTxItem *item;
985 
986  // Keep the head items; they will then marked as lost
987  while (m_sentList.size () > keepItems)
988  {
989  item = m_sentList.back ();
990  item->m_retrans = item->m_sacked = false;
991  m_appList.push_front (item);
992  m_sentList.pop_back ();
993  }
994 
995  if (m_sentList.size () > 0)
996  {
997  item = m_sentList.back ();
998  item->m_lost = true;
999  item->m_sacked = false;
1000  item->m_retrans = false;
1001  m_sentSize = item->m_packet->GetSize ();
1002  }
1003  else
1004  {
1005  m_sentSize = 0;
1006  }
1007 
1008  m_highestSack = std::make_pair (m_sentList.end (), SequenceNumber32 (0));
1009 }
1010 
1011 void
1013 {
1014  NS_LOG_FUNCTION (this);
1015  if (!m_sentList.empty ())
1016  {
1017  TcpTxItem *item = m_sentList.back ();
1018 
1019  m_sentList.pop_back ();
1020  m_sentSize -= item->m_packet->GetSize ();
1021  m_appList.insert (m_appList.begin (), item);
1022  }
1023 }
1024 
1025 void
1027 {
1028  NS_LOG_FUNCTION (this);
1029 
1030  PacketList::iterator it;
1031 
1032  for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
1033  {
1034  (*it)->m_lost = true;
1035  }
1036 }
1037 
1038 bool
1040 {
1041  NS_LOG_FUNCTION (this);
1042 
1043  if (m_sentSize == 0)
1044  {
1045  return false;
1046  }
1047 
1048  NS_ASSERT (m_sentList.size () > 0);
1049  return m_sentList.front ()->m_retrans;
1050 }
1051 
1053 TcpTxBuffer::CraftSackOption (const SequenceNumber32 &seq, uint8_t available) const
1054 {
1055  NS_LOG_FUNCTION (this);
1056  Ptr<TcpOptionSack> sackBlock = 0;
1057  SequenceNumber32 beginOfCurrentPacket = m_firstByteSeq;
1058  Ptr<Packet> current;
1059  TcpTxItem *item;
1060 
1061  NS_LOG_INFO ("Crafting a SACK block, available bytes: " << (uint32_t) available <<
1062  " from seq: " << seq << " buffer starts at seq " << m_firstByteSeq);
1063 
1064  PacketList::const_iterator it;
1065  if (m_highestSack.first == m_sentList.end ())
1066  {
1067  it = m_sentList.begin ();
1068  }
1069  else
1070  {
1071  it = m_highestSack.first;
1072  beginOfCurrentPacket = m_highestSack.second;
1073  }
1074 
1075  while (it != m_sentList.end ())
1076  {
1077  item = *it;
1078  current = item->m_packet;
1079 
1080  SequenceNumber32 endOfCurrentPacket = beginOfCurrentPacket + current->GetSize ();
1081 
1082  // The first segment could not be sacked.. otherwise would be a
1083  // cumulative ACK :)
1084  if (item->m_sacked || it == m_sentList.begin ())
1085  {
1086  NS_LOG_DEBUG ("Analyzing segment: [" << beginOfCurrentPacket <<
1087  ";" << endOfCurrentPacket << "], not usable, sacked=" <<
1088  item->m_sacked);
1089  beginOfCurrentPacket += current->GetSize ();
1090  }
1091  else if (seq > beginOfCurrentPacket)
1092  {
1093  NS_LOG_DEBUG ("Analyzing segment: [" << beginOfCurrentPacket <<
1094  ";" << endOfCurrentPacket << "], not usable, sacked=" <<
1095  item->m_sacked);
1096  beginOfCurrentPacket += current->GetSize ();
1097  }
1098  else
1099  {
1100  // RFC 2018: The first SACK block MUST specify the contiguous
1101  // block of data containing the segment which triggered this ACK.
1102  // Since we are hand-crafting this, select the first non-sacked block.
1103  sackBlock = CreateObject <TcpOptionSack> ();
1104  sackBlock->AddSackBlock (TcpOptionSack::SackBlock (beginOfCurrentPacket,
1105  endOfCurrentPacket));
1106  NS_LOG_DEBUG ("Analyzing segment: [" << beginOfCurrentPacket <<
1107  ";" << endOfCurrentPacket << "] and found to be usable");
1108 
1109  // RFC 2018: The data receiver SHOULD include as many distinct SACK
1110  // blocks as possible in the SACK option
1111  // The SACK option SHOULD be filled out by repeating the most
1112  // recently reported SACK blocks that are not subsets of a SACK block
1113  // already included
1114  // This means go backward until we finish space and include already SACKed block
1115  while (sackBlock->GetSerializedSize () + 8 < available)
1116  {
1117  --it;
1118 
1119  if (it == m_sentList.begin ())
1120  {
1121  return sackBlock;
1122  }
1123 
1124  item = *it;
1125  current = item->m_packet;
1126  endOfCurrentPacket = beginOfCurrentPacket;
1127  beginOfCurrentPacket -= current->GetSize ();
1128  sackBlock->AddSackBlock (TcpOptionSack::SackBlock (beginOfCurrentPacket,
1129  endOfCurrentPacket));
1130  NS_LOG_DEBUG ("Filling the option: Adding [" << beginOfCurrentPacket <<
1131  ";" << endOfCurrentPacket << "], available space now : " <<
1132  (uint32_t) (available - sackBlock->GetSerializedSize ()));
1133  NS_ASSERT (beginOfCurrentPacket > m_firstByteSeq);
1134  }
1135 
1136  return sackBlock;
1137  }
1138 
1139  ++it;
1140  }
1141 
1142  return sackBlock;
1143 }
1144 
1145 std::ostream &
1146 operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf)
1147 {
1148  TcpTxBuffer::PacketList::const_iterator it;
1149  std::stringstream ss;
1150  SequenceNumber32 beginOfCurrentPacket = tcpTxBuf.m_firstByteSeq;
1151  uint32_t sentSize = 0, appSize = 0;
1152 
1153  Ptr<Packet> p;
1154  for (it = tcpTxBuf.m_sentList.begin (); it != tcpTxBuf.m_sentList.end (); ++it)
1155  {
1156  p = (*it)->m_packet;
1157  ss << "[" << beginOfCurrentPacket << ";"
1158  << beginOfCurrentPacket + p->GetSize () << "|" << p->GetSize () << "|";
1159  (*it)->Print (ss);
1160  ss << "]";
1161  sentSize += p->GetSize ();
1162  beginOfCurrentPacket += p->GetSize ();
1163  }
1164 
1165  for (it = tcpTxBuf.m_appList.begin (); it != tcpTxBuf.m_appList.end (); ++it)
1166  {
1167  appSize += (*it)->m_packet->GetSize ();
1168  }
1169 
1170  os << "Sent list: " << ss.str () << ", size = " << tcpTxBuf.m_sentList.size () <<
1171  " Total size: " << tcpTxBuf.m_size <<
1172  " m_firstByteSeq = " << tcpTxBuf.m_firstByteSeq <<
1173  " m_sentSize = " << tcpTxBuf.m_sentSize;
1174 
1175  NS_ASSERT (sentSize == tcpTxBuf.m_sentSize);
1176  NS_ASSERT (tcpTxBuf.m_size - tcpTxBuf.m_sentSize == appSize);
1177  return os;
1178 }
1179 
1180 } // namepsace ns3
TcpTxItem()
Constructor.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:102
bool m_retrans
Indicates if the segment is retransmitted.
Definition: tcp-tx-buffer.h:61
uint32_t Size(void) const
Returns total number of bytes in this buffer.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
void ResetSentList(uint32_t keepItems=1)
Reset the sent list.
TcpTxItem * GetTransmittedSegment(uint32_t numBytes, const SequenceNumber32 &seq)
Get a block of data previously transmitted.
NUMERIC_TYPE GetValue() const
Extracts the numeric value of the sequence number.
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
#define min(a, b)
Definition: 80211b.c:44
uint32_t SizeFromSequence(const SequenceNumber32 &seq) const
Returns the number of bytes from the buffer in the range [seq, tailSequence)
void ResetLastSegmentSent()
Take the last segment sent and put it back into the un-sent list (at the beginning) ...
bool IsLost(const SequenceNumber32 &seq, uint32_t dupThresh, uint32_t segmentSize) const
Check if a segment is lost per RFC 6675.
SequenceNumber32 HeadSequence(void) const
Get the sequence number of the buffer head.
PacketList m_appList
Buffer for application data.
std::list< SackBlock > SackList
SACK list definition.
#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
uint32_t MaxBufferSize(void) const
Get the maximum buffer size.
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:201
uint32_t GetSize(void) const
Returns the the size in bytes of the packet (including the zero-filled initial payload).
Definition: packet.h:796
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:277
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
T Get(void) const
Get the underlying value.
Definition: traced-value.h:218
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
Ptr< Packet > CreateFragment(uint32_t start, uint32_t length) const
Create a new packet which contains a fragment of the original packet.
Definition: packet.cc:227
void AddAtEnd(Ptr< const Packet > packet)
Concatenate the input packet at the end of the current packet.
Definition: packet.cc:312
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:197
Item that encloses the application packet and some flags for it.
Definition: tcp-tx-buffer.h:39
void RemoveAtStart(uint32_t size)
Remove size bytes from the start of the current packet.
Definition: packet.cc:339
uint32_t GetRetransmitsCount(void) const
Return the number of segments in the sent list that have been transmitted more than once...
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.
Definition: tcp-tx-buffer.h:62
uint32_t BytesInFlight(uint32_t dupThresh, uint32_t segmentSize) const
Return total bytes in flight.
uint32_t m_sentSize
Size of sent (and not discarded) segments.
void SetSentListLost()
Set the entire sent list as lost (typically after an RTO)
std::list< TcpTxItem * > PacketList
container for data stored in the buffer
uint32_t m_size
Size of all data in this buffer.
Tcp sender buffer.
void SetMaxBufferSize(uint32_t n)
Set the maximum buffer size.
#define list
std::pair< PacketList::const_iterator, SequenceNumber32 > m_highestSack
Highest SACK byte.
uint32_t Available(void) const
Returns the available capacity of this buffer.
std::ostream & operator<<(std::ostream &os, const Angles &a)
print a struct Angles to output
Definition: angles.cc:42
Ptr< Packet > Copy(void) const
performs a COW copy of the packet.
Definition: packet.cc:121
Ptr< Packet > m_packet
Application packet.
Definition: tcp-tx-buffer.h:59
Every class exported by the ns3 library is enclosed in the ns3 namespace.
bool NextSeg(SequenceNumber32 *seq, uint32_t dupThresh, uint32_t segmentSize, bool isRecovery) const
Get the next sequence number to transmit, according to RFC 6675.
bool Add(Ptr< Packet > p)
Append a data packet to the end of the buffer.
static TypeId GetTypeId(void)
Get the type ID.
static Time Now(void)
Return the current simulation virtual time.
Definition: simulator.cc:249
void SplitItems(TcpTxItem &t1, TcpTxItem &t2, uint32_t size) const
Split one TcpTxItem.
NS_LOG_LOGIC("Net device "<< nd<< " is not bridged")
std::pair< SequenceNumber32, SequenceNumber32 > SackBlock
SACK block definition.
bool Update(const TcpOptionSack::SackList &list)
Update the scoreboard.
bool IsHeadRetransmitted() const
Check if the head is retransmitted.
#define NS_ABORT_MSG_UNLESS(cond, msg)
Abnormal program termination if a condition is false, with a message.
Definition: abort.h:144
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition: log.h:261
PacketList m_sentList
Buffer for sent (but not acked) data.
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:269
uint32_t m_maxBuffer
Max number of data bytes in buffer (SND.WND)
void ResetScoreboard()
Reset the Scoreboard from all SACK informations.
TcpTxItem * GetNewSegment(uint32_t numBytes)
Get a block of data not transmitted yet and move it into SentList.
TcpTxItem * GetPacketFromList(PacketList &list, const SequenceNumber32 &startingSeq, uint32_t numBytes, const SequenceNumber32 &requestedSeq, bool *listEdited) const
Get a block (which is returned as Packet) from a list.
Ptr< const TcpOptionSack > CraftSackOption(const SequenceNumber32 &seq, uint8_t available) const
Craft a SACK block.
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:253
A base class which provides memory management and object aggregation.
Definition: object.h:87
TcpTxBuffer(uint32_t n=0)
Constructor.
virtual ~TcpTxBuffer(void)
void MergeItems(TcpTxItem &t1, TcpTxItem &t2) const
Merge two TcpTxItem.
void Print(std::ostream &os) const
Print the time.
bool m_sacked
Indicates if the segment has been SACKed.
Definition: tcp-tx-buffer.h:64
a unique identifier for an interface.
Definition: type-id.h:58
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:914
SequenceNumber< uint32_t, int32_t > SequenceNumber32
32 bit Sequence number.
bool m_lost
Indicates if the segment has been lost (RTO)
Definition: tcp-tx-buffer.h:60
SequenceNumber32 TailSequence(void) const
Get the sequence number of the buffer tail (plus one)
std::pair< TcpTxBuffer::PacketList::const_iterator, SequenceNumber32 > GetHighestSacked() const
Find the highest SACK byte.
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.