xref: /openbmc/linux/tools/testing/vsock/vsock_test.c (revision cdbcc18d)
1cdbcc18dSStefan Hajnoczi // SPDX-License-Identifier: GPL-2.0-only
2cdbcc18dSStefan Hajnoczi /*
3cdbcc18dSStefan Hajnoczi  * vsock_test - vsock.ko test suite
4cdbcc18dSStefan Hajnoczi  *
5cdbcc18dSStefan Hajnoczi  * Copyright (C) 2017 Red Hat, Inc.
6cdbcc18dSStefan Hajnoczi  *
7cdbcc18dSStefan Hajnoczi  * Author: Stefan Hajnoczi <stefanha@redhat.com>
8cdbcc18dSStefan Hajnoczi  */
9cdbcc18dSStefan Hajnoczi 
10cdbcc18dSStefan Hajnoczi #include <getopt.h>
11cdbcc18dSStefan Hajnoczi #include <stdio.h>
12cdbcc18dSStefan Hajnoczi #include <stdlib.h>
13cdbcc18dSStefan Hajnoczi #include <string.h>
14cdbcc18dSStefan Hajnoczi #include <errno.h>
15cdbcc18dSStefan Hajnoczi #include <unistd.h>
16cdbcc18dSStefan Hajnoczi 
17cdbcc18dSStefan Hajnoczi #include "timeout.h"
18cdbcc18dSStefan Hajnoczi #include "control.h"
19cdbcc18dSStefan Hajnoczi #include "util.h"
20cdbcc18dSStefan Hajnoczi 
21cdbcc18dSStefan Hajnoczi static void test_stream_connection_reset(const struct test_opts *opts)
22cdbcc18dSStefan Hajnoczi {
23cdbcc18dSStefan Hajnoczi 	union {
24cdbcc18dSStefan Hajnoczi 		struct sockaddr sa;
25cdbcc18dSStefan Hajnoczi 		struct sockaddr_vm svm;
26cdbcc18dSStefan Hajnoczi 	} addr = {
27cdbcc18dSStefan Hajnoczi 		.svm = {
28cdbcc18dSStefan Hajnoczi 			.svm_family = AF_VSOCK,
29cdbcc18dSStefan Hajnoczi 			.svm_port = 1234,
30cdbcc18dSStefan Hajnoczi 			.svm_cid = opts->peer_cid,
31cdbcc18dSStefan Hajnoczi 		},
32cdbcc18dSStefan Hajnoczi 	};
33cdbcc18dSStefan Hajnoczi 	int ret;
34cdbcc18dSStefan Hajnoczi 	int fd;
35cdbcc18dSStefan Hajnoczi 
36cdbcc18dSStefan Hajnoczi 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
37cdbcc18dSStefan Hajnoczi 
38cdbcc18dSStefan Hajnoczi 	timeout_begin(TIMEOUT);
39cdbcc18dSStefan Hajnoczi 	do {
40cdbcc18dSStefan Hajnoczi 		ret = connect(fd, &addr.sa, sizeof(addr.svm));
41cdbcc18dSStefan Hajnoczi 		timeout_check("connect");
42cdbcc18dSStefan Hajnoczi 	} while (ret < 0 && errno == EINTR);
43cdbcc18dSStefan Hajnoczi 	timeout_end();
44cdbcc18dSStefan Hajnoczi 
45cdbcc18dSStefan Hajnoczi 	if (ret != -1) {
46cdbcc18dSStefan Hajnoczi 		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
47cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
48cdbcc18dSStefan Hajnoczi 	}
49cdbcc18dSStefan Hajnoczi 	if (errno != ECONNRESET) {
50cdbcc18dSStefan Hajnoczi 		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
51cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
52cdbcc18dSStefan Hajnoczi 	}
53cdbcc18dSStefan Hajnoczi 
54cdbcc18dSStefan Hajnoczi 	close(fd);
55cdbcc18dSStefan Hajnoczi }
56cdbcc18dSStefan Hajnoczi 
57cdbcc18dSStefan Hajnoczi static void test_stream_client_close_client(const struct test_opts *opts)
58cdbcc18dSStefan Hajnoczi {
59cdbcc18dSStefan Hajnoczi 	int fd;
60cdbcc18dSStefan Hajnoczi 
61cdbcc18dSStefan Hajnoczi 	fd = vsock_stream_connect(opts->peer_cid, 1234);
62cdbcc18dSStefan Hajnoczi 	if (fd < 0) {
63cdbcc18dSStefan Hajnoczi 		perror("connect");
64cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
65cdbcc18dSStefan Hajnoczi 	}
66cdbcc18dSStefan Hajnoczi 
67cdbcc18dSStefan Hajnoczi 	send_byte(fd, 1, 0);
68cdbcc18dSStefan Hajnoczi 	close(fd);
69cdbcc18dSStefan Hajnoczi 	control_writeln("CLOSED");
70cdbcc18dSStefan Hajnoczi }
71cdbcc18dSStefan Hajnoczi 
72cdbcc18dSStefan Hajnoczi static void test_stream_client_close_server(const struct test_opts *opts)
73cdbcc18dSStefan Hajnoczi {
74cdbcc18dSStefan Hajnoczi 	int fd;
75cdbcc18dSStefan Hajnoczi 
76cdbcc18dSStefan Hajnoczi 	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
77cdbcc18dSStefan Hajnoczi 	if (fd < 0) {
78cdbcc18dSStefan Hajnoczi 		perror("accept");
79cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
80cdbcc18dSStefan Hajnoczi 	}
81cdbcc18dSStefan Hajnoczi 
82cdbcc18dSStefan Hajnoczi 	control_expectln("CLOSED");
83cdbcc18dSStefan Hajnoczi 
84cdbcc18dSStefan Hajnoczi 	send_byte(fd, -EPIPE, 0);
85cdbcc18dSStefan Hajnoczi 	recv_byte(fd, 1, 0);
86cdbcc18dSStefan Hajnoczi 	recv_byte(fd, 0, 0);
87cdbcc18dSStefan Hajnoczi 	close(fd);
88cdbcc18dSStefan Hajnoczi }
89cdbcc18dSStefan Hajnoczi 
90cdbcc18dSStefan Hajnoczi static void test_stream_server_close_client(const struct test_opts *opts)
91cdbcc18dSStefan Hajnoczi {
92cdbcc18dSStefan Hajnoczi 	int fd;
93cdbcc18dSStefan Hajnoczi 
94cdbcc18dSStefan Hajnoczi 	fd = vsock_stream_connect(opts->peer_cid, 1234);
95cdbcc18dSStefan Hajnoczi 	if (fd < 0) {
96cdbcc18dSStefan Hajnoczi 		perror("connect");
97cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
98cdbcc18dSStefan Hajnoczi 	}
99cdbcc18dSStefan Hajnoczi 
100cdbcc18dSStefan Hajnoczi 	control_expectln("CLOSED");
101cdbcc18dSStefan Hajnoczi 
102cdbcc18dSStefan Hajnoczi 	send_byte(fd, -EPIPE, 0);
103cdbcc18dSStefan Hajnoczi 	recv_byte(fd, 1, 0);
104cdbcc18dSStefan Hajnoczi 	recv_byte(fd, 0, 0);
105cdbcc18dSStefan Hajnoczi 	close(fd);
106cdbcc18dSStefan Hajnoczi }
107cdbcc18dSStefan Hajnoczi 
108cdbcc18dSStefan Hajnoczi static void test_stream_server_close_server(const struct test_opts *opts)
109cdbcc18dSStefan Hajnoczi {
110cdbcc18dSStefan Hajnoczi 	int fd;
111cdbcc18dSStefan Hajnoczi 
112cdbcc18dSStefan Hajnoczi 	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
113cdbcc18dSStefan Hajnoczi 	if (fd < 0) {
114cdbcc18dSStefan Hajnoczi 		perror("accept");
115cdbcc18dSStefan Hajnoczi 		exit(EXIT_FAILURE);
116cdbcc18dSStefan Hajnoczi 	}
117cdbcc18dSStefan Hajnoczi 
118cdbcc18dSStefan Hajnoczi 	send_byte(fd, 1, 0);
119cdbcc18dSStefan Hajnoczi 	close(fd);
120cdbcc18dSStefan Hajnoczi 	control_writeln("CLOSED");
121cdbcc18dSStefan Hajnoczi }
122cdbcc18dSStefan Hajnoczi 
123cdbcc18dSStefan Hajnoczi /* With the standard socket sizes, VMCI is able to support about 100
124cdbcc18dSStefan Hajnoczi  * concurrent stream connections.
125cdbcc18dSStefan Hajnoczi  */
126cdbcc18dSStefan Hajnoczi #define MULTICONN_NFDS 100
127cdbcc18dSStefan Hajnoczi 
128cdbcc18dSStefan Hajnoczi static void test_stream_multiconn_client(const struct test_opts *opts)
129cdbcc18dSStefan Hajnoczi {
130cdbcc18dSStefan Hajnoczi 	int fds[MULTICONN_NFDS];
131cdbcc18dSStefan Hajnoczi 	int i;
132cdbcc18dSStefan Hajnoczi 
133cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++) {
134cdbcc18dSStefan Hajnoczi 		fds[i] = vsock_stream_connect(opts->peer_cid, 1234);
135cdbcc18dSStefan Hajnoczi 		if (fds[i] < 0) {
136cdbcc18dSStefan Hajnoczi 			perror("connect");
137cdbcc18dSStefan Hajnoczi 			exit(EXIT_FAILURE);
138cdbcc18dSStefan Hajnoczi 		}
139cdbcc18dSStefan Hajnoczi 	}
140cdbcc18dSStefan Hajnoczi 
141cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++) {
142cdbcc18dSStefan Hajnoczi 		if (i % 2)
143cdbcc18dSStefan Hajnoczi 			recv_byte(fds[i], 1, 0);
144cdbcc18dSStefan Hajnoczi 		else
145cdbcc18dSStefan Hajnoczi 			send_byte(fds[i], 1, 0);
146cdbcc18dSStefan Hajnoczi 	}
147cdbcc18dSStefan Hajnoczi 
148cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++)
149cdbcc18dSStefan Hajnoczi 		close(fds[i]);
150cdbcc18dSStefan Hajnoczi }
151cdbcc18dSStefan Hajnoczi 
152cdbcc18dSStefan Hajnoczi static void test_stream_multiconn_server(const struct test_opts *opts)
153cdbcc18dSStefan Hajnoczi {
154cdbcc18dSStefan Hajnoczi 	int fds[MULTICONN_NFDS];
155cdbcc18dSStefan Hajnoczi 	int i;
156cdbcc18dSStefan Hajnoczi 
157cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++) {
158cdbcc18dSStefan Hajnoczi 		fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
159cdbcc18dSStefan Hajnoczi 		if (fds[i] < 0) {
160cdbcc18dSStefan Hajnoczi 			perror("accept");
161cdbcc18dSStefan Hajnoczi 			exit(EXIT_FAILURE);
162cdbcc18dSStefan Hajnoczi 		}
163cdbcc18dSStefan Hajnoczi 	}
164cdbcc18dSStefan Hajnoczi 
165cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++) {
166cdbcc18dSStefan Hajnoczi 		if (i % 2)
167cdbcc18dSStefan Hajnoczi 			send_byte(fds[i], 1, 0);
168cdbcc18dSStefan Hajnoczi 		else
169cdbcc18dSStefan Hajnoczi 			recv_byte(fds[i], 1, 0);
170cdbcc18dSStefan Hajnoczi 	}
171cdbcc18dSStefan Hajnoczi 
172cdbcc18dSStefan Hajnoczi 	for (i = 0; i < MULTICONN_NFDS; i++)
173cdbcc18dSStefan Hajnoczi 		close(fds[i]);
174cdbcc18dSStefan Hajnoczi }
175cdbcc18dSStefan Hajnoczi 
176cdbcc18dSStefan Hajnoczi static struct test_case test_cases[] = {
177cdbcc18dSStefan Hajnoczi 	{
178cdbcc18dSStefan Hajnoczi 		.name = "SOCK_STREAM connection reset",
179cdbcc18dSStefan Hajnoczi 		.run_client = test_stream_connection_reset,
180cdbcc18dSStefan Hajnoczi 	},
181cdbcc18dSStefan Hajnoczi 	{
182cdbcc18dSStefan Hajnoczi 		.name = "SOCK_STREAM client close",
183cdbcc18dSStefan Hajnoczi 		.run_client = test_stream_client_close_client,
184cdbcc18dSStefan Hajnoczi 		.run_server = test_stream_client_close_server,
185cdbcc18dSStefan Hajnoczi 	},
186cdbcc18dSStefan Hajnoczi 	{
187cdbcc18dSStefan Hajnoczi 		.name = "SOCK_STREAM server close",
188cdbcc18dSStefan Hajnoczi 		.run_client = test_stream_server_close_client,
189cdbcc18dSStefan Hajnoczi 		.run_server = test_stream_server_close_server,
190cdbcc18dSStefan Hajnoczi 	},
191cdbcc18dSStefan Hajnoczi 	{
192cdbcc18dSStefan Hajnoczi 		.name = "SOCK_STREAM multiple connections",
193cdbcc18dSStefan Hajnoczi 		.run_client = test_stream_multiconn_client,
194cdbcc18dSStefan Hajnoczi 		.run_server = test_stream_multiconn_server,
195cdbcc18dSStefan Hajnoczi 	},
196cdbcc18dSStefan Hajnoczi 	{},
197cdbcc18dSStefan Hajnoczi };
198cdbcc18dSStefan Hajnoczi 
199cdbcc18dSStefan Hajnoczi static const char optstring[] = "";
200cdbcc18dSStefan Hajnoczi static const struct option longopts[] = {
201cdbcc18dSStefan Hajnoczi 	{
202cdbcc18dSStefan Hajnoczi 		.name = "control-host",
203cdbcc18dSStefan Hajnoczi 		.has_arg = required_argument,
204cdbcc18dSStefan Hajnoczi 		.val = 'H',
205cdbcc18dSStefan Hajnoczi 	},
206cdbcc18dSStefan Hajnoczi 	{
207cdbcc18dSStefan Hajnoczi 		.name = "control-port",
208cdbcc18dSStefan Hajnoczi 		.has_arg = required_argument,
209cdbcc18dSStefan Hajnoczi 		.val = 'P',
210cdbcc18dSStefan Hajnoczi 	},
211cdbcc18dSStefan Hajnoczi 	{
212cdbcc18dSStefan Hajnoczi 		.name = "mode",
213cdbcc18dSStefan Hajnoczi 		.has_arg = required_argument,
214cdbcc18dSStefan Hajnoczi 		.val = 'm',
215cdbcc18dSStefan Hajnoczi 	},
216cdbcc18dSStefan Hajnoczi 	{
217cdbcc18dSStefan Hajnoczi 		.name = "peer-cid",
218cdbcc18dSStefan Hajnoczi 		.has_arg = required_argument,
219cdbcc18dSStefan Hajnoczi 		.val = 'p',
220cdbcc18dSStefan Hajnoczi 	},
221cdbcc18dSStefan Hajnoczi 	{
222cdbcc18dSStefan Hajnoczi 		.name = "help",
223cdbcc18dSStefan Hajnoczi 		.has_arg = no_argument,
224cdbcc18dSStefan Hajnoczi 		.val = '?',
225cdbcc18dSStefan Hajnoczi 	},
226cdbcc18dSStefan Hajnoczi 	{},
227cdbcc18dSStefan Hajnoczi };
228cdbcc18dSStefan Hajnoczi 
229cdbcc18dSStefan Hajnoczi static void usage(void)
230cdbcc18dSStefan Hajnoczi {
231cdbcc18dSStefan Hajnoczi 	fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid>\n"
232cdbcc18dSStefan Hajnoczi 		"\n"
233cdbcc18dSStefan Hajnoczi 		"  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
234cdbcc18dSStefan Hajnoczi 		"  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
235cdbcc18dSStefan Hajnoczi 		"\n"
236cdbcc18dSStefan Hajnoczi 		"Run vsock.ko tests.  Must be launched in both guest\n"
237cdbcc18dSStefan Hajnoczi 		"and host.  One side must use --mode=client and\n"
238cdbcc18dSStefan Hajnoczi 		"the other side must use --mode=server.\n"
239cdbcc18dSStefan Hajnoczi 		"\n"
240cdbcc18dSStefan Hajnoczi 		"A TCP control socket connection is used to coordinate tests\n"
241cdbcc18dSStefan Hajnoczi 		"between the client and the server.  The server requires a\n"
242cdbcc18dSStefan Hajnoczi 		"listen address and the client requires an address to\n"
243cdbcc18dSStefan Hajnoczi 		"connect to.\n"
244cdbcc18dSStefan Hajnoczi 		"\n"
245cdbcc18dSStefan Hajnoczi 		"The CID of the other side must be given with --peer-cid=<cid>.\n");
246cdbcc18dSStefan Hajnoczi 	exit(EXIT_FAILURE);
247cdbcc18dSStefan Hajnoczi }
248cdbcc18dSStefan Hajnoczi 
249cdbcc18dSStefan Hajnoczi int main(int argc, char **argv)
250cdbcc18dSStefan Hajnoczi {
251cdbcc18dSStefan Hajnoczi 	const char *control_host = NULL;
252cdbcc18dSStefan Hajnoczi 	const char *control_port = NULL;
253cdbcc18dSStefan Hajnoczi 	struct test_opts opts = {
254cdbcc18dSStefan Hajnoczi 		.mode = TEST_MODE_UNSET,
255cdbcc18dSStefan Hajnoczi 		.peer_cid = VMADDR_CID_ANY,
256cdbcc18dSStefan Hajnoczi 	};
257cdbcc18dSStefan Hajnoczi 
258cdbcc18dSStefan Hajnoczi 	init_signals();
259cdbcc18dSStefan Hajnoczi 
260cdbcc18dSStefan Hajnoczi 	for (;;) {
261cdbcc18dSStefan Hajnoczi 		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
262cdbcc18dSStefan Hajnoczi 
263cdbcc18dSStefan Hajnoczi 		if (opt == -1)
264cdbcc18dSStefan Hajnoczi 			break;
265cdbcc18dSStefan Hajnoczi 
266cdbcc18dSStefan Hajnoczi 		switch (opt) {
267cdbcc18dSStefan Hajnoczi 		case 'H':
268cdbcc18dSStefan Hajnoczi 			control_host = optarg;
269cdbcc18dSStefan Hajnoczi 			break;
270cdbcc18dSStefan Hajnoczi 		case 'm':
271cdbcc18dSStefan Hajnoczi 			if (strcmp(optarg, "client") == 0)
272cdbcc18dSStefan Hajnoczi 				opts.mode = TEST_MODE_CLIENT;
273cdbcc18dSStefan Hajnoczi 			else if (strcmp(optarg, "server") == 0)
274cdbcc18dSStefan Hajnoczi 				opts.mode = TEST_MODE_SERVER;
275cdbcc18dSStefan Hajnoczi 			else {
276cdbcc18dSStefan Hajnoczi 				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
277cdbcc18dSStefan Hajnoczi 				return EXIT_FAILURE;
278cdbcc18dSStefan Hajnoczi 			}
279cdbcc18dSStefan Hajnoczi 			break;
280cdbcc18dSStefan Hajnoczi 		case 'p':
281cdbcc18dSStefan Hajnoczi 			opts.peer_cid = parse_cid(optarg);
282cdbcc18dSStefan Hajnoczi 			break;
283cdbcc18dSStefan Hajnoczi 		case 'P':
284cdbcc18dSStefan Hajnoczi 			control_port = optarg;
285cdbcc18dSStefan Hajnoczi 			break;
286cdbcc18dSStefan Hajnoczi 		case '?':
287cdbcc18dSStefan Hajnoczi 		default:
288cdbcc18dSStefan Hajnoczi 			usage();
289cdbcc18dSStefan Hajnoczi 		}
290cdbcc18dSStefan Hajnoczi 	}
291cdbcc18dSStefan Hajnoczi 
292cdbcc18dSStefan Hajnoczi 	if (!control_port)
293cdbcc18dSStefan Hajnoczi 		usage();
294cdbcc18dSStefan Hajnoczi 	if (opts.mode == TEST_MODE_UNSET)
295cdbcc18dSStefan Hajnoczi 		usage();
296cdbcc18dSStefan Hajnoczi 	if (opts.peer_cid == VMADDR_CID_ANY)
297cdbcc18dSStefan Hajnoczi 		usage();
298cdbcc18dSStefan Hajnoczi 
299cdbcc18dSStefan Hajnoczi 	if (!control_host) {
300cdbcc18dSStefan Hajnoczi 		if (opts.mode != TEST_MODE_SERVER)
301cdbcc18dSStefan Hajnoczi 			usage();
302cdbcc18dSStefan Hajnoczi 		control_host = "0.0.0.0";
303cdbcc18dSStefan Hajnoczi 	}
304cdbcc18dSStefan Hajnoczi 
305cdbcc18dSStefan Hajnoczi 	control_init(control_host, control_port,
306cdbcc18dSStefan Hajnoczi 		     opts.mode == TEST_MODE_SERVER);
307cdbcc18dSStefan Hajnoczi 
308cdbcc18dSStefan Hajnoczi 	run_tests(test_cases, &opts);
309cdbcc18dSStefan Hajnoczi 
310cdbcc18dSStefan Hajnoczi 	control_cleanup();
311cdbcc18dSStefan Hajnoczi 	return EXIT_SUCCESS;
312cdbcc18dSStefan Hajnoczi }
313