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_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
 244static 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
 275static 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
 306static 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
 339static 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    close(sock[0]);
 365    close(sock[1]);
 366}
 367#endif
 368
 369static 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
 408static 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
 421static 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
 460static 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    close(sv[0]);
 491    close(sv[1]);
 492}
 493#endif
 494
 495int 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