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 <charles.my...@spirent.com>
wrote:

> Signed-off-by: Charles Myers <charles.my...@spirent.com>
> ---
>  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 osv-dev+unsubscr...@googlegroups.com.
> 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 osv-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to