A Discrete-Event Network Simulator
API
tcp-cubic.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Natale Patriciello <natale.patriciello@gmail.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation;
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  */
18 
19 #define NS_LOG_APPEND_CONTEXT \
20  { \
21  std::clog << Simulator::Now().GetSeconds() << " "; \
22  }
23 
24 #include "tcp-cubic.h"
25 
26 #include "ns3/log.h"
27 
28 NS_LOG_COMPONENT_DEFINE("TcpCubic");
29 
30 namespace ns3
31 {
32 
34 
35 TypeId
37 {
38  static TypeId tid =
39  TypeId("ns3::TcpCubic")
41  .AddConstructor<TcpCubic>()
42  .SetGroupName("Internet")
43  .AddAttribute("FastConvergence",
44  "Enable (true) or disable (false) fast convergence",
45  BooleanValue(true),
48  .AddAttribute("TcpFriendliness",
49  "Enable (true) or disable (false) TCP friendliness",
50  BooleanValue(true),
53  .AddAttribute("Beta",
54  "Beta for multiplicative decrease",
55  DoubleValue(0.7),
57  MakeDoubleChecker<double>(0.0))
58  .AddAttribute("HyStart",
59  "Enable (true) or disable (false) hybrid slow start algorithm",
60  BooleanValue(true),
63  .AddAttribute("HyStartLowWindow",
64  "Lower bound cWnd for hybrid slow start (segments)",
65  UintegerValue(16),
67  MakeUintegerChecker<uint32_t>())
68  .AddAttribute("HyStartDetect",
69  "Hybrid Slow Start detection mechanisms:"
70  "packet train, delay, both",
71  EnumValue(HybridSSDetectionMode::BOTH),
72  MakeEnumAccessor<HybridSSDetectionMode>(&TcpCubic::m_hystartDetect),
73  MakeEnumChecker(HybridSSDetectionMode::PACKET_TRAIN,
74  "PACKET_TRAIN",
76  "DELAY",
77  HybridSSDetectionMode::BOTH,
78  "BOTH"))
79  .AddAttribute("HyStartMinSamples",
80  "Number of delay samples for detecting the increase of delay",
81  UintegerValue(8),
83  MakeUintegerChecker<uint8_t>())
84  .AddAttribute("HyStartAckDelta",
85  "Spacing between ack's indicating train",
89  .AddAttribute("HyStartDelayMin",
90  "Minimum time for hystart algorithm",
94  .AddAttribute("HyStartDelayMax",
95  "Maximum time for hystart algorithm",
96  TimeValue(MilliSeconds(1000)),
99  .AddAttribute("CubicDelta",
100  "Delta Time to wait after fast recovery before adjusting param",
101  TimeValue(MilliSeconds(10)),
103  MakeTimeChecker())
104  .AddAttribute("CntClamp",
105  "Counter value when no losses are detected (counter is used"
106  " when incrementing cWnd in congestion avoidance, to avoid"
107  " floating point arithmetic). It is the modulo of the (avoided)"
108  " division",
109  UintegerValue(20),
111  MakeUintegerChecker<uint8_t>())
112  .AddAttribute("C",
113  "Cubic Scaling factor",
114  DoubleValue(0.4),
116  MakeDoubleChecker<double>(0.0));
117  return tid;
118 }
119 
121  : TcpCongestionOps(),
122  m_cWndCnt(0),
123  m_lastMaxCwnd(0),
124  m_bicOriginPoint(0),
125  m_bicK(0.0),
126  m_delayMin(Time::Min()),
127  m_epochStart(Time::Min()),
128  m_found(false),
129  m_roundStart(Time::Min()),
130  m_endSeq(0),
131  m_lastAck(Time::Min()),
132  m_cubicDelta(Time::Min()),
133  m_currRtt(Time::Min()),
134  m_sampleCnt(0)
135 {
136  NS_LOG_FUNCTION(this);
137 }
138 
140  : TcpCongestionOps(sock),
141  m_fastConvergence(sock.m_fastConvergence),
142  m_beta(sock.m_beta),
143  m_hystart(sock.m_hystart),
144  m_hystartDetect(sock.m_hystartDetect),
145  m_hystartLowWindow(sock.m_hystartLowWindow),
146  m_hystartAckDelta(sock.m_hystartAckDelta),
147  m_hystartDelayMin(sock.m_hystartDelayMin),
148  m_hystartDelayMax(sock.m_hystartDelayMax),
149  m_hystartMinSamples(sock.m_hystartMinSamples),
150  m_initialCwnd(sock.m_initialCwnd),
151  m_cntClamp(sock.m_cntClamp),
152  m_c(sock.m_c),
153  m_cWndCnt(sock.m_cWndCnt),
154  m_lastMaxCwnd(sock.m_lastMaxCwnd),
155  m_bicOriginPoint(sock.m_bicOriginPoint),
156  m_bicK(sock.m_bicK),
157  m_delayMin(sock.m_delayMin),
158  m_epochStart(sock.m_epochStart),
159  m_found(sock.m_found),
160  m_roundStart(sock.m_roundStart),
161  m_endSeq(sock.m_endSeq),
162  m_lastAck(sock.m_lastAck),
163  m_cubicDelta(sock.m_cubicDelta),
164  m_currRtt(sock.m_currRtt),
165  m_sampleCnt(sock.m_sampleCnt)
166 {
167  NS_LOG_FUNCTION(this);
168 }
169 
170 std::string
172 {
173  return "TcpCubic";
174 }
175 
176 void
178 {
179  NS_LOG_FUNCTION(this);
180 
182  m_endSeq = tcb->m_highTxMark;
183  m_currRtt = Time::Min();
184  m_sampleCnt = 0;
185 }
186 
187 void
188 TcpCubic::IncreaseWindow(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
189 {
190  NS_LOG_FUNCTION(this << tcb << segmentsAcked);
191 
192  if (tcb->m_cWnd < tcb->m_ssThresh)
193  {
194  if (m_hystart && tcb->m_lastAckedSeq > m_endSeq)
195  {
196  HystartReset(tcb);
197  }
198 
199  // In Linux, the QUICKACK socket option enables the receiver to send
200  // immediate acks initially (during slow start) and then transition
201  // to delayed acks. ns-3 does not implement QUICKACK, and if ack
202  // counting instead of byte counting is used during slow start window
203  // growth, when TcpSocket::DelAckCount==2, then the slow start will
204  // not reach as large of an initial window as in Linux. Therefore,
205  // we can approximate the effect of QUICKACK by making this slow
206  // start phase perform Appropriate Byte Counting (RFC 3465)
207  tcb->m_cWnd += segmentsAcked * tcb->m_segmentSize;
208  segmentsAcked = 0;
209 
210  NS_LOG_INFO("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh "
211  << tcb->m_ssThresh);
212  }
213 
214  if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
215  {
216  m_cWndCnt += segmentsAcked;
217  uint32_t cnt = Update(tcb, segmentsAcked);
218 
219  /* According to RFC 6356 even once the new cwnd is
220  * calculated you must compare this to the number of ACKs received since
221  * the last cwnd update. If not enough ACKs have been received then cwnd
222  * cannot be updated.
223  */
224  if (m_cWndCnt >= cnt)
225  {
226  tcb->m_cWnd += tcb->m_segmentSize;
227  m_cWndCnt -= cnt;
228  NS_LOG_INFO("In CongAvoid, updated to cwnd " << tcb->m_cWnd);
229  }
230  else
231  {
232  NS_LOG_INFO("Not enough segments have been ACKed to increment cwnd."
233  "Until now "
234  << m_cWndCnt << " cnd " << cnt);
235  }
236  }
237 }
238 
239 uint32_t
240 TcpCubic::Update(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
241 {
242  NS_LOG_FUNCTION(this);
243  Time t;
244  uint32_t delta;
245  uint32_t bicTarget;
246  uint32_t cnt = 0;
247  uint32_t maxCnt;
248  double offs;
249  uint32_t segCwnd = tcb->GetCwndInSegments();
250 
251  m_ackCnt += segmentsAcked;
252 
253  if (m_epochStart == Time::Min())
254  {
255  m_epochStart = Simulator::Now(); // record the beginning of an epoch
256  m_ackCnt = segmentsAcked;
257  m_tcpCwnd = segCwnd;
258 
259  if (m_lastMaxCwnd <= segCwnd)
260  {
261  NS_LOG_DEBUG("lastMaxCwnd <= m_cWnd. K=0 and origin=" << segCwnd);
262  m_bicK = 0.0;
263  m_bicOriginPoint = segCwnd;
264  }
265  else
266  {
267  m_bicK = std::pow((m_lastMaxCwnd - segCwnd) / m_c, 1 / 3.);
269  NS_LOG_DEBUG("lastMaxCwnd > m_cWnd. K=" << m_bicK << " and origin=" << m_lastMaxCwnd);
270  }
271  }
272 
274 
275  if (t.GetSeconds() < m_bicK) /* t - K */
276  {
277  offs = m_bicK - t.GetSeconds();
278  NS_LOG_DEBUG("t=" << t.GetSeconds() << " <k: offs=" << offs);
279  }
280  else
281  {
282  offs = t.GetSeconds() - m_bicK;
283  NS_LOG_DEBUG("t=" << t.GetSeconds() << " >= k: offs=" << offs);
284  }
285 
286  /* Constant value taken from Experimental Evaluation of Cubic Tcp, available at
287  * eprints.nuim.ie/1716/1/Hamiltonpfldnet2007_cubic_final.pdf */
288  delta = m_c * std::pow(offs, 3);
289 
290  NS_LOG_DEBUG("delta: " << delta);
291 
292  if (t.GetSeconds() < m_bicK)
293  {
294  // below origin
295  bicTarget = m_bicOriginPoint - delta;
296  NS_LOG_DEBUG("t < k: Bic Target: " << bicTarget);
297  }
298  else
299  {
300  // above origin
301  bicTarget = m_bicOriginPoint + delta;
302  NS_LOG_DEBUG("t >= k: Bic Target: " << bicTarget);
303  }
304 
305  // Next the window target is converted into a cnt or count value. CUBIC will
306  // wait until enough new ACKs have arrived that a counter meets or exceeds
307  // this cnt value. This is how the CUBIC implementation simulates growing
308  // cwnd by values other than 1 segment size.
309  if (bicTarget > segCwnd)
310  {
311  cnt = segCwnd / (bicTarget - segCwnd);
312  NS_LOG_DEBUG("target>cwnd. cnt=" << cnt);
313  }
314  else
315  {
316  cnt = 100 * segCwnd;
317  }
318 
319  if (m_lastMaxCwnd == 0 && cnt > m_cntClamp)
320  {
321  cnt = m_cntClamp;
322  }
323 
324  if (m_tcpFriendliness)
325  {
326  auto scale = static_cast<uint32_t>(8 * (1024 + m_beta * 1024) / 3 / (1024 - m_beta * 1024));
327  delta = (segCwnd * scale) >> 3;
328  while (m_ackCnt > delta)
329  {
330  m_ackCnt -= delta;
331  m_tcpCwnd++;
332  }
333  if (m_tcpCwnd > segCwnd)
334  {
335  delta = m_tcpCwnd - segCwnd;
336  maxCnt = segCwnd / delta;
337  if (cnt > maxCnt)
338  {
339  cnt = maxCnt;
340  }
341  }
342  }
343 
344  // The maximum rate of cwnd increase CUBIC allows is 1 packet per
345  // 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
346  return std::max(cnt, 2U);
347 }
348 
349 void
350 TcpCubic::PktsAcked(Ptr<TcpSocketState> tcb, uint32_t segmentsAcked, const Time& rtt)
351 {
352  NS_LOG_FUNCTION(this << tcb << segmentsAcked << rtt);
353 
354  /* Discard delay samples right after fast recovery */
356  {
357  return;
358  }
359 
360  /* first time call or link delay decreases */
361  if (m_delayMin == Time::Min() || m_delayMin > rtt)
362  {
363  m_delayMin = rtt;
364  }
365 
366  /* hystart triggers when cwnd is larger than some threshold */
367  if (m_hystart && tcb->m_cWnd <= tcb->m_ssThresh &&
368  tcb->m_cWnd >= m_hystartLowWindow * tcb->m_segmentSize)
369  {
370  HystartUpdate(tcb, rtt);
371  }
372 }
373 
374 void
376 {
377  NS_LOG_FUNCTION(this << delay);
378 
379  if (!m_found)
380  {
381  Time now = Simulator::Now();
382 
383  /* first detection parameter - ack-train detection */
384  if ((now - m_lastAck) <= m_hystartAckDelta)
385  {
386  m_lastAck = now;
387 
388  if ((now - m_roundStart) > m_delayMin)
389  {
390  if (m_hystartDetect == HybridSSDetectionMode::PACKET_TRAIN ||
391  m_hystartDetect == HybridSSDetectionMode::BOTH)
392  {
393  m_found = true;
394  }
395  }
396  }
397 
398  /* obtain the minimum delay of more than sampling packets */
400  {
401  if (m_currRtt == Time::Min() || m_currRtt > delay)
402  {
403  m_currRtt = delay;
404  }
405 
406  ++m_sampleCnt;
407  }
409  {
411  m_hystartDetect == HybridSSDetectionMode::BOTH)
412  {
413  m_found = true;
414  }
415  }
416 
417  /*
418  * Either one of two conditions are met,
419  * we exit from slow start immediately.
420  */
421  if (m_found)
422  {
423  NS_LOG_DEBUG("Exit from SS, immediately :-)");
424  tcb->m_ssThresh = tcb->m_cWnd;
425  }
426  }
427 }
428 
429 Time
431 {
432  NS_LOG_FUNCTION(this << t);
433 
434  Time ret = t;
435  if (t > m_hystartDelayMax)
436  {
437  ret = m_hystartDelayMax;
438  }
439  else if (t < m_hystartDelayMin)
440  {
441  ret = m_hystartDelayMin;
442  }
443 
444  return ret;
445 }
446 
447 uint32_t
449 {
450  NS_LOG_FUNCTION(this << tcb << bytesInFlight);
451 
452  uint32_t segCwnd = tcb->GetCwndInSegments();
453  NS_LOG_DEBUG("Loss at cWnd=" << segCwnd
454  << " segments in flight=" << bytesInFlight / tcb->m_segmentSize);
455 
456  /* Wmax and fast convergence */
457  if (segCwnd < m_lastMaxCwnd && m_fastConvergence)
458  {
459  m_lastMaxCwnd = (segCwnd * (1 + m_beta)) / 2; // Section 4.6 in RFC 8312
460  }
461  else
462  {
463  m_lastMaxCwnd = segCwnd;
464  }
465 
466  m_epochStart = Time::Min(); // end of epoch
467 
468  /* Formula taken from the Linux kernel */
469  uint32_t ssThresh = std::max(static_cast<uint32_t>(segCwnd * m_beta), 2U) * tcb->m_segmentSize;
470 
471  NS_LOG_DEBUG("SsThresh = " << ssThresh);
472 
473  return ssThresh;
474 }
475 
476 void
478 {
479  NS_LOG_FUNCTION(this << tcb << newState);
480 
481  if (newState == TcpSocketState::CA_LOSS)
482  {
483  CubicReset(tcb);
484  HystartReset(tcb);
485  }
486 }
487 
488 void
490 {
491  NS_LOG_FUNCTION(this << tcb);
492 
493  m_lastMaxCwnd = 0;
494  m_bicOriginPoint = 0;
495  m_bicK = 0;
496  m_ackCnt = 0;
497  m_tcpCwnd = 0;
498  m_delayMin = Time::Min();
499  m_found = false;
500 }
501 
504 {
505  NS_LOG_FUNCTION(this);
506  return CopyObject<TcpCubic>(this);
507 }
508 
509 } // namespace ns3
#define max(a, b)
Definition: 80211b.c:42
This class can be used to hold variables of floating point type such as 'double' or 'float'.
Definition: double.h:42
Hold variables of type enum.
Definition: enum.h:62
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
static Time Now()
Return the current simulation virtual time.
Definition: simulator.cc:208
Congestion control abstract class.
The Cubic Congestion Control Algorithm.
Definition: tcp-cubic.h:70
Time m_currRtt
Current Rtt.
Definition: tcp-cubic.h:136
void HystartReset(Ptr< const TcpSocketState > tcb)
Reset HyStart parameters.
Definition: tcp-cubic.cc:177
uint32_t m_ackCnt
Count the number of ACKed packets.
Definition: tcp-cubic.h:138
Time m_hystartDelayMax
Maximum time for hystart algorithm.
Definition: tcp-cubic.h:115
Time m_cubicDelta
Time to wait after recovery before update.
Definition: tcp-cubic.h:135
uint32_t m_bicOriginPoint
Origin point of bic function.
Definition: tcp-cubic.h:126
uint32_t m_sampleCnt
Count of samples for HyStart.
Definition: tcp-cubic.h:137
uint32_t GetSsThresh(Ptr< const TcpSocketState > tcb, uint32_t bytesInFlight) override
Get the slow start threshold after a loss event.
Definition: tcp-cubic.cc:448
std::string GetName() const override
Get the name of the congestion control algorithm.
Definition: tcp-cubic.cc:171
uint32_t Update(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked)
Cubic window update after a new ack received.
Definition: tcp-cubic.cc:240
Ptr< TcpCongestionOps > Fork() override
Copy the congestion control algorithm across sockets.
Definition: tcp-cubic.cc:503
bool m_hystart
Enable or disable HyStart algorithm.
Definition: tcp-cubic.h:110
double m_bicK
Time to origin point from the beginning.
Definition: tcp-cubic.h:127
uint32_t m_tcpCwnd
Estimated tcp cwnd (for Reno-friendliness)
Definition: tcp-cubic.h:139
void PktsAcked(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked, const Time &rtt) override
Timing information on received ACK.
Definition: tcp-cubic.cc:350
uint32_t m_cWndCnt
cWnd integer-to-float counter
Definition: tcp-cubic.h:124
Time m_hystartDelayMin
Minimum time for hystart algorithm.
Definition: tcp-cubic.h:114
bool m_found
The exit point is found?
Definition: tcp-cubic.h:131
SequenceNumber32 m_endSeq
End sequence of the round.
Definition: tcp-cubic.h:133
void IncreaseWindow(Ptr< TcpSocketState > tcb, uint32_t segmentsAcked) override
Congestion avoidance algorithm implementation.
Definition: tcp-cubic.cc:188
double m_beta
Beta for cubic multiplicative increase.
Definition: tcp-cubic.h:108
Time m_lastAck
Last time when the ACK spacing is close.
Definition: tcp-cubic.h:134
void CongestionStateSet(Ptr< TcpSocketState > tcb, const TcpSocketState::TcpCongState_t newState) override
Trigger events/calculations specific to a congestion state.
Definition: tcp-cubic.cc:477
static TypeId GetTypeId()
Get the type ID.
Definition: tcp-cubic.cc:36
bool m_tcpFriendliness
Enable or disable TCP-friendliness heuristic.
Definition: tcp-cubic.h:107
Time m_hystartAckDelta
Spacing between ack's indicating train.
Definition: tcp-cubic.h:113
bool m_fastConvergence
Enable or disable fast convergence algorithm.
Definition: tcp-cubic.h:106
Time m_delayMin
Min delay.
Definition: tcp-cubic.h:129
Time m_roundStart
Beginning of each round.
Definition: tcp-cubic.h:132
Time m_epochStart
Beginning of an epoch.
Definition: tcp-cubic.h:130
HybridSSDetectionMode m_hystartDetect
Detect way for HyStart algorithm.
Definition: tcp-cubic.h:111
uint8_t m_cntClamp
Modulo of the (avoided) float division for cWnd.
Definition: tcp-cubic.h:119
void HystartUpdate(Ptr< TcpSocketState > tcb, const Time &delay)
Update HyStart parameters.
Definition: tcp-cubic.cc:375
double m_c
Cubic Scaling factor.
Definition: tcp-cubic.h:121
void CubicReset(Ptr< const TcpSocketState > tcb)
Reset Cubic parameters.
Definition: tcp-cubic.cc:489
uint32_t m_lastMaxCwnd
Last maximum cWnd.
Definition: tcp-cubic.h:125
Time HystartDelayThresh(const Time &t) const
Clamp time value in a range.
Definition: tcp-cubic.cc:430
uint8_t m_hystartMinSamples
Number of delay samples for detecting the increase of delay.
Definition: tcp-cubic.h:116
uint32_t m_hystartLowWindow
Lower bound cWnd for hybrid slow start (segments)
Definition: tcp-cubic.h:112
A base class for implementation of a stream socket using TCP.
uint32_t m_segmentSize
Segment size.
uint32_t GetCwndInSegments() const
Get cwnd in segments rather than bytes.
TcpCongState_t
Definition of the Congestion state machine.
@ CA_LOSS
CWND was reduced due to RTO timeout or SACK reneging.
SequenceNumber32 m_lastAckedSeq
Last sequence ACKed.
TracedValue< uint32_t > m_cWnd
Congestion window.
TracedValue< uint32_t > m_ssThresh
Slow start threshold.
Simulation virtual time values and global simulation resolution.
Definition: nstime.h:105
double GetSeconds() const
Get an approximation of the time stored in this instance in the indicated unit.
Definition: nstime.h:403
static Time Min()
Minimum representable Time Not to be confused with Min(Time,Time).
Definition: nstime.h:287
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:931
Hold an unsigned integer type.
Definition: uinteger.h:45
int64x64_t Min(const int64x64_t &a, const int64x64_t &b)
Minimum.
Definition: int64x64.h:229
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Time MilliSeconds(uint64_t value)
Construct a Time in the indicated unit.
Definition: nstime.h:1338
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
Ptr< const AttributeChecker > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition: enum.h:194
Ptr< const AttributeChecker > MakeTimeChecker(const Time min, const Time max)
Helper to make a Time checker with bounded range.
Definition: time.cc:533
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:86
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
#define DELAY(time)
Gets the delay between a given time and the current time.