xref: /openbmc/linux/tools/testing/vsock/vsock_test.c (revision 240e6d25)
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 }
579