[RFC v3 3/7] net/filter-redirector: add AF_PACKET netdev setup helpers

Cindy Lu posted 7 patches 3 days ago
[RFC v3 3/7] net/filter-redirector: add AF_PACKET netdev setup helpers
Posted by Cindy Lu 3 days ago
Prepare filter-redirector for direct TAP access through AF_PACKET sockets.

Add the setup-side plumbing needed by later capture/inject changes:
- extend MirrorState with dedicated AF_PACKET fds and a capture buffer
- resolve the TAP ifname from the backend fd
- create and bind a nonblocking AF_PACKET socket on that interface
- store the socket as the capture or inject endpoint based on the
  redirector role
- clean up the AF_PACKET resources from cleanup/finalize paths

This commit only prepares the sockets and object state.  The actual
capture and inject datapaths are added by follow-up commits.

Signed-off-by: Cindy Lu <lulu@redhat.com>
---
 net/filter-mirror.c | 91 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 91 insertions(+)

diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index ab711e8835..d9f6a11d6b 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -22,10 +22,18 @@
 #include "qemu/error-report.h"
 #include "trace.h"
 #include "chardev/char-fe.h"
+#include "net/vhost_net.h"
 #include "qemu/iov.h"
 #include "qemu/sockets.h"
 #include "block/aio-wait.h"
 #include "system/runstate.h"
+#include "net/tap.h"
+#include "net/tap_int.h"
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <linux/if_packet.h>
+#include <netinet/if_ether.h>
 
 typedef struct MirrorState MirrorState;
 DECLARE_INSTANCE_CHECKER(MirrorState, FILTER_MIRROR,
@@ -40,6 +48,9 @@ struct MirrorState {
     NetFilterState parent_obj;
     char *indev;
     char *outdev;
+    int in_netfd;
+    uint8_t *in_netbuf;
+    int out_netfd;
     CharFrontend chr_in;
     CharFrontend chr_out;
     SocketReadState rs;
@@ -267,6 +278,15 @@ static void filter_redirector_cleanup(NetFilterState *nf)
     qemu_chr_fe_deinit(&s->chr_in, false);
     qemu_chr_fe_deinit(&s->chr_out, false);
     qemu_del_vm_change_state_handler(s->vmsentry);
+    if (s->in_netfd >= 0) {
+        qemu_set_fd_handler(s->in_netfd, NULL, NULL, NULL);
+        close(s->in_netfd);
+        s->in_netfd = -1;
+    }
+    if (s->out_netfd >= 0) {
+        close(s->out_netfd);
+        s->out_netfd = -1;
+    }
 
     if (nf->netdev) {
         nf->netdev->allow_send_when_stopped = 0;
@@ -360,6 +380,70 @@ static void filter_redirector_maybe_enable_read_poll(NetFilterState *nf)
     }
 }
 
+static bool filter_redirector_netdev_setup(NetFilterState *nf, Error **errp)
+{
+    MirrorState *s = FILTER_REDIRECTOR(nf);
+    struct sockaddr_ll sll = { 0 };
+    char ifname[IFNAMSIZ] = { 0 };
+    bool capture_role;
+    bool inject_role;
+    int ifindex;
+    int fd;
+    NetClientState *nc = nf->netdev;
+    int tapfd;
+
+    if (!nc || nc->info->type != NET_CLIENT_DRIVER_TAP) {
+        return true;
+    }
+
+    capture_role = s->outdev && !s->indev && get_vhost_net(nc);
+    inject_role = s->indev && !s->outdev && get_vhost_net(nc);
+    if (!capture_role && !inject_role) {
+        return true;
+    }
+
+    tapfd = tap_get_fd(nc);
+    if (tapfd < 0 || tap_fd_get_ifname(tapfd, ifname) != 0) {
+        error_setg(errp, "failed to resolve TAP ifname for netdev '%s'",
+                   nf->netdev_id);
+        return false;
+    }
+
+    ifindex = if_nametoindex(ifname);
+    if (!ifindex) {
+        error_setg_errno(errp, errno,
+                         "failed to resolve ifindex for '%s'", ifname);
+        return false;
+    }
+
+    fd = qemu_socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
+    if (fd < 0) {
+        error_setg_errno(errp, errno, "failed to create AF_PACKET socket");
+        return false;
+    }
+
+    sll.sll_family = AF_PACKET;
+    sll.sll_ifindex = ifindex;
+    sll.sll_protocol = htons(ETH_P_ALL);
+    if (bind(fd, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
+        error_setg_errno(errp, errno,
+                         "failed to bind AF_PACKET socket for ifname '%s'",
+                         ifname);
+        close(fd);
+        return false;
+    }
+
+    if (capture_role) {
+        s->in_netfd = fd;
+        g_free(s->in_netbuf);
+        s->in_netbuf = g_malloc(REDIRECTOR_MAX_LEN);
+    } else {
+        s->out_netfd = fd;
+    }
+
+    return true;
+}
+
 static void filter_redirector_setup(NetFilterState *nf, Error **errp)
 {
     MirrorState *s = FILTER_REDIRECTOR(nf);
@@ -410,6 +494,10 @@ static void filter_redirector_setup(NetFilterState *nf, Error **errp)
         }
     }
 
+    if (!filter_redirector_netdev_setup(nf, errp)) {
+        return;
+    }
+
     s->vmsentry = qemu_add_vm_change_state_handler(
         filter_redirector_vm_state_change, nf);
 
@@ -623,6 +711,8 @@ static void filter_redirector_init(Object *obj)
     MirrorState *s = FILTER_REDIRECTOR(obj);
 
     s->vnet_hdr = false;
+    s->in_netfd = -1;
+    s->out_netfd = -1;
 }
 
 static void filter_mirror_fini(Object *obj)
@@ -638,6 +728,7 @@ static void filter_redirector_fini(Object *obj)
 
     g_free(s->indev);
     g_free(s->outdev);
+    g_free(s->in_netbuf);
 }
 
 static const TypeInfo filter_redirector_info = {
-- 
2.52.0
Re: [RFC v3 3/7] net/filter-redirector: add AF_PACKET netdev setup helpers
Posted by Jason Wang 1 day, 6 hours ago
On Mon, Mar 30, 2026 at 5:10 PM Cindy Lu <lulu@redhat.com> wrote:
>
> Prepare filter-redirector for direct TAP access through AF_PACKET sockets.
>
> Add the setup-side plumbing needed by later capture/inject changes:
> - extend MirrorState with dedicated AF_PACKET fds and a capture buffer
> - resolve the TAP ifname from the backend fd
> - create and bind a nonblocking AF_PACKET socket on that interface
> - store the socket as the capture or inject endpoint based on the
>   redirector role
> - clean up the AF_PACKET resources from cleanup/finalize paths
>
> This commit only prepares the sockets and object state.  The actual
> capture and inject datapaths are added by follow-up commits.
>
> Signed-off-by: Cindy Lu <lulu@redhat.com>

I think I have the same question as version 1, could we simply reuse

1) netdev socket
or
2) chardev

for packet socket instead of having this?

Thanks