qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering
@ 2009-01-16 21:09 Alex Williamson
  2009-01-16 21:09 ` [Qemu-devel] [PATCH 1/7] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
                   ` (8 more replies)
  0 siblings, 9 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:09 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

This series adds the ability for the guest to set the virtio-net device
MAC address, a new control virtqueue for setting configuration data from
the guest, and new interfaces making use of the control virtqueue for
setting RX mode options, MAC filter table entries, and VLAN filter bits.
The end result is that the virtio-net device has more of the features of
a realy hardware NIC and can be configured to drop packets the guest
isn't interested in seeing.

This version incorporates the review comments from Mark McLoughlin,
particularly including much better commit logs, factoring control
commands into separate functions, and making a local ETH_ALEN define.
Also new in this version is the addition of VLAN filtering.  Please
comment and/or apply.  Thanks,

Alex

Note - This series depends on the previous patch sent to correct
save and load of the virtio-net state.
---

Alex Williamson (7):
      qemu:virtio-net: Add VLAN filtering
      qemu:virtio-net: Add additional MACs via a filter table
      qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti
      qemu:virtio-net: Add promiscuous and all-multicast mode bits
      qemu:virtio-net: Add a virtqueue for control commands from the guest
      qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses
      qemu:virtio-net: Allow setting the MAC address via set_config


 qemu/hw/virtio-net.c |  280 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   61 +++++++++++
 2 files changed, 333 insertions(+), 8 deletions(-)

-- 
Alex Williamson

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 1/7] qemu:virtio-net: Allow setting the MAC address via set_config
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
@ 2009-01-16 21:09 ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 2/7] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses Alex Williamson
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:09 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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>
---

 qemu/hw/virtio-net.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 6ce2ff5..3767ecc 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -45,7 +45,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;
@@ -55,6 +55,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;
@@ -437,7 +450,8 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
     if (!n)
         return NULL;
 
-    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] 14+ messages in thread

* [Qemu-devel] [PATCH 2/7] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
  2009-01-16 21:09 ` [Qemu-devel] [PATCH 1/7] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 3/7] qemu:virtio-net: Add a virtqueue for control commands from the guest Alex Williamson
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

Makes it much easier to search too.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 qemu/hw/virtio-net.c |   16 +++++++++-------
 1 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 3767ecc..44827bc 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -23,10 +23,12 @@
 
 #define VIRTIO_NET_VM_VERSION    3
 
+#define ETH_ALEN    6
+
 typedef struct VirtIONet
 {
     VirtIODevice vdev;
-    uint8_t mac[6];
+    uint8_t mac[ETH_ALEN];
     uint16_t status;
     VirtQueue *rx_vq;
     VirtQueue *tx_vq;
@@ -51,7 +53,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));
 }
 
@@ -62,8 +64,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);
     }
 }
@@ -394,7 +396,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);
 
@@ -416,7 +418,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);
 
@@ -456,7 +458,7 @@ PCIDevice *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);

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 3/7] qemu:virtio-net: Add a virtqueue for control commands from the guest
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
  2009-01-16 21:09 ` [Qemu-devel] [PATCH 1/7] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 2/7] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 4/7] qemu:virtio-net: Add promiscuous and all-multicast mode bits Alex Williamson
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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>
---

 qemu/hw/virtio-net.c |   29 +++++++++++++++++++++++++++++
 qemu/hw/virtio-net.h |   17 +++++++++++++++++
 2 files changed, 46 insertions(+), 0 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 44827bc..3d8cba7 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -32,6 +32,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;
@@ -130,6 +131,33 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
 #endif
 }
 
+static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
+{
+    struct virtio_net_ctrl_hdr *ctrl;
+    virtio_net_ctrl_ack *status;
+    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 = elem.out_sg[0].iov_base;
+        status = elem.in_sg[elem.in_num - 1].iov_base;
+        *status = VIRTIO_NET_ERR;
+
+        virtqueue_push(vq, &elem, sizeof(*status));
+        virtio_notify(vdev, vq);
+    }
+}
+
 /* RX */
 
 static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)
@@ -458,6 +486,7 @@ PCIDevice *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/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index 9ac9e34..9a9641e 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -82,4 +82,21 @@ struct virtio_net_hdr_mrg_rxbuf
 
 PCIDevice *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] 14+ messages in thread

* [Qemu-devel] [PATCH 4/7] qemu:virtio-net: Add promiscuous and all-multicast mode bits
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (2 preceding siblings ...)
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 3/7] qemu:virtio-net: Add a virtqueue for control commands from the guest Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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.

Note, for compatibility with older guest drivers we need to default
to promiscuous.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 qemu/hw/virtio-net.c |   38 +++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |    9 +++++++++
 2 files changed, 46 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 3d8cba7..d34c299 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,7 +21,7 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    3
+#define VIRTIO_NET_VM_VERSION    4
 
 #define ETH_ALEN    6
 
@@ -37,6 +37,8 @@ typedef struct VirtIONet
     QEMUTimer *tx_timer;
     int tx_timer_active;
     int mergeable_rx_bufs;
