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 * 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
28NS_LOG_COMPONENT_DEFINE("TcpCubic");
29
30namespace ns3
31{
32
34
35TypeId
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",
72 MakeEnumAccessor<HybridSSDetectionMode>(&TcpCubic::m_hystartDetect),
74 "PACKET_TRAIN",
76 "DELAY",
78 "BOTH"))
79 .AddAttribute("HyStartMinSamples",
80 "Number of delay samples for detecting the increase of delay",
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",
99 .AddAttribute("CubicDelta",
100 "Delta Time to wait after fast recovery before adjusting param",
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
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
170std::string
172{
173 return "TcpCubic";
174}
175
176void
178{
179 NS_LOG_FUNCTION(this);
180
182 m_endSeq = tcb->m_highTxMark;
184 m_sampleCnt = 0;
185}
186
187void
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
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
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
349void
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
374void
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 {
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 {
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
429Time
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
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
476void
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
488void
490{
491 NS_LOG_FUNCTION(this << tcb);
492
493 m_lastMaxCwnd = 0;
495 m_bicK = 0;
496 m_ackCnt = 0;
497 m_tcpCwnd = 0;
499 m_found = false;
500}
501
504{
505 NS_LOG_FUNCTION(this);
506 return CopyObject<TcpCubic>(this);
507}
508
509} // namespace ns3
#define Min(a, b)
AttributeValue implementation for Boolean.
Definition: boolean.h:37
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
@ DELAY
Detection by delay value.
Definition: tcp-cubic.h:78
@ PACKET_TRAIN
Detection by trains of packet.
Definition: tcp-cubic.h:77
@ BOTH
Detection by both.
Definition: tcp-cubic.h:79
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.
TcpCongState_t
Definition of the Congestion state machine.
@ CA_LOSS
CWND was reduced due to RTO timeout or SACK reneging.
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
AttributeValue implementation for Time.
Definition: nstime.h:1413
a unique identifier for an interface.
Definition: type-id.h:59
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:932
Hold an unsigned integer type.
Definition: uinteger.h:45
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
Ptr< const AttributeChecker > MakeBooleanChecker()
Definition: boolean.cc:124
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1434
Ptr< const AttributeAccessor > MakeTimeAccessor(T1 a1)
Definition: nstime.h:1414
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
#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 > MakeEnumChecker(T v, std::string n, Ts... args)
Make an EnumChecker pre-configured with a set of allowed values by name.
Definition: enum.h:189