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 15 #define CONNECTION_TIMEOUT 5 16 17 #define EXPECT_STATE(q, e, t) \ 18 do { \ 19 char *resp = NULL; \ 20 g_test_timer_start(); \ 21 do { \ 22 g_free(resp); \ 23 resp = qtest_hmp(q, "info network"); \ 24 if (t) { \ 25 strrchr(resp, t)[0] = 0; \ 26 } \ 27 if (g_str_equal(resp, e)) { \ 28 break; \ 29 } \ 30 } while (g_test_timer_elapsed() < CONNECTION_TIMEOUT); \ 31 g_assert_cmpstr(resp, ==, e); \ 32 g_free(resp); \ 33 } while (0) 34 35 static gchar *tmpdir; 36 37 static int inet_get_free_port_socket_ipv4(int sock) 38 { 39 struct sockaddr_in addr; 40 socklen_t len; 41 42 memset(&addr, 0, sizeof(addr)); 43 addr.sin_family = AF_INET; 44 addr.sin_addr.s_addr = INADDR_ANY; 45 addr.sin_port = 0; 46 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 47 return -1; 48 } 49 50 len = sizeof(addr); 51 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { 52 return -1; 53 } 54 55 return ntohs(addr.sin_port); 56 } 57 58 static int inet_get_free_port_socket_ipv6(int sock) 59 { 60 struct sockaddr_in6 addr; 61 socklen_t len; 62 63 memset(&addr, 0, sizeof(addr)); 64 addr.sin6_family = AF_INET6; 65 addr.sin6_addr = in6addr_any; 66 addr.sin6_port = 0; 67 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 68 return -1; 69 } 70 71 len = sizeof(addr); 72 if (getsockname(sock, (struct sockaddr *)&addr, &len) < 0) { 73 return -1; 74 } 75 76 return ntohs(addr.sin6_port); 77 } 78 79 static int inet_get_free_port_multiple(int nb, int *port, bool ipv6) 80 { 81 int sock[nb]; 82 int i; 83 84 for (i = 0; i < nb; i++) { 85 sock[i] = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); 86 if (sock[i] < 0) { 87 break; 88 } 89 port[i] = ipv6 ? inet_get_free_port_socket_ipv6(sock[i]) : 90 inet_get_free_port_socket_ipv4(sock[i]); 91 if (port[i] == -1) { 92 break; 93 } 94 } 95 96 nb = i; 97 for (i = 0; i < nb; i++) { 98 closesocket(sock[i]); 99 } 100 101 return nb; 102 } 103 104 static int inet_get_free_port(bool ipv6) 105 { 106 int nb, port; 107 108 nb = inet_get_free_port_multiple(1, &port, ipv6); 109 g_assert_cmpint(nb, ==, 1); 110 111 return port; 112 } 113 114 static void test_stream_inet_ipv4(void) 115 { 116 QTestState *qts0, *qts1; 117 char *expect; 118 int port; 119 120 port = inet_get_free_port(false); 121 qts0 = qtest_initf("-nodefaults -M none " 122 "-netdev stream,id=st0,server=true,addr.type=inet," 123 "addr.ipv4=on,addr.ipv6=off," 124 "addr.host=127.0.0.1,addr.port=%d", port); 125 126 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); 127 128 qts1 = qtest_initf("-nodefaults -M none " 129 "-netdev stream,server=false,id=st0,addr.type=inet," 130 "addr.ipv4=on,addr.ipv6=off," 131 "addr.host=127.0.0.1,addr.port=%d", port); 132 133 expect = g_strdup_printf("st0: index=0,type=stream,tcp:127.0.0.1:%d\r\n", 134 port); 135 EXPECT_STATE(qts1, expect, 0); 136 g_free(expect); 137 138 /* the port is unknown, check only the address */ 139 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:127.0.0.1", ':'); 140 141 qtest_quit(qts1); 142 qtest_quit(qts0); 143 } 144 145 static void test_stream_inet_ipv6(void) 146 { 147 QTestState *qts0, *qts1; 148 char *expect; 149 int port; 150 151 port = inet_get_free_port(true); 152 qts0 = qtest_initf("-nodefaults -M none " 153 "-netdev stream,id=st0,server=true,addr.type=inet," 154 "addr.ipv4=off,addr.ipv6=on," 155 "addr.host=::1,addr.port=%d", port); 156 157 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); 158 159 qts1 = qtest_initf("-nodefaults -M none " 160 "-netdev stream,server=false,id=st0,addr.type=inet," 161 "addr.ipv4=off,addr.ipv6=on," 162 "addr.host=::1,addr.port=%d", port); 163 164 expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n", 165 port); 166 EXPECT_STATE(qts1, expect, 0); 167 g_free(expect); 168 169 /* the port is unknown, check only the address */ 170 EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':'); 171 172 qtest_quit(qts1); 173 qtest_quit(qts0); 174 } 175 176 static void test_stream_unix(void) 177 { 178 QTestState *qts0, *qts1; 179 char *expect; 180 gchar *path; 181 182 path = g_strconcat(tmpdir, "/stream_unix", NULL); 183 184 qts0 = qtest_initf("-nodefaults -M none " 185 "-netdev stream,id=st0,server=true," 186 "addr.type=unix,addr.path=%s,", 187 path); 188 189 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); 190 191 qts1 = qtest_initf("-nodefaults -M none " 192 "-netdev stream,id=st0,server=false," 193 "addr.type=unix,addr.path=%s", 194 path); 195 196 expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); 197 EXPECT_STATE(qts1, expect, 0); 198 EXPECT_STATE(qts0, expect, 0); 199 g_free(expect); 200 g_free(path); 201 202 qtest_quit(qts1); 203 qtest_quit(qts0); 204 } 205 206 #ifdef CONFIG_LINUX 207 static void test_stream_unix_abstract(void) 208 { 209 QTestState *qts0, *qts1; 210 char *expect; 211 gchar *path; 212 213 path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL); 214 215 qts0 = qtest_initf("-nodefaults -M none " 216 "-netdev stream,id=st0,server=true," 217 "addr.type=unix,addr.path=%s," 218 "addr.abstract=on", 219 path); 220 221 EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0); 222 223 qts1 = qtest_initf("-nodefaults -M none " 224 "-netdev stream,id=st0,server=false," 225 "addr.type=unix,addr.path=%s,addr.abstract=on", 226 path); 227 228 expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path); 229 EXPECT_STATE(qts1, expect, 0); 230 EXPECT_STATE(qts0, expect, 0); 231 g_free(expect); 232 g_free(path); 233 234 qtest_quit(qts1); 235 qtest_quit(qts0); 236 } 237 #endif 238 239 #ifndef _WIN32 240 static void test_stream_fd(void) 241 { 242 QTestState *qts0, *qts1; 243 int sock[2]; 244 int ret; 245 246 ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock); 247 g_assert_true(ret == 0); 248 249 qts0 = qtest_initf("-nodefaults -M none " 250 "-netdev stream,id=st0,addr.type=fd,addr.str=%d", 251 sock[0]); 252 253 EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); 254 255 qts1 = qtest_initf("-nodefaults -M none " 256 "-netdev stream,id=st0,addr.type=fd,addr.str=%d", 257 sock[1]); 258 259 EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0); 260 EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0); 261 262 qtest_quit(qts1); 263 qtest_quit(qts0); 264 265 closesocket(sock[0]); 266 closesocket(sock[1]); 267 } 268 #endif 269 270 static void test_dgram_inet(void) 271 { 272 QTestState *qts0, *qts1; 273 char *expect; 274 int port[2]; 275 int nb; 276 277 nb = inet_get_free_port_multiple(2, port, false); 278 g_assert_cmpint(nb, ==, 2); 279 280 qts0 = qtest_initf("-nodefaults -M none " 281 "-netdev dgram,id=st0," 282 "local.type=inet,local.host=127.0.0.1,local.port=%d," 283 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", 284 port[0], port[1]); 285 286 expect = g_strdup_printf("st0: index=0,type=dgram," 287 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", 288 port[0], port[1]); 289 EXPECT_STATE(qts0, expect, 0); 290 g_free(expect); 291 292 qts1 = qtest_initf("-nodefaults -M none " 293 "-netdev dgram,id=st0," 294 "local.type=inet,local.host=127.0.0.1,local.port=%d," 295 "remote.type=inet,remote.host=127.0.0.1,remote.port=%d", 296 port[1], port[0]); 297 298 expect = g_strdup_printf("st0: index=0,type=dgram," 299 "udp=127.0.0.1:%d/127.0.0.1:%d\r\n", 300 port[1], port[0]); 301 EXPECT_STATE(qts1, expect, 0); 302 g_free(expect); 303 304 qtest_quit(qts1); 305 qtest_quit(qts0); 306 } 307 308 #ifndef _WIN32 309 static void test_dgram_mcast(void) 310 { 311 QTestState *qts; 312 313 qts = qtest_initf("-nodefaults -M none " 314 "-netdev dgram,id=st0," 315 "remote.type=inet,remote.host=230.0.0.1,remote.port=1234"); 316 317 EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0); 318 319 qtest_quit(qts); 320 } 321 322 static void test_dgram_unix(void) 323 { 324 QTestState *qts0, *qts1; 325 char *expect; 326 gchar *path0, *path1; 327 328 path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL); 329 path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL); 330 331 qts0 = qtest_initf("-nodefaults -M none " 332 "-netdev dgram,id=st0,local.type=unix,local.path=%s," 333 "remote.type=unix,remote.path=%s", 334 path0, path1); 335 336 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", 337 path0, path1); 338 EXPECT_STATE(qts0, expect, 0); 339 g_free(expect); 340 341 qts1 = qtest_initf("-nodefaults -M none " 342 "-netdev dgram,id=st0,local.type=unix,local.path=%s," 343 "remote.type=unix,remote.path=%s", 344 path1, path0); 345 346 347 expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n", 348 path1, path0); 349 EXPECT_STATE(qts1, expect, 0); 350 g_free(expect); 351 352 unlink(path0); 353 g_free(path0); 354 unlink(path1); 355 g_free(path1); 356 357 qtest_quit(qts1); 358 qtest_quit(qts0); 359 } 360 361 static void test_dgram_fd(void) 362 { 363 QTestState *qts0, *qts1; 364 char *expect; 365 int ret; 366 int sv[2]; 367 368 ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv); 369 g_assert_cmpint(ret, !=, -1); 370 371 qts0 = qtest_initf("-nodefaults -M none " 372 "-netdev dgram,id=st0,local.type=fd,local.str=%d", 373 sv[0]); 374 375 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]); 376 EXPECT_STATE(qts0, expect, 0); 377 g_free(expect); 378 379 qts1 = qtest_initf("-nodefaults -M none " 380 "-netdev dgram,id=st0,local.type=fd,local.str=%d", 381 sv[1]); 382 383 384 expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]); 385 EXPECT_STATE(qts1, expect, 0); 386 g_free(expect); 387 388 qtest_quit(qts1); 389 qtest_quit(qts0); 390 391 closesocket(sv[0]); 392 closesocket(sv[1]); 393 } 394 #endif 395 396 int main(int argc, char **argv) 397 { 398 int ret; 399 bool has_ipv4, has_ipv6, has_afunix; 400 g_autoptr(GError) err = NULL; 401 402 socket_init(); 403 g_test_init(&argc, &argv, NULL); 404 405 if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) { 406 g_error("socket_check_protocol_support() failed\n"); 407 } 408 409 tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err); 410 if (tmpdir == NULL) { 411 g_error("Can't create temporary directory in %s: %s", 412 g_get_tmp_dir(), err->message); 413 } 414 415 if (has_ipv4) { 416 qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4); 417 qtest_add_func("/netdev/dgram/inet", test_dgram_inet); 418 #ifndef _WIN32 419 qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast); 420 #endif 421 } 422 if (has_ipv6) { 423 qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6); 424 } 425 426 socket_check_afunix_support(&has_afunix); 427 if (has_afunix) { 428 #ifndef _WIN32 429 qtest_add_func("/netdev/dgram/unix", test_dgram_unix); 430 #endif 431 qtest_add_func("/netdev/stream/unix", test_stream_unix); 432 #ifdef CONFIG_LINUX 433 qtest_add_func("/netdev/stream/unix/abstract", 434 test_stream_unix_abstract); 435 #endif 436 #ifndef _WIN32 437 qtest_add_func("/netdev/stream/fd", test_stream_fd); 438 qtest_add_func("/netdev/dgram/fd", test_dgram_fd); 439 #endif 440 } 441 442 ret = g_test_run(); 443 444 g_rmdir(tmpdir); 445 g_free(tmpdir); 446 447 return ret; 448 } 449