qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Alex Williamson <alex.williamson@hp.com>
To: qemu-devel@nongnu.org
Cc: alex.williamson@hp.com, kvm@vger.kernel.org
Subject: [Qemu-devel] [PATCH][RFC] qemu:virtio-net: Use TUNSETTXFILTER for MAC filtering
Date: Thu, 05 Feb 2009 21:51:57 -0700	[thread overview]
Message-ID: <20090206044853.3116.46699.stgit@kvm.aw> (raw)

Now that virtio-net knows what packets the guest wants to see, we
can start moving the filtering down the stack.  This patch adds
an interface to set the software filter in the tap device.  It's
fairly limited, but we can back it up with our own filtering if it
overflows.

Here are a couple issues I'm still pondering:
 - Is the fd_rx_filter() interface sufficiently generic
 - Should vlan_set_hw_rx_filter() live in net.c or elsewhere
 - Is it ok to call fd_rx_filter() against all the vlan clients.  I
   exit on the first one, which covers the simple config.

Insterested in feedback.  Thanks,

Alex

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

 hw/virtio-net.c |   57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 net.c           |   28 +++++++++++++++++++++++++++
 net.h           |    3 +++
 3 files changed, 88 insertions(+), 0 deletions(-)

diff --git a/hw/virtio-net.c b/hw/virtio-net.c
index 62153e9..2556f42 100644
--- a/hw/virtio-net.c
+++ b/hw/virtio-net.c
@@ -15,6 +15,7 @@
 #include "net.h"
 #include "qemu-timer.h"
 #include "virtio-net.h"
+#include <net/if.h>
 
 #define VIRTIO_NET_VM_VERSION    6
 
@@ -35,6 +36,7 @@ typedef struct VirtIONet
     int mergeable_rx_bufs;
     int promisc;
     int allmulti;
+    int hw_mac_filter;
     struct {
         int in_use;
         uint8_t *macs;
@@ -88,6 +90,51 @@ static void virtio_net_set_link_status(VLANClientState *vc)
         virtio_notify_config(&n->vdev);
 }
 
