master
c 569 lines 15.8 KB
Raw
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 <glib/gstdio.h>
11 #include "../unit/socket-helpers.h"
12 #include "libqtest.h"
13 #include "qapi/qapi-visit-sockets.h"
14 #include "qapi/qobject-input-visitor.h"
15 #include "qemu/sockets.h"
16 #include "qobject/qdict.h"
17 #include "qobject/qlist.h"
18 #include "qobject/qstring.h"
19
20 #define CONNECTION_TIMEOUT 60
21
22 /*
23 * Only checks info-str; type is implicit from the netdev created by each test
24 */
25 static char *query_net_info_str(QTestState *q, const char *name)
26 {
27 QDict *ret;
28 QList *clients;
29 QListEntry *entry;
30
31 ret = qtest_qmp_assert_success_ref(q, "{'execute': 'x-query-network'}");
32 clients = qdict_get_qlist(ret, "clients");
33 g_assert_nonnull(clients);
34
35 QLIST_FOREACH_ENTRY(clients, entry) {
36 QDict *client = qobject_to(QDict, qlist_entry_obj(entry));
37 if (g_str_equal(qdict_get_str(client, "name"), name)) {
38 char *info = g_strdup(qdict_get_str(client, "info-str"));
39 qobject_unref(ret);
40 return info;
41 }
42 }
43
44 g_assert_not_reached();
45 }
46
47 #define EXPECT_STATE(q, id, e, t) \
48 do { \
49 char *info = NULL; \
50 g_test_timer_start(); \
51 do { \
52 g_free(info); \
53 info = query_net_info_str(q, id); \
54 if (t) { \
55 char *p = strrchr(info, t); \
56 if (p) { \
57 p[0] = 0; \
58 } \
59 } \
60 if (g_str_equal(info, e)) { \
61 break; \
62 } \
63 } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \
64 g_assert_cmpstr(info, ==, e); \
65 g_free(info); \
66 } while (0)
67
68 static gchar *tmpdir;
69
70 static int inet_get_free_port_socket_ipv4(int sock)
71 {
72 struct sockaddr_in addr;
73 socklen_t len;
74
75 memset(&addr, 0, sizeof(addr));
76 addr.sin_family = AF_INET;
77 addr.sin_addr.s_addr = INADDR_ANY;
78 addr.sin_port = 0;
79 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
80 return -1;
81 }
82
83 len = sizeof(addr);
84 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
85 return -1;
86 }
87
88 return ntohs(addr.sin_port);
89 }
90
91 static int inet_get_free_port_socket_ipv6(int sock)
92 {
93 struct sockaddr_in6 addr;
94 socklen_t len;
95
96 memset(&addr, 0, sizeof(addr));
97 addr.sin6_family = AF_INET6;
98 addr.sin6_addr = in6addr_any;
99 addr.sin6_port = 0;
100 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
101 return -1;
102 }
103
104 len = sizeof(addr);
105 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
106 return -1;
107 }
108
109 return ntohs(addr.sin6_port);
110 }
111
112 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
113 {
114 g_autofree int *sock = g_new(int, nb);
115 int i;
116
117 for (i = 0; i < nb; i++) {
118 sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0);
119 if (sock[i] < 0) {
120 break;
121 }
122 port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) :
123 inet_get_free_port_socket_ipv4(sock[i]);
124 if (port[i] == -1) {
125 break;
126 }
127 }
128
129 nb = i;
130 for (i = 0; i < nb; i++) {
131 close(sock[i]);
132 }
133
134 return nb;
135 }
136
137 static int inet_get_free_port(bool ipv6)
138 {
139 int nb, port;
140
141 nb = inet_get_free_port_multiple(1, &port, ipv6);
142 g_assert_cmpint(nb, ==, 1);
143
144 return port;
145 }
146
147 static void test_stream_inet_ipv4(void)
148 {
149 QTestState *qts0, *qts1;
150 char *expect;
151 int port;
152
153 port = inet_get_free_port(false);
154 qts0 = qtest_initf("-nodefaults -M none "
155 "-netdev stream,id=st0,server=true,addr.type=inet,"
156 "addr.ipv4=on,addr.ipv6=off,"
157 "addr.host=127.0.0.1,addr.port=%d", port);
158
159 EXPECT_STATE(qts0, "st0", "listening", 0);
160
161 qts1 = qtest_initf("-nodefaults -M none "
162 "-netdev stream,server=false,id=st0,addr.type=inet,"
163 "addr.ipv4=on,addr.ipv6=off,"
164 "addr.host=127.0.0.1,addr.port=%d", port);
165
166 expect = g_strdup_printf("tcp:127.0.0.1:%d", port);
167 EXPECT_STATE(qts1, "st0", expect, 0);
168 g_free(expect);
169
170 /* the port is unknown, check only the address */
171 EXPECT_STATE(qts0, "st0", "tcp:127.0.0.1", ':');
172
173 qtest_quit(qts1);
174 qtest_quit(qts0);
175 }
176
177 static void wait_stream_connected(QTestState *qts, const char *id,
178 SocketAddress **addr)
179 {
180 QDict *resp, *data;
181 QString *qstr;
182 QObject *obj;
183 Visitor *v = NULL;
184
185 resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_CONNECTED");
186 g_assert_nonnull(resp);
187 data = qdict_get_qdict(resp, "data");
188 g_assert_nonnull(data);
189
190 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
191 g_assert_nonnull(data);
192
193 g_assert(!strcmp(qstring_get_str(qstr), id));
194
195 obj = qdict_get(data, "addr");
196
197 v = qobject_input_visitor_new(obj);
198 visit_type_SocketAddress(v, NULL, addr, NULL);
199 visit_free(v);
200 qobject_unref(resp);
201 }
202
203 static void wait_stream_disconnected(QTestState *qts, const char *id)
204 {
205 QDict *resp, *data;
206 QString *qstr;
207
208 resp = qtest_qmp_eventwait_ref(qts, "NETDEV_STREAM_DISCONNECTED");
209 g_assert_nonnull(resp);
210 data = qdict_get_qdict(resp, "data");
211 g_assert_nonnull(data);
212
213 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
214 g_assert_nonnull(data);
215
216 g_assert(!strcmp(qstring_get_str(qstr), id));
217 qobject_unref(resp);
218 }
219
220 static void test_stream_unix_reconnect(void)
221 {
222 QTestState *qts0, *qts1;
223 SocketAddress *addr;
224 gchar *path;
225
226 path = g_strconcat(tmpdir, "/stream_unix_reconnect", NULL);
227 qts0 = qtest_initf("-nodefaults -M none "
228 "-netdev stream,id=st0,server=true,addr.type=unix,"
229 "addr.path=%s", path);
230
231 EXPECT_STATE(qts0, "st0", "listening", 0);
232
233 qts1 = qtest_initf("-nodefaults -M none "
234 "-netdev stream,server=false,id=st0,addr.type=unix,"
235 "addr.path=%s,reconnect-ms=1000", path);
236
237 wait_stream_connected(qts0, "st0", &addr);
238 g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
239 g_assert_cmpstr(addr->u.q_unix.path, ==, path);
240 qapi_free_SocketAddress(addr);
241
242 /* kill server */
243 qtest_quit(qts0);
244
245 /* check client has been disconnected */
246 wait_stream_disconnected(qts1, "st0");
247
248 /* restart server */
249 qts0 = qtest_initf("-nodefaults -M none "
250 "-netdev stream,id=st0,server=true,addr.type=unix,"
251 "addr.path=%s", path);
252
253 /* wait connection events*/
254 wait_stream_connected(qts0, "st0", &addr);
255 g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
256 g_assert_cmpstr(addr->u.q_unix.path, ==, path);
257 qapi_free_SocketAddress(addr);
258
259 wait_stream_connected(qts1, "st0", &addr);
260 g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
261 g_assert_cmpstr(addr->u.q_unix.path, ==, path);
262 qapi_free_SocketAddress(addr);
263
264 qtest_quit(qts1);
265 qtest_quit(qts0);
266 g_free(path);
267 }
268
269 static void test_stream_inet_ipv6(void)
270 {
271 QTestState *qts0, *qts1;
272 char *expect;
273 int port;
274
275 port = inet_get_free_port(true);
276 qts0 = qtest_initf("-nodefaults -M none "
277 "-netdev stream,id=st0,server=true,addr.type=inet,"
278 "addr.ipv4=off,addr.ipv6=on,"
279 "addr.host=::1,addr.port=%d", port);
280
281 EXPECT_STATE(qts0, "st0", "listening", 0);
282
283 qts1 = qtest_initf("-nodefaults -M none "
284 "-netdev stream,server=false,id=st0,addr.type=inet,"
285 "addr.ipv4=off,addr.ipv6=on,"
286 "addr.host=::1,addr.port=%d", port);
287
288 expect = g_strdup_printf("tcp:::1:%d", port);
289 EXPECT_STATE(qts1, "st0", expect, 0);
290 g_free(expect);
291
292 /* the port is unknown, check only the address */
293 EXPECT_STATE(qts0, "st0", "tcp:::1", ':');
294
295 qtest_quit(qts1);
296 qtest_quit(qts0);
297 }
298
299 static void test_stream_unix(void)
300 {
301 QTestState *qts0, *qts1;
302 char *expect;
303 gchar *path;
304
305 path = g_strconcat(tmpdir, "/stream_unix", NULL);
306
307 qts0 = qtest_initf("-nodefaults -M none "
308 "-netdev stream,id=st0,server=true,"
309 "addr.type=unix,addr.path=%s,",
310 path);
311
312 EXPECT_STATE(qts0, "st0", "listening", 0);
313
314 qts1 = qtest_initf("-nodefaults -M none "
315 "-netdev stream,id=st0,server=false,"
316 "addr.type=unix,addr.path=%s",
317 path);
318
319 expect = g_strdup_printf("unix:%s", path);
320 EXPECT_STATE(qts1, "st0", expect, 0);
321 EXPECT_STATE(qts0, "st0", expect, 0);
322 g_free(expect);
323 g_free(path);
324
325 qtest_quit(qts1);
326 qtest_quit(qts0);
327 }
328
329 #ifdef CONFIG_LINUX
330 static void test_stream_unix_abstract(void)
331 {
332 QTestState *qts0, *qts1;
333 char *expect;
334 gchar *path;
335
336 path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
337
338 qts0 = qtest_initf("-nodefaults -M none "
339 "-netdev stream,id=st0,server=true,"
340 "addr.type=unix,addr.path=%s,"
341 "addr.abstract=on",
342 path);
343
344 EXPECT_STATE(qts0, "st0", "listening", 0);
345
346 qts1 = qtest_initf("-nodefaults -M none "
347 "-netdev stream,id=st0,server=false,"
348 "addr.type=unix,addr.path=%s,addr.abstract=on",
349 path);
350
351 expect = g_strdup_printf("unix:%s", path);
352 EXPECT_STATE(qts1, "st0", expect, 0);
353 EXPECT_STATE(qts0, "st0", expect, 0);
354 g_free(expect);
355 g_free(path);
356
357 qtest_quit(qts1);
358 qtest_quit(qts0);
359 }
360 #endif
361
362 #ifndef _WIN32
363 static void test_stream_fd(void)
364 {
365 QTestState *qts0, *qts1;
366 int sock[2];
367 int ret;
368
369 ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
370 g_assert_true(ret == 0);
371
372 qts0 = qtest_initf("-nodefaults -M none "
373 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
374 sock[0]);
375
376 EXPECT_STATE(qts0, "st0", "unix:", 0);
377
378 qts1 = qtest_initf("-nodefaults -M none "
379 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
380 sock[1]);
381
382 EXPECT_STATE(qts1, "st0", "unix:", 0);
383 EXPECT_STATE(qts0, "st0", "unix:", 0);
384
385 qtest_quit(qts1);
386 qtest_quit(qts0);
387
388 close(sock[0]);
389 close(sock[1]);
390 }
391 #endif
392
393 static void test_dgram_inet(void)
394 {
395 QTestState *qts0, *qts1;
396 char *expect;
397 int port[2];
398 int nb;
399
400 nb = inet_get_free_port_multiple(2, port, false);
401 g_assert_cmpint(nb, ==, 2);
402
403 qts0 = qtest_initf("-nodefaults -M none "
404 "-netdev dgram,id=st0,"
405 "local.type=inet,local.host=127.0.0.1,local.port=%d,"
406 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
407 port[0], port[1]);
408
409 expect = g_strdup_printf("udp=127.0.0.1:%d/127.0.0.1:%d",
410 port[0], port[1]);
411 EXPECT_STATE(qts0, "st0", expect, 0);
412 g_free(expect);
413
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]);
419
420 expect = g_strdup_printf("udp=127.0.0.1:%d/127.0.0.1:%d",
421 port[1], port[0]);
422 EXPECT_STATE(qts1, "st0", expect, 0);
423 g_free(expect);
424
425 qtest_quit(qts1);
426 qtest_quit(qts0);
427 }
428
429 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
430 static void test_dgram_mcast(void)
431 {
432 QTestState *qts;
433
434 qts = qtest_initf("-nodefaults -M none "
435 "-netdev dgram,id=st0,"
436 "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
437
438 EXPECT_STATE(qts, "st0", "mcast=230.0.0.1:1234", 0);
439
440 qtest_quit(qts);
441 }
442 #endif
443
444 #ifndef _WIN32
445 static void test_dgram_unix(void)
446 {
447 QTestState *qts0, *qts1;
448 char *expect;
449 gchar *path0, *path1;
450
451 path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
452 path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
453
454 qts0 = qtest_initf("-nodefaults -M none "
455 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
456 "remote.type=unix,remote.path=%s",
457 path0, path1);
458
459 expect = g_strdup_printf("udp=%s:%s", path0, path1);
460 EXPECT_STATE(qts0, "st0", expect, 0);
461 g_free(expect);
462
463 qts1 = qtest_initf("-nodefaults -M none "
464 "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
465 "remote.type=unix,remote.path=%s",
466 path1, path0);
467
468 expect = g_strdup_printf("udp=%s:%s", path1, path0);
469 EXPECT_STATE(qts1, "st0", expect, 0);
470 g_free(expect);
471
472 unlink(path0);
473 g_free(path0);
474 unlink(path1);
475 g_free(path1);
476
477 qtest_quit(qts1);
478 qtest_quit(qts0);
479 }
480
481 static void test_dgram_fd(void)
482 {
483 QTestState *qts0, *qts1;
484 char *expect;
485 int ret;
486 int sv[2];
487
488 ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
489 g_assert_cmpint(ret, !=, -1);
490
491 qts0 = qtest_initf("-nodefaults -M none "
492 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
493 sv[0]);
494
495 expect = g_strdup_printf("fd=%d unix", sv[0]);
496 EXPECT_STATE(qts0, "st0", expect, 0);
497 g_free(expect);
498
499 qts1 = qtest_initf("-nodefaults -M none "
500 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
501 sv[1]);
502
503 expect = g_strdup_printf("fd=%d unix", sv[1]);
504 EXPECT_STATE(qts1, "st0", expect, 0);
505 g_free(expect);
506
507 qtest_quit(qts1);
508 qtest_quit(qts0);
509
510 close(sv[0]);
511 close(sv[1]);
512 }
513 #endif
514
515 int main(int argc, char **argv)
516 {
517 int ret;
518 bool has_ipv4, has_ipv6, has_afunix;
519 g_autoptr(GError) err = NULL;
520
521 socket_init();
522 g_test_init(&argc, &argv, NULL);
523
524 if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
525 g_error("socket_check_protocol_support() failed\n");
526 }
527
528 tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err);
529 if (tmpdir == NULL) {
530 g_error("Can't create temporary directory in %s: %s",
531 g_get_tmp_dir(), err->message);
532 }
533
534 if (has_ipv4) {
535 qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
536 qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
537 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
538 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
539 #endif
540 }
541 if (has_ipv6) {
542 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
543 }
544
545 socket_check_afunix_support(&has_afunix);
546 if (has_afunix) {
547 #ifndef _WIN32
548 qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
549 #endif
550 qtest_add_func("/netdev/stream/unix/oneshot", test_stream_unix);
551 qtest_add_func("/netdev/stream/unix/reconnect",
552 test_stream_unix_reconnect);
553 #ifdef CONFIG_LINUX
554 qtest_add_func("/netdev/stream/unix/abstract",
555 test_stream_unix_abstract);
556 #endif
557 #ifndef _WIN32
558 qtest_add_func("/netdev/stream/fd", test_stream_fd);
559 qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
560 #endif
561 }
562
563 ret = g_test_run();
564
565 g_rmdir(tmpdir);
566 g_free(tmpdir);
567
568 return ret;
569 }