chardev/char-socket.c | 385 +++++++++++++++++++++++++++++++++- chardev/char.c | 3 + include/chardev/char-socket.h | 13 ++ io/channel-socket.c | 6 +- net/filter.c | 6 - qapi/char.json | 23 +- qemu-options.hx | 5 +- 7 files changed, 429 insertions(+), 12 deletions(-)
Hi, All
This series wires AF_PACKET-backed packet capture and inject support into
the existing socket chardev backend so filter-redirector can keep using exist
process
Example Usage
=============
Users are expected to create the AF_PACKET socket in userspace, bind it
to the target tap device, and then pass the resulting fd to QEMU via
the existing FD_PLACEHOLDER mechanism.
Creating such a socket requires CAP_NET_RAW (or running as root). A
typical setup looks like:
sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
socket.htons(ETH_P_ALL))
sock.bind((ifname, ETH_P_ALL))
The bound fd can then be passed to QEMU with:
-chardev socket,...,fd=${FD_PLACEHOLDER},...
Primary VM (mirror incoming packets to secondary via chardev socket):
-netdev "tap,id=net0,ifname=${TAP}...vhost=on"
-device "${VIRTIO_NET_DEVICE}"
-chardev "socket,id=chain_out,fd=${FD_PLACEHOLDER},af-packet-mode=capture"
-chardev "socket,id=mirror0,host=${MIRROR_HOST},port=${MIRROR_PORT},reconnect-ms=${MIRROR_RECONNECT_MS}"
-object "filter-redirector,id=r1,netdev=net0,queue=tx,indev=chain_out,status=on,vnet_hdr_support=off,position=head"
-object "filter-redirector,id=r1_mirror,netdev=net0,queue=tx,outdev=mirror0,status=on,vnet_hdr_support=off,insert=behind"
Secondary VM (receive mirrored packets):
-netdev "tap,id=net0,ifname=${TAP}...vhost=on"
-device "${VIRTIO_NET_DEVICE}"
-chardev "socket,id=red0,host=${MIRROR_BIND_HOST},port=${MIRROR_PORT},server=on,wait=off"
-chardev "socket,id=chain_in,fd=${FD_PLACEHOLDER},af-packet-mode=inject"
-object "filter-redirector,id=r1,netdev=net0,queue=tx,indev=red0,status=off,vnet_hdr_support=off,position=head"
-object "filter-redirector,id=r1_inject,netdev=net0,queue=tx,outdev=chain_in,status=off,vnet_hdr_support=off,position=id=r1,insert=behind"
changset
===========
change in v2:
1. add support for filter-buffer
2. remove the in_netdev and out_netdev for AF_PACKET bind port, now only use netdev
when the vhost=on start use AF_PACKET to capture and inject, when use vhost=off will use
the existing code
3. add CAP_NET_RAW check
4. address the comment
change in v3:
1. reuse the exist Capture/inject process
change in v4:
1.move the capture/inject to chardev
2.move the create/bind socket to user script
Testing
=======
- Tested with vhost=on/off TAP device on x86_64
Cindy Lu (5):
net/filter: allow filters on vhost netdevs
chardev/socket: add AF_PACKET initialization
io/channel-socket: tolerate AF_PACKET getpeername
chardev/socket: add AF_PACKET inject path
chardev/socket: add AF_PACKET capture path
chardev/char-socket.c | 385 +++++++++++++++++++++++++++++++++-
chardev/char.c | 3 +
include/chardev/char-socket.h | 13 ++
io/channel-socket.c | 6 +-
net/filter.c | 6 -
qapi/char.json | 23 +-
qemu-options.hx | 5 +-
7 files changed, 429 insertions(+), 12 deletions(-)
--
2.52.0
On Tue, Apr 07, 2026 at 01:05:47PM +0800, Cindy Lu wrote:
> Hi, All
>
> This series wires AF_PACKET-backed packet capture and inject support into
> the existing socket chardev backend so filter-redirector can keep using exist
> process
>
> Example Usage
> =============
> Users are expected to create the AF_PACKET socket in userspace, bind it
> to the target tap device, and then pass the resulting fd to QEMU via
> the existing FD_PLACEHOLDER mechanism.
>
> Creating such a socket requires CAP_NET_RAW (or running as root). A
> typical setup looks like:
>
> sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
> socket.htons(ETH_P_ALL))
> sock.bind((ifname, ETH_P_ALL))
While FD passing is certainly desirable, and indeed required, for
libvirt to manage QEMU, IMHO, the QIOChannelSocket should be made
capable of opening AF_PACKET sockets explicitly too.
I generally only consider "FD" passing for QIOCHannelSocket to be
supported for address families that we can explicitly open - we
shouldn't have address familys that are only supported via FD
passing
>
> The bound fd can then be passed to QEMU with:
>
> -chardev socket,...,fd=${FD_PLACEHOLDER},...
>
> Primary VM (mirror incoming packets to secondary via chardev socket):
>
> -netdev "tap,id=net0,ifname=${TAP}...vhost=on"
> -device "${VIRTIO_NET_DEVICE}"
> -chardev "socket,id=chain_out,fd=${FD_PLACEHOLDER},af-packet-mode=capture"
> -chardev "socket,id=mirror0,host=${MIRROR_HOST},port=${MIRROR_PORT},reconnect-ms=${MIRROR_RECONNECT_MS}"
> -object "filter-redirector,id=r1,netdev=net0,queue=tx,indev=chain_out,status=on,vnet_hdr_support=off,position=head"
> -object "filter-redirector,id=r1_mirror,netdev=net0,queue=tx,outdev=mirror0,status=on,vnet_hdr_support=off,insert=behind"
>
> Secondary VM (receive mirrored packets):
>
> -netdev "tap,id=net0,ifname=${TAP}...vhost=on"
> -device "${VIRTIO_NET_DEVICE}"
> -chardev "socket,id=red0,host=${MIRROR_BIND_HOST},port=${MIRROR_PORT},server=on,wait=off"
> -chardev "socket,id=chain_in,fd=${FD_PLACEHOLDER},af-packet-mode=inject"
> -object "filter-redirector,id=r1,netdev=net0,queue=tx,indev=red0,status=off,vnet_hdr_support=off,position=head"
> -object "filter-redirector,id=r1_inject,netdev=net0,queue=tx,outdev=chain_in,status=off,vnet_hdr_support=off,position=id=r1,insert=behind"
>
>
> changset
> ===========
> change in v2:
> 1. add support for filter-buffer
> 2. remove the in_netdev and out_netdev for AF_PACKET bind port, now only use netdev
> when the vhost=on start use AF_PACKET to capture and inject, when use vhost=off will use
> the existing code
> 3. add CAP_NET_RAW check
> 4. address the comment
>
> change in v3:
> 1. reuse the exist Capture/inject process
>
> change in v4:
> 1.move the capture/inject to chardev
> 2.move the create/bind socket to user script
>
> Testing
> =======
> - Tested with vhost=on/off TAP device on x86_64
>
>
>
> Cindy Lu (5):
> net/filter: allow filters on vhost netdevs
> chardev/socket: add AF_PACKET initialization
> io/channel-socket: tolerate AF_PACKET getpeername
> chardev/socket: add AF_PACKET inject path
> chardev/socket: add AF_PACKET capture path
>
> chardev/char-socket.c | 385 +++++++++++++++++++++++++++++++++-
> chardev/char.c | 3 +
> include/chardev/char-socket.h | 13 ++
> io/channel-socket.c | 6 +-
> net/filter.c | 6 -
> qapi/char.json | 23 +-
> qemu-options.hx | 5 +-
> 7 files changed, 429 insertions(+), 12 deletions(-)
>
> --
> 2.52.0
>
>
With regards,
Daniel
--
|: https://berrange.com ~~ https://hachyderm.io/@berrange :|
|: https://libvirt.org ~~ https://entangle-photo.org :|
|: https://pixelfed.art/berrange ~~ https://fstop138.berrange.com :|
© 2016 - 2026 Red Hat, Inc.