master
c 169 lines 5.66 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * QTest testcase for filter-buffer
4 *
5 * Copyright (c) 2025 Red Hat, Inc.
6 * Author: Jason Wang <jasowang@redhat.com>
7 */
8
9 #include "qemu/osdep.h"
10 #include "libqtest.h"
11 #include "qobject/qdict.h"
12 #include "qemu/iov.h"
13 #include "qemu/sockets.h"
14
15 /*
16 * Test that changing interval at runtime affects packet release timing.
17 *
18 * Traffic flow with filter-buffer and filter-redirector:
19 *
20 * test side | qemu side
21 * |
22 * +--------+ | +---------+
23 * | send +------------------------>| backend |
24 * | sock[0]| | +----+----+
25 * +--------+ | |
26 * | +----v----+
27 * | | fbuf0 | filter-buffer (queue=tx)
28 * | +----+----+
29 * | |
30 * | +----v----+ +----------+
31 * | | rd0 +->| chardev0 |
32 * | +---------+ +----+-----+
33 * | |
34 * +--------+ | |
35 * | recv |<--------------------------------------+
36 * | sock | |
37 * +--------+ |
38 *
39 * The test verifies that when interval is changed via qom-set, the timer
40 * is rescheduled immediately, causing buffered packets to be released
41 * at the new interval rather than waiting for the old interval to elapse.
42 */
43 static void test_change_interval_timer(void)
44 {
45 QTestState *qts;
46 QDict *response;
47 int backend_sock[2], recv_sock;
48 int ret;
49 char send_buf[] = "Hello filter-buffer!";
50 char recv_buf[128];
51 char sock_path[] = "filter-buffer-test.XXXXXX";
52 uint32_t size = sizeof(send_buf);
53 uint32_t len;
54
55 size = htonl(size);
56
57 ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock);
58 g_assert_cmpint(ret, !=, -1);
59
60 ret = mkstemp(sock_path);
61 g_assert_cmpint(ret, !=, -1);
62
63 /*
64 * Start QEMU with:
65 * - socket backend connected to our socketpair
66 * - filter-buffer with a very long interval (1000 seconds)
67 * - filter-redirector to send released packets to a chardev socket
68 *
69 * queue=tx intercepts packets going from backend to the guest,
70 * i.e., data we send from the test side.
71 */
72 qts = qtest_initf(
73 "-nic socket,id=qtest-bn0,fd=%d "
74 "-chardev socket,id=chardev0,path=%s,server=on,wait=off "
75 "-object filter-buffer,id=fbuf0,netdev=qtest-bn0,"
76 "queue=tx,interval=1000000000 "
77 "-object filter-redirector,id=rd0,netdev=qtest-bn0,"
78 "queue=tx,outdev=chardev0",
79 backend_sock[1], sock_path);
80
81 /* Connect to the chardev socket to receive redirected packets */
82 recv_sock = unix_connect(sock_path, NULL);
83 g_assert_cmpint(recv_sock, !=, -1);
84
85 /* Send a QMP command to ensure chardev connection is established */
86 qtest_qmp_assert_success(qts, "{ 'execute' : 'query-status'}");
87
88 /*
89 * Send a packet from the test side.
90 * It should be buffered by filter-buffer.
91 */
92 struct iovec iov[] = {
93 {
94 .iov_base = &size,
95 .iov_len = sizeof(size),
96 }, {
97 .iov_base = send_buf,
98 .iov_len = sizeof(send_buf),
99 },
100 };
101
102 ret = iov_send(backend_sock[0], iov, 2, 0, sizeof(size) + sizeof(send_buf));
103 g_assert_cmpint(ret, ==, sizeof(send_buf) + sizeof(size));
104
105 /*
106 * Advance virtual clock by 1 second (1,000,000,000 ns).
107 * This is much less than the 1000 second interval, so the packet
108 * should still be buffered.
109 */
110 qtest_clock_step(qts, 1000000000LL);
111
112 /* Try to receive with non-blocking - should fail (packet still buffered) */
113 ret = recv(recv_sock, recv_buf, sizeof(recv_buf), MSG_DONTWAIT);
114 g_assert_cmpint(ret, ==, -1);
115 g_assert(errno == EAGAIN || errno == EWOULDBLOCK);
116
117 /*
118 * Now change the interval to 1000 us (1ms) via qom-set.
119 * This should reschedule the timer to fire in 1ms from now.
120 */
121 response = qtest_qmp(qts,
122 "{'execute': 'qom-set',"
123 " 'arguments': {"
124 " 'path': 'fbuf0',"
125 " 'property': 'interval',"
126 " 'value': 1000"
127 "}}");
128 g_assert(response);
129 g_assert(!qdict_haskey(response, "error"));
130 qobject_unref(response);
131
132 /*
133 * Advance virtual clock by 2ms (2,000,000 ns).
134 * This exceeds the new 1ms interval, so the timer should fire
135 * and release the buffered packet.
136 *
137 * If the interval change didn't take effect immediately, we would
138 * still be waiting for the original 1000 second interval to elapse,
139 * and the packet would not be released.
140 */
141 qtest_clock_step(qts, 2000000LL);
142
143 /*
144 * Now we should be able to receive the packet through the redirector.
145 * The packet was released by filter-buffer and sent to filter-redirector,
146 * which forwarded it to the chardev socket.
147 */
148 ret = recv(recv_sock, &len, sizeof(len), 0);
149 g_assert_cmpint(ret, ==, sizeof(len));
150 len = ntohl(len);
151 g_assert_cmpint(len, ==, sizeof(send_buf));
152
153 ret = recv(recv_sock, recv_buf, len, 0);
154 g_assert_cmpint(ret, ==, len);
155 g_assert_cmpstr(recv_buf, ==, send_buf);
156
157 close(recv_sock);
158 close(backend_sock[0]);
159 unlink(sock_path);
160 qtest_quit(qts);
161 }
162
163 int main(int argc, char **argv)
164 {
165 g_test_init(&argc, &argv, NULL);
166 qtest_add_func("/netfilter/change_interval_timer",
167 test_change_interval_timer);
168 return g_test_run();
169 }