qemu/tests/qtest/netdev-socket.c
<<
>>
Prefs
   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)                             \
  22do {                                                      \
  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
  39static gchar *tmpdir;
  40
  41static 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
  62static 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
  83static 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        close(sock[i]);
 103    }
 104
 105    return nb;
 106}
 107
 108static 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
 118static 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
 149static 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
 175static 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
 192static void test_stream_unix_reconnect(void)
 193{
 194    QTestState *qts0, *qts1;
 195    SocketAddress *addr;
 196    gchar *path;
 197
 198    path = g_strconcat(tmpdir, "/stream_unix_reconnect", NULL);
 199    qts0 = qtest_initf("-nodefaults -M none "
 200                       "-netdev stream,id=st0,server=true,addr.type=unix,"
 201                       "addr.path=%s", path);
 202
 203    EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
 204
 205    qts1 = qtest_initf("-nodefaults -M none "
 206                       "-netdev stream,server=false,id=st0,addr.type=unix,"
 207                       "addr.path=%s,reconnect=1", path);
 208
 209    wait_stream_connected(qts0, "st0", &addr);
 210    g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
 211    g_assert_cmpstr(addr->u.q_unix.path, ==, path);
 212    qapi_free_SocketAddress(addr);
 213
 214    /* kill server */
 215    qtest_quit(qts0);
 216
 217    /* check client has been disconnected */
 218    wait_stream_disconnected(qts1, "st0");
 219
 220    /* restart server */
 221    qts0 = qtest_initf("-nodefaults -M none "
 222                       "-netdev stream,id=st0,server=true,addr.type=unix,"
 223                       "addr.path=%s", path);
 224
 225    /* wait connection events*/
 226    wait_stream_connected(qts0, "st0", &addr);
 227    g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
 228    g_assert_cmpstr(addr->u.q_unix.path, ==, path);
 229    qapi_free_SocketAddress(addr);
 230
 231    wait_stream_connected(qts1, "st0", &addr);
 232    g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX);
 233    g_assert_cmpstr(addr->u.q_unix.path, ==, path);
 234    qapi_free_SocketAddress(addr);
 235
 236    qtest_quit(qts1);
 237    qtest_quit(qts0);
 238    g_free(path);
 239}
 240
 241static void test_stream_inet_ipv6(void)
 242{
 243    QTestState *qts0, *qts1;
 244    char *expect;
 245    int port;
 246
 247    port = inet_get_free_port(true);
 248    qts0 = qtest_initf("-nodefaults -M none "
 249                       "-netdev stream,id=st0,server=true,addr.type=inet,"
 250                       "addr.ipv4=off,addr.ipv6=on,"
 251                       "addr.host=::1,addr.port=%d", port);
 252
 253    EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
 254
 255    qts1 = qtest_initf("-nodefaults -M none "
 256                       "-netdev stream,server=false,id=st0,addr.type=inet,"
 257                       "addr.ipv4=off,addr.ipv6=on,"
 258                       "addr.host=::1,addr.port=%d", port);
 259
 260    expect = g_strdup_printf("st0: index=0,type=stream,tcp:::1:%d\r\n",
 261                             port);
 262    EXPECT_STATE(qts1, expect, 0);
 263    g_free(expect);
 264
 265    /* the port is unknown, check only the address */
 266    EXPECT_STATE(qts0, "st0: index=0,type=stream,tcp:::1", ':');
 267
 268    qtest_quit(qts1);
 269    qtest_quit(qts0);
 270}
 271
 272static void test_stream_unix(void)
 273{
 274    QTestState *qts0, *qts1;
 275    char *expect;
 276    gchar *path;
 277
 278    path = g_strconcat(tmpdir, "/stream_unix", NULL);
 279
 280    qts0 = qtest_initf("-nodefaults -M none "
 281                       "-netdev stream,id=st0,server=true,"
 282                       "addr.type=unix,addr.path=%s,",
 283                       path);
 284
 285    EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
 286
 287    qts1 = qtest_initf("-nodefaults -M none "
 288                       "-netdev stream,id=st0,server=false,"
 289                       "addr.type=unix,addr.path=%s",
 290                       path);
 291
 292    expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
 293    EXPECT_STATE(qts1, expect, 0);
 294    EXPECT_STATE(qts0, expect, 0);
 295    g_free(expect);
 296    g_free(path);
 297
 298    qtest_quit(qts1);
 299    qtest_quit(qts0);
 300}
 301
 302#ifdef CONFIG_LINUX
 303static void test_stream_unix_abstract(void)
 304{
 305    QTestState *qts0, *qts1;
 306    char *expect;
 307    gchar *path;
 308
 309    path = g_strconcat(tmpdir, "/stream_unix_abstract", NULL);
 310
 311    qts0 = qtest_initf("-nodefaults -M none "
 312                       "-netdev stream,id=st0,server=true,"
 313                       "addr.type=unix,addr.path=%s,"
 314                       "addr.abstract=on",
 315                       path);
 316
 317    EXPECT_STATE(qts0, "st0: index=0,type=stream,\r\n", 0);
 318
 319    qts1 = qtest_initf("-nodefaults -M none "
 320                       "-netdev stream,id=st0,server=false,"
 321                       "addr.type=unix,addr.path=%s,addr.abstract=on",
 322                       path);
 323
 324    expect = g_strdup_printf("st0: index=0,type=stream,unix:%s\r\n", path);
 325    EXPECT_STATE(qts1, expect, 0);
 326    EXPECT_STATE(qts0, expect, 0);
 327    g_free(expect);
 328    g_free(path);
 329
 330    qtest_quit(qts1);
 331    qtest_quit(qts0);
 332}
 333#endif
 334
 335#ifndef _WIN32
 336static void test_stream_fd(void)
 337{
 338    QTestState *qts0, *qts1;
 339    int sock[2];
 340    int ret;
 341
 342    ret = socketpair(AF_LOCAL, SOCK_STREAM, 0, sock);
 343    g_assert_true(ret == 0);
 344
 345    qts0 = qtest_initf("-nodefaults -M none "
 346                       "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
 347                       sock[0]);
 348
 349    EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
 350
 351    qts1 = qtest_initf("-nodefaults -M none "
 352                       "-netdev stream,id=st0,addr.type=fd,addr.str=%d",
 353                       sock[1]);
 354
 355    EXPECT_STATE(qts1, "st0: index=0,type=stream,unix:\r\n", 0);
 356    EXPECT_STATE(qts0, "st0: index=0,type=stream,unix:\r\n", 0);
 357
 358    qtest_quit(qts1);
 359    qtest_quit(qts0);
 360
 361    close(sock[0]);
 362    close(sock[1]);
 363}
 364#endif
 365
 366static void test_dgram_inet(void)
 367{
 368    QTestState *qts0, *qts1;
 369    char *expect;
 370    int port[2];
 371    int nb;
 372
 373    nb = inet_get_free_port_multiple(2, port, false);
 374    g_assert_cmpint(nb, ==, 2);
 375
 376    qts0 = qtest_initf("-nodefaults -M none "
 377                       "-netdev dgram,id=st0,"
 378                       "local.type=inet,local.host=127.0.0.1,local.port=%d,"
 379                       "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
 380                        port[0], port[1]);
 381
 382    expect = g_strdup_printf("st0: index=0,type=dgram,"
 383                             "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
 384                             port[0], port[1]);
 385    EXPECT_STATE(qts0, expect, 0);
 386    g_free(expect);
 387
 388    qts1 = qtest_initf("-nodefaults -M none "
 389                       "-netdev dgram,id=st0,"
 390                       "local.type=inet,local.host=127.0.0.1,local.port=%d,"
 391                       "remote.type=inet,remote.host=127.0.0.1,remote.port=%d",
 392                        port[1], port[0]);
 393
 394    expect = g_strdup_printf("st0: index=0,type=dgram,"
 395                             "udp=127.0.0.1:%d/127.0.0.1:%d\r\n",
 396                             port[1], port[0]);
 397    EXPECT_STATE(qts1, expect, 0);
 398    g_free(expect);
 399
 400    qtest_quit(qts1);
 401    qtest_quit(qts0);
 402}
 403
 404#ifndef _WIN32
 405static void test_dgram_mcast(void)
 406{
 407    QTestState *qts;
 408
 409    qts = qtest_initf("-nodefaults -M none "
 410                      "-netdev dgram,id=st0,"
 411                      "remote.type=inet,remote.host=230.0.0.1,remote.port=1234");
 412
 413    EXPECT_STATE(qts, "st0: index=0,type=dgram,mcast=230.0.0.1:1234\r\n", 0);
 414
 415    qtest_quit(qts);
 416}
 417
 418static void test_dgram_unix(void)
 419{
 420    QTestState *qts0, *qts1;
 421    char *expect;
 422    gchar *path0, *path1;
 423
 424    path0 = g_strconcat(tmpdir, "/dgram_unix0", NULL);
 425    path1 = g_strconcat(tmpdir, "/dgram_unix1", NULL);
 426
 427    qts0 = qtest_initf("-nodefaults -M none "
 428                       "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
 429                       "remote.type=unix,remote.path=%s",
 430                       path0, path1);
 431
 432    expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
 433                             path0, path1);
 434    EXPECT_STATE(qts0, expect, 0);
 435    g_free(expect);
 436
 437    qts1 = qtest_initf("-nodefaults -M none "
 438                       "-netdev dgram,id=st0,local.type=unix,local.path=%s,"
 439                       "remote.type=unix,remote.path=%s",
 440                       path1, path0);
 441
 442
 443    expect = g_strdup_printf("st0: index=0,type=dgram,udp=%s:%s\r\n",
 444                             path1, path0);
 445    EXPECT_STATE(qts1, expect, 0);
 446    g_free(expect);
 447
 448    unlink(path0);
 449    g_free(path0);
 450    unlink(path1);
 451    g_free(path1);
 452
 453    qtest_quit(qts1);
 454    qtest_quit(qts0);
 455}
 456
 457static void test_dgram_fd(void)
 458{
 459    QTestState *qts0, *qts1;
 460    char *expect;
 461    int ret;
 462    int sv[2];
 463
 464    ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, sv);
 465    g_assert_cmpint(ret, !=, -1);
 466
 467    qts0 = qtest_initf("-nodefaults -M none "
 468                       "-netdev dgram,id=st0,local.type=fd,local.str=%d",
 469                       sv[0]);
 470
 471    expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[0]);
 472    EXPECT_STATE(qts0, expect, 0);
 473    g_free(expect);
 474
 475    qts1 = qtest_initf("-nodefaults -M none "
 476                       "-netdev dgram,id=st0,local.type=fd,local.str=%d",
 477                       sv[1]);
 478
 479
 480    expect = g_strdup_printf("st0: index=0,type=dgram,fd=%d unix\r\n", sv[1]);
 481    EXPECT_STATE(qts1, expect, 0);
 482    g_free(expect);
 483
 484    qtest_quit(qts1);
 485    qtest_quit(qts0);
 486
 487    close(sv[0]);
 488    close(sv[1]);
 489}
 490#endif
 491
 492int main(int argc, char **argv)
 493{
 494    int ret;
 495    bool has_ipv4, has_ipv6, has_afunix;
 496    g_autoptr(GError) err = NULL;
 497
 498    socket_init();
 499    g_test_init(&argc, &argv, NULL);
 500
 501    if (socket_check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
 502        g_error("socket_check_protocol_support() failed\n");
 503    }
 504
 505    tmpdir = g_dir_make_tmp("netdev-socket.XXXXXX", &err);
 506    if (tmpdir == NULL) {
 507        g_error("Can't create temporary directory in %s: %s",
 508                g_get_tmp_dir(), err->message);
 509    }
 510
 511    if (has_ipv4) {
 512        qtest_add_func("/netdev/stream/inet/ipv4", test_stream_inet_ipv4);
 513        qtest_add_func("/netdev/dgram/inet", test_dgram_inet);
 514#ifndef _WIN32
 515        qtest_add_func("/netdev/dgram/mcast", test_dgram_mcast);
 516#endif
 517    }
 518    if (has_ipv6) {
 519        qtest_add_func("/netdev/stream/inet/ipv6", test_stream_inet_ipv6);
 520    }
 521
 522    socket_check_afunix_support(&has_afunix);
 523    if (has_afunix) {
 524#ifndef _WIN32
 525        qtest_add_func("/netdev/dgram/unix", test_dgram_unix);
 526#endif
 527        qtest_add_func("/netdev/stream/unix", test_stream_unix);
 528        qtest_add_func("/netdev/stream/unix/reconnect",
 529                       test_stream_unix_reconnect);
 530#ifdef CONFIG_LINUX
 531        qtest_add_func("/netdev/stream/unix/abstract",
 532                       test_stream_unix_abstract);
 533#endif
 534#ifndef _WIN32
 535        qtest_add_func("/netdev/stream/fd", test_stream_fd);
 536        qtest_add_func("/netdev/dgram/fd", test_dgram_fd);
 537#endif
 538    }
 539
 540    ret = g_test_run();
 541
 542    g_rmdir(tmpdir);
 543    g_free(tmpdir);
 544
 545    return ret;
 546}
 547