+    int promisc;
+    int allmulti;
 } VirtIONet;
 
 /* TODO
@@ -131,8 +133,31 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
 #endif
 }
 
+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 = 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;
     VirtQueueElement elem;
@@ -153,6 +178,9 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
         status = elem.in_sg[elem.in_num - 1].iov_base;
         *status = VIRTIO_NET_ERR;
 
+        if (ctrl->class == VIRTIO_NET_CTRL_RX_MODE)
+            *status = virtio_net_handle_rx_mode(n, ctrl->cmd, &elem);
+
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
     }
@@ -435,6 +463,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
 #endif
 
     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)
@@ -459,6 +489,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
 
     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);
@@ -498,6 +533,7 @@ PCIDevice *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/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index 9a9641e..a4c4005 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -99,4 +99,13 @@ 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.
+ */
+#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] 14+ messages in thread

* [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (3 preceding siblings ...)
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 4/7] qemu:virtio-net: Add promiscuous and all-multicast mode bits Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-20 21:31   ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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>
---

 qemu/hw/virtio-net.c |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index d34c299..bb5348a 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -293,6 +293,25 @@ 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 uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+    if (n->promisc)
+        return 1;
+
+    if ((buf[0] & 1) && n->allmulti)
+        return 1;
+
+    if (!memcmp(buf, bcast, sizeof(bcast)))
+        return 1;
+
+    if (!memcmp(buf, n->mac, ETH_ALEN))
+        return 1;
+
+    return 0;
+}
+
 static void virtio_net_receive(void *opaque, const uint8_t *buf, int size)
 {
     VirtIONet *n = opaque;
@@ -302,6 +321,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] 14+ messages in thread

