]> Git Repo - qemu.git/blobdiff - net/tap.c
tap: invoke downscript when we exit abnormally
[qemu.git] / net / tap.c
index 3f6722ea6cf1cffe9d5298cb9b40f6ebb956cb0b..e42c555029db931ab39691d4600a1d6a0d5e76bc 100644 (file)
--- a/net/tap.c
+++ b/net/tap.c
 #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 "qemu-common.h"
+#include "qemu-error.h"
 
 #include "net/tap-linux.h"
 
 #define TAP_BUFSIZE (4096 + 65536)
 
 typedef struct TAPState {
-    VLANClientState *vc;
+    VLANClientState nc;
     int fd;
     char down_script[1024];
     char down_script_arg[128];
     uint8_t buf[TAP_BUFSIZE];
+    Notifier exit_notifier;
     unsigned int read_poll : 1;
     unsigned int write_poll : 1;
     unsigned int has_vnet_hdr : 1;
@@ -91,7 +94,7 @@ static void tap_writable(void *opaque)
 
     tap_write_poll(s, 0);
 
-    qemu_flush_queued_packets(s->vc);
+    qemu_flush_queued_packets(&s->nc);
 }
 
 static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt)
@@ -110,10 +113,10 @@ static ssize_t tap_write_packet(TAPState *s, const struct iovec *iov, int iovcnt
     return len;
 }
 
-static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov,
+static ssize_t tap_receive_iov(VLANClientState *nc, const struct iovec *iov,
                                int iovcnt)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
     const struct iovec *iovp = iov;
     struct iovec iov_copy[iovcnt + 1];
     struct virtio_net_hdr hdr = { 0, };
@@ -129,9 +132,9 @@ static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov,
     return tap_write_packet(s, iovp, iovcnt);
 }
 
-static ssize_t tap_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t size)
+static ssize_t tap_receive_raw(VLANClientState *nc, const uint8_t *buf, size_t size)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
     struct iovec iov[2];
     int iovcnt = 0;
     struct virtio_net_hdr hdr = { 0, };
@@ -149,13 +152,13 @@ static ssize_t tap_receive_raw(VLANClientState *vc, const uint8_t *buf, size_t s
     return tap_write_packet(s, iov, iovcnt);
 }
 
-static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
+static ssize_t tap_receive(VLANClientState *nc, const uint8_t *buf, size_t size)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
     struct iovec iov[1];
 
     if (s->has_vnet_hdr && !s->using_vnet_hdr) {
-        return tap_receive_raw(vc, buf, size);
+        return tap_receive_raw(nc, buf, size);
     }
 
     iov[0].iov_base = (char *)buf;
@@ -168,7 +171,7 @@ static int tap_can_send(void *opaque)
 {
     TAPState *s = opaque;
 
-    return qemu_can_send_packet(s->vc);
+    return qemu_can_send_packet(&s->nc);
 }
 
 #ifndef __sun__
@@ -178,9 +181,9 @@ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen)
 }
 #endif
 
-static void tap_send_completed(VLANClientState *vc, ssize_t len)
+static void tap_send_completed(VLANClientState *nc, ssize_t len)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
     tap_read_poll(s, 1);
 }
 
@@ -202,75 +205,56 @@ static void tap_send(void *opaque)
             size -= sizeof(struct virtio_net_hdr);
         }
 
-        size = qemu_send_packet_async(s->vc, buf, size, tap_send_completed);
+        size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed);
         if (size == 0) {
             tap_read_poll(s, 0);
         }
-    } while (size > 0);
+    } while (size > 0 && qemu_can_send_packet(&s->nc));
 }
 
-int tap_has_ufo(VLANClientState *vc)
+int tap_has_ufo(VLANClientState *nc)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(vc->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
 
     return s->has_ufo;
 }
 
-int tap_has_vnet_hdr(VLANClientState *vc)
+int tap_has_vnet_hdr(VLANClientState *nc)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    assert(vc->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
 
     return s->has_vnet_hdr;
 }
 
-void tap_using_vnet_hdr(VLANClientState *vc, int using_vnet_hdr)
+void tap_using_vnet_hdr(VLANClientState *nc, int using_vnet_hdr)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
     using_vnet_hdr = using_vnet_hdr != 0;
 
-    assert(vc->type == NET_CLIENT_TYPE_TAP);
+    assert(nc->info->type == NET_CLIENT_TYPE_TAP);
     assert(s->has_vnet_hdr == using_vnet_hdr);
 
     s->using_vnet_hdr = using_vnet_hdr;
 }
 
