linux/tools/testing/vsock/vsock_test.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * vsock_test - vsock.ko test suite
   4 *
   5 * Copyright (C) 2017 Red Hat, Inc.
   6 *
   7 * Author: Stefan Hajnoczi <stefanha@redhat.com>
   8 */
   9
  10#include <getopt.h>
  11#include <stdio.h>
  12#include <stdlib.h>
  13#include <string.h>
  14#include <errno.h>
  15#include <unistd.h>
  16#include <linux/kernel.h>
  17#include <sys/types.h>
  18#include <sys/socket.h>
  19
  20#include "timeout.h"
  21#include "control.h"
  22#include "util.h"
  23
  24static void test_stream_connection_reset(const struct test_opts *opts)
  25{
  26        union {
  27                struct sockaddr sa;
  28                struct sockaddr_vm svm;
  29        } addr = {
  30                .svm = {
  31                        .svm_family = AF_VSOCK,
  32                        .svm_port = 1234,
  33                        .svm_cid = opts->peer_cid,
  34                },
  35        };
  36        int ret;
  37        int fd;
  38
  39        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
  40
  41        timeout_begin(TIMEOUT);
  42        do {
  43                ret = connect(fd, &addr.sa, sizeof(addr.svm));
  44                timeout_check("connect");
  45        } while (ret < 0 && errno == EINTR);
  46        timeout_end();
  47
  48        if (ret != -1) {
  49                fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
  50                exit(EXIT_FAILURE);
  51        }
  52        if (errno != ECONNRESET) {
  53                fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
  54                exit(EXIT_FAILURE);
  55        }
  56
  57        close(fd);
  58}
  59
  60static void test_stream_bind_only_client(const struct test_opts *opts)
  61{
  62        union {
  63                struct sockaddr sa;
  64                struct sockaddr_vm svm;
  65        } addr = {
  66                .svm = {
  67                        .svm_family = AF_VSOCK,
  68                        .svm_port = 1234,
  69                        .svm_cid = opts->peer_cid,
  70                },
  71        };
  72        int ret;
  73        int fd;
  74
  75        /* Wait for the server to be ready */
  76        control_expectln("BIND");
  77
  78        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
  79
  80        timeout_begin(TIMEOUT);
  81        do {
  82                ret = connect(fd, &addr.sa, sizeof(addr.svm));
  83                timeout_check("connect");
  84        } while (ret < 0 && errno == EINTR);
  85        timeout_end();
  86
  87        if (ret != -1) {
  88                fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
  89                exit(EXIT_FAILURE);
  90        }
  91        if (errno != ECONNRESET) {
  92                fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
  93                exit(EXIT_FAILURE);
  94        }
  95
  96        /* Notify the server that the client has finished */
  97        control_writeln("DONE");
  98
  99        close(fd);
 100}
 101
 102static void test_stream_bind_only_server(const struct test_opts *opts)
 103{
 104        union {
 105                struct sockaddr sa;
 106                struct sockaddr_vm svm;
 107        } addr = {
 108                .svm = {
 109                        .svm_family = AF_VSOCK,
 110                        .svm_port = 1234,
 111                        .svm_cid = VMADDR_CID_ANY,
 112                },
 113        };
 114        int fd;
 115
 116        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
 117
 118        if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
 119                perror("bind");
 120                exit(EXIT_FAILURE);
 121        }
 122
 123        /* Notify the client that the server is ready */
 124        control_writeln("BIND");
 125
 126        /* Wait for the client to finish */
 127        control_expectln("DONE");
 128
 129        close(fd);
 130}
 131
 132static void test_stream_client_close_client(const struct test_opts *opts)
 133{
 134        int fd;
 135
 136        fd = vsock_stream_connect(opts->peer_cid, 1234);
 137        if (fd < 0) {
 138                perror("connect");
 139                exit(EXIT_FAILURE);
 140        }
 141
 142        send_byte(fd, 1, 0);
 143        close(fd);
 144}
 145
 146static void test_stream_client_close_server(const struct test_opts *opts)
 147{
 148        int fd;
 149
 150        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 151        if (fd < 0) {
 152                perror("accept");
 153                exit(EXIT_FAILURE);
 154        }
 155
 156        /* Wait for the remote to close the connection, before check
 157         * -EPIPE error on send.
 158         */
 159        vsock_wait_remote_close(fd);
 160
 161        send_byte(fd, -EPIPE, 0);
 162        recv_byte(fd, 1, 0);
 163        recv_byte(fd, 0, 0);
 164        close(fd);
 165}
 166
 167static void test_stream_server_close_client(const struct test_opts *opts)
 168{
 169        int fd;
 170
 171        fd = vsock_stream_connect(opts->peer_cid, 1234);
 172        if (fd < 0) {
 173                perror("connect");
 174                exit(EXIT_FAILURE);
 175        }
 176
 177        /* Wait for the remote to close the connection, before check
 178         * -EPIPE error on send.
 179         */
 180        vsock_wait_remote_close(fd);
 181
 182        send_byte(fd, -EPIPE, 0);
 183        recv_byte(fd, 1, 0);
 184        recv_byte(fd, 0, 0);
 185        close(fd);
 186}
 187
 188static void test_stream_server_close_server(const struct test_opts *opts)
 189{
 190        int fd;
 191
 192        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 193        if (fd < 0) {
 194                perror("accept");
 195                exit(EXIT_FAILURE);
 196        }
 197
 198        send_byte(fd, 1, 0);
 199        close(fd);
 200}
 201
 202/* With the standard socket sizes, VMCI is able to support about 100
 203 * concurrent stream connections.
 204 */
 205#define MULTICONN_NFDS 100
 206
 207static void test_stream_multiconn_client(const struct test_opts *opts)
 208{
 209        int fds[MULTICONN_NFDS];
 210        int i;
 211
 212        for (i = 0; i < MULTICONN_NFDS; i++) {
 213                fds[i] = vsock_stream_connect(opts->peer_cid, 1234);
 214                if (fds[i] < 0) {
 215                        perror("connect");
 216                        exit(EXIT_FAILURE);
 217                }
 218        }
 219
 220        for (i = 0; i < MULTICONN_NFDS; i++) {
 221                if (i % 2)
 222                        recv_byte(fds[i], 1, 0);
 223                else
 224                        send_byte(fds[i], 1, 0);
 225        }
 226
 227        for (i = 0; i < MULTICONN_NFDS; i++)
 228                close(fds[i]);
 229}
 230
 231static void test_stream_multiconn_server(const struct test_opts *opts)
 232{
 233        int fds[MULTICONN_NFDS];
 234        int i;
 235
 236        for (i = 0; i < MULTICONN_NFDS; i++) {
 237                fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 238                if (fds[i] < 0) {
 239                        perror("accept");
 240                        exit(EXIT_FAILURE);
 241                }
 242        }
 243
 244        for (i = 0; i < MULTICONN_NFDS; i++) {
 245                if (i % 2)
 246                        send_byte(fds[i], 1, 0);
 247                else
 248                        recv_byte(fds[i], 1, 0);
 249        }
 250
 251        for (i = 0; i < MULTICONN_NFDS; i++)
 252                close(fds[i]);
 253}
 254
 255static void test_stream_msg_peek_client(const struct test_opts *opts)
 256{
 257        int fd;
 258
 259        fd = vsock_stream_connect(opts->peer_cid, 1234);
 260        if (fd < 0) {
 261                perror("connect");
 262                exit(EXIT_FAILURE);
 263        }
 264
 265        send_byte(fd, 1, 0);
 266        close(fd);
 267}
 268
 269static void test_stream_msg_peek_server(const struct test_opts *opts)
 270{
 271        int fd;
 272
 273        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 274        if (fd < 0) {
 275                perror("accept");
 276                exit(EXIT_FAILURE);
 277        }
 278
 279        recv_byte(fd, 1, MSG_PEEK);
 280        recv_byte(fd, 1, 0);
 281        close(fd);
 282}
 283
 284#define MESSAGES_CNT 7
 285#define MSG_EOR_IDX (MESSAGES_CNT / 2)
 286static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
 287{
 288        int fd;
 289
 290        fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
 291        if (fd < 0) {
 292                perror("connect");
 293                exit(EXIT_FAILURE);
 294        }
 295
 296        /* Send several messages, one with MSG_EOR flag */
 297        for (int i = 0; i < MESSAGES_CNT; i++)
 298                send_byte(fd, 1, (i == MSG_EOR_IDX) ? MSG_EOR : 0);
 299
 300        control_writeln("SENDDONE");
 301        close(fd);
 302}
 303
 304static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
 305{
 306        int fd;
 307        char buf[16];
 308        struct msghdr msg = {0};
 309        struct iovec iov = {0};
 310
 311        fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
 312        if (fd < 0) {
 313                perror("accept");
 314                exit(EXIT_FAILURE);
 315        }
 316
 317        control_expectln("SENDDONE");
 318        iov.iov_base = buf;
 319        iov.iov_len = sizeof(buf);
 320        msg.msg_iov = &iov;
 321        msg.msg_iovlen = 1;
 322
 323        for (int i = 0; i < MESSAGES_CNT; i++) {
 324                if (recvmsg(fd, &msg, 0) != 1) {
 325                        perror("message bound violated");
 326                        exit(EXIT_FAILURE);
 327                }
 328
 329                if ((i == MSG_EOR_IDX) ^ !!(msg.msg_flags & MSG_EOR)) {
 330                        perror("MSG_EOR");
 331                        exit(EXIT_FAILURE);
 332                }
 333        }
 334
 335        close(fd);
 336}
 337
 338#define MESSAGE_TRUNC_SZ 32
 339static void test_seqpacket_msg_trunc_client(const struct test_opts *opts)
 340{
 341        int fd;
 342        char buf[MESSAGE_TRUNC_SZ];
 343
 344        fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
 345        if (fd < 0) {
 346                perror("connect");
 347                exit(EXIT_FAILURE);
 348        }
 349
 350        if (send(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
 351                perror("send failed");
 352                exit(EXIT_FAILURE);
 353        }
 354
 355        control_writeln("SENDDONE");
 356        close(fd);
 357}
 358
 359static void test_seqpacket_msg_trunc_server(const struct test_opts *opts)
 360{
 361        int fd;
 362        char buf[MESSAGE_TRUNC_SZ / 2];
 363        struct msghdr msg = {0};
 364        struct iovec iov = {0};
 365
 366        fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
 367        if (fd < 0) {
 368                perror("accept");
 369                exit(EXIT_FAILURE);
 370        }
 371
 372        control_expectln("SENDDONE");
 373        iov.iov_base = buf;
 374        iov.iov_len = sizeof(buf);
 375        msg.msg_iov = &iov;
 376        msg.msg_iovlen = 1;
 377
 378        ssize_t ret = recvmsg(fd, &msg, MSG_TRUNC);
 379
 380        if (ret != MESSAGE_TRUNC_SZ) {
 381                printf("%zi\n", ret);
 382                perror("MSG_TRUNC doesn't work");
 383                exit(EXIT_FAILURE);
 384        }
 385
 386        if (!(msg.msg_flags & MSG_TRUNC)) {
 387                fprintf(stderr, "MSG_TRUNC expected\n");
 388                exit(EXIT_FAILURE);
 389        }
 390
 391        close(fd);
 392}
 393
 394static struct test_case test_cases[] = {
 395        {
 396                .name = "SOCK_STREAM connection reset",
 397                .run_client = test_stream_connection_reset,
 398        },
 399        {
 400                .name = "SOCK_STREAM bind only",
 401                .run_client = test_stream_bind_only_client,
 402                .run_server = test_stream_bind_only_server,
 403        },
 404        {
 405                .name = "SOCK_STREAM client close",
 406                .run_client = test_stream_client_close_client,
 407                .run_server = test_stream_client_close_server,
 408        },
 409        {
 410                .name = "SOCK_STREAM server close",
 411                .run_client = test_stream_server_close_client,
 412                .run_server = test_stream_server_close_server,
 413        },
 414        {
 415                .name = "SOCK_STREAM multiple connections",
 416                .run_client = test_stream_multiconn_client,
 417                .run_server = test_stream_multiconn_server,
 418        },
 419        {
 420                .name = "SOCK_STREAM MSG_PEEK",
 421                .run_client = test_stream_msg_peek_client,
 422                .run_server = test_stream_msg_peek_server,
 423        },
 424        {
 425                .name = "SOCK_SEQPACKET msg bounds",
 426                .run_client = test_seqpacket_msg_bounds_client,
 427                .run_server = test_seqpacket_msg_bounds_server,
 428        },
 429        {
 430                .name = "SOCK_SEQPACKET MSG_TRUNC flag",
 431                .run_client = test_seqpacket_msg_trunc_client,
 432                .run_server = test_seqpacket_msg_trunc_server,
 433        },
 434        {},
 435};
 436
 437static const char optstring[] = "";
 438static const struct option longopts[] = {
 439        {
 440                .name = "control-host",
 441                .has_arg = required_argument,
 442                .val = 'H',
 443        },
 444        {
 445                .name = "control-port",
 446                .has_arg = required_argument,
 447                .val = 'P',
 448        },
 449        {
 450                .name = "mode",
 451                .has_arg = required_argument,
 452                .val = 'm',
 453        },
 454        {
 455                .name = "peer-cid",
 456                .has_arg = required_argument,
 457                .val = 'p',
 458        },
 459        {
 460                .name = "list",
 461                .has_arg = no_argument,
 462                .val = 'l',
 463        },
 464        {
 465                .name = "skip",
 466                .has_arg = required_argument,
 467                .val = 's',
 468        },
 469        {
 470                .name = "help",
 471                .has_arg = no_argument,
 472                .val = '?',
 473        },
 474        {},
 475};
 476
 477static void usage(void)
 478{
 479        fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
 480                "\n"
 481                "  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
 482                "  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
 483                "\n"
 484                "Run vsock.ko tests.  Must be launched in both guest\n"
 485                "and host.  One side must use --mode=client and\n"
 486                "the other side must use --mode=server.\n"
 487                "\n"
 488                "A TCP control socket connection is used to coordinate tests\n"
 489                "between the client and the server.  The server requires a\n"
 490                "listen address and the client requires an address to\n"
 491                "connect to.\n"
 492                "\n"
 493                "The CID of the other side must be given with --peer-cid=<cid>.\n"
 494                "\n"
 495                "Options:\n"
 496                "  --help                 This help message\n"
 497                "  --control-host <host>  Server IP address to connect to\n"
 498                "  --control-port <port>  Server port to listen on/connect to\n"
 499                "  --mode client|server   Server or client mode\n"
 500                "  --peer-cid <cid>       CID of the other side\n"
 501                "  --list                 List of tests that will be executed\n"
 502                "  --skip <test_id>       Test ID to skip;\n"
 503                "                         use multiple --skip options to skip more tests\n"
 504                );
 505        exit(EXIT_FAILURE);
 506}
 507
 508int main(int argc, char **argv)
 509{
 510        const char *control_host = NULL;
 511        const char *control_port = NULL;
 512        struct test_opts opts = {
 513                .mode = TEST_MODE_UNSET,
 514                .peer_cid = VMADDR_CID_ANY,
 515        };
 516
 517        init_signals();
 518
 519        for (;;) {
 520                int opt = getopt_long(argc, argv, optstring, longopts, NULL);
 521
 522                if (opt == -1)
 523                        break;
 524
 525                switch (opt) {
 526                case 'H':
 527                        control_host = optarg;
 528                        break;
 529                case 'm':
 530                        if (strcmp(optarg, "client") == 0)
 531                                opts.mode = TEST_MODE_CLIENT;
 532                        else if (strcmp(optarg, "server") == 0)
 533                                opts.mode = TEST_MODE_SERVER;
 534                        else {
 535                                fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
 536                                return EXIT_FAILURE;
 537                        }
 538                        break;
 539                case 'p':
 540                        opts.peer_cid = parse_cid(optarg);
 541                        break;
 542                case 'P':
 543                        control_port = optarg;
 544                        break;
 545                case 'l':
 546                        list_tests(test_cases);
 547                        break;
 548                case 's':
 549                        skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
 550                                  optarg);
 551                        break;
 552                case '?':
 553                default:
 554                        usage();
 555                }
 556        }
 557
 558        if (!control_port)
 559                usage();
 560        if (opts.mode == TEST_MODE_UNSET)
 561                usage();
 562        if (opts.peer_cid == VMADDR_CID_ANY)
 563                usage();
 564
 565        if (!control_host) {
 566                if (opts.mode != TEST_MODE_SERVER)
 567                        usage();
 568                control_host = "0.0.0.0";
 569        }
 570
 571        control_init(control_host, control_port,
 572                     opts.mode == TEST_MODE_SERVER);
 573
 574        run_tests(test_cases, &opts);
 575
 576        control_cleanup();
 577        return EXIT_SUCCESS;
 578}
 579