* [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (4 preceding siblings ...)
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-20 21:34   ` Alex Williamson
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering Alex Williamson
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

Use the control virtqueue to allow the guest to allocate and set a MAC
filter table.  A new MAC_TABLE class with commands ALLOC and SET are
defined and documented in virtio-net.h for manipulating the table.

We limit the size of the filter table to the host page size.  This is
likely bigger than makes sense for a filter table and prevents a
malicious guest from abusing the interface.

The fitler table is a simple fixed sized array defined by the ALLOC
command.  We do this to avoid locking issues with receiving packets
while updates to the table are being made.  The table is freed at
device reset as a way to allow "offline" resize, and resizing between
instances of the guest driver loading.  It's the guest's responsibility
to allocate the table before trying to use it.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 qemu/hw/virtio-net.c |   95 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   20 +++++++++++
 2 files changed, 114 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index bb5348a..d6b9641 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,7 +21,7 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    4
+#define VIRTIO_NET_VM_VERSION    5
 
 #define ETH_ALEN    6
 
@@ -39,6 +39,11 @@ typedef struct VirtIONet
     int mergeable_rx_bufs;
     int promisc;
     int allmulti;
+    struct {
+        int entries;
+        int in_use;
+        uint8_t *macs;
+    } mac_table;
 } VirtIONet;
 
 /* TODO
@@ -87,6 +92,17 @@ 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);
+
+    /* Allow the MAC filter table to be re-allocated after a device reset */
+    n->mac_table.in_use = 0;
+    n->mac_table.entries = 0;
+    qemu_free(n->mac_table.macs);
+    n->mac_table.macs = NULL;
+}
+
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
 {
     uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
@@ -155,6 +171,58 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
     return VIRTIO_NET_OK;
 }
 
+static int virtio_net_handle_mac_table(VirtIONet *n, uint8_t cmd,
+                                       VirtQueueElement *elem)
+{
+    if (cmd == VIRTIO_NET_CTRL_MAC_TABLE_ALLOC) {
+        uint32_t *entries;
+        unsigned int size;
+
+        if (n->mac_table.entries || elem->out_num != 2 ||
+            elem->out_sg[1].iov_len != sizeof(*entries))
+            return VIRTIO_NET_ERR;
+
+        entries = elem->out_sg[1].iov_base;
+        size = *entries * ETH_ALEN;
+
+        /*
+         * Limit the MAC filter table to a single page to protect from
+         * malicious guests.  Probably bigger than makes sense anyway.
+         */
+        if (size > getpagesize())
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.macs = qemu_mallocz(size);
+        if (!n->mac_table.macs)
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.entries = *entries;
+        return VIRTIO_NET_OK;
+
+    } else if (cmd == VIRTIO_NET_CTRL_MAC_TABLE_SET) {
+        int entries = 0;
+
+        if (!n->mac_table.entries || elem->out_num > 2)
+            return VIRTIO_NET_ERR;
+
+        if (elem->out_num == 2)
+            entries = elem->out_sg[1].iov_len / ETH_ALEN;
+
+        if (entries > n->mac_table.entries)
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.in_use = 0;
+        if (entries) {
+            memcpy(n->mac_table.macs, elem->out_sg[1].iov_base,
+                   elem->out_sg[1].iov_len);
+            n->mac_table.in_use = entries;
+        }
+        return VIRTIO_NET_OK;
+    }
+
+    return VIRTIO_NET_ERR;
+}
+
 static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
     VirtIONet *n = to_virtio_net(vdev);
@@ -180,6 +248,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_TABLE)
+            *status = virtio_net_handle_mac_table(n, ctrl->cmd, &elem);
 
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
@@ -296,6 +366,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 uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    int i;
 
     if (n->promisc)
         return 1;
@@ -309,6 +380,11 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
     if (!memcmp(buf, n->mac, ETH_ALEN))
         return 1;
 
+    for (i = 0; i < n->mac_table.in_use; i++) {
+        if (!memcmp(buf, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
+            return 1;
+    }
+
     return 0;
 }
 
@@ -487,6 +563,10 @@ 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.entries);
+    qemu_put_be32(f, n->mac_table.in_use);
+    if (n->mac_table.entries)
+        qemu_put_buffer(f, n->mac_table.macs, n->mac_table.entries * ETH_ALEN);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -516,6 +596,18 @@ 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.entries = qemu_get_be32(f);
+        n->mac_table.in_use = qemu_get_be32(f);
+        if (n->mac_table.entries) {
+            n->mac_table.macs = qemu_mallocz(n->mac_table.entries * ETH_ALEN);
+            if (!n->mac_table.macs)
+                return -ENOMEM;
+            qemu_get_buffer(f, n->mac_table.macs,
+                            n->mac_table.entries * ETH_ALEN);
+        }
+    }
+ 
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -541,6 +633,7 @@ PCIDevice *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);
diff --git a/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index a4c4005..6faf497 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -108,4 +108,24 @@ 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 ALLOC command requires a 4 byte sg entry indicating the size of
+ * the MAC filter table to be allocated in number of entries
+ * (ie. bytes = entries * ETH_ALEN).  The MAC filter table may only be
+ * allocated once after a device reset.  A device reset frees the MAC
+ * filter table, allowing a new ALLOC.  The current implementation limits
+ * the size to a single host page.
+ *
+ * The SET command requires an out sg entry containing a buffer of the
+ * entire MAC filter table.  The format is a simple byte stream
+ * concatenating all of the ETH_ALEN MAC adresses to be inserted into
+ * the table.  Partial updates are not available.  The SET command can
+ * only succeed if there is a table allocated.
+ */
+#define VIRTIO_NET_CTRL_MAC_TABLE  1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_ALLOC      0
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET        1
+
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (5 preceding siblings ...)
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
@ 2009-01-16 21:10 ` Alex Williamson
  2009-01-20 16:45   ` Alex Williamson
  2009-01-19  9:45 ` [Qemu-devel] Re: [PATCH 0/7] qemu:virtio-net: Add MAC and " Mark McLoughlin
  2009-01-20  2:27 ` Anthony Liguori
  8 siblings, 1 reply; 14+ messages in thread
From: Alex Williamson @ 2009-01-16 21:10 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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 ENABLE, ADD, and KILL with usage defined in
virtio-net.h.  By default VLAN filtering is disabled to allow backwards
compatibility with guest drivers.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 qemu/hw/virtio-net.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   15 +++++++++++
 2 files changed, 84 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index d6b9641..b0f0282 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,9 +21,10 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    5
+#define VIRTIO_NET_VM_VERSION    6
 
 #define ETH_ALEN    6
+#define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
 
 typedef struct VirtIONet
 {
@@ -44,6 +45,10 @@ typedef struct VirtIONet
         int in_use;
         uint8_t *macs;
     } mac_table;
+    struct {
+        int enabled;
+        uint32_t *vlans;
+    } vlan_table;
 } VirtIONet;
 
 /* TODO
@@ -101,6 +106,9 @@ static void virtio_net_reset(VirtIODevice *vdev)
     n->mac_table.entries = 0;
     qemu_free(n->mac_table.macs);
     n->mac_table.macs = NULL;
+
+    n->vlan_table.enabled = 0;
+    memset(n->vlan_table.vlans, 0, MAX_VLAN >> 3);
 }
 
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
@@ -223,6 +231,45 @@ static int virtio_net_handle_mac_table(VirtIONet *n, uint8_t cmd,
     return VIRTIO_NET_ERR;
 }
 
+static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
+                                        VirtQueueElement *elem)
+{
+    uint16_t *vid;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ENABLE) {
+        uint8_t *on;
+
+        if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*on)) {
+            fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+            exit(1);
+        }
+
+        on = elem->out_sg[1].iov_base;
+
+        n->vlan_table.enabled = *on;
+        return VIRTIO_NET_OK;
+    }
+
+    if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*vid)) {
+        fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+        exit(1);
+    }
+
+    vid = elem->out_sg[1].iov_base;
+
+    if (*vid >= MAX_VLAN)
+        return VIRTIO_NET_ERR;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
+        n->vlan_table.vlans[*vid >> 5] |= (1U << (*vid & 0x1f));
+    else if (cmd == VIRTIO_NET_CTRL_VLAN_KILL)
+        n->vlan_table.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);
@@ -250,6 +297,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_TABLE)
             *status = virtio_net_handle_mac_table(n, ctrl->cmd, &elem);
+        else if (ctrl->class == VIRTIO_NET_CTRL_VLAN)
+            *status = virtio_net_handle_vlan_table(n, ctrl->cmd, &elem);
 
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
@@ -366,8 +415,15 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
 static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
 {
     static uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    static uint8_t vlan[] = {0x81, 0x00};
     int i;
 
+    if (n->vlan_table.enabled && !memcmp(&buf[12], vlan, sizeof(vlan))) {
+        int vid = be16_to_cpup((uint16_t *)(buf + 14)) & 0xfff;
+        if (!(n->vlan_table.vlans[vid >> 5] & (1U << (vid & 0x1f))))
+            return 0;
+    }
+
     if (n->promisc)
         return 1;
 
@@ -567,6 +623,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, n->mac_table.in_use);
     if (n->mac_table.entries)
         qemu_put_buffer(f, n->mac_table.macs, n->mac_table.entries * ETH_ALEN);
+    qemu_put_be32(f, n->vlan_table.enabled);
+    qemu_put_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -608,6 +666,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         }
     }
  
+    if (version_id >= 6) {
+        n->vlan_table.enabled = qemu_get_be32(f);
+        qemu_get_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
+    }
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -650,6 +713,11 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
     n->mergeable_rx_bufs = 0;
     n->promisc = 1; /* for compatibility */
 
+    /* VLAN filter table starts disabled for compatibility */
+    n->vlan_table.vlans = qemu_mallocz(MAX_VLAN >> 3);
+    if (!n->vlan_table.vlans)
+        return NULL;
+
     register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
                     virtio_net_save, virtio_net_load, n);
 
diff --git a/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index 6faf497..4ba8b2a 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -128,4 +128,19 @@ typedef uint8_t virtio_net_ctrl_ack;
  #define VIRTIO_NET_CTRL_MAC_TABLE_ALLOC      0
  #define VIRTIO_NET_CTRL_MAC_TABLE_SET        1
 
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/KILL interface.
+ * VLAN IDs not added will be dropped.  Kill is the opposite of add.
+ * Both commands expect an out entry containing a 2 byte VLAN ID.
+ * The ENABLE command expects an out entry containing a single byte,
+ * zero to disable, non-zero to enable.  The default state is disabled
+ * for compatibility.
+ */
+#define VIRTIO_NET_CTRL_VLAN       2
+ #define VIRTIO_NET_CTRL_VLAN_ENABLE          0
+ #define VIRTIO_NET_CTRL_VLAN_ADD             1
+ #define VIRTIO_NET_CTRL_VLAN_KILL            2
+
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (6 preceding siblings ...)
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering Alex Williamson
@ 2009-01-19  9:45 ` Mark McLoughlin
  2009-01-20  2:27 ` Anthony Liguori
  8 siblings, 0 replies; 14+ messages in thread
From: Mark McLoughlin @ 2009-01-19  9:45 UTC (permalink / raw)
  To: Alex Williamson; +Cc: qemu-devel, kvm

On Fri, 2009-01-16 at 14:09 -0700, Alex Williamson wrote:
> This series adds the ability for the guest to set the virtio-net device
> MAC address, a new control virtqueue for setting configuration data from
> the guest, and new interfaces making use of the control virtqueue for
> setting RX mode options, MAC filter table entries, and VLAN filter bits.
> The end result is that the virtio-net device has more of the features of
> a realy hardware NIC and can be configured to drop packets the guest
> isn't interested in seeing.
> 
> This version incorporates the review comments from Mark McLoughlin,
> particularly including much better commit logs, factoring control
> commands into separate functions, and making a local ETH_ALEN define.
> Also new in this version is the addition of VLAN filtering.  Please
> comment and/or apply.  Thanks,
> 
> Alex
> 
> Note - This series depends on the previous patch sent to correct
> save and load of the virtio-net state.

I think it makes sense to wait for the linux side to be accepted first.
It's there the virtio guest ABI is defined, really. Apart from that, the
whole series looks good to me:

Acked-by: Mark McLoughlin <markmc@redhat.com>

Cheers,
Mark.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] Re: [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering
  2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
                   ` (7 preceding siblings ...)
  2009-01-19  9:45 ` [Qemu-devel] Re: [PATCH 0/7] qemu:virtio-net: Add MAC and " Mark McLoughlin
@ 2009-01-20  2:27 ` Anthony Liguori
  8 siblings, 0 replies; 14+ messages in thread
From: Anthony Liguori @ 2009-01-20  2:27 UTC (permalink / raw)
  To: Alex Williamson; +Cc: markmc, Rusty Russell, qemu-devel, kvm

Any comments Rusty?  I'm waiting for input on the kernel bits to apply 
the QEMU side of this.

Regards,

Anthony Liguori

Alex Williamson wrote:
> This series adds the ability for the guest to set the virtio-net device
> MAC address, a new control virtqueue for setting configuration data from
> the guest, and new interfaces making use of the control virtqueue for
> setting RX mode options, MAC filter table entries, and VLAN filter bits.
> The end result is that the virtio-net device has more of the features of
> a realy hardware NIC and can be configured to drop packets the guest
> isn't interested in seeing.
>
> This version incorporates the review comments from Mark McLoughlin,
> particularly including much better commit logs, factoring control
> commands into separate functions, and making a local ETH_ALEN define.
> Also new in this version is the addition of VLAN filtering.  Please
> comment and/or apply.  Thanks,
>
> Alex
>
> Note - This series depends on the previous patch sent to correct
> save and load of the virtio-net state.
> ---
>
> Alex Williamson (7):
>       qemu:virtio-net: Add VLAN filtering
>       qemu:virtio-net: Add additional MACs via a filter table
>       qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti
>       qemu:virtio-net: Add promiscuous and all-multicast mode bits
>       qemu:virtio-net: Add a virtqueue for control commands from the guest
>       qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses
>       qemu:virtio-net: Allow setting the MAC address via set_config
>
>
>  qemu/hw/virtio-net.c |  280 +++++++++++++++++++++++++++++++++++++++++++++++++-
>  qemu/hw/virtio-net.h |   61 +++++++++++
>  2 files changed, 333 insertions(+), 8 deletions(-)
>
>   

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering Alex Williamson
@ 2009-01-20 16:45   ` Alex Williamson
  2009-01-20 21:38     ` Alex Williamson
  0 siblings, 1 reply; 14+ messages in thread
From: Alex Williamson @ 2009-01-20 16:45 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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 ENABLE, ADD, and DEL with usage defined in
virtio-net.h.  By default VLAN filtering is disabled to allow backwards
compatibility with guest drivers.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Acked-by: Mark McLoughlin <markmc@redhat.com>
---

Updated to reflect VLAN_KILL -> VLAN_DEL rename in the guest driver

 qemu/hw/virtio-net.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   15 +++++++++++
 2 files changed, 84 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index d6b9641..8d37bb1 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,9 +21,10 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    5
+#define VIRTIO_NET_VM_VERSION    6
 
 #define ETH_ALEN    6
+#define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
 
 typedef struct VirtIONet
 {
@@ -44,6 +45,10 @@ typedef struct VirtIONet
         int in_use;
         uint8_t *macs;
     } mac_table;
+    struct {
+        int enabled;
+        uint32_t *vlans;
+    } vlan_table;
 } VirtIONet;
 
 /* TODO
@@ -101,6 +106,9 @@ static void virtio_net_reset(VirtIODevice *vdev)
     n->mac_table.entries = 0;
     qemu_free(n->mac_table.macs);
     n->mac_table.macs = NULL;
+
+    n->vlan_table.enabled = 0;
+    memset(n->vlan_table.vlans, 0, MAX_VLAN >> 3);
 }
 
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
@@ -223,6 +231,45 @@ static int virtio_net_handle_mac_table(VirtIONet *n, uint8_t cmd,
     return VIRTIO_NET_ERR;
 }
 
+static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
+                                        VirtQueueElement *elem)
+{
+    uint16_t *vid;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ENABLE) {
+        uint8_t *on;
+
+        if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*on)) {
+            fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+            exit(1);
+        }
+
+        on = elem->out_sg[1].iov_base;
+
+        n->vlan_table.enabled = *on;
+        return VIRTIO_NET_OK;
+    }
+
+    if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*vid)) {
+        fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+        exit(1);
+    }
+
+    vid = elem->out_sg[1].iov_base;
+
+    if (*vid >= MAX_VLAN)
+        return VIRTIO_NET_ERR;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
+        n->vlan_table.vlans[*vid >> 5] |= (1U << (*vid & 0x1f));
+    else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
+        n->vlan_table.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);
@@ -250,6 +297,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_TABLE)
             *status = virtio_net_handle_mac_table(n, ctrl->cmd, &elem);
+        else if (ctrl->class == VIRTIO_NET_CTRL_VLAN)
+            *status = virtio_net_handle_vlan_table(n, ctrl->cmd, &elem);
 
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
@@ -366,8 +415,15 @@ static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
 static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
 {
     static uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    static uint8_t vlan[] = {0x81, 0x00};
     int i;
 
+    if (n->vlan_table.enabled && !memcmp(&buf[12], vlan, sizeof(vlan))) {
+        int vid = be16_to_cpup((uint16_t *)(buf + 14)) & 0xfff;
+        if (!(n->vlan_table.vlans[vid >> 5] & (1U << (vid & 0x1f))))
+            return 0;
+    }
+
     if (n->promisc)
         return 1;
 
@@ -567,6 +623,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, n->mac_table.in_use);
     if (n->mac_table.entries)
         qemu_put_buffer(f, n->mac_table.macs, n->mac_table.entries * ETH_ALEN);
+    qemu_put_be32(f, n->vlan_table.enabled);
+    qemu_put_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -608,6 +666,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         }
     }
  
+    if (version_id >= 6) {
+        n->vlan_table.enabled = qemu_get_be32(f);
+        qemu_get_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
+    }
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -650,6 +713,11 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
     n->mergeable_rx_bufs = 0;
     n->promisc = 1; /* for compatibility */
 
