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 closesocket(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 closesocket(sock[0]); 365 closesocket(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 closesocket(sv[0]); 491 closesocket(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 } 550