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 22 static 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 58 static void test_stream_client_close_client(const struct test_opts *opts) 59 { 60 int fd; 61 62 fd = vsock_stream_connect(opts->peer_cid, 1234); 63 if (fd < 0) { 64 perror("connect"); 65 exit(EXIT_FAILURE); 66 } 67 68 send_byte(fd, 1, 0); 69 close(fd); 70 } 71 72 static void test_stream_client_close_server(const struct test_opts *opts) 73 { 74 int fd; 75 76 fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); 77 if (fd < 0) { 78 perror("accept"); 79 exit(EXIT_FAILURE); 80 } 81 82 /* Wait for the remote to close the connection, before check 83 * -EPIPE error on send. 84 */ 85 vsock_wait_remote_close(fd); 86 87 send_byte(fd, -EPIPE, 0); 88 recv_byte(fd, 1, 0); 89 recv_byte(fd, 0, 0); 90 close(fd); 91 } 92 93 static void test_stream_server_close_client(const struct test_opts *opts) 94 { 95 int fd; 96 97 fd = vsock_stream_connect(opts->peer_cid, 1234); 98 if (fd < 0) { 99 perror("connect"); 100 exit(EXIT_FAILURE); 101 } 102 103 /* Wait for the remote to close the connection, before check 104 * -EPIPE error on send. 105 */ 106 vsock_wait_remote_close(fd); 107 108 send_byte(fd, -EPIPE, 0); 109 recv_byte(fd, 1, 0); 110 recv_byte(fd, 0, 0); 111 close(fd); 112 } 113 114 static void test_stream_server_close_server(const struct test_opts *opts) 115 { 116 int fd; 117 118 fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); 119 if (fd < 0) { 120 perror("accept"); 121 exit(EXIT_FAILURE); 122 } 123 124 send_byte(fd, 1, 0); 125 close(fd); 126 } 127 128 /* With the standard socket sizes, VMCI is able to support about 100 129 * concurrent stream connections. 130 */ 131 #define MULTICONN_NFDS 100 132 133 static void test_stream_multiconn_client(const struct test_opts *opts) 134 { 135 int fds[MULTICONN_NFDS]; 136 int i; 137 138 for (i = 0; i < MULTICONN_NFDS; i++) { 139 fds[i] = vsock_stream_connect(opts->peer_cid, 1234); 140 if (fds[i] < 0) { 141 perror("connect"); 142 exit(EXIT_FAILURE); 143 } 144 } 145 146 for (i = 0; i < MULTICONN_NFDS; i++) { 147 if (i % 2) 148 recv_byte(fds[i], 1, 0); 149 else 150 send_byte(fds[i], 1, 0); 151 } 152 153 for (i = 0; i < MULTICONN_NFDS; i++) 154 close(fds[i]); 155 } 156 157 static void test_stream_multiconn_server(const struct test_opts *opts) 158 { 159 int fds[MULTICONN_NFDS]; 160 int i; 161 162 for (i = 0; i < MULTICONN_NFDS; i++) { 163 fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); 164 if (fds[i] < 0) { 165 perror("accept"); 166 exit(EXIT_FAILURE); 167 } 168 } 169 170 for (i = 0; i < MULTICONN_NFDS; i++) { 171 if (i % 2) 172 send_byte(fds[i], 1, 0); 173 else 174 recv_byte(fds[i], 1, 0); 175 } 176 177 for (i = 0; i < MULTICONN_NFDS; i++) 178 close(fds[i]); 179 } 180 181 static void test_stream_msg_peek_client(const struct test_opts *opts) 182 { 183 int fd; 184 185 fd = vsock_stream_connect(opts->peer_cid, 1234); 186 if (fd < 0) { 187 perror("connect"); 188 exit(EXIT_FAILURE); 189 } 190 191 send_byte(fd, 1, 0); 192 close(fd); 193 } 194 195 static void test_stream_msg_peek_server(const struct test_opts *opts) 196 { 197 int fd; 198 199 fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL); 200 if (fd < 0) { 201 perror("accept"); 202 exit(EXIT_FAILURE); 203 } 204 205 recv_byte(fd, 1, MSG_PEEK); 206 recv_byte(fd, 1, 0); 207 close(fd); 208 } 209 210 static struct test_case test_cases[] = { 211 { 212 .name = "SOCK_STREAM connection reset", 213 .run_client = test_stream_connection_reset, 214 }, 215 { 216 .name = "SOCK_STREAM client close", 217 .run_client = test_stream_client_close_client, 218 .run_server = test_stream_client_close_server, 219 }, 220 { 221 .name = "SOCK_STREAM server close", 222 .run_client = test_stream_server_close_client, 223 .run_server = test_stream_server_close_server, 224 }, 225 { 226 .name = "SOCK_STREAM multiple connections", 227 .run_client = test_stream_multiconn_client, 228 .run_server = test_stream_multiconn_server, 229 }, 230 { 231 .name = "SOCK_STREAM MSG_PEEK", 232 .run_client = test_stream_msg_peek_client, 233 .run_server = test_stream_msg_peek_server, 234 }, 235 {}, 236 }; 237 238 static const char optstring[] = ""; 239 static const struct option longopts[] = { 240 { 241 .name = "control-host", 242 .has_arg = required_argument, 243 .val = 'H', 244 }, 245 { 246 .name = "control-port", 247 .has_arg = required_argument, 248 .val = 'P', 249 }, 250 { 251 .name = "mode", 252 .has_arg = required_argument, 253 .val = 'm', 254 }, 255 { 256 .name = "peer-cid", 257 .has_arg = required_argument, 258 .val = 'p', 259 }, 260 { 261 .name = "list", 262 .has_arg = no_argument, 263 .val = 'l', 264 }, 265 { 266 .name = "skip", 267 .has_arg = required_argument, 268 .val = 's', 269 }, 270 { 271 .name = "help", 272 .has_arg = no_argument, 273 .val = '?', 274 }, 275 {}, 276 }; 277 278 static void usage(void) 279 { 280 fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n" 281 "\n" 282 " Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n" 283 " Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n" 284 "\n" 285 "Run vsock.ko tests. Must be launched in both guest\n" 286 "and host. One side must use --mode=client and\n" 287 "the other side must use --mode=server.\n" 288 "\n" 289 "A TCP control socket connection is used to coordinate tests\n" 290 "between the client and the server. The server requires a\n" 291 "listen address and the client requires an address to\n" 292 "connect to.\n" 293 "\n" 294 "The CID of the other side must be given with --peer-cid=<cid>.\n" 295 "\n" 296 "Options:\n" 297 " --help This help message\n" 298 " --control-host <host> Server IP address to connect to\n" 299 " --control-port <port> Server port to listen on/connect to\n" 300 " --mode client|server Server or client mode\n" 301 " --peer-cid <cid> CID of the other side\n" 302 " --list List of tests that will be executed\n" 303 " --skip <test_id> Test ID to skip;\n" 304 " use multiple --skip options to skip more tests\n" 305 ); 306 exit(EXIT_FAILURE); 307 } 308 309 int main(int argc, char **argv) 310 { 311 const char *control_host = NULL; 312 const char *control_port = NULL; 313 struct test_opts opts = { 314 .mode = TEST_MODE_UNSET, 315 .peer_cid = VMADDR_CID_ANY, 316 }; 317 318 init_signals(); 319 320 for (;;) { 321 int opt = getopt_long(argc, argv, optstring, longopts, NULL); 322 323 if (opt == -1) 324 break; 325 326 switch (opt) { 327 case 'H': 328 control_host = optarg; 329 break; 330 case 'm': 331 if (strcmp(optarg, "client") == 0) 332 opts.mode = TEST_MODE_CLIENT; 333 else if (strcmp(optarg, "server") == 0) 334 opts.mode = TEST_MODE_SERVER; 335 else { 336 fprintf(stderr, "--mode must be \"client\" or \"server\"\n"); 337 return EXIT_FAILURE; 338 } 339 break; 340 case 'p': 341 opts.peer_cid = parse_cid(optarg); 342 break; 343 case 'P': 344 control_port = optarg; 345 break; 346 case 'l': 347 list_tests(test_cases); 348 break; 349 case 's': 350 skip_test(test_cases, ARRAY_SIZE(test_cases) - 1, 351 optarg); 352 break; 353 case '?': 354 default: 355 usage(); 356 } 357 } 358 359 if (!control_port) 360 usage(); 361 if (opts.mode == TEST_MODE_UNSET) 362 usage(); 363 if (opts.peer_cid == VMADDR_CID_ANY) 364 usage(); 365 366 if (!control_host) { 367 if (opts.mode != TEST_MODE_SERVER) 368 usage(); 369 control_host = "0.0.0.0"; 370 } 371 372 control_init(control_host, control_port, 373 opts.mode == TEST_MODE_SERVER); 374 375 run_tests(test_cases, &opts); 376 377 control_cleanup(); 378 return EXIT_SUCCESS; 379 } 380