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