brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · a3d448a Raw
654 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * vsock test utilities4 *5 * Copyright (C) 2017 Red Hat, Inc.6 *7 * Author: Stefan Hajnoczi <stefanha@redhat.com>8 */9 10#include <errno.h>11#include <stdio.h>12#include <stdint.h>13#include <stdlib.h>14#include <string.h>15#include <signal.h>16#include <unistd.h>17#include <assert.h>18#include <sys/epoll.h>19#include <sys/mman.h>20 21#include "timeout.h"22#include "control.h"23#include "util.h"24 25/* Install signal handlers */26void init_signals(void)27{28	struct sigaction act = {29		.sa_handler = sigalrm,30	};31 32	sigaction(SIGALRM, &act, NULL);33	signal(SIGPIPE, SIG_IGN);34}35 36static unsigned int parse_uint(const char *str, const char *err_str)37{38	char *endptr = NULL;39	unsigned long n;40 41	errno = 0;42	n = strtoul(str, &endptr, 10);43	if (errno || *endptr != '\0') {44		fprintf(stderr, "malformed %s \"%s\"\n", err_str, str);45		exit(EXIT_FAILURE);46	}47	return n;48}49 50/* Parse a CID in string representation */51unsigned int parse_cid(const char *str)52{53	return parse_uint(str, "CID");54}55 56/* Parse a port in string representation */57unsigned int parse_port(const char *str)58{59	return parse_uint(str, "port");60}61 62/* Wait for the remote to close the connection */63void vsock_wait_remote_close(int fd)64{65	struct epoll_event ev;66	int epollfd, nfds;67 68	epollfd = epoll_create1(0);69	if (epollfd == -1) {70		perror("epoll_create1");71		exit(EXIT_FAILURE);72	}73 74	ev.events = EPOLLRDHUP | EPOLLHUP;75	ev.data.fd = fd;76	if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {77		perror("epoll_ctl");78		exit(EXIT_FAILURE);79	}80 81	nfds = epoll_wait(epollfd, &ev, 1, TIMEOUT * 1000);82	if (nfds == -1) {83		perror("epoll_wait");84		exit(EXIT_FAILURE);85	}86 87	if (nfds == 0) {88		fprintf(stderr, "epoll_wait timed out\n");89		exit(EXIT_FAILURE);90	}91 92	assert(nfds == 1);93	assert(ev.events & (EPOLLRDHUP | EPOLLHUP));94	assert(ev.data.fd == fd);95 96	close(epollfd);97}98 99/* Bind to <bind_port>, connect to <cid, port> and return the file descriptor. */100int vsock_bind_connect(unsigned int cid, unsigned int port, unsigned int bind_port, int type)101{102	struct sockaddr_vm sa_client = {103		.svm_family = AF_VSOCK,104		.svm_cid = VMADDR_CID_ANY,105		.svm_port = bind_port,106	};107	struct sockaddr_vm sa_server = {108		.svm_family = AF_VSOCK,109		.svm_cid = cid,110		.svm_port = port,111	};112 113	int client_fd, ret;114 115	client_fd = socket(AF_VSOCK, type, 0);116	if (client_fd < 0) {117		perror("socket");118		exit(EXIT_FAILURE);119	}120 121	if (bind(client_fd, (struct sockaddr *)&sa_client, sizeof(sa_client))) {122		perror("bind");123		exit(EXIT_FAILURE);124	}125 126	timeout_begin(TIMEOUT);127	do {128		ret = connect(client_fd, (struct sockaddr *)&sa_server, sizeof(sa_server));129		timeout_check("connect");130	} while (ret < 0 && errno == EINTR);131	timeout_end();132 133	if (ret < 0) {134		perror("connect");135		exit(EXIT_FAILURE);136	}137 138	return client_fd;139}140 141/* Connect to <cid, port> and return the file descriptor. */142int vsock_connect(unsigned int cid, unsigned int port, int type)143{144	union {145		struct sockaddr sa;146		struct sockaddr_vm svm;147	} addr = {148		.svm = {149			.svm_family = AF_VSOCK,150			.svm_port = port,151			.svm_cid = cid,152		},153	};154	int ret;155	int fd;156 157	control_expectln("LISTENING");158 159	fd = socket(AF_VSOCK, type, 0);160	if (fd < 0) {161		perror("socket");162		exit(EXIT_FAILURE);163	}164 165	timeout_begin(TIMEOUT);166	do {167		ret = connect(fd, &addr.sa, sizeof(addr.svm));168		timeout_check("connect");169	} while (ret < 0 && errno == EINTR);170	timeout_end();171 172	if (ret < 0) {173		int old_errno = errno;174 175		close(fd);176		fd = -1;177		errno = old_errno;178	}179	return fd;180}181 182int vsock_stream_connect(unsigned int cid, unsigned int port)183{184	return vsock_connect(cid, port, SOCK_STREAM);185}186 187int vsock_seqpacket_connect(unsigned int cid, unsigned int port)188{189	return vsock_connect(cid, port, SOCK_SEQPACKET);190}191 192/* Listen on <cid, port> and return the file descriptor. */193static int vsock_listen(unsigned int cid, unsigned int port, int type)194{195	union {196		struct sockaddr sa;197		struct sockaddr_vm svm;198	} addr = {199		.svm = {200			.svm_family = AF_VSOCK,201			.svm_port = port,202			.svm_cid = cid,203		},204	};205	int fd;206 207	fd = socket(AF_VSOCK, type, 0);208	if (fd < 0) {209		perror("socket");210		exit(EXIT_FAILURE);211	}212 213	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {214		perror("bind");215		exit(EXIT_FAILURE);216	}217 218	if (listen(fd, 1) < 0) {219		perror("listen");220		exit(EXIT_FAILURE);221	}222 223	return fd;224}225 226/* Listen on <cid, port> and return the first incoming connection.  The remote227 * address is stored to clientaddrp.  clientaddrp may be NULL.228 */229int vsock_accept(unsigned int cid, unsigned int port,230		 struct sockaddr_vm *clientaddrp, int type)231{232	union {233		struct sockaddr sa;234		struct sockaddr_vm svm;235	} clientaddr;236	socklen_t clientaddr_len = sizeof(clientaddr.svm);237	int fd, client_fd, old_errno;238 239	fd = vsock_listen(cid, port, type);240 241	control_writeln("LISTENING");242 243	timeout_begin(TIMEOUT);244	do {245		client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);246		timeout_check("accept");247	} while (client_fd < 0 && errno == EINTR);248	timeout_end();249 250	old_errno = errno;251	close(fd);252	errno = old_errno;253 254	if (client_fd < 0)255		return client_fd;256 257	if (clientaddr_len != sizeof(clientaddr.svm)) {258		fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",259			(size_t)clientaddr_len);260		exit(EXIT_FAILURE);261	}262	if (clientaddr.sa.sa_family != AF_VSOCK) {263		fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",264			clientaddr.sa.sa_family);265		exit(EXIT_FAILURE);266	}267 268	if (clientaddrp)269		*clientaddrp = clientaddr.svm;270	return client_fd;271}272 273int vsock_stream_accept(unsigned int cid, unsigned int port,274			struct sockaddr_vm *clientaddrp)275{276	return vsock_accept(cid, port, clientaddrp, SOCK_STREAM);277}278 279int vsock_stream_listen(unsigned int cid, unsigned int port)280{281	return vsock_listen(cid, port, SOCK_STREAM);282}283 284int vsock_seqpacket_accept(unsigned int cid, unsigned int port,285			   struct sockaddr_vm *clientaddrp)286{287	return vsock_accept(cid, port, clientaddrp, SOCK_SEQPACKET);288}289 290/* Transmit bytes from a buffer and check the return value.291 *292 * expected_ret:293 *  <0 Negative errno (for testing errors)294 *   0 End-of-file295 *  >0 Success (bytes successfully written)296 */297void send_buf(int fd, const void *buf, size_t len, int flags,298	      ssize_t expected_ret)299{300	ssize_t nwritten = 0;301	ssize_t ret;302 303	timeout_begin(TIMEOUT);304	do {305		ret = send(fd, buf + nwritten, len - nwritten, flags);306		timeout_check("send");307 308		if (ret == 0 || (ret < 0 && errno != EINTR))309			break;310 311		nwritten += ret;312	} while (nwritten < len);313	timeout_end();314 315	if (expected_ret < 0) {316		if (ret != -1) {317			fprintf(stderr, "bogus send(2) return value %zd (expected %zd)\n",318				ret, expected_ret);319			exit(EXIT_FAILURE);320		}321		if (errno != -expected_ret) {322			perror("send");323			exit(EXIT_FAILURE);324		}325		return;326	}327 328	if (ret < 0) {329		perror("send");330		exit(EXIT_FAILURE);331	}332 333	if (nwritten != expected_ret) {334		if (ret == 0)335			fprintf(stderr, "unexpected EOF while sending bytes\n");336 337		fprintf(stderr, "bogus send(2) bytes written %zd (expected %zd)\n",338			nwritten, expected_ret);339		exit(EXIT_FAILURE);340	}341}342 343/* Receive bytes in a buffer and check the return value.344 *345 * expected_ret:346 *  <0 Negative errno (for testing errors)347 *   0 End-of-file348 *  >0 Success (bytes successfully read)349 */350void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret)351{352	ssize_t nread = 0;353	ssize_t ret;354 355	timeout_begin(TIMEOUT);356	do {357		ret = recv(fd, buf + nread, len - nread, flags);358		timeout_check("recv");359 360		if (ret == 0 || (ret < 0 && errno != EINTR))361			break;362 363		nread += ret;364	} while (nread < len);365	timeout_end();366 367	if (expected_ret < 0) {368		if (ret != -1) {369			fprintf(stderr, "bogus recv(2) return value %zd (expected %zd)\n",370				ret, expected_ret);371			exit(EXIT_FAILURE);372		}373		if (errno != -expected_ret) {374			perror("recv");375			exit(EXIT_FAILURE);376		}377		return;378	}379 380	if (ret < 0) {381		perror("recv");382		exit(EXIT_FAILURE);383	}384 385	if (nread != expected_ret) {386		if (ret == 0)387			fprintf(stderr, "unexpected EOF while receiving bytes\n");388 389		fprintf(stderr, "bogus recv(2) bytes read %zd (expected %zd)\n",390			nread, expected_ret);391		exit(EXIT_FAILURE);392	}393}394 395/* Transmit one byte and check the return value.396 *397 * expected_ret:398 *  <0 Negative errno (for testing errors)399 *   0 End-of-file400 *   1 Success401 */402void send_byte(int fd, int expected_ret, int flags)403{404	const uint8_t byte = 'A';405 406	send_buf(fd, &byte, sizeof(byte), flags, expected_ret);407}408 409/* Receive one byte and check the return value.410 *411 * expected_ret:412 *  <0 Negative errno (for testing errors)413 *   0 End-of-file414 *   1 Success415 */416void recv_byte(int fd, int expected_ret, int flags)417{418	uint8_t byte;419 420	recv_buf(fd, &byte, sizeof(byte), flags, expected_ret);421 422	if (byte != 'A') {423		fprintf(stderr, "unexpected byte read %c\n", byte);424		exit(EXIT_FAILURE);425	}426}427 428/* Run test cases.  The program terminates if a failure occurs. */429void run_tests(const struct test_case *test_cases,430	       const struct test_opts *opts)431{432	int i;433 434	for (i = 0; test_cases[i].name; i++) {435		void (*run)(const struct test_opts *opts);436		char *line;437 438		printf("%d - %s...", i, test_cases[i].name);439		fflush(stdout);440 441		/* Full barrier before executing the next test.  This442		 * ensures that client and server are executing the443		 * same test case.  In particular, it means whoever is444		 * faster will not see the peer still executing the445		 * last test.  This is important because port numbers446		 * can be used by multiple test cases.447		 */448		if (test_cases[i].skip)449			control_writeln("SKIP");450		else451			control_writeln("NEXT");452 453		line = control_readln();454		if (control_cmpln(line, "SKIP", false) || test_cases[i].skip) {455 456			printf("skipped\n");457 458			free(line);459			continue;460		}461 462		control_cmpln(line, "NEXT", true);463		free(line);464 465		if (opts->mode == TEST_MODE_CLIENT)466			run = test_cases[i].run_client;467		else468			run = test_cases[i].run_server;469 470		if (run)471			run(opts);472 473		printf("ok\n");474	}475}476 477void list_tests(const struct test_case *test_cases)478{479	int i;480 481	printf("ID\tTest name\n");482 483	for (i = 0; test_cases[i].name; i++)484		printf("%d\t%s\n", i, test_cases[i].name);485 486	exit(EXIT_FAILURE);487}488 489void skip_test(struct test_case *test_cases, size_t test_cases_len,490	       const char *test_id_str)491{492	unsigned long test_id;493	char *endptr = NULL;494 495	errno = 0;496	test_id = strtoul(test_id_str, &endptr, 10);497	if (errno || *endptr != '\0') {498		fprintf(stderr, "malformed test ID \"%s\"\n", test_id_str);499		exit(EXIT_FAILURE);500	}501 502	if (test_id >= test_cases_len) {503		fprintf(stderr, "test ID (%lu) larger than the max allowed (%lu)\n",504			test_id, test_cases_len - 1);505		exit(EXIT_FAILURE);506	}507 508	test_cases[test_id].skip = true;509}510 511unsigned long hash_djb2(const void *data, size_t len)512{513	unsigned long hash = 5381;514	int i = 0;515 516	while (i < len) {517		hash = ((hash << 5) + hash) + ((unsigned char *)data)[i];518		i++;519	}520 521	return hash;522}523 524size_t iovec_bytes(const struct iovec *iov, size_t iovnum)525{526	size_t bytes;527	int i;528 529	for (bytes = 0, i = 0; i < iovnum; i++)530		bytes += iov[i].iov_len;531 532	return bytes;533}534 535unsigned long iovec_hash_djb2(const struct iovec *iov, size_t iovnum)536{537	unsigned long hash;538	size_t iov_bytes;539	size_t offs;540	void *tmp;541	int i;542 543	iov_bytes = iovec_bytes(iov, iovnum);544 545	tmp = malloc(iov_bytes);546	if (!tmp) {547		perror("malloc");548		exit(EXIT_FAILURE);549	}550 551	for (offs = 0, i = 0; i < iovnum; i++) {552		memcpy(tmp + offs, iov[i].iov_base, iov[i].iov_len);553		offs += iov[i].iov_len;554	}555 556	hash = hash_djb2(tmp, iov_bytes);557	free(tmp);558 559	return hash;560}561 562/* Allocates and returns new 'struct iovec *' according pattern563 * in the 'test_iovec'. For each element in the 'test_iovec' it564 * allocates new element in the resulting 'iovec'. 'iov_len'565 * of the new element is copied from 'test_iovec'. 'iov_base' is566 * allocated depending on the 'iov_base' of 'test_iovec':567 *568 * 'iov_base' == NULL -> valid buf: mmap('iov_len').569 *570 * 'iov_base' == MAP_FAILED -> invalid buf:571 *               mmap('iov_len'), then munmap('iov_len').572 *               'iov_base' still contains result of573 *               mmap().574 *575 * 'iov_base' == number -> unaligned valid buf:576 *               mmap('iov_len') + number.577 *578 * 'iovnum' is number of elements in 'test_iovec'.579 *580 * Returns new 'iovec' or calls 'exit()' on error.581 */582struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum)583{584	struct iovec *iovec;585	int i;586 587	iovec = malloc(sizeof(*iovec) * iovnum);588	if (!iovec) {589		perror("malloc");590		exit(EXIT_FAILURE);591	}592 593	for (i = 0; i < iovnum; i++) {594		iovec[i].iov_len = test_iovec[i].iov_len;595 596		iovec[i].iov_base = mmap(NULL, iovec[i].iov_len,597					 PROT_READ | PROT_WRITE,598					 MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE,599					 -1, 0);600		if (iovec[i].iov_base == MAP_FAILED) {601			perror("mmap");602			exit(EXIT_FAILURE);603		}604 605		if (test_iovec[i].iov_base != MAP_FAILED)606			iovec[i].iov_base += (uintptr_t)test_iovec[i].iov_base;607	}608 609	/* Unmap "invalid" elements. */610	for (i = 0; i < iovnum; i++) {611		if (test_iovec[i].iov_base == MAP_FAILED) {612			if (munmap(iovec[i].iov_base, iovec[i].iov_len)) {613				perror("munmap");614				exit(EXIT_FAILURE);615			}616		}617	}618 619	for (i = 0; i < iovnum; i++) {620		int j;621 622		if (test_iovec[i].iov_base == MAP_FAILED)623			continue;624 625		for (j = 0; j < iovec[i].iov_len; j++)626			((uint8_t *)iovec[i].iov_base)[j] = rand() & 0xff;627	}628 629	return iovec;630}631 632/* Frees 'iovec *', previously allocated by 'alloc_test_iovec()'.633 * On error calls 'exit()'.634 */635void free_test_iovec(const struct iovec *test_iovec,636		     struct iovec *iovec, int iovnum)637{638	int i;639 640	for (i = 0; i < iovnum; i++) {641		if (test_iovec[i].iov_base != MAP_FAILED) {642			if (test_iovec[i].iov_base)643				iovec[i].iov_base -= (uintptr_t)test_iovec[i].iov_base;644 645			if (munmap(iovec[i].iov_base, iovec[i].iov_len)) {646				perror("munmap");647				exit(EXIT_FAILURE);648			}649		}650	}651 652	free(iovec);653}654