fd-net-device: add support for emulation in netmap mode

This commit is contained in:
Pasquale Imputato
2020-05-19 13:28:06 +02:00
committed by Tom Henderson
parent 3766889b74
commit ff8083ca2e
20 changed files with 2221 additions and 53 deletions

View File

@@ -38,6 +38,7 @@ SOURCES = \
$(SRC)/mpi/doc/distributed.rst \
$(SRC)/energy/doc/energy.rst \
$(SRC)/fd-net-device/doc/fd-net-device.rst \
$(SRC)/fd-net-device/doc/netmap-net-device.rst \
$(SRC)/tap-bridge/doc/tap.rst \
$(SRC)/mesh/doc/source/mesh.rst \
$(SRC)/mesh/doc/source/mesh-user.rst \

View File

@@ -0,0 +1,79 @@
Netmap NetDevice
-------------------------
.. include:: replace.txt
.. highlight:: cpp
.. heading hierarchy:
------------- Chapter
************* Section (#.#)
============= Subsection (#.#.#)
############# Paragraph (no number)
The ``fd-net-device`` module provides the ``NetmapNetDevice`` class, a class derived
from the ``FdNetDevice`` which is able to read and write traffic using a netmap file descriptor.
This netmap file descriptor must be associated to a real ethernet device in the host machine.
The ``NetmapNetDeviceHelper`` class supports the configuration of a ``NetmapNetDevice``.
netmap is a fast packets processing that bypasses the host networking stack and gains
direct access to network device.
Model Description
*****************
Design
======
Scope and Limitations
=====================
The main scope of ``NetmapNetDevice`` is to support the flow-control between the physical device and
the upper layer and using at best the computational resources to process packets.
Usage
*****
Helpers
=======
Attributes
==========
Output
======
Examples
========
Several examples are provided:
* ``fd-emu-onoff.cc``: This example is aimed at measuring the throughput of the
NetmapNetDevice when using the NetmapNetDeviceHelper to attach the simulated
device to a real device in the host machine. This is achieved by saturating
the channel with TCP or UDP traffic.
* ``fd-emu-ping.cc``: This example uses the NetmapNetDevice to send ICMP
traffic over a real device.
* ``fd-emu-tc.cc``: This example configures a router on a machine with two interfaces
in emulated mode through netmap. The aim is to explore different qdiscs behaviours on the backlog of a
device emulated bottleneck side.
* ``fd-emu-send.cc``: This example builds a node with a device in emulation mode through netmap.
The aim is to measure the maximum tx rate in pps achievable with NetmapNetDevice on a specific machine.
Note that all the examples run in emulation mode through netmap (with NetmapNetDevice) and raw socket (with FdNetDevice).
Acknowledgments
***************
The NetmapNetDevice has been presented and evaluated in
* Pasquale Imputato, Stefano Avallone, Enhancing the fidelity of network emulation through direct access to device buffers, Journal of Network and Computer Applications, Volume 130, 2019, Pages 63-75, (http://www.sciencedirect.com/science/article/pii/S1084804519300220)

View File

@@ -1,6 +1,7 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2012 University of Washington, 2012 INRIA
* 2017 Università' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -16,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Alina Quereilhac <alina.quereilhac@inria.fr>
* Extended by: Pasquale Imputato <p.imputato@gmail.com>
*
*/
@@ -28,10 +30,11 @@
// | +----------------+ | | +----------------+ |
// | | ns-3 IPv4 | | | | ns-3 IPv4 | |
// | +----------------+ | | +----------------+ |
// | | FdNetDevice | | | | FdNetDevice | |
// | | FdNetDevice or | | | | FdNetDevice or | |
// | | NetmapNetDevice| | | | NetmapNetDevice| |
// | | 10.1.1.1 | | | | 10.1.1.2 | |
// | +----------------+ | | +----------------+ |
// | | raw socket | | | | raw socket | |
// | | fd | | | | fd | |
// | +----------------+ | | +----------------+ |
// | | eth0 | | | | eth0 | |
// +-------+------+-------+ +--------+------+-------+
@@ -41,9 +44,9 @@
// | |
// +----------------------------+
//
// This example is aimed at measuring the throughput of the FdNetDevice
// when using the EmuFdNetDeviceHelper. This is achieved by saturating
// the channel with TCP traffic. Then the throughput can be obtained from
// This example is aimed at measuring the throughput of the FdNetDevice (NetmapNetDevice)
// when using the EmuFdNetDeviceHelper (NetmapNetDeviceHelper). This is achieved by saturating
// the channel with TCP or UDP traffic. Then the throughput can be obtained from
// the generated .pcap files.
//
// To run this example you will need two hosts (client & server).
@@ -59,13 +62,20 @@
//
// both machines: $ sudo ip link set eth0 promisc on
//
// 4 - Give root suid to the raw socket creator binary.
// 3' - If you run emulation in netmap mode, you need before to load the netmap.ko module.
// The user is in charge to configure and build netmap separately.
//
// 4 - Give root suid to the raw or netmap socket creator binary.
// If the --enable-sudo option was used to configure ns-3 with waf, then the following
// step will not be necessary.
//
// both hosts: $ sudo chown root.root build/src/fd-net-device/ns3-dev-raw-sock-creator
// both hosts: $ sudo chmod 4755 build/src/fd-net-device/ns3-dev-raw-sock-creator
//
// or (if you run emulation in netmap mode):
// both hosts: $ sudo chown root.root build/src/fd-net-device/ns3-dev-netmap-device-creator
// both hosts: $ sudo chmod 4755 build/src/fd-net-device/ns3-dev-netmap-device-creator
//
// 5 - Run the server side:
//
// server host: $ ./waf --run="fd-emu-onoff --serverMode=1"
@@ -95,8 +105,8 @@ int
main (int argc, char *argv[])
{
uint16_t sinkPort = 8000;
uint32_t packetSize = 10000; // bytes
std::string dataRate ("1000Mb/s");
uint32_t packetSize = 1400; // bytes
std::string dataRate("1000Mb/s");
bool serverMode = false;
std::string deviceName ("eth0");
@@ -105,6 +115,13 @@ main (int argc, char *argv[])
std::string netmask ("255.255.255.0");
std::string macClient ("00:00:00:00:00:01");
std::string macServer ("00:00:00:00:00:02");
std::string transportProt = "Tcp";
std::string socketType;
#ifdef HAVE_PACKET_H
std::string emuMode ("raw");
#else // HAVE_NETMAP_USER_H is true (otherwise this example is not compiled)
std::string emuMode ("netmap");
#endif
CommandLine cmd (__FILE__);
cmd.AddValue ("deviceName", "Device name", deviceName);
@@ -115,8 +132,19 @@ main (int argc, char *argv[])
cmd.AddValue ("mac-client", "Mac Address for Server Client : 00:00:00:00:00:01", macClient);
cmd.AddValue ("mac-server", "Mac Address for Server Default : 00:00:00:00:00:02", macServer);
cmd.AddValue ("data-rate", "Data rate defaults to 1000Mb/s", dataRate);
cmd.AddValue ("transportProt", "Transport protocol to use: Tcp, Udp", transportProt);
cmd.AddValue ("emuMode", "Emulation mode in {raw, netmap}", emuMode);
cmd.Parse (argc, argv);
if (transportProt.compare ("Tcp") == 0)
{
socketType = "ns3::TcpSocketFactory";
}
else
{
socketType = "ns3::UdpSocketFactory";
}
Ipv4Address remoteIp;
Ipv4Address localIp;
Mac48AddressValue localMac;
@@ -144,9 +172,31 @@ main (int argc, char *argv[])
Ptr<Node> node = CreateObject<Node> ();
NS_LOG_INFO ("Create Device");
EmuFdNetDeviceHelper emu;
emu.SetDeviceName (deviceName);
NetDeviceContainer devices = emu.Install (node);
FdNetDeviceHelper* helper = nullptr;
#ifdef HAVE_PACKET_H
if (emuMode == "raw")
{
EmuFdNetDeviceHelper* raw = new EmuFdNetDeviceHelper;
raw->SetDeviceName (deviceName);
helper = raw;
}
#endif
#ifdef HAVE_NETMAP_USER_H
if (emuMode == "netmap")
{
NetmapNetDeviceHelper* netmap = new NetmapNetDeviceHelper;
netmap->SetDeviceName (deviceName);
helper = netmap;
}
#endif
if (helper == nullptr)
{
NS_ABORT_MSG (emuMode << " not supported.");
}
NetDeviceContainer devices = helper->Install (node);
Ptr<NetDevice> device = devices.Get (0);
device->SetAttribute ("Address", localMac);
@@ -193,6 +243,7 @@ main (int argc, char *argv[])
Simulator::Stop (Seconds (61.0));
Simulator::Run ();
Simulator::Destroy ();
delete helper;
return 0;
}

View File

@@ -1,6 +1,7 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2012 University of Washington, 2012 INRIA
* Copyright (c) 2012 University of Washington, 2012 INRIA
* 2017 Università' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -29,7 +30,8 @@
// | +----------------+ |
// | | ns-3 IPv4 | |
// | +----------------+ |
// | | FdNetDevice | |
// | | FdNetDevice or | |
// | | NetmapNetDevice| |
// |--+----------------+--+
// | | eth0 | |
// | +------+ |
@@ -46,6 +48,8 @@
// device name in as a command-line argument
// 2) The host device must be set to promiscuous mode
// (e.g. "sudo ifconfig eth0 promisc")
// 2') If you run emulation in netmap mode, you need before to load the netmap.ko module.
// The user is in charge to configure and build netmap separately.
// 3) Be aware that ns-3 will generate a fake mac address, and that in
// some enterprise networks, this may be considered bad form to be
// sending packets out of your device with "unauthorized" mac addresses
@@ -59,12 +63,16 @@
// 'netstat -rn' command and find the IP address of the default gateway
// on your host. Search for "Ipv4Address gateway" and replace the string
// "1.2.3.4" string with the gateway IP address.
/// 6) Give root suid to the raw socket creator binary.
/// 6) Give root suid to the raw or netmap socket creator binary.
// If the --enable-sudo option was used to configure ns-3 with waf, then the following
// step will not be necessary.
//
// $ sudo chown root.root build/src/fd-net-device/ns3-dev-raw-sock-creator
// $ sudo chmod 4755 build/src/fd-net-device/ns3-dev-raw-sock-creator
//
// or (if you run emulation in netmap mode):
// $ sudo chown root.root build/src/fd-net-device/ns3-dev-netmap-device-creator
// $ sudo chmod 4755 build/src/fd-net-device/ns3-dev-netmap-device-creator
//
#include "ns3/abort.h"
@@ -92,7 +100,14 @@ main (int argc, char *argv[])
NS_LOG_INFO ("Ping Emulation Example");
std::string deviceName ("eth0");
std::string remote ("173.194.34.51"); // example.com
std::string remote ("8.8.8.8");
std::string localAddress ("1.2.3.4");
std::string localGateway ("1.2.3.4");
#ifdef HAVE_PACKET_H
std::string emuMode ("raw");
#else // HAVE_NETMAP_USER_H is true (otherwise this example is not compiled)
std::string emuMode ("netmap");
#endif
//
// Allow the user to override any of the defaults at run-time, via
@@ -101,10 +116,13 @@ main (int argc, char *argv[])
CommandLine cmd (__FILE__);
cmd.AddValue ("deviceName", "Device name", deviceName);
cmd.AddValue ("remote", "Remote IP address (dotted decimal only please)", remote);
cmd.AddValue ("localIp", "Local IP address (dotted decimal only please)", localAddress);
cmd.AddValue ("gateway", "Gateway address (dotted decimal only please)", localGateway);
cmd.AddValue ("emuMode", "Emulation mode in {raw, netmap}", emuMode);
cmd.Parse (argc, argv);
Ipv4Address remoteIp (remote.c_str ());
Ipv4Address localIp ("1.2.3.4");
Ipv4Address localIp (localAddress.c_str ());
NS_ABORT_MSG_IF (localIp == "1.2.3.4", "You must change the local IP address before running this example");
Ipv4Mask localMask ("255.255.255.0");
@@ -149,9 +167,32 @@ main (int argc, char *argv[])
// OUI flying around. Be aware.
//
NS_LOG_INFO ("Create Device");
EmuFdNetDeviceHelper emu;
emu.SetDeviceName (deviceName);
NetDeviceContainer devices = emu.Install (node);
FdNetDeviceHelper* helper = nullptr;
#ifdef HAVE_PACKET_H
if (emuMode == "raw")
{
EmuFdNetDeviceHelper* raw = new EmuFdNetDeviceHelper;
raw->SetDeviceName (deviceName);
helper = raw;
}
#endif
#ifdef HAVE_NETMAP_USER_H
if (emuMode == "netmap")
{
NetmapNetDeviceHelper* netmap = new NetmapNetDeviceHelper;
netmap->SetDeviceName (deviceName);
helper = netmap;
}
#endif
if (helper == nullptr)
{
NS_ABORT_MSG (emuMode << " not supported.");
}
NetDeviceContainer devices = helper->Install (node);
Ptr<NetDevice> device = devices.Get (0);
device->SetAttribute ("Address", Mac48AddressValue (Mac48Address::Allocate ()));
@@ -188,7 +229,7 @@ main (int argc, char *argv[])
// the default gateway on your host and add it below, replacing the
// "1.2.3.4" string.
//
Ipv4Address gateway ("1.2.3.4");
Ipv4Address gateway (localGateway.c_str ());
NS_ABORT_MSG_IF (gateway == "1.2.3.4", "You must change the gateway IP address before running this example");
Ipv4StaticRoutingHelper ipv4RoutingHelper;
@@ -207,7 +248,7 @@ main (int argc, char *argv[])
app->SetAttribute ("Verbose", BooleanValue (true) );
node->AddApplication (app);
app->SetStartTime (Seconds (1.0));
app->SetStopTime (Seconds (21.0));
app->SetStopTime (Seconds (22.0));
//
// Give the application a name. This makes life much easier when constructing
@@ -223,14 +264,15 @@ main (int argc, char *argv[])
//
// Enable a promiscuous pcap trace to see what is coming and going on our device.
//
emu.EnablePcap ("emu-ping", device, true);
helper->EnablePcap (emuMode + "-emu-ping", device, true);
//
// Now, do the actual emulation.
//
NS_LOG_INFO ("Run Emulation.");
Simulator::Stop (Seconds (22.0));
NS_LOG_INFO ("Run Emulation in " << emuMode << " mode.");
Simulator::Stop (Seconds (23.0));
Simulator::Run ();
Simulator::Destroy ();
delete helper;
NS_LOG_INFO ("Done.");
}

View File

@@ -0,0 +1,230 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
/*
* This example builds a node with a device in emulation mode in {raw, netmap}.
* The aim is to measure the maximum tx rate in pps achievable with NetmapNetDevice and FdNetDevice on a specific machine.
* The emulated device must be connected and in promiscuous mode.
*
* If you run emulation in netmap mode, you need before to load the netmap.ko module.
* The user is in charge to configure and build netmap separately.
*
*/
#include "ns3/abort.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/fd-net-device-module.h"
#include "ns3/internet-apps-module.h"
#include "ns3/ipv4-static-routing-helper.h"
#include "ns3/ipv4-list-routing-helper.h"
#include "ns3/applications-module.h"
#include "ns3/traffic-control-module.h"
#include <chrono>
#include <unistd.h>
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("NetmapEmulationSendExample");
// this function sends a number packets by means the SendFrom method or
// the Write method (depending from the level value) of a FdNetDevice or of a NetmapNetDevice
// (depending from the emulation mode value).
static void
Send (Ptr<NetDevice> dev, int level, std::string emuMode)
{
Ptr<FdNetDevice> device = DynamicCast<FdNetDevice> (dev);
int packets = 10000000;
Mac48Address sender = Mac48Address("00:00:00:aa:00:01");
Mac48Address receiver = Mac48Address("ff:ff:ff:ff:ff:ff");
int packetsSize = 64;
Ptr<Packet> packet = Create<Packet> (packetsSize);
EthernetHeader header;
ssize_t len = (size_t) packet->GetSize ();
uint8_t *buffer = (uint8_t*)malloc (len);
packet->CopyData (buffer, len);
int sent = 0;
int failed = 0;
Ptr<NetDeviceQueue> ndq = nullptr;
if (emuMode == "netmap")
{
Ptr<NetDeviceQueueInterface> ndqi = dev->GetObject<NetDeviceQueueInterface> ();
ndq = ndqi->GetTxQueue (0);
}
std::cout << ((level == 0) ? "Writing" : "Sending") << std::endl;
// period to print the stats
std::chrono::milliseconds period (1000);
auto t1 = std::chrono::high_resolution_clock::now();
while (packets > 0)
{
// in case of netmap emulated device we check for
// available slot in the netmap transmission ring
if (ndq)
{
while (ndq->IsStopped ())
{
usleep (10);
}
}
if (level == 1)
{
if (device->SendFrom (packet, sender, receiver, 0) == false)
{
failed++;
}
sent++;
packet->RemoveHeader (header);
}
if (level == 0)
{
if (device->Write (buffer, len) != len)
{
failed++;
}
sent++;
}
auto t2 = std::chrono::high_resolution_clock::now();
if (t2 - t1 >= period)
{
// print stats
std::chrono::duration<double, std::milli> dur = (t2 - t1); // in ms
double estimatedThr = ((sent - failed) * packetsSize * 8) / 1000000; // in Mbps
std::cout << sent << " packets sent in "<< dur.count () << " ms, failed " << failed <<" (" << estimatedThr << " Mbps estimanted throughput)" << std::endl ;
sent = 0;
failed = 0;
t1 = std::chrono::high_resolution_clock::now();
}
packets--;
}
}
int
main (int argc, char *argv[])
{
NS_LOG_INFO ("Emu Send Example");
std::string deviceName ("eno1");
int level = 0;
std::string localAddress ("1.2.3.4");
std::string localGateway ("1.2.3.4");
#ifdef HAVE_PACKET_H
std::string emuMode ("raw");
#else // HAVE_NETMAP_USER_H is true (otherwise this example is not compiled)
std::string emuMode ("netmap");
#endif
CommandLine cmd;
cmd.AddValue ("deviceName", "Device name", deviceName);
cmd.AddValue ("level", "Enable send (1) or write (0) level test", level);
cmd.AddValue ("localIp", "Local IP address (dotted decimal only please)", localAddress);
cmd.AddValue ("gateway", "Gateway address (dotted decimal only please)", localGateway);
cmd.AddValue ("emuMode", "Emulation mode in {raw, netmap}", emuMode);
cmd.Parse (argc, argv);
Ipv4Address localIp (localAddress.c_str ());
NS_ABORT_MSG_IF (localIp == "1.2.3.4", "You must change the local IP address before running this example");
Ipv4Mask localMask (localGateway.c_str ());
NS_ABORT_MSG_IF (localMask == "1.2.3.4", "You must change the local mask before running this example");
GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl"));
GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));
NS_LOG_INFO ("Create Node");
Ptr<Node> node = CreateObject<Node> ();
NS_LOG_INFO ("Create Device");
FdNetDeviceHelper* helper = nullptr;
#ifdef HAVE_PACKET_H
if (emuMode == "raw")
{
EmuFdNetDeviceHelper* raw = new EmuFdNetDeviceHelper;
raw->SetDeviceName (deviceName);
helper = raw;
}
#endif
#ifdef HAVE_NETMAP_USER_H
if (emuMode == "netmap")
{
NetmapNetDeviceHelper* netmap = new NetmapNetDeviceHelper;
netmap->SetDeviceName (deviceName);
helper = netmap;
}
#endif
if (helper == nullptr)
{
NS_ABORT_MSG (emuMode << " not supported.");
}
NetDeviceContainer devices = helper->Install (node);
Ptr<NetDevice> device = devices.Get (0);
device->SetAttribute ("Address", Mac48AddressValue (Mac48Address::Allocate ()));
// NS_LOG_INFO ("Add Internet Stack");
// InternetStackHelper internetStackHelper;
// internetStackHelper.Install (node);
// TrafficControlHelper tch;
// tch.SetRootQueueDisc ("ns3::PfifoFastQueueDisc");
// tch.Install (devices);
Simulator::Schedule (Seconds (3), &Send, device, level, emuMode);
NS_LOG_INFO ("Run Emulation.");
Simulator::Stop (Seconds (6.0));
Simulator::Run ();
Simulator::Destroy ();
delete helper;
NS_LOG_INFO ("Done.");
}

