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 120
20
connection_timeout(void)21 static double connection_timeout(void)
22 {
23 double load;
24 int ret = getloadavg(&load, 1);
25
26 /*
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).
29 */
30 if (ret < 1 || load < 1.0) {
31 load = 1.0;
32 }
33 /*
34 * No one wants to wait more than 10 minutes for this test. Higher load?
35 * Too bad.
36 */
37 if (load > 10.0) {
38 fprintf(stderr, "Warning: load %f higher than 10 - test might timeout\n",
39 load);
40 load = 10.0;
41 }
42
43 /* if load is high increase timeout as we might not get a chance to run */
44 return load * CONNECTION_TIMEOUT;
45 }
46
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)
64
65 static gchar *tmpdir;
66
inet_get_free_port_socket_ipv4(int sock)67 static int inet_get_free_port_socket_ipv4(int sock)
68 {
69 struct sockaddr_in addr;
70 socklen_t len;
71
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;
78 }
79
80 len = sizeof(addr);
81 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
82 return -1;
83 }
84
85 return ntohs(addr.sin_port);
86 }
87
inet_get_free_port_socket_ipv6(int sock)88 static int inet_get_free_port_socket_ipv6(int sock)
89 {
90 struct sockaddr_in6 addr;
91 socklen_t len;
92
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;
99 }
100
101 len = sizeof(addr);
102 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) {
103 return -1;
104 }
105
106 return ntohs(addr.sin6_port);
107 }
108
inet_get_free_port_multiple(int nb,int * port,bool ipv6)109 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6)
110 {
111 g_autofree int *sock = g_new(int, nb);
112 int i;
113
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;
118 }
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;
123 }
124 }
125
126 nb = i;
127 for (i = 0; i < nb; i++) {
128 close(sock[i]);
129 }
130
131 return nb;
132 }
133
inet_get_free_port(bool ipv6)134 static int inet_get_free_port(bool ipv6)
135 {
136 int nb, port;
137
138 nb = inet_get_free_port_multiple(1, &port, ipv6);
139 g_assert_cmpint(nb, ==, 1);
140
141 return port;
142 }
143
test_stream_inet_ipv4(void)144 static void test_stream_inet_ipv4(void)
145 {
146 QTestState *qts0, *qts1;
147 char *expect;
148 int port;
149
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);
155
156 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
157
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);
162
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);
167
168 /* the port is unknown, check only the address */
169 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':');
170
171 qtest_quit(qts1);
172 qtest_quit(qts0);
173 }
174
wait_stream_connected(QTestState * qts,const char * id,SocketAddress ** addr)175 static void wait_stream_connected(QTestState *qts, const char *id,
176 SocketAddress **addr)
177 {
178 QDict *resp, *data;
179 QString *qstr;
180 QObject *obj;
181 Visitor *v = NULL;
182
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);
187
188 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
189 g_assert_nonnull(data);
190
191 g_assert(!strcmp(qstring_get_str(qstr), id));
192
193 obj = qdict_get(data, "addr");
194
195 v = qobject_input_visitor_new(obj);
196 visit_type_SocketAddress(v, NULL, addr, NULL);
197 visit_free(v);
198 qobject_unref(resp);
199 }
200
wait_stream_disconnected(QTestState * qts,const char * id)201 static void wait_stream_disconnected(QTestState *qts, const char *id)
202 {
203 QDict *resp, *data;
204 QString *qstr;
205
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);
210
211 qstr = qobject_to(QString, qdict_get(data, "netdev-id"));
212 g_assert_nonnull(data);
213
214 g_assert(!strcmp(qstring_get_str(qstr), id));
215 qobject_unref(resp);
216 }
217
test_stream_unix_reconnect(void)218 static void test_stream_unix_reconnect(void)
219 {
220 QTestState *qts0, *qts1;
221 SocketAddress *addr;
222 gchar *path;
223
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);
228
229 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
230
231 qts1 = qtest_initf("-nodefaults -M none "
232 "-netdev stream,server=false,id=st0,addr.type=unix,"
233 "addr.path=%s,reconnect=1", path);
234
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);
239
240 /* kill server */
241 qtest_quit(qts0);
242
243 /* check client has been disconnected */
244 wait_stream_disconnected(qts1, "st0");
245
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);
250
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);
256
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);
261
262 qtest_quit(qts1);
263 qtest_quit(qts0);
264 g_free(path);
265 }
266
test_stream_inet_ipv6(void)267 static void test_stream_inet_ipv6(void)
268 {
269 QTestState *qts0, *qts1;
270 char *expect;
271 int port;
272
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);
278
279 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
280
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);
285
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);
290
291 /* the port is unknown, check only the address */
292 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
293
294 qtest_quit(qts1);
295 qtest_quit(qts0);
296 }
297
test_stream_unix(void)298 static void test_stream_unix(void)
299 {
300 QTestState *qts0, *qts1;
301 char *expect;
302 gchar *path;
303
304 path = g_strconcat(tmpdir, "/stream_unix", NULL);
305
306 qts0 = qtest_initf("-nodefaults -M none "
307 "-netdev stream,id=st0,server=true,"
308 "addr.type=unix,addr.path=%s,",
309 path);
310
311 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
312
313 qts1 = qtest_initf("-nodefaults -M none "
314 "-netdev stream,id=st0,server=false,"
315 "addr.type=unix,addr.path=%s",
316 path);
317
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);
323
324 qtest_quit(qts1);
325 qtest_quit(qts0);
326 }
327
328 #ifdef CONFIG_LINUX
test_stream_unix_abstract(void)329 static void test_stream_unix_abstract(void)
330 {
331 QTestState *qts0, *qts1;
332 char *expect;
333 gchar *path;
334
335 path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
336
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);
342
343 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
344
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);
349
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);
355
356 qtest_quit(qts1);
357 qtest_quit(qts0);
358 }
359 #endif
360
361 #ifndef _WIN32
test_stream_fd(void)362 static void test_stream_fd(void)
363 {
364 QTestState *qts0, *qts1;
365 int sock[2];
366 int ret;
367
368 ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
369 g_assert_true(ret == 0);
370
371 qts0 = qtest_initf("-nodefaults -M none "
372 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
373 sock[0]);
374
375 EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
376
377 qts1 = qtest_initf("-nodefaults -M none "
378 "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
379 sock[1]);
380
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);
383
384 qtest_quit(qts1);
385 qtest_quit(qts0);
386
387 close(sock[0]);
388 close(sock[1]);
389 }
390 #endif
391
test_dgram_inet(void)392 static void test_dgram_inet(void)
393 {
394 QTestState *qts0, *qts1;
395 char *expect;
396 int port[2];
397 int nb;
398
399 nb = inet_get_free_port_multiple(2, port, false);
400 g_assert_cmpint(nb, ==, 2);
401
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]);
407
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);
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("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);
425
426 qtest_quit(qts1);
427 qtest_quit(qts0);
428 }
429
430 #if !defined(_WIN32) && !defined(CONFIG_DARWIN)
test_dgram_mcast(void)431 static void test_dgram_mcast(void)
432 {
433 QTestState *qts;
434
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");
438
439 EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
440
441 qtest_quit(qts);
442 }
443 #endif
444
445 #ifndef _WIN32
test_dgram_unix(void)446 static void test_dgram_unix(void)
447 {
448 QTestState *qts0, *qts1;
449 char *expect;
450 gchar *path0, *path1;
451
452 path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
453 path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
454
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);
459
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);
464
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);
469
470
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);
475
476 unlink(path0);
477 g_free(path0);
478 unlink(path1);
479 g_free(path1);
480
481 qtest_quit(qts1);
482 qtest_quit(qts0);
483 }
484
test_dgram_fd(void)485 static void test_dgram_fd(void)
486 {
487 QTestState *qts0, *qts1;
488 char *expect;
489 int ret;
490 int sv[2];
491
492 ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
493 g_assert_cmpint(ret, !=, -1);
494
495 qts0 = qtest_initf("-nodefaults -M none "
496 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
497 sv[0]);
498
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);
502
503 qts1 = qtest_initf("-nodefaults -M none "
504 "-netdev dgram,id=st0,local.type=fd,local.str=%d",
505 sv[1]);
506
507
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);
511
512 qtest_quit(qts1);
513 qtest_quit(qts0);
514
515 close(sv[0]);
516 close(sv[1]);
517 }
518 #endif
519
main(int argc,char ** argv)520 int main(int argc, char **argv)
521 {
522 int ret;
523 bool has_ipv4, has_ipv6, has_afunix;
524 g_autoptr(GError) err = NULL;
525
526 socket_init();
527 g_test_init(&argc, &argv, NULL);
528
529 if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
530 g_error("socket_check_protocol_support() failed\n");
531 }
532
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);
537 }
538
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
545 }
546 if (has_ipv6) {
547 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
548 }
549
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
566 }
567
568 ret = g_test_run();
569
570 g_rmdir(tmpdir);
571 g_free(tmpdir);
572
573 return ret;
574 }
575