A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
emu-fd-net-device-helper.cc
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 
19 #include "emu-fd-net-device-helper.h"
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 <linux/if_tun.h>
38 #include <memory>
39 #include <net/ethernet.h>
40 #include <net/if.h>
41 #include <netinet/in.h>
42 #include <netpacket/packet.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 
56 NS_LOG_COMPONENT_DEFINE ("EmuFdNetDeviceHelper");
57 
58 namespace ns3 {
59 
60 #define EMU_MAGIC 65867
61 
63 {
64  m_deviceName = "undefined";
65 }
66 
67 void
68 EmuFdNetDeviceHelper::SetDeviceName (std::string deviceName)
69 {
70  m_deviceName = deviceName;
71 }
72 
73 std::string
75 {
76  return m_deviceName;
77 }
78 
80 EmuFdNetDeviceHelper::InstallPriv (Ptr<Node> node) const
81 {
82  Ptr<NetDevice> d = FdNetDeviceHelper::InstallPriv (node);
83  Ptr<FdNetDevice> device = d->GetObject<FdNetDevice> ();
84  SetFileDescriptor (device);
85  return device;
86 }
87 
88 void
90 {
91  NS_LOG_LOGIC ("Creating EMU socket");
92 
93  if (m_deviceName == "undefined")
94  {
95  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): m_deviceName is not set");
96  }
97 
98  //
99  // Call out to a separate process running as suid root in order to get a raw
100  // socket. We do this to avoid having the entire simulation running as root.
101  //
102  int fd = CreateFileDescriptor ();
103  device->SetFileDescriptor (fd);
104 
105  //
106  // Figure out which interface index corresponds to the device name in the corresponding attribute.
107  //
108  struct ifreq ifr;
109  bzero (&ifr, sizeof(ifr));
110  strncpy ((char *)ifr.ifr_name, m_deviceName.c_str (), IFNAMSIZ);
111 
112  NS_LOG_LOGIC ("Getting interface index");
113  int32_t rc = ioctl (fd, SIOCGIFINDEX, &ifr);
114  if (rc == -1)
115  {
116  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't get interface index");
117  }
118 
119  //
120  // Bind the socket to the interface we just found.
121  //
122  struct sockaddr_ll ll;
123  bzero (&ll, sizeof(ll));
124 
125  ll.sll_family = AF_PACKET;
126  ll.sll_ifindex = ifr.ifr_ifindex;
127  ll.sll_protocol = htons (ETH_P_ALL);
128 
129  NS_LOG_LOGIC ("Binding socket to interface");
130 
131  rc = bind (fd, (struct sockaddr *)&ll, sizeof (ll));
132  if (rc == -1)
133  {
134  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::SetFileDescriptor (): Can't bind to specified interface");
135  }
136 
137  rc = ioctl (fd, SIOCGIFFLAGS, &ifr);
138  if (rc == -1)
139  {
140  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::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 ("EmuFdNetDeviceHelper::SetFileDescriptor (): " << m_deviceName.c_str () << " is not in promiscuous mode");
157  }
158 
159  if ((ifr.ifr_flags & IFF_BROADCAST) != IFF_BROADCAST)
160  {
161  // We default m_isBroadcast to true but turn it off here if not
162  // supported, because in the common case, overlying IP code will
163  // assert during configuration time if this is false, before this
164  // method has a chance to set it during runtime
165  device->SetIsBroadcast (false);
166  }
167 
168  if ((ifr.ifr_flags & IFF_MULTICAST) == IFF_MULTICAST)
169  {
170  // This one is OK to enable at runtime
171  device->SetIsMulticast (true);
172  }
173 
174  // Set the MTU of the device to the mtu of the associated network interface
175  struct ifreq ifr2;
176 
177  bzero (&ifr2, sizeof (ifr2));
178  strcpy (ifr2.ifr_name, m_deviceName.c_str ());
179 
180  int32_t mtufd = socket (PF_INET, SOCK_DGRAM, IPPROTO_IP);
181 
182  rc = ioctl (mtufd, SIOCGIFMTU, &ifr2);
183  if (rc == -1)
184  {
185  NS_FATAL_ERROR ("FdNetDevice::SetFileDescriptor (): Can't ioctl SIOCGIFMTU");
186  }
187 
188  close (mtufd);
189  device->SetMtu (ifr.ifr_mtu);
190 }
191 
192 int
194 {
195  NS_LOG_FUNCTION (this);
196 
197  //
198  // We want to create a raw socket for our net device. Unfortunately for us
199  // you have to have root privileges to do that. Instead of running the
200  // entire simulation as root, we decided to make a small program who's whole
201  // reason for being is to run as suid root and create a raw socket. We're
202  // going to fork and exec that program soon, but we need to have a socket
203  // to talk to it with. So we create a local interprocess (Unix) socket
204  // for that purpose.
205  //
206  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
207  if (sock == -1)
208  {
209  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = " << strerror (errno));
210  }
211 
212  //
213  // Bind to that socket and let the kernel allocate an endpoint
214  //
215  struct sockaddr_un un;
216  memset (&un, 0, sizeof (un));
217  un.sun_family = AF_UNIX;
218  int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
219  if (status == -1)
220  {
221  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = " << strerror (errno));
222  }
223 
224  NS_LOG_INFO ("Created Unix socket");
225  NS_LOG_INFO ("sun_family = " << un.sun_family);
226  NS_LOG_INFO ("sun_path = " << un.sun_path);
227 
228  //
229  // We have a socket here, but we want to get it there -- to the program we're
230  // going to exec. What we'll do is to do a getsockname and then encode the
231  // resulting address information as a string, and then send the string to the
232  // program as an argument. So we need to get the sock name.
233  //
234  socklen_t len = sizeof (un);
235  status = getsockname (sock, (struct sockaddr*)&un, &len);
236  if (status == -1)
237  {
238  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = " << strerror (errno));
239  }
240 
241  //
242  // Now encode that socket name (family and path) as a string of hex digits
243  //
244  std::string path = BufferToString ((uint8_t *)&un, len);
245  NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
246  //
247  // Fork and exec the process to create our socket. If we're us (the parent)
248  // we wait for the child (the socket creator) to complete and read the
249  // socket it created using the ancillary data mechanism.
250  //
251  // Tom Goff reports the possiblility of a deadlock when trying to acquire the
252  // python GIL here. He says that this might be due to trying to access Python
253  // objects after fork() without calling PyOS_AfterFork() to properly reset
254  // Python state (including the GIL). There is no code to cause the problem
255  // here in emu, but this was visible in similar code in tap-bridge.
256  //
257  pid_t pid = ::fork ();
258  if (pid == 0)
259  {
260  NS_LOG_DEBUG ("Child process");
261 
262  //
263  // build a command line argument from the encoded endpoint string that
264  // the socket creation process will use to figure out how to respond to
265  // the (now) parent process.
266  //
267  std::ostringstream oss;
268  oss << "-p" << path;
269  NS_LOG_INFO ("Parameters set to \"" << oss.str () << "\"");
270 
271  //
272  // Execute the socket creation process image.
273  //
274  status = ::execlp (RAW_SOCK_CREATOR,
275  RAW_SOCK_CREATOR, // argv[0] (filename)
276  oss.str ().c_str (), // argv[1] (-p<path?
277  (char *)NULL);
278 
279  //
280  // If the execlp successfully completes, it never returns. If it returns it failed or the OS is
281  // broken. In either case, we bail.
282  //
283  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), errno = " << ::strerror (errno));
284  }
285  else
286  {
287  NS_LOG_DEBUG ("Parent process");
288  //
289  // We're the process running the emu net device. We need to wait for the
290  // socket creator process to finish its job.
291  //
292  int st;
293  pid_t waited = waitpid (pid, &st, 0);
294  if (waited == -1)
295  {
296  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = " << strerror (errno));
297  }
298  NS_ASSERT_MSG (pid == waited, "EmuFdNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
299 
300  //
301  // Check to see if the socket creator exited normally and then take a
302  // look at the exit code. If it bailed, so should we. If it didn't
303  // even exit normally, we bail too.
304  //
305  if (WIFEXITED (st))
306  {
307  int exitStatus = WEXITSTATUS (st);
308  if (exitStatus != 0)
309  {
310  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited normally with status " << exitStatus);
311  }
312  }
313  else
314  {
315  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
316  }
317 
318  //
319  // At this point, the socket creator has run successfully and should
320  // have created our raw socket and sent it back to the socket address
321  // we provided. Our socket should be waiting on the Unix socket. We've
322  // got to do a bunch of grunto work to get at it, though.
323  //
324  // The struct iovec below is part of a scatter-gather list. It describes a
325  // buffer. In this case, it describes a buffer (an integer) that will
326  // get the data that comes back from the socket creator process. It will
327  // be a magic number that we use as a consistency/sanity check.
328  //
329  struct iovec iov;
330  uint32_t magic;
331  iov.iov_base = &magic;
332  iov.iov_len = sizeof(magic);
333 
334  //
335  // The CMSG macros you'll see below are used to create and access control
336  // messages (which is another name for ancillary data). The ancillary
337  // data is made up of pairs of struct cmsghdr structures and associated
338  // data arrays.
339  //
340  // First, we're going to allocate a buffer on the stack to receive our
341  // data array (that contains the socket). Sometimes you'll see this called
342  // an "ancillary element" but the msghdr uses the control message termimology
343  // so we call it "control."
344  //
345  size_t msg_size = sizeof(int);
346  char control[CMSG_SPACE (msg_size)];
347 
348  //
349  // There is a msghdr that is used to minimize the number of parameters
350  // passed to recvmsg (which we will use to receive our ancillary data).
351  // This structure uses terminology corresponding to control messages, so
352  // you'll see msg_control, which is the pointer to the ancillary data and
353  // controllen which is the size of the ancillary data array.
354  //
355  // So, initialize the message header that describes the ancillary/control
356  // data we expect to receive and point it to buffer.
357  //
358  struct msghdr msg;
359  msg.msg_name = 0;
360  msg.msg_namelen = 0;
361  msg.msg_iov = &iov;
362  msg.msg_iovlen = 1;
363  msg.msg_control = control;
364  msg.msg_controllen = sizeof (control);
365  msg.msg_flags = 0;
366 
367  //
368  // Now we can actually receive the interesting bits from the socket
369  // creator process.
370  //
371  ssize_t bytesRead = recvmsg (sock, &msg, 0);
372  if (bytesRead != sizeof(int))
373  {
374  NS_FATAL_ERROR ("EmuFdNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from socket creator");
375  }
376 
377  //
378  // There may be a number of message headers/ancillary data arrays coming in.
379  // Let's look for the one with a type SCM_RIGHTS which indicates it' the
380  // one we're interested in.
381  //
382  struct cmsghdr *cmsg;
383  for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg))
384  {
385  if (cmsg->cmsg_level == SOL_SOCKET
386  && cmsg->cmsg_type == SCM_RIGHTS)
387  {
388  //
389  // This is the type of message we want. Check to see if the magic
390  // number is correct and then pull out the socket we care about if
391  // it matches
392  //
393  if (magic == EMU_MAGIC)
394  {
395  NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
396  int *rawSocket = (int*)CMSG_DATA (cmsg);
397  NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
398  return *rawSocket;
399  }
400  else
401  {
402  NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);
403  }
404  }
405  }
406  NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
407  }
408 }
409 
410 } // namespace ns3
smart pointer class similar to boost::intrusive_ptr
Definition: ptr.h:59
#define NS_LOG_FUNCTION(parameters)
Definition: log.h:311
#define NS_LOG_COMPONENT_DEFINE(name)
Definition: log.h:122
#define NS_LOG_INFO(msg)
Definition: log.h:264
#define NS_FATAL_ERROR(msg)
fatal error handling
Definition: fatal-error.h:72
std::string BufferToString(uint8_t *buffer, uint32_t len)
Convert a byte buffer to a string containing a hex representation of the buffer. Make the string pret...
virtual int CreateFileDescriptor(void) const
#define NS_LOG_LOGIC(msg)
Definition: log.h:334
void SetDeviceName(std::string deviceName)
#define NS_ASSERT_MSG(condition, message)
Definition: assert.h:86
#define NS_LOG_DEBUG(msg)
Definition: log.h:255
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:82
virtual void SetFileDescriptor(Ptr< FdNetDevice > device) const