tests/unit: Bump test-aio-multithread test timeout to 2 minutes
[qemu/kevin.git] / tests / qtest / netdev-socket.c
blobbb99d08b5e78b73c9ca7e3b3b36788bfd8124577
1 /*
2 * QTest testcase for netdev stream and dgram
4 * Copyright (c) 2022 Red Hat, Inc.
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
9 #include "qemu/osdep.h"
10 #include "qemu/sockets.h"
11 #include <glib/gstdio.h>
12 #include "../unit/socket-helpers.h"
13 #include "libqtest.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)
23 double load;
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) {
31 load = 1.0;
34 * No one wants to wait more than 10 minutes for this test. Higher load?
35 * Too bad.
37 if (load > 10.0) {
38 fprintf(stderr, "Warning: load %f higher than 10 - test might timeout\n",
39 load);
40 load = 10.0;
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) \
48 do { \
49 char *resp = NULL; \
50 g_test_timer_start(); \
51 do { \
52 g_free(resp); \
53 resp = qtest_hmp(q, "info network"); \
54 if (t) { \
55 strrchr(resp, t)[0] = 0; \
56 } \
57 if (g_str_equal(resp, e)) { \
58 break; \
59 } \
60 } while (g_test_timer_elapsed() < connection_timeout()); \
61 g_assert_cmpstr(resp, ==, e); \
62 g_free(resp); \
63 } while (0)
65 static gchar *tmpdir;
67 static int inet_get_free_port_socket_ipv4(int sock)
69 struct sockaddr_in addr;
70 socklen_t len;
72 memset(&addr, 0, sizeof(addr));
73 addr.sin_family = AF_INET;
74 addr.sin_addr.s_addr = INADDR_ANY;
75 addr.sin_port = 0;
76 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
77 return -1;
80 len = sizeof(addr);
81 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
82 return -1;
85 return ntohs(addr.sin_port);
88 static int inet_get_free_port_socket_ipv6(int sock)
90 struct sockaddr_in6 addr;
91 socklen_t len;
93 memset(&addr, 0, sizeof(addr));
94 addr.sin6_family = AF_INET6;
95 addr.sin6_addr = in6addr_any;
96 addr.sin6_port = 0;
97 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
98 return -1;
101 len = sizeof(addr);
102 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
103 return -1;
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);
112 int i;
114 for (i = 0; i < nb; i++) {
115 sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
116 if (sock[i] < 0) {
117 break;
119 port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
120 inet_get_free_port_socket_ipv4(sock[i]);
121 if (port[i] == -1) {
122 break;
126 nb = i;
127 for (i = 0; i < nb; i++) {
128 close(sock[i]);
131 return nb;
134 static int inet_get_free_port(bool ipv6)
136 int nb, port;
138 nb = inet_get_free_port_multiple(1, &port, ipv6);
139 g_assert_cmpint(nb, ==, 1);
141 return port;
144 static void test_stream_inet_ipv4(void)
146 QTestState *qts0, *qts1;
147 char *expect;
148 int port;
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",
164 port);
165 EXPECT_STATE(qts1, expect, 0);
166 g_free(expect);
168 /* the port is unknown, check only the address */
169 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
171 qtest_quit(qts1);
172 qtest_quit(qts0);
175 static void wait_stream_connected(QTestState *qts, const char *id,
176 SocketAddress **addr)
178 QDict *resp, *data;
179 QString *qstr;
180 QObject *obj;
181 Visitor *v = NULL;
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);
197 visit_free(v);
198 qobject_unref(resp);
201 static void wait_stream_disconnected(QTestState *qts, const char *id)
203 QDict *resp, *data;
204 QString *qstr;
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));
215 qobject_unref(resp);
218 static void test_stream_unix_reconnect(void)
220 QTestState *qts0, *qts1;
221 SocketAddress *addr;
222 gchar *path;
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);
240 /* kill server */
241 qtest_quit(qts0);
243 /* check client has been disconnected */
244 wait_stream_disconnected(qts1, "st0");
246 /* restart server */
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);
262 qtest_quit(qts1);
263 qtest_quit(qts0);
264 g_free(path);
267 static void test_stream_inet_ipv6(void)
269 QTestState *qts0, *qts1;
270 char *expect;
271 int port;
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",
287 port);
288 EXPECT_STATE(qts1, expect, 0);
289 g_free(expect);
291 /* the port is unknown, check only the address */
292 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
294 qtest_quit(qts1);
295 qtest_quit(qts0);
298 static void test_stream_unix(void)
300 QTestState *qts0, *qts1;
301 char *expect;
302 gchar *path;
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,",
309 path);
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",
316 path);
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);
321 g_free(expect);
322 g_free(path);
324 qtest_quit(qts1);
325 qtest_quit(qts0);
328 #ifdef CONFIG_LINUX
329 static void test_stream_unix_abstract(void)
331 QTestState *qts0, *qts1;
332 char *expect;
333 gchar *path;
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,"
340 "addr.abstract=on",
341 path);
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",
348 path);
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);
353 g_free(expect);
354 g_free(path);
356 qtest_quit(qts1);
357 qtest_quit(qts0);
359 #endif
361 #ifndef _WIN32
362 static void test_stream_fd(void)
364 QTestState *qts0, *qts1;
365 int sock[2];
366 int ret;
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",
373 sock[0]);
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",
379 sock[1]);
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);
384 qtest_quit(qts1);
385 qtest_quit(qts0);
387 close(sock[0]);
388 close(sock[1]);
390 #endif
392 static void test_dgram_inet(void)
394 QTestState *qts0, *qts1;
395 char *expect;
396 int port[2];
397 int nb;
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",
406 port[0], port[1]);
408 expect = g_strdup_printf("st0: index=0,type=dgram,"
409 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
410 port[0], port[1]);
411 EXPECT_STATE(qts0, expect, 0);
412 g_free(expect);
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",
418 port[1], port[0]);
420 expect = g_strdup_printf("st0: index=0,type=dgram,"
421 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
422 port[1], port[0]);
423 EXPECT_STATE(qts1, expect, 0);
424 g_free(expect);
426 qtest_quit(qts1);
427 qtest_quit(qts0);
430 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
431 static void test_dgram_mcast(void)
433 QTestState *qts;
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);
441 qtest_quit(qts);
443 #endif
445 #ifndef _WIN32
446 static void test_dgram_unix(void)
448 QTestState *qts0, *qts1;
449 char *expect;
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",
458 path0, path1);
460 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
461 path0, path1);
462 EXPECT_STATE(qts0, expect, 0);
463 g_free(expect);
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",
468 path1, path0);
471 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
472 path1, path0);
473 EXPECT_STATE(qts1, expect, 0);
474 g_free(expect);
476 unlink(path0);
477 g_free(path0);
478 unlink(path1);
479 g_free(path1);
481 qtest_quit(qts1);
482 qtest_quit(qts0);
485 static void test_dgram_fd(void)
487 QTestState *qts0, *qts1;
488 char *expect;
489 int ret;
490 int sv[2];
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",
497 sv[0]);
499 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
500 EXPECT_STATE(qts0, expect, 0);
501 g_free(expect);
503 qts1 = qtest_initf("-nodefaults -M none "
504 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
505 sv[1]);
508 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
509 EXPECT_STATE(qts1, expect, 0);
510 g_free(expect);
512 qtest_quit(qts1);
513 qtest_quit(qts0);
515 close(sv[0]);
516 close(sv[1]);
518 #endif
520 int main(int argc, char **argv)
522 int ret;
523 bool has_ipv4, has_ipv6, has_afunix;
524 g_autoptr(GError) err = NULL;
526 socket_init();
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);
539 if (has_ipv4) {
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);
544 #endif
546 if (has_ipv6) {
547 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
550 socket_check_afunix_support(&has_afunix);
551 if (has_afunix) {
552 #ifndef _WIN32
553 qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
554 #endif
555 qtest_add_func("/netdev/stream/unix", test_stream_unix);
556 qtest_add_func("/netdev/stream/unix/reconnect",
557 test_stream_unix_reconnect);
558 #ifdef CONFIG_LINUX
559 qtest_add_func("/netdev/stream/unix/abstract",
560 test_stream_unix_abstract);
561 #endif
562 #ifndef _WIN32
563 qtest_add_func("/netdev/stream/fd", test_stream_fd);
564 qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
565 #endif
568 ret = g_test_run();
570 g_rmdir(tmpdir);
571 g_free(tmpdir);
573 return ret;