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