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 60
21 #define EXPECT_STATE(q, e, t) \
24 g_test_timer_start(); \
27 resp = qtest_hmp(q, "info network"); \
29 strrchr(resp, t)[0] = 0; \
31 if (g_str_equal(resp, e)) { \
34 } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \
35 g_assert_cmpstr(resp, ==, e); \
41 static int inet_get_free_port_socket_ipv4(int sock
)
43 struct sockaddr_in addr
;
46 memset(&addr
, 0, sizeof(addr
));
47 addr
.sin_family
= AF_INET
;
48 addr
.sin_addr
.s_addr
= INADDR_ANY
;
50 if (bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
55 if (getsockname(sock
, (struct sockaddr
*)&addr
, &len
) < 0) {
59 return ntohs(addr
.sin_port
);
62 static int inet_get_free_port_socket_ipv6(int sock
)
64 struct sockaddr_in6 addr
;
67 memset(&addr
, 0, sizeof(addr
));
68 addr
.sin6_family
= AF_INET6
;
69 addr
.sin6_addr
= in6addr_any
;
71 if (bind(sock
, (struct sockaddr
*)&addr
, sizeof(addr
)) < 0) {
76 if (getsockname(sock
, (struct sockaddr
*)&addr
, &len
) < 0) {
80 return ntohs(addr
.sin6_port
);
83 static int inet_get_free_port_multiple(int nb
, int *port
, bool ipv6
)
88 for (i
= 0; i
< nb
; i
++) {
89 sock
[i
] = socket(ipv6
? AF_INET6
: AF_INET
, SOCK_STREAM
, 0);
93 port
[i
] = ipv6
? inet_get_free_port_socket_ipv6(sock
[i
]) :
94 inet_get_free_port_socket_ipv4(sock
[i
]);
101 for (i
= 0; i
< nb
; i
++) {
108 static int inet_get_free_port(bool ipv6
)
112 nb
= inet_get_free_port_multiple(1, &port
, ipv6
);
113 g_assert_cmpint(nb
, ==, 1);
118 static void test_stream_inet_ipv4(void)
120 QTestState
*qts0
, *qts1
;
124 port
= inet_get_free_port(false);
125 qts0
= qtest_initf("-nodefaults -M none "
126 "-netdev stream,id=st0,server=true,addr.type=inet,"
127 "addr.ipv4=on,addr.ipv6=off,"
128 "addr.host=127.0.0.1,addr.port=%d", port
);
130 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
132 qts1
= qtest_initf("-nodefaults -M none "
133 "-netdev stream,server=false,id=st0,addr.type=inet,"
134 "addr.ipv4=on,addr.ipv6=off,"
135 "addr.host=127.0.0.1,addr.port=%d", port
);
137 expect
= g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
139 EXPECT_STATE(qts1
, expect
, 0);
142 /* the port is unknown, check only the address */
143 EXPECT_STATE(qts0
, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
149 static void wait_stream_connected(QTestState
*qts
, const char *id
,
150 SocketAddress
**addr
)
157 resp
= qtest_qmp_eventwait_ref(qts
, "NETDEV_STREAM_CONNECTED");
158 g_assert_nonnull(resp
);
159 data
= qdict_get_qdict(resp
, "data");
160 g_assert_nonnull(data
);
162 qstr
= qobject_to(QString
, qdict_get(data
, "netdev-id"));
163 g_assert_nonnull(data
);
165 g_assert(!strcmp(qstring_get_str(qstr
), id
));
167 obj
= qdict_get(data
, "addr");
169 v
= qobject_input_visitor_new(obj
);
170 visit_type_SocketAddress(v
, NULL
, addr
, NULL
);
175 static void wait_stream_disconnected(QTestState
*qts
, const char *id
)
180 resp
= qtest_qmp_eventwait_ref(qts
, "NETDEV_STREAM_DISCONNECTED");
181 g_assert_nonnull(resp
);
182 data
= qdict_get_qdict(resp
, "data");
183 g_assert_nonnull(data
);
185 qstr
= qobject_to(QString
, qdict_get(data
, "netdev-id"));
186 g_assert_nonnull(data
);
188 g_assert(!strcmp(qstring_get_str(qstr
), id
));
192 static void test_stream_inet_reconnect(void)
194 QTestState
*qts0
, *qts1
;
198 port
= inet_get_free_port(false);
199 qts0
= qtest_initf("-nodefaults -M none "
200 "-netdev stream,id=st0,server=true,addr.type=inet,"
201 "addr.ipv4=on,addr.ipv6=off,"
202 "addr.host=127.0.0.1,addr.port=%d", port
);
204 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
206 qts1
= qtest_initf("-nodefaults -M none "
207 "-netdev stream,server=false,id=st0,addr.type=inet,"
208 "addr.ipv4=on,addr.ipv6=off,reconnect=1,"
209 "addr.host=127.0.0.1,addr.port=%d", port
);
211 wait_stream_connected(qts0
, "st0", &addr
);
212 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_INET
);
213 g_assert_cmpstr(addr
->u
.inet
.host
, ==, "127.0.0.1");
214 qapi_free_SocketAddress(addr
);
219 /* check client has been disconnected */
220 wait_stream_disconnected(qts1
, "st0");
223 qts0
= qtest_initf("-nodefaults -M none "
224 "-netdev stream,id=st0,server=true,addr.type=inet,"
225 "addr.ipv4=on,addr.ipv6=off,"
226 "addr.host=127.0.0.1,addr.port=%d", port
);
228 /* wait connection events*/
229 wait_stream_connected(qts0
, "st0", &addr
);
230 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_INET
);
231 g_assert_cmpstr(addr
->u
.inet
.host
, ==, "127.0.0.1");
232 qapi_free_SocketAddress(addr
);
234 wait_stream_connected(qts1
, "st0", &addr
);
235 g_assert_cmpint(addr
->type
, ==, SOCKET_ADDRESS_TYPE_INET
);
236 g_assert_cmpstr(addr
->u
.inet
.host
, ==, "127.0.0.1");
237 g_assert_cmpint(atoi(addr
->u
.inet
.port
), ==, port
);
238 qapi_free_SocketAddress(addr
);
244 static void test_stream_inet_ipv6(void)
246 QTestState
*qts0
, *qts1
;
250 port
= inet_get_free_port(true);
251 qts0
= qtest_initf("-nodefaults -M none "
252 "-netdev stream,id=st0,server=true,addr.type=inet,"
253 "addr.ipv4=off,addr.ipv6=on,"
254 "addr.host=::1,addr.port=%d", port
);
256 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
258 qts1
= qtest_initf("-nodefaults -M none "
259 "-netdev stream,server=false,id=st0,addr.type=inet,"
260 "addr.ipv4=off,addr.ipv6=on,"
261 "addr.host=::1,addr.port=%d", port
);
263 expect
= g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
265 EXPECT_STATE(qts1
, expect
, 0);
268 /* the port is unknown, check only the address */
269 EXPECT_STATE(qts0
, "st0: index=0,type=stream,tcp:::1", ':');
275 static void test_stream_unix(void)
277 QTestState
*qts0
, *qts1
;
281 path
= g_strconcat(tmpdir
, "/stream_unix", NULL
);
283 qts0
= qtest_initf("-nodefaults -M none "
284 "-netdev stream,id=st0,server=true,"
285 "addr.type=unix,addr.path=%s,",
288 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
290 qts1
= qtest_initf("-nodefaults -M none "
291 "-netdev stream,id=st0,server=false,"
292 "addr.type=unix,addr.path=%s",
295 expect
= g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path
);
296 EXPECT_STATE(qts1
, expect
, 0);
297 EXPECT_STATE(qts0
, expect
, 0);
306 static void test_stream_unix_abstract(void)
308 QTestState
*qts0
, *qts1
;
312 path
= g_strconcat(tmpdir
, "/stream_unix_abstract", NULL
);
314 qts0
= qtest_initf("-nodefaults -M none "
315 "-netdev stream,id=st0,server=true,"
316 "addr.type=unix,addr.path=%s,"
320 EXPECT_STATE(qts0
, "st0: index=0,type=stream,\r\n", 0);
322 qts1
= qtest_initf("-nodefaults -M none "
323 "-netdev stream,id=st0,server=false,"
324 "addr.type=unix,addr.path=%s,addr.abstract=on",
327 expect
= g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path
);
328 EXPECT_STATE(qts1
, expect
, 0);
329 EXPECT_STATE(qts0
, expect
, 0);
339 static void test_stream_fd(void)
341 QTestState
*qts0
, *qts1
;
345 ret
= socketpair(AF_LOCAL
, SOCK_STREAM
, 0, sock
);
346 g_assert_true(ret
== 0);
348 qts0
= qtest_initf("-nodefaults -M none "
349 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
352 EXPECT_STATE(qts0
, "st0: index=0,type=stream,unix:\r\n", 0);
354 qts1
= qtest_initf("-nodefaults -M none "
355 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
358 EXPECT_STATE(qts1
, "st0: index=0,type=stream,unix:\r\n", 0);
359 EXPECT_STATE(qts0
, "st0: index=0,type=stream,unix:\r\n", 0);
369 static void test_dgram_inet(void)
371 QTestState
*qts0
, *qts1
;
376 nb
= inet_get_free_port_multiple(2, port
, false);
377 g_assert_cmpint(nb
, ==, 2);
379 qts0
= qtest_initf("-nodefaults -M none "
380 "-netdev dgram,id=st0,"
381 "local.type=inet,local.host=127.0.0.1,local.port=%d,"
382 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
385 expect
= g_strdup_printf("st0: index=0,type=dgram,"
386 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
388 EXPECT_STATE(qts0
, expect
, 0);
391 qts1
= qtest_initf("-nodefaults -M none "
392 "-netdev dgram,id=st0,"
393 "local.type=inet,local.host=127.0.0.1,local.port=%d,"
394 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
397 expect
= g_strdup_printf("st0: index=0,type=dgram,"
398 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
400 EXPECT_STATE(qts1
, expect
, 0);
408 static void test_dgram_mcast(void)
412 qts
= qtest_initf("-nodefaults -M none "
413 "-netdev dgram,id=st0,"
414 "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
416 EXPECT_STATE(qts
, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
421 static void test_dgram_unix(void)
423 QTestState
*qts0
, *qts1
;
425 gchar
*path0
, *path1
;
427 path0
= g_strconcat(tmpdir
, "/dgram_unix0", NULL
);
428 path1
= g_strconcat(tmpdir
, "/dgram_unix1", NULL
);
430 qts0
= qtest_initf("-nodefaults -M none "
431 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
432 "remote.type=unix,remote.path=%s",
435 expect
= g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
437 EXPECT_STATE(qts0
, expect
, 0);
440 qts1
= qtest_initf("-nodefaults -M none "
441 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
442 "remote.type=unix,remote.path=%s",
446 expect
= g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
448 EXPECT_STATE(qts1
, expect
, 0);
460 static void test_dgram_fd(void)
462 QTestState
*qts0
, *qts1
;
467 ret
= socketpair(PF_UNIX
, SOCK_DGRAM
, 0, sv
);
468 g_assert_cmpint(ret
, !=, -1);
470 qts0
= qtest_initf("-nodefaults -M none "
471 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
474 expect
= g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv
[0]);
475 EXPECT_STATE(qts0
, expect
, 0);
478 qts1
= qtest_initf("-nodefaults -M none "
479 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
483 expect
= g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv
[1]);
484 EXPECT_STATE(qts1
, expect
, 0);
495 int main(int argc
, char **argv
)
498 bool has_ipv4
, has_ipv6
, has_afunix
;
499 g_autoptr(GError
) err
= NULL
;
502 g_test_init(&argc
, &argv
, NULL
);
504 if (socket_check_protocol_support(&has_ipv4
, &has_ipv6
) < 0) {
505 g_error("socket_check_protocol_support() failed\n");
508 tmpdir
= g_dir_make_tmp("netdev-socket.XXXXXX", &err
);
509 if (tmpdir
== NULL
) {
510 g_error("Can't create temporary directory in %s: %s",
511 g_get_tmp_dir(), err
->message
);
515 qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4
);
516 qtest_add_func("/netdev/dgram/inet", test_dgram_inet
);
518 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast
);
520 qtest_add_func("/netdev/stream/inet/reconnect",
521 test_stream_inet_reconnect
);
524 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6
);
527 socket_check_afunix_support(&has_afunix
);
530 qtest_add_func("/netdev/dgram/unix", test_dgram_unix
);
532 qtest_add_func("/netdev/stream/unix", test_stream_unix
);
534 qtest_add_func("/netdev/stream/unix/abstract",
535 test_stream_unix_abstract
);
538 qtest_add_func("/netdev/stream/fd", test_stream_fd
);
539 qtest_add_func("/netdev/dgram/fd", test_dgram_fd
);