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