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
22static 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
58static 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
100static 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
130static 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
144static 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
165static 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
186static 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
205static 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
229static 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
253static 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
267static 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
282static 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
315static const char optstring[] = "";
316static 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
355static 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
386int 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