View File

@@ -0,0 +1,313 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
/*
* node
* --------------------------------------------------
* | |
* | pfifo_fast queueDiscType [pfifo_fast]|
* | bql [false] |
* | interface 0 interface 1 |
* | | | |
* --------------------------------------------------
* | |
* 1 Gbps access incoming link | | 100 Mbps bottleneck outgoing link
* ----------------------------------- -----------------------------------
*
* This example builds a node with two interfaces in emulation mode in {raw, netmap}.
* The aim is to explore different qdiscs behaviours on the backlog of a device emulated bottleneck side.
*
* If you run emulation in netmap mode, you need before to load the netmap.ko module.
* The user is in charge to configure and build netmap separately.
*
*/
#include "ns3/abort.h"
#include "ns3/core-module.h"
#include "ns3/internet-module.h"
#include "ns3/network-module.h"
#include "ns3/fd-net-device-module.h"
#include "ns3/internet-apps-module.h"
#include "ns3/ipv4-list-routing-helper.h"
#include "ns3/traffic-control-module.h"
using namespace ns3;
NS_LOG_COMPONENT_DEFINE ("TrafficControlEmu");
void
TcPacketsInQueue (Ptr<QueueDisc> q, Ptr<OutputStreamWrapper> stream)
{
Simulator::Schedule (Seconds (0.001), &TcPacketsInQueue, q, stream);
*stream->GetStream () << Simulator::Now ().GetSeconds () << " backlog " << q->GetNPackets () << "p " << q->GetNBytes () << "b "<< " dropped "
<< q->GetStats ().nTotalDroppedPackets << "p " << q->GetStats ().nTotalDroppedBytes << "b " << std::endl;
}
#ifdef HAVE_NETMAP_USER_H
void
Inflight (Ptr<NetmapNetDevice> dev, Ptr<OutputStreamWrapper> stream)
{
Simulator::Schedule (Seconds (0.001), &Inflight, dev, stream);
*stream->GetStream () << dev->GetBytesInNetmapTxRing () << std::endl;
}
#endif
int
main (int argc, char *argv[])
{
NS_LOG_INFO ("Traffic Control Emulation Example");
std::string deviceName0 ("enx503f56005a2a");
std::string deviceName1 ("eno1");
std::string ip0 ("10.0.1.2");
std::string ip1 ("10.0.2.1");
std::string mask0 ("255.255.255.0");
std::string mask1 ("255.255.255.0");
std::string m0 ("00:00:00:aa:00:01");
std::string m1 ("00:00:00:aa:00:02");
std::string queueDiscType = "PfifoFast";
bool bql = false;
uint32_t index = 0;
bool writer = true;
#ifdef HAVE_PACKET_H
std::string emuMode ("raw");
#else // HAVE_NETMAP_USER_H is true (otherwise this example is not compiled)
std::string emuMode ("netmap");
#endif
CommandLine cmd;
cmd.AddValue ("deviceName0", "Device name", deviceName0);
cmd.AddValue ("deviceName1", "Device name", deviceName1);
cmd.AddValue ("ip0", "Local IP address", ip0);
cmd.AddValue ("ip1", "Local IP address", ip1);
cmd.AddValue ("mask0", "Local mask", mask0);
cmd.AddValue ("mask1", "Local mask", mask1);
cmd.AddValue ("m0", "Mac address", m0);
cmd.AddValue ("m1", "Mac address", m1);
cmd.AddValue ("writer", "Enable write stats", writer);
cmd.AddValue ("queueDiscType", "Bottleneck queue disc type in {PfifoFast, ARED, CoDel, FqCoDel, PIE}", queueDiscType);
cmd.AddValue ("bql", "Enable byte queue limits on bottleneck netdevice", bql);
cmd.AddValue ("index", "Experiment index", index);
cmd.AddValue ("emuMode", "Emulation mode in {raw, netmap}", emuMode);
cmd.Parse (argc, argv);
Ipv4Address localIp0 (ip0.c_str ());
Ipv4Address localIp1 (ip1.c_str ());
Ipv4Mask netmask0 (mask0.c_str ());
Ipv4Mask netmask1 (mask1.c_str ());
Mac48AddressValue mac0 (m0.c_str ());
Mac48AddressValue mac1 (m1.c_str ());
//
// Since we are using a real piece of hardware we need to use the realtime
// simulator.
//
GlobalValue::Bind ("SimulatorImplementationType", StringValue ("ns3::RealtimeSimulatorImpl"));
//
// Since we are going to be talking to real-world machines, we need to enable
// calculation of checksums in our protocols.
//
GlobalValue::Bind ("ChecksumEnabled", BooleanValue (true));
//
// In such a simple topology, the use of the helper API can be a hindrance
// so we drop down into the low level API and do it manually.
//
// First we need a single node.
//
NS_LOG_INFO ("Create Node");
Ptr<Node> node = CreateObject<Node> ();
//
// Create an emu device, allocate a MAC address and point the device to the
// Linux device name. The device needs a transmit queueing discipline so
// create a droptail queue and give it to the device. Finally, "install"
// the device into the node.
//
// Do understand that the ns-3 allocated MAC address will be sent out over
// your network since the emu net device will spoof it. By default, this
// address will have an Organizationally Unique Identifier (OUI) of zero.
// The Internet Assigned Number Authority IANA
//
// http://www.iana.org/assignments/ethernet-numberslocalIp
//
// reports that this OUI is unassigned, and so should not conflict with
// real hardware on your net. It may raise all kinds of red flags in a
// real environment to have packets from a device with an obviously bogus
// OUI flying around. Be aware.
//
NS_LOG_INFO ("Create Devices");
FdNetDeviceHelper *helper0 = nullptr;
FdNetDeviceHelper *helper1 = nullptr;
#ifdef HAVE_PACKET_H
if (emuMode == "raw")
{
EmuFdNetDeviceHelper *raw0 = new EmuFdNetDeviceHelper;
raw0->SetDeviceName (deviceName0);
helper0 = raw0;
EmuFdNetDeviceHelper *raw1 = new EmuFdNetDeviceHelper;
raw1->SetDeviceName (deviceName1);
helper1 = raw1;
}
#endif
#ifdef HAVE_NETMAP_USER_H
if (emuMode == "netmap")
{
NetmapNetDeviceHelper *netmap0 = new NetmapNetDeviceHelper;
netmap0->SetDeviceName (deviceName0);
helper0 = netmap0;
NetmapNetDeviceHelper *netmap1 = new NetmapNetDeviceHelper;
netmap1->SetDeviceName (deviceName1);
helper1 = netmap1;
}
#endif
if ((helper0 == nullptr) || (helper1 == nullptr))
{
NS_ABORT_MSG (emuMode << " not supported.");
}
NetDeviceContainer devices0 = helper0->Install (node);
NetDeviceContainer devices1 = helper1->Install (node);
Ptr<NetDevice> device0 = devices0.Get (0);
device0->SetAttribute ("Address", mac0);
Ptr<NetDevice> device1 = devices1.Get (0);
device1->SetAttribute ("Address", mac1);
//
// Add a default internet stack to the node. This gets us the ns-3 versions
// of ARP, IPv4, ICMP, UDP and TCP.
//
NS_LOG_INFO ("Add Internet Stack");
InternetStackHelper internetStackHelper;
internetStackHelper.Install (node);
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4> ();
NS_LOG_INFO ("Create IPv4 Interface 0");
uint32_t interface0 = ipv4->AddInterface (device0);
Ipv4InterfaceAddress address0 = Ipv4InterfaceAddress (localIp0, netmask0);
ipv4->AddAddress (interface0, address0);
ipv4->SetMetric (interface0, 0);
ipv4->SetForwarding (interface0, true);
ipv4->SetUp (interface0);
NS_LOG_INFO ("Create IPv4 Interface 1");
uint32_t interface1 = ipv4->AddInterface (device1);
Ipv4InterfaceAddress address1 = Ipv4InterfaceAddress (localIp1, netmask1);
ipv4->AddAddress (interface1, address1);
ipv4->SetMetric (interface1, 0);
ipv4->SetForwarding (interface1, true);
ipv4->SetUp (interface1);
Ipv4GlobalRoutingHelper::PopulateRoutingTables ();
// Traffic control configurations
// Access link side
TrafficControlHelper tch0;
tch0.SetRootQueueDisc ("ns3::PfifoFastQueueDisc", "MaxSize", StringValue ("1000p"));
tch0.Install (devices0);
// Bottleneck link side
TrafficControlHelper tch1;
if (queueDiscType.compare ("PfifoFast") == 0)
{
tch1.SetRootQueueDisc ("ns3::PfifoFastQueueDisc", "MaxSize", StringValue ("1000p"));
}
else if (queueDiscType.compare ("ARED") == 0)
{
tch1.SetRootQueueDisc ("ns3::RedQueueDisc");
Config::SetDefault ("ns3::RedQueueDisc::ARED", BooleanValue (true));
Config::SetDefault ("ns3::RedQueueDisc::MaxSize",
QueueSizeValue (QueueSize (QueueSizeUnit::BYTES, 1000000)));
Config::SetDefault ("ns3::RedQueueDisc::MeanPktSize", UintegerValue (1000));
Config::SetDefault ("ns3::RedQueueDisc::LinkBandwidth", StringValue ("100Mbps"));
Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (83333));
Config::SetDefault ("ns3::RedQueueDisc::MinTh", DoubleValue (250000));
}
else if (queueDiscType.compare ("CoDel") == 0)
{
tch1.SetRootQueueDisc ("ns3::CoDelQueueDisc");
}
else if (queueDiscType.compare ("FqCoDel") == 0)
{
tch1.SetRootQueueDisc ("ns3::FqCoDelQueueDisc");
}
else if (queueDiscType.compare ("PIE") == 0)
{
tch1.SetRootQueueDisc ("ns3::PieQueueDisc");
Config::SetDefault ("ns3::PieQueueDisc::MaxSize",
QueueSizeValue (QueueSize (QueueSizeUnit::PACKETS, 1000)));
Config::SetDefault ("ns3::PieQueueDisc::QueueDelayReference", TimeValue (Seconds (0.02)));
Config::SetDefault ("ns3::PieQueueDisc::Tupdate", TimeValue (Seconds (0.032)));
Config::SetDefault ("ns3::PieQueueDisc::A", DoubleValue (2));
Config::SetDefault ("ns3::PieQueueDisc::B", DoubleValue (20));
}
else
{
NS_ABORT_MSG ("--queueDiscType not valid");
}
if (bql)
{
tch1.SetQueueLimits ("ns3::DynamicQueueLimits");
}
QueueDiscContainer qdiscs = tch1.Install (devices1);
Ptr<QueueDisc> q = qdiscs.Get (0);
if (writer)
{
AsciiTraceHelper ascii;
Ptr<OutputStreamWrapper> stream = ascii.CreateFileStream ("exp-" + std::to_string (index) + "-router-tc-stats.txt");
Simulator::Schedule (Seconds (0.001), &TcPacketsInQueue, q, stream);
#ifdef HAVE_NETMAP_USER_H
if (emuMode.compare ("netmap") == 0)
{
Ptr<NetmapNetDevice> dev = StaticCast <NetmapNetDevice> (device1);
Ptr<OutputStreamWrapper> stream2 = ascii.CreateFileStream ("exp-" + std::to_string (index) + "-router-inflight.txt");
Simulator::Schedule (Seconds (0.001), &Inflight, dev, stream2);
}
#endif
Ipv4GlobalRoutingHelper g;
Ptr<OutputStreamWrapper> routingStream = Create<OutputStreamWrapper> ("router-routes.txt", std::ios::out);
g.PrintRoutingTableAllAt (Seconds (3), routingStream);
}
NS_LOG_INFO ("Run Emulation.");
Simulator::Stop (Seconds (50.0));
Simulator::Run ();
Simulator::Destroy ();
delete helper0;
delete helper1;
NS_LOG_INFO ("Done.");
}

