#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;
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)
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, };
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, };
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;
{
TAPState *s = opaque;
- return qemu_can_send_packet(s->vc);
+ return qemu_can_send_packet(&s->nc);
}
#ifndef __sun__
}
#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);
}
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);
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;
}
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;
}
}
fd = net_tap_init(opts, &vnet_hdr);
+ if (fd == -1) {
+ return -1;
+ }
}
s = net_tap_fd_init(vlan, "tap", name, fd, vnet_hdr);
}
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;
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);
}
}
- if (vlan) {
- vlan->nb_host_devs++;
- }
-
return 0;
}