1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2014 Natale Patriciello <natale.patriciello@gmail.com>
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  */
19 #include "tcp-bic.h"
20 #include "ns3/log.h"
21 #include "ns3/simulator.h"
22 
23 namespace ns3 {
24 
25 NS_LOG_COMPONENT_DEFINE ("TcpBic");
26 NS_OBJECT_ENSURE_REGISTERED (TcpBic);
27 
28 TypeId
GetTypeId(void)29 TcpBic::GetTypeId (void)
30 {
31   static TypeId tid = TypeId ("ns3::TcpBic")
32     .SetParent<TcpCongestionOps> ()
33     .AddConstructor<TcpBic> ()
34     .SetGroupName ("Internet")
35     .AddAttribute ("FastConvergence", "Turn on/off fast convergence.",
36                    BooleanValue (true),
37                    MakeBooleanAccessor (&TcpBic::m_fastConvergence),
38                    MakeBooleanChecker ())
39     .AddAttribute ("Beta", "Beta for multiplicative decrease",
40                    DoubleValue (0.8),
41                    MakeDoubleAccessor (&TcpBic::m_beta),
42                    MakeDoubleChecker <double> (0.0))
43     .AddAttribute ("MaxIncr", "Limit on increment allowed during binary search",
44                    UintegerValue (16),
45                    MakeUintegerAccessor (&TcpBic::m_maxIncr),
46                    MakeUintegerChecker <uint32_t> (1))
47     .AddAttribute ("LowWnd", "Threshold window size (in segments) for engaging BIC response",
48                    UintegerValue (14),
49                    MakeUintegerAccessor (&TcpBic::m_lowWnd),
50                    MakeUintegerChecker <uint32_t> ())
51     .AddAttribute ("SmoothPart", "Number of RTT needed to approach cWnd_max from "
52                    "cWnd_max-BinarySearchCoefficient. It can be viewed as the gradient "
53                    "of the slow start AIM phase: less this value is, "
54                    "more steep the increment will be.",
55                    UintegerValue (5),
56                    MakeUintegerAccessor (&TcpBic::m_smoothPart),
57                    MakeUintegerChecker <uint32_t> (1))
58     .AddAttribute ("BinarySearchCoefficient", "Inverse of the coefficient for the "
59                    "binary search. Default 4, as in Linux",
60                    UintegerValue (4),
61                    MakeUintegerAccessor (&TcpBic::m_b),
62                    MakeUintegerChecker <uint8_t> (2))
63   ;
64   return tid;
65 }
66 
67 
TcpBic()68 TcpBic::TcpBic ()
69   : TcpCongestionOps (),
70     m_cWndCnt (0),
71     m_lastMaxCwnd (0),
72     m_lastCwnd (0),
73     m_epochStart (Time::Min ())
74 {
75   NS_LOG_FUNCTION (this);
76 }
77 
TcpBic(const TcpBic & sock)78 TcpBic::TcpBic (const TcpBic &sock)
79   : TcpCongestionOps (sock),
80     m_fastConvergence (sock.m_fastConvergence),
81     m_beta (sock.m_beta),
82     m_maxIncr (sock.m_maxIncr),
83     m_lowWnd (sock.m_lowWnd),
84     m_smoothPart (sock.m_smoothPart),
85     m_cWndCnt (sock.m_cWndCnt),
86     m_lastMaxCwnd (sock.m_lastMaxCwnd),
87     m_lastCwnd (sock.m_lastCwnd),
88     m_epochStart (sock.m_epochStart),
89     m_b (sock.m_b)
90 {
91   NS_LOG_FUNCTION (this);
92 }
93 
94 
95 void
IncreaseWindow(Ptr<TcpSocketState> tcb,uint32_t segmentsAcked)96 TcpBic::IncreaseWindow (Ptr<TcpSocketState> tcb, uint32_t segmentsAcked)
97 {
98   NS_LOG_FUNCTION (this << tcb << segmentsAcked);
99 
100   if (tcb->m_cWnd < tcb->m_ssThresh)
101     {
102       tcb->m_cWnd += tcb->m_segmentSize;
103       segmentsAcked -= 1;
104 
105       NS_LOG_INFO ("In SlowStart, updated to cwnd " << tcb->m_cWnd <<
106                    " ssthresh " << tcb->m_ssThresh);
107     }
108 
109   if (tcb->m_cWnd >= tcb->m_ssThresh && segmentsAcked > 0)
110     {
111       m_cWndCnt += segmentsAcked;
112       uint32_t cnt = Update (tcb);
113 
114       /* According to the BIC paper and RFC 6356 even once the new cwnd is
115        * calculated you must compare this to the number of ACKs received since
116        * the last cwnd update. If not enough ACKs have been received then cwnd
117        * cannot be updated.
118        */
119       if (m_cWndCnt > cnt)
120         {
121           tcb->m_cWnd += tcb->m_segmentSize;
122           m_cWndCnt = 0;
123           NS_LOG_INFO ("In CongAvoid, updated to cwnd " << tcb->m_cWnd);
124         }
125       else
126         {
127           NS_LOG_INFO ("Not enough segments have been ACKed to increment cwnd."
128                        "Until now " << m_cWndCnt);
129         }
130     }
131 }
132 
133 uint32_t
Update(Ptr<TcpSocketState> tcb)134 TcpBic::Update (Ptr<TcpSocketState> tcb)
135 {
136   NS_LOG_FUNCTION (this << tcb);
137 
138   uint32_t segCwnd = tcb->GetCwndInSegments ();
139   uint32_t cnt;
140 
141   m_lastCwnd = segCwnd;
142 
143   if (m_epochStart == Time::Min ())
144     {
145       m_epochStart = Simulator::Now ();   /* record the beginning of an epoch */
146     }
147 
148   if (segCwnd < m_lowWnd)
149     {
150       NS_LOG_INFO ("Under lowWnd, compatibility mode. Behaving as NewReno");
151       cnt = segCwnd;
152       return cnt;
153     }
154 
155   if (segCwnd < m_lastMaxCwnd)
156     {
157       double dist = (m_lastMaxCwnd - segCwnd) / m_b;
158 
159       NS_LOG_INFO ("cWnd = " << segCwnd << " under lastMax, " <<
160                    m_lastMaxCwnd << " and dist=" << dist);
161       if (dist > m_maxIncr)
162         {
163           /* Linear increase */
164           cnt = segCwnd / m_maxIncr;
165           NS_LOG_INFO ("Linear increase (maxIncr=" << m_maxIncr << "), cnt=" << cnt);
166         }
167       else if (dist <= 1)
168         {
169           /* smoothed binary search increase: when our window is really
170            * close to the last maximum, we parameterize in m_smoothPart the number
171            * of RTT needed to reach that window.
172            */
173           cnt = (segCwnd * m_smoothPart) / m_b;
174 
175           NS_LOG_INFO ("Binary search increase (smoothPart=" << m_smoothPart <<
176                        "), cnt=" << cnt);
177         }
178       else
179         {
180           /* binary search increase */
181           cnt = static_cast<uint32_t> (segCwnd / dist);
182 
183           NS_LOG_INFO ("Binary search increase, cnt=" << cnt);
184         }
185     }
186   else
187     {
188       NS_LOG_INFO ("cWnd = " << segCwnd << " above last max, " <<
189                    m_lastMaxCwnd);
190       if (segCwnd < m_lastMaxCwnd + m_b)
191         {
192           /* slow start AMD linear increase */
193           cnt = (segCwnd * m_smoothPart) / m_b;
194           NS_LOG_INFO ("Slow start AMD, cnt=" << cnt);
195         }
196       else if (segCwnd < m_lastMaxCwnd + m_maxIncr * (m_b - 1))
197         {
198           /* slow start */
199           cnt = (segCwnd * (m_b - 1)) / (segCwnd - m_lastMaxCwnd);
200 
201           NS_LOG_INFO ("Slow start, cnt=" << cnt);
202         }
203       else
204         {
205           /* linear increase */
206           cnt = segCwnd / m_maxIncr;
207 
208           NS_LOG_INFO ("Linear, cnt=" << cnt);
209         }
210     }
211 
212   /* if in slow start or link utilization is very low. Code taken from Linux
213    * kernel, not sure of the source they take it. Usually, it is not reached,
214    * since if m_lastMaxCwnd is 0, we are (hopefully) in slow start.
215    */
216   if (m_lastMaxCwnd == 0)
217     {
218       if (cnt > 20) /* increase cwnd 5% per RTT */
219         {
220           cnt = 20;
221         }
222     }
223 
224   if (cnt == 0)
225     {
226       cnt = 1;
227     }
228 
229   return cnt;
230 }
231 
232 std::string
GetName() const233 TcpBic::GetName () const
234 {
235   return "TcpBic";
236 }
237 
238 uint32_t
GetSsThresh(Ptr<const TcpSocketState> tcb,uint32_t bytesInFlight)239 TcpBic::GetSsThresh (Ptr<const TcpSocketState> tcb, uint32_t bytesInFlight)
240 {
241   NS_LOG_FUNCTION (this);
242 
243   uint32_t segCwnd = tcb->GetCwndInSegments ();
244   uint32_t ssThresh = 0;
245 
246   m_epochStart = Time::Min ();
247 
248   /* Wmax and fast convergence */
249   if (segCwnd < m_lastMaxCwnd && m_fastConvergence)
250     {
251       NS_LOG_INFO ("Fast Convergence. Last max cwnd: " << m_lastMaxCwnd <<
252                    " updated to " << static_cast<uint32_t> (m_beta * segCwnd));
253       m_lastMaxCwnd = static_cast<uint32_t> (m_beta * segCwnd);
254     }
255   else
256     {
257       NS_LOG_INFO ("Last max cwnd: " << m_lastMaxCwnd << " updated to " <<
258                    segCwnd);
259       m_lastMaxCwnd = segCwnd;
260     }
261 
262   if (segCwnd < m_lowWnd)
263     {
264       ssThresh = std::max (2 * tcb->m_segmentSize, bytesInFlight / 2);
265       NS_LOG_INFO ("Less than lowWindow, ssTh= " << ssThresh);
266     }
267   else
268     {
269       ssThresh = static_cast<uint32_t> (std::max (segCwnd * m_beta, 2.0) * tcb->m_segmentSize);
270       NS_LOG_INFO ("More than lowWindow, ssTh= " << ssThresh);
271     }
272 
273   return ssThresh;
274 }
275 
276 Ptr<TcpCongestionOps>
Fork(void)277 TcpBic::Fork (void)
278 {
279   return CopyObject<TcpBic> (this);
280 }
281 
282 } // namespace ns3
283