View File

@@ -16,13 +16,19 @@ def build(bld):
obj = bld.create_ns3_program('realtime-fd2fd-onoff', ['fd-net-device', 'internet', 'applications'])
obj.source = 'realtime-fd2fd-onoff.cc'
if bld.env['ENABLE_TAP']:
if bld.env['ENABLE_EMU' or 'ENABLE_NETMAP_EMU']:
obj = bld.create_ns3_program('fd-emu-ping', ['fd-net-device', 'internet', 'internet-apps'])
obj.source = 'fd-emu-ping.cc'
obj = bld.create_ns3_program('fd-emu-udp-echo', ['fd-net-device', 'internet', 'applications'])
obj.source = 'fd-emu-udp-echo.cc'
obj = bld.create_ns3_program('fd-emu-onoff', ['fd-net-device', 'internet', 'applications'])
obj.source = 'fd-emu-onoff.cc'
obj = bld.create_ns3_program('fd-emu-tc', ['fd-net-device', 'internet', 'internet-apps', 'applications', 'traffic-control'])
obj.source = 'fd-emu-tc.cc'
obj = bld.create_ns3_program('fd-emu-send', ['fd-net-device', 'internet', 'internet-apps', 'applications', 'traffic-control'])
obj.source = 'fd-emu-send.cc'
if bld.env['ENABLE_EMU']:
obj = bld.create_ns3_program('fd-emu-udp-echo', ['fd-net-device', 'internet', 'applications'])
obj.source = 'fd-emu-udp-echo.cc'
if bld.env['ENABLE_DPDKNETDEV']:
obj = bld.create_ns3_program('fd-dpdk-emu-ping', ['fd-net-device', 'internet', 'internet-apps', 'applications'])
@@ -39,4 +45,3 @@ def build(bld):
if bld.env['ENABLE_PLANETLAB']:
obj = bld.create_ns3_program('fd-planetlab-ping', ['fd-net-device', 'internet', 'internet-apps'])
obj.source = 'fd-planetlab-ping.cc'

