Thanks. I am very happy you added these tests :-)

Only some minor comments below.


On Tue, Aug 7, 2018 at 5:49 AM, Charles Myers <[email protected]>
wrote:

> 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.
>

You can use your own copyright statement (and current year) instead.


> + *
> + * 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
>

Change tst-uio.cc on this command line to tst-pktinfo.cc


> +
> +// This test tests the SO_TIMESTMAP socket option.
>

Is this really what this test tests? Looks more like testing IP_PKTINFO
(I'm not familiar with this option, so I don't know if it tests anything
else).


> +
> +#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.
>

Again, probably update year and copyright.

+ *
> + * 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
>

again tst-uio.cc :-)


> +
> +// This test tests the SO_TIMESTMAP socket option.
>

Oh, so this comment came from here :-)

+
> +#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.
>

-- 
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