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