2806 lines · 64.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * vsock_test - vsock.ko test suite * * Copyright (C) 2017 Red Hat, Inc. * * Author: Stefan Hajnoczi <stefanha@redhat.com> */ #include <getopt.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <linux/kernel.h>#include <sys/types.h>#include <sys/socket.h>#include <time.h>#include <sys/mman.h>#include <poll.h>#include <signal.h>#include <sys/ioctl.h>#include <linux/time64.h>#include <pthread.h>#include <fcntl.h>#include <linux/sockios.h> #include "vsock_test_zerocopy.h"#include "timeout.h"#include "control.h"#include "util.h" /* Basic messages for control_writeulong(), control_readulong() */#define CONTROL_CONTINUE	1#define CONTROL_DONE		0 static void test_stream_connection_reset(const struct test_opts *opts){	union {		struct sockaddr sa;		struct sockaddr_vm svm;	} addr = {		.svm = {			.svm_family = AF_VSOCK,			.svm_port = opts->peer_port,			.svm_cid = opts->peer_cid,		},	};	int ret;	int fd; 	fd = socket(AF_VSOCK, SOCK_STREAM, 0); 	timeout_begin(TIMEOUT);	do {		ret = connect(fd, &addr.sa, sizeof(addr.svm));		timeout_check("connect");	} while (ret < 0 && errno == EINTR);	timeout_end(); 	if (ret != -1) {		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);		exit(EXIT_FAILURE);	}	if (errno != ECONNRESET) {		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);		exit(EXIT_FAILURE);	} 	close(fd);} static void test_stream_bind_only_client(const struct test_opts *opts){	union {		struct sockaddr sa;		struct sockaddr_vm svm;	} addr = {		.svm = {			.svm_family = AF_VSOCK,			.svm_port = opts->peer_port,			.svm_cid = opts->peer_cid,		},	};	int ret;	int fd; 	/* Wait for the server to be ready */	control_expectln("BIND"); 	fd = socket(AF_VSOCK, SOCK_STREAM, 0); 	timeout_begin(TIMEOUT);	do {		ret = connect(fd, &addr.sa, sizeof(addr.svm));		timeout_check("connect");	} while (ret < 0 && errno == EINTR);	timeout_end(); 	if (ret != -1) {		fprintf(stderr, "expected connect(2) failure, got %d\n", ret);		exit(EXIT_FAILURE);	}	if (errno != ECONNRESET) {		fprintf(stderr, "unexpected connect(2) errno %d\n", errno);		exit(EXIT_FAILURE);	} 	/* Notify the server that the client has finished */	control_writeln("DONE"); 	close(fd);} static void test_stream_bind_only_server(const struct test_opts *opts){	int fd; 	fd = vsock_bind(VMADDR_CID_ANY, opts->peer_port, SOCK_STREAM); 	/* Notify the client that the server is ready */	control_writeln("BIND"); 	/* Wait for the client to finish */	control_expectln("DONE"); 	close(fd);} static void test_stream_client_close_client(const struct test_opts *opts){	int fd; 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);	if (fd < 0) {		perror("connect");		exit(EXIT_FAILURE);	} 	send_byte(fd, 1, 0);	close(fd);} static void test_stream_client_close_server(const struct test_opts *opts){	int fd; 	fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);	if (fd < 0) {		perror("accept");		exit(EXIT_FAILURE);	} 	/* Wait for the remote to close the connection, before check	 * -EPIPE error on send.	 */	vsock_wait_remote_close(fd); 	send_byte(fd, -EPIPE, 0);	recv_byte(fd, 1, 0);	recv_byte(fd, 0, 0);	close(fd);} static void test_stream_server_close_client(const struct test_opts *opts){	int fd; 	fd = vsock_stream_connect(opts->peer_cid, opts->peer_port);	if (fd < 0) {		perror("connect");		exit(EXIT_FAILURE);	} 	/* Wait for the remote to close the connection, before check	 * -EPIPE error on send.	 */	vsock_wait_remote_close(fd); 	send_byte(fd, -EPIPE, 0);	recv_byte(fd, 1, 0);	recv_byte(fd, 0, 0);	close(fd);} static void test_stream_server_close_server(const struct test_opts *opts){	int fd; 	fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);	if (fd < 0) {		perror("accept");		exit(EXIT_FAILURE);	} 	send_byte(fd, 1, 0);	close(fd);} /* With the standard socket sizes, VMCI is able to support about 100 * concurrent stream connections. */#define MULTICONN_NFDS 100 static void test_stream_multiconn_client(const struct test_opts *opts){	int fds[MULTICONN_NFDS];	int i; 	for (i = 0; i < MULTICONN_NFDS; i++) {		fds[i] = vsock_stream_connect(opts->peer_cid, opts->peer_port);		if (fds[i] < 0) {			perror("connect");			exit(EXIT_FAILURE);		}	} 	for (i = 0; i < MULTICONN_NFDS; i++) {		if (i % 2)			recv_byte(fds[i], 1, 0);		else			send_byte(fds[i], 1, 0);	} 	for (i = 0; i < MULTICONN_NFDS; i++)		close(fds[i]);} static void test_stream_multiconn_server(const struct test_opts *opts){	int fds[MULTICONN_NFDS];	int i; 	for (i = 0; i < MULTICONN_NFDS; i++) {		fds[i] = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);		if (fds[i] < 0) {			perror("accept");			exit(EXIT_FAILURE);		}	} 	for (i = 0; i < MULTICONN_NFDS; i++) {		if (i % 2)			send_byte(fds[i], 1, 0);		else			recv_byte(fds[i], 1, 0);	} 	for (i = 0; i < MULTICONN_NFDS; i++)		close(fds[i]);} #define MSG_PEEK_BUF_LEN 64 static void test_msg_peek_client(const struct test_opts *opts,				 bool seqpacket){	unsigned char buf[MSG_PEEK_BUF_LEN];	int fd;	int i; 	if (seqpacket)		fd = vsock_seqpacket_connect(opts->peer_cid, opts->peer_port);	else		fd = vsock_stream_connect(opts->peer_cid, opts->peer_port); 	if (fd < 0) {		perror("connect");		exit(EXIT_FAILURE);	} 	for (i = 0; i < sizeof(buf); i++)		buf[i] = rand() & 0xFF; 	control_expectln("SRVREADY"); 	send_buf(fd, buf, sizeof(buf), 0, sizeof(buf)); 	close(fd);} static void test_msg_peek_server(const struct test_opts *opts,				 bool seqpacket){	unsigned char buf_half[MSG_PEEK_BUF_LEN / 2];	unsigned char buf_normal[MSG_PEEK_BUF_LEN];	unsigned char buf_peek[MSG_PEEK_BUF_LEN];	int fd; 	if (seqpacket)		fd = vsock_seqpacket_accept(VMADDR_CID_ANY, opts->peer_port, NULL);	else		fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL); 	if (fd < 0) {		perror("accept");		exit(EXIT_FAILURE);	} 	/* Peek from empty socket. */	recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK | MSG_DONTWAIT,		 -EAGAIN); 	control_writeln("SRVREADY"); 	/* Peek part of data. */	recv_buf(fd, buf_half, sizeof(buf_half), MSG_PEEK, sizeof(buf_half)); 	/* Peek whole data. */	recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek)); 	/* Compare partial and full peek. */	if (memcmp(buf_half, buf_peek, sizeof(buf_half))) {		fprintf(stderr, "Partial peek data mismatch\n");		exit(EXIT_FAILURE);	} 	if (seqpacket) {		/* This type of socket supports MSG_TRUNC flag,		 * so check it with MSG_PEEK. We must get length		 * of the message.		 */		recv_buf(fd, buf_half, sizeof(buf_half), MSG_PEEK | MSG_TRUNC,			 sizeof(buf_peek));	} 	recv_buf(fd, buf_normal, sizeof(buf_normal), 0, sizeof(buf_normal)); 	/* Compare full peek and normal read. */	if (memcmp(buf_peek, buf_normal, sizeof(buf_peek))) {		fprintf(stderr, "Full peek data mismatch\n");		exit(EXIT_FAILURE);	} 	close(fd);} static void test_stream_msg_peek_client(const struct test_opts *opts){	return test_msg_peek_client(opts, false);} static void test_stream_msg_peek_server(const struct test_opts *opts){	return test_msg_peek_server(opts, false);} static void test_stream_peek_after_recv_server(const struct test_opts *opts){	unsigned char buf_normal[MSG_PEEK_BUF_LEN];	unsigned char buf_peek[MSG_PEEK_BUF_LEN];	int fd; 	fd = vsock_stream_accept(VMADDR_CID_ANY, opts->peer_port, NULL);	if (fd < 0) {		perror("accept");		exit(EXIT_FAILURE);	} 	control_writeln("SRVREADY"); 	/* Partial recv to advance offset within the skb */	recv_buf(fd, buf_normal, 1, 0, 1); 	/* Peek with a buffer larger than the remaining data */	recv_buf(fd, buf_peek, sizeof(buf_peek), MSG_PEEK, sizeof(buf_peek) - 1); 	/* Consume the remaining data */	recv_buf(fd, buf_normal, sizeof(buf_normal) - 1, 0, sizeof(buf_normal) - 1); 	/* Compare full peek and normal read. */	if (memcmp(buf_peek, buf_normal, sizeof(buf_peek) - 1)) {		fprintf(stderr, "Full peek data mismatch\n");		exit(EXIT_FAILURE);	} 	close(fd);} #define SOCK_BUF_SIZE (2 * 1024 * 1024)#define SOCK_BUF_SIZE_SMALL (64 * 1024)#define MAX_MSG_PAGES 4 static void test_seqpacket_msg_bounds_client(const struct test_opts *opts){	unsigned long long sock_buf_size;	unsigned long curr_hash;	size_t max_msg_size;	int page_size;	int msg_count;	int fd; 	fd = vsock_seqpacket_connect(opts->peer_cid, opts->peer_port);	if (fd < 0) {		perror("connect");		exit(EXIT_FAILURE);	} 	sock_buf_size = SOCK_BUF_SIZE; 	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,			     sock_buf_size,			     "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)"); 	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,			     sock_buf_size,			     "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)"); 	/* Wait, until receiver sets buffer size. */	control_expectln("SRVREADY"); 	curr_hash = 0;	page_size = getpagesize();	max_msg_size = MAX_MSG_PAGES * page_size;	msg_count = SOCK_BUF_SIZE / max_msg_size; 	for (int i = 0; i < msg_count; i++) {		size_t buf_size;		int flags;		void *buf; 		/* Use "small" buffers and "big" buffers. */		if (i & 1)			buf_size = page_size +					(rand() % (max_msg_size - page_size));		else			buf_size = 1 + (rand() % page_size); 		buf = malloc(buf_size); 		if (!buf) {			perror("malloc");			exit(EXIT_FAILURE);		} 		memset(buf, rand() & 0xff, buf_size);		/* Set at least one MSG_EOR + some random. */		if (i == (msg_count / 2) || (rand() & 1)) {			flags = MSG_EOR;			curr_hash++;		} else {			flags = 0;		} 		send_buf(fd, buf, buf_size, flags, buf_size); 		/*		 * Hash sum is computed at both client and server in		 * the same way:		 * H += hash('message data')		 * Such hash "controls" both data integrity and message		 * bounds. After data exchange, both sums are compared		 * using control socket, and if message bounds wasn't		 * broken - two values must be equal.		 */		curr_hash += hash_djb2(buf, buf_size);		free(buf);	} 	control_writeln("SENDDONE");	control_writeulong(curr_hash);	close(fd);} static void test_seqpacket_msg_bounds_server(const struct test_opts *opts){	unsigned long long sock_buf_size;	unsigned long remote_hash;	unsigned long curr_hash;	int fd;	struct msghdr msg = {0};	struct iovec iov = {0}; 	fd = vsock_seqpacket_accept(VMADDR_CID_ANY, opts->peer_port, NULL);	if (fd < 0) {		perror("accept");		exit(EXIT_FAILURE);	} 	sock_buf_size = SOCK_BUF_SIZE; 	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_MAX_SIZE,			     sock_buf_size,			     "setsockopt(SO_VM_SOCKETS_BUFFER_MAX_SIZE)"); 	setsockopt_ull_check(fd, AF_VSOCK, SO_VM_SOCKETS_BUFFER_SIZE,			     sock_buf_size,			     "setsockopt(SO_VM_SOCKETS_BUFFER_SIZE)"); 	/* Ready to receive data. */	control_writeln("SRVREADY");	/* Wait, until peer sends whole data. */	control_expectln("SENDDONE");	iov.iov_len = MAX_MSG_PAGES * getpagesize();	iov.iov_base = malloc(iov.iov_len);	if (!iov.iov_base) {		perror("malloc");		exit(EXIT_FAILURE);	}

Lines 1–500 of 2806.