diff options
Diffstat (limited to 'net/tap.c')
-rw-r--r-- | net/tap.c | 204 |
1 files changed, 201 insertions, 3 deletions
@@ -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; |