A Discrete-Event Network Simulator
QKDNetSim v2.0 (NS-3 v3.41) @ (+)
API
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 
58 namespace ns3
59 {
60 
61 NS_LOG_COMPONENT_DEFINE("NetmapNetDeviceHelper");
62 
63 #define EMU_MAGIC 65867
64 
66 {
67  m_deviceName = "undefined";
68  SetTypeId("ns3::NetmapNetDevice");
69 }
70 
71 std::string
73 {
74  return m_deviceName;
75 }
76 
77 void
78 NetmapNetDeviceHelper::SetDeviceName(std::string deviceName)
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 
108 void
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 
197 int
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 
429 void
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
Hold an unsigned integer type.
Definition: uinteger.h:45
#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.
#define EMU_MAGIC
ns3::StringValue attribute value declarations.