xref: /openbmc/linux/tools/testing/vsock/util.c (revision 2f65b44e)
1df7e0e0dSStefan Hajnoczi // SPDX-License-Identifier: GPL-2.0-only
2df7e0e0dSStefan Hajnoczi /*
3df7e0e0dSStefan Hajnoczi  * vsock test utilities
4df7e0e0dSStefan Hajnoczi  *
5df7e0e0dSStefan Hajnoczi  * Copyright (C) 2017 Red Hat, Inc.
6df7e0e0dSStefan Hajnoczi  *
7df7e0e0dSStefan Hajnoczi  * Author: Stefan Hajnoczi <stefanha@redhat.com>
8df7e0e0dSStefan Hajnoczi  */
9df7e0e0dSStefan Hajnoczi 
10df7e0e0dSStefan Hajnoczi #include <errno.h>
11df7e0e0dSStefan Hajnoczi #include <stdio.h>
12df7e0e0dSStefan Hajnoczi #include <stdlib.h>
13df7e0e0dSStefan Hajnoczi #include <signal.h>
149bb8a29dSStefan Hajnoczi #include <unistd.h>
15df7e0e0dSStefan Hajnoczi 
16df7e0e0dSStefan Hajnoczi #include "timeout.h"
179bb8a29dSStefan Hajnoczi #include "control.h"
18df7e0e0dSStefan Hajnoczi #include "util.h"
19df7e0e0dSStefan Hajnoczi 
20df7e0e0dSStefan Hajnoczi /* Install signal handlers */
21df7e0e0dSStefan Hajnoczi void init_signals(void)
22df7e0e0dSStefan Hajnoczi {
23df7e0e0dSStefan Hajnoczi 	struct sigaction act = {
24df7e0e0dSStefan Hajnoczi 		.sa_handler = sigalrm,
25df7e0e0dSStefan Hajnoczi 	};
26df7e0e0dSStefan Hajnoczi 
27df7e0e0dSStefan Hajnoczi 	sigaction(SIGALRM, &act, NULL);
28df7e0e0dSStefan Hajnoczi 	signal(SIGPIPE, SIG_IGN);
29df7e0e0dSStefan Hajnoczi }
30df7e0e0dSStefan Hajnoczi 
31df7e0e0dSStefan Hajnoczi /* Parse a CID in string representation */
32df7e0e0dSStefan Hajnoczi unsigned int parse_cid(const char *str)
33df7e0e0dSStefan Hajnoczi {
34df7e0e0dSStefan Hajnoczi 	char *endptr = NULL;
35df7e0e0dSStefan Hajnoczi 	unsigned long n;
36df7e0e0dSStefan Hajnoczi 
37df7e0e0dSStefan Hajnoczi 	errno = 0;
38df7e0e0dSStefan Hajnoczi 	n = strtoul(str, &endptr, 10);
39df7e0e0dSStefan Hajnoczi 	if (errno || *endptr != '\0') {
40df7e0e0dSStefan Hajnoczi 		fprintf(stderr, "malformed CID \"%s\"\n", str);
41df7e0e0dSStefan Hajnoczi 		exit(EXIT_FAILURE);
42df7e0e0dSStefan Hajnoczi 	}
43df7e0e0dSStefan Hajnoczi 	return n;
44df7e0e0dSStefan Hajnoczi }
45df7e0e0dSStefan Hajnoczi 
469bb8a29dSStefan Hajnoczi /* Connect to <cid, port> and return the file descriptor. */
479bb8a29dSStefan Hajnoczi int vsock_stream_connect(unsigned int cid, unsigned int port)
489bb8a29dSStefan Hajnoczi {
499bb8a29dSStefan Hajnoczi 	union {
509bb8a29dSStefan Hajnoczi 		struct sockaddr sa;
519bb8a29dSStefan Hajnoczi 		struct sockaddr_vm svm;
529bb8a29dSStefan Hajnoczi 	} addr = {
539bb8a29dSStefan Hajnoczi 		.svm = {
549bb8a29dSStefan Hajnoczi 			.svm_family = AF_VSOCK,
559bb8a29dSStefan Hajnoczi 			.svm_port = port,
569bb8a29dSStefan Hajnoczi 			.svm_cid = cid,
579bb8a29dSStefan Hajnoczi 		},
589bb8a29dSStefan Hajnoczi 	};
599bb8a29dSStefan Hajnoczi 	int ret;
609bb8a29dSStefan Hajnoczi 	int fd;
619bb8a29dSStefan Hajnoczi 
629bb8a29dSStefan Hajnoczi 	control_expectln("LISTENING");
639bb8a29dSStefan Hajnoczi 
649bb8a29dSStefan Hajnoczi 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
659bb8a29dSStefan Hajnoczi 
669bb8a29dSStefan Hajnoczi 	timeout_begin(TIMEOUT);
679bb8a29dSStefan Hajnoczi 	do {
689bb8a29dSStefan Hajnoczi 		ret = connect(fd, &addr.sa, sizeof(addr.svm));
699bb8a29dSStefan Hajnoczi 		timeout_check("connect");
709bb8a29dSStefan Hajnoczi 	} while (ret < 0 && errno == EINTR);
719bb8a29dSStefan Hajnoczi 	timeout_end();
729bb8a29dSStefan Hajnoczi 
739bb8a29dSStefan Hajnoczi 	if (ret < 0) {
749bb8a29dSStefan Hajnoczi 		int old_errno = errno;
759bb8a29dSStefan Hajnoczi 
769bb8a29dSStefan Hajnoczi 		close(fd);
779bb8a29dSStefan Hajnoczi 		fd = -1;
789bb8a29dSStefan Hajnoczi 		errno = old_errno;
799bb8a29dSStefan Hajnoczi 	}
809bb8a29dSStefan Hajnoczi 	return fd;
819bb8a29dSStefan Hajnoczi }
829bb8a29dSStefan Hajnoczi 
839bb8a29dSStefan Hajnoczi /* Listen on <cid, port> and return the first incoming connection.  The remote
849bb8a29dSStefan Hajnoczi  * address is stored to clientaddrp.  clientaddrp may be NULL.
859bb8a29dSStefan Hajnoczi  */
869bb8a29dSStefan Hajnoczi int vsock_stream_accept(unsigned int cid, unsigned int port,
879bb8a29dSStefan Hajnoczi 			struct sockaddr_vm *clientaddrp)
889bb8a29dSStefan Hajnoczi {
899bb8a29dSStefan Hajnoczi 	union {
909bb8a29dSStefan Hajnoczi 		struct sockaddr sa;
919bb8a29dSStefan Hajnoczi 		struct sockaddr_vm svm;
929bb8a29dSStefan Hajnoczi 	} addr = {
939bb8a29dSStefan Hajnoczi 		.svm = {
949bb8a29dSStefan Hajnoczi 			.svm_family = AF_VSOCK,
959bb8a29dSStefan Hajnoczi 			.svm_port = port,
969bb8a29dSStefan Hajnoczi 			.svm_cid = cid,
979bb8a29dSStefan Hajnoczi 		},
989bb8a29dSStefan Hajnoczi 	};
999bb8a29dSStefan Hajnoczi 	union {
1009bb8a29dSStefan Hajnoczi 		struct sockaddr sa;
1019bb8a29dSStefan Hajnoczi 		struct sockaddr_vm svm;
1029bb8a29dSStefan Hajnoczi 	} clientaddr;
1039bb8a29dSStefan Hajnoczi 	socklen_t clientaddr_len = sizeof(clientaddr.svm);
1049bb8a29dSStefan Hajnoczi 	int fd;
1059bb8a29dSStefan Hajnoczi 	int client_fd;
1069bb8a29dSStefan Hajnoczi 	int old_errno;
1079bb8a29dSStefan Hajnoczi 
1089bb8a29dSStefan Hajnoczi 	fd = socket(AF_VSOCK, SOCK_STREAM, 0);
1099bb8a29dSStefan Hajnoczi 
1109bb8a29dSStefan Hajnoczi 	if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) {
1119bb8a29dSStefan Hajnoczi 		perror("bind");
1129bb8a29dSStefan Hajnoczi 		exit(EXIT_FAILURE);
1139bb8a29dSStefan Hajnoczi 	}
1149bb8a29dSStefan Hajnoczi 
1159bb8a29dSStefan Hajnoczi 	if (listen(fd, 1) < 0) {
1169bb8a29dSStefan Hajnoczi 		perror("listen");
1179bb8a29dSStefan Hajnoczi 		exit(EXIT_FAILURE);
1189bb8a29dSStefan Hajnoczi 	}
1199bb8a29dSStefan Hajnoczi 
1209bb8a29dSStefan Hajnoczi 	control_writeln("LISTENING");
1219bb8a29dSStefan Hajnoczi 
1229bb8a29dSStefan Hajnoczi 	timeout_begin(TIMEOUT);
1239bb8a29dSStefan Hajnoczi 	do {
1249bb8a29dSStefan Hajnoczi 		client_fd = accept(fd, &clientaddr.sa, &clientaddr_len);
1259bb8a29dSStefan Hajnoczi 		timeout_check("accept");
1269bb8a29dSStefan Hajnoczi 	} while (client_fd < 0 && errno == EINTR);
1279bb8a29dSStefan Hajnoczi 	timeout_end();
1289bb8a29dSStefan Hajnoczi 
1299bb8a29dSStefan Hajnoczi 	old_errno = errno;
1309bb8a29dSStefan Hajnoczi 	close(fd);
1319bb8a29dSStefan Hajnoczi 	errno = old_errno;
1329bb8a29dSStefan Hajnoczi 
1339bb8a29dSStefan Hajnoczi 	if (client_fd < 0)
1349bb8a29dSStefan Hajnoczi 		return client_fd;
1359bb8a29dSStefan Hajnoczi 
1369bb8a29dSStefan Hajnoczi 	if (clientaddr_len != sizeof(clientaddr.svm)) {
1379bb8a29dSStefan Hajnoczi 		fprintf(stderr, "unexpected addrlen from accept(2), %zu\n",
1389bb8a29dSStefan Hajnoczi 			(size_t)clientaddr_len);
1399bb8a29dSStefan Hajnoczi 		exit(EXIT_FAILURE);
1409bb8a29dSStefan Hajnoczi 	}
1419bb8a29dSStefan Hajnoczi 	if (clientaddr.sa.sa_family != AF_VSOCK) {
1429bb8a29dSStefan Hajnoczi 		fprintf(stderr, "expected AF_VSOCK from accept(2), got %d\n",
1439bb8a29dSStefan Hajnoczi 			clientaddr.sa.sa_family);
1449bb8a29dSStefan Hajnoczi 		exit(EXIT_FAILURE);
1459bb8a29dSStefan Hajnoczi 	}
1469bb8a29dSStefan Hajnoczi 
1479bb8a29dSStefan Hajnoczi 	if (clientaddrp)
1489bb8a29dSStefan Hajnoczi 		*clientaddrp = clientaddr.svm;
1499bb8a29dSStefan Hajnoczi 	return client_fd;
1509bb8a29dSStefan Hajnoczi }
1519bb8a29dSStefan Hajnoczi 
152df7e0e0dSStefan Hajnoczi /* Run test cases.  The program terminates if a failure occurs. */
153df7e0e0dSStefan Hajnoczi void run_tests(const struct test_case *test_cases,
154df7e0e0dSStefan Hajnoczi 	       const struct test_opts *opts)
155df7e0e0dSStefan Hajnoczi {
156df7e0e0dSStefan Hajnoczi 	int i;
157df7e0e0dSStefan Hajnoczi 
158df7e0e0dSStefan Hajnoczi 	for (i = 0; test_cases[i].name; i++) {
159df7e0e0dSStefan Hajnoczi 		void (*run)(const struct test_opts *opts);
160df7e0e0dSStefan Hajnoczi 
161df7e0e0dSStefan Hajnoczi 		printf("%s...", test_cases[i].name);
162df7e0e0dSStefan Hajnoczi 		fflush(stdout);
163df7e0e0dSStefan Hajnoczi 
1642f65b44eSStefan Hajnoczi 		if (opts->mode == TEST_MODE_CLIENT) {
1652f65b44eSStefan Hajnoczi 			/* Full barrier before executing the next test.  This
1662f65b44eSStefan Hajnoczi 			 * ensures that client and server are executing the
1672f65b44eSStefan Hajnoczi 			 * same test case.  In particular, it means whoever is
1682f65b44eSStefan Hajnoczi 			 * faster will not see the peer still executing the
1692f65b44eSStefan Hajnoczi 			 * last test.  This is important because port numbers
1702f65b44eSStefan Hajnoczi 			 * can be used by multiple test cases.
1712f65b44eSStefan Hajnoczi 			 */
1722f65b44eSStefan Hajnoczi 			control_expectln("NEXT");
1732f65b44eSStefan Hajnoczi 			control_writeln("NEXT");
1742f65b44eSStefan Hajnoczi 
175df7e0e0dSStefan Hajnoczi 			run = test_cases[i].run_client;
1762f65b44eSStefan Hajnoczi 		} else {
1772f65b44eSStefan Hajnoczi 			control_writeln("NEXT");
1782f65b44eSStefan Hajnoczi 			control_expectln("NEXT");
1792f65b44eSStefan Hajnoczi 
180df7e0e0dSStefan Hajnoczi 			run = test_cases[i].run_server;
1812f65b44eSStefan Hajnoczi 		}
182df7e0e0dSStefan Hajnoczi 
183df7e0e0dSStefan Hajnoczi 		if (run)
184df7e0e0dSStefan Hajnoczi 			run(opts);
185df7e0e0dSStefan Hajnoczi 
186df7e0e0dSStefan Hajnoczi 		printf("ok\n");
187df7e0e0dSStefan Hajnoczi 	}
188df7e0e0dSStefan Hajnoczi }
189