diff options
Diffstat (limited to 'tools/testing/vsock/util.c')
-rw-r--r-- | tools/testing/vsock/util.c | 344 |
1 files changed, 288 insertions, 56 deletions
diff --git a/tools/testing/vsock/util.c b/tools/testing/vsock/util.c index 01b636d303..ae2b33c21c 100644 --- a/tools/testing/vsock/util.c +++ b/tools/testing/vsock/util.c @@ -11,10 +11,12 @@ #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/mman.h> #include "timeout.h" #include "control.h" @@ -83,6 +85,48 @@ void vsock_wait_remote_close(int fd) close(epollfd); } +/* 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) +{ + struct sockaddr_vm sa_client = { + .svm_family = AF_VSOCK, + .svm_cid = VMADDR_CID_ANY, + .svm_port = bind_port, + }; + struct sockaddr_vm sa_server = { + .svm_family = AF_VSOCK, + .svm_cid = cid, + .svm_port = port, + }; + + int client_fd, ret; + + client_fd = socket(AF_VSOCK, type, 0); + if (client_fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } + + if (bind(client_fd, (struct sockaddr *)&sa_client, sizeof(sa_client))) { + perror("bind"); + exit(EXIT_FAILURE); + } + + timeout_begin(TIMEOUT); + do { + ret = connect(client_fd, (struct sockaddr *)&sa_server, sizeof(sa_server)); + timeout_check("connect"); + } while (ret < 0 && errno == EINTR); + timeout_end(); + + if (ret < 0) { + perror("connect"); + exit(EXIT_FAILURE); + } + + return client_fd; +} + /* Connect to <cid, port> and return the file descriptor. */ static int vsock_connect(unsigned int cid, unsigned int port, int type) { @@ -102,6 +146,10 @@ static int vsock_connect(unsigned int cid, unsigned int port, int type) control_expectln("LISTENING"); fd = socket(AF_VSOCK, type, 0); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } timeout_begin(TIMEOUT); do { @@ -130,11 +178,8 @@ int vsock_seqpacket_connect(unsigned int cid, unsigned int port) return vsock_connect(cid, port, SOCK_SEQPACKET); } -/* Listen on <cid, port> and return the first incoming connection. The remote - * address is stored to clientaddrp. clientaddrp may be NULL. - */ -static int vsock_accept(unsigned int cid, unsigned int port, - struct sockaddr_vm *clientaddrp, int type) +/* Listen on <cid, port> and return the file descriptor. */ +static int vsock_listen(unsigned int cid, unsigned int port, int type) { union { struct sockaddr sa; @@ -146,16 +191,13 @@ static int vsock_accept(unsigned int cid, unsigned int port, .svm_cid = cid, }, }; - union { - struct sockaddr sa; - struct sockaddr_vm svm; - } clientaddr; - socklen_t clientaddr_len = sizeof(clientaddr.svm); int fd; - int client_fd; - int old_errno; fd = socket(AF_VSOCK, type, 0); + if (fd < 0) { + perror("socket"); + exit(EXIT_FAILURE); + } if (bind(fd, &addr.sa, sizeof(addr.svm)) < 0) { perror("bind"); @@ -167,6 +209,24 @@ static int vsock_accept(unsigned int cid, unsigned int port, 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. + */ +static 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); @@ -205,108 +265,149 @@ int vsock_stream_accept(unsigned int cid, unsigned int port, 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 one byte and check the return value. +/* Transmit bytes from a buffer and check the return value. * * expected_ret: * <0 Negative errno (for testing errors) * 0 End-of-file - * 1 Success + * >0 Success (bytes successfully written) */ -void send_byte(int fd, int expected_ret, int flags) +void send_buf(int fd, const void *buf, size_t len, int flags, + ssize_t expected_ret) { - const uint8_t byte = 'A'; - ssize_t nwritten; + ssize_t nwritten = 0; + ssize_t ret; timeout_begin(TIMEOUT); do { - nwritten = send(fd, &byte, sizeof(byte), flags); - timeout_check("write"); - } while (nwritten < 0 && errno == EINTR); + ret = send(fd, buf + nwritten, len - nwritten, flags); + timeout_check("send"); + + if (ret == 0 || (ret < 0 && errno != EINTR)) + break; + + nwritten += ret; + } while (nwritten < len); timeout_end(); if (expected_ret < 0) { - if (nwritten != -1) { - fprintf(stderr, "bogus send(2) return value %zd\n", - nwritten); + 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("write"); + perror("send"); exit(EXIT_FAILURE); } return; } - if (nwritten < 0) { - perror("write"); + if (ret < 0) { + perror("send"); exit(EXIT_FAILURE); } - if (nwritten == 0) { - if (expected_ret == 0) - return; - fprintf(stderr, "unexpected EOF while sending byte\n"); - exit(EXIT_FAILURE); - } - if (nwritten != sizeof(byte)) { - fprintf(stderr, "bogus send(2) return value %zd\n", nwritten); + 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); } } -/* Receive one byte and check the return value. +/* Receive bytes in a buffer and check the return value. * * expected_ret: * <0 Negative errno (for testing errors) * 0 End-of-file - * 1 Success + * >0 Success (bytes successfully read) */ -void recv_byte(int fd, int expected_ret, int flags) +void recv_buf(int fd, void *buf, size_t len, int flags, ssize_t expected_ret) { - uint8_t byte; - ssize_t nread; + ssize_t nread = 0; + ssize_t ret; timeout_begin(TIMEOUT); do { - nread = recv(fd, &byte, sizeof(byte), flags); - timeout_check("read"); - } while (nread < 0 && errno == EINTR); + ret = recv(fd, buf + nread, len - nread, flags); + timeout_check("recv"); + + if (ret == 0 || (ret < 0 && errno != EINTR)) + break; + + nread += ret; + } while (nread < len); timeout_end(); if (expected_ret < 0) { - if (nread != -1) { - fprintf(stderr, "bogus recv(2) return value %zd\n", - nread); + 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("read"); + perror("recv"); exit(EXIT_FAILURE); } return; } - if (nread < 0) { - perror("read"); + if (ret < 0) { + perror("recv"); exit(EXIT_FAILURE); } - if (nread == 0) { - if (expected_ret == 0) - return; - fprintf(stderr, "unexpected EOF while receiving byte\n"); - exit(EXIT_FAILURE); - } - if (nread != sizeof(byte)) { - fprintf(stderr, "bogus recv(2) return value %zd\n", nread); + 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) +{ + 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 %c\n", byte); exit(EXIT_FAILURE); @@ -408,3 +509,134 @@ unsigned long hash_djb2(const void *data, size_t len) return hash; } + +size_t iovec_bytes(const struct iovec *iov, size_t iovnum) +{ + size_t bytes; + int i; + + for (bytes = 0, i = 0; i < iovnum; i++) + bytes += iov[i].iov_len; + + return bytes; +} + +unsigned long iovec_hash_djb2(const struct iovec *iov, size_t iovnum) +{ + unsigned long hash; + size_t iov_bytes; + size_t offs; + void *tmp; + int i; + + iov_bytes = iovec_bytes(iov, iovnum); + + tmp = malloc(iov_bytes); + if (!tmp) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + for (offs = 0, i = 0; i < iovnum; i++) { + memcpy(tmp + offs, iov[i].iov_base, iov[i].iov_len); + offs += iov[i].iov_len; + } + + hash = hash_djb2(tmp, iov_bytes); + free(tmp); + + return hash; +} + +/* Allocates and returns new 'struct iovec *' according pattern + * in the 'test_iovec'. For each element in the 'test_iovec' it + * allocates new element in the resulting 'iovec'. 'iov_len' + * of the new element is copied from 'test_iovec'. 'iov_base' is + * allocated depending on the 'iov_base' of 'test_iovec': + * + * 'iov_base' == NULL -> valid buf: mmap('iov_len'). + * + * 'iov_base' == MAP_FAILED -> invalid buf: + * mmap('iov_len'), then munmap('iov_len'). + * 'iov_base' still contains result of + * mmap(). + * + * 'iov_base' == number -> unaligned valid buf: + * mmap('iov_len') + number. + * + * 'iovnum' is number of elements in 'test_iovec'. + * + * Returns new 'iovec' or calls 'exit()' on error. + */ +struct iovec *alloc_test_iovec(const struct iovec *test_iovec, int iovnum) +{ + struct iovec *iovec; + int i; + + iovec = malloc(sizeof(*iovec) * iovnum); + if (!iovec) { + perror("malloc"); + exit(EXIT_FAILURE); + } + + for (i = 0; i < iovnum; i++) { + iovec[i].iov_len = test_iovec[i].iov_len; + + iovec[i].iov_base = mmap(NULL, iovec[i].iov_len, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, + -1, 0); + if (iovec[i].iov_base == MAP_FAILED) { + perror("mmap"); + exit(EXIT_FAILURE); + } + + if (test_iovec[i].iov_base != MAP_FAILED) + iovec[i].iov_base += (uintptr_t)test_iovec[i].iov_base; + } + + /* Unmap "invalid" elements. */ + for (i = 0; i < iovnum; i++) { + if (test_iovec[i].iov_base == MAP_FAILED) { + if (munmap(iovec[i].iov_base, iovec[i].iov_len)) { + perror("munmap"); + exit(EXIT_FAILURE); + } + } + } + + for (i = 0; i < iovnum; i++) { + int j; + + if (test_iovec[i].iov_base == MAP_FAILED) + continue; + + for (j = 0; j < iovec[i].iov_len; j++) + ((uint8_t *)iovec[i].iov_base)[j] = rand() & 0xff; + } + + return iovec; +} + +/* Frees 'iovec *', previously allocated by 'alloc_test_iovec()'. + * On error calls 'exit()'. + */ +void free_test_iovec(const struct iovec *test_iovec, + struct iovec *iovec, int iovnum) +{ + int i; + + for (i = 0; i < iovnum; i++) { + if (test_iovec[i].iov_base != MAP_FAILED) { + if (test_iovec[i].iov_base) + iovec[i].iov_base -= (uintptr_t)test_iovec[i].iov_base; + + if (munmap(iovec[i].iov_base, iovec[i].iov_len)) { + perror("munmap"); + exit(EXIT_FAILURE); + } + } + } + + free(iovec); +} |