]> git.proxmox.com Git - mirror_qemu.git/blob - tests/qtest/netdev-socket.c
Merge tag 'block-pull-request' of https://gitlab.com/stefanha/qemu into staging
[mirror_qemu.git] / tests / qtest / netdev-socket.c
1 /*
2 * QTest testcase for netdev stream and dgram
3 *
4 * Copyright (c) 2022 Red Hat, Inc.
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
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"
18
19 #define CONNECTION_TIMEOUT 60
20
21 #define EXPECT_STATE(q, e, t) \
22 do { \
23 char *resp = NULL; \
24 g_test_timer_start(); \
25 do { \
26 g_free(resp); \
27 resp = qtest_hmp(q, "info network"); \
28 if (t) { \
29 strrchr(resp, t)[0] = 0; \
30 } \
31 if (g_str_equal(resp, e)) { \
32 break; \
33 } \
34 } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \
35 g_assert_cmpstr(resp, ==, e); \
36 g_free(resp); \
37 } while (0)
38
39 static gchar *tmpdir;
40
41 static int inet_get_free_port_socket_ipv4(int sock)
42 {
43 struct sockaddr_in addr;
44 socklen_t len;
45
46 memset(&addr, 0, sizeof(addr));
47 addr.sin_family = AF_INET;
48 addr.sin_addr.s_addr = INADDR_ANY;
49 addr.sin_port = 0;
50 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
51 return -1;
52 }
53
54 len = sizeof(addr);
55 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
56 return -1;
57 }
58
59 return ntohs(addr.sin_port);
60 }
61
62 static int inet_get_free_port_socket_ipv6(int sock)
63 {
64 struct sockaddr_in6 addr;
65 socklen_t len;
66
67 memset(&addr, 0, sizeof(addr));
68 addr.sin6_family = AF_INET6;
69 addr.sin6_addr = in6addr_any;
70 addr.sin6_port = 0;
71 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
72 return -1;
73 }
74
75 len = sizeof(addr);
76 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
77 return -1;
78 }
79
80 return ntohs(addr.sin6_port);
81 }
82
83 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
84 {
85 int sock[nb];
86 int i;
87
88 for (i = 0; i < nb; i++) {
89 sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
90 if (sock[i] < 0) {
91 break;
92 }
93 port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
94 inet_get_free_port_socket_ipv4(sock[i]);
95 if (port[i] == -1) {
96 break;
97 }
98 }
99
100 nb = i;
101 for (i = 0; i < nb; i++) {
102 close(sock[i]);
103 }
104
105 return nb;
106 }
107
108 static int inet_get_free_port(bool ipv6)
109 {
110 int nb, port;
111
112 nb = inet_get_free_port_multiple(1, &port, ipv6);
113 g_assert_cmpint(nb, ==, 1);
114
115 return port;
116 }
117
118 static void test_stream_inet_ipv4(void)
119 {
120 QTestState *qts0, *qts1;
121 char *expect;
122 int port;
123
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);
129
130 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
131
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);
136
137 expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n",
138 port);
139 EXPECT_STATE(qts1, expect, 0);
140 g_free(expect);
141
142 /* the port is unknown, check only the address */
143 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
144
145 qtest_quit(qts1);
146 qtest_quit(qts0);
147 }
148
149 static void wait_stream_connected(QTestState *qts, const char *id,
150 SocketAddress **addr)
151 {
152 QDict *resp, *data;
153 QString *qstr;
154 QObject *obj;
155 Visitor *v = NULL;
156
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);
161
162 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
163 g_assert_nonnull(data);
164
165 g_assert(!strcmp(qstring_get_str(qstr), id));
166
167 obj = qdict_get(data, "addr");
168
169 v = qobject_input_visitor_new(obj);
170 visit_type_SocketAddress(v, NULL, addr, NULL);
171 visit_free(v);
172 qobject_unref(resp);
173 }
174
175 static void wait_stream_disconnected(QTestState *qts, const char *id)
176 {
177 QDict *resp, *data;
178 QString *qstr;
179
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);
184
185 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
186 g_assert_nonnull(data);
187
188 g_assert(!strcmp(qstring_get_str(qstr), id));
189 qobject_unref(resp);
190 }
191
192 static void test_stream_inet_reconnect(void)
193 {
194 QTestState *qts0, *qts1;
195 int port;
196 SocketAddress *addr;
197
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);
203
204 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
205
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);
210
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);
215
216 /* kill server */
217 qtest_quit(qts0);
218
219 /* check client has been disconnected */
220 wait_stream_disconnected(qts1, "st0");
221
222 /* restart server */
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);
227
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);
233
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);
239
240 qtest_quit(qts1);
241 qtest_quit(qts0);
242 }
243
244 static void test_stream_inet_ipv6(void)
245 {
246 QTestState *qts0, *qts1;
247 char *expect;
248 int port;
249
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);
255
256 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
257
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);
262
263 expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
264 port);
265 EXPECT_STATE(qts1, expect, 0);
266 g_free(expect);
267
268 /* the port is unknown, check only the address */
269 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
270
271 qtest_quit(qts1);
272 qtest_quit(qts0);
273 }
274
275 static void test_stream_unix(void)
276 {
277 QTestState *qts0, *qts1;
278 char *expect;
279 gchar *path;
280
281 path = g_strconcat(tmpdir, "/stream_unix", NULL);
282
283 qts0 = qtest_initf("-nodefaults -M none "
284 "-netdev stream,id=st0,server=true,"
285 "addr.type=unix,addr.path=%s,",
286 path);
287
288 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
289
290 qts1 = qtest_initf("-nodefaults -M none "
291 "-netdev stream,id=st0,server=false,"
292 "addr.type=unix,addr.path=%s",
293 path);
294
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);
298 g_free(expect);
299 g_free(path);
300
301 qtest_quit(qts1);
302 qtest_quit(qts0);
303 }
304
305 #ifdef CONFIG_LINUX
306 static void test_stream_unix_abstract(void)
307 {
308 QTestState *qts0, *qts1;
309 char *expect;
310 gchar *path;
311
312 path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
313
314 qts0 = qtest_initf("-nodefaults -M none "
315 "-netdev stream,id=st0,server=true,"
316 "addr.type=unix,addr.path=%s,"
317 "addr.abstract=on",
318 path);
319
320 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
321
322 qts1 = qtest_initf("-nodefaults -M none "
323 "-netdev stream,id=st0,server=false,"
324 "addr.type=unix,addr.path=%s,addr.abstract=on",
325 path);
326
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);
330 g_free(expect);
331 g_free(path);
332
333 qtest_quit(qts1);
334 qtest_quit(qts0);
335 }
336 #endif
337
338 #ifndef _WIN32
339 static void test_stream_fd(void)
340 {
341 QTestState *qts0, *qts1;
342 int sock[2];
343 int ret;
344
345 ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
346 g_assert_true(ret == 0);
347
348 qts0 = qtest_initf("-nodefaults -M none "
349 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
350 sock[0]);
351
352 EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
353
354 qts1 = qtest_initf("-nodefaults -M none "
355 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
356 sock[1]);
357
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);
360
361 qtest_quit(qts1);
362 qtest_quit(qts0);
363
364 close(sock[0]);
365 close(sock[1]);
366 }
367 #endif
368
369 static void test_dgram_inet(void)
370 {
371 QTestState *qts0, *qts1;
372 char *expect;
373 int port[2];
374 int nb;
375
376 nb = inet_get_free_port_multiple(2, port, false);
377 g_assert_cmpint(nb, ==, 2);
378
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",
383 port[0], port[1]);
384
385 expect = g_strdup_printf("st0: index=0,type=dgram,"
386 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
387 port[0], port[1]);
388 EXPECT_STATE(qts0, expect, 0);
389 g_free(expect);
390
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",
395 port[1], port[0]);
396
397 expect = g_strdup_printf("st0: index=0,type=dgram,"
398 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
399 port[1], port[0]);
400 EXPECT_STATE(qts1, expect, 0);
401 g_free(expect);
402
403 qtest_quit(qts1);
404 qtest_quit(qts0);
405 }
406
407 #ifndef _WIN32
408 static void test_dgram_mcast(void)
409 {
410 QTestState *qts;
411
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");
415
416 EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
417
418 qtest_quit(qts);
419 }
420
421 static void test_dgram_unix(void)
422 {
423 QTestState *qts0, *qts1;
424 char *expect;
425 gchar *path0, *path1;
426
427 path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
428 path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
429
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",
433 path0, path1);
434
435 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
436 path0, path1);
437 EXPECT_STATE(qts0, expect, 0);
438 g_free(expect);
439
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",
443 path1, path0);
444
445
446 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
447 path1, path0);
448 EXPECT_STATE(qts1, expect, 0);
449 g_free(expect);
450
451 unlink(path0);
452 g_free(path0);
453 unlink(path1);
454 g_free(path1);
455
456 qtest_quit(qts1);
457 qtest_quit(qts0);
458 }
459
460 static void test_dgram_fd(void)
461 {
462 QTestState *qts0, *qts1;
463 char *expect;
464 int ret;
465 int sv[2];
466
467 ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
468 g_assert_cmpint(ret, !=, -1);
469
470 qts0 = qtest_initf("-nodefaults -M none "
471 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
472 sv[0]);
473
474 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
475 EXPECT_STATE(qts0, expect, 0);
476 g_free(expect);
477
478 qts1 = qtest_initf("-nodefaults -M none "
479 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
480 sv[1]);
481
482
483 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
484 EXPECT_STATE(qts1, expect, 0);
485 g_free(expect);
486
487 qtest_quit(qts1);
488 qtest_quit(qts0);
489
490 close(sv[0]);
491 close(sv[1]);
492 }
493 #endif
494
495 int main(int argc, char **argv)
496 {
497 int ret;
498 bool has_ipv4, has_ipv6, has_afunix;
499 g_autoptr(GError) err = NULL;
500
501 socket_init();
502 g_test_init(&argc, &argv, NULL);
503
504 if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
505 g_error("socket_check_protocol_support() failed\n");
506 }
507
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);
512 }
513
514 if (has_ipv4) {
515 qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
516 qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
517 #ifndef _WIN32
518 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
519 #endif
520 qtest_add_func("/netdev/stream/inet/reconnect",
521 test_stream_inet_reconnect);
522 }
523 if (has_ipv6) {
524 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
525 }
526
527 socket_check_afunix_support(&has_afunix);
528 if (has_afunix) {
529 #ifndef _WIN32
530 qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
531 #endif
532 qtest_add_func("/netdev/stream/unix", test_stream_unix);
533 #ifdef CONFIG_LINUX
534 qtest_add_func("/netdev/stream/unix/abstract",
535 test_stream_unix_abstract);
536 #endif
537 #ifndef _WIN32
538 qtest_add_func("/netdev/stream/fd", test_stream_fd);
539 qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
540 #endif
541 }
542
543 ret = g_test_run();
544
545 g_rmdir(tmpdir);
546 g_free(tmpdir);
547
548 return ret;
549 }