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
