A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
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
53using namespace ns3;
54
59{
60 struct in6_addr ifr6_addr;
63};
64
65char
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
82void
83AsciiToMac48(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
113void
114SetIpv4(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
151void
152SetIpv6(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
185void
186SetMacAddress(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
197void
198SetUp(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
216int
217CreateTap(char* deviceName,
218 const char* mac,
219 bool ifftap,
220 bool 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
286int
287main(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 bool tap = false;
297 bool 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.
ns 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)
int CreateTap(char *deviceName, const char *mac, bool ifftap, bool iffpi, const char *ip4, const char *netmask, const char *ip6, const int netprefix)
char AsciiToLowCase(char c)
#define ASCII_ZERO
#define ASCII_a
void SetMacAddress(int fd, const char *mac)
#define TAP_MAGIC