Merge tag 'perf-tools-for-v5.15-2021-09-11' of git://git.kernel.org/pub/scm/linux...
[linux-2.6-microblaze.git] / tools / testing / vsock / vsock_test.c
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
24 static 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
60 static 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
102 static 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
132 static 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
146 static 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
167 static 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
188 static 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
207 static 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
231 static 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
255 static 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
269 static 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)
286 static 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
304 static 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
339 static 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
359 static 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
394 static 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
437 static const char optstring[] = "";
438 static 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
477 static 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
508 int 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 }