[RFC v4 0/5] net/filter: Add AF_PACKET support for vhost-net

Cindy Lu posted 5 patches 4 days, 12 hours ago
Failed in applying to current master (apply log)
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(-)
[RFC v4 0/5] net/filter: Add AF_PACKET support for vhost-net
Posted by Cindy Lu 4 days, 12 hours ago
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
Re: [RFC v4 0/5] net/filter: Add AF_PACKET support for vhost-net
Posted by Daniel P. Berrangé 3 days, 4 hours ago
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 :|