A Discrete-Event Network Simulator
API
tap-creator.cc
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2009 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
19#include <unistd.h>
20#include <stdint.h>
21#include <string>
22#include <cstring> // for strerror
23#include <iostream>
24#include <iomanip>
25#include <sstream>
26#include <cstdlib>
27#include <cerrno>
28#include <fcntl.h>
29#include <sys/ioctl.h>
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <net/if.h>
34#include <linux/if_tun.h>
35#include <net/route.h>
36#include <netinet/in.h>
37
38#include "tap-encode-decode.h"
39
40#define TAP_MAGIC 95549
41
42static int gVerbose = 0; // Set to true to turn on logging messages.
43
44#define LOG(msg) \
45 if (gVerbose) \
46 { \
47 std::cout << __FUNCTION__ << "(): " << msg << std::endl; \
48 }
49
50#define ABORT(msg, printErrno) \
51 std::cout << __FILE__ << ": fatal error at line " << __LINE__ << ": " << __FUNCTION__ << "(): " << msg << std::endl; \
52 if (printErrno) \
53 { \
54 std::cout << " errno = " << errno << " (" << std::strerror (errno) << ")" << std::endl; \
55 } \
56 std::exit (-1);
57
58#define ABORT_IF(cond, msg, printErrno) \
59 if (cond) \
60 { \
61 ABORT (msg, printErrno); \
62 }
63
64//
65// Lots of the following helper code taken from corresponding functions in src/node.
66//
67#define ASCII_DOT (0x2e)
68#define ASCII_ZERO (0x30)
69#define ASCII_a (0x41)
70#define ASCII_z (0x5a)
71#define ASCII_A (0x61)
72#define ASCII_Z (0x7a)
73#define ASCII_COLON (0x3a)
74#define ASCII_ZERO (0x30)
75
76static char
78{
79 if (c >= ASCII_a && c <= ASCII_z) {
80 return c;
81 } else if (c >= ASCII_A && c <= ASCII_Z) {
82 return c + (ASCII_a - ASCII_A);
83 } else {
84 return c;
85 }
86}
87
88static uint32_t
89AsciiToIpv4 (const char *address)
90{
91 uint32_t host = 0;
92 while (true) {
93 uint8_t byte = 0;
94 while (*address != ASCII_DOT &&
95 *address != 0) {
96 byte *= 10;
97 byte += *address - ASCII_ZERO;
98 address++;
99 }
100 host <<= 8;
101 host |= byte;
102 if (*address == 0) {
103 break;
104 }
105 address++;
106 }
107 return host;
108}
109
110static void
111AsciiToMac48 (const char *str, uint8_t addr[6])
112{
113 int i = 0;
114 while (*str != 0 && i < 6)
115 {
116 uint8_t byte = 0;
117 while (*str != ASCII_COLON && *str != 0)
118 {
119 byte <<= 4;
120 char low = AsciiToLowCase (*str);
121 if (low >= ASCII_a)
122 {
123 byte |= low - ASCII_a + 10;
124 }
125 else
126 {
127 byte |= low - ASCII_ZERO;
128 }
129 str++;
130 }
131 addr[i] = byte;
132 i++;
133 if (*str == 0)
134 {
135 break;
136 }
137 str++;
138 }
139}
140
141static sockaddr
143{
144 union {
145 struct sockaddr any_socket;
146 struct sockaddr_in si;
147 } s;
148 s.si.sin_family = AF_INET;
149 s.si.sin_port = 0; // unused
150 s.si.sin_addr.s_addr = htonl (networkOrder);
151 return s.any_socket;
152}
153
154static void
155SendSocket (const char *path, int fd)
156{
157 //
158 // Open a Unix (local interprocess) socket to call back to the tap bridge
159 //
160 LOG ("Create Unix socket");
161 int sock = socket (PF_UNIX, SOCK_DGRAM, 0);
162 ABORT_IF (sock == -1, "Unable to open socket", 1);
163
164 //
165 // We have this string called path, which is really a hex representation
166 // of the endpoint that the tap bridge created. It used a forward encoding
167 // method (TapBufferToString) to take the sockaddr_un it made and passed
168 // the resulting string to us. So we need to take the inverse method
169 // (TapStringToBuffer) and build the same sockaddr_un over here.
170 //
171 socklen_t clientAddrLen;
172 struct sockaddr_un clientAddr;
173
174 LOG ("Decode address " << path);
175 bool rc = ns3::TapStringToBuffer (path, (uint8_t *)&clientAddr, &clientAddrLen);
176 ABORT_IF (rc == false, "Unable to decode path", 0);
177
178 LOG ("Connect");
179 int status = connect (sock, (struct sockaddr*)&clientAddr, clientAddrLen);
180 ABORT_IF (status == -1, "Unable to connect to tap bridge", 1);
181
182 LOG ("Connected");
183
184 //
185 // This is arcane enough that a few words are worthwhile to explain what's
186 // going on here.
187 //
188 // The interesting information (the socket FD) is going to go back to the
189 // tap bridge as an integer of ancillary data. Ancillary data is bits
190 // that are not a part a socket payload (out-of-band data). We're also
191 // going to send one integer back. It's just initialized to a magic number
192 // we use to make sure that the tap bridge is talking to the tap socket
193 // creator and not some other creator process (emu, specifically)
194 //
195 // The struct iovec below is part of a scatter-gather list. It describes a
196 // buffer. In this case, it describes a buffer (an integer) containing the
197 // data that we're going to send back to the tap bridge (that magic number).
198 //
199 struct iovec iov;
200 uint32_t magic = TAP_MAGIC;
201 iov.iov_base = &magic;
202 iov.iov_len = sizeof(magic);
203
204 //
205 // The CMSG macros you'll see below are used to create and access control
206 // messages (which is another name for ancillary data). The ancillary
207 // data is made up of pairs of struct cmsghdr structures and associated
208 // data arrays.
209 //
210 // First, we're going to allocate a buffer on the stack to contain our
211 // data array (that contains the socket). Sometimes you'll see this called
212 // an "ancillary element" but the msghdr uses the control message termimology
213 // so we call it "control."
214 //
215 size_t msg_size = sizeof(int);
216 char control[CMSG_SPACE (msg_size)];
217
218 //
219 // There is a msghdr that is used to minimize the number of parameters
220 // passed to sendmsg (which we will use to send our ancillary data). This
221 // structure uses terminology corresponding to control messages, so you'll
222 // see msg_control, which is the pointer to the ancillary data and controllen
223 // which is the size of the ancillary data array.
224 //
225 // So, initialize the message header that describes our ancillary/control data
226 // and point it to the control message/ancillary data we just allocated space
227 // for.
228 //
229 struct msghdr msg;
230 msg.msg_name = 0;
231 msg.msg_namelen = 0;
232 msg.msg_iov = &iov;
233 msg.msg_iovlen = 1;
234 msg.msg_control = control;
235 msg.msg_controllen = sizeof (control);
236 msg.msg_flags = 0;
237
238 //
239 // A cmsghdr contains a length field that is the length of the header and
240 // the data. It has a cmsg_level field corresponding to the originating
241 // protocol. This takes values which are legal levels for getsockopt and
242 // setsockopt (here SOL_SOCKET). We're going to use the SCM_RIGHTS type of
243 // cmsg, that indicates that the ancillary data array contains access rights
244 // that we are sending back to the tap bridge.
245 //
246 // We have to put together the first (and only) cmsghdr that will describe
247 // the whole package we're sending.
248 //
249 struct cmsghdr *cmsg;
250 cmsg = CMSG_FIRSTHDR (&msg);
251 cmsg->cmsg_level = SOL_SOCKET;
252 cmsg->cmsg_type = SCM_RIGHTS;
253 cmsg->cmsg_len = CMSG_LEN (msg_size);
254 //
255 // We also have to update the controllen in case other stuff is actually
256 // in there we may not be aware of (due to macros).
257 //
258 msg.msg_controllen = cmsg->cmsg_len;
259
260 //
261 // Finally, we get a pointer to the start of the ancillary data array and
262 // put our file descriptor in.
263 //
264 int *fdptr = (int*)(CMSG_DATA (cmsg));
265 *fdptr = fd; //
266
267 //
268 // Actually send the file descriptor back to the tap bridge.
269 //
270 ssize_t len = sendmsg (sock, &msg, 0);
271 ABORT_IF (len == -1, "Could not send socket back to tap bridge", 1);
272
273 LOG ("sendmsg complete");
274}
275
276static int
277CreateTap (const char *dev, const char *gw, const char *ip, const char *mac, const char *mode, const char *netmask)
278{
279 //
280 // Creation and management of Tap devices is done via the tun device
281 //
282 int tap = open ("/dev/net/tun", O_RDWR);
283 ABORT_IF (tap == -1, "Could not open /dev/net/tun", true);
284
285 //
286 // Allocate a tap device, making sure that it will not send the tun_pi header.
287 // If we provide a null name to the ifr.ifr_name, we tell the kernel to pick
288 // a name for us (i.e., tapn where n = 0..255.
289 //
290 // If the device does not already exist, the system will create one.
291 //
292 struct ifreq ifr;
293 ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
294 strcpy (ifr.ifr_name, dev);
295 int status = ioctl (tap, TUNSETIFF, (void *) &ifr);
296 ABORT_IF (status == -1, "Could not allocate tap device", true);
297
298 std::string tapDeviceName = (char *)ifr.ifr_name;
299 LOG ("Allocated TAP device " << tapDeviceName);
300
301 //
302 // Operating mode "2" corresponds to USE_LOCAL and "3" to USE_BRIDGE mode.
303 // This means that we expect that the user will have named, created and
304 // configured a network tap that we are just going to use. So don't mess
305 // up his hard work by changing anything, just return the tap fd.
306 //
307 if (strcmp (mode, "2") == 0 || strcmp (mode, "3") == 0)
308 {
309 LOG ("Returning precreated tap ");
310 return tap;
311 }
312
313 //
314 // Set the hardware (MAC) address of the new device
315 //
316 ifr.ifr_hwaddr.sa_family = 1; // this is ARPHRD_ETHER from if_arp.h
317 AsciiToMac48 (mac, (uint8_t*)ifr.ifr_hwaddr.sa_data);
318 status = ioctl (tap, SIOCSIFHWADDR, &ifr);
319 ABORT_IF (status == -1, "Could not set MAC address", true);
320 LOG ("Set device MAC address to " << mac);
321
322 int fd = socket (AF_INET, SOCK_DGRAM, 0);
323
324 //
325 // Bring the interface up.
326 //
327 status = ioctl (fd, SIOCGIFFLAGS, &ifr);
328 ABORT_IF (status == -1, "Could not get flags for interface", true);
329 ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
330 status = ioctl (fd, SIOCSIFFLAGS, &ifr);
331 ABORT_IF (status == -1, "Could not bring interface up", true);
332 LOG ("Device is up");
333
334 //
335 // Set the IP address of the new interface/device.
336 //
337 ifr.ifr_addr = CreateInetAddress (AsciiToIpv4 (ip));
338 status = ioctl (fd, SIOCSIFADDR, &ifr);
339 ABORT_IF (status == -1, "Could not set IP address", true);
340 LOG ("Set device IP address to " << ip);
341
342 //
343 // Set the net mask of the new interface/device
344 //
345 ifr.ifr_netmask = CreateInetAddress (AsciiToIpv4 (netmask));
346 status = ioctl (fd, SIOCSIFNETMASK, &ifr);
347 ABORT_IF (status == -1, "Could not set net mask", true);
348 LOG ("Set device Net Mask to " << netmask);
349
350 return tap;
351}
352
353int
354main (int argc, char *argv[])
355{
356 int c;
357 char *dev = (char *)"";
358 char *gw = NULL;
359 char *ip = NULL;
360 char *mac = NULL;
361 char *netmask = NULL;
362 char *operatingMode = NULL;
363 char *path = NULL;
364
365 opterr = 0;
366
367 while ((c = getopt (argc, argv, "vd:g:i:m:n:o:p:")) != -1)
368 {
369 switch (c)
370 {
371 case 'd':
372 dev = optarg; // name of the new tap device
373 break;
374 case 'g':
375 gw = optarg; // gateway address for the new device
376 break;
377 case 'i':
378 ip = optarg; // ip address of the new device
379 break;
380 case 'm':
381 mac = optarg; // mac address of the new device
382 break;
383 case 'n':
384 netmask = optarg; // net mask for the new device
385 break;
386 case 'o':
387 operatingMode = optarg; // operating mode of tap bridge
388 break;
389 case 'p':
390 path = optarg; // path back to the tap bridge
391 break;
392 case 'v':
393 gVerbose = true;
394 break;
395 }
396 }
397
398 //
399 // We have got to be able to coordinate the name of the tap device we are
400 // going to create and or open with the device that an external Linux host
401 // will use. If this name is provided we use it. If not we let the system
402 // create the device for us. This name is given in dev
403 //
404 LOG ("Provided Device Name is \"" << dev << "\"");
405
406 //
407 // We have got to be able to provide a gateway to the external Linux host
408 // so it can talk to the ns-3 network. This ip address is provided in
409 // gw.
410 //
411 ABORT_IF (gw == NULL, "Gateway Address is a required argument", 0);
412 LOG ("Provided Gateway Address is \"" << gw << "\"");
413
414 //
415 // We have got to be able to assign an IP address to the tap device we are
416 // allocating. This address is allocated in the simulation and assigned to
417 // the tap bridge. This address is given in ip.
418 //
419 ABORT_IF (ip == NULL, "IP Address is a required argument", 0);
420 LOG ("Provided IP Address is \"" << ip << "\"");
421
422 //
423 // We have got to be able to assign a Mac address to the tap device we are
424 // allocating. This address is allocated in the simulation and assigned to
425 // the bridged device. This allows packets addressed to the bridged device
426 // to appear in the Linux host as if they were received there.
427 //
428 ABORT_IF (mac == NULL, "MAC Address is a required argument", 0);
429 LOG ("Provided MAC Address is \"" << mac << "\"");
430
431 //
432 // We have got to be able to assign a net mask to the tap device we are
433 // allocating. This mask is allocated in the simulation and given to
434 // the bridged device.
435 //
436 ABORT_IF (netmask == NULL, "Net Mask is a required argument", 0);
437 LOG ("Provided Net Mask is \"" << netmask << "\"");
438
439 //
440 // We have got to know whether or not to create the TAP.
441 //
442 ABORT_IF (operatingMode == NULL, "Operating Mode is a required argument", 0);
443 LOG ("Provided Operating Mode is \"" << operatingMode << "\"");
444
445 //
446 // This program is spawned by a tap bridge running in a simulation. It
447 // wants to create a socket as described below. We are going to do the
448 // work here since we're running suid root. Once we create the socket,
449 // we have to send it back to the tap bridge. We do that over a Unix
450 // (local interprocess) socket. The tap bridge created a socket to
451 // listen for our response on, and it is expected to have encoded the address
452 // information as a string and to have passed that string as an argument to
453 // us. We see it here as the "path" string. We can't do anything useful
454 // unless we have that string.
455 //
456 ABORT_IF (path == NULL, "path is a required argument", 0);
457 LOG ("Provided path is \"" << path << "\"");
458
459 //
460 // The whole reason for all of the hoops we went through to call out to this
461 // program will pay off here. We created this program to run as suid root
462 // in order to keep the main simulation program from having to be run with
463 // root privileges. We need root privileges to be able to futz with the
464 // Tap device underlying all of this. So all of these hoops are to allow
465 // us to exeucte the following code:
466 //
467 LOG ("Creating Tap");
468 int sock = CreateTap (dev, gw, ip, mac, operatingMode, netmask);
469 ABORT_IF (sock == -1, "main(): Unable to create tap socket", 1);
470
471 //
472 // Send the socket back to the tap net device so it can go about its business
473 //
474 SendSocket (path, sock);
475
476 return 0;
477}
address
Definition: first.py:44
bool TapStringToBuffer(std::string s, uint8_t *buffer, uint32_t *len)
Convert string encoded by the inverse function (TapBufferToString) back into a byte buffer.
mac
Definition: third.py:96
static void SendSocket(const char *path, int fd)
Definition: tap-creator.cc:155
#define LOG(msg)
Definition: tap-creator.cc:44
#define ASCII_Z
Definition: tap-creator.cc:72
static sockaddr CreateInetAddress(uint32_t networkOrder)
Definition: tap-creator.cc:142
#define ASCII_A
Definition: tap-creator.cc:71
#define ASCII_DOT
Definition: tap-creator.cc:67
#define ASCII_z
Definition: tap-creator.cc:70
static int gVerbose
Definition: tap-creator.cc:42
#define ABORT_IF(cond, msg, printErrno)
Definition: tap-creator.cc:58
#define ASCII_COLON
Definition: tap-creator.cc:73
#define ASCII_ZERO
Definition: tap-creator.cc:74
static uint32_t AsciiToIpv4(const char *address)
Definition: tap-creator.cc:89
static int CreateTap(const char *dev, const char *gw, const char *ip, const char *mac, const char *mode, const char *netmask)
Definition: tap-creator.cc:277
static char AsciiToLowCase(char c)
Definition: tap-creator.cc:77
#define ASCII_a
Definition: tap-creator.cc:69
static void AsciiToMac48(const char *str, uint8_t addr[6])
Definition: tap-creator.cc:111
#define TAP_MAGIC
Definition: tap-creator.cc:40