A Discrete-Event Network Simulator
API
planetlab-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
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: Alina Quereilhac <alina.quereilhac@inria.fr>
19  * Claudio Freire <klaussfreire@sourceforge.net>
20  *
21  */
22 
24 #include "encode-decode.h"
25 
26 #include "ns3/abort.h"
27 #include "ns3/config.h"
28 #include "ns3/fd-net-device.h"
29 #include "ns3/log.h"
30 #include "ns3/names.h"
31 #include "ns3/object-factory.h"
32 #include "ns3/packet.h"
33 #include "ns3/simulator.h"
34 #include "ns3/trace-helper.h"
35 #include "ns3/internet-module.h"
36 
37 #include <arpa/inet.h>
38 #include <errno.h>
39 #include <iostream>
40 #include <iomanip>
41 #include <limits>
42 #include <linux/if_tun.h>
43 #include <memory>
44 #include <net/ethernet.h>
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <netpacket/packet.h>
48 
49 #include <stdlib.h>
50 #include <string.h>
51 #include <sys/wait.h>
52 #include <sys/stat.h>
53 #include <sys/socket.h>
54 #include <sys/un.h>
55 #include <sys/ioctl.h>
56 #include <time.h>
57 #include <unistd.h>
58 
59 #include <string>
60 
61 namespace ns3 {
62 
63 NS_LOG_COMPONENT_DEFINE ("PlanetLabFdNetDeviceHelper");
64 
65 #define PLANETLAB_MAGIC 75867
66 
68 {
69  m_tapIp = Ipv4Address ("255.255.255.255");
70  m_tapMask = Ipv4Mask ("255.255.255.255");
71 }
72 
73 void
75 {
76  m_tapIp = address;
77 }
78 
79 void
81 {
82  m_tapMask = mask;
83 }
84 
87 {
89  Ptr<FdNetDevice> device = d->GetObject<FdNetDevice> ();
90 
91  //
92  // The PlanetLab mechanism to create a TAP device doesn't allow
93  // for the moment to set the IFF_NOPI flag. In consequence, a PI
94  // header will be present in the traffic.
95  // We need to explicitly set the encapsulation mode to DIXPI,
96  // so the FdNetDevice is able to treat correctly the traffic
97  // traversing TAP device.
98  //
99  Ptr<FdNetDevice> fdnd = device->GetObject<FdNetDevice> ();
101 
102  SetFileDescriptor (device);
103  return device;
104 }
105 
106 void
108 {
109  NS_LOG_LOGIC ("Creating TAP device");
110 
111  //
112  // Call out to a separate process running as suid root in order to create a
113  // TAP device. We do this to avoid having the entire simulation running as root.
114  //
115  int fd = CreateFileDescriptor ();
116  device->SetFileDescriptor (fd);
117 }
118 
119 int
121 {
122  NS_LOG_FUNCTION (this);
123 
124  //
125  // We're going to fork and exec that program soon, but first we need to have
126  // a socket to talk to it with. So we create a local interprocess (Unix)
127  // socket for that purpose.
128  //
129  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
130  NS_ABORT_MSG_IF (sock == -1, "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = " << strerror (errno));
131 
132  //
133  // Bind to that socket and let the kernel allocate an endpoint
134  //
135  struct sockaddr_un un;
136  memset (&un, 0, sizeof (un));
137  un.sun_family = AF_UNIX;
138  int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
139  NS_ABORT_MSG_IF (status == -1, "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = " << strerror (errno));
140  NS_LOG_INFO ("Created Unix socket");
141  NS_LOG_INFO ("sun_family = " << un.sun_family);
142  NS_LOG_INFO ("sun_path = " << un.sun_path);
143 
144  //
145  // We have a socket here, but we want to get it there -- to the program we're
146  // going to exec. What we'll do is to do a getsockname and then encode the
147  // resulting address information as a string, and then send the string to the
148  // program as an argument. So we need to get the sock name.
149  //
150  socklen_t len = sizeof (un);
151  status = getsockname (sock, (struct sockaddr*)&un, &len);
152  NS_ABORT_MSG_IF (status == -1, "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = " << strerror (errno));
153 
154  //
155  // Now encode that socket name (family and path) as a string of hex digits
156  //
157  std::string path = BufferToString ((uint8_t *)&un, len);
158  NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
159 
160  //
161  // Fork and exec the process to create our socket. If we're us (the parent)
162  // we wait for the child (the creator) to complete and read the socket it
163  // created and passed back using the ancillary data mechanism.
164  //
165  pid_t pid = ::fork ();
166  if (pid == 0)
167  {
168  NS_LOG_DEBUG ("Child process");
169 
170  //
171  // build a command line argument from the encoded endpoint string that
172  // the socket creation process will use to figure out how to respond to
173  // the (now) parent process. We're going to have to give this program
174  // quite a bit of information.
175  //
176  // -i<IP-address> The IP address to assign to the new tap device;
177  // -n<network-prefix> The network prefix to assign to the new tap device;
178  // -t Set the IFF_TAP flag
179  // -p<path> the path to the unix socket described above.
180  //
181  // Example tap-creator -i1.2.3.1 -n24 -t -pblah
182  //
183 
184  std::ostringstream ossIp;
185  ossIp << "-i" << m_tapIp;
186 
187  std::ostringstream ossPrefix;
188  ossPrefix << "-n" << m_tapMask.GetPrefixLength ();
189 
190  std::ostringstream ossMode;
191  ossMode << "-t";
192 
193  std::ostringstream ossPath;
194  ossPath << "-p" << path;
195 
196  //
197  // Execute the socket creation process image.
198  //
199  status = ::execlp (PLANETLAB_TAP_CREATOR,
200  PLANETLAB_TAP_CREATOR, // argv[0] (filename)
201  ossIp.str ().c_str (), // argv[1] (-i<IP address>)
202  ossPrefix.str ().c_str (), // argv[2] (-n<prefix>)
203  ossMode.str ().c_str (), // argv[3] (-t <tap>)
204  ossPath.str ().c_str (), // argv[4] (-p<path>)
205  (char *)NULL);
206 
207  //
208  // If the execlp successfully completes, it never returns. If it returns it failed or the OS is
209  // broken. In either case, we bail.
210  //
211  NS_FATAL_ERROR ("PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), errno = " << ::strerror (errno));
212  }
213  else
214  {
215  NS_LOG_DEBUG ("Parent process");
216  //
217  // We're the process running the emu net device. We need to wait for the
218  // socket creator process to finish its job.
219  //
220  int st;
221  pid_t waited = waitpid (pid, &st, 0);
222  NS_ABORT_MSG_IF (waited == -1, "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = " << strerror (errno));
223  NS_ASSERT_MSG (pid == waited, "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
224 
225  //
226  // Check to see if the socket creator exited normally and then take a
227  // look at the exit code. If it bailed, so should we. If it didn't
228  // even exit normally, we bail too.
229  //
230  if (WIFEXITED (st))
231  {
232  int exitStatus = WEXITSTATUS (st);
233  NS_ABORT_MSG_IF (exitStatus != 0,
234  "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited normally with status " << exitStatus);
235  }
236  else
237  {
238  NS_FATAL_ERROR ("PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
239  }
240 
241  //
242  // At this point, the socket creator has run successfully and should
243  // have created our tap device, initialized it with the information we
244  // passed and sent it back to the socket address we provided. A socket
245  // (fd) we can use to talk to this tap device should be waiting on the
246  // Unix socket we set up to receive information back from the creator
247  // program. We've got to do a bunch of grunt work to get at it, though.
248  //
249  // The struct iovec below is part of a scatter-gather list. It describes a
250  // buffer. In this case, it describes a buffer (an integer) that will
251  // get the data that comes back from the socket creator process. It will
252  // be a magic number that we use as a consistency/sanity check.
253  //
254  struct iovec iov;
255  uint32_t magic;
256  iov.iov_base = &magic;
257  iov.iov_len = sizeof(magic);
258 
259  //
260  // The CMSG macros you'll see below are used to create and access control
261  // messages (which is another name for ancillary data). The ancillary
262  // data is made up of pairs of struct cmsghdr structures and associated
263  // data arrays.
264  //
265  // First, we're going to allocate a buffer on the stack to receive our
266  // data array (that contains the socket). Sometimes you'll see this called
267  // an "ancillary element" but the msghdr uses the control message termimology
268  // so we call it "control."
269  //
270  size_t msg_size = sizeof(int);
271  char control[CMSG_SPACE (msg_size)];
272 
273  //
274  // There is a msghdr that is used to minimize the number of parameters
275  // passed to recvmsg (which we will use to receive our ancillary data).
276  // This structure uses terminology corresponding to control messages, so
277  // you'll see msg_control, which is the pointer to the ancillary data and
278  // controllen which is the size of the ancillary data array.
279  //
280  // So, initialize the message header that describes the ancillary/control
281  // data we expect to receive and point it to buffer.
282  //
283  struct msghdr msg;
284  msg.msg_name = 0;
285  msg.msg_namelen = 0;
286  msg.msg_iov = &iov;
287  msg.msg_iovlen = 1;
288  msg.msg_control = control;
289  msg.msg_controllen = sizeof (control);
290  msg.msg_flags = 0;
291 
292  //
293  // Now we can actually receive the interesting bits from the tap
294  // creator process. Lots of pain to get four bytes.
295  //
296  ssize_t bytesRead = recvmsg (sock, &msg, 0);
297  NS_ABORT_MSG_IF (bytesRead != sizeof(int), "PlanetLabFdNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from socket creator");
298 
299  //
300  // There may be a number of message headers/ancillary data arrays coming in.
301  // Let's look for the one with a type SCM_RIGHTS which indicates it's the
302  // one we're interested in.
303  //
304  struct cmsghdr *cmsg;
305  for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg))
306  {
307  if (cmsg->cmsg_level == SOL_SOCKET
308  && cmsg->cmsg_type == SCM_RIGHTS)
309  {
310  //
311  // This is the type of message we want. Check to see if the magic
312  // number is correct and then pull out the socket we care about if
313  // it matches
314  //
315  if (magic == PLANETLAB_MAGIC)
316  {
317  NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
318  int *rawSocket = (int*)CMSG_DATA (cmsg);
319  NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
320  return *rawSocket;
321  }
322  else
323  {
324  NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);
325  }
326  }
327  }
328  NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
329  }
330 
331 }
332 
333 } // namespace ns3
334 
335 
virtual Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice and associates it to a node.
virtual int CreateFileDescriptor(void) const
Call out to a separate process running as suid root in order to create a TAP device and obtain the fi...
Smart pointer class similar to boost::intrusive_ptr.
Definition: ptr.h:73
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by "...
NS_ASSERT_MSG(false, "Ipv4AddressGenerator::MaskToIndex(): Impossible")
ns3::StringValue attribute value declarations.
a class to represent an Ipv4 address mask
Definition: ipv4-address.h:263
When using TAP devices, if flag IFF_NO_PI is not set on the device, IP packets will have an extra hea...
#define PLANETLAB_MAGIC
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
void SetTapMask(Ipv4Mask mask)
Set the network mask for the TAP device.
virtual void SetFileDescriptor(Ptr< FdNetDevice > device) const
Sets a file descriptor on the FileDescriptorNetDevice.
std::string BufferToString(uint8_t *buffer, uint32_t len)
Convert a byte buffer to a string containing a hex representation of the buffer.
Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice attached to a virtual TAP network interface.
void SetEncapsulationMode(FdNetDevice::EncapsulationMode mode)
Set the link layer encapsulation mode of this device.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
address
Definition: first.py:44
NS_LOG_LOGIC("Net device "<< nd<< " is not bridged")
Ipv4Mask m_tapMask
The network mask for the TAP device.
Ipv4 addresses are stored in host order in this class.
Definition: ipv4-address.h:41
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:273
uint16_t GetPrefixLength(void) const
Ipv4Address m_tapIp
The IP address for the TAP device.
void SetTapIpAddress(Ipv4Address address)
Set the device IPv4 address.
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:84
PlanetLabFdNetDeviceHelper()
Construct a PlanetLabFdNetDeviceHelper.