1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3 * Copyright (c) 2016 ResiliNets, ITTC, University of Kansas
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Truc Anh N. Nguyen <annguyen@ittc.ku.edu>
19 *
20 * James P.G. Sterbenz <jpgs@ittc.ku.edu>, director
21 * ResiliNets Research Group http://wiki.ittc.ku.edu/resilinets
22 * Information and Telecommunication Technology Center (ITTC)
23 * and Department of Electrical Engineering and Computer Science
24 * The University of Kansas Lawrence, KS USA.
25 */
26
27 #include "ns3/test.h"
28 #include "ns3/log.h"
29 #include "ns3/tcp-congestion-ops.h"
30 #include "ns3/tcp-socket-base.h"
31 #include "ns3/tcp-veno.h"
32
33 using namespace ns3;
34
35 NS_LOG_COMPONENT_DEFINE ("TcpVenoTestSuite");
36
37 /**
38 * \ingroup internet-test
39 * \ingroup tests
40 *
41 * \brief Testing the additive increase and multiplicative decrease of TcpVeno
42 */
43 class TcpVenoTest : public TestCase
44 {
45 public:
46 /**
47 * \brief Constructor.
48 * \param cWnd Congestion window.
49 * \param segmentSize Segment size.
50 * \param ssThresh Slow Start Threshold.
51 * \param rtt The RTT.
52 * \param segmentsAcked Number of segments acked.
53 * \param numRtt Number of RTTs (i.e., rounds) in the test.
54 * \param name Test description.
55 */
56 TcpVenoTest (uint32_t cWnd,
57 uint32_t segmentSize,
58 uint32_t ssThresh,
59 Time rtt,
60 uint32_t segmentsAcked,
61 uint32_t numRtt,
62 const std::string &name);
63
64 private:
65 virtual void DoRun (void);
66
67 /**
68 * \brief TCP Veno additive increase formula.
69 * \param state The congestion control state.
70 * \param diff The difference between actual and expected throughput.
71 * \param beta TCP Veno beta param.
72 */
73 void AdditiveIncrease (Ptr<TcpSocketState> state, uint32_t diff, UintegerValue beta);
74
75 /**
76 * \brief TCP Veno multiplicative decrease formula.
77 * \param diff The difference between actual and expected throughput.
78 * \param beta TCP Veno beta param.
79 * \param bytesInFlight Bytes in flight.
80 * \returns The calculated SsThresh.
81 */
82 uint32_t MultiplicativeDecrease (uint32_t diff, const UintegerValue &beta, uint32_t bytesInFlight);
83
84 /**
85 * \brief Mimics the NewReno IncreaseWindow algorithm.
86 * \param state TCP socket state.
87 * \param segmentsAcked Number of segments acked.
88 */
89 void NewReno_IncreaseWindow (Ptr<TcpSocketState> state, uint32_t segmentsAcked);
90
91 /**
92 * \brief Mimics the NewReno SlowStart algorithm.
93 * \param state TCP socket state.
94 * \param segmentsAcked Number of segments acked.
95 * \returns The number of segments that could be sent.
96 */
97 uint32_t NewReno_SlowStart (Ptr<TcpSocketState> state, uint32_t segmentsAcked);
98
99 /**
100 * \brief Mimics the NewReno Congestion Avoidance algorithm.
101 * \param state TCP socket state.
102 * \param segmentsAcked Number of segments acked.
103 */
104 void NewReno_CongestionAvoidance (Ptr<TcpSocketState> state, uint32_t segmentsAcked);
105
106
107 uint32_t m_cWnd; //!< Congestion window.
108 uint32_t m_segmentSize; //!< Segment size.
109 uint32_t m_ssThresh; //!< Slow Start Threshold.
110 Time m_rtt; //!< RTT.
111 uint32_t m_segmentsAcked; //!< Number of segments ACKed.
112 uint32_t m_numRtt; //!< Number of RTT (i.e., rounds) of the test.
113 bool m_inc; //!< Internal flag to increase every other round.
114 Ptr<TcpSocketState> m_state; //!< TCP socket state.
115 };
116
TcpVenoTest(uint32_t cWnd,uint32_t segmentSize,uint32_t ssThresh,Time rtt,uint32_t segmentsAcked,uint32_t numRtt,const std::string & name)117 TcpVenoTest::TcpVenoTest (uint32_t cWnd,
118 uint32_t segmentSize,
119 uint32_t ssThresh,
120 Time rtt,
121 uint32_t segmentsAcked,
122 uint32_t numRtt,
123 const std::string &name)
124 : TestCase (name),
125 m_cWnd (cWnd),
126 m_segmentSize (segmentSize),
127 m_ssThresh (ssThresh),
128 m_rtt (rtt),
129 m_segmentsAcked (segmentsAcked),
130 m_numRtt (numRtt),
131 m_inc (true)
132 {
133 }
134
135 void
DoRun()136 TcpVenoTest::DoRun ()
137 {
138 m_state = CreateObject<TcpSocketState> ();
139
140 m_state->m_cWnd = m_cWnd;
141 m_state->m_segmentSize = m_segmentSize;
142 m_state->m_ssThresh = m_ssThresh;
143 m_state->m_minRtt = m_rtt;
144
145 Ptr<TcpVeno> cong = CreateObject <TcpVeno> ();
146
147 // Set baseRtt to 100 ms
148 Time baseRtt = MilliSeconds (100);
149 cong->PktsAcked (m_state, m_segmentsAcked, baseRtt);
150
151 // Re-set Veno to assign a new value of minRtt
152 cong->CongestionStateSet (m_state, TcpSocketState::CA_OPEN);
153
154 uint32_t segCwnd = m_cWnd / m_segmentSize;
155
156 // Calculate expected throughput
157 uint32_t expectedCwnd;
158 double tmp = baseRtt.GetSeconds () / m_rtt.GetSeconds ();
159 expectedCwnd = segCwnd * tmp;
160
161 // Calculate the difference between actual and expected throughput
162 uint32_t diff;
163 diff = segCwnd - expectedCwnd;
164
165 // Get the beta attribute
166 UintegerValue beta;
167 cong->GetAttribute ("Beta", beta);
168
169 uint32_t cntRtt = 0;
170
171 TcpSocketState state;
172 state.m_cWnd = m_cWnd;
173 state.m_ssThresh = m_ssThresh;
174 state.m_segmentSize = m_segmentSize;
175
176 while (m_numRtt != 0)
177 {
178 // Update cwnd using Veno's additive increase algorithm
179 cong->PktsAcked (m_state, m_segmentsAcked, m_rtt);
180 cong->IncreaseWindow (m_state, m_segmentsAcked);
181
182 // The first round the internal m_diff of cong will be 4, just like us
183 if (cntRtt == 0)
184 {
185 // Update ssthresh using Veno's multiplicative decrease algorithm
186 uint32_t ssThresh = cong->GetSsThresh (m_state, m_state->m_cWnd);
187
188 // Our calculation of ssthresh
189 uint32_t calculatedSsThresh = MultiplicativeDecrease (diff, beta, m_state->m_cWnd.Get ());
190
191 NS_TEST_ASSERT_MSG_EQ (ssThresh, calculatedSsThresh,
192 "Veno has not decremented cWnd correctly based on its"
193 "multiplicative decrease algo.");
194 }
195
196 // Our calculation of cwnd
197 if (cntRtt <= 2)
198 {
199 NewReno_IncreaseWindow (&state, 1);
200 }
201 else
202 {
203 AdditiveIncrease (&state, diff, beta);
204 }
205
206 NS_TEST_ASSERT_MSG_EQ (m_state->m_cWnd.Get (), state.m_cWnd.Get (),
207 "CWnd has not updated correctly based on Veno linear increase algorithm");
208 m_numRtt--;
209 cntRtt++;
210 }
211 }
212
213 void
AdditiveIncrease(Ptr<TcpSocketState> state,uint32_t diff,UintegerValue beta)214 TcpVenoTest::AdditiveIncrease (Ptr<TcpSocketState> state, uint32_t diff, UintegerValue beta)
215 {
216 if (m_cWnd < m_ssThresh)
217 { // Slow start
218 NewReno_SlowStart (state, 1);
219 }
220 else
221 { // Congestion avoidance
222 if (diff < beta.Get ())
223 { // Increase cwnd by 1 every RTT when bandwidth is not fully utilized
224 NewReno_CongestionAvoidance (state, 1);
225 }
226 else
227 { // Increase cwnd by 1 every other RTT when bandwidth is fully utilized
228 if (m_inc)
229 {
230 NewReno_CongestionAvoidance (state, 1);
231 m_inc = false;
232 }
233 else
234 {
235 m_inc = true;
236 }
237 }
238 }
239 }
240
241 uint32_t
MultiplicativeDecrease(uint32_t diff,const UintegerValue & beta,uint32_t bytesInFlight)242 TcpVenoTest::MultiplicativeDecrease (uint32_t diff, const UintegerValue &beta,
243 uint32_t bytesInFlight)
244 {
245 uint32_t calculatedSsThresh;
246 if (diff < beta.Get ())
247 {
248 static double tmp = 4.0 / 5.0;
249 calculatedSsThresh = std::max (2 * m_segmentSize, static_cast<uint32_t> (bytesInFlight * tmp));
250 }
251 else
252 {
253 calculatedSsThresh = std::max (2 * m_segmentSize, bytesInFlight / 2);
254 }
255 return calculatedSsThresh;
256 }
257
258 void
NewReno_IncreaseWindow(Ptr<TcpSocketState> state,uint32_t segmentsAcked)259 TcpVenoTest::NewReno_IncreaseWindow (Ptr<TcpSocketState> state, uint32_t segmentsAcked)
260 {
261 if (state->m_cWnd < state->m_ssThresh)
262 {
263 segmentsAcked = NewReno_SlowStart (state, segmentsAcked);
264 }
265
266 if (state->m_cWnd >= state->m_ssThresh)
267 {
268 NewReno_CongestionAvoidance (state, segmentsAcked);
269 }
270 }
271
272 uint32_t
NewReno_SlowStart(Ptr<TcpSocketState> state,uint32_t segmentsAcked)273 TcpVenoTest::NewReno_SlowStart (Ptr<TcpSocketState> state, uint32_t segmentsAcked)
274 {
275 if (segmentsAcked >= 1)
276 {
277 state->m_cWnd += state->m_segmentSize;
278 return segmentsAcked - 1;
279 }
280
281 return 0;
282 }
283
284 void
NewReno_CongestionAvoidance(Ptr<TcpSocketState> state,uint32_t segmentsAcked)285 TcpVenoTest::NewReno_CongestionAvoidance (Ptr<TcpSocketState> state, uint32_t segmentsAcked)
286 {
287 if (segmentsAcked > 0)
288 {
289 double adder = static_cast<double> (state->m_segmentSize * state->m_segmentSize) / state->m_cWnd.Get ();
290 adder = std::max (1.0, adder);
291 state->m_cWnd += static_cast<uint32_t> (adder);
292 }
293 }
294
295
296 /**
297 * \ingroup internet-test
298 * \ingroup tests
299 *
300 * \brief TCP Veno TestSuite
301 */
302 class TcpVenoTestSuite : public TestSuite
303 {
304 public:
TcpVenoTestSuite()305 TcpVenoTestSuite () : TestSuite ("tcp-veno-test", UNIT)
306 {
307 AddTestCase (new TcpVenoTest (38 * 1446, 1446, 40 * 1446, MilliSeconds (100), 1, 1,
308 "Veno test on cWnd in slow start and non-congestive loss"),
309 TestCase::QUICK);
310 AddTestCase (new TcpVenoTest (30 * 536, 536, 20 * 536, MilliSeconds (106), 1, 1,
311 "Veno test on cWnd with diff < beta"),
312 TestCase::QUICK);
313 AddTestCase (new TcpVenoTest (60 * 536, 536, 40 * 536, MilliSeconds (106), 1, 3,
314 "Veno increment test on cWnd with diff > beta"),
315 TestCase::QUICK);
316 }
317 };
318
319 static TcpVenoTestSuite g_tcpVenoTest; //!< Static variable for test initialization
320
321
322