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 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 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 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 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 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 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 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 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 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 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 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 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 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 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) 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 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 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 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