Signed-off-by: Richa Marwaha<rmar...@linux.vnet.ibm.com>
---
configure | 2 +
net.c | 8 +++
net.h | 2 +
net/tap.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
qemu-options.hx | 48 +++++++++++++-----
5 files changed, 190 insertions(+), 20 deletions(-)
diff --git a/configure b/configure
index f46e9b7..ef05954 100755
--- a/configure
+++ b/configure
@@ -2775,6 +2775,8 @@ echo "sysconfdir=$sysconfdir">> $config_host_mak
echo "docdir=$docdir">> $config_host_mak
echo "libexecdir=\${prefix}/libexec">> $config_host_mak
echo "confdir=$confdir">> $config_host_mak
+echo "CONFIG_QEMU_SHAREDIR=\"$prefix$datasuffix\"">> $config_host_mak
+echo "CONFIG_QEMU_HELPERDIR=\"$prefix/libexec\"">> $config_host_mak
case "$cpu" in
i386|x86_64|alpha|cris|hppa|ia64|lm32|m68k|microblaze|mips|mips64|ppc|ppc64|s390|s390x|sparc|sparc64|unicore32)
diff --git a/net.c b/net.c
index d05930c..4c3c551 100644
--- a/net.c
+++ b/net.c
@@ -956,6 +956,14 @@ static const struct {
.type = QEMU_OPT_STRING,
.help = "script to shut down the interface",
}, {
+ .name = "br",
+ .type = QEMU_OPT_STRING,
+ .help = "bridge name",
+ }, {
+ .name = "helper",
+ .type = QEMU_OPT_STRING,
+ .help = "command to execute to configure bridge",
+ }, {
.name = "sndbuf",
.type = QEMU_OPT_SIZE,
.help = "send buffer limit"
diff --git a/net.h b/net.h
index 9f633f8..eeb19a7 100644
--- a/net.h
+++ b/net.h
@@ -174,6 +174,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 "qemubr0"
void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd);
diff --git a/net/tap.c b/net/tap.c
index 1f26dc9..74f103a 100644
--- a/net/tap.c
+++ b/net/tap.c
@@ -388,6 +388,108 @@ 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 buf[32];
+
+ snprintf(buf, sizeof(buf), "%d", sv[1]);
+
+ for (i = 0; i< open_max; i++) {
+ if (i != STDIN_FILENO&&
+ i != STDOUT_FILENO&&
+ i != STDERR_FILENO&&
+ i != sv[1]) {
+ close(i);
+ }
+ }
+ parg = args;
+ *parg++ = (char *)helper;
+ *parg++ = (char *)"--use-vnet";
+ *parg++ = (char *)bridge;
+ *parg++ = 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;
+}
+
static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
{
int fd, vnet_hdr_required;
@@ -433,8 +535,11 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char
*name, VLANState *vlan
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, "br") ||
+ qemu_opt_get(opts, "helper")) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=,"
+ "br= and helper= are invalid with fd=");
return -1;
}
@@ -446,6 +551,37 @@ 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);
+ } else if (qemu_opt_get(opts, "helper")) {
+ if (qemu_opt_get(opts, "ifname") ||
+ qemu_opt_get(opts, "script") ||
+ qemu_opt_get(opts, "downscript")) {
+ error_report("ifname=, script= and downscript="
+ "are invalid with helper=");
+ return -1;
+ }
+
+ if (!qemu_opt_get(opts, "br")) {
+ qemu_opt_set(opts, "br", DEFAULT_BRIDGE_INTERFACE);
+ }
+
+ fd = net_bridge_run_helper(qemu_opt_get(opts, "helper"),
+ qemu_opt_get(opts, "br"));
+
+ 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),
+ "br=%s", qemu_opt_get(opts, "br"));
+
+ return 0;
} else {
if (!qemu_opt_get(opts, "script")) {
qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
@@ -459,12 +595,12 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char
*name, VLANState *vlan
if (fd == -1) {
return -1;
}
- }
- s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
- if (!s) {
- close(fd);
- return -1;
+ s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
+ if (!s) {
+ close(fd);
+ return -1;
+ }
}
if (tap_set_sndbuf(s->fd, opts)< 0) {
diff --git a/qemu-options.hx b/qemu-options.hx
index dfbabd0..ad4afa9 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1149,11 +1149,15 @@ 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][,br=bridge][,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. This requires root privilege.\n"
" use '[down]script=no' to disable script execution\n"
+ " use network helper 'helper' (default=" DEFAULT_BRIDGE_HELPER
") and\n"
+ " use bridge 'br' (default=" DEFAULT_BRIDGE_INTERFACE ") to
configure it. This\n"
+ " does not require root privilege.\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"
@@ -1322,26 +1326,44 @@ 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}][,br=@var{bridge}][,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
+bridge helper executable is @file{/usr/local/libexec/qemu-bridge-helper}
+and bridge name interface is @file{qemubr0}.
+
+@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,br=br0
+@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