aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--net.c83
-rw-r--r--tap-linux.h14
2 files changed, 84 insertions, 13 deletions
diff --git a/net.c b/net.c
index a96e42d8b0..f4cf2c549d 100644
--- a/net.c
+++ b/net.c
@@ -1260,14 +1260,20 @@ void do_info_usernet(Monitor *mon)
#if !defined(_WIN32)
+/* Maximum GSO packet size (64k) plus plenty of room for
+ * the ethernet and virtio_net headers
+ */
+#define TAP_BUFSIZE (4096 + 65536)
+
typedef struct TAPState {
VLANClientState *vc;
int fd;
char down_script[1024];
char down_script_arg[128];
- uint8_t buf[4096];
+ uint8_t buf[TAP_BUFSIZE];
unsigned int read_poll : 1;
unsigned int write_poll : 1;
+ unsigned int has_vnet_hdr : 1;
} TAPState;
static int launch_script(const char *setup_script, const char *ifname, int fd);
@@ -1326,15 +1332,33 @@ static ssize_t tap_receive_iov(VLANClientState *vc, const struct iovec *iov,
int iovcnt)
{
TAPState *s = vc->opaque;
+ const struct iovec *iovp = iov;
+ struct iovec iov_copy[iovcnt + 1];
+ struct virtio_net_hdr hdr = { 0, };
- return tap_write_packet(s, iov, iovcnt);
+ if (s->has_vnet_hdr) {
+ iov_copy[0].iov_base = &hdr;
+ iov_copy[0].iov_len = sizeof(hdr);
+ memcpy(&iov_copy[1], iov, iovcnt * sizeof(*iov));
+ iovp = iov_copy;
+ iovcnt++;
+ }
+
+ return tap_write_packet(s, iovp, iovcnt);
}
static ssize_t tap_receive(VLANClientState *vc, const uint8_t *buf, size_t size)
{
TAPState *s = vc->opaque;
- struct iovec iov[1];
+ struct iovec iov[2];
int iovcnt = 0;
+ struct virtio_net_hdr hdr = { 0, };
+
+ if (s->has_vnet_hdr) {
+ iov[iovcnt].iov_base = &hdr;
+ iov[iovcnt].iov_len = sizeof(hdr);
+ iovcnt++;
+ }
iov[iovcnt].iov_base = (char *)buf;
iov[iovcnt].iov_len = size;
@@ -1380,12 +1404,19 @@ static void tap_send(void *opaque)
int size;
do {
+ uint8_t *buf = s->buf;
+
size = tap_read_packet(s->fd, s->buf, sizeof(s->buf));
if (size <= 0) {
break;
}
- size = qemu_send_packet_async(s->vc, s->buf, size, tap_send_completed);
+ if (s->has_vnet_hdr) {
+ buf += sizeof(struct virtio_net_hdr);
+ size -= sizeof(struct virtio_net_hdr);
+ }
+
+ size = qemu_send_packet_async(s->vc, buf, size, tap_send_completed);
if (size == 0) {
tap_read_poll(s, 0);
}
@@ -1415,6 +1446,18 @@ static int tap_set_sndbuf(TAPState *s, QemuOpts *opts)
return 0;
}
+static int tap_probe_vnet_hdr(int fd)
+{
+ struct ifreq ifr;
+
+ if (ioctl(fd, TUNGETIFF, &ifr) != 0) {
+ qemu_error("TUNGETIFF ioctl() failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ return ifr.ifr_flags & IFF_VNET_HDR;
+}
+
static void tap_cleanup(VLANClientState *vc)
{
TAPState *s = vc->opaque;
@@ -1435,12 +1478,14 @@ static void tap_cleanup(VLANClientState *vc)
static TAPState *net_tap_fd_init(VLANState *vlan,
const char *model,
const char *name,
- int fd)
+ int fd,
+ int vnet_hdr)
{
TAPState *s;
s = qemu_mallocz(sizeof(TAPState));
s->fd = fd;
+ s->has_vnet_hdr = vnet_hdr != 0;
s->vc = qemu_new_vlan_client(vlan, NULL, model, name, NULL,
tap_receive, tap_receive_iov,
tap_cleanup, s);
@@ -1450,7 +1495,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan,
}
#if defined (CONFIG_BSD) || defined (__FreeBSD_kernel__)
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
int fd;
char *dev;
@@ -1592,7 +1637,7 @@ static int tap_alloc(char *dev, size_t dev_size)
return tap_fd;
}
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
char dev[10]="";
int fd;
@@ -1605,13 +1650,13 @@ static int tap_open(char *ifname, int ifname_size)
return fd;
}
#elif defined (_AIX)
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
fprintf (stderr, "no tap on AIX\n");
return -1;
}
#else
-static int tap_open(char *ifname, int ifname_size)
+static int tap_open(char *ifname, int ifname_size, int *vnet_hdr)
{
struct ifreq ifr;
int fd, ret;
@@ -1623,6 +1668,17 @@ static int tap_open(char *ifname, int ifname_size)
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ {
+ unsigned int features;
+
+ if (ioctl(fd, TUNGETFEATURES, &features) == 0 &&
+ features & IFF_VNET_HDR) {
+ *vnet_hdr = 1;
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ }
+ }
+
if (ifname[0] != '\0')
pstrcpy(ifr.ifr_name, IFNAMSIZ, ifname);
else
@@ -1688,14 +1744,15 @@ static TAPState *net_tap_init(VLANState *vlan, const char *model,
const char *setup_script, const char *down_script)
{
TAPState *s;
- int fd;
+ int fd, vnet_hdr;
char ifname[128];
if (ifname1 != NULL)
pstrcpy(ifname, sizeof(ifname), ifname1);
else
ifname[0] = '\0';
- TFR(fd = tap_open(ifname, sizeof(ifname)));
+ vnet_hdr = 0;
+ TFR(fd = tap_open(ifname, sizeof(ifname), &vnet_hdr));
if (fd < 0)
return NULL;
@@ -1705,7 +1762,7 @@ static TAPState *net_tap_init(VLANState *vlan, const char *model,
launch_script(setup_script, ifname, fd)) {
return NULL;
}
- s = net_tap_fd_init(vlan, model, name, fd);
+ s = net_tap_fd_init(vlan, model, name, fd, vnet_hdr);
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
"ifname=%s,script=%s,downscript=%s",
ifname, setup_script, down_script);
@@ -2659,7 +2716,7 @@ static int net_init_tap(QemuOpts *opts,
fcntl(fd, F_SETFL, O_NONBLOCK);
- s = net_tap_fd_init(vlan, "tap", name, fd);
+ s = net_tap_fd_init(vlan, "tap", name, fd, tap_probe_vnet_hdr(fd));
if (!s) {
close(fd);
}
diff --git a/tap-linux.h b/tap-linux.h
index cd07ea8efb..8e7534829e 100644
--- a/tap-linux.h
+++ b/tap-linux.h
@@ -16,14 +16,28 @@
#ifndef QEMU_TAP_H
#define QEMU_TAP_H
+#include <stdint.h>
#include <linux/ioctl.h>
/* Ioctl defines */
#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
#define TUNSETSNDBUF _IOW('T', 212, int)
/* TUNSETIFF ifr flags */
#define IFF_TAP 0x0002
#define IFF_NO_PI 0x1000
+#define IFF_VNET_HDR 0x4000
+
+struct virtio_net_hdr
+{
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+};
#endif /* QEMU_TAP_H */