View File

@@ -67,6 +67,7 @@ EmuFdNetDeviceHelper::EmuFdNetDeviceHelper ()
{
m_deviceName = "undefined";
m_dpdkMode = false;
m_hostQdiscBypass = false;
}
void
@@ -92,6 +93,12 @@ EmuFdNetDeviceHelper::SetDpdkMode (int argc, char **argv)
#endif
}
void
EmuFdNetDeviceHelper::HostQdiscBypass (bool hostQdiscBypass)
{
m_hostQdiscBypass = hostQdiscBypass;
}
std::string
EmuFdNetDeviceHelper::GetDeviceName (void)
{
@@ -172,6 +179,18 @@ EmuFdNetDeviceHelper::SetFileDescriptor (Ptr<FdNetDevice> device) const
NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't get interface flags");
}
if (m_hostQdiscBypass)
{
static const int32_t sock_qdisc_bypass = 1;
int32_t sock_qdisc_ret = setsockopt (fd, SOL_PACKET, PACKET_QDISC_BYPASS, &sock_qdisc_bypass,
sizeof (sock_qdisc_bypass));
if (sock_qdisc_ret == -1)
{
NS_LOG_ERROR ("Cannot use the qdisc bypass option");
}
}
//
// This device only works if the underlying interface is up in promiscuous
// mode. We could have turned it on in the socket creator, but the situation

View File

@@ -70,6 +70,12 @@ public:
*/
void SetDpdkMode (int argc, char **argv);
/**
* \brief Request host qdisc bypass
* \param hostQdiscBypass to enable host qdisc bypass
*/
void HostQdiscBypass (bool hostQdiscBypass);
protected:
/**
* This method creates an ns3::FdNetDevice attached to a physical network
@@ -111,6 +117,8 @@ protected:
* Dpdk EAL arguements list.
*/
char **m_ealArgv;
bool m_hostQdiscBypass;
};
} // namespace ns3

View File

@@ -42,6 +42,12 @@ FdNetDeviceHelper::FdNetDeviceHelper ()
m_deviceFactory.SetTypeId ("ns3::FdNetDevice");
}
void
FdNetDeviceHelper::SetTypeId (std::string type)
{
m_deviceFactory.SetTypeId (type);
}
void
FdNetDeviceHelper::SetAttribute (std::string n1, const AttributeValue &v1)
{

View File

@@ -0,0 +1,95 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) University of Washington
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <unistd.h>
#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "creator-utils.h"
#define EMU_MAGIC 65867
using namespace ns3;
int
main (int argc, char *argv[])
{
int c;
char *path = NULL;
opterr = 0;
while ((c = getopt (argc, argv, "vp:")) != -1)
{
switch (c)
{
case 'v':
gVerbose = true;
break;
case 'p':
path = optarg;
break;
}
}
//
// This program is spawned by an emu net device running in a simulation. It
// wants to create a raw socket as described below. We are going to do the
// work here since we're running suid root. Once we create the raw socket,
// we have to send it back to the emu net device. We do that over a Unix
// (local interprocess) socket. The emu net device created a socket to
// listen for our response on, and it is expected to have encoded the address
// information as a string and to have passed that string as an argument to
// us. We see it here as the "path" string. We can't do anything useful
// unless we have that string.
//
ABORT_IF (path == NULL, "path is a required argument", 0);
LOG ("Provided path is \"" << path << "\"");
//
// The whole reason for all of the hoops we went through to call out to this
// program will pay off here. We created this program to run as suid root
// in order to keep the main simulation program from having to be run with
// root privileges. We need root privileges to be able to open a raw socket
// though. So all of these hoops are to allow us to execute the following
// single line of code:
//
LOG ("Creating netmap fd");
int sock = open ("/dev/netmap", O_RDWR);
ABORT_IF (sock == -1, "CreateSocket(): Unable to open netmap fd", 1);
//
// Send the socket back to the emu net device so it can go about its business
//
SendSocket (path, sock, EMU_MAGIC);
return 0;
}

View File

