root / qemu-bridge-helper.c @ 7b93fadf
History | View | Annotate | Download (5.4 kB)
1 |
/*
|
---|---|
2 |
* QEMU Bridge Helper
|
3 |
*
|
4 |
* Copyright IBM, Corp. 2011
|
5 |
*
|
6 |
* Authors:
|
7 |
* Anthony Liguori <aliguori@us.ibm.com>
|
8 |
* Richa Marwaha <rmarwah@linux.vnet.ibm.com>
|
9 |
* Corey Bryant <coreyb@linux.vnet.ibm.com>
|
10 |
*
|
11 |
* This work is licensed under the terms of the GNU GPL, version 2. See
|
12 |
* the COPYING file in the top-level directory.
|
13 |
*
|
14 |
*/
|
15 |
|
16 |
#include "config-host.h" |
17 |
|
18 |
#include <stdio.h> |
19 |
#include <errno.h> |
20 |
#include <fcntl.h> |
21 |
#include <unistd.h> |
22 |
#include <string.h> |
23 |
#include <stdlib.h> |
24 |
#include <stdbool.h> |
25 |
#include <ctype.h> |
26 |
|
27 |
#include <sys/types.h> |
28 |
#include <sys/ioctl.h> |
29 |
#include <sys/socket.h> |
30 |
#include <sys/un.h> |
31 |
#include <sys/prctl.h> |
32 |
|
33 |
#include <net/if.h> |
34 |
|
35 |
#include <linux/sockios.h> |
36 |
|
37 |
#include "net/tap-linux.h" |
38 |
|
39 |
static void usage(void) |
40 |
{ |
41 |
fprintf(stderr, |
42 |
"Usage: qemu-bridge-helper [--use-vnet] --br=bridge --fd=unixfd\n");
|
43 |
} |
44 |
|
45 |
static bool has_vnet_hdr(int fd) |
46 |
{ |
47 |
unsigned int features = 0; |
48 |
|
49 |
if (ioctl(fd, TUNGETFEATURES, &features) == -1) { |
50 |
return false; |
51 |
} |
52 |
|
53 |
if (!(features & IFF_VNET_HDR)) {
|
54 |
return false; |
55 |
} |
56 |
|
57 |
return true; |
58 |
} |
59 |
|
60 |
static void prep_ifreq(struct ifreq *ifr, const char *ifname) |
61 |
{ |
62 |
memset(ifr, 0, sizeof(*ifr)); |
63 |
snprintf(ifr->ifr_name, IFNAMSIZ, "%s", ifname);
|
64 |
} |
65 |
|
66 |
static int send_fd(int c, int fd) |
67 |
{ |
68 |
char msgbuf[CMSG_SPACE(sizeof(fd))]; |
69 |
struct msghdr msg = {
|
70 |
.msg_control = msgbuf, |
71 |
.msg_controllen = sizeof(msgbuf),
|
72 |
}; |
73 |
struct cmsghdr *cmsg;
|
74 |
struct iovec iov;
|
75 |
char req[1] = { 0x00 }; |
76 |
|
77 |
cmsg = CMSG_FIRSTHDR(&msg); |
78 |
cmsg->cmsg_level = SOL_SOCKET; |
79 |
cmsg->cmsg_type = SCM_RIGHTS; |
80 |
cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
|
81 |
msg.msg_controllen = cmsg->cmsg_len; |
82 |
|
83 |
iov.iov_base = req; |
84 |
iov.iov_len = sizeof(req);
|
85 |
|
86 |
msg.msg_iov = &iov; |
87 |
msg.msg_iovlen = 1;
|
88 |
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
89 |
|
90 |
return sendmsg(c, &msg, 0); |
91 |
} |
92 |
|
93 |
int main(int argc, char **argv) |
94 |
{ |
95 |
struct ifreq ifr;
|
96 |
int fd, ctlfd, unixfd = -1; |
97 |
int use_vnet = 0; |
98 |
int mtu;
|
99 |
const char *bridge = NULL; |
100 |
char iface[IFNAMSIZ];
|
101 |
int index;
|
102 |
int ret = EXIT_SUCCESS;
|
103 |
|
104 |
/* parse arguments */
|
105 |
for (index = 1; index < argc; index++) { |
106 |
if (strcmp(argv[index], "--use-vnet") == 0) { |
107 |
use_vnet = 1;
|
108 |
} else if (strncmp(argv[index], "--br=", 5) == 0) { |
109 |
bridge = &argv[index][5];
|
110 |
} else if (strncmp(argv[index], "--fd=", 5) == 0) { |
111 |
unixfd = atoi(&argv[index][5]);
|
112 |
} else {
|
113 |
usage(); |
114 |
return EXIT_FAILURE;
|
115 |
} |
116 |
} |
117 |
|
118 |
if (bridge == NULL || unixfd == -1) { |
119 |
usage(); |
120 |
return EXIT_FAILURE;
|
121 |
} |
122 |
|
123 |
/* open a socket to use to control the network interfaces */
|
124 |
ctlfd = socket(AF_INET, SOCK_STREAM, 0);
|
125 |
if (ctlfd == -1) { |
126 |
fprintf(stderr, "failed to open control socket: %s\n", strerror(errno));
|
127 |
ret = EXIT_FAILURE; |
128 |
goto cleanup;
|
129 |
} |
130 |
|
131 |
/* open the tap device */
|
132 |
fd = open("/dev/net/tun", O_RDWR);
|
133 |
if (fd == -1) { |
134 |
fprintf(stderr, "failed to open /dev/net/tun: %s\n", strerror(errno));
|
135 |
ret = EXIT_FAILURE; |
136 |
goto cleanup;
|
137 |
} |
138 |
|
139 |
/* request a tap device, disable PI, and add vnet header support if
|
140 |
* requested and it's available. */
|
141 |
prep_ifreq(&ifr, "tap%d");
|
142 |
ifr.ifr_flags = IFF_TAP|IFF_NO_PI; |
143 |
if (use_vnet && has_vnet_hdr(fd)) {
|
144 |
ifr.ifr_flags |= IFF_VNET_HDR; |
145 |
} |
146 |
|
147 |
if (ioctl(fd, TUNSETIFF, &ifr) == -1) { |
148 |
fprintf(stderr, "failed to create tun device: %s\n", strerror(errno));
|
149 |
ret = EXIT_FAILURE; |
150 |
goto cleanup;
|
151 |
} |
152 |
|
153 |
/* save tap device name */
|
154 |
snprintf(iface, sizeof(iface), "%s", ifr.ifr_name); |
155 |
|
156 |
/* get the mtu of the bridge */
|
157 |
prep_ifreq(&ifr, bridge); |
158 |
if (ioctl(ctlfd, SIOCGIFMTU, &ifr) == -1) { |
159 |
fprintf(stderr, "failed to get mtu of bridge `%s': %s\n",
|
160 |
bridge, strerror(errno)); |
161 |
ret = EXIT_FAILURE; |
162 |
goto cleanup;
|
163 |
} |
164 |
|
165 |
/* save mtu */
|
166 |
mtu = ifr.ifr_mtu; |
167 |
|
168 |
/* set the mtu of the interface based on the bridge */
|
169 |
prep_ifreq(&ifr, iface); |
170 |
ifr.ifr_mtu = mtu; |
171 |
if (ioctl(ctlfd, SIOCSIFMTU, &ifr) == -1) { |
172 |
fprintf(stderr, "failed to set mtu of device `%s' to %d: %s\n",
|
173 |
iface, mtu, strerror(errno)); |
174 |
ret = EXIT_FAILURE; |
175 |
goto cleanup;
|
176 |
} |
177 |
|
178 |
/* add the interface to the bridge */
|
179 |
prep_ifreq(&ifr, bridge); |
180 |
ifr.ifr_ifindex = if_nametoindex(iface); |
181 |
|
182 |
if (ioctl(ctlfd, SIOCBRADDIF, &ifr) == -1) { |
183 |
fprintf(stderr, "failed to add interface `%s' to bridge `%s': %s\n",
|
184 |
iface, bridge, strerror(errno)); |
185 |
ret = EXIT_FAILURE; |
186 |
goto cleanup;
|
187 |
} |
188 |
|
189 |
/* bring the interface up */
|
190 |
prep_ifreq(&ifr, iface); |
191 |
if (ioctl(ctlfd, SIOCGIFFLAGS, &ifr) == -1) { |
192 |
fprintf(stderr, "failed to get interface flags for `%s': %s\n",
|
193 |
iface, strerror(errno)); |
194 |
ret = EXIT_FAILURE; |
195 |
goto cleanup;
|
196 |
} |
197 |
|
198 |
ifr.ifr_flags |= IFF_UP; |
199 |
if (ioctl(ctlfd, SIOCSIFFLAGS, &ifr) == -1) { |
200 |
fprintf(stderr, "failed to bring up interface `%s': %s\n",
|
201 |
iface, strerror(errno)); |
202 |
ret = EXIT_FAILURE; |
203 |
goto cleanup;
|
204 |
} |
205 |
|
206 |
/* write fd to the domain socket */
|
207 |
if (send_fd(unixfd, fd) == -1) { |
208 |
fprintf(stderr, "failed to write fd to unix socket: %s\n",
|
209 |
strerror(errno)); |
210 |
ret = EXIT_FAILURE; |
211 |
goto cleanup;
|
212 |
} |
213 |
|
214 |
/* ... */
|
215 |
|
216 |
/* profit! */
|
217 |
|
218 |
cleanup:
|
219 |
|
220 |
return ret;
|
221 |
} |