Signed-off-by: Charles Myers <[email protected]>
---
modules/tests/Makefile | 16 ++-
tests/tst-pktinfo.cc | 245 ++++++++++++++++++++++++++++++++++++++++++
tests/tst-socket-timestamp.cc | 162 ++++++++++++++++++++++++++++
tests/tst-tcp-v6.cc | 240 +++++++++++++++++++++++++++++++++++++----
4 files changed, 641 insertions(+), 22 deletions(-)
create mode 100644 tests/tst-pktinfo.cc
create mode 100644 tests/tst-socket-timestamp.cc
diff --git a/modules/tests/Makefile b/modules/tests/Makefile
index cec4feb..60fc1eb 100644
--- a/modules/tests/Makefile
+++ b/modules/tests/Makefile
@@ -22,6 +22,13 @@ makedir = $(call very-quiet, mkdir -p $(dir $@))
autodepend = -MD -MT $@ -MP
+include $(OSV_BASE)/conf/base.mk
+
+configuration-defines = conf-preempt conf-debug_memory conf-logger_debug
conf-INET6
+configuration = $(foreach cf,$(configuration-defines), \
+ -D$(cf:conf-%=CONF_%)=$($(cf)))
+
+
INCLUDES = -I$(src)/arch/$(ARCH) -I$(src) -I$(src)/include \
-I$(src)/arch/common -isystem $(src)/include/glibc-compat \
$(shell $(CXX) -E -xc++ - -v </dev/null 2>&1 | awk '/^End/ {exit} /^
.*c\+\+/ {print "-isystem" $$0}') \
@@ -29,7 +36,7 @@ INCLUDES = -I$(src)/arch/$(ARCH) -I$(src) -I$(src)/include \
-isystem $(out)/gen/include
COMMON = $(autodepend) $(INCLUDES) -g -O2 -fPIC -DBOOST_TEST_DYN_LINK \
- -U _FORTIFY_SOURCE -D_KERNEL -D__OSV__ -DCONF_debug_memory=0 \
+ -U _FORTIFY_SOURCE -D_KERNEL -D__OSV__ $(configuration) \
-Wall -Wno-pointer-arith -Wformat=0 -Wno-format-security
LIBS =
@@ -113,8 +120,9 @@ tests := tst-pthread.so misc-ramdisk.so tst-vblk.so
tst-bsd-evh.so \
tst-pthread-setcancelstate.so tst-syscall.so tst-pin.so tst-run.so \
tst-ifaddrs.so tst-pthread-affinity-inherit.so tst-sem-timed-wait.so \
tst-ttyname.so tst-pthread-barrier.so tst-feexcept.so tst-math.so \
- tst-sigaltstack.so tst-fread.so tst-tcp-cork.so tst-tcp-v6.so \
- tst-calloc.so tst-crypt.so
+ tst-sigaltstack.so tst-fread.so tst-tcp-cork.so \
+ tst-calloc.so tst-crypt.so \
+ tst-socket-timestamp.so tst-pktinfo.so
# libstatic-thread-variable.so tst-static-thread-variable.so \
@@ -145,7 +153,7 @@ boost-tests := tst-vfs.so tst-libc-locking.so
misc-fs-stress.so \
tst-bsd-tcp1-zsndrcv.so tst-async.so tst-rcu-list.so tst-tcp-listen.so \
tst-poll.so tst-bitset-iter.so tst-timer-set.so tst-clock.so \
tst-rcu-hashtable.so tst-unordered-ring-mpsc.so \
- tst-seek.so
+ tst-seek.so tst-tcp-v6.so
rofs-only-boost-tests :=
diff --git a/tests/tst-pktinfo.cc b/tests/tst-pktinfo.cc
new file mode 100644
index 0000000..41022ac
--- /dev/null
+++ b/tests/tst-pktinfo.cc
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2014 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+// To compile on Linux, use: g++ -g -pthread -std=c++11 tests/tst-uio.cc
+
+// This test tests the SO_TIMESTMAP socket option.
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <iostream>
+#include <thread>
+#include <vector>
+#include <mutex>
+
+#ifdef __OSV__
+#include <bsd/porting/netport.h> // Get INET6
+#else
+#define INET6
+#endif
+
+
+// Multiple threads can call expect functions at the same time
+// so need to protect against concurrent writes to cout.
+std::mutex test_mutex;
+
+static int tests = 0, fails = 0;
+
+template<typename T>
+bool do_expect(T actual, T expected, const char *actuals, const char
*expecteds, const char *file, int line)
+{
+ std::lock_guard<std::mutex> lock(test_mutex);
+
+ ++tests;
+ if (actual != expected) {
+ fails++;
+ std::cout << "FAIL: " << file << ":" << line << ": For " << actuals
+ << " expected " << expecteds << "(" << expected << "), saw "
+ << actual << ".\n";
+ return false;
+ }
+ std::cout << "OK: " << file << ":" << line << ".\n";
+ return true;
+}
+template<typename T>
+bool do_expectge(T actual, T expected, const char *actuals, const char
*expecteds, const char *file, int line)
+{
+ std::lock_guard<std::mutex> lock(test_mutex);
+
+ ++tests;
+ if (actual < expected) {
+ fails++;
+ std::cout << "FAIL: " << file << ":" << line << ": For " << actuals
+ << " expected >=" << expecteds << ", saw " << actual << ".\n";
+ return false;
+ }
+ std::cout << "OK: " << file << ":" << line << ".\n";
+ return true;
+}
+#define expect(actual, expected) do_expect(actual, expected, #actual,
#expected, __FILE__, __LINE__)
+#define expectge(actual, expected) do_expectge(actual, expected, #actual,
#expected, __FILE__, __LINE__)
+#define expect_errno(call, experrno) ( \
+ do_expect((long)(call), (long)-1, #call, "-1", __FILE__, __LINE__) && \
+ do_expect(errno, experrno, #call " errno", #experrno, __FILE__,
__LINE__) )
+#define expect_success(var, call) \
+ errno = 0; \
+ var = call; \
+ do_expectge(var, 0, #call, "0", __FILE__, __LINE__); \
+ do_expect(errno, 0, #call " errno", "0", __FILE__, __LINE__);
+
+void test_ipv4()
+{
+ int sockfd;
+ int optval = 1;
+ struct sockaddr_in serveraddr;
+ socklen_t serveraddr_len = sizeof(serveraddr);
+ const int npacket = 5;
+ int ret;
+
+ expect_success(sockfd, socket(AF_INET, SOCK_DGRAM, 0));
+ expect_success(ret, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)));
+ expect_success(ret, setsockopt(sockfd, IPPROTO_IP, IP_PKTINFO, &optval,
sizeof(optval)));
+
+ bzero(&serveraddr, sizeof(serveraddr));
+ serveraddr.sin_family = AF_INET;
+ serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ serveraddr.sin_port = 0; // Find next available port
+ expect_success(ret, bind(sockfd, (struct sockaddr*) &serveraddr,
serveraddr_len));
+
+ expect_success(ret, getsockname(sockfd, (struct sockaddr*) &serveraddr,
&serveraddr_len));
+ expect(serveraddr.sin_family, (in_port_t)AF_INET);
+ std::cout << "Server bound to UDP port " << ntohs(serveraddr.sin_port) <<
std::endl;
+
+ std::thread t([sockfd, npacket] {
+ struct msghdr msg;
+ struct iovec iov;
+ uint8_t buf[64];
+ uint8_t controlbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];
+
+ bzero(&msg, sizeof(msg));
+ bzero(&iov, sizeof(iov));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = sizeof(controlbuf);
+
+ int n;
+ expect_success(n, recvmsg(sockfd, &msg, 0));
+ expect(n, 6);
+
+ struct in_pktinfo pktinfo;
+ bool pktinfo_valid = false;
+
+ for (auto cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr =
CMSG_NXTHDR(&msg, cmptr)) {
+ if ((cmptr->cmsg_level == IPPROTO_IP) && (cmptr->cmsg_type ==
IP_PKTINFO)) {
+ memcpy(&pktinfo, CMSG_DATA(cmptr), sizeof(pktinfo));
+ pktinfo_valid = true;
+ break;
+ }
+ }
+
+ expect(pktinfo_valid, true);
+
+ char ipaddr[INET_ADDRSTRLEN];
+ inet_ntop(AF_INET, &pktinfo.ipi_addr, ipaddr, sizeof(ipaddr));
+ std::cout << "ifindex " << pktinfo.ipi_ifindex << " ipaddr " <<
ipaddr << std::endl;
+ expect(pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
+ }
+ });
+
+ int sendsock;
+
+ expect_success(sendsock, socket(AF_INET, SOCK_DGRAM, 0));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ expect_success(ret, sendto(sendsock, "Hello!", 6, 0, (const sockaddr*)
&serveraddr, sizeof(serveraddr)));
+ }
+ t.join();
+ close(sockfd);
+ close(sendsock);
+}
+
+#ifdef INET6
+
+void test_ipv6()
+{
+ int sockfd;
+ int optval = 1;
+ struct sockaddr_in6 serveraddr;
+ socklen_t serveraddr_len = sizeof(serveraddr);
+ const int npacket = 5;
+ int ret;
+
+ expect_success(sockfd, socket(AF_INET6, SOCK_DGRAM, 0));
+ expect_success(ret, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)));
+ expect_success(ret, setsockopt(sockfd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&optval, sizeof(optval)));
+
+ bzero(&serveraddr, sizeof(serveraddr));
+ serveraddr.sin6_family = AF_INET6;
+ serveraddr.sin6_addr = in6addr_loopback;
+ serveraddr.sin6_port = 0; // Find next available port
+ expect_success(ret, bind(sockfd, (struct sockaddr*) &serveraddr,
serveraddr_len));
+
+ expect_success(ret, getsockname(sockfd, (struct sockaddr*) &serveraddr,
&serveraddr_len));
+ expect(serveraddr.sin6_family, (in_port_t)AF_INET6);
+ std::cout << "Server bound to UDP port " << ntohs(serveraddr.sin6_port) <<
std::endl;
+
+ std::thread t([sockfd, npacket] {
+ struct msghdr msg;
+ struct iovec iov;
+ uint8_t buf[64];
+ uint8_t controlbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
+
+ bzero(&msg, sizeof(msg));
+ bzero(&iov, sizeof(iov));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = sizeof(controlbuf);
+
+ int n;
+ expect_success(n, recvmsg(sockfd, &msg, 0));
+ expect(n, 6);
+
+ struct in6_pktinfo pktinfo;
+ bool pktinfo_valid = false;
+
+ for (auto cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr =
CMSG_NXTHDR(&msg, cmptr)) {
+ if ((cmptr->cmsg_level == IPPROTO_IPV6) && (cmptr->cmsg_type
== IPV6_PKTINFO)) {
+ memcpy(&pktinfo, CMSG_DATA(cmptr), sizeof(pktinfo));
+ pktinfo_valid = true;
+ break;
+ }
+ }
+
+ expect(pktinfo_valid, true);
+
+ char ipaddr[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &pktinfo.ipi6_addr, ipaddr, sizeof(ipaddr));
+ std::cout << "ifindex " << pktinfo.ipi6_ifindex << " ipaddr " <<
ipaddr << std::endl;
+ expect(std::string(ipaddr), std::string("::1"));
+ }
+ });
+
+ int sendsock;
+
+ expect_success(sendsock, socket(AF_INET6, SOCK_DGRAM, 0));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ expect_success(ret, sendto(sendsock, "Hello!", 6, 0, (const sockaddr*)
&serveraddr, sizeof(serveraddr)));
+ }
+ t.join();
+ close(sockfd);
+ close(sendsock);
+}
+
+#endif
+
+int main()
+{
+ test_ipv4();
+#ifdef INET6
+ test_ipv6();
+#endif
+ std::cout << "SUMMARY: " << tests << " tests, " << fails << " failures\n";
+ return fails == 0 ? 0 : 1;
+}
+
diff --git a/tests/tst-socket-timestamp.cc b/tests/tst-socket-timestamp.cc
new file mode 100644
index 0000000..1ad1792
--- /dev/null
+++ b/tests/tst-socket-timestamp.cc
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 Cloudius Systems, Ltd.
+ *
+ * This work is open source software, licensed under the terms of the
+ * BSD license as described in the LICENSE file in the top-level directory.
+ */
+// To compile on Linux, use: g++ -g -pthread -std=c++11 tests/tst-uio.cc
+
+// This test tests the SO_TIMESTMAP socket option.
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+
+#include <iostream>
+#include <thread>
+#include <vector>
+#include <mutex>
+
+// Multiple threads can call expect functions at the same time
+// so need to protect against concurrent writes to cout.
+std::mutex test_mutex;
+
+static int tests = 0, fails = 0;
+
+template<typename T>
+bool do_expect(T actual, T expected, const char *actuals, const char
*expecteds, const char *file, int line)
+{
+ std::lock_guard<std::mutex> lock(test_mutex);
+
+ ++tests;
+ if (actual != expected) {
+ fails++;
+ std::cout << "FAIL: " << file << ":" << line << ": For " << actuals
+ << " expected " << expecteds << "(" << expected << "), saw "
+ << actual << ".\n";
+ return false;
+ }
+ std::cout << "OK: " << file << ":" << line << ".\n";
+ return true;
+}
+template<typename T>
+bool do_expectge(T actual, T expected, const char *actuals, const char
*expecteds, const char *file, int line)
+{
+ std::lock_guard<std::mutex> lock(test_mutex);
+
+ ++tests;
+ if (actual < expected) {
+ fails++;
+ std::cout << "FAIL: " << file << ":" << line << ": For " << actuals
+ << " expected >=" << expecteds << ", saw " << actual << ".\n";
+ return false;
+ }
+ std::cout << "OK: " << file << ":" << line << ".\n";
+ return true;
+}
+#define expect(actual, expected) do_expect(actual, expected, #actual,
#expected, __FILE__, __LINE__)
+#define expectge(actual, expected) do_expectge(actual, expected, #actual,
#expected, __FILE__, __LINE__)
+#define expect_errno(call, experrno) ( \
+ do_expect((long)(call), (long)-1, #call, "-1", __FILE__, __LINE__) && \
+ do_expect(errno, experrno, #call " errno", #experrno, __FILE__,
__LINE__) )
+#define expect_success(var, call) \
+ errno = 0; \
+ var = call; \
+ do_expectge(var, 0, #call, "0", __FILE__, __LINE__); \
+ do_expect(errno, 0, #call " errno", "0", __FILE__, __LINE__);
+
+int main()
+{
+ int sockfd;
+ int optval = 1;
+ struct sockaddr_in serveraddr;
+ socklen_t serveraddr_len = sizeof(serveraddr);
+ const int npacket = 5;
+ int ret;
+
+ expect_success(sockfd, socket(AF_INET, SOCK_DGRAM, 0));
+ expect(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval)), 0);
+ expect(setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMP, &optval,
sizeof(optval)), 0);
+
+ bzero(&serveraddr, sizeof(serveraddr));
+ serveraddr.sin_family = AF_INET;
+ serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ serveraddr.sin_port = 0; // Find next available port
+ expect_success(ret, bind(sockfd, (struct sockaddr*) &serveraddr,
serveraddr_len));
+
+ expect_success(ret, getsockname(sockfd, (struct sockaddr*) &serveraddr,
&serveraddr_len));
+ expect(serveraddr.sin_family, (in_port_t)AF_INET);
+ std::cout << "Server bound to UDP port " << ntohs(serveraddr.sin_port) <<
std::endl;
+
+ std::thread t([sockfd, npacket] {
+ struct msghdr msg;
+ struct iovec iov;
+ uint8_t buf[64];
+ uint8_t controlbuf[CMSG_SPACE(sizeof(struct timeval))];
+ struct timeval start, end;
+ std::vector<struct timeval> timestamps;
+
+ gettimeofday(&start, NULL);
+
+ bzero(&msg, sizeof(msg));
+ bzero(&iov, sizeof(iov));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = controlbuf;
+ msg.msg_controllen = sizeof(controlbuf);
+
+ int n;
+ expect_success(n, recvmsg(sockfd, &msg, 0));
+ expect(n, 6);
+
+ // Extract receive timestamp from cmsg data
+ for (auto cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr =
CMSG_NXTHDR(&msg, cmptr)) {
+ if ((cmptr->cmsg_level == SOL_SOCKET) && (cmptr->cmsg_type ==
SCM_TIMESTAMP)) {
+ struct timeval tv;
+ memcpy(&tv, CMSG_DATA(cmptr), sizeof(tv));
+ timestamps.push_back(tv);
+ }
+ }
+ }
+
+ gettimeofday(&end, NULL);
+
+ // Validate received timestamps
+ expect(timestamps.size(), (std::size_t)5);
+ const struct timeval *prev_tv = NULL;
+ for (auto const & tv : timestamps) {
+ expect(timercmp(&tv, &start, >=), true);
+ expect(timercmp(&tv, &end, <=), true);
+ if (prev_tv) {
+ expect(timercmp(&tv, prev_tv, >=), true);
+ }
+ prev_tv = &tv;
+ }
+ });
+
+ int sendsock;
+
+ expect_success(sendsock, socket(AF_INET, SOCK_DGRAM, 0));
+ for (int ipacket = 0; ipacket < npacket; ++ipacket) {
+ expect_success(ret, sendto(sendsock, "Hello!", 6, 0, (const sockaddr*)
&serveraddr, sizeof(serveraddr)));
+ }
+ t.join();
+ close(sockfd);
+ close(sendsock);
+
+ std::cout << "SUMMARY: " << tests << " tests, " << fails << " failures\n";
+ return fails == 0 ? 0 : 1;
+}
+
diff --git a/tests/tst-tcp-v6.cc b/tests/tst-tcp-v6.cc
index 6858c1d..49268d3 100644
--- a/tests/tst-tcp-v6.cc
+++ b/tests/tst-tcp-v6.cc
@@ -5,38 +5,242 @@
* BSD license as described in the LICENSE file in the top-level directory.
*/
+#define BOOST_TEST_MODULE tst-tcp-v6
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <vector>
+#include <thread>
+#include <chrono>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <osv/latch.hh>
+#include <boost/test/unit_test.hpp>
+#include <boost/interprocess/sync/interprocess_semaphore.hpp>
+
+#ifdef __OSV__
+#include <bsd/porting/netport.h> // Get INET6
+#else
+#define INET6
+#endif
+
+#ifndef INET6
+
/*
* This is a test for OSv's IPv6 non-support :-) Although we do not support
* IPv6, we should return the errors which applications expect - see issue
* #865 on how us returning the wrong error from socket() confused redis.
*/
-#include <stdio.h>
-#include <strings.h>
-#include <unistd.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <errno.h>
-
-static int tests = 0, fails = 0;
-static void report(bool ok, const char* msg)
+BOOST_AUTO_TEST_CASE(test_no_ipv6)
{
- ++tests;
- fails += !ok;
- printf("%s: %s\n", (ok ? "PASS" : "FAIL"), msg);
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
+ BOOST_REQUIRE_MESSAGE(sock>=0, "open AF_INET socket succeeds");
+ close(sock);
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+ BOOST_REQUIRE_MESSAGE(sock<0, "open AF_INET6 socket fails");
+ BOOST_REQUIRE_MESSAGE(errno==EAFNOSUPPORT, "failure should be
EAFNOSUPPORT");
}
+#else
-int main(int argc, char **argv)
+/*
+ * OSv compiled with IP6 support.
+ */
+
+BOOST_AUTO_TEST_CASE(test_has_ipv6)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
- report(sock>=0, "open AF_INET socket succeeds");
+ BOOST_REQUIRE_MESSAGE(sock>=0, "open AF_INET socket succeeds");
close(sock);
sock = socket(AF_INET6, SOCK_STREAM, 0);
- report(sock<0, "open AF_INET6 socket fails");
- report(errno==EAFNOSUPPORT, "failure should be EAFNOSUPPORT");
+ BOOST_REQUIRE_MESSAGE(sock>=0, "open AF_INET6 socket succeeds");
close(sock);
- printf("SUMMARY: %d tests, %d failures\n", tests, fails);
- return fails;
}
+
+#define LISTEN_PORT 7777
+
+using _clock = std::chrono::high_resolution_clock;
+
+static int accept_with_timeout(int listening_socket, int timeout_in_seconds)
+{
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(listening_socket, &rfds);
+
+ struct timeval tv = { .tv_sec = timeout_in_seconds, .tv_usec = 0 };
+ int retval = select(listening_socket + 1, &rfds, NULL, NULL, &tv);
+ BOOST_REQUIRE_EQUAL(1, retval);
+
+ int client_socket = accept(listening_socket, NULL, NULL);
+ BOOST_REQUIRE(client_socket > 0);
+ return client_socket;
+}
+
+BOOST_AUTO_TEST_CASE(test_ipv6_connections_get_accepted_even_when_backlog_gets_overflowed)
+{
+ std::vector<int> sockets_to_close;
+
+ constexpr int n_connections = 7;
+ constexpr int backlog_size = 2;
+
+ static_assert(n_connections < SOMAXCONN,
+ "The number of connections should not exceed maximum backlog size");
+
+ static_assert(backlog_size < n_connections,
+ "The test makes sense only when number of connections is greater than
backlog size");
+
+ auto listen_s = socket(AF_INET6, SOCK_STREAM, 0);
+ BOOST_REQUIRE(listen_s > 0);
+
+ sockets_to_close.push_back(listen_s);
+
+ int reuse = 1;
+ BOOST_REQUIRE(setsockopt(listen_s, SOL_SOCKET, SO_REUSEADDR, (const
char*)&reuse, sizeof(reuse)) == 0);
+
+ struct sockaddr_in6 laddr = {};
+ laddr.sin6_family = AF_INET6;
+ laddr.sin6_addr = in6addr_any;
+ laddr.sin6_port = htons(LISTEN_PORT);
+
+ BOOST_REQUIRE(bind(listen_s, (struct sockaddr *) &laddr, sizeof(laddr)) ==
0);
+ BOOST_REQUIRE(listen(listen_s, backlog_size) == 0);
+
+ BOOST_MESSAGE("listening...");
+
+ for (int i = 0; i < n_connections; i++) {
+ int s = socket(AF_INET6, SOCK_STREAM, 0);
+ BOOST_REQUIRE(s > 0);
+
+ struct sockaddr_in6 raddr = {};
+ raddr.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, "::1", &raddr.sin6_addr);
+ raddr.sin6_port = htons(LISTEN_PORT);
+
+ BOOST_MESSAGE("connecting...");
+
+ BOOST_REQUIRE(connect(s, (struct sockaddr *)&raddr, sizeof(raddr)) ==
0);
+ sockets_to_close.push_back(s);
+ }
+
+ BOOST_MESSAGE("starting to accept...");
+
+ for (int i = 0; i < n_connections; i++) {
+ int client_s = accept_with_timeout(listen_s, 3);
+ BOOST_REQUIRE(client_s >= 0);
+ BOOST_MESSAGE("accepted");
+
+ sockets_to_close.push_back(client_s);
+ }
+
+ BOOST_MESSAGE("closing...");
+
+ for (auto& fd : sockets_to_close) {
+ close(fd);
+ }
+}
+
+BOOST_AUTO_TEST_CASE(test_ipv6_clients_are_not_reset_when_backlog_is_full_and_they_write)
+{
+ constexpr int backlog_size = 5;
+ constexpr int n_connections = backlog_size * 3;
+
+ auto listen_s = socket(AF_INET6, SOCK_STREAM, 0);
+ if (listen_s < 0) {
+ perror("socket");
+ BOOST_REQUIRE(false);
+ exit(1);
+ }
+
+ int reuse = 1;
+ BOOST_REQUIRE(setsockopt(listen_s, SOL_SOCKET, SO_REUSEADDR, (const
char*)&reuse, sizeof(reuse)) == 0);
+
+ struct sockaddr_in6 laddr = {};
+ laddr.sin6_family = AF_INET6;
+ laddr.sin6_addr = in6addr_any;
+ laddr.sin6_port = htons(LISTEN_PORT);
+
+ if (bind(listen_s, (struct sockaddr *) &laddr, sizeof(laddr)) < 0) {
+ perror("bind");
+ BOOST_REQUIRE(false);
+ exit(1);
+ }
+
+ BOOST_REQUIRE(listen(listen_s, backlog_size) == 0);
+
+ BOOST_MESSAGE("listening...");
+
+ std::vector<std::thread*> threads;
+
+ latch _latch(n_connections);
+
+ for (int i = 0; i < n_connections; i++) {
+ threads.push_back(new std::thread([i, &_latch] {
+ int s = socket(AF_INET6, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("socket");
+ BOOST_REQUIRE(false);
+ exit(1);
+ }
+
+ struct sockaddr_in6 raddr = {};
+ raddr.sin6_family = AF_INET6;
+ inet_pton(AF_INET6, "::1", &raddr.sin6_addr);
+ raddr.sin6_port = htons(LISTEN_PORT);
+
+ _latch.count_down();
+
+ if (connect(s, (struct sockaddr *)&raddr, sizeof(raddr))) {
+ perror("connect");
+ BOOST_REQUIRE(false);
+ exit(1);
+ }
+
+ while (true) {
+ const char* msg = "hello";
+ int bytes = write(s, msg, strlen(msg) + 1);
+ if (bytes < 0) {
+ break;
+ }
+ }
+ close(s);
+ }));
+ }
+
+ // Start accepting after all clients are lined up
+ // to create a thundering storm effect.
+ _latch.await();
+
+ for (int i = 0; i < n_connections; i++) {
+ int client_s = accept_with_timeout(listen_s, 3);
+ BOOST_MESSAGE("accepted");
+
+ threads.push_back(new std::thread([client_s] {
+ auto close_at = _clock::now() + std::chrono::milliseconds(50);
+ while (_clock::now() < close_at) {
+ char buf[1];
+ int bytes = read(client_s, &buf, sizeof(buf));
+ if (bytes < 0) {
+ perror("read");
+ BOOST_REQUIRE(false);
+ exit(1);
+ }
+ }
+
+ close(client_s);
+ }));
+ }
+
+ for (auto& thread : threads) {
+ thread->join();
+ delete thread;
+ }
+
+ close(listen_s);
+}
+
+#endif /* INET6 */
+
--
2.7.4
--
You received this message because you are subscribed to the Google Groups "OSv
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.