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