2 * QTest testcase for netdev stream and dgram
4 * Copyright (c) 2022 Red Hat, Inc.
6 * SPDX-License-Identifier: GPL-2.0-or-later
9 #include "qemu/osdep.h"
10 #include "qemu/sockets.h"
11 #include <glib/gstdio.h>
12 #include "../unit/socket-helpers.h"
14 #include "qapi/qmp/qstring.h"
15 #include "qemu/sockets.h"
16 #include "qapi/qobject-input-visitor.h"
17 #include "qapi/qapi-visit-sockets.h"
19 #define CONNECTION_TIMEOUT 120
21 static double connection_timeout(void)
24 int ret
= getloadavg(&load
, 1);
27 * If we can't get load data, or load is low because we just started
28 * running, assume load of 1 (we are alone in this system).
30 if (ret
< 1 || load
< 1.0) {
34 * No one wants to wait more than 10 minutes for this test. Higher load?
38 fprintf(stderr
, "Warning: load %f higher than 10 - test might timeout\n",
43 /* if load is high increase timeout as we might not get a chance to run */
44 return load
* CONNECTION_TIMEOUT
;
47 #define EXPECT_STATE(q, e, t) \
50 g_test_timer_start(); \
53 resp = qtest_hmp(q, "info network"); \
55 strrchr(resp, t)[0] = 0; \
57 if (g_str_equal(resp, e)) { \
60 } while (g_test_timer_elapsed() < connection_timeout()); \
61 g_assert_cmpstr(resp, ==, e); \
67 static int inet_get_free_port_socket_ipv4(int sock
)
69 struct sockaddr_in addr
;
72 memset(&addr
, 0, sizeof(addr
));
73 addr
.sin_family
= AF_INET
;
74 addr
.sin_addr
.s_addr
= INADDR_ANY
;
76 if (bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
81 if (getsockname(sock
, (struct sockaddr
*)&addr
, &len
) < 0) {
85 return ntohs(addr
.sin_port
);
88 static int inet_get_free_port_socket_ipv6(int sock
)
90 struct sockaddr_in6 addr
;
93 memset(&addr
, 0, sizeof(addr
));
94 addr
.sin6_family
= AF_INET6
;
95 addr
.sin6_addr
= in6addr_any
;
97 if (bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
102 if (getsockname(sock
, (struct sockaddr
*)&addr
, &len
) < 0) {
106 return ntohs(addr
.sin6_port
);
109 static int inet_get_free_port_multiple(int nb
, int *port
, bool ipv6
)
111 g_autofree
int *sock
= g_new(int, nb
);
114 for (i
= 0; i
< nb
; i
++) {
115 sock
[i
] = socket(ipv6
? AF_INET6
: AF_INET
, SOCK_STREAM
, 0);
119 port
[i
] = ipv6
? inet_get_free_port_socket_ipv6(sock
[i
]) :
120 inet_get_free_port_socket_ipv4(sock
[i
]);
127 for (i
= 0; i
< nb
; i
++) {
134 static int inet_get_free_port(bool ipv6
)
138 nb
= inet_get_free_port_multiple(1, &port
, ipv6
);
139 g_assert_cmpint(nb
, ==, 1);
144 static void test_stream_inet_ipv4(void)
146 QTestState
*qts0
, *qts1
;
150 port
= inet_get_free_port(false);
151 qts0
= qtest_initf("-nodefaults -M none "
152 "-netdev stream,id=st0,server=true,addr.type=inet,"
153 "addr.ipv4=on,addr.ipv6=off,"
154 "addr.host=127.0.0.1,addr.port=%d", port
);
156 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
158 qts1
= qtest_initf("-nodefaults -M none "
159 "-netdev stream,server=false,id=st0,addr.type=inet,"
160 "addr.ipv4=on,addr.ipv6=off,"
161 "addr.host=127.0.0.1,addr.port=%d", port
);
163 expect
= g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
165 EXPECT_STATE(qts1
, expect
, 0);
168 /* the port is unknown, check only the address */
169 EXPECT_STATE(qts0
, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
175 static void wait_stream_connected(QTestState
*qts
, const char *id
,
176 SocketAddress
**addr
)
183 resp
= qtest_qmp_eventwait_ref(qts
, "NETDEV_STREAM_CONNECTED");
184 g_assert_nonnull(resp
);
185 data
= qdict_get_qdict(resp
, "data");
186 g_assert_nonnull(data
);
188 qstr
= qobject_to(QString
, qdict_get(data
, "netdev-id"));
189 g_assert_nonnull(data
);
191 g_assert(!strcmp(qstring_get_str(qstr
), id
));
193 obj
= qdict_get(data
, "addr");
195 v
= qobject_input_visitor_new(obj
);
196 visit_type_SocketAddress(v
, NULL
, addr
, NULL
);
201 static void wait_stream_disconnected(QTestState
*qts
, const char *id
)
206 resp
= qtest_qmp_eventwait_ref(qts
, "NETDEV_STREAM_DISCONNECTED");
207 g_assert_nonnull(resp
);
208 data
= qdict_get_qdict(resp
, "data");
209 g_assert_nonnull(data
);
211 qstr
= qobject_to(QString
, qdict_get(data
, "netdev-id"));
212 g_assert_nonnull(data
);
214 g_assert(!strcmp(qstring_get_str(qstr
), id
));
218 static void test_stream_unix_reconnect(void)
220 QTestState
*qts0
, *qts1
;
224 path
= g_strconcat(tmpdir
, "/stream_unix_reconnect", NULL
);
225 qts0
= qtest_initf("-nodefaults -M none "
226 "-netdev stream,id=st0,server=true,addr.type=unix,"
227 "addr.path=%s", path
);
229 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
231 qts1
= qtest_initf("-nodefaults -M none "
232 "-netdev stream,server=false,id=st0,addr.type=unix,"
233 "addr.path=%s,reconnect=1", path
);
235 wait_stream_connected(qts0
, "st0", &addr
);
236 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_UNIX
);
237 g_assert_cmpstr(addr
->u
.q_unix
.path
, ==, path
);
238 qapi_free_SocketAddress(addr
);
243 /* check client has been disconnected */
244 wait_stream_disconnected(qts1
, "st0");
247 qts0
= qtest_initf("-nodefaults -M none "
248 "-netdev stream,id=st0,server=true,addr.type=unix,"
249 "addr.path=%s", path
);
251 /* wait connection events*/
252 wait_stream_connected(qts0
, "st0", &addr
);
253 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_UNIX
);
254 g_assert_cmpstr(addr
->u
.q_unix
.path
, ==, path
);
255 qapi_free_SocketAddress(addr
);
257 wait_stream_connected(qts1
, "st0", &addr
);
258 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_UNIX
);
259 g_assert_cmpstr(addr
->u
.q_unix
.path
, ==, path
);
260 qapi_free_SocketAddress(addr
);
267 static void test_stream_inet_ipv6(void)
269 QTestState
*qts0
, *qts1
;
273 port
= inet_get_free_port(true);
274 qts0
= qtest_initf("-nodefaults -M none "
275 "-netdev stream,id=st0,server=true,addr.type=inet,"
276 "addr.ipv4=off,addr.ipv6=on,"
277 "addr.host=::1,addr.port=%d", port
);
279 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
281 qts1
= qtest_initf("-nodefaults -M none "
282 "-netdev stream,server=false,id=st0,addr.type=inet,"
283 "addr.ipv4=off,addr.ipv6=on,"
284 "addr.host=::1,addr.port=%d", port
);
286 expect
= g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
288 EXPECT_STATE(qts1
, expect
, 0);
291 /* the port is unknown, check only the address */
292 EXPECT_STATE(qts0
, "st0: index=0,type=stream,tcp:::1", ':');
298 static void test_stream_unix(void)
300 QTestState
*qts0
, *qts1
;
304 path
= g_strconcat(tmpdir
, "/stream_unix", NULL
);
306 qts0
= qtest_initf("-nodefaults -M none "
307 "-netdev stream,id=st0,server=true,"
308 "addr.type=unix,addr.path=%s,",
311 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
313 qts1
= qtest_initf("-nodefaults -M none "
314 "-netdev stream,id=st0,server=false,"
315 "addr.type=unix,addr.path=%s",
318 expect
= g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path
);
319 EXPECT_STATE(qts1
, expect
, 0);
320 EXPECT_STATE(qts0
, expect
, 0);
329 static void test_stream_unix_abstract(void)
331 QTestState
*qts0
, *qts1
;
335 path
= g_strconcat(tmpdir
, "/stream_unix_abstract", NULL
);
337 qts0
= qtest_initf("-nodefaults -M none "
338 "-netdev stream,id=st0,server=true,"
339 "addr.type=unix,addr.path=%s,"
343 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
345 qts1
= qtest_initf("-nodefaults -M none "
346 "-netdev stream,id=st0,server=false,"
347 "addr.type=unix,addr.path=%s,addr.abstract=on",
350 expect
= g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path
);
351 EXPECT_STATE(qts1
, expect
, 0);
352 EXPECT_STATE(qts0
, expect
, 0);
362 static void test_stream_fd(void)
364 QTestState
*qts0
, *qts1
;
368 ret
= socketpair(AF_LOCAL
, SOCK_STREAM
, 0, sock
);
369 g_assert_true(ret
== 0);
371 qts0
= qtest_initf("-nodefaults -M none "
372 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
375 EXPECT_STATE(qts0
, "st0: index=0,type=stream,unix:\r\n", 0);
377 qts1
= qtest_initf("-nodefaults -M none "
378 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
381 EXPECT_STATE(qts1
, "st0: index=0,type=stream,unix:\r\n", 0);
382 EXPECT_STATE(qts0
, "st0: index=0,type=stream,unix:\r\n", 0);
392 static void test_dgram_inet(void)
394 QTestState
*qts0
, *qts1
;
399 nb
= inet_get_free_port_multiple(2, port
, false);
400 g_assert_cmpint(nb
, ==, 2);
402 qts0
= qtest_initf("-nodefaults -M none "
403 "-netdev dgram,id=st0,"
404 "local.type=inet,local.host=127.0.0.1,local.port=%d,"
405 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
408 expect
= g_strdup_printf("st0: index=0,type=dgram,"
409 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
411 EXPECT_STATE(qts0
, expect
, 0);
414 qts1
= qtest_initf("-nodefaults -M none "
415 "-netdev dgram,id=st0,"
416 "local.type=inet,local.host=127.0.0.1,local.port=%d,"
417 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
420 expect
= g_strdup_printf("st0: index=0,type=dgram,"
421 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
423 EXPECT_STATE(qts1
, expect
, 0);
430 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
431 static void test_dgram_mcast(void)
435 qts
= qtest_initf("-nodefaults -M none "
436 "-netdev dgram,id=st0,"
437 "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
439 EXPECT_STATE(qts
, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
446 static void test_dgram_unix(void)
448 QTestState
*qts0
, *qts1
;
450 gchar
*path0
, *path1
;
452 path0
= g_strconcat(tmpdir
, "/dgram_unix0", NULL
);
453 path1
= g_strconcat(tmpdir
, "/dgram_unix1", NULL
);
455 qts0
= qtest_initf("-nodefaults -M none "
456 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
457 "remote.type=unix,remote.path=%s",
460 expect
= g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
462 EXPECT_STATE(qts0
, expect
, 0);
465 qts1
= qtest_initf("-nodefaults -M none "
466 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
467 "remote.type=unix,remote.path=%s",
471 expect
= g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
473 EXPECT_STATE(qts1
, expect
, 0);
485 static void test_dgram_fd(void)
487 QTestState
*qts0
, *qts1
;
492 ret
= socketpair(PF_UNIX
, SOCK_DGRAM
, 0, sv
);
493 g_assert_cmpint(ret
, !=, -1);
495 qts0
= qtest_initf("-nodefaults -M none "
496 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
499 expect
= g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv
[0]);
500 EXPECT_STATE(qts0
, expect
, 0);
503 qts1
= qtest_initf("-nodefaults -M none "
504 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
508 expect
= g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv
[1]);
509 EXPECT_STATE(qts1
, expect
, 0);
520 int main(int argc
, char **argv
)
523 bool has_ipv4
, has_ipv6
, has_afunix
;
524 g_autoptr(GError
) err
= NULL
;
527 g_test_init(&argc
, &argv
, NULL
);
529 if (socket_check_protocol_support(&has_ipv4
, &has_ipv6
) < 0) {
530 g_error("socket_check_protocol_support() failed\n");
533 tmpdir
= g_dir_make_tmp("netdev-socket.XXXXXX", &err
);
534 if (tmpdir
== NULL
) {
535 g_error("Can't create temporary directory in %s: %s",
536 g_get_tmp_dir(), err
->message
);
540 qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4
);
541 qtest_add_func("/netdev/dgram/inet", test_dgram_inet
);
542 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
543 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast
);
547 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6
);
550 socket_check_afunix_support(&has_afunix
);
553 qtest_add_func("/netdev/dgram/unix", test_dgram_unix
);
555 qtest_add_func("/netdev/stream/unix", test_stream_unix
);
556 qtest_add_func("/netdev/stream/unix/reconnect",
557 test_stream_unix_reconnect
);
559 qtest_add_func("/netdev/stream/unix/abstract",
560 test_stream_unix_abstract
);
563 qtest_add_func("/netdev/stream/fd", test_stream_fd
);
564 qtest_add_func("/netdev/dgram/fd", test_dgram_fd
);