+static int vlan_set_hw_rx_filter(VLANState *vlan, int flags,
+                                 int count, uint8_t *buf)
+{
+    VLANClientState *vc;
+
+    for (vc = vlan->first_client; vc != NULL; vc = vc->next) {
+        int ret;
+
+        if (!vc->fd_rx_filter)
+            continue;
+
+        ret = vc->fd_rx_filter(vc->opaque, flags, count, buf);
+        return (ret == count);
+    } 
+    return 0;
+}
+
+static void virtio_net_set_hw_rx_filter(VirtIONet *n)
+{
+    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    uint8_t *buf;
+    int flags = 0;
+
+    if (n->promisc)
+        flags |= IFF_PROMISC;
+    if (n->allmulti)
+        flags |= IFF_ALLMULTI;
+
+    buf = qemu_mallocz((n->mac_table.in_use + 2) * ETH_ALEN);
+    if (!buf) {
+        fprintf(stderr, "virtio-net no memory for set_rx_filter\n");
+        vlan_set_hw_rx_filter(n->vc->vlan, IFF_PROMISC, 0, NULL);
+        n->hw_mac_filter = 0;
+        return;
+    }
+
+    memcpy(&buf[ETH_ALEN*0], n->mac, ETH_ALEN);
+    memcpy(&buf[ETH_ALEN*1], bcast, ETH_ALEN);
+    memcpy(&buf[ETH_ALEN*2], n->mac_table.macs, n->mac_table.in_use * ETH_ALEN);
+
+    n->hw_mac_filter = vlan_set_hw_rx_filter(n->vc->vlan, flags,
+                                             n->mac_table.in_use + 2, buf);
+    qemu_free(buf);
+}
+
 static void virtio_net_reset(VirtIODevice *vdev)
 {
     VirtIONet *n = to_virtio_net(vdev);
@@ -99,6 +146,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
     /* 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);
+    virtio_net_set_hw_rx_filter(n);
     memset(n->vlans, 0, MAX_VLAN >> 3);
 }
 
@@ -247,6 +295,10 @@ static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
 
         virtqueue_push(vq, &elem, sizeof(status));
         virtio_notify(vdev, vq);
+
+        if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE ||
+            ctrl.class == VIRTIO_NET_CTRL_MAC)
+            virtio_net_set_hw_rx_filter(n);
     }
 }
 
@@ -334,6 +386,9 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
             return 0;
     }
 
+    if (n->hw_mac_filter)
+        return 1;
+
     if ((ptr[0] & 1) && n->allmulti)
         return 1;
 
@@ -552,6 +607,8 @@ 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);
 
+    virtio_net_set_hw_rx_filter(n);
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);
diff --git a/net.c b/net.c
index 8d9b3de..fcf5272 100644
--- a/net.c
+++ b/net.c
@@ -685,6 +685,33 @@ static ssize_t tap_receive_iov(void *opaque, const struct iovec *iov,
 }
 #endif
 
+static int tap_rx_filter(void *opaque, unsigned int flags, int count,
+                         uint8_t *list)
+{
+    TAPState *s = opaque;
+    struct tun_filter *filter;
+    int ret;
+
+    if (flags & IFF_PROMISC)
+        count = 0;
+
+    filter = qemu_mallocz(sizeof(*filter) + (count * ETH_ALEN));
+    if (!filter)
+	return -1;
+
+    memcpy(filter->addr, list, count * ETH_ALEN);
+    filter->count += count;
+
+    if (flags & IFF_ALLMULTI)
+        filter->flags |= TUN_FLT_ALLMULTI;
+  
+    ret = ioctl(s->fd, TUNSETTXFILTER, filter);
+
+    qemu_free(filter);
+
+    return ret;
+}
+
 static void tap_receive(void *opaque, const uint8_t *buf, int size)
 {
     TAPState *s = opaque;
@@ -735,6 +762,7 @@ static TAPState *net_tap_fd_init(VLANState *vlan,
 #ifdef HAVE_IOVEC
     s->vc->fd_readv = tap_receive_iov;
 #endif
+    s->vc->fd_rx_filter = tap_rx_filter;
     qemu_set_fd_handler(s->fd, tap_send, NULL, s);
     snprintf(s->vc->info_str, sizeof(s->vc->info_str), "fd=%d", fd);
     return s;
diff --git a/net.h b/net.h
index 291807a..3e915dc 100644
--- a/net.h
+++ b/net.h
@@ -11,12 +11,15 @@ typedef struct VLANClientState VLANClientState;
 
 typedef void (LinkStatusChanged)(VLANClientState *);
 
+typedef int (IORXFilter)(void *, unsigned int , int , uint8_t *);
+
 struct VLANClientState {
     IOReadHandler *fd_read;
     IOReadvHandler *fd_readv;
     /* Packets may still be sent if this returns zero.  It's used to
        rate-limit the slirp code.  */
     IOCanRWHandler *fd_can_read;
+    IORXFilter *fd_rx_filter;
     LinkStatusChanged *link_status_changed;
     int link_down;
     void *opaque;

             reply	other threads:[~2009-02-06  4:54 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-06  4:51 Alex Williamson [this message]
2009-02-06  7:47 ` [Qemu-devel] Re: [PATCH][RFC] qemu:virtio-net: Use TUNSETTXFILTER for MAC filtering Mark McLoughlin
2009-02-06 18:09   ` Alex Williamson
2009-02-06 13:59 ` Anthony Liguori
2009-02-06 18:06   ` Alex Williamson
2009-02-06 15:12 ` [Qemu-devel] " Paul Brook
2009-02-06 17:59   ` Alex Williamson
2009-02-06 22:16     ` Paul Brook

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090206044853.3116.46699.stgit@kvm.aw \
    --to=alex.williamson@hp.com \
    --cc=kvm@vger.kernel.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).