Complete the AF_PACKET based packet forwarding implementation for
filter-redirector:

1. filter_redirector_send_netdev_packet(): Send packets via AF_PACKET
   socket to out_netdev. Updates netdev_tx statistics.

2. filter_redirector_recv_from_chardev(): Handle packets received from
   chardev indev. Can forward to either chardev outdev, AF_PACKET
   out_netdev, or inject into the netfilter chain.

3. filter_redirector_recv_from_netdev(): Handle packets received from
   AF_PACKET in_netdev. Can forward to chardev outdev or inject into
   the netfilter chain.

4. Updated filter_redirector_receive_iov() to support out_netdev as
   an output endpoint. Added logic to skip netdev path consumption
   when redirector has an input endpoint (indev/in_netdev) to prevent
   packet loops.

5. Added netdev_rx and netdev_tx counters to query-netfilter-stats
   output for monitoring AF_PACKET datapath activity.

Signed-off-by: Cindy Lu <[email protected]>
---
 net/filter-mirror.c | 177 ++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 171 insertions(+), 6 deletions(-)

diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index f8001612ec..d9e8fcba59 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -65,6 +65,11 @@ struct MirrorState {
     uint64_t indev_bytes;
     uint64_t outdev_packets;
     uint64_t outdev_bytes;
+    /* netdev replay/capture statistics for filter-redirector */
+    uint64_t netdev_rx_packets;
+    uint64_t netdev_rx_bytes;
+    uint64_t netdev_tx_packets;
+    uint64_t netdev_tx_bytes;
 };
 
 typedef struct FilterSendCo {
@@ -158,6 +163,59 @@ static int filter_send(MirrorState *s,
     return data.ret;
 }
 
+static ssize_t filter_redirector_send_netdev_packet(MirrorState *s,
+                                                    const struct iovec *iov,
+                                                    int iovcnt)
+{
+    ssize_t size = iov_size(iov, iovcnt);
+    g_autofree uint8_t *buf = NULL;
+
+    if (s->out_netfd < 0) {
+        return -ENODEV;
+    }
+    if (size > NET_BUFSIZE) {
+        return -EINVAL;
+    }
+
+    buf = g_malloc(size);
+    iov_to_buf(iov, iovcnt, 0, buf, size);
+
+    ssize_t ret = send(s->out_netfd, buf, size, 0);
+    if (ret < 0) {
+        return -errno;
+    }
+    if (ret > 0) {
+        s->netdev_tx_packets++;
+        s->netdev_tx_bytes += ret;
+    }
+    return ret;
+}
+static ssize_t filter_redirector_send_chardev_iov(MirrorState *s,
+                                                  const struct iovec *iov,
+                                                  int iovcnt)
+{
+    if (!s->outdev) {
+        return -ENODEV;
+    }
+
+    if (!qemu_chr_fe_backend_connected(&s->chr_out)) {
+        return 0;
+    }
+
+    return filter_send(s, iov, iovcnt);
+}
+
+static ssize_t filter_redirector_send_netdev_iov(MirrorState *s,
+                                                 const struct iovec *iov,
+                                                 int iovcnt)
+{
+    if (!s->out_netdev) {
+        return -ENODEV;
+    }
+
+    return filter_redirector_send_netdev_packet(s, iov, iovcnt);
+}
+
 static void redirector_to_filter(NetFilterState *nf,
                                  const uint8_t *buf,
                                  int len)
@@ -230,6 +288,75 @@ static void redirector_chr_event(void *opaque, 
QEMUChrEvent event)
     }
 }
 