+    /* VLAN filter table starts disabled for compatibility */
+    n->vlan_table.vlans = qemu_mallocz(MAX_VLAN >> 3);
+    if (!n->vlan_table.vlans)
+        return NULL;
+
     register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
                     virtio_net_save, virtio_net_load, n);
 
diff --git a/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index 6faf497..bf40207 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -128,4 +128,19 @@ typedef uint8_t virtio_net_ctrl_ack;
  #define VIRTIO_NET_CTRL_MAC_TABLE_ALLOC      0
  #define VIRTIO_NET_CTRL_MAC_TABLE_SET        1
 
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added will be dropped.  Del is the opposite of add.
+ * Both commands expect an out entry containing a 2 byte VLAN ID.
+ * The ENABLE command expects an out entry containing a single byte,
+ * zero to disable, non-zero to enable.  The default state is disabled
+ * for compatibility.
+ */
+#define VIRTIO_NET_CTRL_VLAN       2
+ #define VIRTIO_NET_CTRL_VLAN_ENABLE          0
+ #define VIRTIO_NET_CTRL_VLAN_ADD             1
+ #define VIRTIO_NET_CTRL_VLAN_DEL             2
+
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
@ 2009-01-20 21:31   ` Alex Williamson
  0 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-20 21:31 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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>
---

 Updated to look past vnet_hdr where necessary.

 qemu/hw/virtio-net.c |   28 ++++++++++++++++++++++++++++
 1 files changed, 28 insertions(+), 0 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index d34c299..18877b4 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -293,6 +293,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 uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    uint8_t *ptr = (uint8_t *)buf;
+
+#ifdef TAP_VNET_HDR
+    if (tap_has_vnet_hdr(n->vc->vlan->first_client))
+        ptr += sizeof(struct virtio_net_hdr);
+#endif
+
+    if (n->promisc)
+        return 1;
+
+    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;
@@ -302,6 +327,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] 14+ messages in thread

* [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table
  2009-01-16 21:10 ` [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
@ 2009-01-20 21:34   ` Alex Williamson
  0 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-20 21:34 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

Use the control virtqueue to allow the guest to allocate and set a MAC
filter table.  A new MAC_TABLE class with commands ALLOC and SET are
defined and documented in virtio-net.h for manipulating the table.

We limit the size of the filter table to the host page size.  This is
likely bigger than makes sense for a filter table and prevents a
malicious guest from abusing the interface.

The fitler table is a simple fixed sized array defined by the ALLOC
command.  We do this to avoid locking issues with receiving packets
while updates to the table are being made.  The table is freed at
device reset as a way to allow "offline" resize, and resizing between
instances of the guest driver loading.  It's the guest's responsibility
to allocate the table before trying to use it.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 Updated to reflect change in receive_filter() looking past vnet_hdr.

 qemu/hw/virtio-net.c |   95 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   20 +++++++++++
 2 files changed, 114 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 18877b4..528171e 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,7 +21,7 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    4
+#define VIRTIO_NET_VM_VERSION    5
 
 #define ETH_ALEN    6
 
@@ -39,6 +39,11 @@ typedef struct VirtIONet
     int mergeable_rx_bufs;
     int promisc;
     int allmulti;
+    struct {
+        int entries;
+        int in_use;
+        uint8_t *macs;
+    } mac_table;
 } VirtIONet;
 
 /* TODO
@@ -87,6 +92,17 @@ 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);
+
+    /* Allow the MAC filter table to be re-allocated after a device reset */
+    n->mac_table.in_use = 0;
+    n->mac_table.entries = 0;
+    qemu_free(n->mac_table.macs);
+    n->mac_table.macs = NULL;
+}
+
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
 {
     uint32_t features = (1 << VIRTIO_NET_F_MAC) | (1 << VIRTIO_NET_F_STATUS);
@@ -155,6 +171,58 @@ static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd,
     return VIRTIO_NET_OK;
 }
 
+static int virtio_net_handle_mac_table(VirtIONet *n, uint8_t cmd,
+                                       VirtQueueElement *elem)
+{
+    if (cmd == VIRTIO_NET_CTRL_MAC_TABLE_ALLOC) {
+        uint32_t *entries;
+        unsigned int size;
+
+        if (n->mac_table.entries || elem->out_num != 2 ||
+            elem->out_sg[1].iov_len != sizeof(*entries))
+            return VIRTIO_NET_ERR;
+
+        entries = elem->out_sg[1].iov_base;
+        size = *entries * ETH_ALEN;
+
+        /*
+         * Limit the MAC filter table to a single page to protect from
+         * malicious guests.  Probably bigger than makes sense anyway.
+         */
+        if (size > getpagesize())
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.macs = qemu_mallocz(size);
+        if (!n->mac_table.macs)
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.entries = *entries;
+        return VIRTIO_NET_OK;
+
+    } else if (cmd == VIRTIO_NET_CTRL_MAC_TABLE_SET) {
+        int entries = 0;
+
+        if (!n->mac_table.entries || elem->out_num > 2)
+            return VIRTIO_NET_ERR;
+
+        if (elem->out_num == 2)
+            entries = elem->out_sg[1].iov_len / ETH_ALEN;
+
+        if (entries > n->mac_table.entries)
+            return VIRTIO_NET_ERR;
+
+        n->mac_table.in_use = 0;
+        if (entries) {
+            memcpy(n->mac_table.macs, elem->out_sg[1].iov_base,
+                   elem->out_sg[1].iov_len);
+            n->mac_table.in_use = entries;
+        }
+        return VIRTIO_NET_OK;
+    }
+
+    return VIRTIO_NET_ERR;
+}
+
 static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 {
     VirtIONet *n = to_virtio_net(vdev);
@@ -180,6 +248,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_TABLE)
+            *status = virtio_net_handle_mac_table(n, ctrl->cmd, &elem);
 
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
@@ -297,6 +367,7 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
 {
     static uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
     uint8_t *ptr = (uint8_t *)buf;
+    int i;
 
 #ifdef TAP_VNET_HDR
     if (tap_has_vnet_hdr(n->vc->vlan->first_client))
@@ -315,6 +386,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;
 }
 
@@ -493,6 +569,10 @@ 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.entries);
+    qemu_put_be32(f, n->mac_table.in_use);
+    if (n->mac_table.entries)
+        qemu_put_buffer(f, n->mac_table.macs, n->mac_table.entries * ETH_ALEN);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -522,6 +602,18 @@ 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.entries = qemu_get_be32(f);
+        n->mac_table.in_use = qemu_get_be32(f);
+        if (n->mac_table.entries) {
+            n->mac_table.macs = qemu_mallocz(n->mac_table.entries * ETH_ALEN);
+            if (!n->mac_table.macs)
+                return -ENOMEM;
+            qemu_get_buffer(f, n->mac_table.macs,
+                            n->mac_table.entries * ETH_ALEN);
+        }
+    }
+ 
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -547,6 +639,7 @@ PCIDevice *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);
diff --git a/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index a4c4005..6faf497 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -108,4 +108,24 @@ 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 ALLOC command requires a 4 byte sg entry indicating the size of
+ * the MAC filter table to be allocated in number of entries
+ * (ie. bytes = entries * ETH_ALEN).  The MAC filter table may only be
+ * allocated once after a device reset.  A device reset frees the MAC
+ * filter table, allowing a new ALLOC.  The current implementation limits
+ * the size to a single host page.
+ *
+ * The SET command requires an out sg entry containing a buffer of the
+ * entire MAC filter table.  The format is a simple byte stream
+ * concatenating all of the ETH_ALEN MAC adresses to be inserted into
+ * the table.  Partial updates are not available.  The SET command can
+ * only succeed if there is a table allocated.
+ */
+#define VIRTIO_NET_CTRL_MAC_TABLE  1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_ALLOC      0
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET        1
+
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering
  2009-01-20 16:45   ` Alex Williamson
@ 2009-01-20 21:38     ` Alex Williamson
  0 siblings, 0 replies; 14+ messages in thread
From: Alex Williamson @ 2009-01-20 21:38 UTC (permalink / raw)
  To: kvm; +Cc: markmc, qemu-devel

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 ENABLE, ADD, and DEL with usage defined in
virtio-net.h.  By default VLAN filtering is disabled to allow backwards
compatibility with guest drivers.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 Updated to reflect VLAN_KILL -> VLAN_DEL rename in the guest driver
 Updated to reflect change in receive_filter() looking past vnet_hdr.

 qemu/hw/virtio-net.c |   70 +++++++++++++++++++++++++++++++++++++++++++++++++-
 qemu/hw/virtio-net.h |   15 +++++++++++
 2 files changed, 84 insertions(+), 1 deletions(-)

diff --git a/qemu/hw/virtio-net.c b/qemu/hw/virtio-net.c
index 528171e..8f3c41d 100644
--- a/qemu/hw/virtio-net.c
+++ b/qemu/hw/virtio-net.c
@@ -21,9 +21,10 @@
 
 #define TAP_VNET_HDR
 
-#define VIRTIO_NET_VM_VERSION    5
+#define VIRTIO_NET_VM_VERSION    6
 
 #define ETH_ALEN    6
+#define MAX_VLAN    (1 << 12)   /* Per 802.1Q definition */
 
 typedef struct VirtIONet
 {
@@ -44,6 +45,10 @@ typedef struct VirtIONet
         int in_use;
         uint8_t *macs;
     } mac_table;
+    struct {
+        int enabled;
+        uint32_t *vlans;
+    } vlan_table;
 } VirtIONet;
 
 /* TODO
@@ -101,6 +106,9 @@ static void virtio_net_reset(VirtIODevice *vdev)
     n->mac_table.entries = 0;
     qemu_free(n->mac_table.macs);
     n->mac_table.macs = NULL;
+
+    n->vlan_table.enabled = 0;
+    memset(n->vlan_table.vlans, 0, MAX_VLAN >> 3);
 }
 
 static uint32_t virtio_net_get_features(VirtIODevice *vdev)
@@ -223,6 +231,45 @@ static int virtio_net_handle_mac_table(VirtIONet *n, uint8_t cmd,
     return VIRTIO_NET_ERR;
 }
 
+static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd,
+                                        VirtQueueElement *elem)
+{
+    uint16_t *vid;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ENABLE) {
+        uint8_t *on;
+
+        if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*on)) {
+            fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+            exit(1);
+        }
+
+        on = elem->out_sg[1].iov_base;
+
+        n->vlan_table.enabled = *on;
+        return VIRTIO_NET_OK;
+    }
+
+    if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(*vid)) {
+        fprintf(stderr, "virtio-net ctrl invalid vlan command\n");
+        exit(1);
+    }
+
+    vid = elem->out_sg[1].iov_base;
+
+    if (*vid >= MAX_VLAN)
+        return VIRTIO_NET_ERR;
+
+    if (cmd == VIRTIO_NET_CTRL_VLAN_ADD)
+        n->vlan_table.vlans[*vid >> 5] |= (1U << (*vid & 0x1f));
+    else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL)
+        n->vlan_table.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);
@@ -250,6 +297,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_TABLE)
             *status = virtio_net_handle_mac_table(n, ctrl->cmd, &elem);
+        else if (ctrl->class == VIRTIO_NET_CTRL_VLAN)
+            *status = virtio_net_handle_vlan_table(n, ctrl->cmd, &elem);
 
         virtqueue_push(vq, &elem, sizeof(*status));
         virtio_notify(vdev, vq);
@@ -366,6 +415,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 uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    static uint8_t vlan[] = {0x81, 0x00};
     uint8_t *ptr = (uint8_t *)buf;
     int i;
 
@@ -374,6 +424,12 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
         ptr += sizeof(struct virtio_net_hdr);
 #endif
 
+    if (n->vlan_table.enabled && !memcmp(&ptr[12], vlan, sizeof(vlan))) {
+        int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff;
+        if (!(n->vlan_table.vlans[vid >> 5] & (1U << (vid & 0x1f))))
+            return 0;
+    }
+
     if (n->promisc)
         return 1;
 
@@ -573,6 +629,8 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, n->mac_table.in_use);
     if (n->mac_table.entries)
         qemu_put_buffer(f, n->mac_table.macs, n->mac_table.entries * ETH_ALEN);
+    qemu_put_be32(f, n->vlan_table.enabled);
+    qemu_put_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
 }
 
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
@@ -614,6 +672,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         }
     }
  
+    if (version_id >= 6) {
+        n->vlan_table.enabled = qemu_get_be32(f);
+        qemu_get_buffer(f, (uint8_t *)n->vlan_table.vlans, MAX_VLAN >> 3);
+    }
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
@@ -656,6 +719,11 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
     n->mergeable_rx_bufs = 0;
     n->promisc = 1; /* for compatibility */
 
+    /* VLAN filter table starts disabled for compatibility */
+    n->vlan_table.vlans = qemu_mallocz(MAX_VLAN >> 3);
+    if (!n->vlan_table.vlans)
+        return NULL;
+
     register_savevm("virtio-net", virtio_net_id++, VIRTIO_NET_VM_VERSION,
                     virtio_net_save, virtio_net_load, n);
 
diff --git a/qemu/hw/virtio-net.h b/qemu/hw/virtio-net.h
index 6faf497..bf40207 100644
--- a/qemu/hw/virtio-net.h
+++ b/qemu/hw/virtio-net.h
@@ -128,4 +128,19 @@ typedef uint8_t virtio_net_ctrl_ack;
  #define VIRTIO_NET_CTRL_MAC_TABLE_ALLOC      0
  #define VIRTIO_NET_CTRL_MAC_TABLE_SET        1
 
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added will be dropped.  Del is the opposite of add.
+ * Both commands expect an out entry containing a 2 byte VLAN ID.
+ * The ENABLE command expects an out entry containing a single byte,
+ * zero to disable, non-zero to enable.  The default state is disabled
+ * for compatibility.
+ */
+#define VIRTIO_NET_CTRL_VLAN       2
+ #define VIRTIO_NET_CTRL_VLAN_ENABLE          0
+ #define VIRTIO_NET_CTRL_VLAN_ADD             1
+ #define VIRTIO_NET_CTRL_VLAN_DEL             2
+
 #endif

^ permalink raw reply related	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-01-20 21:40 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-01-16 21:09 [Qemu-devel] [PATCH 0/7] qemu:virtio-net: Add MAC and VLAN filtering Alex Williamson
2009-01-16 21:09 ` [Qemu-devel] [PATCH 1/7] qemu:virtio-net: Allow setting the MAC address via set_config Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 2/7] qemu:virtio-net: Define ETH_ALEN for use when manipulating MAC addresses Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 3/7] qemu:virtio-net: Add a virtqueue for control commands from the guest Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 4/7] qemu:virtio-net: Add promiscuous and all-multicast mode bits Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 5/7] qemu:virtio-net: Enable filtering based on MAC, promisc, broadcast and allmulti Alex Williamson
2009-01-20 21:31   ` Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 6/7] qemu:virtio-net: Add additional MACs via a filter table Alex Williamson
2009-01-20 21:34   ` Alex Williamson
2009-01-16 21:10 ` [Qemu-devel] [PATCH 7/7] qemu:virtio-net: Add VLAN filtering Alex Williamson
2009-01-20 16:45   ` Alex Williamson
2009-01-20 21:38     ` Alex Williamson
2009-01-19  9:45 ` [Qemu-devel] Re: [PATCH 0/7] qemu:virtio-net: Add MAC and " Mark McLoughlin
2009-01-20  2:27 ` Anthony Liguori

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).