-void tap_set_offload(VLANClientState *vc, int csum, int tso4,
+void tap_set_offload(VLANClientState *nc, int csum, int tso4,
                      int tso6, int ecn, int ufo)
 {
-    TAPState *s = vc->opaque;
-    unsigned int offload = 0;
-
-    if (csum) {
-        offload |= TUN_F_CSUM;
-        if (tso4)
-            offload |= TUN_F_TSO4;
-        if (tso6)
-            offload |= TUN_F_TSO6;
-        if ((tso4 || tso6) && ecn)
-            offload |= TUN_F_TSO_ECN;
-        if (ufo)
-            offload |= TUN_F_UFO;
-    }
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    if (ioctl(s->fd, TUNSETOFFLOAD, offload) != 0) {
-        offload &= ~TUN_F_UFO;
-        if (ioctl(s->fd, TUNSETOFFLOAD, offload) != 0) {
-            fprintf(stderr, "TUNSETOFFLOAD ioctl() failed: %s\n",
-                    strerror(errno));
-        }
-    }
+    return tap_fd_set_offload(s->fd, csum, tso4, tso6, ecn, ufo);
 }
 
-static void tap_cleanup(VLANClientState *vc)
+static void tap_cleanup(VLANClientState *nc)
 {
-    TAPState *s = vc->opaque;
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
 
-    qemu_purge_queued_packets(vc);
+    qemu_purge_queued_packets(nc);
 
     if (s->down_script[0])
         launch_script(s->down_script, s->down_script_arg, s->fd);
@@ -278,34 +262,60 @@ static void tap_cleanup(VLANClientState *vc)
     tap_read_poll(s, 0);
     tap_write_poll(s, 0);
     close(s->fd);
-    qemu_free(s);
+    exit_notifier_remove(&s->exit_notifier);
+}
+
+/* Instead of exiting gracefully, we're exiting because someone called
+ * exit(), make sure to invoke down script at least
+ */
+static void tap_cleanup_at_exit(Notifier *notifier)
+{
+    TAPState *s = container_of(notifier, TAPState, exit_notifier);
+
+    if (s->down_script[0]) {
+        launch_script(s->down_script, s->down_script_arg, s->fd);
+    }
+}
+
+static void tap_poll(VLANClientState *nc, bool enable)
+{
+    TAPState *s = DO_UPCAST(TAPState, nc, nc);
+    tap_read_poll(s, enable);
+    tap_write_poll(s, enable);
 }
 
 /* fd support */
 
+static NetClientInfo net_tap_info = {
+    .type = NET_CLIENT_TYPE_TAP,
+    .size = sizeof(TAPState),
+    .receive = tap_receive,
+    .receive_raw = tap_receive_raw,
+    .receive_iov = tap_receive_iov,
+    .poll = tap_poll,
+    .cleanup = tap_cleanup,
+};
+
 static TAPState *net_tap_fd_init(VLANState *vlan,
                                  const char *model,
                                  const char *name,
                                  int fd,
                                  int vnet_hdr)
 {
+    VLANClientState *nc;
     TAPState *s;
-    unsigned int offload;
 
-    s = qemu_mallocz(sizeof(TAPState));
+    nc = qemu_new_net_client(&net_tap_info, vlan, NULL, model, name);
+
+    s = DO_UPCAST(TAPState, nc, nc);
+
     s->fd = fd;
     s->has_vnet_hdr = vnet_hdr != 0;
     s->using_vnet_hdr = 0;
-    s->vc = qemu_new_vlan_client(NET_CLIENT_TYPE_TAP,
-                                 vlan, NULL, model, name, NULL,
-                                 tap_receive, tap_receive_raw,
-                                 tap_receive_iov, tap_cleanup, s);
-    s->has_ufo = 0;
-    /* Check if tap supports UFO */
-    offload = TUN_F_CSUM | TUN_F_UFO;
-    if (ioctl(s->fd, TUNSETOFFLOAD, offload) == 0)
-       s->has_ufo = 1;
-    tap_set_offload(s->vc, 0, 0, 0, 0, 0);
+    s->has_ufo = tap_probe_has_ufo(s->fd);
+    s->exit_notifier.notify = tap_cleanup_at_exit;
+    exit_notifier_add(&s->exit_notifier);
+    tap_set_offload(&s->nc, 0, 0, 0, 0, 0);
     tap_read_poll(s, 1);
     return s;
 }
@@ -393,14 +403,14 @@ static int net_tap_init(QemuOpts *opts, int *vnet_hdr)
 int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan)
 {
     TAPState *s;
-    int fd, vnet_hdr;
+    int fd, vnet_hdr = 0;
 
     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_error("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=\n");
+            error_report("ifname=, script=, downscript= and vnet_hdr= is invalid with fd=");
             return -1;
         }
 
@@ -422,6 +432,9 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
         }
 
         fd = net_tap_init(opts, &vnet_hdr);
+        if (fd == -1) {
+            return -1;
+        }
     }
 
     s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
@@ -435,7 +448,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
     }
 
     if (qemu_opt_get(opts, "fd")) {
-        snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd);
+        snprintf(s->nc.info_str, sizeof(s->nc.info_str), "fd=%d", fd);
     } else {
         const char *ifname, *script, *downscript;
 
@@ -443,7 +456,7 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
         script     = qemu_opt_get(opts, "script");
         downscript = qemu_opt_get(opts, "downscript");
 
-        snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+        snprintf(s->nc.info_str, sizeof(s->nc.info_str),
                  "ifname=%s,script=%s,downscript=%s",
                  ifname, script, downscript);
 
@@ -453,9 +466,5 @@ int net_init_tap(QemuOpts *opts, Monitor *mon, const char *name, VLANState *vlan
         }
     }
 
-    if (vlan) {
-        vlan->nb_host_devs++;
-    }
-
     return 0;
 }
This page took 0.034498 seconds and 4 git commands to generate.