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_isCwndLimited)
193 {
194 NS_LOG_DEBUG("No increase because current cwnd " << tcb->m_cWnd
195 << " is not limiting the flow");
196 return;
197 }
198
199 if (tcb->m_cWnd < tcb->m_ssThresh)
200 {
201 if (m_hystart && tcb->m_lastAckedSeq > m_endSeq)
202 {
203 HystartReset(tcb);
204 }
205
206 // In Linux, the QUICKACK socket option enables the receiver to send
207 // immediate acks initially (during slow start) and then transition
208 // to delayed acks. ns-3 does not implement QUICKACK, and if ack
209 // counting instead of byte counting is used during slow start window
210 // growth, when TcpSocket::DelAckCount==2, then the slow start will
211 // not reach as large of an initial window as in Linux. Therefore,
212 // we can approximate the effect of QUICKACK by making this slow
213 // start phase perform Appropriate Byte Counting (RFC 3465)
214 tcb->m_cWnd += segmentsAcked * tcb->m_segmentSize;
215 segmentsAcked = 0;
216
217 NS_LOG_INFO("In SlowStart, updated to cwnd " << tcb->m_cWnd << " ssthresh "
218 << tcb->m_ssThresh);
219 }
220
221 if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
222 {
223 m_cWndCnt += segmentsAcked;
224 uint32_t cnt = Update(tcb, segmentsAcked);
225
226 /* According to RFC 6356 even once the new cwnd is
227 * calculated you must compare this to the number of ACKs received since
228 * the last cwnd update. If not enough ACKs have been received then cwnd
229 * cannot be updated.
230 */
231 if (m_cWndCnt >= cnt)
232 {
233 tcb->m_cWnd += tcb->m_segmentSize;
234 m_cWndCnt -= cnt;
235 NS_LOG_INFO("In CongAvoid, updated to cwnd " << tcb->m_cWnd);
236 }
237 else
238 {
239 NS_LOG_INFO("Not enough segments have been ACKed to increment cwnd."
240 "Until now "
241 << m_cWndCnt << " cnd " << cnt);
242 }
243 }
244}
245
248{
249 NS_LOG_FUNCTION(this);
250 Time t;
251 uint32_t delta;
252 uint32_t bicTarget;
253 uint32_t cnt = 0;
254 uint32_t maxCnt;
255 double offs;
256 uint32_t segCwnd = tcb->GetCwndInSegments();
257
258 m_ackCnt += segmentsAcked;
259
260 if (m_epochStart == Time::Min())
261 {
262 m_epochStart = Simulator::Now(); // record the beginning of an epoch
263 m_ackCnt = segmentsAcked;
264 m_tcpCwnd = segCwnd;
265
266 if (m_lastMaxCwnd <= segCwnd)
267 {
268 NS_LOG_DEBUG("lastMaxCwnd <= m_cWnd. K=0 and origin=" << segCwnd);
269 m_bicK = 0.0;
270 m_bicOriginPoint = segCwnd;
271 }
272 else
273 {
274 m_bicK = std::pow((m_lastMaxCwnd - segCwnd) / m_c, 1 / 3.);
276 NS_LOG_DEBUG("lastMaxCwnd > m_cWnd. K=" << m_bicK << " and origin=" << m_lastMaxCwnd);
277 }
278 }
279
281
282 if (t.GetSeconds() < m_bicK) /* t - K */
283 {
284 offs = m_bicK - t.GetSeconds();
285 NS_LOG_DEBUG("t=" << t.GetSeconds() << " <k: offs=" << offs);
286 }
287 else
288 {
289 offs = t.GetSeconds() - m_bicK;
290 NS_LOG_DEBUG("t=" << t.GetSeconds() << " >= k: offs=" << offs);
291 }
292
293 /* Constant value taken from Experimental Evaluation of Cubic Tcp, available at
294 * eprints.nuim.ie/1716/1/Hamiltonpfldnet2007_cubic_final.pdf */
295 delta = m_c * std::pow(offs, 3);
296
297 NS_LOG_DEBUG("delta: " << delta);
298
299 if (t.GetSeconds() < m_bicK)
300 {
301 // below origin
302 bicTarget = m_bicOriginPoint - delta;
303 NS_LOG_DEBUG("t < k: Bic Target: " << bicTarget);
304 }
305 else
306 {
307 // above origin
308 bicTarget = m_bicOriginPoint + delta;
309 NS_LOG_DEBUG("t >= k: Bic Target: " << bicTarget);
310 }
311
312 // Next the window target is converted into a cnt or count value. CUBIC will
313 // wait until enough new ACKs have arrived that a counter meets or exceeds
314 // this cnt value. This is how the CUBIC implementation simulates growing
315 // cwnd by values other than 1 segment size.
316 if (bicTarget > segCwnd)
317 {
318 cnt = segCwnd / (bicTarget - segCwnd);
319 NS_LOG_DEBUG("target>cwnd. cnt=" << cnt);
320 }
321 else
322 {
323 cnt = 100 * segCwnd;
324 }
325
326 if (m_lastMaxCwnd == 0 && cnt > m_cntClamp)
327 {
328 cnt = m_cntClamp;
329 }
330
332 {
333 auto scale = static_cast<uint32_t>(8 * (1024 + m_beta * 1024) / 3 / (1024 - m_beta * 1024));
334 delta = (segCwnd * scale) >> 3;
335 while (m_ackCnt > delta)
336 {
337 m_ackCnt -= delta;
338 m_tcpCwnd++;
339 }
340 if (m_tcpCwnd > segCwnd)
341 {
342 delta = m_tcpCwnd - segCwnd;
343 maxCnt = segCwnd / delta;
344 if (cnt > maxCnt)
345 {
346 cnt = maxCnt;
347 }
348 }
349 }
350
351 // The maximum rate of cwnd increase CUBIC allows is 1 packet per
352 // 2 packets ACKed, meaning cwnd grows at 1.5x per RTT.
353 return std::max(cnt, 2U);
354}
355
356void
358{
359 NS_LOG_FUNCTION(this << tcb << segmentsAcked << rtt);
360
361 /* Discard delay samples right after fast recovery */
363 {
364 return;
365 }
366
367 /* first time call or link delay decreases */
368 if (m_delayMin == Time::Min() || m_delayMin > rtt)
369 {
370 m_delayMin = rtt;
371 }
372
373 /* hystart triggers when cwnd is larger than some threshold */
374 if (m_hystart && tcb->m_cWnd <= tcb->m_ssThresh &&
375 tcb->m_cWnd >= m_hystartLowWindow * tcb->m_segmentSize)
376 {
377 HystartUpdate(tcb, rtt);
378 }
379}
380
381void
383{
384 NS_LOG_FUNCTION(this << delay);
385
386 if (!m_found)
387 {
388 Time now = Simulator::Now();
389
390 /* first detection parameter - ack-train detection */
391 if ((now - m_lastAck) <= m_hystartAckDelta)
392 {
393 m_lastAck = now;
394
395 if ((now - m_roundStart) > m_delayMin)
396 {
399 {
400 m_found = true;
401 }
402 }
403 }
404
405 /* obtain the minimum delay of more than sampling packets */
407 {
408 if (m_currRtt == Time::Min() || m_currRtt > delay)
409 {
410 m_currRtt = delay;
411 }
412
413 ++m_sampleCnt;
414 }
416 {
419 {
420 m_found = true;
421 }
422 }
423
424 /*
425 * Either one of two conditions are met,
426 * we exit from slow start immediately.
427 */
428 if (m_found)
429 {
430 NS_LOG_DEBUG("Exit from SS, immediately :-)");
431 tcb->m_ssThresh = tcb->m_cWnd;
432 }
433 }
434}
435
436Time
438{
439 NS_LOG_FUNCTION(this << t);
440
441 Time ret = t;
442 if (t > m_hystartDelayMax)
443 {
444 ret = m_hystartDelayMax;
445 }
446 else if (t < m_hystartDelayMin)
447 {
448 ret = m_hystartDelayMin;
449 }
450
451 return ret;
452}
453
456{
457 NS_LOG_FUNCTION(this << tcb << bytesInFlight);
458
459 uint32_t segCwnd = tcb->GetCwndInSegments();
460 NS_LOG_DEBUG("Loss at cWnd=" << segCwnd
461 << " segments in flight=" << bytesInFlight / tcb->m_segmentSize);
462
463 /* Wmax and fast convergence */
464 if (segCwnd < m_lastMaxCwnd && m_fastConvergence)
465 {
466 m_lastMaxCwnd = (segCwnd * (1 + m_beta)) / 2; // Section 4.6 in RFC 8312
467 }
468 else
469 {
470 m_lastMaxCwnd = segCwnd;
471 }
472
473 m_epochStart = Time::Min(); // end of epoch
474
475 /* Formula taken from the Linux kernel */
476 uint32_t ssThresh = std::max(static_cast<uint32_t>(segCwnd * m_beta), 2U) * tcb->m_segmentSize;
477
478 NS_LOG_DEBUG("SsThresh = " << ssThresh);
479
480 return ssThresh;
481}
482
483void
485{
486 NS_LOG_FUNCTION(this << tcb << newState);
487
488 if (newState == TcpSocketState::CA_LOSS)
489 {
490 CubicReset(tcb);
491 HystartReset(tcb);
492 }
493}
494
495void
497{
498 NS_LOG_FUNCTION(this << tcb);
499
501 m_bicK = 0;
502 m_ackCnt = 0;
503 m_tcpCwnd = 0;
505 m_found = false;
506}
507
510{
511 NS_LOG_FUNCTION(this);
512 return CopyObject<TcpCubic>(this);
513}
514
515} // namespace ns3
#define Min(a, b)
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:455
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:247
Ptr< TcpCongestionOps > Fork() override
Copy the congestion control algorithm across sockets.
Definition: tcp-cubic.cc:509
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:357
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:484
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:382
double m_c
Cubic Scaling factor.
Definition: tcp-cubic.h:121
void CubicReset(Ptr< const TcpSocketState > tcb)
Reset Cubic parameters.
Definition: tcp-cubic.cc:496
@ 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:437
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
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
#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:1331
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:1407
Ptr< const AttributeAccessor > MakeUintegerAccessor(T1 a1)
Definition: uinteger.h:46
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
Ptr< const AttributeAccessor > MakeBooleanAccessor(T1 a1)
Definition: boolean.h:81
Ptr< const AttributeAccessor > MakeDoubleAccessor(T1 a1)
Definition: double.h:43
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition: nstime.h:1427