1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
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: Stefano Avallone <stavallo@unina.it>
19  *
20  */
21 
22 #include <algorithm>
23 #include <string>
24 
25 #include "ns3/test.h"
26 #include "ns3/uinteger.h"
27 #include "ns3/pointer.h"
28 #include "ns3/string.h"
29 #include "ns3/double.h"
30 #include "ns3/log.h"
31 #include "ns3/simulator.h"
32 #include "ns3/node-container.h"
33 #include "ns3/traffic-control-layer.h"
34 #include "ns3/traffic-control-helper.h"
35 #include "ns3/simple-net-device-helper.h"
36 #include "ns3/data-rate.h"
37 #include "ns3/net-device-queue-interface.h"
38 #include "ns3/queue.h"
39 #include "ns3/config.h"
40 
41 using namespace ns3;
42 
43 /**
44  * \ingroup traffic-control-test
45  * \ingroup tests
46  *
47  * \brief Queue Disc Test Item
48  */
49 class QueueDiscTestItem : public QueueDiscItem {
50 public:
51   /**
52    * Constructor
53    *
54    * \param p the packet stored in this item
55    */
56   QueueDiscTestItem (Ptr<Packet> p);
57   virtual ~QueueDiscTestItem ();
58   virtual void AddHeader (void);
59   virtual bool Mark(void);
60 
61 private:
62   QueueDiscTestItem ();
63   /**
64    * \brief Copy constructor
65    * Disable default implementation to avoid misuse
66    */
67   QueueDiscTestItem (const QueueDiscTestItem &);
68   /**
69    * \brief Assignment operator
70    * \return this object
71    * Disable default implementation to avoid misuse
72    */
73   QueueDiscTestItem &operator = (const QueueDiscTestItem &);
74 };
75 
QueueDiscTestItem(Ptr<Packet> p)76 QueueDiscTestItem::QueueDiscTestItem (Ptr<Packet> p)
77   : QueueDiscItem (p, Mac48Address (), 0)
78 {
79 }
80 
~QueueDiscTestItem()81 QueueDiscTestItem::~QueueDiscTestItem ()
82 {
83 }
84 
85 void
AddHeader(void)86 QueueDiscTestItem::AddHeader (void)
87 {
88 }
89 
90 bool
Mark(void)91 QueueDiscTestItem::Mark (void)
92 {
93   return false;
94 }
95 
96 /**
97  * \ingroup traffic-control-test
98  * \ingroup tests
99  *
100  * \brief Traffic Control Flow Control Test Case
101  */
102 class TcFlowControlTestCase : public TestCase
103 {
104 public:
105   /**
106    * Constructor
107    *
108    * \param tt the test type
109    */
110   TcFlowControlTestCase (QueueSizeUnit tt, uint32_t deviceQueueLength, uint32_t totalTxPackets);
111   virtual ~TcFlowControlTestCase ();
112 private:
113   virtual void DoRun (void);
114   /**
115    * Instruct a node to send a specified number of packets
116    * \param n the node
117    * \param nPackets the number of packets to send
118    */
119   void SendPackets (Ptr<Node> n, uint16_t nPackets);
120   /**
121    * Check if the device queue stores the expected number of packets
122    * \param dev the device
123    * \param nPackets the expected number of packets stored in the device queue
124    * \param msg the message to print if a different number of packets are stored
125    */
126   void CheckPacketsInDeviceQueue (Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg);
127   /**
128    * Check if the device queue is in the expected status (stopped or not)
129    * \param dev the device
130    * \param value the expected status of the queue (true means stopped)
131    * \param msg the message to print if the status of the device queue is different
132    */
133   void CheckDeviceQueueStopped (Ptr<NetDevice> dev, bool value, const std::string msg);
134   /**
135    * Check if the queue disc stores the expected number of packets
136    * \param dev the device the queue disc is installed on
137    * \param nPackets the expected number of packets stored in the queue disc
138    * \param msg the message to print if a different number of packets are stored
139    */
140   void CheckPacketsInQueueDisc (Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg);
141   QueueSizeUnit m_type;       //!< the test type
142   uint32_t m_deviceQueueLength;
143   uint32_t m_totalTxPackets;
144 };
145 
TcFlowControlTestCase(QueueSizeUnit tt,uint32_t deviceQueueLength,uint32_t totalTxPackets)146 TcFlowControlTestCase::TcFlowControlTestCase (QueueSizeUnit tt, uint32_t deviceQueueLength, uint32_t totalTxPackets)
147   : TestCase ("Test the operation of the flow control mechanism"),
148     m_type (tt), m_deviceQueueLength(deviceQueueLength), m_totalTxPackets(totalTxPackets)
149 {
150 }
151 
~TcFlowControlTestCase()152 TcFlowControlTestCase::~TcFlowControlTestCase ()
153 {
154 }
155 
156 void
SendPackets(Ptr<Node> n,uint16_t nPackets)157 TcFlowControlTestCase::SendPackets (Ptr<Node> n, uint16_t nPackets)
158 {
159   Ptr<TrafficControlLayer> tc = n->GetObject<TrafficControlLayer> ();
160   for (uint16_t i = 0; i < nPackets; i++)
161     {
162       tc->Send (n->GetDevice (0), Create<QueueDiscTestItem> (Create<Packet> (1000)));
163     }
164 }
165 
166 void
CheckPacketsInDeviceQueue(Ptr<NetDevice> dev,uint16_t nPackets,const std::string msg)167 TcFlowControlTestCase::CheckPacketsInDeviceQueue (Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg)
168 {
169   PointerValue ptr;
170   dev->GetAttributeFailSafe ("TxQueue", ptr);
171   Ptr<Queue<Packet> > queue = ptr.Get<Queue<Packet> > ();
172   NS_TEST_EXPECT_MSG_EQ (queue->GetNPackets (), nPackets, msg);
173 }
174 
175 void
CheckDeviceQueueStopped(Ptr<NetDevice> dev,bool value,const std::string msg)176 TcFlowControlTestCase::CheckDeviceQueueStopped (Ptr<NetDevice> dev, bool value, const std::string msg)
177 {
178   Ptr<NetDeviceQueueInterface> ndqi = dev->GetObject<NetDeviceQueueInterface> ();
179   NS_ASSERT_MSG (ndqi, "A device queue interface has not been aggregated to the device");
180   NS_TEST_EXPECT_MSG_EQ (ndqi->GetTxQueue (0)->IsStopped (), value, msg);
181 }
182 
183 void
CheckPacketsInQueueDisc(Ptr<NetDevice> dev,uint16_t nPackets,const std::string msg)184 TcFlowControlTestCase::CheckPacketsInQueueDisc (Ptr<NetDevice> dev, uint16_t nPackets, const std::string msg)
185 {
186   Ptr<TrafficControlLayer> tc = dev->GetNode ()->GetObject<TrafficControlLayer> ();
187   Ptr<QueueDisc> qdisc = tc->GetRootQueueDiscOnDevice (dev);
188   NS_TEST_EXPECT_MSG_EQ (qdisc->GetNPackets (), nPackets, msg);
189 }
190 
191 
192 void
DoRun(void)193 TcFlowControlTestCase::DoRun (void)
194 {
195   NodeContainer n;
196   n.Create (2);
197 
198   n.Get (0)->AggregateObject (CreateObject<TrafficControlLayer> ());
199   n.Get (1)->AggregateObject (CreateObject<TrafficControlLayer> ());
200 
201   SimpleNetDeviceHelper simple;
202 
203   NetDeviceContainer rxDevC = simple.Install (n.Get (1));
204 
205   simple.SetDeviceAttribute ("DataRate", DataRateValue (DataRate ("1Mb/s")));
206   simple.SetQueue ("ns3::DropTailQueue", "MaxSize",
207                    StringValue (m_type == QueueSizeUnit::PACKETS
208                                     ? std::to_string (m_deviceQueueLength) + "p"
209                                     : std::to_string (m_deviceQueueLength) + "B"));
210 
211   Ptr<NetDevice> txDev;
212   txDev = simple.Install (n.Get (0), DynamicCast<SimpleChannel> (rxDevC.Get (0)->GetChannel ())).Get (0);
213   txDev->SetMtu (2500);
214 
215   TrafficControlHelper tch = TrafficControlHelper::Default ();
216   tch.Install (txDev);
217 
218   // transmit 10 packets at time 0
219   Simulator::Schedule (Time (Seconds (0)), &TcFlowControlTestCase::SendPackets,
220                       this, n.Get (0), m_totalTxPackets);
221 
222   if (m_type == QueueSizeUnit::PACKETS)
223     {
224       /*
225        * When the device queue is in packet mode, all the packets enqueued in the
226        * queue disc are correctly transmitted, even if the device queue is stopped
227        * when the last packet is received from the upper layers
228        *
229        * We have the following invariants:
230        *  - totalPackets = txPackets + deviceQueuePackets + qdiscPackets
231        *  - deviceQueuePackets = MIN(totalPackets - txPackets, deviceQueueLen)
232        *  - qdiscPackets = MAX(totalPackets - txPackets - deviceQueuePackets, 0)
233        *
234        * The transmission of each packet takes 1000B/1Mbps = 8ms
235        *
236        * We check the values of deviceQueuePackets and qdiscPackets 1ms after each
237        * packet is transmitted (i.e. at 1ms, 9ms, 17ms, ...), as well as verifying
238        * that the device queue is stopped or not, as appropriate.
239        */
240 
241       uint32_t checkTimeMs = 0;
242       uint32_t deviceQueuePackets = 0;
243       uint32_t qdiscPackets = 0;
244 
245       uint32_t txPackets = 0;
246       for (txPackets = 1; txPackets <= m_totalTxPackets; txPackets++)
247         {
248           checkTimeMs = 8 * (txPackets - 1) + 1; // Check 1ms after each packet is sent
249           deviceQueuePackets = std::min (m_totalTxPackets - txPackets, m_deviceQueueLength);
250           qdiscPackets = std::max (m_totalTxPackets - txPackets - deviceQueuePackets, (uint32_t)0);
251           if (deviceQueuePackets == m_deviceQueueLength)
252             {
253               Simulator::Schedule (
254                   Time (MilliSeconds (checkTimeMs)),
255                   &TcFlowControlTestCase::CheckDeviceQueueStopped, this, txDev, true,
256                   "The device queue must be stopped after " + std::to_string (checkTimeMs) + "ms");
257             }
258           else
259             {
260               Simulator::Schedule (Time (MilliSeconds (checkTimeMs)),
261                                    &TcFlowControlTestCase::CheckDeviceQueueStopped, this, txDev,
262                                    false,
263                                    "The device queue must not be stopped after " +
264                                        std::to_string (checkTimeMs) + "ms");
265             }
266 
267           Simulator::Schedule (
268               Time (MilliSeconds (checkTimeMs)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
269               this, txDev, deviceQueuePackets,
270               "There must be " + std::to_string (m_deviceQueueLength) + " packets in the device after " +
271                   std::to_string (checkTimeMs) + "ms");
272 
273           Simulator::Schedule (
274               Time (MilliSeconds (checkTimeMs)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
275               this, txDev, qdiscPackets,
276               "There must be " + std::to_string (qdiscPackets) +
277                   " packets in the queue disc after " + std::to_string (checkTimeMs) + "ms");
278         }
279     }
280   else
281     {
282       // TODO: Make this test parametric as well, and add new test cases
283       /*
284        * When the device queue is in byte mode, all the packets enqueued in the
285        * queue disc are correctly transmitted, even if the device queue is stopped
286        * when the last packet is received from the upper layers
287        */
288 
289       // The transmission of each packet takes 1000B/1Mbps = 8ms
290       // After 1ms, we have 3 packets in the device queue (stopped) and 6 in the queue disc
291       Simulator::Schedule (Time (MilliSeconds (1)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
292                           this, txDev, 3, "There must be 3 packets in the device queue after 1ms");
293       Simulator::Schedule (Time (MilliSeconds (1)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
294                           this, txDev, true, "The device queue must be stopped after 1ms");
295       Simulator::Schedule (Time (MilliSeconds (1)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
296                           this, txDev, 6, "There must be 6 packets in the queue disc after 1ms");
297 
298       // After 9ms, we have 3 packets in the device queue (stopped) and 5 in the queue disc
299       Simulator::Schedule (Time (MilliSeconds (9)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
300                           this, txDev, 3, "There must be 3 packets in the device queue after 9ms");
301       Simulator::Schedule (Time (MilliSeconds (9)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
302                           this, txDev, true, "The device queue must be stopped after 9ms");
303       Simulator::Schedule (Time (MilliSeconds (9)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
304                           this, txDev, 5, "There must be 5 packets in the queue disc after 9ms");
305 
306       // After 17ms, we have 3 packets in the device queue (stopped) and 4 in the queue disc
307       Simulator::Schedule (Time (MilliSeconds (17)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
308                           this, txDev, 3, "There must be 3 packets in the device queue after 17ms");
309       Simulator::Schedule (Time (MilliSeconds (17)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
310                           this, txDev, true, "The device queue must be stopped after 17ms");
311       Simulator::Schedule (Time (MilliSeconds (17)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
312                           this, txDev, 4, "There must be 4 packets in the queue disc after 17ms");
313 
314       // After 25ms, we have 3 packets in the device queue (stopped) and 3 in the queue disc
315       Simulator::Schedule (Time (MilliSeconds (25)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
316                           this, txDev, 3, "There must be 3 packets in the device queue after 25ms");
317       Simulator::Schedule (Time (MilliSeconds (25)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
318                           this, txDev, true, "The device queue must be stopped after 25ms");
319       Simulator::Schedule (Time (MilliSeconds (25)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
320                           this, txDev, 3, "There must be 3 packets in the queue disc after 25ms");
321 
322       // After 33ms, we have 3 packets in the device queue (stopped) and 2 in the queue disc
323       Simulator::Schedule (Time (MilliSeconds (33)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
324                           this, txDev, 3, "There must be 3 packets in the device queue after 33ms");
325       Simulator::Schedule (Time (MilliSeconds (33)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
326                           this, txDev, true, "The device queue must be stopped after 33ms");
327       Simulator::Schedule (Time (MilliSeconds (33)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
328                           this, txDev, 2, "There must be 2 packets in the queue disc after 33ms");
329 
330       // After 41ms, we have 3 packets in the device queue (stopped) and 1 in the queue disc
331       Simulator::Schedule (Time (MilliSeconds (41)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
332                           this, txDev, 3, "There must be 3 packets in the device queue after 41ms");
333       Simulator::Schedule (Time (MilliSeconds (41)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
334                           this, txDev, true, "The device queue must be stopped after 41ms");
335       Simulator::Schedule (Time (MilliSeconds (41)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
336                           this, txDev, 1, "There must be 1 packet in the queue disc after 41ms");
337 
338       // After 49ms, we have 3 packets in the device queue (stopped) and the queue disc is empty
339       Simulator::Schedule (Time (MilliSeconds (49)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
340                           this, txDev, 3, "There must be 3 packets in the device queue after 49ms");
341       Simulator::Schedule (Time (MilliSeconds (49)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
342                           this, txDev, true, "The device queue must be stopped after 49ms");
343       Simulator::Schedule (Time (MilliSeconds (49)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
344                           this, txDev, 0, "The queue disc must be empty after 49ms");
345 
346       // After 57ms, we have 2 packets in the device queue (not stopped) and the queue disc is empty
347       Simulator::Schedule (Time (MilliSeconds (57)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
348                           this, txDev, 2, "There must be 2 packets in the device queue after 57ms");
349       Simulator::Schedule (Time (MilliSeconds (57)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
350                           this, txDev, false, "The device queue must not be stopped after 57ms");
351       Simulator::Schedule (Time (MilliSeconds (57)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
352                           this, txDev, 0, "The queue disc must be empty after 57ms");
353 
354       // After 81ms, all packets must have been transmitted (the device queue and the queue disc are empty)
355       Simulator::Schedule (Time (MilliSeconds (81)), &TcFlowControlTestCase::CheckPacketsInDeviceQueue,
356                           this, txDev, 0, "The device queue must be empty after 81ms");
357       Simulator::Schedule (Time (MilliSeconds (81)), &TcFlowControlTestCase::CheckDeviceQueueStopped,
358                           this, txDev, false, "The device queue must not be stopped after 81ms");
359       Simulator::Schedule (Time (MilliSeconds (81)), &TcFlowControlTestCase::CheckPacketsInQueueDisc,
360                           this, txDev, 0, "The queue disc must be empty after 81ms");
361     }
362 
363   Simulator::Run ();
364   Simulator::Destroy ();
365 }
366 
367 /**
368  * \ingroup traffic-control-test
369  * \ingroup tests
370  *
371  * \brief Traffic Control Flow Control Test Suite
372  */
373 static class TcFlowControlTestSuite : public TestSuite
374 {
375 public:
TcFlowControlTestSuite()376   TcFlowControlTestSuite ()
377     : TestSuite ("tc-flow-control", UNIT)
378   {
379     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 1, 10), TestCase::QUICK);
380     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 5, 10), TestCase::QUICK);
381     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 9, 10), TestCase::QUICK);
382     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 10, 10), TestCase::QUICK);
383     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 11, 10), TestCase::QUICK);
384     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 15, 10), TestCase::QUICK);
385     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 1, 1), TestCase::QUICK);
386     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 2, 1), TestCase::QUICK);
387     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::PACKETS, 5, 1), TestCase::QUICK);
388 
389     // TODO: Right now, this test only works for 5000B and 10 packets (it's hard coded). Should
390     // also be made parametric.
391     AddTestCase (new TcFlowControlTestCase (QueueSizeUnit::BYTES, 5000, 10), TestCase::QUICK);
392   }
393 } g_tcFlowControlTestSuite; ///< the test suite
394