summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure2
-rw-r--r--net.c25
-rw-r--r--net.h3
-rw-r--r--net/tap.c204
-rw-r--r--net/tap.h3
-rw-r--r--qemu-options.hx73
6 files changed, 293 insertions, 17 deletions
diff --git a/configure b/configure
index 9f2a7e19d3..763db24cd0 100755
--- a/configure
+++ b/configure
@@ -2942,6 +2942,8 @@ echo "sysconfdir=$sysconfdir" >> $config_host_mak
echo "docdir=$docdir" >> $config_host_mak
echo "confdir=$confdir" >> $config_host_mak
echo "libexecdir=\${prefix}/libexec" >> $config_host_mak
+echo "CONFIG_QEMU_SHAREDIR=\"$prefix$datasuffix\"" >> $config_host_mak
+echo "CONFIG_QEMU_HELPERDIR=\"$prefix/libexec\"" >> $config_host_mak
echo "ARCH=$ARCH" >> $config_host_mak
if test "$debug_tcg" = "yes" ; then
diff --git a/net.c b/net.c
index 2b0f766408..1944539050 100644
--- a/net.c
+++ b/net.c
@@ -952,6 +952,10 @@ static const struct {
.type = QEMU_OPT_STRING,
.help = "script to shut down the interface",
}, {
+ .name = "helper",
+ .type = QEMU_OPT_STRING,
+ .help = "command to execute to configure bridge",
+ }, {
.name = "sndbuf",
.type = QEMU_OPT_SIZE,
.help = "send buffer limit"
@@ -1053,6 +1057,23 @@ static const struct {
{ /* end of list */ }
},
},
+ [NET_CLIENT_TYPE_BRIDGE] = {
+ .type = "bridge",
+ .init = net_init_bridge,
+ .desc = {
+ NET_COMMON_PARAMS_DESC,
+ {
+ .name = "br",
+ .type = QEMU_OPT_STRING,
+ .help = "bridge name",
+ }, {
+ .name = "helper",
+ .type = QEMU_OPT_STRING,
+ .help = "command to execute to configure bridge",
+ },
+ { /* end of list */ }
+ },
+ },
};
int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
@@ -1075,7 +1096,8 @@ int net_client_init(Monitor *mon, QemuOpts *opts, int is_netdev)
#ifdef CONFIG_VDE
strcmp(type, "vde") != 0 &&
#endif
- strcmp(type, "socket") != 0) {
+ strcmp(type, "socket") != 0 &&
+ strcmp(type, "bridge") != 0) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "type",
"a netdev backend type");
return -1;
@@ -1145,6 +1167,7 @@ static int net_host_check_device(const char *device)
#ifdef CONFIG_VDE
,"vde"
#endif
+ , "bridge"
};
for (i = 0; i < sizeof(valid_param_list) / sizeof(char *); i++) {
if (!strncmp(valid_param_list[i], device,
diff --git a/net.h b/net.h
index eab089d577..75a8c155e5 100644
--- a/net.h
+++ b/net.h
@@ -37,6 +37,7 @@ typedef enum {
NET_CLIENT_TYPE_SOCKET,
NET_CLIENT_TYPE_VDE,
NET_CLIENT_TYPE_DUMP,
+ NET_CLIENT_TYPE_BRIDGE,
NET_CLIENT_TYPE_MAX
} net_client_type;
@@ -174,6 +175,8 @@ int do_netdev_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
#define DEFAULT_NETWORK_SCRIPT "/etc/qemu-ifup"
#define DEFAULT_NETWORK_DOWN_SCRIPT "/etc/qemu-ifdown"
+#define DEFAULT_BRIDGE_HELPER CONFIG_QEMU_HELPERDIR "/qemu-bridge-helper"
+#define DEFAULT_BRIDGE_INTERFACE "br0"
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd);
diff --git a/net/tap.c b/net/tap.c
index 6c27a9427f..f2400283c3 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -382,6 +382,171 @@ static int launch_script(const char *setup_script, const char *ifname, int fd)
return -1;
}
+static int recv_fd(int c)
+{
+ int fd;
+ uint8_t msgbuf[CMSG_SPACE(sizeof(fd))];
+ struct msghdr msg = {
+ .msg_control = msgbuf,
+ .msg_controllen = sizeof(msgbuf),
+ };
+ struct cmsghdr *cmsg;
+ struct iovec iov;
+ uint8_t req[1];
+ ssize_t len;
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ iov.iov_base = req;
+ iov.iov_len = sizeof(req);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ len = recvmsg(c, &msg, 0);
+ if (len > 0) {
+ memcpy(&fd, CMSG_DATA(cmsg), sizeof(fd));
+ return fd;
+ }
+
+ return len;
+}
+
+static int net_bridge_run_helper(const char *helper, const char *bridge)
+{
+ sigset_t oldmask, mask;
+ int pid, status;
+ char *args[5];
+ char **parg;
+ int sv[2];
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+
+ if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) {
+ return -1;
+ }
+
+ /* try to launch bridge helper */
+ pid = fork();
+ if (pid == 0) {
+ int open_max = sysconf(_SC_OPEN_MAX), i;
+ char fd_buf[6+10];
+ char br_buf[6+IFNAMSIZ] = {0};
+ char helper_cmd[PATH_MAX + sizeof(fd_buf) + sizeof(br_buf) + 15];
+
+ for (i = 0; i < open_max; i++) {
+ if (i != STDIN_FILENO &&
+ i != STDOUT_FILENO &&
+ i != STDERR_FILENO &&
+ i != sv[1]) {
+ close(i);
+ }
+ }
+
+ snprintf(fd_buf, sizeof(fd_buf), "%s%d", "--fd=", sv[1]);
+
+ if (strrchr(helper, ' ') || strrchr(helper, '\t')) {
+ /* assume helper is a command */
+
+ if (strstr(helper, "--br=") == NULL) {
+ snprintf(br_buf, sizeof(br_buf), "%s%s", "--br=", bridge);
+ }
+
+ snprintf(helper_cmd, sizeof(helper_cmd), "%s %s %s %s",
+ helper, "--use-vnet", fd_buf, br_buf);
+
+ parg = args;
+ *parg++ = (char *)"sh";
+ *parg++ = (char *)"-c";
+ *parg++ = helper_cmd;
+ *parg++ = NULL;
+
+ execv("/bin/sh", args);
+ } else {
+ /* assume helper is just the executable path name */
+
+ snprintf(br_buf, sizeof(br_buf), "%s%s", "--br=", bridge);
+
+ parg = args;
+ *parg++ = (char *)helper;
+ *parg++ = (char *)"--use-vnet";
+ *parg++ = fd_buf;
+ *parg++ = br_buf;
+ *parg++ = NULL;
+
+ execv(helper, args);
+ }
+ _exit(1);
+
+ } else if (pid > 0) {
+ int fd;
+
+ close(sv[1]);
+
+ do {
+ fd = recv_fd(sv[0]);
+ } while (fd == -1 && errno == EINTR);
+
+ close(sv[0]);
+
+ while (waitpid(pid, &status, 0) != pid) {
+ /* loop */
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ if (fd < 0) {
+ fprintf(stderr, "failed to recv file descriptor\n");
+ return -1;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ return fd;
+ }
+ }
+ fprintf(stderr, "failed to launch bridge helper\n");
+ return -1;
+}
+
+int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name,
+ VLANState *vlan)
+{
+ TAPState *s;
+ int fd, vnet_hdr;
+
+ if (!qemu_opt_get(opts, "br")) {
+ qemu_opt_set(opts, "br", DEFAULT_BRIDGE_INTERFACE);
+ }
+ if (!qemu_opt_get(opts, "helper")) {
+ qemu_opt_set(opts, "helper", DEFAULT_BRIDGE_HELPER);
+ }
+
+ fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
+ qemu_opt_get(opts, "br"));
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ s = net_tap_fd_init(vlan, "bridge", name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
+
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s,br=%s",
+ qemu_opt_get(opts, "helper"), qemu_opt_get(opts, "br"));
+
+ return 0;
+}
+
static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
{
int fd, vnet_hdr_required;
@@ -422,13 +587,16 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
{
TAPState *s;
int fd, vnet_hdr = 0;
+ const char *model;
if (qemu_opt_get(opts, "fd")) {
if (qemu_opt_get(opts, "ifname") ||
qemu_opt_get(opts, "script") ||
qemu_opt_get(opts, "downscript") ||
- qemu_opt_get(opts, "vnet_hdr")) {
- error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=");
+ qemu_opt_get(opts, "vnet_hdr") ||
+ qemu_opt_get(opts, "helper")) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "and helper= are invalid with fd=");
return -1;
}
@@ -440,6 +608,31 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
fcntl(fd, F_SETFL, O_NONBLOCK);
vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ model = "tap";
+
+ } else if (qemu_opt_get(opts, "helper")) {
+ if (qemu_opt_get(opts, "ifname") ||
+ qemu_opt_get(opts, "script") ||
+ qemu_opt_get(opts, "downscript") ||
+ qemu_opt_get(opts, "vnet_hdr")) {
+ error_report("ifname=, script=, downscript=, and vnet_hdr= "
+ "are invalid with helper=");
+ return -1;
+ }
+
+ fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
+ DEFAULT_BRIDGE_INTERFACE);
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ model = "bridge";
+
} else {
if (!qemu_opt_get(opts, "script")) {
qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
@@ -453,9 +646,11 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
if (fd == -1) {
return -1;
}
+
+ model = "tap";
}
- s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
+ s = net_tap_fd_init(vlan, model, name, fd, vnet_hdr);
if (!s) {
close(fd);
return -1;
@@ -467,6 +662,9 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
if (qemu_opt_get(opts, "fd")) {
snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
+ } else if (qemu_opt_get(opts, "helper")) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str),
+ "helper=%s", qemu_opt_get(opts, "helper"));
} else {
const char *ifname, *script, *downscript;
diff --git a/net/tap.h b/net/tap.h
index e44bd2bc5f..56c591fc8e 100644
--- a/net/tap.h
+++ b/net/tap.h
@@ -57,4 +57,7 @@ int tap_get_fd(VLANClientState *vc);
struct vhost_net;
struct vhost_net *tap_get_vhost_net(VLANClientState *vc);
+int net_init_bridge(QemuOpts *opts, Monitor *mon, const char *name,
+ VLANState *vlan);
+
#endif /* QEMU_NET_TAP_H */
diff --git a/qemu-options.hx b/qemu-options.hx
index 3a07ae8231..19906e57e1 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1222,11 +1222,14 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
"-net tap[,vlan=n][,name=str],ifname=name\n"
" connect the host TAP network interface to VLAN 'n'\n"
#else
- "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n"
- " connect the host TAP network interface to VLAN 'n' and use the\n"
- " network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
- " and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
+ "-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]\n"
+ " connect the host TAP network interface to VLAN 'n' \n"
+ " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n"
+ " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n"
+ " to deconfigure it\n"
" use '[down]script=no' to disable script execution\n"
+ " use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER ") to\n"
+ " configure it\n"
" use 'fd=h' to connect to an already opened TAP interface\n"
" use 'sndbuf=nbytes' to limit the size of the send buffer (the\n"
" default is disabled 'sndbuf=0' to enable flow control set 'sndbuf=1048576')\n"
@@ -1236,6 +1239,10 @@ DEF("net", HAS_ARG, QEMU_OPTION_net,
" (only has effect for virtio guests which use MSIX)\n"
" use vhostforce=on to force vhost on for non-MSIX virtio guests\n"
" use 'vhostfd=h' to connect to an already opened vhost net device\n"
+ "-net bridge[,vlan=n][,name=str][,br=bridge][,helper=helper]\n"
+ " connects a host TAP network interface to a host bridge device 'br'\n"
+ " (default=" DEFAULT_BRIDGE_INTERFACE ") using the program 'helper'\n"
+ " (default=" DEFAULT_BRIDGE_HELPER ")\n"
#endif
"-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port]\n"
" connect the vlan 'n' to another VLAN using a socket connection\n"
@@ -1261,6 +1268,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev,
"user|"
#endif
"tap|"
+ "bridge|"
#ifdef CONFIG_VDE
"vde|"
#endif
@@ -1397,26 +1405,65 @@ processed and applied to -net user. Mixing them with the new configuration
syntax gives undefined results. Their use for new applications is discouraged
as they will be removed from future versions.
-@item -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}] [,script=@var{file}][,downscript=@var{dfile}]
-Connect the host TAP network interface @var{name} to VLAN @var{n}, use
-the network script @var{file} to configure it and the network script
+@item -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,helper=@var{helper}]
+Connect the host TAP network interface @var{name} to VLAN @var{n}.
+
+Use the network script @var{file} to configure it and the network script
@var{dfile} to deconfigure it. If @var{name} is not provided, the OS
-automatically provides one. @option{fd}=@var{h} can be used to specify
-the handle of an already opened host TAP interface. The default network
-configure script is @file{/etc/qemu-ifup} and the default network
-deconfigure script is @file{/etc/qemu-ifdown}. Use @option{script=no}
-or @option{downscript=no} to disable script execution. Example:
+automatically provides one. The default network configure script is
+@file{/etc/qemu-ifup} and the default network deconfigure script is
+@file{/etc/qemu-ifdown}. Use @option{script=no} or @option{downscript=no}
+to disable script execution.
+
+If running QEMU as an unprivileged user, use the network helper
+@var{helper} to configure the TAP interface. The default network
+helper executable is @file{/usr/local/libexec/qemu-bridge-helper}.
+
+@option{fd}=@var{h} can be used to specify the handle of an already
+opened host TAP interface.
+
+Examples:
@example
+#launch a QEMU instance with the default network script
qemu linux.img -net nic -net tap
@end example
-More complicated example (two NICs, each one connected to a TAP device)
@example
+#launch a QEMU instance with two NICs, each one connected
+#to a TAP device
qemu linux.img -net nic,vlan=0 -net tap,vlan=0,ifname=tap0 \
-net nic,vlan=1 -net tap,vlan=1,ifname=tap1
@end example
+@example
+#launch a QEMU instance with the default network helper to
+#connect a TAP device to bridge br0
+qemu linux.img -net nic -net tap,"helper=/usr/local/libexec/qemu-bridge-helper"
+@end example
+
+@item -net bridge[,vlan=@var{n}][,name=@var{name}][,br=@var{bridge}][,helper=@var{helper}]
+Connect a host TAP network interface to a host bridge device.
+
+Use the network helper @var{helper} to configure the TAP interface and
+attach it to the bridge. The default network helper executable is
+@file{/usr/local/libexec/qemu-bridge-helper} and the default bridge
+device is @file{br0}.
+
+Examples:
+
+@example
+#launch a QEMU instance with the default network helper to
+#connect a TAP device to bridge br0
+qemu linux.img -net bridge -net nic,model=virtio
+@end example
+
+@example
+#launch a QEMU instance with the default network helper to
+#connect a TAP device to bridge qemubr0
+qemu linux.img -net bridge,br=qemubr0 -net nic,model=virtio
+@end example
+
@item -net socket[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}] [,listen=[@var{host}]:@var{port}][,connect=@var{host}:@var{port}]
Connect the VLAN @var{n} to a remote VLAN in another QEMU virtual