* THE SOFTWARE.
*/
-#include "net/tap.h"
+#include "tap_int.h"
#include "config-host.h"
-#include <signal.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <net/if.h>
-#include "net.h"
-#include "sysemu.h"
-#include "qemu-char.h"
+#include "net/net.h"
+#include "clients.h"
+#include "monitor/monitor.h"
+#include "sysemu/sysemu.h"
#include "qemu-common.h"
-#include "qemu-error.h"
+#include "qemu/error-report.h"
-#include "net/tap-linux.h"
+#include "net/tap.h"
#include "hw/vhost_net.h"
#define TAP_BUFSIZE (4096 + 65536)
typedef struct TAPState {
- VLANClientState nc;
+ NetClientState nc;
int fd;
char down_script[1024];
char down_script_arg[128];
return len;
}
-static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
+static ssize_t tap_receive_iov(NetClientState *nc, const struct iovec *iov,
int iovcnt)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
return tap_write_packet(s, iovp, iovcnt);
}
-static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t tap_receive_raw(NetClientState *nc, const uint8_t *buf, size_t size)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
struct iovec iov[2];
return tap_write_packet(s, iov, iovcnt);
}
-static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
struct iovec iov[1];
}
#endif
-static void tap_send_completed(VLANClientState *nc, ssize_t len)
+static void tap_send_completed(NetClientState *nc, ssize_t len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
tap_read_poll(s, 1);
} while (size > 0 && qemu_can_send_packet(&s->nc));
}
-int tap_has_ufo(VLANClientState *nc)
+int tap_has_ufo(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->has_ufo;
}
-int tap_has_vnet_hdr(VLANClientState *nc)
+int tap_has_vnet_hdr(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return !!s->host_vnet_hdr_len;
}
-int tap_has_vnet_hdr_len(VLANClientState *nc, int len)
+int tap_has_vnet_hdr_len(NetClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return tap_probe_vnet_hdr_len(s->fd, len);
}
-void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
+void tap_set_vnet_hdr_len(NetClientState *nc, int len)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(len == sizeof(struct virtio_net_hdr_mrg_rxbuf) ||
len == sizeof(struct virtio_net_hdr));
s->host_vnet_hdr_len = len;
}
-void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
+void tap_using_vnet_hdr(NetClientState *nc, int using_vnet_hdr)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
using_vnet_hdr = using_vnet_hdr != 0;
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
assert(!!s->host_vnet_hdr_len == using_vnet_hdr);
s->using_vnet_hdr = using_vnet_hdr;
}
-void tap_set_offload(VLANClientState *nc, int csum, int tso4,
+void tap_set_offload(NetClientState *nc, int csum, int tso4,
int tso6, int ecn, int ufo)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
}
-static void tap_cleanup(VLANClientState *nc)
+static void tap_cleanup(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
s->fd = -1;
}
-static void tap_poll(VLANClientState *nc, bool enable)
+static void tap_poll(NetClientState *nc, bool enable)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
tap_read_poll(s, enable);
tap_write_poll(s, enable);
}
-int tap_get_fd(VLANClientState *nc)
+int tap_get_fd(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->fd;
}
/* fd support */
static NetClientInfo net_tap_info = {
- .type = NET_CLIENT_TYPE_TAP,
+ .type = NET_CLIENT_OPTIONS_KIND_TAP,
.size = sizeof(TAPState),
.receive = tap_receive,
.receive_raw = tap_receive_raw,
.cleanup = tap_cleanup,
};
-static TAPState *net_tap_fd_init(VLANState *vlan,
+static TAPState *net_tap_fd_init(NetClientState *peer,
const char *model,
const char *name,
int fd,
int vnet_hdr)
{
- VLANClientState *nc;
+ NetClientState *nc;
TAPState *s;
- nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
+ nc = qemu_new_net_client(&net_tap_info, peer, model, name);
s = DO_UPCAST(TAPState, nc, nc);
s->using_vnet_hdr = 0;
s->has_ufo = tap_probe_has_ufo(s->fd);
tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
+ /*
+ * Make sure host header length is set correctly in tap:
+ * it might have been modified by another instance of qemu.
+ */
+ if (tap_probe_vnet_hdr_len(s->fd, s->host_vnet_hdr_len)) {
+ tap_fd_set_vnet_hdr_len(s->fd, s->host_vnet_hdr_len);
+ }
tap_read_poll(s, 1);
s->vhost_net = NULL;
return s;
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 net_tap_init(QemuOpts *opts, int *vnet_hdr)
+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(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
+{
+ const NetdevBridgeOptions *bridge;
+ const char *helper, *br;
+
+ TAPState *s;
+ int fd, vnet_hdr;
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_BRIDGE);
+ bridge = opts->bridge;
+
+ helper = bridge->has_helper ? bridge->helper : DEFAULT_BRIDGE_HELPER;
+ br = bridge->has_br ? bridge->br : DEFAULT_BRIDGE_INTERFACE;
+
+ fd = net_bridge_run_helper(helper, br);
+ if (fd == -1) {
+ return -1;
+ }
+
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ s = net_tap_fd_init(peer, "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", helper,
+ br);
+
+ return 0;
+}
+
+static int net_tap_init(const NetdevTapOptions *tap, int *vnet_hdr,
+ const char *setup_script, char *ifname,
+ size_t ifname_sz)
{
int fd, vnet_hdr_required;
- char ifname[128] = {0,};
- const char *setup_script;
- if (qemu_opt_get(opts, "ifname")) {
- pstrcpy(ifname, sizeof(ifname), qemu_opt_get(opts, "ifname"));
+ if (tap->has_ifname) {
+ pstrcpy(ifname, ifname_sz, tap->ifname);
+ } else {
+ assert(ifname_sz > 0);
+ ifname[0] = '\0';
}
- *vnet_hdr = qemu_opt_get_bool(opts, "vnet_hdr", 1);
- if (qemu_opt_get(opts, "vnet_hdr")) {
+ if (tap->has_vnet_hdr) {
+ *vnet_hdr = tap->vnet_hdr;
vnet_hdr_required = *vnet_hdr;
} else {
+ *vnet_hdr = 1;
vnet_hdr_required = 0;
}
- TFR(fd = tap_open(ifname, sizeof(ifname), vnet_hdr, vnet_hdr_required));
+ TFR(fd = tap_open(ifname, ifname_sz, vnet_hdr, vnet_hdr_required));
if (fd < 0) {
return -1;
}
- setup_script = qemu_opt_get(opts, "script");
if (setup_script &&
setup_script[0] != '\0' &&
strcmp(setup_script, "no") != 0 &&
return -1;
}
- qemu_opt_set(opts, "ifname", ifname);
-
return fd;
}
-int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
+int net_init_tap(const NetClientOptions *opts, const char *name,
+ NetClientState *peer)
{
- TAPState *s;
+ const NetdevTapOptions *tap;
+
int fd, vnet_hdr = 0;
+ const char *model;
+ TAPState *s;
- 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=");
+ /* for the no-fd, no-helper case */
+ const char *script = NULL; /* suppress wrong "uninit'd use" gcc warning */
+ char ifname[128];
+
+ assert(opts->kind == NET_CLIENT_OPTIONS_KIND_TAP);
+ tap = opts->tap;
+
+ if (tap->has_fd) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr || tap->has_helper) {
+ error_report("ifname=, script=, downscript=, vnet_hdr=, "
+ "and helper= are invalid with fd=");
return -1;
}
- fd = net_handle_fd_param(mon, qemu_opt_get(opts, "fd"));
+ fd = monitor_handle_fd_param(cur_mon, tap->fd);
if (fd == -1) {
return -1;
}
fcntl(fd, F_SETFL, O_NONBLOCK);
vnet_hdr = tap_probe_vnet_hdr(fd);
- } else {
- if (!qemu_opt_get(opts, "script")) {
- qemu_opt_set(opts, "script", DEFAULT_NETWORK_SCRIPT);
+
+ model = "tap";
+
+ } else if (tap->has_helper) {
+ if (tap->has_ifname || tap->has_script || tap->has_downscript ||
+ tap->has_vnet_hdr) {
+ error_report("ifname=, script=, downscript=, and vnet_hdr= "
+ "are invalid with helper=");
+ return -1;
}
- if (!qemu_opt_get(opts, "downscript")) {
- qemu_opt_set(opts, "downscript", DEFAULT_NETWORK_DOWN_SCRIPT);
+ fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE);
+ if (fd == -1) {
+ return -1;
}
- fd = net_tap_init(opts, &vnet_hdr);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
+
+ vnet_hdr = tap_probe_vnet_hdr(fd);
+
+ model = "bridge";
+
+ } else {
+ script = tap->has_script ? tap->script : DEFAULT_NETWORK_SCRIPT;
+ fd = net_tap_init(tap, &vnet_hdr, script, ifname, sizeof ifname);
if (fd == -1) {
return -1;
}
+
+ model = "tap";
}
- s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
+ s = net_tap_fd_init(peer, model, name, fd, vnet_hdr);
if (!s) {
close(fd);
return -1;
}
- if (tap_set_sndbuf(s->fd, opts) < 0) {
+ if (tap_set_sndbuf(s->fd, tap) < 0) {
return -1;
}
- if (qemu_opt_get(opts, "fd")) {
+ if (tap->has_fd) {
snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
+ } else if (tap->has_helper) {
+ snprintf(s->nc.info_str, sizeof(s->nc.info_str), "helper=%s",
+ tap->helper);
} else {
- const char *ifname, *script, *downscript;
+ const char *downscript;
- ifname = qemu_opt_get(opts, "ifname");
- script = qemu_opt_get(opts, "script");
- downscript = qemu_opt_get(opts, "downscript");
+ downscript = tap->has_downscript ? tap->downscript :
+ DEFAULT_NETWORK_DOWN_SCRIPT;
snprintf(s->nc.info_str, sizeof(s->nc.info_str),
- "ifname=%s,script=%s,downscript=%s",
- ifname, script, downscript);
+ "ifname=%s,script=%s,downscript=%s", ifname, script,
+ downscript);
if (strcmp(downscript, "no") != 0) {
snprintf(s->down_script, sizeof(s->down_script), "%s", downscript);
}
}
- if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd") ||
- qemu_opt_get_bool(opts, "vhostforce", false))) {
- 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"));
- if (r == -1) {
+ if (tap->has_vhost ? tap->vhost :
+ tap->has_vhostfd || (tap->has_vhostforce && tap->vhostforce)) {
+ int vhostfd;
+
+ if (tap->has_vhostfd) {
+ vhostfd = monitor_handle_fd_param(cur_mon, tap->vhostfd);
+ if (vhostfd == -1) {
return -1;
}
- vhostfd = r;
} else {
vhostfd = -1;
}
- s->vhost_net = vhost_net_init(&s->nc, vhostfd, force);
+
+ s->vhost_net = vhost_net_init(&s->nc, vhostfd,
+ tap->has_vhostforce && tap->vhostforce);
if (!s->vhost_net) {
error_report("vhost-net requested but could not be initialized");
return -1;
}
- } else if (qemu_opt_get(opts, "vhostfd")) {
+ } else if (tap->has_vhostfd) {
error_report("vhostfd= is not valid without vhost");
return -1;
}
return 0;
}
-VHostNetState *tap_get_vhost_net(VLANClientState *nc)
+VHostNetState *tap_get_vhost_net(NetClientState *nc)
{
TAPState *s = DO_UPCAST(TAPState, nc, nc);
- assert(nc->info->type == NET_CLIENT_TYPE_TAP);
+ assert(nc->info->type == NET_CLIENT_OPTIONS_KIND_TAP);
return s->vhost_net;
}