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
61namespace ns3 {
62
63NS_LOG_COMPONENT_DEFINE ("NetmapNetDeviceHelper");
64
65#define EMU_MAGIC 65867
66
68{
69 m_deviceName = "undefined";
70 SetTypeId ("ns3::NetmapNetDevice");
71}
72
73std::string
75{
76 return m_deviceName;
77}
78
79void
81{
82 m_deviceName = deviceName;
83}
84
87{
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
108void
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
198int
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
421void
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
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:86
static TypeId GetTypeId(void)
Get the type ID.
std::string GetDeviceName(void)
Get the device name of this device.
Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice attached to a physical network interface.
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.
virtual int CreateFileDescriptor(void) const
Call out to a separate process running as suid root in order to get a raw socket.
virtual void SetDeviceAttributes(Ptr< FdNetDevice > device) const
Sets device flags and MTU.
Ptr< T > GetObject(void) const
Get a pointer to the requested aggregated Object.
Definition: object.h:470
AttributeValue implementation for TypeId.
Definition: type-id.h:595
Hold an unsigned integer type.
Definition: uinteger.h:44
#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
#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
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:165
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:289
#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:281
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.