Path: blob/main/tests/sys/netinet/so_reuseport_lb_test.c
39478 views
/*-1* Copyright (c) 2018 The FreeBSD Foundation2*3* This software was developed by Mark Johnston under sponsorship from4* the FreeBSD Foundation.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions are8* met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in13* the documentation and/or other materials provided with the14* distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/event.h>31#include <sys/socket.h>3233#include <netinet/in.h>34#include <netinet/tcp.h>3536#include <err.h>37#include <errno.h>38#include <pthread.h>39#include <stdatomic.h>40#include <stdlib.h>41#include <unistd.h>4243#include <atf-c.h>4445/*46* Given an array of non-blocking listening sockets configured in a LB group47* for "addr", try connecting to "addr" in a loop and verify that connections48* are roughly balanced across the sockets.49*/50static void51lb_simple_accept_loop(int domain, const struct sockaddr *addr, int sds[],52size_t nsds, int nconns)53{54size_t i;55int *acceptcnt;56int csd, error, excnt, sd;57const struct linger lopt = { 1, 0 };5859/*60* We expect each listening socket to accept roughly nconns/nsds61* connections, but allow for some error.62*/63excnt = nconns / nsds / 8;64acceptcnt = calloc(nsds, sizeof(*acceptcnt));65ATF_REQUIRE_MSG(acceptcnt != NULL, "calloc() failed: %s",66strerror(errno));6768while (nconns-- > 0) {69sd = socket(domain, SOCK_STREAM, 0);70ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s",71strerror(errno));7273error = connect(sd, addr, addr->sa_len);74ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",75strerror(errno));7677error = setsockopt(sd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));78ATF_REQUIRE_MSG(error == 0, "Setting linger failed: %s",79strerror(errno));8081/*82* Poll the listening sockets.83*/84do {85for (i = 0; i < nsds; i++) {86csd = accept(sds[i], NULL, NULL);87if (csd < 0) {88ATF_REQUIRE_MSG(errno == EWOULDBLOCK ||89errno == EAGAIN,90"accept() failed: %s",91strerror(errno));92continue;93}9495error = close(csd);96ATF_REQUIRE_MSG(error == 0,97"close() failed: %s", strerror(errno));9899acceptcnt[i]++;100break;101}102} while (i == nsds);103104error = close(sd);105ATF_REQUIRE_MSG(error == 0, "close() failed: %s",106strerror(errno));107}108109for (i = 0; i < nsds; i++)110ATF_REQUIRE_MSG(acceptcnt[i] > excnt, "uneven balancing");111}112113static int114lb_listen_socket(int domain, int flags)115{116int one;117int error, sd;118119sd = socket(domain, SOCK_STREAM | flags, 0);120ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s", strerror(errno));121122one = 1;123error = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT_LB, &one, sizeof(one));124ATF_REQUIRE_MSG(error == 0, "setsockopt(SO_REUSEPORT_LB) failed: %s",125strerror(errno));126127return (sd);128}129130ATF_TC_WITHOUT_HEAD(basic_ipv4);131ATF_TC_BODY(basic_ipv4, tc)132{133struct sockaddr_in addr;134socklen_t slen;135size_t i;136const int nconns = 16384;137int error, sds[16];138uint16_t port;139140sds[0] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);141142memset(&addr, 0, sizeof(addr));143addr.sin_len = sizeof(addr);144addr.sin_family = AF_INET;145addr.sin_port = htons(0);146addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);147error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));148ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));149error = listen(sds[0], 1);150ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));151152slen = sizeof(addr);153error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);154ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",155strerror(errno));156ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");157port = addr.sin_port;158159memset(&addr, 0, sizeof(addr));160addr.sin_len = sizeof(addr);161addr.sin_family = AF_INET;162addr.sin_port = port;163addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);164for (i = 1; i < nitems(sds); i++) {165sds[i] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);166167error = bind(sds[i], (const struct sockaddr *)&addr,168sizeof(addr));169ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",170strerror(errno));171error = listen(sds[i], 1);172ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",173strerror(errno));174}175176lb_simple_accept_loop(PF_INET, (struct sockaddr *)&addr, sds,177nitems(sds), nconns);178for (i = 0; i < nitems(sds); i++) {179error = close(sds[i]);180ATF_REQUIRE_MSG(error == 0, "close() failed: %s",181strerror(errno));182}183}184185ATF_TC_WITHOUT_HEAD(basic_ipv6);186ATF_TC_BODY(basic_ipv6, tc)187{188const struct in6_addr loopback6 = IN6ADDR_LOOPBACK_INIT;189struct sockaddr_in6 addr;190socklen_t slen;191size_t i;192const int nconns = 16384;193int error, sds[16];194uint16_t port;195196sds[0] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);197198memset(&addr, 0, sizeof(addr));199addr.sin6_len = sizeof(addr);200addr.sin6_family = AF_INET6;201addr.sin6_port = htons(0);202addr.sin6_addr = loopback6;203error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));204ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));205error = listen(sds[0], 1);206ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));207208slen = sizeof(addr);209error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);210ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",211strerror(errno));212ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");213port = addr.sin6_port;214215memset(&addr, 0, sizeof(addr));216addr.sin6_len = sizeof(addr);217addr.sin6_family = AF_INET6;218addr.sin6_port = port;219addr.sin6_addr = loopback6;220for (i = 1; i < nitems(sds); i++) {221sds[i] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);222223error = bind(sds[i], (const struct sockaddr *)&addr,224sizeof(addr));225ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",226strerror(errno));227error = listen(sds[i], 1);228ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",229strerror(errno));230}231232lb_simple_accept_loop(PF_INET6, (struct sockaddr *)&addr, sds,233nitems(sds), nconns);234for (i = 0; i < nitems(sds); i++) {235error = close(sds[i]);236ATF_REQUIRE_MSG(error == 0, "close() failed: %s",237strerror(errno));238}239}240241struct concurrent_add_softc {242struct sockaddr_storage ss;243int socks[128];244int kq;245};246247static void *248listener(void *arg)249{250for (struct concurrent_add_softc *sc = arg;;) {251struct kevent kev;252ssize_t n;253int error, count, cs, s;254uint8_t b;255256count = kevent(sc->kq, NULL, 0, &kev, 1, NULL);257ATF_REQUIRE_MSG(count == 1,258"kevent() failed: %s", strerror(errno));259260s = (int)kev.ident;261cs = accept(s, NULL, NULL);262ATF_REQUIRE_MSG(cs >= 0,263"accept() failed: %s", strerror(errno));264265b = 'M';266n = write(cs, &b, sizeof(b));267ATF_REQUIRE_MSG(n >= 0, "write() failed: %s", strerror(errno));268ATF_REQUIRE(n == 1);269270error = close(cs);271ATF_REQUIRE_MSG(error == 0 || errno == ECONNRESET,272"close() failed: %s", strerror(errno));273}274}275276static void *277connector(void *arg)278{279for (struct concurrent_add_softc *sc = arg;;) {280ssize_t n;281int error, s;282uint8_t b;283284s = socket(sc->ss.ss_family, SOCK_STREAM, 0);285ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));286287error = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int[]){1},288sizeof(int));289290error = connect(s, (struct sockaddr *)&sc->ss, sc->ss.ss_len);291ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",292strerror(errno));293294n = read(s, &b, sizeof(b));295ATF_REQUIRE_MSG(n >= 0, "read() failed: %s",296strerror(errno));297ATF_REQUIRE(n == 1);298ATF_REQUIRE(b == 'M');299error = close(s);300ATF_REQUIRE_MSG(error == 0,301"close() failed: %s", strerror(errno));302}303}304305/*306* Run three threads. One accepts connections from listening sockets on a307* kqueue, while the other makes connections. The third thread slowly adds308* sockets to the LB group. This is meant to help flush out race conditions.309*/310ATF_TC_WITHOUT_HEAD(concurrent_add);311ATF_TC_BODY(concurrent_add, tc)312{313struct concurrent_add_softc sc;314struct sockaddr_in *sin;315pthread_t threads[4];316int error;317318sc.kq = kqueue();319ATF_REQUIRE_MSG(sc.kq >= 0, "kqueue() failed: %s", strerror(errno));320321error = pthread_create(&threads[0], NULL, listener, &sc);322ATF_REQUIRE_MSG(error == 0, "pthread_create() failed: %s",323strerror(error));324325sin = (struct sockaddr_in *)&sc.ss;326memset(sin, 0, sizeof(*sin));327sin->sin_len = sizeof(*sin);328sin->sin_family = AF_INET;329sin->sin_port = htons(0);330sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);331332for (size_t i = 0; i < nitems(sc.socks); i++) {333struct kevent kev;334int s;335336sc.socks[i] = s = socket(AF_INET, SOCK_STREAM, 0);337ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));338339error = setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},340sizeof(int));341ATF_REQUIRE_MSG(error == 0,342"setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));343344error = bind(s, (struct sockaddr *)sin, sizeof(*sin));345ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",346strerror(errno));347348error = listen(s, 5);349ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",350strerror(errno));351352EV_SET(&kev, s, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);353error = kevent(sc.kq, &kev, 1, NULL, 0, NULL);354ATF_REQUIRE_MSG(error == 0, "kevent() failed: %s",355strerror(errno));356357if (i == 0) {358socklen_t slen = sizeof(sc.ss);359360error = getsockname(sc.socks[i],361(struct sockaddr *)&sc.ss, &slen);362ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",363strerror(errno));364ATF_REQUIRE(sc.ss.ss_family == AF_INET);365366for (size_t j = 1; j < nitems(threads); j++) {367error = pthread_create(&threads[j], NULL,368connector, &sc);369ATF_REQUIRE_MSG(error == 0,370"pthread_create() failed: %s",371strerror(error));372}373}374375usleep(20000);376}377}378379/*380* Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.381*/382ATF_TC_WITHOUT_HEAD(double_listen_ipv4);383ATF_TC_BODY(double_listen_ipv4, tc)384{385struct sockaddr_in sin;386int error, s;387388s = lb_listen_socket(PF_INET, 0);389390memset(&sin, 0, sizeof(sin));391sin.sin_len = sizeof(sin);392sin.sin_family = AF_INET;393sin.sin_port = htons(0);394sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);395error = bind(s, (struct sockaddr *)&sin, sizeof(sin));396ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));397398error = listen(s, 1);399ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));400error = listen(s, 2);401ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));402403error = close(s);404ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));405}406407/*408* Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.409*/410ATF_TC_WITHOUT_HEAD(double_listen_ipv6);411ATF_TC_BODY(double_listen_ipv6, tc)412{413struct sockaddr_in6 sin6;414int error, s;415416s = lb_listen_socket(PF_INET6, 0);417418memset(&sin6, 0, sizeof(sin6));419sin6.sin6_len = sizeof(sin6);420sin6.sin6_family = AF_INET6;421sin6.sin6_port = htons(0);422sin6.sin6_addr = in6addr_loopback;423error = bind(s, (struct sockaddr *)&sin6, sizeof(sin6));424ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));425426error = listen(s, 1);427ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));428error = listen(s, 2);429ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));430431error = close(s);432ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));433}434435/*436* Try binding many sockets to the same lbgroup without calling listen(2) on437* them.438*/439ATF_TC_WITHOUT_HEAD(bind_without_listen);440ATF_TC_BODY(bind_without_listen, tc)441{442const int nsockets = 100;443struct sockaddr_in sin;444socklen_t socklen;445int error, s, s2[nsockets];446447s = lb_listen_socket(PF_INET, 0);448449memset(&sin, 0, sizeof(sin));450sin.sin_len = sizeof(sin);451sin.sin_family = AF_INET;452sin.sin_port = htons(0);453sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);454error = bind(s, (struct sockaddr *)&sin, sizeof(sin));455ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));456457socklen = sizeof(sin);458error = getsockname(s, (struct sockaddr *)&sin, &socklen);459ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",460strerror(errno));461462for (int i = 0; i < nsockets; i++) {463s2[i] = lb_listen_socket(PF_INET, 0);464error = bind(s2[i], (struct sockaddr *)&sin, sizeof(sin));465ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));466}467for (int i = 0; i < nsockets; i++) {468error = listen(s2[i], 1);469ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));470}471for (int i = 0; i < nsockets; i++) {472error = close(s2[i]);473ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));474}475476error = close(s);477ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));478}479480/*481* Check that SO_REUSEPORT_LB doesn't mess with connect(2).482* Two sockets:483* 1) auxiliary peer socket 'p', where we connect to484* 2) test socket 's', that sets SO_REUSEPORT_LB and then connect(2)s to 'p'485*/486ATF_TC_WITHOUT_HEAD(connect_not_bound);487ATF_TC_BODY(connect_not_bound, tc)488{489struct sockaddr_in sin = {490.sin_family = AF_INET,491.sin_len = sizeof(sin),492.sin_addr = { htonl(INADDR_LOOPBACK) },493};494socklen_t slen = sizeof(struct sockaddr_in);495int p, s, rv;496497ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);498ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);499ATF_REQUIRE(listen(p, 1) == 0);500ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);501502s = lb_listen_socket(PF_INET, 0);503rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));504ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,505"Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",506rv, errno);507rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,508sizeof(sin));509ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,510"Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",511rv, errno);512513close(p);514close(s);515}516517/*518* Same as above, but we also bind(2) between setsockopt(2) of SO_REUSEPORT_LB519* and the connect(2).520*/521ATF_TC_WITHOUT_HEAD(connect_bound);522ATF_TC_BODY(connect_bound, tc)523{524struct sockaddr_in sin = {525.sin_family = AF_INET,526.sin_len = sizeof(sin),527.sin_addr = { htonl(INADDR_LOOPBACK) },528};529socklen_t slen = sizeof(struct sockaddr_in);530int p, s, rv;531532ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);533ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);534ATF_REQUIRE(listen(p, 1) == 0);535536s = lb_listen_socket(PF_INET, 0);537ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);538ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);539rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));540ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,541"Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",542rv, errno);543rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,544sizeof(sin));545ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,546"Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",547rv, errno);548549close(p);550close(s);551}552553ATF_TP_ADD_TCS(tp)554{555ATF_TP_ADD_TC(tp, basic_ipv4);556ATF_TP_ADD_TC(tp, basic_ipv6);557ATF_TP_ADD_TC(tp, concurrent_add);558ATF_TP_ADD_TC(tp, double_listen_ipv4);559ATF_TP_ADD_TC(tp, double_listen_ipv6);560ATF_TP_ADD_TC(tp, bind_without_listen);561ATF_TP_ADD_TC(tp, connect_not_bound);562ATF_TP_ADD_TC(tp, connect_bound);563564return (atf_no_error());565}566567568