diff options
author | asomers <asomers@FreeBSD.org> | 2014-02-13 22:19:24 +0000 |
---|---|---|
committer | asomers <asomers@FreeBSD.org> | 2014-02-13 22:19:24 +0000 |
commit | 999d9343fa00efb4ad9b30bd96bfaaac0abe2afe (patch) | |
tree | c6ab4b2c129b21791ff389a036e5fbff65e99500 /tests/sys | |
parent | ccd36e4300ee156bc8af97fd65e1be79cec892e0 (diff) | |
download | FreeBSD-src-999d9343fa00efb4ad9b30bd96bfaaac0abe2afe.zip FreeBSD-src-999d9343fa00efb4ad9b30bd96bfaaac0abe2afe.tar.gz |
MFC r261081, r261133, and r261615
r261081
Replace the old unix_seqpacket and unix_seqpacket_exercise
tests, which were a little broken and not automatable, with
unix_seqpacket_test. It's coverage is a superset of the old
tests and it uses ATF. It includes test cases for bugs
kern/185813 and kern/185812.
r261133
Fix the Makefiles so that the tests I submitted in r261081 will
actually get built.
r261615
tests/sys/Makefile
use TESTS_SUBDIRS for kern instead of SUBDIRS. I don't
think it makes a difference in this case, but
TESTS_SUBDIRS is generally correct for subdirectories
that contain tests.
Diffstat (limited to 'tests/sys')
-rw-r--r-- | tests/sys/Makefile | 12 | ||||
-rw-r--r-- | tests/sys/kern/Makefile | 10 | ||||
-rw-r--r-- | tests/sys/kern/unix_seqpacket_test.c | 1117 |
3 files changed, 1139 insertions, 0 deletions
diff --git a/tests/sys/Makefile b/tests/sys/Makefile new file mode 100644 index 0000000..ea8b3c4 --- /dev/null +++ b/tests/sys/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +.PATH: ${.CURDIR}/.. + +TESTS_SUBDIRS+= kern +TESTSDIR= ${TESTSBASE}/sys + +KYUAFILE= yes + +.include <bsd.test.mk> diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile new file mode 100644 index 0000000..576e7d2 --- /dev/null +++ b/tests/sys/kern/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/kern + +ATF_TESTS_C= unix_seqpacket_test +TEST_METADATA.unix_seqpacket_test+= timeout="15" + +LDADD+= -lpthread + +.include <atf.test.mk> diff --git a/tests/sys/kern/unix_seqpacket_test.c b/tests/sys/kern/unix_seqpacket_test.c new file mode 100644 index 0000000..5b8632d --- /dev/null +++ b/tests/sys/kern/unix_seqpacket_test.c @@ -0,0 +1,1117 @@ +/*- + * Copyright (c) 2014 Spectra Logic Corporation. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +#include <sys/socket.h> +#include <sys/un.h> + +#include <stdio.h> + +#include <atf-c.h> + +/* + * Helper functions + */ + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +void +do_socketpair(int *sv) +{ + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_REQUIRE_EQ(0, s); + ATF_REQUIRE(sv[0] >= 0); + ATF_REQUIRE(sv[1] >= 0); + ATF_REQUIRE(sv[0] != sv[1]); +} + +void +do_socketpair_nonblocking(int *sv) +{ + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_REQUIRE_EQ(0, s); + ATF_REQUIRE(sv[0] >= 0); + ATF_REQUIRE(sv[1] >= 0); + ATF_REQUIRE(sv[0] != sv[1]); + ATF_REQUIRE(-1 != fcntl(sv[0], F_SETFL, O_NONBLOCK)); + ATF_REQUIRE(-1 != fcntl(sv[1], F_SETFL, O_NONBLOCK)); +} + +/* + * Returns a pair of sockets made the hard way: bind, listen, connect & accept + * @return const char* The path to the socket + */ +const char* +mk_pair_of_sockets(int *sv) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, err, s2, s1; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + err = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + err = listen(s, -1); + ATF_CHECK_EQ(0, err); + ATF_CHECK_EQ(0, err); + + /* Create the other socket */ + s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s2 >= 0); + err = connect(s2, (struct sockaddr*)&sun, sizeof(sun)); + if (err != 0) { + perror("connect"); + atf_tc_fail("connect(2) failed"); + } + + /* Accept it */ + s1 = accept(s, NULL, NULL); + if (s1 == -1) { + perror("accept"); + atf_tc_fail("accept(2) failed"); + } + + sv[0] = s1; + sv[1] = s2; + return (path); +} + +static volatile sig_atomic_t got_sigpipe = 0; +static void +shutdown_send_sigpipe_handler(int x) +{ + got_sigpipe = 1; +} + +/* + * Parameterized test function bodies + */ +void +test_eagain(size_t sndbufsize, size_t rcvbufsize) +{ + int i; + int sv[2]; + const size_t totalsize = (sndbufsize + rcvbufsize) * 2; + const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + bzero(sndbuf, pktsize); + /* Send data until we get EAGAIN */ + for(i=0; i < totalsize / pktsize; i++) { + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize == -1) { + if (errno == EAGAIN) + atf_tc_pass(); + else { + perror("send"); + atf_tc_fail("send returned < 0 but not EAGAIN"); + } + } + } + atf_tc_fail("Never got EAGAIN"); +} + +void +test_sendrecv_symmetric_buffers(size_t bufsize, int blocking) { + int s; + int sv[2]; + const size_t pktsize = bufsize / 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + if (blocking) + do_socketpair(sv); + else + do_socketpair_nonblocking(sv); + + /* Setup the buffers */ + s = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); + ATF_REQUIRE_EQ(0, s); + s = setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); + ATF_REQUIRE_EQ(0, s); + + /* Fill the send buffer */ + bzero(sndbuf, pktsize); + + /* send and receive the packet */ + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, ssize, "expected %zd=send(...) but got %zd", + pktsize, ssize); + + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + if (rsize < 0) { + perror("recv"); + atf_tc_fail("recv returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, rsize, "expected %zd=send(...) but got %zd", + pktsize, rsize); +} + +void +test_pipe_simulator(size_t sndbufsize, size_t rcvbufsize) +{ + int s, num_sent, num_received; + int sv[2]; + const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4; + int numpkts; + char sndbuf[pktsize]; + char rcvbuf[pktsize]; + char comparebuf[pktsize]; + ssize_t ssize, rsize; + bool currently_sending = true; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + /* Send a total amount of data comfortably greater than the buffers */ + numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize; + for (num_sent=0, num_received=0; + num_sent < numpkts || num_received < numpkts; ) { + if (currently_sending && num_sent < numpkts) { + /* The simulated sending process */ + /* fill the buffer */ + memset(sndbuf, num_sent, pktsize); + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + /* + * XXX: This is bug-compatible with the kernel. + * The kernel returns EMSGSIZE when it should + * return EAGAIN + */ + if (errno == EAGAIN || errno == EMSGSIZE) + currently_sending = false; + else { + perror("send"); + atf_tc_fail("send failed"); + } + } else { + ATF_CHECK_EQ_MSG(pktsize, ssize, + "expected %zd=send(...) but got %zd", + pktsize, ssize); + num_sent++; + } + } else { + /* The simulated receiving process */ + rsize = recv(sv[1], rcvbuf, pktsize, MSG_WAITALL); + if (rsize < 0) { + if (errno == EAGAIN) { + currently_sending = true; + ATF_REQUIRE_MSG(num_sent < numpkts, + "Packets were lost!"); + } + else { + perror("recv"); + atf_tc_fail("recv failed"); + } + } else { + ATF_CHECK_EQ_MSG(pktsize, rsize, + "expected %zd=recv(...) but got %zd", + pktsize, rsize); + memset(comparebuf, num_received, pktsize); + ATF_CHECK_EQ_MSG(0, memcmp(comparebuf, rcvbuf, + pktsize), + "Received data miscompare"); + num_received++; + } + } + } +} + +typedef struct { + ssize_t pktsize; + int numpkts; + int so; +} test_pipe_thread_data_t; + +static void* +test_pipe_writer(void* args) +{ + test_pipe_thread_data_t* td = args; + char sndbuf[td->pktsize]; + ssize_t ssize; + int i; + + for(i=0; i < td->numpkts; i++) { + memset(sndbuf, i, td->pktsize); + ssize = send(td->so, sndbuf, td->pktsize, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(td->pktsize, ssize, + "expected %zd=send(...) but got %zd", + td->pktsize, ssize); + } + return (0); +} + +static void* +test_pipe_reader(void* args) +{ + test_pipe_thread_data_t* td = args; + char rcvbuf[td->pktsize]; + char comparebuf[td->pktsize]; + ssize_t rsize; + int i, d; + + for(i=0; i < td->numpkts; i++) { + memset(comparebuf, i, td->pktsize); + rsize = recv(td->so, rcvbuf, td->pktsize, MSG_WAITALL); + if (rsize < 0) { + perror("recv"); + atf_tc_fail("recv returned < 0"); + } + ATF_CHECK_EQ_MSG(td->pktsize, rsize, + "expected %zd=send(...) but got %zd", + td->pktsize, rsize); + d = memcmp(comparebuf, rcvbuf, td->pktsize); + ATF_CHECK_EQ_MSG(0, d, + "Received data miscompare on packet %d", i); + } + return (0); +} + + +void +test_pipe(size_t sndbufsize, size_t rcvbufsize) +{ + test_pipe_thread_data_t writer_data, reader_data; + pthread_t writer, reader; + int num_sent, num_received; + int sv[2]; + const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4; + int numpkts; + + /* setup the socket pair */ + do_socketpair(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + /* Send a total amount of data comfortably greater than the buffers */ + numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize; + + /* Start the child threads */ + writer_data.pktsize = pktsize; + writer_data.numpkts = numpkts; + writer_data.so = sv[0]; + reader_data.pktsize = pktsize; + reader_data.numpkts = numpkts; + reader_data.so = sv[1]; + ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, test_pipe_writer, + (void*)&writer_data)); + ATF_REQUIRE_EQ(0, pthread_create(&reader, NULL, test_pipe_reader, + (void*)&reader_data)); + + /* Join the children */ + ATF_REQUIRE_EQ(0, pthread_join(writer, NULL)); + ATF_REQUIRE_EQ(0, pthread_join(reader, NULL)); +} + + +/* + * Test Cases + */ + +/* Create a SEQPACKET socket */ +ATF_TC_WITHOUT_HEAD(create_socket); +ATF_TC_BODY(create_socket, tc) +{ + int s; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_CHECK(s >= 0); +} + +/* Create SEQPACKET sockets using socketpair(2) */ +ATF_TC_WITHOUT_HEAD(create_socketpair); +ATF_TC_BODY(create_socketpair, tc) +{ + int sv[2]; + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_CHECK_EQ(0, s); + ATF_CHECK(sv[0] >= 0); + ATF_CHECK(sv[1] >= 0); + ATF_CHECK(sv[0] != sv[1]); +} + +/* Call listen(2) without first calling bind(2). It should fail */ +ATF_TC_WITHOUT_HEAD(listen_unbound); +ATF_TC_BODY(listen_unbound, tc) +{ + int s, r; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s > 0); + r = listen(s, -1); + /* expect listen to fail since we haven't called bind(2) */ + ATF_CHECK(r != 0); +} + +/* Bind the socket to a file */ +ATF_TC_WITHOUT_HEAD(bind); +ATF_TC_BODY(bind, tc) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, r; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + r = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + ATF_CHECK_EQ(0, r); +} + +/* listen(2) a socket that is already bound(2) should succeed */ +ATF_TC_WITHOUT_HEAD(listen_bound); +ATF_TC_BODY(listen_bound, tc) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, r, l; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + r = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + l = listen(s, -1); + ATF_CHECK_EQ(0, r); + ATF_CHECK_EQ(0, l); +} + +/* connect(2) can make a connection */ +ATF_TC_WITHOUT_HEAD(connect); +ATF_TC_BODY(connect, tc) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, r, err, l, s2; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + r = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + l = listen(s, -1); + ATF_CHECK_EQ(0, r); + ATF_CHECK_EQ(0, l); + + /* Create the other socket */ + s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s2 >= 0); + err = connect(s2, (struct sockaddr*)&sun, sizeof(sun)); + if (err != 0) { + perror("connect"); + atf_tc_fail("connect(2) failed"); + } +} + +/* accept(2) can receive a connection */ +ATF_TC_WITHOUT_HEAD(accept); +ATF_TC_BODY(accept, tc) +{ + int sv[2]; + + mk_pair_of_sockets(sv); +} + + +/* Set O_NONBLOCK on the socket */ +ATF_TC_WITHOUT_HEAD(fcntl_nonblock); +ATF_TC_BODY(fcntl_nonblock, tc) +{ + int s; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl"); + atf_tc_fail("fcntl failed"); + } +} + +/* Resize the send and receive buffers */ +ATF_TC_WITHOUT_HEAD(resize_buffers); +ATF_TC_BODY(resize_buffers, tc) +{ + int s; + int sndbuf = 12345; + int rcvbuf = 23456; + int xs, xr; + socklen_t sl = sizeof(xs); + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + printf(" Socket Buffer Sizes\n"); + printf(" | SNDBUF | RCVBUF |\n"); + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl)); + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl)); + printf("Default | %7d | %7d |\n", xs, xr); + + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_SNDBUF) failed"); + } + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl)); + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl)); + printf("After changing SNDBUF | %7d | %7d |\n", xs, xr); + + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_RCVBUF) failed"); + } + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl)); + ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl)); + printf("After changing RCVBUF | %7d | %7d |\n", xs, xr); +} + +/* + * Resize the send and receive buffers of a connected socketpair + * Print some useful debugging info too + */ +ATF_TC_WITHOUT_HEAD(resize_connected_buffers); +ATF_TC_BODY(resize_connected_buffers, tc) +{ + int sv[2]; + int sndbuf = 12345; + int rcvbuf = 23456; + int err; + int ls, lr, rs, rr; + socklen_t sl = sizeof(ls); + + /* setup the socket pair */ + do_socketpair(sv); + + printf(" Socket Buffer Sizes\n"); + printf(" | Left Socket | Right Socket |\n"); + printf(" | SNDBUF | RCVBUF | SNDBUF | RCVBUF |\n"); + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl)); + printf("Default | %7d | %7d | %7d | %7d |\n", + ls, lr, rs, rr); + + /* Update one side's send buffer */ + err = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + if (err != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_SNDBUF) failed"); + } + + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl)); + printf("After changing Left's SNDBUF | %7d | %7d | %7d | %7d |\n", + ls, lr, rs, rr); + + /* Update the same side's receive buffer */ + err = setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)); + if (err != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_RCVBUF) failed"); + } + + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl)); + ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl)); + printf("After changing Left's RCVBUF | %7d | %7d | %7d | %7d |\n", + ls, lr, rs, rr); +} + + +/* send(2) and recv(2) a single short record */ +ATF_TC_WITHOUT_HEAD(send_recv); +ATF_TC_BODY(send_recv, tc) +{ + int s; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = send(sv[0], data, datalen, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(datalen, rsize); +} + +/* sendto(2) and recvfrom(2) a single short record + * According to The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 + * Edition, sendto(2) is exactly the same as send(2) on a connection-mode socket + * + * According to the same spec, not all protocols are required to provide the + * source addres in recvfrom(2). + */ +ATF_TC_WITHOUT_HEAD(sendto_recvfrom); +ATF_TC_BODY(sendto_recvfrom, tc) +{ + const char* path; + struct sockaddr_storage from; + int s; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + socklen_t fromlen; + + /* setup the socket pair */ + path = mk_pair_of_sockets(sv); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = sendto(sv[0], data, datalen, MSG_EOR, NULL, 0); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + fromlen = sizeof(from); + rsize = recvfrom(sv[1], recv_buf, bufsize, MSG_WAITALL, + (struct sockaddr*)&from, &fromlen); + if (ssize < 0) { + perror("recvfrom"); + atf_tc_fail("recvfrom returned < 0"); + } + ATF_CHECK_EQ(datalen, rsize); + + /* + * FreeBSD does not currently provide the source address for SEQ_PACKET + * AF_UNIX sockets, and POSIX does not require it, so these two checks + * are disabled. If FreeBSD gains that feature in the future, then + * these checks may be reenabled + */ + /* ATF_CHECK_EQ(PF_LOCAL, from.ss_family); */ + /* ATF_CHECK_STREQ(path, ((struct sockaddr_un*)&from)->sun_path); */ +} + +/* + * send(2) and recv(2) a single short record with sockets created the + * traditional way, involving bind, listen, connect, and accept + */ +ATF_TC_WITHOUT_HEAD(send_recv_with_connect); +ATF_TC_BODY(send_recv_with_connect, tc) +{ + const char* path; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + + mk_pair_of_sockets(sv); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = send(sv[0], data, datalen, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(datalen, rsize); +} + +/* send(2) should fail on a shutdown socket */ +ATF_TC_WITHOUT_HEAD(shutdown_send); +ATF_TC_BODY(shutdown_send, tc) +{ + int s; + const char *data = "data"; + ssize_t ssize; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_CHECK(s >= 0); + ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR)); + /* USE MSG_NOSIGNAL so we don't get SIGPIPE */ + ssize = send(s, data, sizeof(data), MSG_EOR | MSG_NOSIGNAL); + ATF_CHECK_EQ(EPIPE, errno); + ATF_CHECK_EQ(-1, ssize); +} + +/* send(2) should cause SIGPIPE on a shutdown socket */ +ATF_TC_WITHOUT_HEAD(shutdown_send_sigpipe); +ATF_TC_BODY(shutdown_send_sigpipe, tc) +{ + int s; + const char *data = "data"; + ssize_t ssize; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_CHECK(s >= 0); + ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR)); + ATF_REQUIRE(SIG_ERR != signal(SIGPIPE, shutdown_send_sigpipe_handler)); + ssize = send(s, data, sizeof(data), MSG_EOR); + ATF_CHECK_EQ(1, got_sigpipe); +} + +/* nonblocking send(2) and recv(2) a single short record */ +ATF_TC_WITHOUT_HEAD(send_recv_nonblocking); +ATF_TC_BODY(send_recv_nonblocking, tc) +{ + int s; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + + /* Verify that there is nothing to receive */ + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(EAGAIN, errno); + ATF_CHECK_EQ(-1, rsize); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = send(sv[0], data, datalen, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(datalen, rsize); +} + +/* + * We should get EMSGSIZE if we try to send a message larger than the socket + * buffer, with blocking sockets + */ +ATF_TC_WITHOUT_HEAD(emsgsize); +ATF_TC_BODY(emsgsize, tc) +{ + int s; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 8192; + const size_t pktsize = (sndbufsize + rcvbufsize) * 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + ATF_CHECK_EQ(EMSGSIZE, errno); + ATF_CHECK_EQ(-1, ssize); +} + +/* + * We should get EMSGSIZE if we try to send a message larger than the socket + * buffer, with nonblocking sockets + */ +ATF_TC_WITHOUT_HEAD(emsgsize_nonblocking); +ATF_TC_BODY(emsgsize_nonblocking, tc) +{ + int s; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 8192; + const size_t pktsize = (sndbufsize + rcvbufsize) * 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + ATF_CHECK_EQ(EMSGSIZE, errno); + ATF_CHECK_EQ(-1, ssize); +} + + +/* + * We should get EAGAIN if we try to send a message larger than the socket + * buffer, with nonblocking sockets. Test with several different sockbuf sizes + */ +ATF_TC_WITHOUT_HEAD(eagain_8k_8k); +ATF_TC_BODY(eagain_8k_8k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_eagain(8192, 8192); +} +ATF_TC_WITHOUT_HEAD(eagain_8k_128k); +ATF_TC_BODY(eagain_8k_128k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_eagain(8192, 131072); +} +ATF_TC_WITHOUT_HEAD(eagain_128k_8k); +ATF_TC_BODY(eagain_128k_8k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_eagain(131072, 8192); +} +ATF_TC_WITHOUT_HEAD(eagain_128k_128k); +ATF_TC_BODY(eagain_128k_128k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_eagain(131072, 131072); +} + + +/* + * nonblocking send(2) and recv(2) of several records, which should collectively + * fill up the send buffer but not the receive buffer + */ +ATF_TC_WITHOUT_HEAD(rcvbuf_oversized); +ATF_TC_BODY(rcvbuf_oversized, tc) +{ + int s, i, j; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 131072; + const size_t geom_mean_bufsize = 32768; + const int pktsize = 1024; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + + /* + * Send and receive packets that are collectively greater than the send + * buffer, but less than the receive buffer + */ + for (i=0; i < geom_mean_bufsize / pktsize; i++) { + /* Fill the buffer */ + memset(sndbuf, i, pktsize); + + /* send the packet */ + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, ssize, + "expected %zd=send(...) but got %zd", pktsize, ssize); + + /* Receive it */ + + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + if (rsize < 0) { + perror("recv"); + atf_tc_fail("recv returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, rsize, + "expected %zd=send(...) but got %zd", pktsize, rsize); + + /* Verify the contents */ + ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize), + "Received data miscompare"); + } + + /* Trying to receive again should return EAGAIN */ + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + ATF_CHECK_EQ(EAGAIN, errno); + ATF_CHECK_EQ(-1, rsize); +} + +/* + * Simulate the behavior of a blocking pipe. The sender will send until his + * buffer fills up, then we'll simulate a scheduler switch that will allow the + * receiver to read until his buffer empties. Repeat the process until the + * transfer is complete. + * Repeat the test with multiple send and receive buffer sizes + */ +ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_8k); +ATF_TC_BODY(pipe_simulator_8k_8k, tc) +{ + test_pipe_simulator(8192, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_128k); +ATF_TC_BODY(pipe_simulator_8k_128k, tc) +{ + test_pipe_simulator(8192, 131072); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_8k); +ATF_TC_BODY(pipe_simulator_128k_8k, tc) +{ + atf_tc_expect_fail("PR kern/185812 SOCK_SEQPACKET AF_UNIX sockets with asymmetrical buffers drop packets"); + test_pipe_simulator(131072, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_128k); +ATF_TC_BODY(pipe_simulator_128k_128k, tc) +{ + test_pipe_simulator(131072, 131072); +} + +/* + * Test blocking I/O by passing data between two threads. The total amount of + * data will be >> buffer size to force blocking. Repeat the test with multiple + * send and receive buffer sizes + */ +ATF_TC_WITHOUT_HEAD(pipe_8k_8k); +ATF_TC_BODY(pipe_8k_8k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_pipe(8192, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_8k_128k); +ATF_TC_BODY(pipe_8k_128k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_pipe(8192, 131072); +} + +ATF_TC_WITHOUT_HEAD(pipe_128k_8k); +ATF_TC_BODY(pipe_128k_8k, tc) +{ + /* + * kern/185812 causes this test case to both fail and timeout. The + * atf-c-api(3) doesn't have a way to set such an expectation. + * If you use atf_tc_expect_fail, then it will timeout. If you use + * atf_tc_expect_timeout, then it will fail. If you use both, then it + * will show up as an unexpected pass, which is much worse + * + * https://code.google.com/p/kyua/issues/detail?id=76 + */ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_pipe(131072, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_128k_128k); +ATF_TC_BODY(pipe_128k_128k, tc) +{ + atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN"); + test_pipe(131072, 131072); +} + + +/* + * Test single-packet I/O with and without blocking, with symmetric buffers of + * various sizes + */ +ATF_TC_WITHOUT_HEAD(sendrecv_8k); +ATF_TC_BODY(sendrecv_8k, tc) +{ + test_sendrecv_symmetric_buffers(8 * 1024, true); +} +ATF_TC_WITHOUT_HEAD(sendrecv_16k); +ATF_TC_BODY(sendrecv_16k, tc) +{ + test_sendrecv_symmetric_buffers(16 * 1024, true); +} +ATF_TC_WITHOUT_HEAD(sendrecv_32k); +ATF_TC_BODY(sendrecv_32k, tc) +{ + test_sendrecv_symmetric_buffers(32 * 1024, true); +} +ATF_TC_WITHOUT_HEAD(sendrecv_64k); +ATF_TC_BODY(sendrecv_64k, tc) +{ + test_sendrecv_symmetric_buffers(64 * 1024, true); +} +ATF_TC_WITHOUT_HEAD(sendrecv_128k); +ATF_TC_BODY(sendrecv_128k, tc) +{ + test_sendrecv_symmetric_buffers(128 * 1024, true); +} +ATF_TC_WITHOUT_HEAD(sendrecv_8k_nonblocking); +ATF_TC_BODY(sendrecv_8k_nonblocking, tc) +{ + test_sendrecv_symmetric_buffers(8 * 1024, false); +} +ATF_TC_WITHOUT_HEAD(sendrecv_16k_nonblocking); +ATF_TC_BODY(sendrecv_16k_nonblocking, tc) +{ + test_sendrecv_symmetric_buffers(16 * 1024, false); +} +ATF_TC_WITHOUT_HEAD(sendrecv_32k_nonblocking); +ATF_TC_BODY(sendrecv_32k_nonblocking, tc) +{ + test_sendrecv_symmetric_buffers(32 * 1024, false); +} +ATF_TC_WITHOUT_HEAD(sendrecv_64k_nonblocking); +ATF_TC_BODY(sendrecv_64k_nonblocking, tc) +{ + test_sendrecv_symmetric_buffers(64 * 1024, false); +} +ATF_TC_WITHOUT_HEAD(sendrecv_128k_nonblocking); +ATF_TC_BODY(sendrecv_128k_nonblocking, tc) +{ + test_sendrecv_symmetric_buffers(128 * 1024, false); +} + + +/* + * Main. + */ + +ATF_TP_ADD_TCS(tp) +{ + /* Basic creation and connection tests */ + ATF_TP_ADD_TC(tp, create_socket); + ATF_TP_ADD_TC(tp, create_socketpair); + ATF_TP_ADD_TC(tp, listen_unbound); + ATF_TP_ADD_TC(tp, bind); + ATF_TP_ADD_TC(tp, listen_bound); + ATF_TP_ADD_TC(tp, connect); + ATF_TP_ADD_TC(tp, accept); + ATF_TP_ADD_TC(tp, fcntl_nonblock); + ATF_TP_ADD_TC(tp, resize_buffers); + ATF_TP_ADD_TC(tp, resize_connected_buffers); + + /* Unthreaded I/O tests */ + ATF_TP_ADD_TC(tp, send_recv); + ATF_TP_ADD_TC(tp, send_recv_nonblocking); + ATF_TP_ADD_TC(tp, send_recv_with_connect); + ATF_TP_ADD_TC(tp, sendto_recvfrom); + ATF_TP_ADD_TC(tp, shutdown_send); + ATF_TP_ADD_TC(tp, shutdown_send_sigpipe); + ATF_TP_ADD_TC(tp, emsgsize); + ATF_TP_ADD_TC(tp, emsgsize_nonblocking); + ATF_TP_ADD_TC(tp, eagain_8k_8k); + ATF_TP_ADD_TC(tp, eagain_8k_128k); + ATF_TP_ADD_TC(tp, eagain_128k_8k); + ATF_TP_ADD_TC(tp, eagain_128k_128k); + ATF_TP_ADD_TC(tp, sendrecv_8k); + ATF_TP_ADD_TC(tp, sendrecv_16k); + ATF_TP_ADD_TC(tp, sendrecv_32k); + ATF_TP_ADD_TC(tp, sendrecv_64k); + ATF_TP_ADD_TC(tp, sendrecv_128k); + ATF_TP_ADD_TC(tp, sendrecv_8k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_16k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_32k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_64k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_128k_nonblocking); + ATF_TP_ADD_TC(tp, rcvbuf_oversized); + ATF_TP_ADD_TC(tp, pipe_simulator_8k_8k); + ATF_TP_ADD_TC(tp, pipe_simulator_8k_128k); + ATF_TP_ADD_TC(tp, pipe_simulator_128k_8k); + ATF_TP_ADD_TC(tp, pipe_simulator_128k_128k); + + /* Threaded I/O tests with blocking sockets */ + ATF_TP_ADD_TC(tp, pipe_8k_8k); + ATF_TP_ADD_TC(tp, pipe_8k_128k); + ATF_TP_ADD_TC(tp, pipe_128k_8k); + ATF_TP_ADD_TC(tp, pipe_128k_128k); + + return atf_no_error(); +} |