Allow filter-buffer on the same vhost backend as filter-redirector,
add an internal redirector-injected packet flag, and route indev packets
through the preceding filter-buffer before they are reinjected.

Signed-off-by: Cindy Lu <[email protected]>
---
 include/net/queue.h |  5 +++
 net/filter-mirror.c | 98 +++++++++++++++++++++++++++++++++++++++++----
 net/filter.c        |  5 ++-
 3 files changed, 98 insertions(+), 10 deletions(-)

diff --git a/include/net/queue.h b/include/net/queue.h
index 2e686b1b61..213abe62ec 100644
--- a/include/net/queue.h
+++ b/include/net/queue.h
@@ -32,6 +32,11 @@ typedef void (NetPacketSent) (NetClientState *sender, 
ssize_t ret);
 
 #define QEMU_NET_PACKET_FLAG_NONE  0
 #define QEMU_NET_PACKET_FLAG_RAW  (1<<0)
+/*
+ * Internal marker used by filter-redirector when packets are injected from
+ * indev through filter-buffer before being reinjected.
+ */
+#define QEMU_NET_PACKET_FLAG_REDIRECTOR_INJECT (1<<1)
 
 /* Returns:
  *   >0 - success
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 1ff58e1d27..dabf52275a 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -233,6 +233,73 @@ static ssize_t 
filter_redirector_send_netdev_iov(MirrorState *s,
     return filter_redirector_send_netdev_packet(s, iov, iovcnt);
 }
 
+static NetFilterState *filter_redirector_prev_in_direction(NetFilterState *nf,
+                                                           NetFilterDirection 
dir)
+{
+    if (dir == NET_FILTER_DIRECTION_TX) {
+        return QTAILQ_PREV(nf, next);
+    }
+    return QTAILQ_NEXT(nf, next);
+}
+
+static NetFilterState *filter_redirector_find_buffer_before(NetFilterState *nf,
+                                                            NetFilterDirection 
dir)
+{
+    NetFilterState *iter = filter_redirector_prev_in_direction(nf, dir);
+
+    while (iter) {
+        if ((iter->direction == dir ||
+             iter->direction == NET_FILTER_DIRECTION_ALL) &&
+            object_dynamic_cast(OBJECT(iter), "filter-buffer")) {
+            return iter;
+        }
+        iter = filter_redirector_prev_in_direction(iter, dir);
+    }
+
+    return NULL;
+}
+
+static bool filter_redirector_inject_to_buffer(NetFilterState *nf,
+                                               const uint8_t *buf,
+                                               int len)
+{
+    struct iovec iov = {
+        .iov_base = (void *)buf,
+        .iov_len = len,
+    };
+    NetFilterState *buffer;
+    bool injected = false;
+
+    if (nf->direction == NET_FILTER_DIRECTION_ALL ||
+        nf->direction == NET_FILTER_DIRECTION_TX) {
+        buffer = filter_redirector_find_buffer_before(nf,
+                                                      NET_FILTER_DIRECTION_TX);
+        if (buffer) {
+            qemu_netfilter_receive(buffer, NET_FILTER_DIRECTION_TX,
+                                   nf->netdev,
+                                   QEMU_NET_PACKET_FLAG_REDIRECTOR_INJECT,
+                                   &iov, 1, NULL);
+            injected = true;
+        }
+    }
+
+    if ((nf->direction == NET_FILTER_DIRECTION_ALL ||
+         nf->direction == NET_FILTER_DIRECTION_RX) &&
+        nf->netdev->peer) {
+        buffer = filter_redirector_find_buffer_before(nf,
+                                                      NET_FILTER_DIRECTION_RX);
+        if (buffer) {
+            qemu_netfilter_receive(buffer, NET_FILTER_DIRECTION_RX,
+                                   nf->netdev->peer,
+                                   QEMU_NET_PACKET_FLAG_REDIRECTOR_INJECT,
+                                   &iov, 1, NULL);
+            injected = true;
+        }
+    }
+
+    return injected;
+}
+
 static void redirector_to_filter(NetFilterState *nf,
                                  const uint8_t *buf,
                                  int len)
@@ -310,7 +377,6 @@ static void 
filter_redirector_recv_from_chardev(NetFilterState *nf,
                                                         int len)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
-    bool inject_netdev = filter_redirector_use_inject_netdev(nf);
     ssize_t ret;
     struct iovec iov = {
         .iov_base = (void *)buf,
@@ -325,7 +391,11 @@ static void 
filter_redirector_recv_from_chardev(NetFilterState *nf,
     s->indev_packets++;
     s->indev_bytes += len;
 
-    if (inject_netdev) {
+    if (!s->outdev && filter_redirector_inject_to_buffer(nf, buf, len)) {
+        return;
+    }
+
+    if (s->out_netfd >= 0) {
         ret = filter_redirector_send_netdev_iov(s, &iov, 1);
         if (ret < 0) {
             error_report("filter redirector send failed(%s)", strerror(-ret));
@@ -446,16 +516,22 @@ static ssize_t 
filter_redirector_receive_iov(NetFilterState *nf,
                                              NetPacketSent *sent_cb)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
-    bool capture_netdev = filter_redirector_use_capture_netdev(nf);
-    bool inject_netdev = filter_redirector_use_inject_netdev(nf);
     int ret;
 
-    if (s->indev || inject_netdev) {
-        return 0;
+    if (s->out_netfd >= 0) {
+        if (!(flags & QEMU_NET_PACKET_FLAG_REDIRECTOR_INJECT)) {
+            return 0;
+        }
+
+        ret = filter_redirector_send_netdev_iov(s, iov, iovcnt);
+        if (ret < 0) {
+            error_report("filter redirector send failed(%s)", strerror(-ret));
+        }
+        return iov_size(iov, iovcnt);
     }
 
-    if (capture_netdev || s->outdev) {
-        if (capture_netdev) {
+    if (s->outdev) {
+        if (s->in_netfd >= 0) {
             return 0;
         }
 
@@ -473,6 +549,12 @@ static ssize_t 
filter_redirector_receive_iov(NetFilterState *nf,
         return 0;
     }
 
+    if (s->indev) {
+        if (!(flags & QEMU_NET_PACKET_FLAG_REDIRECTOR_INJECT)) {
+            return 0;
+        }
+    }
+
     return 0;
 }
 
diff --git a/net/filter.c b/net/filter.c
index b9646b9e00..cc23e743cf 100644
--- a/net/filter.c
+++ b/net/filter.c
@@ -260,8 +260,9 @@ static void netfilter_complete(UserCreatable *uc, Error 
**errp)
         bool buffer = object_dynamic_cast(OBJECT(uc), "filter-buffer");
         bool vhost_filter = redirector || buffer;
 
-        if (!redirector) {
-            error_setg(errp, "Vhost is not supported");
+        if (!vhost_filter) {
+            error_setg(errp, "Vhost only supports filter-redirector and "
+                             "filter-buffer");
             return;
         }
         if (vhost_filter && ncs[0]->info->type != NET_CLIENT_DRIVER_TAP) {
-- 
2.52.0


Reply via email to