qemu/tests/test-io-channel-socket.c
<<
>>
Prefs
   1/*
   2 * QEMU I/O channel sockets test
   3 *
   4 * Copyright (c) 2015-2016 Red Hat, Inc.
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 *
  19 */
  20
  21#include "qemu/osdep.h"
  22#include "io/channel-socket.h"
  23#include "io/channel-util.h"
  24#include "io-channel-helpers.h"
  25#include "socket-helpers.h"
  26#include "qapi/error.h"
  27
  28
  29static void test_io_channel_set_socket_bufs(QIOChannel *src,
  30                                            QIOChannel *dst)
  31{
  32    int buflen = 64 * 1024;
  33
  34    /*
  35     * Make the socket buffers small so that we see
  36     * the effects of partial reads/writes
  37     */
  38    setsockopt(((QIOChannelSocket *)src)->fd,
  39               SOL_SOCKET, SO_SNDBUF,
  40               (char *)&buflen,
  41               sizeof(buflen));
  42
  43    setsockopt(((QIOChannelSocket *)dst)->fd,
  44               SOL_SOCKET, SO_SNDBUF,
  45               (char *)&buflen,
  46               sizeof(buflen));
  47}
  48
  49
  50static void test_io_channel_setup_sync(SocketAddress *listen_addr,
  51                                       SocketAddress *connect_addr,
  52                                       QIOChannel **srv,
  53                                       QIOChannel **src,
  54                                       QIOChannel **dst)
  55{
  56    QIOChannelSocket *lioc;
  57
  58    lioc = qio_channel_socket_new();
  59    qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
  60
  61    if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) {
  62        SocketAddress *laddr = qio_channel_socket_get_local_address(
  63            lioc, &error_abort);
  64
  65        g_free(connect_addr->u.inet.port);
  66        connect_addr->u.inet.port = g_strdup(laddr->u.inet.port);
  67
  68        qapi_free_SocketAddress(laddr);
  69    }
  70
  71    *src = QIO_CHANNEL(qio_channel_socket_new());
  72    qio_channel_socket_connect_sync(
  73        QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
  74    qio_channel_set_delay(*src, false);
  75
  76    qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
  77    *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
  78    g_assert(*dst);
  79
  80    test_io_channel_set_socket_bufs(*src, *dst);
  81
  82    *srv = QIO_CHANNEL(lioc);
  83}
  84
  85
  86struct TestIOChannelData {
  87    bool err;
  88    GMainLoop *loop;
  89};
  90
  91
  92static void test_io_channel_complete(QIOTask *task,
  93                                     gpointer opaque)
  94{
  95    struct TestIOChannelData *data = opaque;
  96    data->err = qio_task_propagate_error(task, NULL);
  97    g_main_loop_quit(data->loop);
  98}
  99
 100
 101static void test_io_channel_setup_async(SocketAddress *listen_addr,
 102                                        SocketAddress *connect_addr,
 103                                        QIOChannel **srv,
 104                                        QIOChannel **src,
 105                                        QIOChannel **dst)
 106{
 107    QIOChannelSocket *lioc;
 108    struct TestIOChannelData data;
 109
 110    data.loop = g_main_loop_new(g_main_context_default(),
 111                                TRUE);
 112
 113    lioc = qio_channel_socket_new();
 114    qio_channel_socket_listen_async(
 115        lioc, listen_addr,
 116        test_io_channel_complete, &data, NULL, NULL);
 117
 118    g_main_loop_run(data.loop);
 119    g_main_context_iteration(g_main_context_default(), FALSE);
 120
 121    g_assert(!data.err);
 122
 123    if (listen_addr->type == SOCKET_ADDRESS_TYPE_INET) {
 124        SocketAddress *laddr = qio_channel_socket_get_local_address(
 125            lioc, &error_abort);
 126
 127        g_free(connect_addr->u.inet.port);
 128        connect_addr->u.inet.port = g_strdup(laddr->u.inet.port);
 129
 130        qapi_free_SocketAddress(laddr);
 131    }
 132
 133    *src = QIO_CHANNEL(qio_channel_socket_new());
 134
 135    qio_channel_socket_connect_async(
 136        QIO_CHANNEL_SOCKET(*src), connect_addr,
 137        test_io_channel_complete, &data, NULL, NULL);
 138
 139    g_main_loop_run(data.loop);
 140    g_main_context_iteration(g_main_context_default(), FALSE);
 141
 142    g_assert(!data.err);
 143
 144    qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
 145    *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
 146    g_assert(*dst);
 147
 148    qio_channel_set_delay(*src, false);
 149    test_io_channel_set_socket_bufs(*src, *dst);
 150
 151    *srv = QIO_CHANNEL(lioc);
 152
 153    g_main_loop_unref(data.loop);
 154}
 155
 156
 157static void test_io_channel_socket_path_exists(SocketAddress *addr,
 158                                               bool expectExists)
 159{
 160    if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) {
 161        return;
 162    }
 163
 164    g_assert(g_file_test(addr->u.q_unix.path,
 165                         G_FILE_TEST_EXISTS) == expectExists);
 166}
 167
 168
 169static void test_io_channel(bool async,
 170                            SocketAddress *listen_addr,
 171                            SocketAddress *connect_addr,
 172                            bool passFD)
 173{
 174    QIOChannel *src, *dst, *srv;
 175    QIOChannelTest *test;
 176    if (async) {
 177        test_io_channel_setup_async(listen_addr, connect_addr,
 178                                    &srv, &src, &dst);
 179
 180        g_assert(!passFD ||
 181                 qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
 182        g_assert(!passFD ||
 183                 qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
 184        g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
 185        g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 186
 187        test_io_channel_socket_path_exists(listen_addr, true);
 188
 189        test = qio_channel_test_new();
 190        qio_channel_test_run_threads(test, true, src, dst);
 191        qio_channel_test_validate(test);
 192
 193        test_io_channel_socket_path_exists(listen_addr, true);
 194
 195        /* unref without close, to ensure finalize() cleans up */
 196
 197        object_unref(OBJECT(src));
 198        object_unref(OBJECT(dst));
 199        test_io_channel_socket_path_exists(listen_addr, true);
 200
 201        object_unref(OBJECT(srv));
 202        test_io_channel_socket_path_exists(listen_addr, false);
 203
 204        test_io_channel_setup_async(listen_addr, connect_addr,
 205                                    &srv, &src, &dst);
 206
 207        g_assert(!passFD ||
 208                 qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
 209        g_assert(!passFD ||
 210                 qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
 211        g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
 212        g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 213
 214        test = qio_channel_test_new();
 215        qio_channel_test_run_threads(test, false, src, dst);
 216        qio_channel_test_validate(test);
 217
 218        /* close before unref, to ensure finalize copes with already closed */
 219
 220        qio_channel_close(src, &error_abort);
 221        qio_channel_close(dst, &error_abort);
 222        test_io_channel_socket_path_exists(listen_addr, true);
 223
 224        object_unref(OBJECT(src));
 225        object_unref(OBJECT(dst));
 226        test_io_channel_socket_path_exists(listen_addr, true);
 227
 228        qio_channel_close(srv, &error_abort);
 229        test_io_channel_socket_path_exists(listen_addr, false);
 230
 231        object_unref(OBJECT(srv));
 232        test_io_channel_socket_path_exists(listen_addr, false);
 233    } else {
 234        test_io_channel_setup_sync(listen_addr, connect_addr,
 235                                   &srv, &src, &dst);
 236
 237        g_assert(!passFD ||
 238                 qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
 239        g_assert(!passFD ||
 240                 qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
 241        g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
 242        g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 243
 244        test_io_channel_socket_path_exists(listen_addr, true);
 245
 246        test = qio_channel_test_new();
 247        qio_channel_test_run_threads(test, true, src, dst);
 248        qio_channel_test_validate(test);
 249
 250        test_io_channel_socket_path_exists(listen_addr, true);
 251
 252        /* unref without close, to ensure finalize() cleans up */
 253
 254        object_unref(OBJECT(src));
 255        object_unref(OBJECT(dst));
 256        test_io_channel_socket_path_exists(listen_addr, true);
 257
 258        object_unref(OBJECT(srv));
 259        test_io_channel_socket_path_exists(listen_addr, false);
 260
 261        test_io_channel_setup_sync(listen_addr, connect_addr,
 262                                   &srv, &src, &dst);
 263
 264        g_assert(!passFD ||
 265                 qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
 266        g_assert(!passFD ||
 267                 qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
 268        g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN));
 269        g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN));
 270
 271        test = qio_channel_test_new();
 272        qio_channel_test_run_threads(test, false, src, dst);
 273        qio_channel_test_validate(test);
 274
 275        test_io_channel_socket_path_exists(listen_addr, true);
 276
 277        /* close before unref, to ensure finalize copes with already closed */
 278
 279        qio_channel_close(src, &error_abort);
 280        qio_channel_close(dst, &error_abort);
 281        test_io_channel_socket_path_exists(listen_addr, true);
 282
 283        object_unref(OBJECT(src));
 284        object_unref(OBJECT(dst));
 285        test_io_channel_socket_path_exists(listen_addr, true);
 286
 287        qio_channel_close(srv, &error_abort);
 288        test_io_channel_socket_path_exists(listen_addr, false);
 289
 290        object_unref(OBJECT(srv));
 291        test_io_channel_socket_path_exists(listen_addr, false);
 292    }
 293}
 294
 295
 296static void test_io_channel_ipv4(bool async)
 297{
 298    SocketAddress *listen_addr = g_new0(SocketAddress, 1);
 299    SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 300
 301    listen_addr->type = SOCKET_ADDRESS_TYPE_INET;
 302    listen_addr->u.inet = (InetSocketAddress) {
 303        .host = g_strdup("127.0.0.1"),
 304        .port = NULL, /* Auto-select */
 305    };
 306
 307    connect_addr->type = SOCKET_ADDRESS_TYPE_INET;
 308    connect_addr->u.inet = (InetSocketAddress) {
 309        .host = g_strdup("127.0.0.1"),
 310        .port = NULL, /* Filled in later */
 311    };
 312
 313    test_io_channel(async, listen_addr, connect_addr, false);
 314
 315    qapi_free_SocketAddress(listen_addr);
 316    qapi_free_SocketAddress(connect_addr);
 317}
 318
 319
 320static void test_io_channel_ipv4_sync(void)
 321{
 322    return test_io_channel_ipv4(false);
 323}
 324
 325
 326static void test_io_channel_ipv4_async(void)
 327{
 328    return test_io_channel_ipv4(true);
 329}
 330
 331
 332static void test_io_channel_ipv6(bool async)
 333{
 334    SocketAddress *listen_addr = g_new0(SocketAddress, 1);
 335    SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 336
 337    listen_addr->type = SOCKET_ADDRESS_TYPE_INET;
 338    listen_addr->u.inet = (InetSocketAddress) {
 339        .host = g_strdup("::1"),
 340        .port = NULL, /* Auto-select */
 341    };
 342
 343    connect_addr->type = SOCKET_ADDRESS_TYPE_INET;
 344    connect_addr->u.inet = (InetSocketAddress) {
 345        .host = g_strdup("::1"),
 346        .port = NULL, /* Filled in later */
 347    };
 348
 349    test_io_channel(async, listen_addr, connect_addr, false);
 350
 351    qapi_free_SocketAddress(listen_addr);
 352    qapi_free_SocketAddress(connect_addr);
 353}
 354
 355
 356static void test_io_channel_ipv6_sync(void)
 357{
 358    return test_io_channel_ipv6(false);
 359}
 360
 361
 362static void test_io_channel_ipv6_async(void)
 363{
 364    return test_io_channel_ipv6(true);
 365}
 366
 367
 368#ifndef _WIN32
 369static void test_io_channel_unix(bool async)
 370{
 371    SocketAddress *listen_addr = g_new0(SocketAddress, 1);
 372    SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 373
 374#define TEST_SOCKET "test-io-channel-socket.sock"
 375    listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
 376    listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 377
 378    connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
 379    connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 380
 381    test_io_channel(async, listen_addr, connect_addr, true);
 382
 383    qapi_free_SocketAddress(listen_addr);
 384    qapi_free_SocketAddress(connect_addr);
 385}
 386
 387
 388static void test_io_channel_unix_sync(void)
 389{
 390    return test_io_channel_unix(false);
 391}
 392
 393
 394static void test_io_channel_unix_async(void)
 395{
 396    return test_io_channel_unix(true);
 397}
 398
 399static void test_io_channel_unix_fd_pass(void)
 400{
 401    SocketAddress *listen_addr = g_new0(SocketAddress, 1);
 402    SocketAddress *connect_addr = g_new0(SocketAddress, 1);
 403    QIOChannel *src, *dst, *srv;
 404    int testfd;
 405    int fdsend[3];
 406    int *fdrecv = NULL;
 407    size_t nfdrecv = 0;
 408    size_t i;
 409    char bufsend[12], bufrecv[12];
 410    struct iovec iosend[1], iorecv[1];
 411
 412#define TEST_SOCKET "test-io-channel-socket.sock"
 413#define TEST_FILE "test-io-channel-socket.txt"
 414
 415    testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700);
 416    g_assert(testfd != -1);
 417    fdsend[0] = testfd;
 418    fdsend[1] = testfd;
 419    fdsend[2] = testfd;
 420
 421    listen_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
 422    listen_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 423
 424    connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX;
 425    connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET);
 426
 427    test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst);
 428
 429    memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend));
 430
 431    iosend[0].iov_base = bufsend;
 432    iosend[0].iov_len = G_N_ELEMENTS(bufsend);
 433
 434    iorecv[0].iov_base = bufrecv;
 435    iorecv[0].iov_len = G_N_ELEMENTS(bufrecv);
 436
 437    g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
 438    g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
 439
 440    qio_channel_writev_full(src,
 441                            iosend,
 442                            G_N_ELEMENTS(iosend),
 443                            fdsend,
 444                            G_N_ELEMENTS(fdsend),
 445                            &error_abort);
 446
 447    qio_channel_readv_full(dst,
 448                           iorecv,
 449                           G_N_ELEMENTS(iorecv),
 450                           &fdrecv,
 451                           &nfdrecv,
 452                           &error_abort);
 453
 454    g_assert(nfdrecv == G_N_ELEMENTS(fdsend));
 455    /* Each recvd FD should be different from sent FD */
 456    for (i = 0; i < nfdrecv; i++) {
 457        g_assert_cmpint(fdrecv[i], !=, testfd);
 458    }
 459    /* Each recvd FD should be different from each other */
 460    g_assert_cmpint(fdrecv[0], !=, fdrecv[1]);
 461    g_assert_cmpint(fdrecv[0], !=, fdrecv[2]);
 462    g_assert_cmpint(fdrecv[1], !=, fdrecv[2]);
 463
 464    /* Check the I/O buf we sent at the same time matches */
 465    g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
 466
 467    /* Write some data into the FD we received */
 468    g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) ==
 469             G_N_ELEMENTS(bufsend));
 470
 471    /* Read data from the original FD and make sure it matches */
 472    memset(bufrecv, 0, G_N_ELEMENTS(bufrecv));
 473    g_assert(lseek(testfd, 0, SEEK_SET) == 0);
 474    g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) ==
 475             G_N_ELEMENTS(bufrecv));
 476    g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
 477
 478    object_unref(OBJECT(src));
 479    object_unref(OBJECT(dst));
 480    object_unref(OBJECT(srv));
 481    qapi_free_SocketAddress(listen_addr);
 482    qapi_free_SocketAddress(connect_addr);
 483    unlink(TEST_SOCKET);
 484    unlink(TEST_FILE);
 485    close(testfd);
 486    for (i = 0; i < nfdrecv; i++) {
 487        close(fdrecv[i]);
 488    }
 489    g_free(fdrecv);
 490}
 491
 492static void test_io_channel_unix_listen_cleanup(void)
 493{
 494    QIOChannelSocket *ioc;
 495    struct sockaddr_un un;
 496    int sock;
 497
 498#define TEST_SOCKET "test-io-channel-socket.sock"
 499
 500    ioc = qio_channel_socket_new();
 501
 502    /* Manually bind ioc without calling the qio api to avoid setting
 503     * the LISTEN feature */
 504    sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
 505    memset(&un, 0, sizeof(un));
 506    un.sun_family = AF_UNIX;
 507    snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET);
 508    unlink(TEST_SOCKET);
 509    bind(sock, (struct sockaddr *)&un, sizeof(un));
 510    ioc->fd = sock;
 511    ioc->localAddrLen = sizeof(ioc->localAddr);
 512    getsockname(sock, (struct sockaddr *)&ioc->localAddr,
 513                &ioc->localAddrLen);
 514
 515    g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS));
 516    object_unref(OBJECT(ioc));
 517    g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS));
 518
 519    unlink(TEST_SOCKET);
 520}
 521
 522#endif /* _WIN32 */
 523
 524
 525static void test_io_channel_ipv4_fd(void)
 526{
 527    QIOChannel *ioc;
 528    int fd = -1;
 529    struct sockaddr_in sa = {
 530        .sin_family = AF_INET,
 531        .sin_addr = {
 532            .s_addr =  htonl(INADDR_LOOPBACK),
 533        }
 534        /* Leave port unset for auto-assign */
 535    };
 536    socklen_t salen = sizeof(sa);
 537
 538    fd = socket(AF_INET, SOCK_STREAM, 0);
 539    g_assert_cmpint(fd, >, -1);
 540
 541    g_assert_cmpint(bind(fd, (struct sockaddr *)&sa, salen), ==, 0);
 542
 543    ioc = qio_channel_new_fd(fd, &error_abort);
 544
 545    g_assert_cmpstr(object_get_typename(OBJECT(ioc)),
 546                    ==,
 547                    TYPE_QIO_CHANNEL_SOCKET);
 548
 549    object_unref(OBJECT(ioc));
 550}
 551
 552
 553int main(int argc, char **argv)
 554{
 555    bool has_ipv4, has_ipv6;
 556
 557    module_call_init(MODULE_INIT_QOM);
 558    socket_init();
 559
 560    g_test_init(&argc, &argv, NULL);
 561
 562    /* We're creating actual IPv4/6 sockets, so we should
 563     * check if the host running tests actually supports
 564     * each protocol to avoid breaking tests on machines
 565     * with either IPv4 or IPv6 disabled.
 566     */
 567    if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
 568        return 1;
 569    }
 570
 571    if (has_ipv4) {
 572        g_test_add_func("/io/channel/socket/ipv4-sync",
 573                        test_io_channel_ipv4_sync);
 574        g_test_add_func("/io/channel/socket/ipv4-async",
 575                        test_io_channel_ipv4_async);
 576        g_test_add_func("/io/channel/socket/ipv4-fd",
 577                        test_io_channel_ipv4_fd);
 578    }
 579    if (has_ipv6) {
 580        g_test_add_func("/io/channel/socket/ipv6-sync",
 581                        test_io_channel_ipv6_sync);
 582        g_test_add_func("/io/channel/socket/ipv6-async",
 583                        test_io_channel_ipv6_async);
 584    }
 585
 586#ifndef _WIN32
 587    g_test_add_func("/io/channel/socket/unix-sync",
 588                    test_io_channel_unix_sync);
 589    g_test_add_func("/io/channel/socket/unix-async",
 590                    test_io_channel_unix_async);
 591    g_test_add_func("/io/channel/socket/unix-fd-pass",
 592                    test_io_channel_unix_fd_pass);
 593    g_test_add_func("/io/channel/socket/unix-listen-cleanup",
 594                    test_io_channel_unix_listen_cleanup);
 595#endif /* _WIN32 */
 596
 597    return g_test_run();
 598}
 599