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