Files
qemu/tests/qtest/test-filter-buffer.c
Jason Wang 50f6d44850 tests/qtest: add test for filter-buffer interval change
Add test_change_interval_timer to verify that modifying the 'interval'
property of filter-buffer at runtime takes effect immediately.

The test uses socket backend and filter-redirector to verify timer behavior:
- Creates filter-buffer with a very long interval (1000 seconds)
- Sends a packet which gets buffered
- Advances virtual clock by 1 second, verifies packet is still buffered
- Changes interval to 1ms via qom-set (timer should be rescheduled)
- Advances virtual clock by 2ms, verifies packet is now released
- This proves the timer was rescheduled immediately when interval changed

The test uses filter-redirector to observe when packets are released
by filter-buffer, providing end-to-end verification of the timer
rescheduling behavior.

Reviewed-by: Zhang Chen <zhangckid@gmail.com>
Reviewed-by: Fabiano Rosas <farosas@suse.de>
Signed-off-by: Jason Wang <jasowang@redhat.com>
2026-01-23 14:44:17 +08:00

170 lines
5.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* QTest testcase for filter-buffer
*
* Copyright (c) 2025 Red Hat, Inc.
* Author: Jason Wang <jasowang@redhat.com>
*/
#include "qemu/osdep.h"
#include "libqtest.h"
#include "qobject/qdict.h"
#include "qemu/iov.h"
#include "qemu/sockets.h"
/*
* Test that changing interval at runtime affects packet release timing.
*
* Traffic flow with filter-buffer and filter-redirector:
*
* test side | qemu side
* |
* +--------+ | +---------+
* | send +------------------------>| backend |
* | sock[0]| | +----+----+
* +--------+ | |
* | +----v----+
* | | fbuf0 | filter-buffer (queue=tx)
* | +----+----+
* | |
* | +----v----+ +----------+
* | | rd0 +->| chardev0 |
* | +---------+ +----+-----+
* | |
* +--------+ | |
* | recv |<--------------------------------------+
* | sock | |
* +--------+ |
*
* The test verifies that when interval is changed via qom-set, the timer
* is rescheduled immediately, causing buffered packets to be released
* at the new interval rather than waiting for the old interval to elapse.
*/
static void test_change_interval_timer(void)
{
QTestState *qts;
QDict *response;
int backend_sock[2], recv_sock;
int ret;
char send_buf[] = "Hello filter-buffer!";
char recv_buf[128];
char sock_path[] = "filter-buffer-test.XXXXXX";
uint32_t size = sizeof(send_buf);
uint32_t len;
size = htonl(size);
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock);
g_assert_cmpint(ret, !=, -1);
ret = mkstemp(sock_path);
g_assert_cmpint(ret, !=, -1);
/*
* Start QEMU with:
* - socket backend connected to our socketpair
* - filter-buffer with a very long interval (1000 seconds)
* - filter-redirector to send released packets to a chardev socket
*
* queue=tx intercepts packets going from backend to the guest,
* i.e., data we send from the test side.
*/
qts = qtest_initf(
"-nic socket,id=qtest-bn0,fd=%d "
"-chardev socket,id=chardev0,path=%s,server=on,wait=off "
"-object filter-buffer,id=fbuf0,netdev=qtest-bn0,"
"queue=tx,interval=1000000000 "
"-object filter-redirector,id=rd0,netdev=qtest-bn0,"
"queue=tx,outdev=chardev0",
backend_sock[1], sock_path);
/* Connect to the chardev socket to receive redirected packets */
recv_sock = unix_connect(sock_path, NULL);
g_assert_cmpint(recv_sock, !=, -1);
/* Send a QMP command to ensure chardev connection is established */
qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}");
/*
* Send a packet from the test side.
* It should be buffered by filter-buffer.
*/
struct iovec iov[] = {
{
.iov_base = &size,
.iov_len = sizeof(size),
}, {
.iov_base = send_buf,
.iov_len = sizeof(send_buf),
},
};
ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf));
g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
/*
* Advance virtual clock by 1 second (1,000,000,000 ns).
* This is much less than the 1000 second interval, so the packet
* should still be buffered.
*/
qtest_clock_step(qts, 1000000000LL);
/* Try to receive with non-blocking - should fail (packet still buffered) */
ret = recv(recv_sock, recv_buf, sizeof(recv_buf), MSG_DONTWAIT);
g_assert_cmpint(ret, ==, -1);
g_assert(errno == EAGAIN || errno == EWOULDBLOCK);
/*
* Now change the interval to 1000 us (1ms) via qom-set.
* This should reschedule the timer to fire in 1ms from now.
*/
response = qtest_qmp(qts,
"{'execute': 'qom-set',"
" 'arguments': {"
" 'path': 'fbuf0',"
" 'property': 'interval',"
" 'value': 1000"
"}}");
g_assert(response);
g_assert(!qdict_haskey(response, "error"));
qobject_unref(response);
/*
* Advance virtual clock by 2ms (2,000,000 ns).
* This exceeds the new 1ms interval, so the timer should fire
* and release the buffered packet.
*
* If the interval change didn't take effect immediately, we would
* still be waiting for the original 1000 second interval to elapse,
* and the packet would not be released.
*/
qtest_clock_step(qts, 2000000LL);
/*
* Now we should be able to receive the packet through the redirector.
* The packet was released by filter-buffer and sent to filter-redirector,
* which forwarded it to the chardev socket.
*/
ret = recv(recv_sock, &len, sizeof(len), 0);
g_assert_cmpint(ret, ==, sizeof(len));
len = ntohl(len);
g_assert_cmpint(len, ==, sizeof(send_buf));
ret = recv(recv_sock, recv_buf, len, 0);
g_assert_cmpint(ret, ==, len);
g_assert_cmpstr(recv_buf, ==, send_buf);
close(recv_sock);
close(backend_sock[0]);
unlink(sock_path);
qtest_quit(qts);
}
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
qtest_add_func("/netfilter/change_interval_timer",
test_change_interval_timer);
return g_test_run();
}