A Discrete-Event Network Simulator
API
traffic-control-layer.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Natale Patriciello <natale.patriciello@gmail.com>
3  * 2016 Stefano Avallone <stavallo@unina.it>
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 "traffic-control-layer.h"
20 
21 #include "ns3/log.h"
22 #include "ns3/net-device-queue-interface.h"
23 #include "ns3/object-map.h"
24 #include "ns3/packet.h"
25 #include "ns3/queue-disc.h"
26 #include "ns3/socket.h"
27 
28 #include <tuple>
29 
30 namespace ns3
31 {
32 
33 NS_LOG_COMPONENT_DEFINE("TrafficControlLayer");
34 
35 NS_OBJECT_ENSURE_REGISTERED(TrafficControlLayer);
36 
37 TypeId
39 {
40  static TypeId tid =
41  TypeId("ns3::TrafficControlLayer")
42  .SetParent<Object>()
43  .SetGroupName("TrafficControl")
44  .AddConstructor<TrafficControlLayer>()
45  .AddAttribute(
46  "RootQueueDiscList",
47  "The list of root queue discs associated to this Traffic Control layer.",
51  MakeObjectMapChecker<QueueDisc>())
52  .AddTraceSource("TcDrop",
53  "Trace source indicating a packet has been dropped by the Traffic "
54  "Control layer because no queue disc is installed on the device, the "
55  "device supports flow control and the device queue is stopped",
57  "ns3::Packet::TracedCallback");
58  return tid;
59 }
60 
61 TypeId
63 {
64  return GetTypeId();
65 }
66 
68  : Object()
69 {
70  NS_LOG_FUNCTION(this);
71 }
72 
74 {
75  NS_LOG_FUNCTION(this);
76 }
77 
78 void
80 {
81  NS_LOG_FUNCTION(this);
82  m_node = nullptr;
83  m_handlers.clear();
84  m_netDevices.clear();
86 }
87 
88 void
90 {
91  NS_LOG_FUNCTION(this);
92 
93  ScanDevices();
94 
95  // initialize the root queue discs
96  for (auto& ndi : m_netDevices)
97  {
98  if (ndi.second.m_rootQueueDisc)
99  {
100  ndi.second.m_rootQueueDisc->Initialize();
101  }
102  }
103 
105 }
106 
107 void
109  uint16_t protocolType,
110  Ptr<NetDevice> device)
111 {
112  NS_LOG_FUNCTION(this << protocolType << device);
113 
114  ProtocolHandlerEntry entry;
115  entry.handler = handler;
116  entry.protocol = protocolType;
117  entry.device = device;
118  entry.promiscuous = false;
119 
120  m_handlers.push_back(entry);
121 
122  NS_LOG_DEBUG("Handler for NetDevice: " << device << " registered for protocol " << protocolType
123  << ".");
124 }
125 
126 void
128 {
129  NS_LOG_FUNCTION(this);
130 
131  NS_ASSERT_MSG(m_node, "Cannot run ScanDevices without an aggregated node");
132 
133  NS_LOG_DEBUG("Scanning devices on node " << m_node->GetId());
134  for (uint32_t i = 0; i < m_node->GetNDevices(); i++)
135  {
136  NS_LOG_DEBUG("Scanning devices on node " << m_node->GetId());
137  Ptr<NetDevice> dev = m_node->GetDevice(i);
138  NS_LOG_DEBUG("Checking device " << i << " with pointer " << dev << " of type "
139  << dev->GetInstanceTypeId().GetName());
140 
141  // note: there may be no NetDeviceQueueInterface aggregated to the device
142  Ptr<NetDeviceQueueInterface> ndqi = dev->GetObject<NetDeviceQueueInterface>();
143  NS_LOG_DEBUG("Pointer to NetDeviceQueueInterface: " << ndqi);
144 
145  std::map<Ptr<NetDevice>, NetDeviceInfo>::iterator ndi = m_netDevices.find(dev);
146 
147  if (ndi != m_netDevices.end())
148  {
149  NS_LOG_DEBUG("Device entry found; installing NetDeviceQueueInterface pointer "
150  << ndqi << " to internal map");
151  ndi->second.m_ndqi = ndqi;
152  }
153  else if (ndqi)
154  // if no entry for the device is found, it means that no queue disc has been
155  // installed. Nonetheless, create an entry for the device and store a pointer
156  // to the NetDeviceQueueInterface object if the latter is not null, because
157  // the Traffic Control layer checks whether the device queue is stopped even
158  // when there is no queue disc.
159  {
160  NS_LOG_DEBUG("No device entry found; create entry for device and store pointer to "
161  "NetDeviceQueueInterface: "
162  << ndqi);
163  m_netDevices[dev] = {nullptr, ndqi, QueueDiscVector()};
164  ndi = m_netDevices.find(dev);
165  }
166 
167  // if a queue disc is installed, set the wake callbacks on netdevice queues
168  if (ndi != m_netDevices.end() && ndi->second.m_rootQueueDisc)
169  {
170  NS_LOG_DEBUG("Setting the wake callbacks on NetDevice queues");
171  ndi->second.m_queueDiscsToWake.clear();
172 
173  if (ndqi)
174  {
175  for (std::size_t i = 0; i < ndqi->GetNTxQueues(); i++)
176  {
177  Ptr<QueueDisc> qd;
178 
179  if (ndi->second.m_rootQueueDisc->GetWakeMode() == QueueDisc::WAKE_ROOT)
180  {
181  qd = ndi->second.m_rootQueueDisc;
182  }
183  else if (ndi->second.m_rootQueueDisc->GetWakeMode() == QueueDisc::WAKE_CHILD)
184  {
185  NS_ABORT_MSG_IF(ndi->second.m_rootQueueDisc->GetNQueueDiscClasses() !=
186  ndqi->GetNTxQueues(),
187  "The number of child queue discs does not match the number "
188  "of netdevice queues");
189 
190  qd = ndi->second.m_rootQueueDisc->GetQueueDiscClass(i)->GetQueueDisc();
191  }
192  else
193  {
194  NS_ABORT_MSG("Invalid wake mode");
195  }
196 
197  ndqi->GetTxQueue(i)->SetWakeCallback(MakeCallback(&QueueDisc::Run, qd));
198  ndi->second.m_queueDiscsToWake.push_back(qd);
199  }
200  }
201  else
202  {
203  ndi->second.m_queueDiscsToWake.push_back(ndi->second.m_rootQueueDisc);
204  }
205 
206  // set the NetDeviceQueueInterface object and the SendCallback on the queue discs
207  // into which packets are enqueued and dequeued by calling Run
208  for (auto& q : ndi->second.m_queueDiscsToWake)
209  {
210  q->SetNetDeviceQueueInterface(ndqi);
211  q->SetSendCallback([dev](Ptr<QueueDiscItem> item) {
212  dev->Send(item->GetPacket(), item->GetAddress(), item->GetProtocol());
213  });
214  }
215  }
216  }
217 }
218 
219 void
221 {
222  NS_LOG_FUNCTION(this << device << qDisc);
223 
224  std::map<Ptr<NetDevice>, NetDeviceInfo>::iterator ndi = m_netDevices.find(device);
225 
226  if (ndi == m_netDevices.end())
227  {
228  // No entry found for this device. Create one.
229  m_netDevices[device] = {qDisc, nullptr, QueueDiscVector()};
230  }
231  else
232  {
233  NS_ABORT_MSG_IF(ndi->second.m_rootQueueDisc,
234  "Cannot install a root queue disc on a device already having one. "
235  "Delete the existing queue disc first.");
236 
237  ndi->second.m_rootQueueDisc = qDisc;
238  }
239 }
240 
243 {
244  NS_LOG_FUNCTION(this << device);
245 
246  std::map<Ptr<NetDevice>, NetDeviceInfo>::const_iterator ndi = m_netDevices.find(device);
247 
248  if (ndi == m_netDevices.end())
249  {
250  return nullptr;
251  }
252  return ndi->second.m_rootQueueDisc;
253 }
254 
257 {
258  NS_LOG_FUNCTION(this << index);
259  return GetRootQueueDiscOnDevice(m_node->GetDevice(index));
260 }
261 
262 void
264 {
265  NS_LOG_FUNCTION(this << device);
266 
267  std::map<Ptr<NetDevice>, NetDeviceInfo>::iterator ndi = m_netDevices.find(device);
268 
269  NS_ASSERT_MSG(ndi != m_netDevices.end() && ndi->second.m_rootQueueDisc,
270  "No root queue disc installed on device " << device);
271 
272  // remove the root queue disc
273  ndi->second.m_rootQueueDisc = nullptr;
274  for (auto& q : ndi->second.m_queueDiscsToWake)
275  {
276  q->SetNetDeviceQueueInterface(nullptr);
277  q->SetSendCallback(nullptr);
278  }
279  ndi->second.m_queueDiscsToWake.clear();
280 
281  Ptr<NetDeviceQueueInterface> ndqi = ndi->second.m_ndqi;
282  if (ndqi)
283  {
284  // remove configured callbacks, if any
285  for (std::size_t i = 0; i < ndqi->GetNTxQueues(); i++)
286  {
287  ndqi->GetTxQueue(i)->SetWakeCallback(MakeNullCallback<void>());
288  }
289  }
290  else
291  {
292  // remove the empty entry
293  m_netDevices.erase(ndi);
294  }
295 }
296 
297 void
299 {
300  NS_LOG_FUNCTION(this << node);
301  m_node = node;
302 }
303 
304 void
306 {
307  NS_LOG_FUNCTION(this);
308  if (!m_node)
309  {
310  Ptr<Node> node = this->GetObject<Node>();
311  // verify that it's a valid node and that
312  // the node was not set before
313  if (node)
314  {
315  this->SetNode(node);
316  }
317  }
319 }
320 
321 uint32_t
323 {
324  return m_node->GetNDevices();
325 }
326 
327 void
330  uint16_t protocol,
331  const Address& from,
332  const Address& to,
333  NetDevice::PacketType packetType)
334 {
335  NS_LOG_FUNCTION(this << device << p << protocol << from << to << packetType);
336 
337  bool found = false;
338 
339  for (ProtocolHandlerList::iterator i = m_handlers.begin(); i != m_handlers.end(); i++)
340  {
341  if (!i->device || (i->device == device))
342  {
343  if (i->protocol == 0 || i->protocol == protocol)
344  {
345  NS_LOG_DEBUG("Found handler for packet " << p << ", protocol " << protocol
346  << " and NetDevice " << device
347  << ". Send packet up");
348  i->handler(device, p, protocol, from, to, packetType);
349  found = true;
350  }
351  }
352  }
353 
354  NS_ABORT_MSG_IF(!found,
355  "Handler for protocol " << p << " and device " << device
356  << " not found. It isn't forwarded up; it dies here.");
357 }
358 
359 void
361 {
362  NS_LOG_FUNCTION(this << device << item);
363 
364  NS_LOG_DEBUG("Send packet to device " << device << " protocol number " << item->GetProtocol());
365 
366  Ptr<NetDeviceQueueInterface> devQueueIface;
367  std::map<Ptr<NetDevice>, NetDeviceInfo>::iterator ndi = m_netDevices.find(device);
368 
369  if (ndi != m_netDevices.end())
370  {
371  devQueueIface = ndi->second.m_ndqi;
372  }
373 
374  // determine the transmission queue of the device where the packet will be enqueued
375  std::size_t txq = 0;
376  if (devQueueIface && devQueueIface->GetNTxQueues() > 1)
377  {
378  txq = devQueueIface->GetSelectQueueCallback()(item);
379  // otherwise, Linux determines the queue index by using a hash function
380  // and associates such index to the socket which the packet belongs to,
381  // so that subsequent packets of the same socket will be mapped to the
382  // same tx queue (__netdev_pick_tx function in net/core/dev.c). It is
383  // pointless to implement this in ns-3 because currently the multi-queue
384  // devices provide a select queue callback
385  }
386 
387  NS_ASSERT(!devQueueIface || txq < devQueueIface->GetNTxQueues());
388 
389  if (ndi == m_netDevices.end() || !ndi->second.m_rootQueueDisc)
390  {
391  // The device has no attached queue disc, thus add the header to the packet and
392  // send it directly to the device if the selected queue is not stopped
393  item->AddHeader();
394  if (!devQueueIface || !devQueueIface->GetTxQueue(txq)->IsStopped())
395  {
396  // a single queue device makes no use of the priority tag
397  if (!devQueueIface || devQueueIface->GetNTxQueues() == 1)
398  {
399  SocketPriorityTag priorityTag;
400  item->GetPacket()->RemovePacketTag(priorityTag);
401  }
402  device->Send(item->GetPacket(), item->GetAddress(), item->GetProtocol());
403  }
404  else
405  {
406  m_dropped(item->GetPacket());
407  }
408  }
409  else
410  {
411  // Enqueue the packet in the queue disc associated with the netdevice queue
412  // selected for the packet and try to dequeue packets from such queue disc
413  item->SetTxQueueIndex(txq);
414 
415  Ptr<QueueDisc> qDisc = ndi->second.m_queueDiscsToWake[txq];
416  NS_ASSERT(qDisc);
417  qDisc->Enqueue(item);
418  qDisc->Run();
419  }
420 }
421 
422 } // namespace ns3
a polymophic address class
Definition: address.h:100
PacketType
Packet types are used as they are in Linux.
Definition: net-device.h:300
Network device transmission queue interface.
uint32_t GetNDevices() const
Definition: node.cc:162
uint32_t GetId() const
Definition: node.cc:117
Ptr< NetDevice > GetDevice(uint32_t index) const
Retrieve the index-th NetDevice associated to this node.
Definition: node.cc:152
A base class which provides memory management and object aggregation.
Definition: object.h:89
virtual void NotifyNewAggregate()
Notify all Objects aggregated to this one of a new Object being aggregated.
Definition: object.cc:332
virtual void DoInitialize()
Initialize() implementation.
Definition: object.cc:360
virtual void DoDispose()
Destructor implementation.
Definition: object.cc:353
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
void Run()
Modelled after the Linux function __qdisc_run (net/sched/sch_generic.c) Dequeues multiple packets,...
Definition: queue-disc.cc:953
Ptr< QueueDiscClass > GetQueueDiscClass(std::size_t i) const
Get the i-th queue disc class.
Definition: queue-disc.cc:658
bool Enqueue(Ptr< QueueDiscItem > item)
Pass a packet to store to the queue discipline.
Definition: queue-disc.cc:859
indicates whether the socket has a priority set.
Definition: socket.h:1316
The Traffic Control layer aims at introducing an equivalent of the Linux Traffic Control infrastructu...
void NotifyNewAggregate() override
Notify all Objects aggregated to this one of a new Object being aggregated.
static TypeId GetTypeId()
Get the type ID.
TypeId GetInstanceTypeId() const override
Get the type ID for the instance.
Ptr< QueueDisc > GetRootQueueDiscOnDeviceByIndex(uint32_t index) const
Required by the object map accessor.
void DoInitialize() override
Initialize() implementation.
void DoDispose() override
Destructor implementation.
TracedCallback< Ptr< const Packet > > m_dropped
The trace source fired when the Traffic Control layer drops a packet because no queue disc is install...
ProtocolHandlerList m_handlers
List of upper-layer handlers.
Ptr< Node > m_node
The node this TrafficControlLayer object is aggregated to.
uint32_t GetNDevices() const
Required by the object map accessor.
std::map< Ptr< NetDevice >, NetDeviceInfo > m_netDevices
Map storing the required information for each device with a queue disc installed.
virtual void DeleteRootQueueDiscOnDevice(Ptr< NetDevice > device)
This method can be used to remove the root queue disc (and associated filters, classes and queues) in...
std::vector< Ptr< QueueDisc > > QueueDiscVector
Typedef for queue disc vector.
virtual void ScanDevices()
Collect information needed to determine how to handle packets destined to each of the NetDevices of t...
virtual void Send(Ptr< NetDevice > device, Ptr< QueueDiscItem > item)
Called from upper layer to queue a packet for the transmission.
void SetNode(Ptr< Node > node)
Set node associated with this stack.
virtual Ptr< QueueDisc > GetRootQueueDiscOnDevice(Ptr< NetDevice > device) const
This method can be used to get the root queue disc installed on a device.
virtual void Receive(Ptr< NetDevice > device, Ptr< const Packet > p, uint16_t protocol, const Address &from, const Address &to, NetDevice::PacketType packetType)
Called by NetDevices, incoming packet.
virtual void SetRootQueueDiscOnDevice(Ptr< NetDevice > device, Ptr< QueueDisc > qDisc)
This method can be used to set the root queue disc installed on a device.
void RegisterProtocolHandler(Node::ProtocolHandler handler, uint16_t protocolType, Ptr< NetDevice > device)
Register an IN handler.
a unique identifier for an interface.
Definition: type-id.h:60
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:935
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
ObjectPtrContainerValue ObjectMapValue
ObjectMapValue is an alias for ObjectPtrContainerValue.
Definition: object-map.h:40
Ptr< const AttributeAccessor > MakeObjectMapAccessor(U T::*memberVariable)
MakeAccessorHelper implementation for ObjectVector.
Definition: object-map.h:76
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition: abort.h:49
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#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_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:46
Ptr< const TraceSourceAccessor > MakeTraceSourceAccessor(T a)
Create a TraceSourceAccessor which will control access to the underlying trace source.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
Callback< R, Args... > MakeCallback(R(T::*memPtr)(Args...), OBJ objPtr)
Build Callbacks for class method members which take varying numbers of arguments and potentially retu...
Definition: callback.h:707
Information to store for each device.
Protocol handler entry.
Node::ProtocolHandler handler
the protocol handler
bool promiscuous
true if it is a promiscuous handler
uint16_t protocol
the protocol number
Ptr< NetDevice > device
the NetDevice