A Discrete-Event Network Simulator
API
tap-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 #include "ns3/internet-module.h"
32 
33 #include <arpa/inet.h>
34 #include <errno.h>
35 #include <iostream>
36 #include <iomanip>
37 #include <limits>
38 #include <linux/if_tun.h>
39 #include <memory>
40 #include <net/ethernet.h>
41 #include <net/if.h>
42 #include <netinet/in.h>
43 #include <netpacket/packet.h>
44 
45 #include <stdlib.h>
46 #include <string.h>
47 #include <sys/wait.h>
48 #include <sys/stat.h>
49 #include <sys/socket.h>
50 #include <sys/un.h>
51 #include <sys/ioctl.h>
52 #include <time.h>
53 #include <unistd.h>
54 
55 #include <string>
56 
57 namespace ns3 {
58 
59 NS_LOG_COMPONENT_DEFINE ("TapFdNetDeviceHelper");
60 
61 #define TAP_MAGIC 95549
62 
64 {
65  m_deviceName = "";
66  m_modePi = false;
70  m_tapPrefix6 = 64;
72 }
73 
74 void
76 {
77  m_modePi = modePi;
78 }
79 
80 void
82 {
83  m_tapIp4 = address;
84 }
85 
86 void
88 {
89  m_tapMask4 = mask;
90 }
91 
92 void
94 {
95  m_tapIp6 = address;
96 }
97 
98 void
100 {
101  m_tapPrefix6 = prefix;
102 }
103 
104 void
106 {
107  m_tapMac = mac;
108 }
109 
112 {
114  Ptr<FdNetDevice> device = d->GetObject<FdNetDevice> ();
115 
116  //
117  // We need to explicitly set the encapsulation mode for the traffic
118  // traversing the TAP device, so the FdNetDevice is able to know
119  // how to treat the traffic in a way that in compatible with the
120  // TAP device.
121  //
122  if (m_modePi)
123  {
124  device->SetEncapsulationMode (FdNetDevice::DIXPI);
125  }
126 
127  SetFileDescriptor (device);
128  return device;
129 }
130 
131 void
133 {
134  NS_LOG_LOGIC ("Creating TAP device");
135 
136  //
137  // Call out to a separate process running as suid root in order to create a
138  // TAP device. We do this to avoid having the entire simulation running as root.
139  //
140  int fd = CreateFileDescriptor ();
141  device->SetFileDescriptor (fd);
142 }
143 
144 int
146 {
147  NS_LOG_FUNCTION (this);
148 
149  //
150  // We're going to fork and exec that program soon, but first we need to have
151  // a socket to talk to it with. So we create a local interprocess (Unix)
152  // socket for that purpose.
153  //
154  int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
155  NS_ABORT_MSG_IF (sock == -1, "TapFdNetDeviceHelper::CreateFileDescriptor(): Unix socket creation error, errno = " << strerror (errno));
156 
157  //
158  // Bind to that socket and let the kernel allocate an endpoint
159  //
160  struct sockaddr_un un;
161  memset (&un, 0, sizeof (un));
162  un.sun_family = AF_UNIX;
163  int status = bind (sock, (struct sockaddr*)&un, sizeof (sa_family_t));
164  NS_ABORT_MSG_IF (status == -1, "TapFdNetDeviceHelper::CreateFileDescriptor(): Could not bind(): errno = " << strerror (errno));
165  NS_LOG_INFO ("Created Unix socket");
166  NS_LOG_INFO ("sun_family = " << un.sun_family);
167  NS_LOG_INFO ("sun_path = " << un.sun_path);
168 
169  //
170  // We have a socket here, but we want to get it there -- to the program we're
171  // going to exec. What we'll do is to do a getsockname and then encode the
172  // resulting address information as a string, and then send the string to the
173  // program as an argument. So we need to get the sock name.
174  //
175  socklen_t len = sizeof (un);
176  status = getsockname (sock, (struct sockaddr*)&un, &len);
177  NS_ABORT_MSG_IF (status == -1, "TapFdNetDeviceHelper::CreateFileDescriptor(): Could not getsockname(): errno = " << strerror (errno));
178 
179  //
180  // Now encode that socket name (family and path) as a string of hex digits
181  //
182  std::string path = BufferToString ((uint8_t *)&un, len);
183  NS_LOG_INFO ("Encoded Unix socket as \"" << path << "\"");
184 
185  //
186  // Fork and exec the process to create our socket. If we're us (the parent)
187  // we wait for the child (the creator) to complete and read the socket it
188  // created and passed back using the ancillary data mechanism.
189  //
190  pid_t pid = ::fork ();
191  if (pid == 0)
192  {
193  NS_LOG_DEBUG ("Child process");
194 
195  //
196  // build a command line argument from the encoded endpoint string that
197  // the socket creation process will use to figure out how to respond to
198  // the (now) parent process. We're going to have to give this program
199  // quite a bit of information.
200  //
201  // -d<device-name> The name of the tap device we want to create;
202  // -m<MAC-address> The MAC-48 address to assign to the new tap device;
203  // -i<IPv4-address> The IP v4 address to assign to the new tap device;
204  // -I<IPv6-address> The IP v6 address to assign to the new tap device;
205  // -n<network-IPv4-mask> The network IPv4 mask to assign to the new tap device;
206  // -N<network-IPv6-mask> The network IPv6 mask to assign to the new tap device;
207  // -t Set the IFF_TAP flag
208  // -h Set the IFF_NO_PI flag
209  // -p<path> the path to the unix socket described above.
210  //
211  // Example tap-creator -dnewdev -i1.2.3.1 -m08:00:2e:00:01:23 -n255.255.255.0 -t -h -pblah
212  //
213 
214  //
215  // The device-name is something we may want the system to make up in
216  // every case. We also rely on it being configured via an Attribute
217  // through the helper. By default, it is set to the empty string
218  // which tells the system to make up a device name such as "tap123".
219  //
220  std::ostringstream ossDeviceName;
221  if (m_deviceName != "")
222  {
223  ossDeviceName << "-d" << m_deviceName;
224  }
225 
226  std::ostringstream ossMac;
227  ossMac << "-m" << m_tapMac;
228 
229  std::ostringstream ossIp4;
230  if (m_tapIp4 != Ipv4Address::GetZero ())
231  {
232  ossIp4 << "-i" << m_tapIp4;
233  }
234 
235  std::ostringstream ossIp6;
236  if (m_tapIp6 != Ipv6Address::GetZero ())
237  {
238  ossIp6 << "-I" << m_tapIp6;
239  }
240 
241  std::ostringstream ossNetmask4;
242  if (m_tapMask4 != Ipv4Mask::GetZero () )
243  {
244  ossNetmask4 << "-n" << m_tapMask4;
245  }
246 
247  std::ostringstream ossPrefix6;
248  ossPrefix6 << "-P" << m_tapPrefix6;
249 
250  std::ostringstream ossMode;
251  ossMode << "-t";
252 
253  std::ostringstream ossPI;
254  if (m_modePi)
255  {
256  ossPI << "-h";
257  }
258 
259  std::ostringstream ossPath;
260  ossPath << "-p" << path;
261 
262  //
263  // Execute the socket creation process image.
264  //
265  status = ::execlp (TAP_DEV_CREATOR,
266  TAP_DEV_CREATOR, // argv[0] (filename)
267  ossDeviceName.str ().c_str (), // argv[1] (-d<device name>)
268  ossMac.str ().c_str (), // argv[2] (-m<MAC address>
269  ossIp4.str ().c_str (), // argv[3] (-i<IP v4 address>)
270  ossIp6.str ().c_str (), // argv[4] (-I<IP v6 address>)
271  ossNetmask4.str ().c_str (), // argv[5] (-n<IP v4 net mask>)
272  ossPrefix6.str ().c_str (), // argv[6] (-P<IP v6 prefix>)
273  ossMode.str ().c_str (), // argv[7] (-t <tap>)
274  ossPI.str ().c_str (), // argv[8] (-h <pi>)
275  ossPath.str ().c_str (), // argv[9] (-p<path>)
276  (char *)NULL);
277 
278  //
279  // If the execlp successfully completes, it never returns. If it returns it failed or the OS is
280  // broken. In either case, we bail.
281  //
282  NS_FATAL_ERROR ("TapFdNetDeviceHelper::CreateFileDescriptor(): Back from execlp(), status = " <<
283  status << ", 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  NS_ABORT_MSG_IF (waited == -1, "TapFdNetDeviceHelper::CreateFileDescriptor(): waitpid() fails, errno = " << strerror (errno));
295  NS_ASSERT_MSG (pid == waited, "TapFdNetDeviceHelper::CreateFileDescriptor(): pid mismatch");
296 
297  //
298  // Check to see if the socket creator exited normally and then take a
299  // look at the exit code. If it bailed, so should we. If it didn't
300  // even exit normally, we bail too.
301  //
302  if (WIFEXITED (st))
303  {
304  int exitStatus = WEXITSTATUS (st);
305  NS_ABORT_MSG_IF (exitStatus != 0,
306  "TapFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited normally with status " << exitStatus);
307  }
308  else
309  {
310  NS_FATAL_ERROR ("TapFdNetDeviceHelper::CreateFileDescriptor(): socket creator exited abnormally");
311  }
312 
313  //
314  // At this point, the socket creator has run successfully and should
315  // have created our tap device, initialized it with the information we
316  // passed and sent it back to the socket address we provided. A socket
317  // (fd) we can use to talk to this tap device should be waiting on the
318  // Unix socket we set up to receive information back from the creator
319  // program. We've got to do a bunch of grunt work to get at it, though.
320  //
321  // The struct iovec below is part of a scatter-gather list. It describes a
322  // buffer. In this case, it describes a buffer (an integer) that will
323  // get the data that comes back from the socket creator process. It will
324  // be a magic number that we use as a consistency/sanity check.
325  //
326  struct iovec iov;
327  uint32_t magic;
328  iov.iov_base = &magic;
329  iov.iov_len = sizeof(magic);
330 
331  //
332  // The CMSG macros you'll see below are used to create and access control
333  // messages (which is another name for ancillary data). The ancillary
334  // data is made up of pairs of struct cmsghdr structures and associated
335  // data arrays.
336  //
337  // First, we're going to allocate a buffer on the stack to receive our
338  // data array (that contains the socket). Sometimes you'll see this called
339  // an "ancillary element" but the msghdr uses the control message termimology
340  // so we call it "control."
341  //
342  size_t msg_size = sizeof(int);
343  char control[CMSG_SPACE (msg_size)];
344 
345  //
346  // There is a msghdr that is used to minimize the number of parameters
347  // passed to recvmsg (which we will use to receive our ancillary data).
348  // This structure uses terminology corresponding to control messages, so
349  // you'll see msg_control, which is the pointer to the ancillary data and
350  // controllen which is the size of the ancillary data array.
351  //
352  // So, initialize the message header that describes the ancillary/control
353  // data we expect to receive and point it to buffer.
354  //
355  struct msghdr msg;
356  msg.msg_name = 0;
357  msg.msg_namelen = 0;
358  msg.msg_iov = &iov;
359  msg.msg_iovlen = 1;
360  msg.msg_control = control;
361  msg.msg_controllen = sizeof (control);
362  msg.msg_flags = 0;
363 
364  //
365  // Now we can actually receive the interesting bits from the tap
366  // creator process. Lots of pain to get four bytes.
367  //
368  ssize_t bytesRead = recvmsg (sock, &msg, 0);
369  NS_ABORT_MSG_IF (bytesRead != sizeof(int), "TapFdNetDeviceHelper::CreateFileDescriptor(): Wrong byte count from socket creator");
370 
371  //
372  // There may be a number of message headers/ancillary data arrays coming in.
373  // Let's look for the one with a type SCM_RIGHTS which indicates it's the
374  // one we're interested in.
375  //
376  struct cmsghdr *cmsg;
377  for (cmsg = CMSG_FIRSTHDR (&msg); cmsg != NULL; cmsg = CMSG_NXTHDR (&msg, cmsg))
378  {
379  if (cmsg->cmsg_level == SOL_SOCKET
380  && cmsg->cmsg_type == SCM_RIGHTS)
381  {
382  //
383  // This is the type of message we want. Check to see if the magic
384  // number is correct and then pull out the socket we care about if
385  // it matches
386  //
387  if (magic == TAP_MAGIC)
388  {
389  NS_LOG_INFO ("Got SCM_RIGHTS with correct magic " << magic);
390  int *rawSocket = (int*)CMSG_DATA (cmsg);
391  NS_LOG_INFO ("Got the socket from the socket creator = " << *rawSocket);
392  return *rawSocket;
393  }
394  else
395  {
396  NS_LOG_INFO ("Got SCM_RIGHTS, but with bad magic " << magic);
397  }
398  }
399  }
400  NS_FATAL_ERROR ("Did not get the raw socket from the socket creator");
401  }
402 
403 }
404 
405 } // namespace ns3
406 
407 
virtual Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice and associates it to a node.
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 "...
Ipv4Address m_tapIp4
The IPv4 address for the TAP device.
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...
NS_ASSERT_MSG(false, "Ipv4AddressGenerator::MaskToIndex(): Impossible")
ns3::StringValue attribute value declarations.
a class to represent an Ipv4 address mask
Definition: ipv4-address.h:258
When using TAP devices, if flag IFF_NO_PI is not set on the device, IP packets will have an extra hea...
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:204
bool m_modePi
The TAP device flag IFF_NO_PI.
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:280
#define NS_FATAL_ERROR(msg)
Report a fatal error with a message and terminate.
Definition: fatal-error.h:162
void SetTapIpv4Address(Ipv4Address address)
Set the device IPv4 address.
static Mac48Address Allocate(void)
Allocate a new Mac48Address.
std::string BufferToString(uint8_t *buffer, uint32_t len)
Convert a byte buffer to a string containing a hex representation of the buffer.
static Ipv4Mask GetZero(void)
static Ipv6Address GetZero()
Get the 0 (::) Ipv6Address.
void SetTapMacAddress(Mac48Address mac)
Set the MAC address for the TAP device.
mac
Definition: third.py:99
#define TAP_MAGIC
TapFdNetDeviceHelper()
Construct a TapFdNetDeviceHelper.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
address
Definition: first.py:37
int m_tapPrefix6
The network prefix IPv6 for the TAP device.
an EUI-48 address
Definition: mac48-address.h:43
static Ipv4Address GetZero(void)
void SetTapIpv4Mask(Ipv4Mask mask)
Set the IPv4 network mask for the TAP device.
NS_LOG_LOGIC("Net device "<< nd<< " is not bridged")
Mac48Address m_tapMac
The TAP device MAC address.
virtual void SetFileDescriptor(Ptr< FdNetDevice > device) const
Sets a file descriptor on the FileDescriptorNetDevice.
Describes an IPv6 address.
Definition: ipv6-address.h:49
Ipv4 addresses are stored in host order in this class.
Definition: ipv4-address.h:40
Ptr< NetDevice > InstallPriv(Ptr< Node > node) const
This method creates an ns3::FdNetDevice attached to a virtual TAP network interface.
#define NS_ABORT_MSG_IF(cond, msg)
Abnormal program termination if a condition is true, with a message.
Definition: abort.h:108
void SetTapIpv6Prefix(int prefix)
Set the IPv6 network mask for the TAP device.
void SetTapIpv6Address(Ipv6Address address)
Set the device IPv6 address.
Ipv6Address m_tapIp6
The IPv6 address for the TAP device.
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition: log.h:272
a NetDevice to read/write network traffic from/into a file descriptor.
Definition: fd-net-device.h:84
Ipv4Mask m_tapMask4
The network mask IPv4 for the TAP device.
void SetModePi(bool pi)
Set flag IFF_NO_PI on the device.
std::string m_deviceName
The unix/linux name of the underlying device (e.g., eth0)