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
  18#include "timeout.h"
  19#include "control.h"
  20#include "util.h"
  21
  22static void test_stream_connection_reset(const struct test_opts *opts)
  23{
  24        union {
  25                struct sockaddr sa;
  26                struct sockaddr_vm svm;
  27        } addr = {
  28                .svm = {
  29                        .svm_family = AF_VSOCK,
  30                        .svm_port = 1234,
  31                        .svm_cid = opts->peer_cid,
  32                },
  33        };
  34        int ret;
  35        int fd;
  36
  37        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
  38
  39        timeout_begin(TIMEOUT);
  40        do {
  41                ret = connect(fd, &addr.sa, sizeof(addr.svm));
  42                timeout_check("connect");
  43        } while (ret < 0 && errno == EINTR);
  44        timeout_end();
  45
  46        if (ret != -1) {
  47                fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
  48                exit(EXIT_FAILURE);
  49        }
  50        if (errno != ECONNRESET) {
  51                fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
  52                exit(EXIT_FAILURE);
  53        }
  54
  55        close(fd);
  56}
  57
  58static void test_stream_bind_only_client(const struct test_opts *opts)
  59{
  60        union {
  61                struct sockaddr sa;
  62                struct sockaddr_vm svm;
  63        } addr = {
  64                .svm = {
  65                        .svm_family = AF_VSOCK,
  66                        .svm_port = 1234,
  67                        .svm_cid = opts->peer_cid,
  68                },
  69        };
  70        int ret;
  71        int fd;
  72
  73        /* Wait for the server to be ready */
  74        control_expectln("BIND");
  75
  76        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
  77
  78        timeout_begin(TIMEOUT);
  79        do {
  80                ret = connect(fd, &addr.sa, sizeof(addr.svm));
  81                timeout_check("connect");
  82        } while (ret < 0 && errno == EINTR);
  83        timeout_end();
  84
  85        if (ret != -1) {
  86                fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
  87                exit(EXIT_FAILURE);
  88        }
  89        if (errno != ECONNRESET) {
  90                fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
  91                exit(EXIT_FAILURE);
  92        }
  93
  94        /* Notify the server that the client has finished */
  95        control_writeln("DONE");
  96
  97        close(fd);
  98}
  99
 100static void test_stream_bind_only_server(const struct test_opts *opts)
 101{
 102        union {
 103                struct sockaddr sa;
 104                struct sockaddr_vm svm;
 105        } addr = {
 106                .svm = {
 107                        .svm_family = AF_VSOCK,
 108                        .svm_port = 1234,
 109                        .svm_cid = VMADDR_CID_ANY,
 110                },
 111        };
 112        int fd;
 113
 114        fd = socket(AF_VSOCK, SOCK_STREAM, 0);
 115
 116        if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
 117                perror("bind");
 118                exit(EXIT_FAILURE);
 119        }
 120
 121        /* Notify the client that the server is ready */
 122        control_writeln("BIND");
 123
 124        /* Wait for the client to finish */
 125        control_expectln("DONE");
 126
 127        close(fd);
 128}
 129
 130static void test_stream_client_close_client(const struct test_opts *opts)
 131{
 132        int fd;
 133
 134        fd = vsock_stream_connect(opts->peer_cid, 1234);
 135        if (fd < 0) {
 136                perror("connect");
 137                exit(EXIT_FAILURE);
 138        }
 139
 140        send_byte(fd, 1, 0);
 141        close(fd);
 142}
 143
 144static void test_stream_client_close_server(const struct test_opts *opts)
 145{
 146        int fd;
 147
 148        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 149        if (fd < 0) {
 150                perror("accept");
 151                exit(EXIT_FAILURE);
 152        }
 153
 154        /* Wait for the remote to close the connection, before check
 155         * -EPIPE error on send.
 156         */
 157        vsock_wait_remote_close(fd);
 158
 159        send_byte(fd, -EPIPE, 0);
 160        recv_byte(fd, 1, 0);
 161        recv_byte(fd, 0, 0);
 162        close(fd);
 163}
 164
 165static void test_stream_server_close_client(const struct test_opts *opts)
 166{
 167        int fd;
 168
 169        fd = vsock_stream_connect(opts->peer_cid, 1234);
 170        if (fd < 0) {
 171                perror("connect");
 172                exit(EXIT_FAILURE);
 173        }
 174
 175        /* Wait for the remote to close the connection, before check
 176         * -EPIPE error on send.
 177         */
 178        vsock_wait_remote_close(fd);
 179
 180        send_byte(fd, -EPIPE, 0);
 181        recv_byte(fd, 1, 0);
 182        recv_byte(fd, 0, 0);
 183        close(fd);
 184}
 185
 186static void test_stream_server_close_server(const struct test_opts *opts)
 187{
 188        int fd;
 189
 190        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 191        if (fd < 0) {
 192                perror("accept");
 193                exit(EXIT_FAILURE);
 194        }
 195
 196        send_byte(fd, 1, 0);
 197        close(fd);
 198}
 199
 200/* With the standard socket sizes, VMCI is able to support about 100
 201 * concurrent stream connections.
 202 */
 203#define MULTICONN_NFDS 100
 204
 205static void test_stream_multiconn_client(const struct test_opts *opts)
 206{
 207        int fds[MULTICONN_NFDS];
 208        int i;
 209
 210        for (i = 0; i < MULTICONN_NFDS; i++) {
 211                fds[i] = vsock_stream_connect(opts->peer_cid, 1234);
 212                if (fds[i] < 0) {
 213                        perror("connect");
 214                        exit(EXIT_FAILURE);
 215                }
 216        }
 217
 218        for (i = 0; i < MULTICONN_NFDS; i++) {
 219                if (i % 2)
 220                        recv_byte(fds[i], 1, 0);
 221                else
 222                        send_byte(fds[i], 1, 0);
 223        }
 224
 225        for (i = 0; i < MULTICONN_NFDS; i++)
 226                close(fds[i]);
 227}
 228
 229static void test_stream_multiconn_server(const struct test_opts *opts)
 230{
 231        int fds[MULTICONN_NFDS];
 232        int i;
 233
 234        for (i = 0; i < MULTICONN_NFDS; i++) {
 235                fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 236                if (fds[i] < 0) {
 237                        perror("accept");
 238                        exit(EXIT_FAILURE);
 239                }
 240        }
 241
 242        for (i = 0; i < MULTICONN_NFDS; i++) {
 243                if (i % 2)
 244                        send_byte(fds[i], 1, 0);
 245                else
 246                        recv_byte(fds[i], 1, 0);
 247        }
 248
 249        for (i = 0; i < MULTICONN_NFDS; i++)
 250                close(fds[i]);
 251}
 252
 253static void test_stream_msg_peek_client(const struct test_opts *opts)
 254{
 255        int fd;
 256
 257        fd = vsock_stream_connect(opts->peer_cid, 1234);
 258        if (fd < 0) {
 259                perror("connect");
 260                exit(EXIT_FAILURE);
 261        }
 262
 263        send_byte(fd, 1, 0);
 264        close(fd);
 265}
 266
 267static void test_stream_msg_peek_server(const struct test_opts *opts)
 268{
 269        int fd;
 270
 271        fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
 272        if (fd < 0) {
 273                perror("accept");
 274                exit(EXIT_FAILURE);
 275        }
 276
 277        recv_byte(fd, 1, MSG_PEEK);
 278        recv_byte(fd, 1, 0);
 279        close(fd);
 280}
 281
 282static struct test_case test_cases[] = {
 283        {
 284                .name = "SOCK_STREAM connection reset",
 285                .run_client = test_stream_connection_reset,
 286        },
 287        {
 288                .name = "SOCK_STREAM bind only",
 289                .run_client = test_stream_bind_only_client,
 290                .run_server = test_stream_bind_only_server,
 291        },
 292        {
 293                .name = "SOCK_STREAM client close",
 294                .run_client = test_stream_client_close_client,
 295                .run_server = test_stream_client_close_server,
 296        },
 297        {
 298                .name = "SOCK_STREAM server close",
 299                .run_client = test_stream_server_close_client,
 300                .run_server = test_stream_server_close_server,
 301        },
 302        {
 303                .name = "SOCK_STREAM multiple connections",
 304                .run_client = test_stream_multiconn_client,
 305                .run_server = test_stream_multiconn_server,
 306        },
 307        {
 308                .name = "SOCK_STREAM MSG_PEEK",
 309                .run_client = test_stream_msg_peek_client,
 310                .run_server = test_stream_msg_peek_server,
 311        },
 312        {},
 313};
 314
 315static const char optstring[] = "";
 316static const struct option longopts[] = {
 317        {
 318                .name = "control-host",
 319                .has_arg = required_argument,
 320                .val = 'H',
 321        },
 322        {
 323                .name = "control-port",
 324                .has_arg = required_argument,
 325                .val = 'P',
 326        },
 327        {
 328                .name = "mode",
 329                .has_arg = required_argument,
 330                .val = 'm',
 331        },
 332        {
 333                .name = "peer-cid",
 334                .has_arg = required_argument,
 335                .val = 'p',
 336        },
 337        {
 338                .name = "list",
 339                .has_arg = no_argument,
 340                .val = 'l',
 341        },
 342        {
 343                .name = "skip",
 344                .has_arg = required_argument,
 345                .val = 's',
 346        },
 347        {
 348                .name = "help",
 349                .has_arg = no_argument,
 350                .val = '?',
 351        },
 352        {},
 353};
 354
 355static void usage(void)
 356{
 357        fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
 358                "\n"
 359                "  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
 360                "  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
 361                "\n"
 362                "Run vsock.ko tests.  Must be launched in both guest\n"
 363                "and host.  One side must use --mode=client and\n"
 364                "the other side must use --mode=server.\n"
 365                "\n"
 366                "A TCP control socket connection is used to coordinate tests\n"
 367                "between the client and the server.  The server requires a\n"
 368                "listen address and the client requires an address to\n"
 369                "connect to.\n"
 370                "\n"
 371                "The CID of the other side must be given with --peer-cid=<cid>.\n"
 372                "\n"
 373                "Options:\n"
 374                "  --help                 This help message\n"
 375                "  --control-host <host>  Server IP address to connect to\n"
 376                "  --control-port <port>  Server port to listen on/connect to\n"
 377                "  --mode client|server   Server or client mode\n"
 378                "  --peer-cid <cid>       CID of the other side\n"
 379                "  --list                 List of tests that will be executed\n"
 380                "  --skip <test_id>       Test ID to skip;\n"
 381                "                         use multiple --skip options to skip more tests\n"
 382                );
 383        exit(EXIT_FAILURE);
 384}
 385
 386int main(int argc, char **argv)
 387{
 388        const char *control_host = NULL;
 389        const char *control_port = NULL;
 390        struct test_opts opts = {
 391                .mode = TEST_MODE_UNSET,
 392                .peer_cid = VMADDR_CID_ANY,
 393        };
 394
 395        init_signals();
 396
 397        for (;;) {
 398                int opt = getopt_long(argc, argv, optstring, longopts, NULL);
 399
 400                if (opt == -1)
 401                        break;
 402
 403                switch (opt) {
 404                case 'H':
 405                        control_host = optarg;
 406                        break;
 407                case 'm':
 408                        if (strcmp(optarg, "client") == 0)
 409                                opts.mode = TEST_MODE_CLIENT;
 410                        else if (strcmp(optarg, "server") == 0)
 411                                opts.mode = TEST_MODE_SERVER;
 412                        else {
 413                                fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
 414                                return EXIT_FAILURE;
 415                        }
 416                        break;
 417                case 'p':
 418                        opts.peer_cid = parse_cid(optarg);
 419                        break;
 420                case 'P':
 421                        control_port = optarg;
 422                        break;
 423                case 'l':
 424                        list_tests(test_cases);
 425                        break;
 426                case 's':
 427                        skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
 428                                  optarg);
 429                        break;
 430                case '?':
 431                default:
 432                        usage();
 433                }
 434        }
 435
 436        if (!control_port)
 437                usage();
 438        if (opts.mode == TEST_MODE_UNSET)
 439                usage();
 440        if (opts.peer_cid == VMADDR_CID_ANY)
 441                usage();
 442
 443        if (!control_host) {
 444                if (opts.mode != TEST_MODE_SERVER)
 445                        usage();
 446                control_host = "0.0.0.0";
 447        }
 448
 449        control_init(control_host, control_port,
 450                     opts.mode == TEST_MODE_SERVER);
 451
 452        run_tests(test_cases, &opts);
 453
 454        control_cleanup();
 455        return EXIT_SUCCESS;
 456}
 457