xref: /openbmc/linux/tools/testing/vsock/vsock_test.c (revision f17f06a0)
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 
18 #include "timeout.h"
19 #include "control.h"
20 #include "util.h"
21 
22 static void test_stream_connection_reset(const struct test_opts *opts)
23 {
24 	union {
25 		struct sockaddr sa;
26 		struct sockaddr_vm svm;
27 	} addr = {
28 		.svm = {
29 			.svm_family = AF_VSOCK,
30 			.svm_port = 1234,
31 			.svm_cid = opts->peer_cid,
32 		},
33 	};
34 	int ret;
35 	int fd;
36 
37 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
38 
39 	timeout_begin(TIMEOUT);
40 	do {
41 		ret = connect(fd, &addr.sa, sizeof(addr.svm));
42 		timeout_check("connect");
43 	} while (ret < 0 && errno == EINTR);
44 	timeout_end();
45 
46 	if (ret != -1) {
47 		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);
48 		exit(EXIT_FAILURE);
49 	}
50 	if (errno != ECONNRESET) {
51 		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);
52 		exit(EXIT_FAILURE);
53 	}
54 
55 	close(fd);
56 }
57 
58 static void test_stream_client_close_client(const struct test_opts *opts)
59 {
60 	int fd;
61 
62 	fd = vsock_stream_connect(opts->peer_cid, 1234);
63 	if (fd < 0) {
64 		perror("connect");
65 		exit(EXIT_FAILURE);
66 	}
67 
68 	send_byte(fd, 1, 0);
69 	close(fd);
70 }
71 
72 static void test_stream_client_close_server(const struct test_opts *opts)
73 {
74 	int fd;
75 
76 	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
77 	if (fd < 0) {
78 		perror("accept");
79 		exit(EXIT_FAILURE);
80 	}
81 
82 	/* Wait for the remote to close the connection, before check
83 	 * -EPIPE error on send.
84 	 */
85 	vsock_wait_remote_close(fd);
86 
87 	send_byte(fd, -EPIPE, 0);
88 	recv_byte(fd, 1, 0);
89 	recv_byte(fd, 0, 0);
90 	close(fd);
91 }
92 
93 static void test_stream_server_close_client(const struct test_opts *opts)
94 {
95 	int fd;
96 
97 	fd = vsock_stream_connect(opts->peer_cid, 1234);
98 	if (fd < 0) {
99 		perror("connect");
100 		exit(EXIT_FAILURE);
101 	}
102 
103 	/* Wait for the remote to close the connection, before check
104 	 * -EPIPE error on send.
105 	 */
106 	vsock_wait_remote_close(fd);
107 
108 	send_byte(fd, -EPIPE, 0);
109 	recv_byte(fd, 1, 0);
110 	recv_byte(fd, 0, 0);
111 	close(fd);
112 }
113 
114 static void test_stream_server_close_server(const struct test_opts *opts)
115 {
116 	int fd;
117 
118 	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
119 	if (fd < 0) {
120 		perror("accept");
121 		exit(EXIT_FAILURE);
122 	}
123 
124 	send_byte(fd, 1, 0);
125 	close(fd);
126 }
127 
128 /* With the standard socket sizes, VMCI is able to support about 100
129  * concurrent stream connections.
130  */
131 #define MULTICONN_NFDS 100
132 
133 static void test_stream_multiconn_client(const struct test_opts *opts)
134 {
135 	int fds[MULTICONN_NFDS];
136 	int i;
137 
138 	for (i = 0; i < MULTICONN_NFDS; i++) {
139 		fds[i] = vsock_stream_connect(opts->peer_cid, 1234);
140 		if (fds[i] < 0) {
141 			perror("connect");
142 			exit(EXIT_FAILURE);
143 		}
144 	}
145 
146 	for (i = 0; i < MULTICONN_NFDS; i++) {
147 		if (i % 2)
148 			recv_byte(fds[i], 1, 0);
149 		else
150 			send_byte(fds[i], 1, 0);
151 	}
152 
153 	for (i = 0; i < MULTICONN_NFDS; i++)
154 		close(fds[i]);
155 }
156 
157 static void test_stream_multiconn_server(const struct test_opts *opts)
158 {
159 	int fds[MULTICONN_NFDS];
160 	int i;
161 
162 	for (i = 0; i < MULTICONN_NFDS; i++) {
163 		fds[i] = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
164 		if (fds[i] < 0) {
165 			perror("accept");
166 			exit(EXIT_FAILURE);
167 		}
168 	}
169 
170 	for (i = 0; i < MULTICONN_NFDS; i++) {
171 		if (i % 2)
172 			send_byte(fds[i], 1, 0);
173 		else
174 			recv_byte(fds[i], 1, 0);
175 	}
176 
177 	for (i = 0; i < MULTICONN_NFDS; i++)
178 		close(fds[i]);
179 }
180 
181 static void test_stream_msg_peek_client(const struct test_opts *opts)
182 {
183 	int fd;
184 
185 	fd = vsock_stream_connect(opts->peer_cid, 1234);
186 	if (fd < 0) {
187 		perror("connect");
188 		exit(EXIT_FAILURE);
189 	}
190 
191 	send_byte(fd, 1, 0);
192 	close(fd);
193 }
194 
195 static void test_stream_msg_peek_server(const struct test_opts *opts)
196 {
197 	int fd;
198 
199 	fd = vsock_stream_accept(VMADDR_CID_ANY, 1234, NULL);
200 	if (fd < 0) {
201 		perror("accept");
202 		exit(EXIT_FAILURE);
203 	}
204 
205 	recv_byte(fd, 1, MSG_PEEK);
206 	recv_byte(fd, 1, 0);
207 	close(fd);
208 }
209 
210 static struct test_case test_cases[] = {
211 	{
212 		.name = "SOCK_STREAM connection reset",
213 		.run_client = test_stream_connection_reset,
214 	},
215 	{
216 		.name = "SOCK_STREAM client close",
217 		.run_client = test_stream_client_close_client,
218 		.run_server = test_stream_client_close_server,
219 	},
220 	{
221 		.name = "SOCK_STREAM server close",
222 		.run_client = test_stream_server_close_client,
223 		.run_server = test_stream_server_close_server,
224 	},
225 	{
226 		.name = "SOCK_STREAM multiple connections",
227 		.run_client = test_stream_multiconn_client,
228 		.run_server = test_stream_multiconn_server,
229 	},
230 	{
231 		.name = "SOCK_STREAM MSG_PEEK",
232 		.run_client = test_stream_msg_peek_client,
233 		.run_server = test_stream_msg_peek_server,
234 	},
235 	{},
236 };
237 
238 static const char optstring[] = "";
239 static const struct option longopts[] = {
240 	{
241 		.name = "control-host",
242 		.has_arg = required_argument,
243 		.val = 'H',
244 	},
245 	{
246 		.name = "control-port",
247 		.has_arg = required_argument,
248 		.val = 'P',
249 	},
250 	{
251 		.name = "mode",
252 		.has_arg = required_argument,
253 		.val = 'm',
254 	},
255 	{
256 		.name = "peer-cid",
257 		.has_arg = required_argument,
258 		.val = 'p',
259 	},
260 	{
261 		.name = "list",
262 		.has_arg = no_argument,
263 		.val = 'l',
264 	},
265 	{
266 		.name = "skip",
267 		.has_arg = required_argument,
268 		.val = 's',
269 	},
270 	{
271 		.name = "help",
272 		.has_arg = no_argument,
273 		.val = '?',
274 	},
275 	{},
276 };
277 
278 static void usage(void)
279 {
280 	fprintf(stderr, "Usage: vsock_test [--help] [--control-host=<host>] --control-port=<port> --mode=client|server --peer-cid=<cid> [--list] [--skip=<test_id>]\n"
281 		"\n"
282 		"  Server: vsock_test --control-port=1234 --mode=server --peer-cid=3\n"
283 		"  Client: vsock_test --control-host=192.168.0.1 --control-port=1234 --mode=client --peer-cid=2\n"
284 		"\n"
285 		"Run vsock.ko tests.  Must be launched in both guest\n"
286 		"and host.  One side must use --mode=client and\n"
287 		"the other side must use --mode=server.\n"
288 		"\n"
289 		"A TCP control socket connection is used to coordinate tests\n"
290 		"between the client and the server.  The server requires a\n"
291 		"listen address and the client requires an address to\n"
292 		"connect to.\n"
293 		"\n"
294 		"The CID of the other side must be given with --peer-cid=<cid>.\n"
295 		"\n"
296 		"Options:\n"
297 		"  --help                 This help message\n"
298 		"  --control-host <host>  Server IP address to connect to\n"
299 		"  --control-port <port>  Server port to listen on/connect to\n"
300 		"  --mode client|server   Server or client mode\n"
301 		"  --peer-cid <cid>       CID of the other side\n"
302 		"  --list                 List of tests that will be executed\n"
303 		"  --skip <test_id>       Test ID to skip;\n"
304 		"                         use multiple --skip options to skip more tests\n"
305 		);
306 	exit(EXIT_FAILURE);
307 }
308 
309 int main(int argc, char **argv)
310 {
311 	const char *control_host = NULL;
312 	const char *control_port = NULL;
313 	struct test_opts opts = {
314 		.mode = TEST_MODE_UNSET,
315 		.peer_cid = VMADDR_CID_ANY,
316 	};
317 
318 	init_signals();
319 
320 	for (;;) {
321 		int opt = getopt_long(argc, argv, optstring, longopts, NULL);
322 
323 		if (opt == -1)
324 			break;
325 
326 		switch (opt) {
327 		case 'H':
328 			control_host = optarg;
329 			break;
330 		case 'm':
331 			if (strcmp(optarg, "client") == 0)
332 				opts.mode = TEST_MODE_CLIENT;
333 			else if (strcmp(optarg, "server") == 0)
334 				opts.mode = TEST_MODE_SERVER;
335 			else {
336 				fprintf(stderr, "--mode must be \"client\" or \"server\"\n");
337 				return EXIT_FAILURE;
338 			}
339 			break;
340 		case 'p':
341 			opts.peer_cid = parse_cid(optarg);
342 			break;
343 		case 'P':
344 			control_port = optarg;
345 			break;
346 		case 'l':
347 			list_tests(test_cases);
348 			break;
349 		case 's':
350 			skip_test(test_cases, ARRAY_SIZE(test_cases) - 1,
351 				  optarg);
352 			break;
353 		case '?':
354 		default:
355 			usage();
356 		}
357 	}
358 
359 	if (!control_port)
360 		usage();
361 	if (opts.mode == TEST_MODE_UNSET)
362 		usage();
363 	if (opts.peer_cid == VMADDR_CID_ANY)
364 		usage();
365 
366 	if (!control_host) {
367 		if (opts.mode != TEST_MODE_SERVER)
368 			usage();
369 		control_host = "0.0.0.0";
370 	}
371 
372 	control_init(control_host, control_port,
373 		     opts.mode == TEST_MODE_SERVER);
374 
375 	run_tests(test_cases, &opts);
376 
377 	control_cleanup();
378 	return EXIT_SUCCESS;
379 }
380