]> git.proxmox.com Git - qemu.git/blobdiff - net/tap.c
janitor: do not include qemu-char everywhere
[qemu.git] / net / tap.c
index 4afb314fde032bdd7ee15fa07047c8f7e4684438..5ff78934dc59d16b19006645fb74b16a5d986aff 100644 (file)
--- a/net/tap.c
+++ b/net/tap.c
  * THE SOFTWARE.
  */
 
-#include "net/tap.h"
+#include "tap.h"
 
 #include "config-host.h"
 
-#include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/wait.h>
@@ -35,8 +34,9 @@
 #include <net/if.h>
 
 #include "net.h"
+#include "clients.h"
+#include "monitor.h"
 #include "sysemu.h"
-#include "qemu-char.h"
 #include "qemu-common.h"
 #include "qemu-error.h"
 
@@ -50,7 +50,7 @@
 #define TAP_BUFSIZE (4096 + 65536)
 
 typedef struct TAPState {
-    VLANClientState nc;
+    NetClientState nc;
     int fd;
     char down_script[1024];
     char down_script_arg[128];
@@ -115,7 +115,7 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
     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);
@@ -134,7 +134,7 @@ static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
     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];
@@ -154,7 +154,7 @@ static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t s
     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];
@@ -183,7 +183,7 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
 }
 #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);
@@ -214,38 +214,38 @@ static void tap_send(void *opaque)
     } 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));
 
@@ -253,32 +253,36 @@ void tap_set_vnet_hdr_len(VLANClientState *nc, int len)
     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);
+    if (s->fd < 0) {
+        return;
+    }
 
-    return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
+    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);
 
     if (s->vhost_net) {
         vhost_net_cleanup(s->vhost_net);
+        s->vhost_net = NULL;
     }
 
     qemu_purge_queued_packets(nc);
@@ -289,26 +293,27 @@ static void tap_cleanup(VLANClientState *nc)
     tap_read_poll(s, 0);
     tap_write_poll(s, 0);
     close(s->fd);
+    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,
@@ -317,16 +322,16 @@ static NetClientInfo net_tap_info = {
     .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);
 
@@ -335,6 +340,13 @@ static TAPState *net_tap_fd_init(VLANState *vlan,
     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;
@@ -342,15 +354,10 @@ static TAPState *net_tap_fd_init(VLANState *vlan,
 
 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) {
@@ -374,7 +381,6 @@ static int launch_script(const char *setup_script, const char *ifname, int fd)
         while (waitpid(pid, &status, 0) != pid) {
             /* loop */
         }
-        sigprocmask(SIG_SETMASK, &oldmask, NULL);
 
         if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
             return 0;
@@ -384,29 +390,198 @@ static int launch_script(const char *setup_script, const char *ifname, int fd)
     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 &&
@@ -415,26 +590,34 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
         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;
         }
@@ -442,43 +625,62 @@ 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, "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);
@@ -486,23 +688,26 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
         }
     }
 
-    if (qemu_opt_get_bool(opts, "vhost", !!qemu_opt_get(opts, "vhostfd"))) {
-        int vhostfd, r;
-        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);
+
+        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;
     }
@@ -510,9 +715,9 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
     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;
 }