Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 24static 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
 60static 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
102static 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
132static 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
146static 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
167static 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
188static 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
207static 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
231static 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
255static 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
269static 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
285static 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
303static 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
333static 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
353static 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
388static 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
431static const char optstring[] = "";
432static 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
471static 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
502int 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}