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
 285static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
 286{
 287        int fd;
 288
 289        fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
 290        if (fd < 0) {
 291                perror("connect");
 292                exit(EXIT_FAILURE);
 293        }
 294
 295        /* Send several messages, one with MSG_EOR flag */
 296        for (int i = 0; i < MESSAGES_CNT; i++)
 297                send_byte(fd, 1, 0);
 298
 299        control_writeln("SENDDONE");
 300        close(fd);
 301}
 302
 303static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
 304{
 305        int fd;
 306        char buf[16];
 307        struct msghdr msg = {0};
 308        struct iovec iov = {0};
 309
 310        fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
 311        if (fd < 0) {
 312                perror("accept");
 313                exit(EXIT_FAILURE);
 314        }
 315
 316        control_expectln("SENDDONE");
 317        iov.iov_base = buf;
 318        iov.iov_len = sizeof(buf);
 319        msg.msg_iov = &iov;
 320        msg.msg_iovlen = 1;
 321
 322        for (int i = 0; i < MESSAGES_CNT; i++) {
 323                if (recvmsg(fd, &msg, 0) != 1) {
 324                        perror("message bound violated");
 325                        exit(EXIT_FAILURE);
 326                }
 327        }
 328
 329        close(fd);
 330}
 331
 332#define MESSAGE_TRUNC_SZ 32
 333static void test_seqpacket_msg_trunc_client(const struct test_opts *opts)
 334{
 335        int fd;
 336        char buf[MESSAGE_TRUNC_SZ];
 337
 338        fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
 339        if (fd < 0) {
 340                perror("connect");
 341                exit(EXIT_FAILURE);
 342        }
 343
 344        if (send(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
 345                perror("send failed");
 346                exit(EXIT_FAILURE);
 347        }
 348
 349        control_writeln("SENDDONE");
 350        close(fd);
 351}
 352
 353static void test_seqpacket_msg_trunc_server(const struct test_opts *opts)
 354{
 355        int fd;
 356        char buf[MESSAGE_TRUNC_SZ / 2];
 357        struct msghdr msg = {0};
 358        struct iovec iov = {0};
 359
 360        fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
 361        if (fd < 0) {
 362                perror("accept");
 363                exit(EXIT_FAILURE);
 364        }
 365
 366        control_expectln("SENDDONE");
 367        iov.iov_base = buf;
 368        iov.iov_len = sizeof(buf);
 369        msg.msg_iov = &iov;
 370        msg.msg_iovlen = 1;
 371
 372        ssize_t ret = recvmsg(fd, &msg, MSG_TRUNC);
 373
 374        if (ret != MESSAGE_TRUNC_SZ) {
 375                printf("%zi\n", ret);
 376                perror("MSG_TRUNC doesn't work");
 377                exit(EXIT_FAILURE);
 378        }
 379
 380        if (!(msg.msg_flags & MSG_TRUNC)) {
 381                fprintf(stderr, "MSG_TRUNC expected\n");
 382                exit(EXIT_FAILURE);
 383        }
 384
 385        close(fd);
 386}
 387
 388static struct test_case test_cases[] = {
 389        {
 390                .name = "SOCK_STREAM connection reset",
 391                .run_client = test_stream_connection_reset,
 392        },
 393        {
 394                .name = "SOCK_STREAM bind only",
 395                .run_client = test_stream_bind_only_client,
 396                .run_server = test_stream_bind_only_server,
 397        },
 398        {
 399                .name = "SOCK_STREAM client close",
 400                .run_client = test_stream_client_close_client,
 401                .run_server = test_stream_client_close_server,
 402        },
 403        {
 404                .name = "SOCK_STREAM server close",
 405                .run_client = test_stream_server_close_client,
 406                .run_server = test_stream_server_close_server,
 407        },
 408        {
 409                .name = "SOCK_STREAM multiple connections",
 410                .run_client = test_stream_multiconn_client,
 411                .run_server = test_stream_multiconn_server,
 412        },
 413        {
 414                .name = "SOCK_STREAM MSG_PEEK",
 415                .run_client = test_stream_msg_peek_client,
 416                .run_server = test_stream_msg_peek_server,
 417        },
 418        {
 419                .name = "SOCK_SEQPACKET msg bounds",
 420                .run_client = test_seqpacket_msg_bounds_client,
 421                .run_server = test_seqpacket_msg_bounds_server,
 422        },
 423        {
 424                .name = "SOCK_SEQPACKET MSG_TRUNC flag",
 425                .run_client = test_seqpacket_msg_trunc_client,
 426                .run_server = test_seqpacket_msg_trunc_server,
 427        },
 428        {},
 429};
 430
 431static const char optstring[] = "";
 432static const struct option longopts[] = {
 433        {
 434                .name = "control-host",
 435                .has_arg = required_argument,
 436                .val = 'H',
 437        },
 438        {
 439                .name = "control-port",
 440                .has_arg = required_argument,
 441                .val = 'P',
 442        },
 443        {
 444                .name = "mode",
 445                .has_arg = required_argument,
 446                .val = 'm',
 447        },
 448        {
 449                .name = "peer-cid",
 450                .has_arg = required_argument,
 451                .val = 'p',
 452        },
 453        {
 454                .name = "list",
 455                .has_arg = no_argument,
 456                .val = 'l',
 457        },
 458        {
 459                .name = "skip",
 460                .has_arg = required_argument,
 461                .val = 's',
 462        },
 463        {
 464                .name = "help",
 465                .has_arg = no_argument,
 466                .val = '?',
 467        },
 468        {},
 469};
 470
 471static void usage(void)
 472{
 473        fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
 474                "\n"
 475                "  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
 476                "  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
 477                "\n"
 478                "Run vsock.ko tests.  Must be launched in both guest\n"
 479                "and host.  One side must use --mode=client and\n"
 480                "the other side must use --mode=server.\n"
 481                "\n"
 482                "A TCP control socket connection is used to coordinate tests\n"
 483                "between the client and the server.  The server requires a\n"
 484                "listen address and the client requires an address to\n"
 485                "connect to.\n"
 486                "\n"
 487                "The CID of the other side must be given with --peer-cid=<cid>.\n"
 488                "\n"
 489                "Options:\n"
 490                "  --help                 This help message\n"
 491                "  --control-host <host>  Server IP address to connect to\n"
 492                "  --control-port <port>  Server port to listen on/connect to\n"
 493                "  --mode client|server   Server or client mode\n"
 494                "  --peer-cid <cid>       CID of the other side\n"
 495                "  --list                 List of tests that will be executed\n"
 496                "  --skip <test_id>       Test ID to skip;\n"
 497                "                         use multiple --skip options to skip more tests\n"
 498                );
 499        exit(EXIT_FAILURE);
 500}
 501
 502int main(int argc, char **argv)
 503{
 504        const char *control_host = NULL;
 505        const char *control_port = NULL;
 506        struct test_opts opts = {
 507                .mode = TEST_MODE_UNSET,
 508                .peer_cid = VMADDR_CID_ANY,
 509        };
 510
 511        init_signals();
 512
 513        for (;;) {
 514                int opt = getopt_long(argc, argv, optstring, longopts, NULL);
 515
 516                if (opt == -1)
 517                        break;
 518
 519                switch (opt) {
 520                case 'H':
 521                        control_host = optarg;
 522                        break;
 523                case 'm':
 524                        if (strcmp(optarg, "client") == 0)
 525                                opts.mode = TEST_MODE_CLIENT;
 526                        else if (strcmp(optarg, "server") == 0)
 527                                opts.mode = TEST_MODE_SERVER;
 528                        else {
 529                                fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
 530                                return EXIT_FAILURE;
 531                        }
 532                        break;
 533                case 'p':
 534                        opts.peer_cid = parse_cid(optarg);
 535                        break;
 536                case 'P':
 537                        control_port = optarg;
 538                        break;
 539                case 'l':
 540                        list_tests(test_cases);
 541                        break;
 542                case 's':
 543                        skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
 544                                  optarg);
 545                        break;
 546                case '?':
 547                default:
 548                        usage();
 549                }
 550        }
 551
 552        if (!control_port)
 553                usage();
 554        if (opts.mode == TEST_MODE_UNSET)
 555                usage();
 556        if (opts.peer_cid == VMADDR_CID_ANY)
 557                usage();
 558
 559        if (!control_host) {
 560                if (opts.mode != TEST_MODE_SERVER)
 561                        usage();
 562                control_host = "0.0.0.0";
 563        }
 564
 565        control_init(control_host, control_port,
 566                     opts.mode == TEST_MODE_SERVER);
 567
 568        run_tests(test_cases, &opts);
 569
 570        control_cleanup();
 571        return EXIT_SUCCESS;
 572}
 573