#include <net/if.h>
#include "net.h"
+#include "monitor.h"
#include "sysemu.h"
#include "qemu-char.h"
#include "qemu-common.h"
static int launch_script(const char *setup_script, const char *ifname, int fd)
{
- sigset_t oldmask, mask;
int pid, status;
char *args[3];
char **parg;
- sigemptyset(&mask);
- sigaddset(&mask, SIGCHLD);
- sigprocmask(SIG_BLOCK, &mask, &oldmask);
-
/* try to launch network script */
pid = fork();
if (pid == 0) {
while (waitpid(pid, &status, 0) != pid) {
/* loop */
}
- sigprocmask(SIG_SETMASK, &oldmask, NULL);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
return 0;
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, 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;
return fd;
}
-int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_tap(QemuOpts *opts, 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") ||
+ qemu_opt_get(opts, "helper")) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "and helper= are invalid with fd=");
+ return -1;
+ }
+
+ fd = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "fd"));
+ if (fd == -1) {
+ return -1;
+ }
+
+ 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= is invalid with fd=");
+ error_report("ifname=, script=, downscript=, and vnet_hdr= "
+ "are invalid with helper=");
return -1;
}
- fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ 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);
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;
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;
int vhostfd, r;
bool force = qemu_opt_get_bool(opts, "vhostforce", false);
if (qemu_opt_get(opts, "vhostfd")) {
- r = net_handle_fd_param(mon, qemu_opt_get(opts, "vhostfd"));
+ r = net_handle_fd_param(cur_mon, qemu_opt_get(opts, "vhostfd"));
if (r == -1) {
return -1;
}