A Discrete-Event Network Simulator
API
emu-fd-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) 2012 INRIA, 2012 University of Washington
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
20#include "encode-decode.h"
21
22#include "ns3/abort.h"
23#include "ns3/config.h"
24#include "ns3/fd-net-device.h"
25#include "ns3/log.h"
26#include "ns3/names.h"
27#include "ns3/object-factory.h"
28#include "ns3/packet.h"
29#include "ns3/simulator.h"
30#include "ns3/trace-helper.h"
31
32#include <arpa/inet.h>
33#include <errno.h>
34#include <iostream>
35#include <iomanip>
36#include <limits>
37#include <memory>
38#include <net/ethernet.h>
39#include <net/if.h>
40#include <netinet/in.h>
41#include <netpacket/packet.h>
42#include <linux/if_tun.h>
43
44#include <stdlib.h>
45#include <string.h>
46#include <sys/wait.h>
47#include <sys/stat.h>
48#include <sys/socket.h>
49#include <sys/un.h>
50#include <sys/ioctl.h>
51#include <time.h>
52#include <unistd.h>
53
54#include <string>
55
56namespace ns3 {
57
58NS_LOG_COMPONENT_DEFINE ("EmuFdNetDeviceHelper");
59
60#define EMU_MAGIC 65867
61
63{
64 m_deviceName = "undefined";
65 m_hostQdiscBypass = false;
66}
67
68void
70{
71 m_deviceName = deviceName;
72}
73
74void
76{
77 m_hostQdiscBypass = hostQdiscBypass;
78}
79
80std::string
82{
83 return m_deviceName;
84}
85
88{
90 Ptr<FdNetDevice> device = d->GetObject<FdNetDevice> ();
91 SetFileDescriptor (device);
92 return device;
93}
94
95void
97{
98 NS_LOG_LOGIC ("Creating EMU socket");
99
100 if (m_deviceName == "undefined")
101 {
102 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::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 = CreateFileDescriptor ();
110 device->SetFileDescriptor (fd);
111
112 //
113 // Figure out which interface index corresponds to the device name in the corresponding 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 ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't get interface index");
124 }
125
126 //
127 // Bind the socket to the interface we just found.
128 //
129 struct sockaddr_ll ll;
130 bzero (&ll, sizeof(ll));
131
132 ll.sll_family = AF_PACKET;
133 ll.sll_ifindex = ifr.ifr_ifindex;
134 ll.sll_protocol = htons (ETH_P_ALL);
135
136 NS_LOG_LOGIC ("Binding socket to interface");
137
138 rc = bind (fd, (struct sockaddr *)&ll, sizeof (ll));
139 if (rc == -1)
140 {
141 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't bind to specified interface");
142 }
143
144 rc = ioctl (fd, SIOCGIFFLAGS, &ifr);
145 if (rc == -1)
146 {
147 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't get interface flags");
148 }
149
151 {
152#ifdef PACKET_QDISC_BYPASS
153 static const int32_t sock_qdisc_bypass = 1;
154 int32_t sock_qdisc_ret = setsockopt (fd, SOL_PACKET, PACKET_QDISC_BYPASS, &sock_qdisc_bypass,
155 sizeof (sock_qdisc_bypass));
156
157 if (sock_qdisc_ret == -1)
158 {
159 NS_LOG_ERROR ("Cannot use the qdisc bypass option");
160 }
161#else
162 // PACKET_QDISC_BYPASS is defined since Linux 3.14
163 NS_LOG_ERROR ("PACKET_QDISC_BYPASS undefined; cannot use the qdisc bypass option");
164#endif
165 }
166
167 //
168 // This device only works if the underlying interface is up in promiscuous
169 // mode. We could have turned it on in the socket creator, but the situation
170 // is that we expect these devices to be used in conjunction with virtual
171 // machines with connected host-only (simulated) networks, or in a testbed.
172 // There is a lot of setup and configuration happening outside of this one
173 // issue, and we expect that configuration to include choosing a valid
174 // interface (e.g, "ath1"), ensuring that the device supports promiscuous
175 // mode, and placing it in promiscuous mode. We just make sure of the
176 // end result.
177 //
178 if ((ifr.ifr_flags & IFF_PROMISC) == 0)
179 {
180 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): " << m_deviceName.c_str () << " is not in promiscuous mode");
181 }
182
183 if ((ifr.ifr_flags & IFF_BROADCAST) != IFF_BROADCAST)
184 {
185 // We default m_isBroadcast to true but turn it off here if not
186 // supported, because in the common case, overlying IP code will
187 // assert during configuration time if this is false, before this
188 // method has a chance to set it during runtime
189 device->SetIsBroadcast (false);
190 }
191
192 if ((ifr.ifr_flags & IFF_MULTICAST) == IFF_MULTICAST)
193 {
194 // This one is OK to enable at runtime
195 device->SetIsMulticast (true);
196 }
197
198 // Set the MTU of the device to the mtu of the associated network interface
199 struct ifreq ifr2;
200
201 bzero (&ifr2, sizeof (ifr2));
202 strcpy (ifr2.ifr_name, m_deviceName.c_str ());
203
204 int32_t mtufd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP);
205
206 rc = ioctl (mtufd, SIOCGIFMTU, &ifr2);
207 if (rc == -1)
208 {
209 NS_FATAL_ERROR ("FdNetDevice::SetFileDescriptor (): Can't ioctl SIOCGIFMTU");
210 }
211
212 close (mtufd);
213 device->SetMtu (ifr2.ifr_mtu);
214}
215
216int
218{
219 NS_LOG_FUNCTION (this);
220
221 //
222 // We want to create a raw socket for our net device. Unfortunately for us
223 // you have to have root privileges to do that. Instead of running the
224 // entire simulation as root, we decided to make a small program who's whole
225 // reason for being is to run as suid root and create a raw socket. We're
226 // going to fork and exec that program soon, but we need to have a socket
227 // to talk to it with. So we create a local interprocess (Unix) socket
228 // for that purpose.
229 //
230 int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
231 if (sock == -1)
232 {
233 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = " << strerror (errno));
234 }
235
236 //
237 // Bind to that socket and let the kernel allocate an endpoint
238 //
239 struct sockaddr_un un;
240 memset (&un, 0, sizeof (un));
241 un.sun_family = AF_UNIX;
242 int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
243 if (status == -1)
244 {
245 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = " << strerror (errno));
246 }
247
248 NS_LOG_INFO ("Created Unix socket");
249 NS_LOG_INFO ("sun_family = " << un.sun_family);
250 NS_LOG_INFO ("sun_path = " << un.sun_path);
251
252 //
253 // We have a socket here, but we want to get it there -- to the program we're
254 // going to exec. What we'll do is to do a getsockname and then encode the
255 // resulting address information as a string, and then send the string to the
256 // program as an argument. So we need to get the sock name.
257 //
258 socklen_t len = sizeof (un);
259 status = getsockname (sock, (struct sockaddr*)&un, &len);
260 if (status == -1)
261 {
262 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = " << strerror (errno));
263 }
264
265 //
266 // Now encode that socket name (family and path) as a string of hex digits
267 //
268 std::string path = BufferToString ((uint8_t *)&un, len);
269 NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
270 //
271 // Fork and exec the process to create our socket. If we're us (the parent)
272 // we wait for the child (the socket creator) to complete and read the
273 // socket it created using the ancillary data mechanism.
274 //
275 // Tom Goff reports the possibility of a deadlock when trying to acquire the
276 // python GIL here. He says that this might be due to trying to access Python
277 // objects after fork() without calling PyOS_AfterFork() to properly reset
278 // Python state (including the GIL). There is no code to cause the problem
279 // here in emu, but this was visible in similar code in tap-bridge.
280 //
281 pid_t pid = ::fork ();
282 if (pid == 0)
283 {
284 NS_LOG_DEBUG ("Child process");
285
286 //
287 // build a command line argument from the encoded endpoint string that
288 // the socket creation process will use to figure out how to respond to
289 // the (now) parent process.
290 //
291 std::ostringstream oss;
292 oss << "-p" << path;
293 NS_LOG_INFO ("Parameters set to \"" << oss.str () << "\"");
294
295 //
296 // Execute the socket creation process image.
297 //
298 status = ::execlp (RAW_SOCK_CREATOR,
299 RAW_SOCK_CREATOR, // argv[0] (filename)
300 oss.str ().c_str (), // argv[1] (-p<path?
301 (char *)NULL);
302
303 //
304 // If the execlp successfully completes, it never returns. If it returns it failed or the OS is
305 // broken. In either case, we bail.
306 //
307 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), status = " <<
308 status << ", errno = " << ::strerror (errno));
309 }
310 else
311 {
312 NS_LOG_DEBUG ("Parent process");
313 //
314 // We're the process running the emu net device. We need to wait for the
315 // socket creator process to finish its job.
316 //
317 int st;
318 pid_t waited = waitpid (pid, &st, 0);
319 if (waited == -1)
320 {
321 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = " << strerror (errno));
322 }
323 NS_ASSERT_MSG (pid == waited, "EmuFdNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
324
325 //
326 // Check to see if the socket creator exited normally and then take a
327 // look at the exit code. If it bailed, so should we. If it didn't
328 // even exit normally, we bail too.
329 //
330 if (WIFEXITED (st))
331 {
332 int exitStatus = WEXITSTATUS (st);
333 if (exitStatus != 0)
334 {
335 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited normally with status " << exitStatus);
336 }
337 }
338 else
339 {
340 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
341 }
342
343 //
344 // At this point, the socket creator has run successfully and should
345 // have created our raw socket and sent it back to the socket address
346 // we provided. Our socket should be waiting on the Unix socket. We've
347 // got to do a bunch of grunto work to get at it, though.
348 //
349 // The struct iovec below is part of a scatter-gather list. It describes a
350 // buffer. In this case, it describes a buffer (an integer) that will
351 // get the data that comes back from the socket creator process. It will
352 // be a magic number that we use as a consistency/sanity check.
353 //
354 struct iovec iov;
355 uint32_t magic;
356 iov.iov_base = &magic;
357 iov.iov_len = sizeof(magic);
358
359 //
360 // The CMSG macros you'll see below are used to create and access control
361 // messages (which is another name for ancillary data). The ancillary
362 // data is made up of pairs of struct cmsghdr structures and associated
363 // data arrays.
364 //
365 // First, we're going to allocate a buffer on the stack to receive our
366 // data array (that contains the socket). Sometimes you'll see this called
367 // an "ancillary element" but the msghdr uses the control message terminology
368 // so we call it "control."
369 //
370 size_t msg_size = sizeof(int);
371 char control[CMSG_SPACE (msg_size)];
372
373 //
374 // There is a msghdr that is used to minimize the number of parameters
375 // passed to recvmsg (which we will use to receive our ancillary data).
376 // This structure uses terminology corresponding to control messages, so
377 // you'll see msg_control, which is the pointer to the ancillary data and
378 // controller which is the size of the ancillary data array.
379 //
380 // So, initialize the message header that describes the ancillary/control
381 // data we expect to receive and point it to buffer.
382 //
383 struct msghdr msg;
384 msg.msg_name = 0;
385 msg.msg_namelen = 0;
386 msg.msg_iov = &iov;
387 msg.msg_iovlen = 1;
388 msg.msg_control = control;
389 msg.msg_controllen = sizeof (control);
390 msg.msg_flags = 0;
391
392 //
393 // Now we can actually receive the interesting bits from the socket
394 // creator process.
395 //
396 ssize_t bytesRead = recvmsg (sock, &msg, 0);
397 if (bytesRead != sizeof(int))
398 {
399 NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from socket creator");
400 }
401
402 //
403 // There may be a number of message headers/ancillary data arrays coming in.
404 // Let's look for the one with a type SCM_RIGHTS which indicates it' the
405 // one we're interested in.
406 //
407 struct cmsghdr *cmsg;
408 for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg))
409 {
410 if (cmsg->cmsg_level == SOL_SOCKET
411 && cmsg->cmsg_type == SCM_RIGHTS)
412 {
413 //
414 // This is the type of message we want. Check to see if the magic
415 // number is correct and then pull out the socket we care about if
416 // it matches
417 //
418 if (magic == EMU_MAGIC)
419 {
420 NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
421 int *rawSocket = (int*)CMSG_DATA (cmsg);
422 NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
423 return *rawSocket;
424 }
425 else
426 {
427 NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);
428 }
429 }
430 }
431 NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
432 }
433 NS_FATAL_ERROR ("Should be unreachable");
434 return 0; // Silence compiler warning about lack of return value
435}
436
437} // namespace ns3
std::string GetDeviceName(void)
Get the device name of this device.
bool m_hostQdiscBypass
True if request host qdisc bypass.
std::string m_deviceName
The Unix/Linux name of the underlying device (e.g., eth0)
virtual int CreateFileDescriptor(void) const
Call out to a separate process running as suid root in order to get a raw socket.
void HostQdiscBypass(bool hostQdiscBypass)
Request host qdisc bypass.
void SetDeviceName(std::string deviceName)
Set the device name of this device.
virtual void SetFileDescriptor(Ptr< FdNetDevice > device) const
Sets a file descriptor on the FileDescriptorNetDevice.
EmuFdNetDeviceHelper()
Construct a EmuFdNetDeviceHelper.
Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice attached to a physical network interface.
virtual Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice and associates it to a node.
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:85
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:78
#define EMU_MAGIC
#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_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition: log.h:258
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:206
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:274
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition: log.h:290
#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:282
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.