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 "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
43using namespace ns3;
44
45/**
46 * Struct holding IPv6 address data
47 */
49{
50 struct in6_addr ifr6_addr; //!< IPv6 address
51 uint32_t ifr6_prefixlen; //!< IPv6 prefix length
52 int32_t ifr6_ifindex; //!< interface index
53};
54
55void
56SetIpv4(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
93void
94SetIpv6(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
127void
128SetMacAddress(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
139void
140SetUp(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
158int
159CreateTap(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
228int
229main(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.
ns 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