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.

Reply via email to