@@ -0,0 +1,477 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
#include "netmap-net-device-helper.h"
#include "encode-decode.h"
#include "ns3/abort.h"
#include "ns3/config.h"
#include "ns3/fd-net-device.h"
#include "ns3/log.h"
#include "ns3/names.h"
#include "ns3/object-factory.h"
#include "ns3/packet.h"
#include "ns3/simulator.h"
#include "ns3/trace-helper.h"
#include "ns3/netmap-net-device.h"
#include "ns3/uinteger.h"
#include <arpa/inet.h>
#include <errno.h>
#include <iostream>
#include <iomanip>
#include <limits>
#include <memory>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#include <string>
#include <net/netmap_user.h>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("NetmapNetDeviceHelper");
#define EMU_MAGIC 65867
NetmapNetDeviceHelper::NetmapNetDeviceHelper ()
{
m_deviceName = "undefined";
SetTypeId ("ns3::NetmapNetDevice");
}
std::string
NetmapNetDeviceHelper::GetDeviceName (void)
{
return m_deviceName;
}
void
NetmapNetDeviceHelper::SetDeviceName (std::string deviceName)
{
m_deviceName = deviceName;
}
Ptr<NetDevice>
NetmapNetDeviceHelper::InstallPriv (Ptr<Node> node) const
{
Ptr<NetDevice> d = FdNetDeviceHelper::InstallPriv (node);
Ptr<FdNetDevice> device = d->GetObject<FdNetDevice> ();
SetDeviceAttributes (device);
int fd = CreateFileDescriptor ();
Ptr<NetmapNetDevice> netmapDevice = DynamicCast<NetmapNetDevice> (device);
SwitchInNetmapMode (fd, netmapDevice);
// Aggregate NetDeviceQueueInterface object
Ptr<NetDeviceQueueInterface> ndqi = CreateObjectWithAttributes<NetDeviceQueueInterface> (
"TxQueuesType", TypeIdValue (NetDeviceQueueLock::GetTypeId ()),
"NTxQueues", UintegerValue (1));
device->AggregateObject (ndqi);
netmapDevice->SetNetDeviceQueue (ndqi->GetTxQueue (0));
return device;
}
void
NetmapNetDeviceHelper::SetDeviceAttributes (Ptr<FdNetDevice> device) const
{
if (m_deviceName == "undefined")
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::SetFileDescriptor (): m_deviceName is not set");
}
//
// Call out to a separate process running as suid root in order to get a raw
// socket. We do this to avoid having the entire simulation running as root.
//
int fd = socket (PF_INET, SOCK_DGRAM, 0);
//
// Figure out which interface index corresponds to the device name in the corresponding attribute.
//
struct ifreq ifr;
bzero (&ifr, sizeof(ifr));
strncpy ((char *)ifr.ifr_name, m_deviceName.c_str (), IFNAMSIZ - 1);
NS_LOG_LOGIC ("Getting interface index");
int32_t rc = ioctl (fd, SIOCGIFINDEX, &ifr);
if (rc == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::SetFileDescriptor (): Can't get interface index");
}
rc = ioctl (fd, SIOCGIFFLAGS, &ifr);
if (rc == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::SetFileDescriptor (): Can't get interface flags");
}
//
// This device only works if the underlying interface is up in promiscuous
// mode. We could have turned it on in the socket creator, but the situation
// is that we expect these devices to be used in conjunction with virtual
// machines with connected host-only (simulated) networks, or in a testbed.
// There is a lot of setup and configuration happening outside of this one
// issue, and we expect that configuration to include choosing a valid
// interface (e.g, "ath1"), ensuring that the device supports promiscuous
// mode, and placing it in promiscuous mode. We just make sure of the
// end result.
//
if ((ifr.ifr_flags & IFF_PROMISC) == 0)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::SetFileDescriptor (): "
<< m_deviceName.c_str ()
<< " is not in promiscuous mode. Please config the interface in promiscuous "
"mode before to run the simulation.");
}
if ((ifr.ifr_flags & IFF_BROADCAST) != IFF_BROADCAST)
{
// We default m_isBroadcast to true but turn it off here if not
// supported, because in the common case, overlying IP code will
// assert during configuration time if this is false, before this
// method has a chance to set it during runtime
device->SetIsBroadcast (false);
}
if ((ifr.ifr_flags & IFF_MULTICAST) == IFF_MULTICAST)
{
// This one is OK to enable at runtime
device->SetIsMulticast (true);
}
// Set the MTU of the device to the mtu of the associated network interface
// struct ifreq ifr2;
//
// bzero (&ifr2, sizeof (ifr2));
// strcpy (ifr2.ifr_name, m_deviceName.c_str ());
// int32_t mtufd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP);
rc = ioctl (fd, SIOCGIFMTU, &ifr);
if (rc == -1)
{
NS_FATAL_ERROR ("FdNetDevice::SetFileDescriptor (): Can't ioctl SIOCGIFMTU");
}
NS_LOG_DEBUG ("Device MTU " << ifr.ifr_mtu);
device->SetMtu (ifr.ifr_mtu);
close (fd);
}
int
NetmapNetDeviceHelper::CreateFileDescriptor (void) const
{
NS_LOG_FUNCTION (this);
//
// We want to create a raw socket for our net device. Unfortunately for us
// you have to have root privileges to do that. Instead of running the
// entire simulation as root, we decided to make a small program who's whole
// reason for being is to run as suid root and create a raw socket. We're
// going to fork and exec that program soon, but we need to have a socket
// to talk to it with. So we create a local interprocess (Unix) socket
// for that purpose.
//
int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
if (sock == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = " << strerror (errno));
}
//
// Bind to that socket and let the kernel allocate an endpoint
//
struct sockaddr_un un;
memset (&un, 0, sizeof (un));
un.sun_family = AF_UNIX;
int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
if (status == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = " << strerror (errno));
}
NS_LOG_INFO ("Created Unix socket");
NS_LOG_INFO ("sun_family = " << un.sun_family);
NS_LOG_INFO ("sun_path = " << un.sun_path);
//
// We have a socket here, but we want to get it there -- to the program we're
// going to exec. What we'll do is to do a getsockname and then encode the
// resulting address information as a string, and then send the string to the
// program as an argument. So we need to get the sock name.
//
socklen_t len = sizeof (un);
status = getsockname (sock, (struct sockaddr*)&un, &len);
if (status == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = " << strerror (errno));
}
//
// Now encode that socket name (family and path) as a string of hex digits
//
std::string path = BufferToString ((uint8_t *)&un, len);
NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
//
// Fork and exec the process to create our socket. If we're us (the parent)
// we wait for the child (the socket creator) to complete and read the
// socket it created using the ancillary data mechanism.
//
// Tom Goff reports the possiblility of a deadlock when trying to acquire the
// python GIL here. He says that this might be due to trying to access Python
// objects after fork() without calling PyOS_AfterFork() to properly reset
// Python state (including the GIL). There is no code to cause the problem
// here in emu, but this was visible in similar code in tap-bridge.
//
pid_t pid = ::fork ();
if (pid == 0)
{
NS_LOG_DEBUG ("Child process");
//
// build a command line argument from the encoded endpoint string that
// the socket creation process will use to figure out how to respond to
// the (now) parent process.
//
std::ostringstream oss;
oss << "-p" << path;
NS_LOG_INFO ("Parameters set to \"" << oss.str () << "\"");
//
// Execute the socket creation process image.
//
status = ::execlp (NETMAP_DEV_CREATOR,
NETMAP_DEV_CREATOR, // argv[0] (filename)
oss.str ().c_str (), // argv[1] (-p<path?
(char *)NULL);
//
// If the execlp successfully completes, it never returns. If it returns it failed or the OS is
// broken. In either case, we bail.
//
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), status = " <<
status << ", errno = " << ::strerror (errno));
}
else
{
NS_LOG_DEBUG ("Parent process");
//
// We're the process running the emu net device. We need to wait for the
// socket creator process to finish its job.
//
int st;
pid_t waited = waitpid (pid, &st, 0);
if (waited == -1)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = " << strerror (errno));
}
NS_ASSERT_MSG (pid == waited, "NetmapNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
//
// Check to see if the socket creator exited normally and then take a
// look at the exit code. If it bailed, so should we. If it didn't
// even exit normally, we bail too.
//
if (WIFEXITED (st))
{
int exitStatus = WEXITSTATUS (st);
if (exitStatus != 0)
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): socket creator exited normally with status " << exitStatus);
}
}
else
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
}
//
// At this point, the socket creator has run successfully and should
// have created our raw socket and sent it back to the socket address
// we provided. Our socket should be waiting on the Unix socket. We've
// got to do a bunch of grunto work to get at it, though.
//
// The struct iovec below is part of a scatter-gather list. It describes a
// buffer. In this case, it describes a buffer (an integer) that will
// get the data that comes back from the socket creator process. It will
// be a magic number that we use as a consistency/sanity check.
//
struct iovec iov;
uint32_t magic;
iov.iov_base = &magic;
iov.iov_len = sizeof(magic);
//
// The CMSG macros you'll see below are used to create and access control
// messages (which is another name for ancillary data). The ancillary
// data is made up of pairs of struct cmsghdr structures and associated
// data arrays.
//
// First, we're going to allocate a buffer on the stack to receive our
// data array (that contains the socket). Sometimes you'll see this called
// an "ancillary element" but the msghdr uses the control message terminology
// so we call it "control."
//
size_t msg_size = sizeof(int);
char control[CMSG_SPACE (msg_size)];
//
// There is a msghdr that is used to minimize the number of parameters
// passed to recvmsg (which we will use to receive our ancillary data).
// This structure uses terminology corresponding to control messages, so
// you'll see msg_control, which is the pointer to the ancillary data and
// controller which is the size of the ancillary data array.
//
// So, initialize the message header that describes the ancillary/control
// data we expect to receive and point it to buffer.
//
struct msghdr msg;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = control;
msg.msg_controllen = sizeof (control);
msg.msg_flags = 0;
//
// Now we can actually receive the interesting bits from the socket
// creator process.
//
ssize_t bytesRead = recvmsg (sock, &msg, 0);
if (bytesRead != sizeof(int))
{
NS_FATAL_ERROR ("NetmapNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from socket creator");
}
//
// There may be a number of message headers/ancillary data arrays coming in.
// Let's look for the one with a type SCM_RIGHTS which indicates it' the
// one we're interested in.
//
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg))
{
if (cmsg->cmsg_level == SOL_SOCKET
&& cmsg->cmsg_type == SCM_RIGHTS)
{
//
// This is the type of message we want. Check to see if the magic
// number is correct and then pull out the socket we care about if
// it matches
//
if (magic == EMU_MAGIC)
{
NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
int *rawSocket = (int*)CMSG_DATA (cmsg);
NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
return *rawSocket;
}
else
{
NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);
}
}
}
NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
}
}
void
NetmapNetDeviceHelper::SwitchInNetmapMode (int fd, Ptr<NetmapNetDevice> device) const
{
NS_LOG_FUNCTION (this << fd << device);
NS_ASSERT (device);
if (m_deviceName == "undefined")
{
NS_FATAL_ERROR ("NetmapNetDevice: m_deviceName is not set");
}
if (fd == -1)
{
NS_FATAL_ERROR ("NetmapNetDevice: fd is not set");
}
struct nmreq nmr;
memset (&nmr, 0, sizeof (nmr));
nmr.nr_version = NETMAP_API;
// setting the interface name in the netmap request
strncpy (nmr.nr_name, m_deviceName.c_str (), m_deviceName.length ());
// switch the interface in netmap mode
int code = ioctl (fd, NIOCREGIF, &nmr);
if (code == -1)
{
NS_FATAL_ERROR ("Switching failed");
}
// memory mapping
uint8_t *memory = (uint8_t *) mmap (0, nmr.nr_memsize, PROT_WRITE | PROT_READ,
MAP_SHARED, fd, 0);
if (memory == MAP_FAILED)
{
NS_FATAL_ERROR ("Memory mapping failed");
}
// getting the base struct of the interface in netmap mode
struct netmap_if *nifp = NETMAP_IF (memory, nmr.nr_offset);
if (!nifp)
{
NS_FATAL_ERROR ("Failed getting the base struct of the interface in netmap mode");
}
device->SetNetmapInterfaceRepresentation (nifp);
device->SetTxRingsInfo (nifp->ni_tx_rings, nmr.nr_tx_slots);
device->SetRxRingsInfo (nifp->ni_rx_rings, nmr.nr_rx_slots);
device->SetFileDescriptor (fd);
}
} // namespace ns3

