A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
netmap-net-device-helper.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2017 Universita' degli Studi di Napoli Federico II
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 *
17 * Author: Pasquale Imputato <p.imputato@gmail.com>
18 */
19
21
22#include "encode-decode.h"
23
24#include "ns3/abort.h"
25#include "ns3/config.h"
26#include "ns3/fd-net-device.h"
27#include "ns3/log.h"
28#include "ns3/names.h"
29#include "ns3/netmap-net-device.h"
30#include "ns3/object-factory.h"
31#include "ns3/packet.h"
32#include "ns3/simulator.h"
33#include "ns3/trace-helper.h"
34#include "ns3/uinteger.h"
35
36#include <arpa/inet.h>
37#include <errno.h>
38#include <iomanip>
39#include <iostream>
40#include <limits>
41#include <memory>
42#include <net/ethernet.h>
43#include <net/if.h>
44#include <net/netmap_user.h>
45#include <netinet/in.h>
46#include <stdlib.h>
47#include <string.h>
48#include <string>
49#include <sys/ioctl.h>
50#include <sys/mman.h>
51#include <sys/socket.h>
52#include <sys/stat.h>
53#include <sys/un.h>
54#include <sys/wait.h>
55#include <time.h>
56#include <unistd.h>
57
58namespace ns3
59{
60
61NS_LOG_COMPONENT_DEFINE("NetmapNetDeviceHelper");
62
63#define EMU_MAGIC 65867
64
66{
67 m_deviceName = "undefined";
68 SetTypeId("ns3::NetmapNetDevice");
69}
70
71std::string
73{
74 return m_deviceName;
75}
76
77void
79{
80 m_deviceName = deviceName;
81}
82
85{
87 Ptr<FdNetDevice> device = d->GetObject<FdNetDevice>();
88
89 SetDeviceAttributes(device);
90
91 int fd = CreateFileDescriptor();
92 Ptr<NetmapNetDevice> netmapDevice = DynamicCast<NetmapNetDevice>(device);
93 SwitchInNetmapMode(fd, netmapDevice);
94
95 // Aggregate NetDeviceQueueInterface object
96 Ptr<NetDeviceQueueInterface> ndqi = CreateObjectWithAttributes<NetDeviceQueueInterface>(
97 "TxQueuesType",
99 "NTxQueues",
100 UintegerValue(1));
101
102 device->AggregateObject(ndqi);
103 netmapDevice->SetNetDeviceQueue(ndqi->GetTxQueue(0));
104
105 return device;
106}
107
108void
110{
111 if (m_deviceName == "undefined")
112 {
113 NS_FATAL_ERROR("NetmapNetDeviceHelper::SetFileDescriptor (): m_deviceName is not set");
114 }
115
116 //
117 // Call out to a separate process running as suid root in order to get a raw
118 // socket. We do this to avoid having the entire simulation running as root.
119 //
120 int fd = socket(PF_INET, SOCK_DGRAM, 0);
121
122 //
123 // Figure out which interface index corresponds to the device name in the corresponding
124 // attribute.
125 //
126 struct ifreq ifr;
127 bzero(&ifr, sizeof(ifr));
128 strncpy((char*)ifr.ifr_name, m_deviceName.c_str(), IFNAMSIZ - 1);
129
130 NS_LOG_LOGIC("Getting interface index");
131 int32_t rc = ioctl(fd, SIOCGIFINDEX, &ifr);
132 if (rc == -1)
133 {
134 NS_FATAL_ERROR("NetmapNetDeviceHelper::SetFileDescriptor (): Can't get interface index");
135 }
136
137 rc = ioctl(fd, SIOCGIFFLAGS, &ifr);
138 if (rc == -1)
139 {
140 NS_FATAL_ERROR("NetmapNetDeviceHelper::SetFileDescriptor (): Can't get interface flags");
141 }
142
143 //
144 // This device only works if the underlying interface is up in promiscuous
145 // mode. We could have turned it on in the socket creator, but the situation
146 // is that we expect these devices to be used in conjunction with virtual
147 // machines with connected host-only (simulated) networks, or in a testbed.
148 // There is a lot of setup and configuration happening outside of this one
149 // issue, and we expect that configuration to include choosing a valid
150 // interface (e.g, "ath1"), ensuring that the device supports promiscuous
151 // mode, and placing it in promiscuous mode. We just make sure of the
152 // end result.
153 //
154 if ((ifr.ifr_flags & IFF_PROMISC) == 0)
155 {
156 NS_FATAL_ERROR("NetmapNetDeviceHelper::SetFileDescriptor (): "
157 << m_deviceName
158 << " is not in promiscuous mode. Please config the interface in promiscuous "
159 "mode before to run the simulation.");
160 }
161
162 if ((ifr.ifr_flags & IFF_BROADCAST) != IFF_BROADCAST)
163 {
164 // We default m_isBroadcast to true but turn it off here if not
165 // supported, because in the common case, overlying IP code will
166 // assert during configuration time if this is false, before this
167 // method has a chance to set it during runtime
168 device->SetIsBroadcast(false);
169 }
170
171 if ((ifr.ifr_flags & IFF_MULTICAST) == IFF_MULTICAST)
172 {
173 // This one is OK to enable at runtime
174 device->SetIsMulticast(true);
175 }
176
177 // Set the MTU of the device to the mtu of the associated network interface
178 // struct ifreq ifr2;
179 //
180 // bzero (&ifr2, sizeof (ifr2));
181 // strcpy (ifr2.ifr_name, m_deviceName.c_str ());
182
183 // int32_t mtufd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP);
184
185 rc = ioctl(fd, SIOCGIFMTU, &ifr);
186 if (rc == -1)
187 {
188 NS_FATAL_ERROR("FdNetDevice::SetFileDescriptor (): Can't ioctl SIOCGIFMTU");
189 }
190
191 NS_LOG_DEBUG("Device MTU " << ifr.ifr_mtu);
192 device->SetMtu(ifr.ifr_mtu);
193
194 close(fd);
195}
196
197int
199{
200 NS_LOG_FUNCTION(this);
201
202 //
203 // We want to create a raw socket for our net device. Unfortunately for us
204 // you have to have root privileges to do that. Instead of running the
205 // entire simulation as root, we decided to make a small program who's whole
206 // reason for being is to run as suid root and create a raw socket. We're
207 // going to fork and exec that program soon, but we need to have a socket
208 // to talk to it with. So we create a local interprocess (Unix) socket
209 // for that purpose.
210 //
211 int sock = socket(PF_UNIX, SOCK_DGRAM, 0);
212 if (sock == -1)
213 {
215 "NetmapNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = "
216 << strerror(errno));
217 }
218
219 //
220 // Bind to that socket and let the kernel allocate an endpoint
221 //
222 struct sockaddr_un un;
223 memset(&un, 0, sizeof(un));
224 un.sun_family = AF_UNIX;
225 int status = bind(sock, (struct sockaddr*)&un, sizeof(sa_family_t));
226 if (status == -1)
227 {
228 NS_FATAL_ERROR("NetmapNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = "
229 << strerror(errno));
230 }
231
232 NS_LOG_INFO("Created Unix socket");
233 NS_LOG_INFO("sun_family = " << un.sun_family);
234 NS_LOG_INFO("sun_path = " << un.sun_path);
235
236 //
237 // We have a socket here, but we want to get it there -- to the program we're
238 // going to exec. What we'll do is to do a getsockname and then encode the
239 // resulting address information as a string, and then send the string to the
240 // program as an argument. So we need to get the sock name.
241 //
242 socklen_t len = sizeof(un);
243 status = getsockname(sock, (struct sockaddr*)&un, &len);
244 if (status == -1)
245 {
247 "NetmapNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = "
248 << strerror(errno));
249 }
250
251 //
252 // Now encode that socket name (family and path) as a string of hex digits
253 //
254 std::string path = BufferToString((uint8_t*)&un, len);
255 NS_LOG_INFO("Encoded Unix socket as \"" << path << "\"");
256 //
257 // Fork and exec the process to create our socket. If we're us (the parent)
258 // we wait for the child (the socket creator) to complete and read the
259 // socket it created using the ancillary data mechanism.
260 //
261 // Tom Goff reports the possibility of a deadlock when trying to acquire the
262 // python GIL here. He says that this might be due to trying to access Python
263 // objects after fork() without calling PyOS_AfterFork() to properly reset
264 // Python state (including the GIL). There is no code to cause the problem
265 // here in emu, but this was visible in similar code in tap-bridge.
266 //
267 pid_t pid = ::fork();
268 if (pid == 0)
269 {
270 NS_LOG_DEBUG("Child process");
271
272 //
273 // build a command line argument from the encoded endpoint string that
274 // the socket creation process will use to figure out how to respond to
275 // the (now) parent process.
276 //
277 std::ostringstream oss;
278
279 oss << "-p" << path;
280
281 NS_LOG_INFO("Parameters set to \"" << oss.str() << "\"");
282
283 //
284 // Execute the socket creation process image.
285 //
286 status = ::execlp(NETMAP_DEV_CREATOR,
287 NETMAP_DEV_CREATOR, // argv[0] (filename)
288 oss.str().c_str(), // argv[1] (-p<path?
289 nullptr);
290
291 //
292 // If the execlp successfully completes, it never returns. If it returns it failed or the
293 // OS is broken. In either case, we bail.
294 //
296 "NetmapNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), status = "
297 << status << ", errno = " << ::strerror(errno));
298 }
299 else
300 {
301 NS_LOG_DEBUG("Parent process");
302 //
303 // We're the process running the emu net device. We need to wait for the
304 // socket creator process to finish its job.
305 //
306 int st;
307 pid_t waited = waitpid(pid, &st, 0);
308 if (waited == -1)
309 {
311 "NetmapNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = "
312 << strerror(errno));
313 }
314 NS_ASSERT_MSG(pid == waited, "NetmapNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
315
316 //
317 // Check to see if the socket creator exited normally and then take a
318 // look at the exit code. If it bailed, so should we. If it didn't
319 // even exit normally, we bail too.
320 //
321 if (WIFEXITED(st))
322 {
323 int exitStatus = WEXITSTATUS(st);
324 if (exitStatus != 0)
325 {
326 NS_FATAL_ERROR("NetmapNetDeviceHelper::CreateFileDescriptor(): socket creator "
327 "exited normally with status "
328 << exitStatus);
329 }
330 }
331 else
332 {
334 "NetmapNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
335 }
336
337 //
338 // At this point, the socket creator has run successfully and should
339 // have created our raw socket and sent it back to the socket address
340 // we provided. Our socket should be waiting on the Unix socket. We've
341 // got to do a bunch of grunto work to get at it, though.
342 //
343 // The struct iovec below is part of a scatter-gather list. It describes a
344 // buffer. In this case, it describes a buffer (an integer) that will
345 // get the data that comes back from the socket creator process. It will
346 // be a magic number that we use as a consistency/sanity check.
347 //
348 struct iovec iov;
349 uint32_t magic;
350 iov.iov_base = &magic;
351 iov.iov_len = sizeof(magic);
352
353 //
354 // The CMSG macros you'll see below are used to create and access control
355 // messages (which is another name for ancillary data). The ancillary
356 // data is made up of pairs of struct cmsghdr structures and associated
357 // data arrays.
358 //
359 // First, we're going to allocate a buffer on the stack to receive our
360 // data array (that contains the socket). Sometimes you'll see this called
361 // an "ancillary element" but the msghdr uses the control message terminology
362 // so we call it "control."
363 //
364 size_t msg_size = sizeof(int);
365 char control[CMSG_SPACE(msg_size)];
366
367 //
368 // There is a msghdr that is used to minimize the number of parameters
369 // passed to recvmsg (which we will use to receive our ancillary data).
370 // This structure uses terminology corresponding to control messages, so
371 // you'll see msg_control, which is the pointer to the ancillary data and
372 // controller which is the size of the ancillary data array.
373 //
374 // So, initialize the message header that describes the ancillary/control
375 // data we expect to receive and point it to buffer.
376 //
377 struct msghdr msg;
378 msg.msg_name = nullptr;
379 msg.msg_namelen = 0;
380 msg.msg_iov = &iov;
381 msg.msg_iovlen = 1;
382 msg.msg_control = control;
383 msg.msg_controllen = sizeof(control);
384 msg.msg_flags = 0;
385
386 //
387 // Now we can actually receive the interesting bits from the socket
388 // creator process.
389 //
390 ssize_t bytesRead = recvmsg(sock, &msg, 0);
391 if (bytesRead != sizeof(int))
392 {
393 NS_FATAL_ERROR("NetmapNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from "
394 "socket creator");
395 }
396
397 //
398 // There may be a number of message headers/ancillary data arrays coming in.
399 // Let's look for the one with a type SCM_RIGHTS which indicates it' the
400 // one we're interested in.
401 //
402 struct cmsghdr* cmsg;
403 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != nullptr; cmsg = CMSG_NXTHDR(&msg, cmsg))
404 {
405 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
406 {
407 //
408 // This is the type of message we want. Check to see if the magic
409 // number is correct and then pull out the socket we care about if
410 // it matches
411 //
412 if (magic == EMU_MAGIC)
413 {
414 NS_LOG_INFO("Got SCM_RIGHTS with correct magic " << magic);
415 int* rawSocket = (int*)CMSG_DATA(cmsg);
416 NS_LOG_INFO("Got the socket from the socket creator = " << *rawSocket);
417 return *rawSocket;
418 }
419 else
420 {
421 NS_LOG_INFO("Got SCM_RIGHTS, but with bad magic " << magic);
422 }
423 }
424 }
425 NS_FATAL_ERROR("Did not get the raw socket from the socket creator");
426 }
427}
428
429void
431{
432 NS_LOG_FUNCTION(this << fd << device);
433 NS_ASSERT(device);
434
435 if (m_deviceName == "undefined")
436 {
437 NS_FATAL_ERROR("NetmapNetDevice: m_deviceName is not set");
438 }
439
440 if (fd == -1)
441 {
442 NS_FATAL_ERROR("NetmapNetDevice: fd is not set");
443 }
444
445 struct nmreq nmr;
446 memset(&nmr, 0, sizeof(nmr));
447
448 nmr.nr_version = NETMAP_API;
449
450 // setting the interface name in the netmap request
451 strncpy(nmr.nr_name, m_deviceName.c_str(), m_deviceName.length());
452
453 // switch the interface in netmap mode
454 int code = ioctl(fd, NIOCREGIF, &nmr);
455 if (code == -1)
456 {
457 NS_FATAL_ERROR("Switching failed");
458 }
459
460 // memory mapping
461 uint8_t* memory = (uint8_t*)mmap(0, nmr.nr_memsize, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);
462
463 if (memory == MAP_FAILED)
464 {
465 NS_FATAL_ERROR("Memory mapping failed");
466 }
467
468 // getting the base struct of the interface in netmap mode
469 struct netmap_if* nifp = NETMAP_IF(memory, nmr.nr_offset);
470
471 if (!nifp)
472 {
473 NS_FATAL_ERROR("Failed getting the base struct of the interface in netmap mode");
474 }
475
476 device->SetNetmapInterfaceRepresentation(nifp);
477 device->SetTxRingsInfo(nifp->ni_tx_rings, nmr.nr_tx_slots);
478 device->SetRxRingsInfo(nifp->ni_rx_rings, nmr.nr_rx_slots);
479
480 device->SetFileDescriptor(fd);
481}
482
483} // namespace ns3
virtual Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice and associates it to a node.
void SetTypeId(std::string type)
Set the TypeId of the Objects to be created by this helper.
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:84
static TypeId GetTypeId()
Get the type ID.
Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice attached to a physical network interface.
virtual int CreateFileDescriptor() const
Call out to a separate process running as suid root in order to get a raw socket.
void SetDeviceName(std::string deviceName)
Set the device name of this device.
std::string m_deviceName
The unix/linux name of the underlying device (e.g., eth0)
void SwitchInNetmapMode(int fd, Ptr< NetmapNetDevice > device) const
Switch the fd in netmap mode.
std::string GetDeviceName()
Get the device name of this device.
virtual void SetDeviceAttributes(Ptr< FdNetDevice > device) const
Sets device flags and MTU.
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:77
AttributeValue implementation for TypeId.
Definition: type-id.h:598
Hold an unsigned integer type.
Definition: uinteger.h:45
#define EMU_MAGIC
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:66
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition: assert.h:86
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:179
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:202
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:268
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:282
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:275
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::string BufferToString(uint8_t *buffer, uint32_t len)
Convert a byte buffer to a string containing a hex representation of the buffer.
ns3::StringValue attribute value declarations.