* [Qemu-devel] [PATCH v3 1/8] qemu:virtio-net: Save status and add some save infrastructure
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
@ 2009-02-05 0:17 ` Alex Williamson
2009-02-05 22:36 ` [Qemu-devel] " Anthony Liguori
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 2/8] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
` (6 subsequent siblings)
7 siblings, 1 reply; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:17 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
The status register should probably be saved since its guest visible.
Also add a little bit if infrastructure for handling various save
revisions.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 9e40678..2eb52b8 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,6 +16,8 @@
#include "qemu-timer.h"
#include "virtio-net.h"
+#define VIRTIO_NET_VM_VERSION 3
+
typedef struct VirtIONet
{
VirtIODevice vdev;
@@ -292,13 +294,14 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
qemu_put_buffer(f, n->mac, 6);
qemu_put_be32(f, n->tx_timer_active);
qemu_put_be32(f, n->mergeable_rx_bufs);
+ qemu_put_be16(f, n->status);
}
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
{
VirtIONet *n = opaque;
- if (version_id != 2)
+ if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
return -EINVAL;
virtio_load(&n->vdev, f);
@@ -307,6 +310,9 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
n->tx_timer_active = qemu_get_be32(f);
n->mergeable_rx_bufs = qemu_get_be32(f);
+ if (version_id >= 3)
+ n->status = qemu_get_be16(f);
+
if (n->tx_timer_active) {
qemu_mod_timer(n->tx_timer,
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -348,6 +354,6 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->tx_timer_active = 0;
n->mergeable_rx_bufs = 0;
- register_savevm("virtio-net", virtio_net_id++, 2,
+ register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
}
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 2/8] qemu:virtio-net: Allow setting the MAC address via set_config
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
2009-02-05 0:17 ` [Qemu-devel] [PATCH v3 1/8] qemu:virtio-net: Save status and add some save infrastructure Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 3/8] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses Alex Williamson
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Allow the guest to write to the MAC address config space and update
the network info string when it does. Rename get_config for symmetry.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Acked-by: Mark McLoughlin <markmc@redhat.com>
---
hw/virtio-net.c | 18 ++++++++++++++++--
1 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 2eb52b8..105daa9 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -40,7 +40,7 @@ static VirtIONet *to_virtio_net(VirtIODevice *vdev)
return (VirtIONet *)vdev;
}
-static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
+static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
{
VirtIONet *n = to_virtio_net(vdev);
struct virtio_net_config netcfg;
@@ -50,6 +50,19 @@ static void virtio_net_update_config(VirtIODevice *vdev, uint8_t *config)
memcpy(config, &netcfg, sizeof(netcfg));
}
+static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+ struct virtio_net_config netcfg;
+
+ memcpy(&netcfg, config, sizeof(netcfg));
+
+ if (memcmp(netcfg.mac, n->mac, 6)) {
+ memcpy(n->mac, netcfg.mac, 6);
+ qemu_format_nic_info_str(n->vc, n->mac);
+ }
+}
+
static void virtio_net_set_link_status(VLANClientState *vc)
{
VirtIONet *n = vc->opaque;
@@ -337,7 +350,8 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
if (!n)
return;
- n->vdev.get_config = virtio_net_update_config;
+ n->vdev.get_config = virtio_net_get_config;
+ n->vdev.set_config = virtio_net_set_config;
n->vdev.get_features = virtio_net_get_features;
n->vdev.set_features = virtio_net_set_features;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 3/8] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
2009-02-05 0:17 ` [Qemu-devel] [PATCH v3 1/8] qemu:virtio-net: Save status and add some save infrastructure Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 2/8] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 4/8] qemu:virtio-net: Add a virtqueue for control commands from the guest Alex Williamson
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Makes it much easier to search too.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Acked-by: Mark McLoughlin <markmc@redhat.com>
---
hw/virtio-net.c | 14 +++++++-------
hw/virtio-net.h | 2 ++
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 105daa9..073f23a 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -21,7 +21,7 @@
typedef struct VirtIONet
{
VirtIODevice vdev;
- uint8_t mac[6];
+ uint8_t mac[ETH_ALEN];
uint16_t status;
VirtQueue *rx_vq;
VirtQueue *tx_vq;
@@ -46,7 +46,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config)
struct virtio_net_config netcfg;
netcfg.status = n->status;
- memcpy(netcfg.mac, n->mac, 6);
+ memcpy(netcfg.mac, n->mac, ETH_ALEN);
memcpy(config, &netcfg, sizeof(netcfg));
}
@@ -57,8 +57,8 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config)
memcpy(&netcfg, config, sizeof(netcfg));
- if (memcmp(netcfg.mac, n->mac, 6)) {
- memcpy(n->mac, netcfg.mac, 6);
+ if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) {
+ memcpy(n->mac, netcfg.mac, ETH_ALEN);
qemu_format_nic_info_str(n->vc, n->mac);
}
}
@@ -304,7 +304,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
virtio_save(&n->vdev, f);
- qemu_put_buffer(f, n->mac, 6);
+ qemu_put_buffer(f, n->mac, ETH_ALEN);
qemu_put_be32(f, n->tx_timer_active);
qemu_put_be32(f, n->mergeable_rx_bufs);
qemu_put_be16(f, n->status);
@@ -319,7 +319,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
virtio_load(&n->vdev, f);
- qemu_get_buffer(f, n->mac, 6);
+ qemu_get_buffer(f, n->mac, ETH_ALEN);
n->tx_timer_active = qemu_get_be32(f);
n->mergeable_rx_bufs = qemu_get_be32(f);
@@ -356,7 +356,7 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->vdev.set_features = virtio_net_set_features;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
- memcpy(n->mac, nd->macaddr, 6);
+ memcpy(n->mac, nd->macaddr, ETH_ALEN);
n->status = VIRTIO_NET_S_LINK_UP;
n->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
virtio_net_receive, virtio_net_can_receive, n);
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 148ec47..9dce663 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -18,6 +18,8 @@
#include "net.h"
#include "pci.h"
+#define ETH_ALEN 6
+
/* from Linux's virtio_net.h */
/* The ID for virtio_net */
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 4/8] qemu:virtio-net: Add a virtqueue for control commands from the guest
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
` (2 preceding siblings ...)
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 3/8] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 5/8] qemu:virtio-net: Add promiscuous and all-multicast mode bits Alex Williamson
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
This will be used for RX mode, MAC table, VLAN table control, etc...
The control transaction consists of one or more "out" sg entries and
one or more "in" sg entries. The first out entry contains a header
defining the class and command. Additional out entries may provide
data for the command. A response via the ack entry is required
and the guest will typically be waiting for it.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 34 +++++++++++++++++++++++++++++++++-
hw/virtio-net.h | 18 ++++++++++++++++++
2 files changed, 51 insertions(+), 1 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 073f23a..5a8287b 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -25,6 +25,7 @@ typedef struct VirtIONet
uint16_t status;
VirtQueue *rx_vq;
VirtQueue *tx_vq;
+ VirtQueue *ctrl_vq;
VLANClientState *vc;
QEMUTimer *tx_timer;
int tx_timer_active;
@@ -79,7 +80,9 @@ static void virtio_net_set_link_status(VLANClientState *vc)
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
{
- uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
+ uint32_t features = (1 << VIRTIO_NET_F_MAC) |
+ (1 << VIRTIO_NET_F_STATUS) |
+ (1 << VIRTIO_NET_F_CTRL_VQ);
return features;
}
@@ -91,6 +94,34 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
}
+static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+ struct virtio_net_ctrl_hdr ctrl;
+ virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
+ VirtQueueElement elem;
+
+ while (virtqueue_pop(vq, &elem)) {
+ if ((elem.in_num < 1) || (elem.out_num < 1)) {
+ fprintf(stderr, "virtio-net ctrl missing headers\n");
+ exit(1);
+ }
+
+ if (elem.out_sg[0].iov_len < sizeof(ctrl) ||
+ elem.out_sg[elem.in_num - 1].iov_len < sizeof(status)) {
+ fprintf(stderr, "virtio-net ctrl header not in correct element\n");
+ exit(1);
+ }
+
+ ctrl.class = ldub_p(elem.out_sg[0].iov_base);
+ ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class));
+
+ stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
+
+ virtqueue_push(vq, &elem, sizeof(status));
+ virtio_notify(vdev, vq);
+ }
+}
+
/* RX */
static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
@@ -356,6 +387,7 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->vdev.set_features = virtio_net_set_features;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
+ n->ctrl_vq = virtio_add_queue(&n->vdev, 16, virtio_net_handle_ctrl);
memcpy(n->mac, nd->macaddr, ETH_ALEN);
n->status = VIRTIO_NET_S_LINK_UP;
n->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 9dce663..119e38d 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -40,6 +40,7 @@
#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */
#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
+#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
@@ -84,4 +85,21 @@ struct virtio_net_hdr_mrg_rxbuf
void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn);
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry. Data for the
+ * command goes in between.
+ */
+struct virtio_net_ctrl_hdr {
+ uint8_t class;
+ uint8_t cmd;
+};
+
+typedef uint8_t virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK 0
+#define VIRTIO_NET_ERR 1
+
#endif
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 5/8] qemu:virtio-net: Add promiscuous and all-multicast mode bits
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
` (3 preceding siblings ...)
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 4/8] qemu:virtio-net: Add a virtqueue for control commands from the guest Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 6/8] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Add a new RX_MODE control virtqueue class with commands PROMISC and
ALLMULTI and usage documented in virtio-net.h allowing the guest to
manipulate packet receiving options. We don't export a feature for
this until we also add the MAC filter table.
Note, for compatibility with older guest drivers we need to default
to promiscuous.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++-
hw/virtio-net.h | 11 +++++++++++
2 files changed, 58 insertions(+), 1 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 5a8287b..7e130f7 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,7 +16,7 @@
#include "qemu-timer.h"
#include "virtio-net.h"
-#define VIRTIO_NET_VM_VERSION 3
+#define VIRTIO_NET_VM_VERSION 4
typedef struct VirtIONet
{
@@ -30,6 +30,8 @@ typedef struct VirtIONet
QEMUTimer *tx_timer;
int tx_timer_active;
int mergeable_rx_bufs;
+ int promisc;
+ int allmulti;
} VirtIONet;
/* TODO
@@ -78,6 +80,15 @@ static void virtio_net_set_link_status(VLANClientState *vc)
virtio_notify_config(&n->vdev);
}
+static void virtio_net_reset(VirtIODevice *vdev)
+{
+ VirtIONet *n = to_virtio_net(vdev);
+
+ /* Reset back to compatibility mode */
+ n->promisc = 1;
+ n->allmulti = 0;
+}
+
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
{
uint32_t features = (1 << VIRTIO_NET_F_MAC) |
@@ -94,8 +105,31 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF));
}
+static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ uint8_t on;
+
+ if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) {
+ fprintf(stderr, "virtio-net ctrl invalid rx mode command\n");
+ exit(1);
+ }
+
+ on = ldub_p(elem->out_sg[1].iov_base);
+
+ if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC)
+ n->promisc = on;
+ else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI)
+ n->allmulti = on;
+ else
+ return VIRTIO_NET_ERR;
+
+ return VIRTIO_NET_OK;
+}
+
static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
{
+ VirtIONet *n = to_virtio_net(vdev);
struct virtio_net_ctrl_hdr ctrl;
virtio_net_ctrl_ack status = VIRTIO_NET_ERR;
VirtQueueElement elem;
@@ -115,6 +149,9 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
ctrl.class = ldub_p(elem.out_sg[0].iov_base);
ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class));
+ if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE)
+ status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem);
+
stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
virtqueue_push(vq, &elem, sizeof(status));
@@ -339,6 +376,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, n->tx_timer_active);
qemu_put_be32(f, n->mergeable_rx_bufs);
qemu_put_be16(f, n->status);
+ qemu_put_be32(f, n->promisc);
+ qemu_put_be32(f, n->allmulti);
}
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -357,6 +396,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
if (version_id >= 3)
n->status = qemu_get_be16(f);
+ if (version_id >= 4) {
+ n->promisc = qemu_get_be32(f);
+ n->allmulti = qemu_get_be32(f);
+ }
+
if (n->tx_timer_active) {
qemu_mod_timer(n->tx_timer,
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -385,6 +429,7 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->vdev.set_config = virtio_net_set_config;
n->vdev.get_features = virtio_net_get_features;
n->vdev.set_features = virtio_net_set_features;
+ n->vdev.reset = virtio_net_reset;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);
n->ctrl_vq = virtio_add_queue(&n->vdev, 16, virtio_net_handle_ctrl);
@@ -399,6 +444,7 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->tx_timer = qemu_new_timer(vm_clock, virtio_net_tx_timer, n);
n->tx_timer_active = 0;
n->mergeable_rx_bufs = 0;
+ n->promisc = 1; /* for compatibility */
register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 119e38d..ce8e4b4 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -41,6 +41,7 @@
#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */
#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
+#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
@@ -102,4 +103,14 @@ typedef uint8_t virtio_net_ctrl_ack;
#define VIRTIO_NET_OK 0
#define VIRTIO_NET_ERR 1
+/*
+ * Control the RX mode, ie. promisucous and allmulti. PROMISC and
+ * ALLMULTI commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable. These commands
+ * are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ */
+#define VIRTIO_NET_CTRL_RX_MODE 0
+ #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1
+
#endif
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 6/8] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
` (4 preceding siblings ...)
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 5/8] qemu:virtio-net: Add promiscuous and all-multicast mode bits Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 7/8] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 8/8] qemu:virtio-net: Add VLAN filtering Alex Williamson
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Make use of the new RX_MODE control virtqueue class by dropping
packets the guest doesn't want to see.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 28 ++++++++++++++++++++++++++++
1 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 7e130f7..001169d 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -222,6 +222,31 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
return offset;
}
+static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
+{
+ static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ uint8_t *ptr = (uint8_t *)buf;
+
+ if (n->promisc)
+ return 1;
+
+#ifdef TAP_VNET_HDR
+ if (tap_has_vnet_hdr(n->vc->vlan->first_client))
+ ptr += sizeof(struct virtio_net_hdr);
+#endif
+
+ if ((ptr[0] & 1) && n->allmulti)
+ return 1;
+
+ if (!memcmp(ptr, bcast, sizeof(bcast)))
+ return 1;
+
+ if (!memcmp(ptr, n->mac, ETH_ALEN))
+ return 1;
+
+ return 0;
+}
+
static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
{
VirtIONet *n = opaque;
@@ -231,6 +256,9 @@ static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
if (!do_virtio_net_can_receive(n, size))
return;
+ if (!receive_filter(n, buf, size))
+ return;
+
/* hdr_len refers to the header we supply to the guest */
hdr_len = n->mergeable_rx_bufs ?
sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr);
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 7/8] qemu:virtio-net: Add additional MACs via a filter table
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
` (5 preceding siblings ...)
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 6/8] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 8/8] qemu:virtio-net: Add VLAN filtering Alex Williamson
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Create a filter table and allow the guest to populate it with the
MAC class control commands. We manage the size and usage of the
filter table including enabling promiscuous and all-multi modes
as necessary. The guest should therefore assume the table is
infinite. Eventually this might allow us to bind directly to a
hardware NIC and manipulate a physical MAC filter.
The specifics of the TABLE_SET command are documented in
virtio-net.h. Separate buffers in the same command are used
for unicaste and multicast addresses for priority and
sychronization. With this we can export the VIRTIO_NET_F_CTRL_RX
feature bit.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
hw/virtio-net.h | 22 ++++++++++++++
2 files changed, 109 insertions(+), 2 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 001169d..524ef37 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,7 +16,9 @@
#include "qemu-timer.h"
#include "virtio-net.h"
-#define VIRTIO_NET_VM_VERSION 4
+#define VIRTIO_NET_VM_VERSION 5
+
+#define MAC_TABLE_ENTRIES 32
typedef struct VirtIONet
{
@@ -32,6 +34,10 @@ typedef struct VirtIONet
int mergeable_rx_bufs;
int promisc;
int allmulti;
+ struct {
+ int in_use;
+ uint8_t *macs;
+ } mac_table;
} VirtIONet;
/* TODO
@@ -87,13 +93,18 @@ static void virtio_net_reset(VirtIODevice *vdev)
/* Reset back to compatibility mode */
n->promisc = 1;
n->allmulti = 0;
+
+ /* Flush any MAC filter table state */
+ n->mac_table.in_use = 0;
+ memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
}
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
{
uint32_t features = (1 << VIRTIO_NET_F_MAC) |
(1 << VIRTIO_NET_F_STATUS) |
- (1 << VIRTIO_NET_F_CTRL_VQ);
+ (1 << VIRTIO_NET_F_CTRL_VQ) |
+ (1 << VIRTIO_NET_F_CTRL_RX);
return features;
}
@@ -127,6 +138,53 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
return VIRTIO_NET_OK;
}
+static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ struct virtio_net_ctrl_mac mac_data;
+
+ if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 ||
+ elem->out_sg[1].iov_len < sizeof(mac_data) ||
+ elem->out_sg[2].iov_len < sizeof(mac_data))
+ return VIRTIO_NET_ERR;
+
+ n->mac_table.in_use = 0;
+ memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
+
+ mac_data.entries = ldl_le_p(elem->out_sg[1].iov_base);
+
+ if (sizeof(mac_data.entries) +
+ (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len)
+ return VIRTIO_NET_ERR;
+
+ if (mac_data.entries <= MAC_TABLE_ENTRIES) {
+ memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data),
+ mac_data.entries * ETH_ALEN);
+ n->mac_table.in_use += mac_data.entries;
+ } else {
+ n->promisc = 1;
+ return VIRTIO_NET_OK;
+ }
+
+ mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
+
+ if (sizeof(mac_data.entries) +
+ (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len)
+ return VIRTIO_NET_ERR;
+
+ if (mac_data.entries) {
+ if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
+ memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN),
+ elem->out_sg[2].iov_base + sizeof(mac_data),
+ mac_data.entries * ETH_ALEN);
+ n->mac_table.in_use += mac_data.entries;
+ } else
+ n->allmulti = 1;
+ }
+
+ return VIRTIO_NET_OK;
+}
+
static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
@@ -151,6 +209,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE)
status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem);
+ else if (ctrl.class == VIRTIO_NET_CTRL_MAC)
+ status = virtio_net_handle_mac(n, ctrl.cmd, &elem);
stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
@@ -226,6 +286,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
{
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint8_t *ptr = (uint8_t *)buf;
+ int i;
if (n->promisc)
return 1;
@@ -244,6 +305,11 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
if (!memcmp(ptr, n->mac, ETH_ALEN))
return 1;
+ for (i = 0; i < n->mac_table.in_use; i++) {
+ if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
+ return 1;
+ }
+
return 0;
}
@@ -406,6 +472,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
qemu_put_be16(f, n->status);
qemu_put_be32(f, n->promisc);
qemu_put_be32(f, n->allmulti);
+ qemu_put_be32(f, n->mac_table.in_use);
+ qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
}
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -429,6 +497,19 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
n->allmulti = qemu_get_be32(f);
}
+ if (version_id >= 5) {
+ n->mac_table.in_use = qemu_get_be32(f);
+ /* MAC_TABLE_ENTRIES may be different from the saved image */
+ if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
+ qemu_get_buffer(f, n->mac_table.macs,
+ n->mac_table.in_use * ETH_ALEN);
+ } else if (n->mac_table.in_use) {
+ qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR);
+ n->promisc = 1;
+ n->mac_table.in_use = 0;
+ }
+ }
+
if (n->tx_timer_active) {
qemu_mod_timer(n->tx_timer,
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -474,6 +555,10 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->mergeable_rx_bufs = 0;
n->promisc = 1; /* for compatibility */
+ n->mac_table.macs = qemu_mallocz(MAC_TABLE_ENTRIES * ETH_ALEN);
+ if (!n->mac_table.macs)
+ return;
+
register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
}
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index ce8e4b4..291fa9d 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -113,4 +113,26 @@ typedef uint8_t virtio_net_ctrl_ack;
#define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0
#define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1
+/*
+ * Control the MAC filter table.
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite. Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists. Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ */
+struct virtio_net_ctrl_mac {
+ uint32_t entries;
+ uint8_t macs[][ETH_ALEN];
+};
+#define VIRTIO_NET_CTRL_MAC 1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+
#endif
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [Qemu-devel] [PATCH v3 8/8] qemu:virtio-net: Add VLAN filtering
2009-02-05 0:17 [Qemu-devel] [PATCH v3 0/8] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
` (6 preceding siblings ...)
2009-02-05 0:18 ` [Qemu-devel] [PATCH v3 7/8] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
@ 2009-02-05 0:18 ` Alex Williamson
7 siblings, 0 replies; 10+ messages in thread
From: Alex Williamson @ 2009-02-05 0:18 UTC (permalink / raw)
To: anthony, qemu-devel; +Cc: markmc, alex.williamson, kvm
Use the control virtqueue to allow the guest to enable and manipulate
a VLAN filter table. This allows us to drop more packets the guest
doesn't want to see. We define a new VLAN class for the control
virtqueue with commands ADD and DEL with usage defined in virtio-net.h.
Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---
hw/virtio-net.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++---
hw/virtio-net.h | 14 ++++++++++++++
2 files changed, 63 insertions(+), 3 deletions(-)
diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 524ef37..62153e9 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -16,9 +16,10 @@
#include "qemu-timer.h"
#include "virtio-net.h"
-#define VIRTIO_NET_VM_VERSION 5
+#define VIRTIO_NET_VM_VERSION 6
#define MAC_TABLE_ENTRIES 32
+#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */
typedef struct VirtIONet
{
@@ -38,6 +39,7 @@ typedef struct VirtIONet
int in_use;
uint8_t *macs;
} mac_table;
+ uint32_t *vlans;
} VirtIONet;
/* TODO
@@ -94,9 +96,10 @@ static void virtio_net_reset(VirtIODevice *vdev)
n->promisc = 1;
n->allmulti = 0;
- /* Flush any MAC filter table state */
+ /* Flush any MAC and VLAN filter table state */
n->mac_table.in_use = 0;
memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
+ memset(n->vlans, 0, MAX_VLAN >> 3);
}
static uint32_t virtio_net_get_features(VirtIODevice *vdev)
@@ -104,7 +107,8 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev)
uint32_t features = (1 << VIRTIO_NET_F_MAC) |
(1 << VIRTIO_NET_F_STATUS) |
(1 << VIRTIO_NET_F_CTRL_VQ) |
- (1 << VIRTIO_NET_F_CTRL_RX);
+ (1 << VIRTIO_NET_F_CTRL_RX) |
+ (1 << VIRTIO_NET_F_CTRL_VLAN);
return features;
}
@@ -185,6 +189,31 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
return VIRTIO_NET_OK;
}
+static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
+ VirtQueueElement *elem)
+{
+ uint16_t vid;
+
+ if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) {
+ fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+ return VIRTIO_NET_ERR;
+ }
+
+ vid = lduw_le_p(elem->out_sg[1].iov_base);
+
+ if (vid >= MAX_VLAN)
+ return VIRTIO_NET_ERR;
+
+ if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
+ n->vlans[vid >> 5] |= (1U << (vid & 0x1f));
+ else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
+ n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f));
+ else
+ return VIRTIO_NET_ERR;
+
+ return VIRTIO_NET_OK;
+}
+
static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIONet *n = to_virtio_net(vdev);
@@ -211,6 +240,8 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem);
else if (ctrl.class == VIRTIO_NET_CTRL_MAC)
status = virtio_net_handle_mac(n, ctrl.cmd, &elem);
+ else if (ctrl.class == VIRTIO_NET_CTRL_VLAN)
+ status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem);
stb_p(elem.in_sg[elem.in_num - 1].iov_base, status);
@@ -285,6 +316,7 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
{
static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ static const uint8_t vlan[] = {0x81, 0x00};
uint8_t *ptr = (uint8_t *)buf;
int i;
@@ -296,6 +328,12 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
ptr += sizeof(struct virtio_net_hdr);
#endif
+ if (!memcmp(&ptr[12], vlan, sizeof(vlan))) {
+ int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff;
+ if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f))))
+ return 0;
+ }
+
if ((ptr[0] & 1) && n->allmulti)
return 1;
@@ -474,6 +512,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
qemu_put_be32(f, n->allmulti);
qemu_put_be32(f, n->mac_table.in_use);
qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
+ qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
}
static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -510,6 +549,9 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
}
}
+ if (version_id >= 6)
+ qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3);
+
if (n->tx_timer_active) {
qemu_mod_timer(n->tx_timer,
qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -559,6 +601,10 @@ void virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
if (!n->mac_table.macs)
return;
+ n->vlans = qemu_mallocz(MAX_VLAN >> 3);
+ if (!n->vlans)
+ return;
+
register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
virtio_net_save, virtio_net_load, n);
}
diff --git a/hw/virtio-net.h b/hw/virtio-net.h
index 291fa9d..95587f7 100644
--- a/hw/virtio-net.h
+++ b/hw/virtio-net.h
@@ -42,6 +42,7 @@
#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */
#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */
#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */
+#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */
#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
@@ -135,4 +136,17 @@ struct virtio_net_ctrl_mac {
#define VIRTIO_NET_CTRL_MAC 1
#define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filterd by the hypervisor. Del is the
+ * opposite of add. Both commands expect an out entry containing a 2
+ * byte VLAN ID. VLAN filterting is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN 2
+ #define VIRTIO_NET_CTRL_VLAN_ADD 0
+ #define VIRTIO_NET_CTRL_VLAN_DEL 1
+
#endif
^ permalink raw reply related [flat|nested] 10+ messages in thread