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