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
test_stream_connection_reset(const struct test_opts * opts)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
test_stream_bind_only_client(const struct test_opts * opts)58 static 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
test_stream_bind_only_server(const struct test_opts * opts)100 static 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
test_stream_client_close_client(const struct test_opts * opts)130 static 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
test_stream_client_close_server(const struct test_opts * opts)144 static 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
test_stream_server_close_client(const struct test_opts * opts)165 static 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
test_stream_server_close_server(const struct test_opts * opts)186 static 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
test_stream_multiconn_client(const struct test_opts * opts)205 static 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
test_stream_multiconn_server(const struct test_opts * opts)229 static 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
test_stream_msg_peek_client(const struct test_opts * opts)253 static 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
test_stream_msg_peek_server(const struct test_opts * opts)267 static 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
282 static 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
315 static const char optstring[] = "";
316 static 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
usage(void)355 static 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
main(int argc,char ** argv)386 int 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