+static void filter_redirector_recv_from_chardev(NetFilterState *nf,
+                                                        const uint8_t *buf,
+                                                        int len)
+{
+    MirrorState *s = FILTER_REDIRECTOR(nf);
+    ssize_t ret;
+    struct iovec iov = {
+        .iov_base = (void *)buf,
+        .iov_len = len,
+    };
+
+    if (len <= 0) {
+        return;
+    }
+
+    /* chardev indev */
+    s->indev_packets++;
+    s->indev_bytes += len;
+
+    if (s->out_netdev) {
+        ret = filter_redirector_send_netdev_iov(s, &iov, 1);
+        if (ret < 0) {
+            error_report("filter redirector send failed(%s)", strerror(-ret));
+        }
+        return;
+    }
+
+    if (s->outdev) {
+        ret = filter_redirector_send_chardev_iov(s, &iov, 1);
+        if (ret < 0) {
+            error_report("filter redirector send failed(%s)", strerror(-ret));
+        } else if (ret > 0) {
+            s->outdev_packets++;
+            s->outdev_bytes += ret;
+        }
+        return;
+    }
+
+    redirector_to_filter(nf, buf, len);
+}
+
+static bool filter_redirector_recv_from_netdev(NetFilterState *nf,
+                                                          const uint8_t *buf,
+                                                          int len)
+{
+    MirrorState *s = FILTER_REDIRECTOR(nf);
+    ssize_t ret;
+    struct iovec iov = {
+        .iov_base = (void *)buf,
+        .iov_len = len,
+    };
+
+    if (len <= 0) {
+        return false;
+    }
+    if (s->outdev) {
+        ret = filter_redirector_send_chardev_iov(s, &iov, 1);
+    } else {
+        redirector_to_filter(nf, buf, len);
+        return true;
+    }
+
+    if (ret < 0) {
+        error_report("filter redirector send failed(%s)", strerror(-ret));
+        return false;
+    }
+    return true;
+}
+
 static void filter_redirector_netdev_read(void *opaque)
 {
     NetFilterState *nf = opaque;
@@ -254,7 +381,9 @@ static void filter_redirector_netdev_read(void *opaque)
             continue;
         }
 
-        redirector_to_filter(nf, s->in_netbuf, len);
+        s->netdev_rx_packets++;
+        s->netdev_rx_bytes += len;
+        filter_redirector_recv_from_netdev(nf, s->in_netbuf, len);
     }
 
     if (len < 0 && errno != EAGAIN && errno != EWOULDBLOCK &&
@@ -296,19 +425,33 @@ static ssize_t 
filter_redirector_receive_iov(NetFilterState *nf,
     MirrorState *s = FILTER_REDIRECTOR(nf);
     int ret;
 
-    if (qemu_chr_fe_backend_connected(&s->chr_out)) {
-        ret = filter_send(s, iov, iovcnt);
+    /*
+     * If this redirector has an explicit input endpoint (indev/in_netdev),
+     * it acts as an injector for that endpoint and must not consume packets
+     * from the regular netdev data path. Consuming here can create loops when
+     * out_netdev points back to the same TAP netdev.
+     */
+    if (s->indev || s->in_netdev) {
+        return 0;
+    }
+
+    if (s->out_netdev || s->outdev) {
+        if (s->out_netdev) {
+            ret = filter_redirector_send_netdev_iov(s, iov, iovcnt);
+        } else {
+            ret = filter_redirector_send_chardev_iov(s, iov, iovcnt);
+        }
         if (ret < 0) {
             error_report("filter redirector send failed(%s)", strerror(-ret));
-        } else if (ret > 0) {
+        } else if (ret > 0 && !s->out_netdev) {
             /* Update outdev statistics on successful send */
             s->outdev_packets++;
             s->outdev_bytes += ret;
         }
         return iov_size(iov, iovcnt);
-    } else {
-        return 0;
     }
+
+    return 0;
 }
 
 static void filter_mirror_cleanup(NetFilterState *nf)
@@ -369,6 +512,16 @@ static void redirector_rs_finalize(SocketReadState *rs)
     MirrorState *s = container_of(rs, MirrorState, rs);
     NetFilterState *nf = NETFILTER(s);
 
+    /*
+     * If redirector has an explicit output endpoint, keep the redirect path
+     * (e.g. indev=red0 -> out_netdev=net0).
+     * Fallback to direct netfilter injection only when no output is set.
+     */
+    if (s->outdev || s->out_netdev) {
+        filter_redirector_recv_from_chardev(nf, rs->buf, rs->packet_len);
+        return;
+    }
+
     /* Update indev statistics */
     s->indev_packets++;
     s->indev_bytes += rs->packet_len;
@@ -826,6 +979,18 @@ static GList *filter_redirector_get_stats(NetFilterState 
*nf)
     counter->bytes = s->outdev_bytes;
     list = g_list_append(list, counter);
 
+    counter = g_new0(NetFilterCounter, 1);
+    counter->name = g_strdup("netdev_rx");
+    counter->packets = s->netdev_rx_packets;
+    counter->bytes = s->netdev_rx_bytes;
+    list = g_list_append(list, counter);
+
+    counter = g_new0(NetFilterCounter, 1);
+    counter->name = g_strdup("netdev_tx");
+    counter->packets = s->netdev_tx_packets;
+    counter->bytes = s->netdev_tx_bytes;
+    list = g_list_append(list, counter);
+
     return list;
 }
 
-- 
2.52.0


Reply via email to