fd-net-device: add support for emulation in netmap mode
This commit is contained in:
committed by
Tom Henderson
parent
3766889b74
commit
ff8083ca2e
@@ -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 \
|
||||
|
||||
79
src/fd-net-device/doc/netmap-net-device.rst
Normal file
79
src/fd-net-device/doc/netmap-net-device.rst
Normal 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)
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
230
src/fd-net-device/examples/fd-emu-send.cc
Normal file
230
src/fd-net-device/examples/fd-emu-send.cc
Normal 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.");
|
||||
}
|
||||
313
src/fd-net-device/examples/fd-emu-tc.cc
Normal file
313
src/fd-net-device/examples/fd-emu-tc.cc
Normal 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.");
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
95
src/fd-net-device/helper/netmap-device-creator.cc
Normal file
95
src/fd-net-device/helper/netmap-device-creator.cc
Normal 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;
|
||||
}
|
||||
|
||||
477
src/fd-net-device/helper/netmap-net-device-helper.cc
Normal file
477
src/fd-net-device/helper/netmap-net-device-helper.cc
Normal 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
|
||||
100
src/fd-net-device/helper/netmap-net-device-helper.h
Normal file
100
src/fd-net-device/helper/netmap-net-device-helper.h
Normal 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 */
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
396
src/fd-net-device/model/netmap-net-device.cc
Normal file
396
src/fd-net-device/model/netmap-net-device.cc
Normal 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
|
||||
219
src/fd-net-device/model/netmap-net-device.h
Normal file
219
src/fd-net-device/model/netmap-net-device.h
Normal 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 */
|
||||
@@ -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',
|
||||
|
||||
@@ -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> ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user