View File

@@ -0,0 +1,100 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
#ifndef NETMAP_NET_DEVICE_HELPER_H
#define NETMAP_NET_DEVICE_HELPER_H
#include <string>
#include "ns3/attribute.h"
#include "ns3/fd-net-device.h"
#include "ns3/fd-net-device-helper.h"
#include "ns3/object-factory.h"
#include "ns3/net-device-container.h"
#include "ns3/node-container.h"
#include "ns3/netmap-net-device.h"
namespace ns3 {
class NetmapNetDevice;
/**
* \ingroup fd-net-device
* \brief build a set of FdNetDevice objects attached to a physical network
* interface
*
*/
class NetmapNetDeviceHelper : public FdNetDeviceHelper
{
public:
NetmapNetDeviceHelper ();
virtual ~NetmapNetDeviceHelper ()
{
}
/**
* \brief Get the device name of this device.
* \returns The device name of this device.
*/
std::string GetDeviceName (void);
/**
* \brief Set the device name of this device.
* \param deviceName The device name of this device.
*/
void SetDeviceName (std::string deviceName);
protected:
/**
* \brief This method creates an ns3::FdNetDevice attached to a physical network
* interface
* \param node The node to install the device in
* \returns A container holding the added net device.
*/
Ptr<NetDevice> InstallPriv (Ptr<Node> node) const;
/**
* \brief Sets device flags and MTU.
* \param device the FdNetDevice
*/
virtual void SetDeviceAttributes (Ptr<FdNetDevice> device) const;
/**
* \brief Call out to a separate process running as suid root in order to get a raw
* socket. We do this to avoid having the entire simulation running as root.
* \return the rawSocket number
*/
virtual int CreateFileDescriptor (void) const;
/**
* \brief Switch the fd in netmap mode.
* \param fd the file descriptor
* \param device the NetmapNetDevice
*/
void SwitchInNetmapMode (int fd, Ptr<NetmapNetDevice> device) const;
std::string m_deviceName; //!< The unix/linux name of the underlying device (e.g., eth0)
};
} // namespace ns3
#endif /* NETMAP_NET_DEVICE_HELPER_H */

View File

