A Discrete-Event Network Simulator
API
tap-device-creator.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2009 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 
18 #include "creator-utils.h"
19 
20 #include "ns3/mac48-address.h"
21 
22 #include <arpa/inet.h>
23 #include <cstring>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <iomanip>
27 #include <iostream>
28 #include <linux/if_tun.h>
29 #include <net/if.h>
30 #include <net/route.h>
31 #include <netinet/in.h>
32 #include <sstream>
33 #include <stdint.h>
34 #include <stdlib.h>
35 #include <sys/ioctl.h>
36 #include <sys/socket.h>
37 #include <sys/types.h>
38 #include <sys/un.h>
39 #include <unistd.h>
40 
41 #define TAP_MAGIC 95549
42 
43 using namespace ns3;
44 
48 struct in6_ifreq
49 {
50  struct in6_addr ifr6_addr;
51  uint32_t ifr6_prefixlen;
52  int32_t ifr6_ifindex;
53 };
54 
55 void
56 SetIpv4(const char* deviceName, const char* ip, const char* netmask)
57 {
58  struct ifreq ifr;
59  struct sockaddr_in* sin;
60 
61  int sock = socket(AF_INET, SOCK_DGRAM, 0);
62 
63  //
64  // Set the IP address of the new interface/device.
65  //
66  memset(&ifr, 0, sizeof(struct ifreq));
67  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
68 
69  sin = (struct sockaddr_in*)&ifr.ifr_addr;
70  inet_pton(AF_INET, ip, &sin->sin_addr);
71  ifr.ifr_addr.sa_family = AF_INET;
72 
73  ABORT_IF(ioctl(sock, SIOCSIFADDR, &ifr) == -1, "Could not set IP address", true);
74 
75  LOG("Set device IP address to " << ip);
76 
77  //
78  // Set the net mask of the new interface/device
79  //
80  memset(&ifr, 0, sizeof(struct ifreq));
81  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
82 
83  sin = (struct sockaddr_in*)&ifr.ifr_netmask;
84  inet_pton(AF_INET, netmask, &sin->sin_addr);
85  ifr.ifr_addr.sa_family = AF_INET;
86 
87  ABORT_IF(ioctl(sock, SIOCSIFNETMASK, &ifr) == -1, "Could not set net mask", true);
88 
89  LOG("Set device Net Mask to " << netmask);
90  close(sock);
91 }
92 
93 void
94 SetIpv6(const char* deviceName, const char* ip, int netprefix)
95 {
96  struct ifreq ifr;
97  struct sockaddr_in6 sin;
98  struct in6_ifreq ifr6;
99 
100  int sock = socket(AF_INET6, SOCK_DGRAM, 0);
101  memset(&ifr, 0, sizeof(struct ifreq));
102  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
103 
104  ABORT_IF(ioctl(sock, SIOGIFINDEX, &ifr) == -1, "Could not get interface index", true);
105 
106  LOG("Set device IP v6 address to " << ip);
107 
108  memset(&sin, 0, sizeof(struct sockaddr_in6));
109  sin.sin6_family = AF_INET6;
110  inet_pton(AF_INET6, ip, (void*)&sin.sin6_addr);
111 
112  memset(&ifr6, 0, sizeof(in6_ifreq));
113  memcpy((char*)&ifr6.ifr6_addr, (char*)&sin.sin6_addr, sizeof(struct in6_addr));
114 
115  ifr6.ifr6_ifindex = ifr.ifr_ifindex;
116  ifr6.ifr6_prefixlen = netprefix;
117 
118  //
119  // Set the IP address of the new interface/device.
120  //
121  ABORT_IF(ioctl(sock, SIOCSIFADDR, &ifr6) == -1, "Could not set IP v6 address", true);
122 
123  LOG("Set device IP v6 address to " << ip);
124  close(sock);
125 }
126 
127 void
128 SetMacAddress(int fd, const char* mac)
129 {
130  struct ifreq ifr;
131  memset(&ifr, 0, sizeof(struct ifreq));
132 
133  ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
134  Mac48Address(mac).CopyTo((uint8_t*)ifr.ifr_hwaddr.sa_data);
135  ABORT_IF(ioctl(fd, SIOCSIFHWADDR, &ifr) == -1, "Could not set MAC address", true);
136  LOG("Set device MAC address to " << mac);
137 }
138 
139 void
140 SetUp(char* deviceName)
141 {
142  struct ifreq ifr;
143 
144  int sock = socket(AF_INET, SOCK_DGRAM, 0);
145 
146  memset(&ifr, 0, sizeof(struct ifreq));
147  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
148 
149  ABORT_IF(ioctl(sock, SIOCGIFFLAGS, &ifr) == -1, "Could not get flags for interface", true);
150  ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
151 
152  ABORT_IF(ioctl(sock, SIOCSIFFLAGS, &ifr) == -1, "Could not bring interface up", true);
153 
154  LOG("Device is up");
155  close(sock);
156 }
157 
158 int
159 CreateTap(char* deviceName,
160  const char* mac,
161  bool ifftap,
162  bool iffpi,
163  const char* ip4,
164  const char* netmask,
165  const char* ip6,
166  const int netprefix)
167 {
168  //
169  // Creation and management of Tap devices is done via the tun device
170  //
171  int fd = open("/dev/net/tun", O_RDWR);
172  ABORT_IF(fd == -1, "Could not open /dev/net/tun", true);
173 
174  //
175  // Set flags for device type and PI header.
176  //
177  struct ifreq ifr;
178 
179  memset(&ifr, 0, sizeof(struct ifreq));
180 
181  ifr.ifr_flags = (ifftap ? IFF_TAP : IFF_TUN);
182  if (!iffpi)
183  {
184  ifr.ifr_flags |= IFF_NO_PI;
185  }
186 
187  //
188  // If device name is not specified, the kernel chooses one.
189  //
190  if (*deviceName)
191  {
192  strncpy(ifr.ifr_name, deviceName, IFNAMSIZ - 1);
193  }
194 
195  ABORT_IF(ioctl(fd, TUNSETIFF, (void*)&ifr) == -1, "Could not allocate tap device", true);
196 
197  LOG("Allocated TAP device " << deviceName);
198 
199  //
200  // Set the hardware (MAC) address of the new device
201  //
202  if (ifftap)
203  {
204  SetMacAddress(fd, mac);
205  }
206 
207  //
208  // Set the IP address and netmask of the new interface/device.
209  //
210  if (ip4)
211  {
212  SetIpv4(deviceName, ip4, netmask);
213  }
214 
215  if (ip6)
216  {
217  SetIpv6(deviceName, ip6, netprefix);
218  }
219 
220  //
221  // Bring the interface up.
222  //
223  SetUp(deviceName);
224 
225  return fd;
226 }
227 
228 int
229 main(int argc, char* argv[])
230 {
231  int c;
232  char* dev = nullptr;
233  char* ip4 = nullptr;
234  char* ip6 = nullptr;
235  char* mac = nullptr;
236  char* netmask = nullptr;
237  char* path = nullptr;
238  bool tap = false;
239  bool pi = false;
240  int prefix = -1;
241 
242  while ((c = getopt(argc, argv, "vd:i:m:n:I:P:thp:")) != -1)
243  {
244  switch (c)
245  {
246  case 'd':
247  dev = optarg; // name of the new tap device
248  break;
249  case 'i':
250  ip4 = optarg; // ip v4 address of the new device
251  break;
252  case 'I':
253  ip6 = optarg; // ip v6 address of the new device
254  break;
255  case 'm':
256  mac = optarg; // mac address of the new device
257  break;
258  case 'n':
259  netmask = optarg; // ip v4 net mask for the new device
260  break;
261  case 'P':
262  prefix = atoi(optarg); // ip v6 prefix for the new device
263  break;
264  case 't':
265  tap = true; // mode for the device (TAP or TUN)
266  break;
267  case 'h':
268  pi = true; // set the IFF_NO_PI flag
269  break;
270  case 'p':
271  path = optarg; // path back to the tap bridge
272  break;
273  case 'v':
274  gVerbose = true;
275  break;
276  }
277  }
278 
279  //
280  // We have got to be able to coordinate the name of the tap device we are
281  // going to create and or open with the device that an external Linux host
282  // will use. If this name is provided we use it. If not we let the system
283  // create the device for us. This name is given in dev
284  //
285  LOG("Provided Device Name is \"" << dev << "\"");
286 
287  //
288  // We have got to be able to assign an IP address to the tap device we are
289  // allocating. This address is allocated in the simulation and assigned to
290  // the tap bridge. This address is given in ip.
291  //
292  ABORT_IF(ip4 == nullptr && ip6 == nullptr, "IP Address is a required argument", 0);
293  if (ip4)
294  {
295  ABORT_IF(netmask == nullptr, "Net mask is a required argument", 0);
296  LOG("Provided IP v4 Address is \"" << ip4 << "\"");
297  LOG("Provided IP v4 Net Mask is \"" << netmask << "\"");
298  }
299  if (ip6)
300  {
301  ABORT_IF(prefix == -1, "Prefix is a required argument", 0);
302  LOG("Provided IP v6 Address is \"" << ip6 << "\"");
303  LOG("Provided IP v6 Prefix is \"" << prefix << "\"");
304  }
305 
306  //
307  // We have got to be able to assign a Mac address to the tap device we are
308  // allocating. This address is allocated in the simulation and assigned to
309  // the bridged device. This allows packets addressed to the bridged device
310  // to appear in the Linux host as if they were received there.
311  //
312  ABORT_IF(mac == nullptr, "MAC Address is a required argument", 0);
313  LOG("Provided MAC Address is \"" << mac << "\"");
314 
315  //
316  // We have got to know whether or not to create the TAP.
317  //
318  if (tap)
319  {
320  LOG("Provided device Mode is TAP");
321  }
322  else
323  {
324  LOG("Provided device Mode is TUN");
325  }
326 
327  //
328  // IFF_NO_PI flag.
329  //
330  if (pi)
331  {
332  LOG("IFF_NO_PI flag set. Packet Information will be present in the traffic");
333  }
334 
335  //
336  // This program is spawned by a tap bridge running in a simulation. It
337  // wants to create a socket as described below. We are going to do the
338  // work here since we're running suid root. Once we create the socket,
339  // we have to send it back to the tap bridge. We do that over a Unix
340  // (local interprocess) socket. The tap bridge created a socket to
341  // listen for our response on, and it is expected to have encoded the address
342  // information as a string and to have passed that string as an argument to
343  // us. We see it here as the "path" string. We can't do anything useful
344  // unless we have that string.
345  //
346  ABORT_IF(path == nullptr, "path is a required argument", 0);
347  LOG("Provided path is \"" << path << "\"");
348 
349  //
350  // The whole reason for all of the hoops we went through to call out to this
351  // program will pay off here. We created this program to run as suid root
352  // in order to keep the main simulation program from having to be run with
353  // root privileges. We need root privileges to be able to futz with the
354  // Tap device underlying all of this. So all of these hoops are to allow
355  // us to execute the following code:
356  //
357  LOG("Creating Tap");
358  int sock = CreateTap(dev, mac, tap, pi, ip4, netmask, ip6, prefix);
359  ABORT_IF(sock == -1, "main(): Unable to create tap socket", 1);
360 
361  //
362  // Send the socket back to the tap net device so it can go about its business
363  //
364  SendSocket(path, sock, TAP_MAGIC);
365 
366  return 0;
367 }
#define LOG(x)
Log to std::cout.
an EUI-48 address
Definition: mac48-address.h:46
void CopyTo(uint8_t buffer[6]) const
#define ABORT_IF(cond, msg, printErrno)
Definition: creator-utils.h:51
void SendSocket(const char *path, int fd, const int magic_number)
Send the file descriptor back to the code that invoked the creation.
Every class exported by the ns3 library is enclosed in the ns3 namespace.
bool gVerbose
Flag to enable / disable verbose log mode.
mac
Definition: third.py:92
Struct holding IPv6 address data.
int32_t ifr6_ifindex
interface index
uint32_t ifr6_prefixlen
IPv6 prefix length.
struct in6_addr ifr6_addr
IPv6 address.
void SetIpv6(const char *deviceName, const char *ip, int netprefix)
void SetIpv4(const char *deviceName, const char *ip, const char *netmask)
void SetUp(char *deviceName)
int CreateTap(char *deviceName, const char *mac, bool ifftap, bool iffpi, const char *ip4, const char *netmask, const char *ip6, const int netprefix)
void SetMacAddress(int fd, const char *mac)
#define TAP_MAGIC