954 lines · 19.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * vsock test utilities * * Copyright (C) 2017 Red Hat, Inc. * * Author: Stefan Hajnoczi <stefanha@redhat.com> */ #include <ctype.h>#include <errno.h>#include <stdio.h>#include <stdint.h>#include <stdlib.h>#include <string.h>#include <signal.h>#include <unistd.h>#include <assert.h>#include <sys/epoll.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <linux/sockios.h> #include "timeout.h"#include "control.h"#include "util.h" #define KALLSYMS_PATH		"/proc/kallsyms"#define KALLSYMS_LINE_LEN	512 /* Install signal handlers */void init_signals(void){	struct sigaction act = {		.sa_handler = sigalrm,	}; 	sigaction(SIGALRM, &act, NULL);	signal(SIGPIPE, SIG_IGN);} static unsigned int parse_uint(const char *str, const char *err_str){	char *endptr = NULL;	unsigned long n; 	errno = 0;	n = strtoul(str, &endptr, 10);	if (errno || *endptr != '\0') {		fprintf(stderr, "malformed %s \"%s\"\n", err_str, str);		exit(EXIT_FAILURE);	}	return n;} /* Parse a CID in string representation */unsigned int parse_cid(const char *str){	return parse_uint(str, "CID");} /* Parse a port in string representation */unsigned int parse_port(const char *str){	return parse_uint(str, "port");} /* Wait for the remote to close the connection */void vsock_wait_remote_close(int fd){	struct epoll_event ev;	int epollfd, nfds; 	epollfd = epoll_create1(0);	if (epollfd == -1) {		perror("epoll_create1");		exit(EXIT_FAILURE);	} 	ev.events = EPOLLRDHUP | EPOLLHUP;	ev.data.fd = fd;	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {		perror("epoll_ctl");		exit(EXIT_FAILURE);	} 	nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000);	if (nfds == -1) {		perror("epoll_wait");		exit(EXIT_FAILURE);	} 	if (nfds == 0) {		fprintf(stderr, "epoll_wait timed out\n");		exit(EXIT_FAILURE);	} 	assert(nfds == 1);	assert(ev.events & (EPOLLRDHUP | EPOLLHUP));	assert(ev.data.fd == fd); 	close(epollfd);} /* Wait until ioctl gives an expected int value. * Return false if the op is not supported. */bool vsock_ioctl_int(int fd, unsigned long op, int expected){	int actual, ret;	char name[32]; 	snprintf(name, sizeof(name), "ioctl(%lu)", op); 	timeout_begin(TIMEOUT);	do {		ret = ioctl(fd, op, &actual);		if (ret < 0) {			if (errno == EOPNOTSUPP || errno == ENOTTY)				break; 			perror(name);			exit(EXIT_FAILURE);		}		timeout_check(name);	} while (actual != expected);	timeout_end(); 	return ret >= 0;} /* Wait until transport reports no data left to be sent. * Return false if transport does not implement the unsent_bytes() callback. */bool vsock_wait_sent(int fd){	return vsock_ioctl_int(fd, SIOCOUTQ, 0);} /* Create socket <type>, bind to <cid, port>. * Return the file descriptor, or -1 on error. */int vsock_bind_try(unsigned int cid, unsigned int port, int type){	struct sockaddr_vm sa = {		.svm_family = AF_VSOCK,		.svm_cid = cid,		.svm_port = port,	};	int fd, saved_errno; 	fd = socket(AF_VSOCK, type, 0);	if (fd < 0) {		perror("socket");		exit(EXIT_FAILURE);	} 	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa))) {		saved_errno = errno;		close(fd);		errno = saved_errno;		fd = -1;	} 	return fd;} /* Create socket <type>, bind to <cid, port> and return the file descriptor. */int vsock_bind(unsigned int cid, unsigned int port, int type){	int fd; 	fd = vsock_bind_try(cid, port, type);	if (fd < 0) {		perror("bind");		exit(EXIT_FAILURE);	} 	return fd;} int vsock_connect_fd(int fd, unsigned int cid, unsigned int port){	struct sockaddr_vm sa = {		.svm_family = AF_VSOCK,		.svm_cid = cid,		.svm_port = port,	};	int ret; 	timeout_begin(TIMEOUT);	do {		ret = connect(fd, (struct sockaddr *)&sa, sizeof(sa));		timeout_check("connect");	} while (ret < 0 && errno == EINTR);	timeout_end(); 	return ret;} /* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type){	int client_fd; 	client_fd = vsock_bind(VMADDR_CID_ANY, bind_port, type); 	if (vsock_connect_fd(client_fd, cid, port)) {		perror("connect");		exit(EXIT_FAILURE);	} 	return client_fd;} /* Connect to <cid, port> and return the file descriptor. */int vsock_connect(unsigned int cid, unsigned int port, int type){	int fd; 	control_expectln("LISTENING"); 	fd = socket(AF_VSOCK, type, 0);	if (fd < 0) {		perror("socket");		exit(EXIT_FAILURE);	} 	if (vsock_connect_fd(fd, cid, port)) {		int old_errno = errno; 		close(fd);		fd = -1;		errno = old_errno;	} 	return fd;} int vsock_stream_connect(unsigned int cid, unsigned int port){	return vsock_connect(cid, port, SOCK_STREAM);} int vsock_seqpacket_connect(unsigned int cid, unsigned int port){	return vsock_connect(cid, port, SOCK_SEQPACKET);} /* Listen on <cid, port> and return the file descriptor. */static int vsock_listen(unsigned int cid, unsigned int port, int type){	int fd; 	fd = vsock_bind(cid, port, type); 	if (listen(fd, 1) < 0) {		perror("listen");		exit(EXIT_FAILURE);	} 	return fd;} /* Listen on <cid, port> and return the first incoming connection.  The remote * address is stored to clientaddrp.  clientaddrp may be NULL. */int vsock_accept(unsigned int cid, unsigned int port,		 struct sockaddr_vm *clientaddrp, int type){	union {		struct sockaddr sa;		struct sockaddr_vm svm;	} clientaddr;	socklen_t clientaddr_len = sizeof(clientaddr.svm);	int fd, client_fd, old_errno; 	fd = vsock_listen(cid, port, type); 	control_writeln("LISTENING"); 	timeout_begin(TIMEOUT);	do {		client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);		timeout_check("accept");	} while (client_fd < 0 && errno == EINTR);	timeout_end(); 	old_errno = errno;	close(fd);	errno = old_errno; 	if (client_fd < 0)		return client_fd; 	if (clientaddr_len != sizeof(clientaddr.svm)) {		fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",			(size_t)clientaddr_len);		exit(EXIT_FAILURE);	}	if (clientaddr.sa.sa_family != AF_VSOCK) {		fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",			clientaddr.sa.sa_family);		exit(EXIT_FAILURE);	} 	if (clientaddrp)		*clientaddrp = clientaddr.svm;	return client_fd;} int vsock_stream_accept(unsigned int cid, unsigned int port,			struct sockaddr_vm *clientaddrp){	return vsock_accept(cid, port, clientaddrp, SOCK_STREAM);} int vsock_stream_listen(unsigned int cid, unsigned int port){	return vsock_listen(cid, port, SOCK_STREAM);} int vsock_seqpacket_accept(unsigned int cid, unsigned int port,			   struct sockaddr_vm *clientaddrp){	return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET);} /* Transmit bytes from a buffer and check the return value. * * expected_ret: *  <0 Negative errno (for testing errors) *   0 End-of-file *  >0 Success (bytes successfully written) */void send_buf(int fd, const void *buf, size_t len, int flags,	      ssize_t expected_ret){	ssize_t nwritten = 0;	ssize_t ret; 	timeout_begin(TIMEOUT);	do {		ret = send(fd, buf + nwritten, len - nwritten, flags);		timeout_check("send"); 		if (ret < 0 && errno == EINTR)			continue;		if (ret <= 0)			break; 		nwritten += ret;	} while (nwritten < len);	timeout_end(); 	if (expected_ret < 0) {		if (ret != -1) {			fprintf(stderr, "bogus send(2) return value %zd (expected %zd)\n",				ret, expected_ret);			exit(EXIT_FAILURE);		}		if (errno != -expected_ret) {			perror("send");			exit(EXIT_FAILURE);		}		return;	} 	if (ret < 0) {		perror("send");		exit(EXIT_FAILURE);	} 	if (nwritten != expected_ret) {		if (ret == 0)			fprintf(stderr, "unexpected EOF while sending bytes\n"); 		fprintf(stderr, "bogus send(2) bytes written %zd (expected %zd)\n",			nwritten, expected_ret);		exit(EXIT_FAILURE);	}} #define RECV_PEEK_RETRY_USEC (10 * 1000) /* Receive bytes in a buffer and check the return value. * * When MSG_PEEK is set, recv() is retried until it returns at least * expected_ret bytes. The function returns on error, EOF, or timeout * as usual. * * expected_ret: *  <0 Negative errno (for testing errors) *   0 End-of-file *  >0 Success (bytes successfully read) */void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret){	ssize_t nread = 0;	ssize_t ret; 	timeout_begin(TIMEOUT);	do {		ret = recv(fd, buf + nread, len - nread, flags);		timeout_check("recv"); 		if (ret < 0 && errno == EINTR)			continue;		if (ret <= 0)			break; 		if (flags & MSG_PEEK) {			if (ret >= expected_ret) {				nread = ret;				break;			}			timeout_usleep(RECV_PEEK_RETRY_USEC);			continue;		} 		nread += ret;	} while (nread < len);	timeout_end(); 	if (expected_ret < 0) {		if (ret != -1) {			fprintf(stderr, "bogus recv(2) return value %zd (expected %zd)\n",				ret, expected_ret);			exit(EXIT_FAILURE);		}		if (errno != -expected_ret) {			perror("recv");			exit(EXIT_FAILURE);		}		return;	} 	if (ret < 0) {		perror("recv");		exit(EXIT_FAILURE);	} 	if (nread != expected_ret) {		if (ret == 0)			fprintf(stderr, "unexpected EOF while receiving bytes\n"); 		fprintf(stderr, "bogus recv(2) bytes read %zd (expected %zd)\n",			nread, expected_ret);		exit(EXIT_FAILURE);	}} /* Transmit one byte and check the return value. * * expected_ret: *  <0 Negative errno (for testing errors) *   0 End-of-file *   1 Success */void send_byte(int fd, int expected_ret, int flags){	static const uint8_t byte = 'A'; 	send_buf(fd, &byte, sizeof(byte), flags, expected_ret);} /* Receive one byte and check the return value. * * expected_ret: *  <0 Negative errno (for testing errors) *   0 End-of-file *   1 Success */void recv_byte(int fd, int expected_ret, int flags){	uint8_t byte; 	recv_buf(fd, &byte, sizeof(byte), flags, expected_ret); 	if (byte != 'A') {		fprintf(stderr, "unexpected byte read 0x%02x\n", byte);		exit(EXIT_FAILURE);	}} /* Run test cases.  The program terminates if a failure occurs. */void run_tests(const struct test_case *test_cases,	       const struct test_opts *opts){	int i; 	for (i = 0; test_cases[i].name; i++) {		void (*run)(const struct test_opts *opts);		char *line; 		printf("%d - %s...", i, test_cases[i].name);		fflush(stdout); 		/* Full barrier before executing the next test.  This		 * ensures that client and server are executing the

Lines 1–500 of 954.