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