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