xref: /openbmc/linux/tools/testing/vsock/vsock_test.c (revision 87875c10)
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 static void test_seqpacket_msg_bounds_client(const struct test_opts *opts)
286 {
287 	int fd;
288 
289 	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
290 	if (fd < 0) {
291 		perror("connect");
292 		exit(EXIT_FAILURE);
293 	}
294 
295 	/* Send several messages, one with MSG_EOR flag */
296 	for (int i = 0; i < MESSAGES_CNT; i++)
297 		send_byte(fd, 1, 0);
298 
299 	control_writeln("SENDDONE");
300 	close(fd);
301 }
302 
303 static void test_seqpacket_msg_bounds_server(const struct test_opts *opts)
304 {
305 	int fd;
306 	char buf[16];
307 	struct msghdr msg = {0};
308 	struct iovec iov = {0};
309 
310 	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
311 	if (fd < 0) {
312 		perror("accept");
313 		exit(EXIT_FAILURE);
314 	}
315 
316 	control_expectln("SENDDONE");
317 	iov.iov_base = buf;
318 	iov.iov_len = sizeof(buf);
319 	msg.msg_iov = &iov;
320 	msg.msg_iovlen = 1;
321 
322 	for (int i = 0; i < MESSAGES_CNT; i++) {
323 		if (recvmsg(fd, &msg, 0) != 1) {
324 			perror("message bound violated");
325 			exit(EXIT_FAILURE);
326 		}
327 	}
328 
329 	close(fd);
330 }
331 
332 #define MESSAGE_TRUNC_SZ 32
333 static void test_seqpacket_msg_trunc_client(const struct test_opts *opts)
334 {
335 	int fd;
336 	char buf[MESSAGE_TRUNC_SZ];
337 
338 	fd = vsock_seqpacket_connect(opts->peer_cid, 1234);
339 	if (fd < 0) {
340 		perror("connect");
341 		exit(EXIT_FAILURE);
342 	}
343 
344 	if (send(fd, buf, sizeof(buf), 0) != sizeof(buf)) {
345 		perror("send failed");
346 		exit(EXIT_FAILURE);
347 	}
348 
349 	control_writeln("SENDDONE");
350 	close(fd);
351 }
352 
353 static void test_seqpacket_msg_trunc_server(const struct test_opts *opts)
354 {
355 	int fd;
356 	char buf[MESSAGE_TRUNC_SZ / 2];
357 	struct msghdr msg = {0};
358 	struct iovec iov = {0};
359 
360 	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, 1234, NULL);
361 	if (fd < 0) {
362 		perror("accept");
363 		exit(EXIT_FAILURE);
364 	}
365 
366 	control_expectln("SENDDONE");
367 	iov.iov_base = buf;
368 	iov.iov_len = sizeof(buf);
369 	msg.msg_iov = &iov;
370 	msg.msg_iovlen = 1;
371 
372 	ssize_t ret = recvmsg(fd, &msg, MSG_TRUNC);
373 
374 	if (ret != MESSAGE_TRUNC_SZ) {
375 		printf("%zi\n", ret);
376 		perror("MSG_TRUNC doesn't work");
377 		exit(EXIT_FAILURE);
378 	}
379 
380 	if (!(msg.msg_flags & MSG_TRUNC)) {
381 		fprintf(stderr, "MSG_TRUNC expected\n");
382 		exit(EXIT_FAILURE);
383 	}
384 
385 	close(fd);
386 }
387 
388 static struct test_case test_cases[] = {
389 	{
390 		.name = "SOCK_STREAM connection reset",
391 		.run_client = test_stream_connection_reset,
392 	},
393 	{
394 		.name = "SOCK_STREAM bind only",
395 		.run_client = test_stream_bind_only_client,
396 		.run_server = test_stream_bind_only_server,
397 	},
398 	{
399 		.name = "SOCK_STREAM client close",
400 		.run_client = test_stream_client_close_client,
401 		.run_server = test_stream_client_close_server,
402 	},
403 	{
404 		.name = "SOCK_STREAM server close",
405 		.run_client = test_stream_server_close_client,
406 		.run_server = test_stream_server_close_server,
407 	},
408 	{
409 		.name = "SOCK_STREAM multiple connections",
410 		.run_client = test_stream_multiconn_client,
411 		.run_server = test_stream_multiconn_server,
412 	},
413 	{
414 		.name = "SOCK_STREAM MSG_PEEK",
415 		.run_client = test_stream_msg_peek_client,
416 		.run_server = test_stream_msg_peek_server,
417 	},
418 	{
419 		.name = "SOCK_SEQPACKET msg bounds",
420 		.run_client = test_seqpacket_msg_bounds_client,
421 		.run_server = test_seqpacket_msg_bounds_server,
422 	},
423 	{
424 		.name = "SOCK_SEQPACKET MSG_TRUNC flag",
425 		.run_client = test_seqpacket_msg_trunc_client,
426 		.run_server = test_seqpacket_msg_trunc_server,
427 	},
428 	{},
429 };
430 
431 static const char optstring[] = "";
432 static const struct option longopts[] = {
433 	{
434 		.name = "control-host",
435 		.has_arg = required_argument,
436 		.val = 'H',
437 	},
438 	{
439 		.name = "control-port",
440 		.has_arg = required_argument,
441 		.val = 'P',
442 	},
443 	{
444 		.name = "mode",
445 		.has_arg = required_argument,
446 		.val = 'm',
447 	},
448 	{
449 		.name = "peer-cid",
450 		.has_arg = required_argument,
451 		.val = 'p',
452 	},
453 	{
454 		.name = "list",
455 		.has_arg = no_argument,
456 		.val = 'l',
457 	},
458 	{
459 		.name = "skip",
460 		.has_arg = required_argument,
461 		.val = 's',
462 	},
463 	{
464 		.name = "help",
465 		.has_arg = no_argument,
466 		.val = '?',
467 	},
468 	{},
469 };
470 
471 static void usage(void)
472 {
473 	fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
474 		"\n"
475 		"  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
476 		"  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
477 		"\n"
478 		"Run vsock.ko tests.  Must be launched in both guest\n"
479 		"and host.  One side must use --mode=client and\n"
480 		"the other side must use --mode=server.\n"
481 		"\n"
482 		"A TCP control socket connection is used to coordinate tests\n"
483 		"between the client and the server.  The server requires a\n"
484 		"listen address and the client requires an address to\n"
485 		"connect to.\n"
486 		"\n"
487 		"The CID of the other side must be given with --peer-cid=<cid>.\n"
488 		"\n"
489 		"Options:\n"
490 		"  --help                 This help message\n"
491 		"  --control-host <host>  Server IP address to connect to\n"
492 		"  --control-port <port>  Server port to listen on/connect to\n"
493 		"  --mode client|server   Server or client mode\n"
494 		"  --peer-cid <cid>       CID of the other side\n"
495 		"  --list                 List of tests that will be executed\n"
496 		"  --skip <test_id>       Test ID to skip;\n"
497 		"                         use multiple --skip options to skip more tests\n"
498 		);
499 	exit(EXIT_FAILURE);
500 }
501 
502 int main(int argc, char **argv)
503 {
504 	const char *control_host = NULL;
505 	const char *control_port = NULL;
506 	struct test_opts opts = {
507 		.mode = TEST_MODE_UNSET,
508 		.peer_cid = VMADDR_CID_ANY,
509 	};
510 
511 	init_signals();
512 
513 	for (;;) {
514 		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
515 
516 		if (opt == -1)
517 			break;
518 
519 		switch (opt) {
520 		case 'H':
521 			control_host = optarg;
522 			break;
523 		case 'm':
524 			if (strcmp(optarg, "client") == 0)
525 				opts.mode = TEST_MODE_CLIENT;
526 			else if (strcmp(optarg, "server") == 0)
527 				opts.mode = TEST_MODE_SERVER;
528 			else {
529 				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
530 				return EXIT_FAILURE;
531 			}
532 			break;
533 		case 'p':
534 			opts.peer_cid = parse_cid(optarg);
535 			break;
536 		case 'P':
537 			control_port = optarg;
538 			break;
539 		case 'l':
540 			list_tests(test_cases);
541 			break;
542 		case 's':
543 			skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
544 				  optarg);
545 			break;
546 		case '?':
547 		default:
548 			usage();
549 		}
550 	}
551 
552 	if (!control_port)
553 		usage();
554 	if (opts.mode == TEST_MODE_UNSET)
555 		usage();
556 	if (opts.peer_cid == VMADDR_CID_ANY)
557 		usage();
558 
559 	if (!control_host) {
560 		if (opts.mode != TEST_MODE_SERVER)
561 			usage();
562 		control_host = "0.0.0.0";
563 	}
564 
565 	control_init(control_host, control_port,
566 		     opts.mode == TEST_MODE_SERVER);
567 
568 	run_tests(test_cases, &opts);
569 
570 	control_cleanup();
571 	return EXIT_SUCCESS;
572 }
573