@@ -247,14 +247,37 @@ FdNetDevice::StartDevice (void)
//
m_nodeId = GetNode ()->GetId ();
m_fdReader = Create<FdNetDeviceFdReader> ();
// 22 bytes covers 14 bytes Ethernet header with possible 8 bytes LLC/SNAP
m_fdReader->SetBufferSize (m_mtu + 22);
m_fdReader = DoCreateFdReader ();
m_fdReader->Start (m_fd, MakeCallback (&FdNetDevice::ReceiveCallback, this));
DoFinishStartingDevice ();
NotifyLinkUp ();
}
Ptr<FdReader>
FdNetDevice::DoCreateFdReader (void)
{
NS_LOG_FUNCTION (this);
Ptr<FdNetDeviceFdReader> fdReader = Create<FdNetDeviceFdReader> ();
// 22 bytes covers 14 bytes Ethernet header with possible 8 bytes LLC/SNAP
fdReader->SetBufferSize (m_mtu + 22);
return fdReader;
}
void
FdNetDevice::DoFinishStartingDevice (void)
{
NS_LOG_FUNCTION (this);
}
void
FdNetDevice::DoFinishStoppingDevice (void)
{
NS_LOG_FUNCTION (this);
}
void
FdNetDevice::StopDevice (void)
{
@@ -272,17 +295,7 @@ FdNetDevice::StopDevice (void)
m_fd = -1;
}
{
CriticalSection cs (m_pendingReadMutex);
while (!m_pendingQueue.empty ())
{
std::pair<uint8_t *, ssize_t> next = m_pendingQueue.front ();
m_pendingQueue.pop ();
FreeBuffer (next.first);
}
}
DoFinishStoppingDevice ();
}
void
@@ -605,7 +618,8 @@ FdNetDevice::SendFrom (Ptr<Packet> packet, const Address& src, const Address& de
AddPIHeader (buffer, len);
}
ssize_t written = Write(buffer, len);
ssize_t written = Write (buffer, len);
free (buffer);
if (written == -1 || (size_t) written != len)
{
@@ -621,9 +635,7 @@ FdNetDevice::Write (uint8_t *buffer, size_t length)
{
NS_LOG_FUNCTION (this << buffer << length);
uint32_t ret = write (m_fd, buffer, length);
FreeBuffer (buffer);
return ret;
return write (m_fd, buffer, length);
}
void
@@ -635,6 +647,12 @@ FdNetDevice::SetFileDescriptor (int fd)
}
}
int
FdNetDevice::GetFileDescriptor (void) const
{
return m_fd;
}
void
FdNetDevice::SetAddress (Address address)
{

View File

@@ -212,6 +212,11 @@ protected:
* Callback to invoke when a new frame is received
*/
void ReceiveCallback (uint8_t *buf, ssize_t len);
/**
* Get the associated file descriptor.
* \return the associated file descriptor
*/
int GetFileDescriptor (void) const;
private:
/**
@@ -232,6 +237,27 @@ private:
*/
void StopDevice (void);
/**
* Create the FdReader object
* \return the created FdReader object
*/
virtual Ptr<FdReader> DoCreateFdReader (void);
/**
* Complete additional actions, if any, to spin up down the device
*/
virtual void DoFinishStartingDevice (void);
/**
* Complete additional actions, if any, to tear down the device
*/
virtual void DoFinishStoppingDevice (void);
/**
* Callback to invoke when a new frame is received
*/
void ReceiveCallback (uint8_t *buf, ssize_t len);
/**
* Forward the frame to the appropriate callback for processing
*/
@@ -279,7 +305,7 @@ private:
/**
* Reader for the file descriptor.
*/
Ptr<FdNetDeviceFdReader> m_fdReader;
Ptr<FdReader> m_fdReader;
/**
* The net device mac address.

View File

@@ -0,0 +1,396 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
#include "netmap-net-device.h"
#include "ns3/system-thread.h"
#include <sys/ioctl.h>
#include <unistd.h>
namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("NetmapNetDevice");
TypeId
NetDeviceQueueLock::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::NetDeviceQueueLock")
.SetParent<NetDeviceQueue> ()
.SetGroupName ("Network")
.AddConstructor<NetDeviceQueueLock> ();
return tid;
}
NetDeviceQueueLock::NetDeviceQueueLock ()
{
}
NetDeviceQueueLock::~NetDeviceQueueLock ()
{
}
bool
NetDeviceQueueLock::IsStopped (void) const
{
m_mutex.lock ();
bool stopped = NetDeviceQueue::IsStopped ();
m_mutex.unlock ();
return stopped;
}
void
NetDeviceQueueLock::Start (void)
{
m_mutex.lock ();
NetDeviceQueue::Start ();
m_mutex.unlock ();
}
void
NetDeviceQueueLock::Stop (void)
{
m_mutex.lock ();
NetDeviceQueue::Stop ();
m_mutex.unlock ();
}
void
NetDeviceQueueLock::Wake (void)
{
m_mutex.lock ();
NetDeviceQueue::Wake ();
m_mutex.unlock ();
}
void
NetDeviceQueueLock::NotifyQueuedBytes (uint32_t bytes)
{
m_mutex.lock ();
NetDeviceQueue::NotifyQueuedBytes (bytes);
m_mutex.unlock ();
}
void
NetDeviceQueueLock::NotifyTransmittedBytes (uint32_t bytes)
{
m_mutex.lock ();
NetDeviceQueue::NotifyTransmittedBytes (bytes);
m_mutex.unlock ();
}
NetmapNetDeviceFdReader::NetmapNetDeviceFdReader ()
: m_bufferSize (65536), // Defaults to maximum TCP window size
m_nifp (nullptr)
{
}
void
NetmapNetDeviceFdReader::SetBufferSize (uint32_t bufferSize)
{
NS_LOG_FUNCTION (this << bufferSize);
m_bufferSize = bufferSize;
}
void
NetmapNetDeviceFdReader::SetNetmapIfp (struct netmap_if *nifp)
{
NS_LOG_FUNCTION (this << nifp);
m_nifp = nifp;
}
FdReader::Data
NetmapNetDeviceFdReader::DoRead (void)
{
NS_LOG_FUNCTION (this);
uint8_t *buf = (uint8_t *) malloc (m_bufferSize);
NS_ABORT_MSG_IF (buf == 0, "malloc() failed");
NS_LOG_LOGIC ("Calling read on fd " << m_fd);
struct netmap_ring *rxring;
uint16_t len = 0;
uint32_t rxRingIndex = 0;
// we have a packet in one of the receiver rings
// we check for the first non empty receiver ring
while (rxRingIndex < m_nifp->ni_rx_rings)
{
rxring = NETMAP_RXRING (m_nifp, rxRingIndex);
if (!nm_ring_empty (rxring))
{
uint32_t i = rxring->cur;
uint8_t *buffer = (uint8_t *) NETMAP_BUF (rxring, rxring->slot[i].buf_idx);
len = rxring->slot[i].len;
NS_LOG_DEBUG ("Received a packet of " << len << " bytes");
// copy buffer in the destination memory area
memcpy (buf, buffer, len);
// advance the netmap pointers and sync the fd
rxring->head = rxring->cur = nm_ring_next (rxring, i);
ioctl (m_fd, NIOCRXSYNC, NULL);
break;
}
rxRingIndex++;
}
if (len <= 0)
{
free (buf);
buf = 0;
len = 0;
}
NS_LOG_LOGIC ("Read " << len << " bytes on fd " << m_fd);
return FdReader::Data (buf, len);
}
NS_OBJECT_ENSURE_REGISTERED (NetmapNetDevice);
TypeId
NetmapNetDevice::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::NetmapNetDevice")
.SetParent<FdNetDevice> ()
.SetGroupName ("FdNetDevice")
.AddConstructor<NetmapNetDevice> ();
return tid;
}
NetmapNetDevice::NetmapNetDevice ()
{
NS_LOG_FUNCTION (this);
m_nifp = nullptr;
m_nTxRings = 0;
m_nTxRingsSlots = 0;
m_nRxRings = 0;
m_nRxRingsSlots = 0;
m_queue = nullptr;
m_totalQueuedBytes = 0;
m_syncAndNotifyQueueThread = nullptr;
m_syncAndNotifyQueueThreadRun = false;
}
NetmapNetDevice::~NetmapNetDevice ()
{
NS_LOG_FUNCTION (this);
m_nifp = nullptr;
m_queue = nullptr;
}
Ptr<FdReader>
NetmapNetDevice::DoCreateFdReader (void)
{
NS_LOG_FUNCTION (this);
Ptr<NetmapNetDeviceFdReader> fdReader = Create<NetmapNetDeviceFdReader> ();
// 22 bytes covers 14 bytes Ethernet header with possible 8 bytes LLC/SNAP
fdReader->SetBufferSize (GetMtu () + 22);
fdReader->SetNetmapIfp (m_nifp);
return fdReader;
}
void
NetmapNetDevice::DoFinishStartingDevice (void)
{
NS_LOG_FUNCTION (this);
m_syncAndNotifyQueueThreadRun = true;
m_syncAndNotifyQueueThread = Create<SystemThread> (MakeCallback (&NetmapNetDevice::SyncAndNotifyQueue, this));
m_syncAndNotifyQueueThread->Start ();
}
void
NetmapNetDevice::DoFinishStoppingDevice (void)
{
NS_LOG_FUNCTION (this);
m_queue->Stop ();
m_syncAndNotifyQueueThreadRun = false;
m_syncAndNotifyQueueThread->Join ();
m_syncAndNotifyQueueThread = nullptr;
}
uint32_t
NetmapNetDevice::GetBytesInNetmapTxRing ()
{
NS_LOG_FUNCTION (this);
struct netmap_ring *txring;
txring = NETMAP_TXRING (m_nifp, 0);
int tail = txring->tail;
// the netmap ring has one slot reserved
int inQueue = (m_nTxRingsSlots - 1) - nm_ring_space (txring);
uint32_t bytesInQueue = 0;
for (int i = 1; i < inQueue; i++)
{
bytesInQueue += txring->slot[tail].len;
tail++;
tail = tail % m_nTxRingsSlots;
}
return bytesInQueue;
}
void
NetmapNetDevice::SetNetDeviceQueue (Ptr<NetDeviceQueue> queue)
{
NS_LOG_FUNCTION (this);
m_queue = queue;
}
void
NetmapNetDevice::SetNetmapInterfaceRepresentation (struct netmap_if *nifp)
{
NS_LOG_FUNCTION (this << nifp);
m_nifp = nifp;
}
void
NetmapNetDevice::SetTxRingsInfo (uint32_t nTxRings, uint32_t nTxRingsSlots)
{
NS_LOG_FUNCTION (this << nTxRings << nTxRingsSlots);
m_nTxRings = nTxRings;
m_nTxRingsSlots = nTxRingsSlots;
}
void
NetmapNetDevice::SetRxRingsInfo (uint32_t nRxRings, uint32_t nRxRingsSlots)
{
NS_LOG_FUNCTION (this << nRxRings << nRxRingsSlots);
m_nRxRings = nRxRings;
m_nRxRingsSlots = nRxRingsSlots;
}
int
NetmapNetDevice::GetSpaceInNetmapTxRing () const
{
NS_LOG_FUNCTION (this);
struct netmap_ring *txring;
txring = NETMAP_TXRING (m_nifp, 0);
return nm_ring_space (txring);
}
// This function runs in a separate thread.
void
NetmapNetDevice::SyncAndNotifyQueue ()
{
NS_LOG_FUNCTION (this);
struct netmap_ring *txring = NETMAP_TXRING (m_nifp, 0);
uint32_t prevTotalTransmittedBytes = 0;
while (m_syncAndNotifyQueueThreadRun == true)
{
// we sync the netmap ring periodically.
// the traffic control layer can write packets during the period between two syncs.
ioctl (GetFileDescriptor (), NIOCTXSYNC, NULL);
// we need of a nearly periodic notification to queue limits of the transmitted bytes.
uint32_t totalTransmittedBytes = m_totalQueuedBytes - GetBytesInNetmapTxRing ();
uint32_t deltaBytes = totalTransmittedBytes - prevTotalTransmittedBytes;
NS_LOG_DEBUG (deltaBytes << " delta transmitted bytes");
prevTotalTransmittedBytes = totalTransmittedBytes;
if (m_queue)
{
m_queue->NotifyTransmittedBytes (deltaBytes);
if (GetSpaceInNetmapTxRing () >= 32) // WAKE_THRESHOLD
{
if (m_queue->IsStopped ())
{
m_queue->Wake ();
}
}
}
// we use a period to sync, check and notify of 200 us; it is a value close to the interrupt coalescence
// period of a real device
usleep (200);
NS_LOG_DEBUG ("Space in the netmap ring of " << nm_ring_space (txring) << " packets");
}
ioctl (GetFileDescriptor (), NIOCTXSYNC, NULL);
}
ssize_t
NetmapNetDevice::Write (uint8_t *buffer, size_t length)
{
NS_LOG_FUNCTION (this << buffer << length);
struct netmap_ring *txring;
// we use one ring also in case of multiqueue device to perform an accurate flow control on that ring
txring = NETMAP_TXRING (m_nifp, 0);
uint16_t ret = -1;
if (m_queue->IsStopped ())
{
// the device queue is stopped and we cannot write other packets
return ret;
}
if (!nm_ring_empty (txring))
{
uint32_t i = txring->cur;
uint8_t *buf = (uint8_t *) NETMAP_BUF (txring, txring->slot[i].buf_idx);
memcpy (buf, buffer, length);
txring->slot[i].len = length;
txring->head = txring->cur = nm_ring_next (txring, i);
ret = length;
// we update the total transmitted bytes counter and notify queue limits of the queued bytes
m_totalQueuedBytes += length;
m_queue->NotifyQueuedBytes (length);
// if there is no room for other packets then stop the queue.
if (nm_ring_space (txring) == 0)
{
m_queue->Stop ();
}
}
return ret;
}
} // namespace ns3

View File

@@ -0,0 +1,219 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
* Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation;
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author: Pasquale Imputato <p.imputato@gmail.com>
*/
#ifndef NETMAP_NET_DEVICE_H
#define NETMAP_NET_DEVICE_H
#include "ns3/net-device-queue-interface.h"
#include <mutex>
#include "fd-net-device.h"
#include <net/netmap_user.h>
#include <atomic>
namespace ns3 {
class SystemThread;
/**
* \ingroup fd-net-device
*
* \brief Network device transmission queue with lock
*
* This class stores information about a single transmission queue
* of a network device that is exposed to queue discs. This class extends
* the NetDeviceQueue base class by introducing a lock for methods which
* require mutual exclusion on data access in emulation.
*/
class NetDeviceQueueLock : public NetDeviceQueue
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
NetDeviceQueueLock ();
virtual ~NetDeviceQueueLock ();
/**
* Called by the device to start this device transmission queue.
* This is the analogous to the netif_tx_start_queue function of the Linux kernel.
*/
virtual void Start (void);
/**
* Called by the device to stop this device transmission queue.
* This is the analogous to the netif_tx_stop_queue function of the Linux kernel.
*/
virtual void Stop (void);
/**
* Called by the device to wake the queue disc associated with this
* device transmission queue. This is done by invoking the wake callback.
* This is the analogous to the netif_tx_wake_queue function of the Linux kernel.
*/
virtual void Wake (void);
/**
* \brief Get the status of the device transmission queue.
* \return true if the device transmission queue is stopped.
*
* Called by queue discs to enquire about the status of a given transmission queue.
* This is the analogous to the netif_xmit_stopped function of the Linux kernel.
*/
virtual bool IsStopped (void) const;
/**
* \brief Called by the netdevice to report the number of bytes queued to the device queue
* \param bytes number of bytes queued to the device queue
*/
virtual void NotifyQueuedBytes (uint32_t bytes);
/**
* \brief Called by the netdevice to report the number of bytes it is going to transmit
* \param bytes number of bytes the device is going to transmit
*/
virtual void NotifyTransmittedBytes (uint32_t bytes);
private:
mutable std::mutex m_mutex; //!< Mutex to serialize the operations performed on the queue
};
/**
* \ingroup fd-net-device
*
* \brief This class performs the actual data reading from the netmap ring.
*/
class NetmapNetDeviceFdReader : public FdReader
{
public:
NetmapNetDeviceFdReader ();
/**
* \brief Set size of the read buffer.
* \param bufferSize the size of the read buffer
*/
void SetBufferSize (uint32_t bufferSize);
/**
* \brief Set netmap interface representation.
* \param nifp the netmap interface representation
*/
void SetNetmapIfp (struct netmap_if *nifp);
private:
FdReader::Data DoRead (void);
uint32_t m_bufferSize; //!< size of the read buffer
struct netmap_if *m_nifp; //!< Netmap interface representation
};
/**
* \ingroup fd-net-device
*
* \brief a NetDevice to read/write network traffic from/into a netmap file descriptor.
*
* A NetmapNetDevice object will read and write packets from/to a netmap file descriptor.
*
*/
class NetmapNetDevice : public FdNetDevice
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
NetmapNetDevice ();
virtual ~NetmapNetDevice ();
/**
* \brief Get the number of bytes currently in the netmap transmission ring.
* \return the number of bytes in the netmap transmission ring.
*/
uint32_t GetBytesInNetmapTxRing ();
/**
* \brief Get the number of slots currently available in the netmap transmission ring.
* \return the number of slots currently available in the netmap transmission ring.
*/
int GetSpaceInNetmapTxRing () const;
/**
* \brief Set the NetDeviceQueue
* \param queue the NetDeviceQueue
*/
void SetNetDeviceQueue (Ptr<NetDeviceQueue> queue);
/**
* \brief Set the netmap interface representation
* \param nifp the pointer to netmap interface representation
*/
void SetNetmapInterfaceRepresentation (struct netmap_if *nifp);
/**
* \brief Set the netmap transmission rings info
* \param nTxRings the number of transmission rings
* \param nTxRingsSlots the number of slots for each transmission ring
*/
void SetTxRingsInfo (uint32_t nTxRings, uint32_t nTxRingsSlots);
/**
* \brief Set the netmap receiver rings info
* \param nTxRings the number of receiver rings
* \param nTxRingsSlots the number of slots for each receiver ring
*/
void SetRxRingsInfo (uint32_t nRxRings, uint32_t nRxRingsSlots);
/**
* \brief The function Writes a packet into the netmap transmission ring.
* \param buffer the pointer to packet
* \param lenght the packet lenght
* \return the number of written bytes
*/
virtual ssize_t Write (uint8_t *buffer, size_t length);
private:
Ptr<FdReader> DoCreateFdReader (void);
void DoFinishStartingDevice (void);
void DoFinishStoppingDevice (void);
/**
* \brief This function syncs netmap ring and notifies netdevice queue.
* This function runs in a separate thread.
*/
virtual void SyncAndNotifyQueue ();
struct netmap_if *m_nifp; //!< Netmap interface representation
uint32_t m_nTxRings; //!< Number of transmission rings
uint32_t m_nTxRingsSlots; //!< Number of slots in the transmission rings
uint32_t m_nRxRings; //!< Number of receiver rings
uint32_t m_nRxRingsSlots; //!< Number of slots in the receiver rings
Ptr<NetDeviceQueue> m_queue; //!< NetDevice queue
uint32_t m_totalQueuedBytes; //!< Total queued bytes
Ptr<SystemThread> m_syncAndNotifyQueueThread; //!< Thread used to perform the flow control
std::atomic<bool> m_syncAndNotifyQueueThreadRun; //!< Running flag of the flow control thread
};
} // namespace ns3
#endif /* NETMAP_NET_DEVICE_H */

