Packets captured from AF_PACKET now either go to the chardev outdev or
into the filter chain, and both paths update the new netdev statistics.
Use the same routing from redirector_rs_finalize() so replay traffic and
normal receive handling share one dispatch policy.
Signed-off-by: Cindy Lu <lulu@redhat.com>
---
net/filter-mirror.c | 107 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 101 insertions(+), 6 deletions(-)
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index e57fbc94b8..1ff58e1d27 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -305,6 +305,81 @@ 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);
+ bool inject_netdev = filter_redirector_use_inject_netdev(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 (inject_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);
+ if (ret > 0) {
+ s->outdev_packets++;
+ s->outdev_bytes += ret;
+ }
+ } 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;
@@ -329,7 +404,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 &&
@@ -369,21 +446,34 @@ 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 (qemu_chr_fe_backend_connected(&s->chr_out)) {
- ret = filter_send(s, iov, iovcnt);
+ if (s->indev || inject_netdev) {
+ return 0;
+ }
+
+ if (capture_netdev || s->outdev) {
+ if (capture_netdev) {
+ return 0;
+ }
+
+ 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) {
- /* Update outdev statistics on successful send */
s->outdev_packets++;
s->outdev_bytes += ret;
}
- return iov_size(iov, iovcnt);
- } else {
+ /*
+ * Without an active AF_PACKET capture socket, outdev mirroring is a
+ * sideband copy only and must not consume the guest-bound packet.
+ */
return 0;
}
+
+ return 0;
}
static void filter_mirror_cleanup(NetFilterState *nf)
@@ -444,6 +534,11 @@ static void redirector_rs_finalize(SocketReadState *rs)
MirrorState *s = container_of(rs, MirrorState, rs);
NetFilterState *nf = NETFILTER(s);
+ if (s->outdev || filter_redirector_use_inject_netdev(nf)) {
+ filter_redirector_recv_from_chardev(nf, rs->buf, rs->packet_len);
+ return;
+ }
+
/* Update indev statistics */
s->indev_packets++;
s->indev_bytes += rs->packet_len;
--
2.52.0