1
2
3
4
5
6
7
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
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
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
124 control_writeln("BIND");
125
126
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
157
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
178
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
203
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
285#define MSG_EOR_IDX (MESSAGES_CNT / 2)
286static 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
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
304static 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
339static 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
359static 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
394static 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
437static const char optstring[] = "";
438static 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
477static 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
508int 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}
579