View File

@@ -93,7 +93,7 @@ def configure(conf):
conf.env['MODULES_NOT_BUILT'].append('fd-net-device')
# Next, check for whether specialized FdNetDevice features are enabled
# such as tap device support, raw socket support, and planetlab
# such as tap device support, raw socket support, netmap support, and planetlab
if conf.env['ENABLE_FDNETDEV']:
conf.env['ENABLE_TAP'] = conf.check_nonfatal(
@@ -127,6 +127,22 @@ def configure(conf):
False,
"needs netpacket/packet.h")
# Enable use of netmap EMU support
conf.env['ENABLE_NETMAP_EMU'] = conf.check_nonfatal(
header_name='net/netmap_user.h',
define_name='HAVE_NETMAP_USER_H') and have_sysioctl and have_netif
if conf.env['ENABLE_NETMAP_EMU']:
conf.report_optional_feature("NetmapFdNetDevice",
"Netmap emulation FdNetDevice",
True,
"Netmap emulation support enabled")
else:
conf.report_optional_feature("NetmapFdNetDevice",
"Netmap emulation FdNetDevice",
False,
"needs net/netmap_user.h")
# Enable use of PlanetLab TAP helper
# TODO: How to validate
(sysname, nodename, release, version, machine) = os.uname()
@@ -224,6 +240,27 @@ def build(bld):
module.env.append_value("DEFINES",
"RAW_SOCK_CREATOR=\"%s\"" % (creator.target,))
if bld.env['ENABLE_NETMAP_EMU']:
module.source.extend([
'model/netmap-net-device.cc',
'helper/netmap-net-device-helper.cc',
])
headers.source.extend([
'model/netmap-net-device.h',
'helper/netmap-net-device-helper.h',
])
creator = bld.create_suid_program('netmap-device-creator')
creator.source = [
'helper/netmap-device-creator.cc',
'helper/encode-decode.cc',
'helper/creator-utils.cc',
]
module.env.append_value("DEFINES",
"NETMAP_DEV_CREATOR=\"%s\"" % (creator.target,))
if bld.env['ENABLE_PLANETLAB']:
module.source.extend([
'helper/planetlab-fd-net-device-helper.cc',

View File

@@ -29,6 +29,17 @@ namespace ns3 {
NS_LOG_COMPONENT_DEFINE ("NetDeviceQueueInterface");
TypeId
NetDeviceQueue::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::NetDeviceQueue")
.SetParent<Object> ()
.SetGroupName("Network")
.AddConstructor<NetDeviceQueue> ()
;
return tid;
}
NetDeviceQueue::NetDeviceQueue ()
: m_stoppedByDevice (false),
m_stoppedByQueueLimits (false),
@@ -170,6 +181,12 @@ NetDeviceQueueInterface::GetTypeId (void)
.SetParent<Object> ()
.SetGroupName("Network")
.AddConstructor<NetDeviceQueueInterface> ()
.AddAttribute ("TxQueuesType",
"The type of transmission queues to be used",
TypeId::ATTR_CONSTRUCT,
TypeIdValue (NetDeviceQueue::GetTypeId ()),
MakeTypeIdAccessor (&NetDeviceQueueInterface::SetTxQueuesType),
MakeTypeIdChecker ())
.AddAttribute ("NTxQueues", "The number of device transmission queues",
TypeId::ATTR_GET | TypeId::ATTR_CONSTRUCT,
UintegerValue (1),
@@ -228,6 +245,17 @@ NetDeviceQueueInterface::NotifyNewAggregate (void)
Object::NotifyNewAggregate ();
}
void
NetDeviceQueueInterface::SetTxQueuesType (TypeId type)
{
NS_LOG_FUNCTION (this << type);
NS_ABORT_MSG_IF (!m_txQueuesVector.empty (), "Cannot call SetTxQueuesType after creating device queues");
m_txQueues = ObjectFactory ();
m_txQueues.SetTypeId (type);
}
void
NetDeviceQueueInterface::SetNTxQueues (std::size_t numTxQueues)
{
@@ -239,7 +267,7 @@ NetDeviceQueueInterface::SetNTxQueues (std::size_t numTxQueues)
// create the netdevice queues
for (std::size_t i = 0; i < numTxQueues; i++)
{
m_txQueuesVector.push_back (Create<NetDeviceQueue> ());
m_txQueuesVector.push_back (m_txQueues.Create ()->GetObject<NetDeviceQueue> ());
}
}

View File

@@ -27,6 +27,7 @@
#include "ns3/ptr.h"
#include "ns3/log.h"
#include "ns3/net-device.h"
#include "ns3/object-factory.h"
namespace ns3 {
@@ -52,9 +53,15 @@ class QueueItem;
*
* This class roughly models the struct netdev_queue of Linux.
*/
class NetDeviceQueue : public SimpleRefCount<NetDeviceQueue>
class NetDeviceQueue : public Object
{
public:
/**
* \brief Get the type ID.
* \return the object TypeId
*/
static TypeId GetTypeId (void);
NetDeviceQueue ();
virtual ~NetDeviceQueue();
@@ -84,7 +91,7 @@ public:
* Called by queue discs to enquire about the status of a given transmission queue.
* This is the analogous to the netif_xmit_stopped function of the Linux kernel.
*/
bool IsStopped (void) const;
virtual bool IsStopped (void) const;
/**
* \brief Notify this NetDeviceQueue that the NetDeviceQueueInterface was
@@ -116,13 +123,13 @@ public:
* \brief Called by the netdevice to report the number of bytes queued to the device queue
* \param bytes number of bytes queued to the device queue
*/
void NotifyQueuedBytes (uint32_t bytes);
virtual void NotifyQueuedBytes (uint32_t bytes);
/**
* \brief Called by the netdevice to report the number of bytes it is going to transmit
* \param bytes number of bytes the device is going to transmit
*/
void NotifyTransmittedBytes (uint32_t bytes);
virtual void NotifyTransmittedBytes (uint32_t bytes);
/**
* \brief Reset queue limits state
@@ -251,6 +258,16 @@ public:
*/
std::size_t GetNTxQueues (void) const;
/**
* \brief Set the type of device transmission queues to create.
* \param type type of device transmission queues to create.
*
* This method is called when the TxQueuesType attribute is set to create
* the corresponding type of device transmission queues. It cannot be
* called again afterwards.
*/
void SetTxQueuesType (TypeId type);
/**
* \brief Set the number of device transmission queues to create.
* \param numTxQueues number of device transmission queues to create.
@@ -293,6 +310,7 @@ protected:
virtual void NotifyNewAggregate (void);
private:
ObjectFactory m_txQueues; //!< Device transmission queues TypeId
std::vector< Ptr<NetDeviceQueue> > m_txQueuesVector; //!< Device transmission queues
SelectQueueCallback m_selectQueueCallback; //!< Select queue callback
};