Path: blob/main/tests/sys/netinet/so_reuseport_lb_test.c
107074 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/filio.h>32#include <sys/ioccom.h>33#include <sys/socket.h>3435#include <netinet/in.h>36#include <netinet/tcp.h>3738#include <err.h>39#include <errno.h>40#include <pthread.h>41#include <stdatomic.h>42#include <stdlib.h>43#include <unistd.h>4445#include <atf-c.h>4647/*48* Given an array of non-blocking listening sockets configured in a LB group49* for "addr", try connecting to "addr" in a loop and verify that connections50* are roughly balanced across the sockets.51*/52static void53lb_simple_accept_loop(int domain, const struct sockaddr *addr, int sds[],54size_t nsds, int nconns)55{56size_t i;57int *acceptcnt;58int csd, error, excnt, sd;59const struct linger lopt = { 1, 0 };6061/*62* We expect each listening socket to accept roughly nconns/nsds63* connections, but allow for some error.64*/65excnt = nconns / nsds / 8;66acceptcnt = calloc(nsds, sizeof(*acceptcnt));67ATF_REQUIRE_MSG(acceptcnt != NULL, "calloc() failed: %s",68strerror(errno));6970while (nconns-- > 0) {71sd = socket(domain, SOCK_STREAM, 0);72ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s",73strerror(errno));7475error = connect(sd, addr, addr->sa_len);76ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",77strerror(errno));7879error = setsockopt(sd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));80ATF_REQUIRE_MSG(error == 0, "Setting linger failed: %s",81strerror(errno));8283/*84* Poll the listening sockets.85*/86do {87for (i = 0; i < nsds; i++) {88csd = accept(sds[i], NULL, NULL);89if (csd < 0) {90ATF_REQUIRE_MSG(errno == EWOULDBLOCK ||91errno == EAGAIN,92"accept() failed: %s",93strerror(errno));94continue;95}9697error = close(csd);98ATF_REQUIRE_MSG(error == 0,99"close() failed: %s", strerror(errno));100101acceptcnt[i]++;102break;103}104} while (i == nsds);105106error = close(sd);107ATF_REQUIRE_MSG(error == 0, "close() failed: %s",108strerror(errno));109}110111for (i = 0; i < nsds; i++)112ATF_REQUIRE_MSG(acceptcnt[i] > excnt, "uneven balancing");113}114115static int116lb_listen_socket(int domain, int flags)117{118int one;119int error, sd;120121sd = socket(domain, SOCK_STREAM | flags, 0);122ATF_REQUIRE_MSG(sd >= 0, "socket() failed: %s", strerror(errno));123124one = 1;125error = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT_LB, &one, sizeof(one));126ATF_REQUIRE_MSG(error == 0, "setsockopt(SO_REUSEPORT_LB) failed: %s",127strerror(errno));128129return (sd);130}131132ATF_TC_WITHOUT_HEAD(basic_ipv4);133ATF_TC_BODY(basic_ipv4, tc)134{135struct sockaddr_in addr;136socklen_t slen;137size_t i;138const int nconns = 16384;139int error, sds[16];140uint16_t port;141142sds[0] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);143144memset(&addr, 0, sizeof(addr));145addr.sin_len = sizeof(addr);146addr.sin_family = AF_INET;147addr.sin_port = htons(0);148addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);149error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));150ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));151error = listen(sds[0], 1);152ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));153154slen = sizeof(addr);155error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);156ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",157strerror(errno));158ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");159port = addr.sin_port;160161memset(&addr, 0, sizeof(addr));162addr.sin_len = sizeof(addr);163addr.sin_family = AF_INET;164addr.sin_port = port;165addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);166for (i = 1; i < nitems(sds); i++) {167sds[i] = lb_listen_socket(PF_INET, SOCK_NONBLOCK);168169error = bind(sds[i], (const struct sockaddr *)&addr,170sizeof(addr));171ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",172strerror(errno));173error = listen(sds[i], 1);174ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",175strerror(errno));176}177178lb_simple_accept_loop(PF_INET, (struct sockaddr *)&addr, sds,179nitems(sds), nconns);180for (i = 0; i < nitems(sds); i++) {181error = close(sds[i]);182ATF_REQUIRE_MSG(error == 0, "close() failed: %s",183strerror(errno));184}185}186187ATF_TC_WITHOUT_HEAD(basic_ipv6);188ATF_TC_BODY(basic_ipv6, tc)189{190const struct in6_addr loopback6 = IN6ADDR_LOOPBACK_INIT;191struct sockaddr_in6 addr;192socklen_t slen;193size_t i;194const int nconns = 16384;195int error, sds[16];196uint16_t port;197198sds[0] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);199200memset(&addr, 0, sizeof(addr));201addr.sin6_len = sizeof(addr);202addr.sin6_family = AF_INET6;203addr.sin6_port = htons(0);204addr.sin6_addr = loopback6;205error = bind(sds[0], (const struct sockaddr *)&addr, sizeof(addr));206ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));207error = listen(sds[0], 1);208ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));209210slen = sizeof(addr);211error = getsockname(sds[0], (struct sockaddr *)&addr, &slen);212ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",213strerror(errno));214ATF_REQUIRE_MSG(slen == sizeof(addr), "sockaddr size changed");215port = addr.sin6_port;216217memset(&addr, 0, sizeof(addr));218addr.sin6_len = sizeof(addr);219addr.sin6_family = AF_INET6;220addr.sin6_port = port;221addr.sin6_addr = loopback6;222for (i = 1; i < nitems(sds); i++) {223sds[i] = lb_listen_socket(PF_INET6, SOCK_NONBLOCK);224225error = bind(sds[i], (const struct sockaddr *)&addr,226sizeof(addr));227ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",228strerror(errno));229error = listen(sds[i], 1);230ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",231strerror(errno));232}233234lb_simple_accept_loop(PF_INET6, (struct sockaddr *)&addr, sds,235nitems(sds), nconns);236for (i = 0; i < nitems(sds); i++) {237error = close(sds[i]);238ATF_REQUIRE_MSG(error == 0, "close() failed: %s",239strerror(errno));240}241}242243struct concurrent_add_softc {244struct sockaddr_storage ss;245int socks[128];246int kq;247};248249static void *250listener(void *arg)251{252for (struct concurrent_add_softc *sc = arg;;) {253struct kevent kev;254ssize_t n;255int error, count, cs, s;256uint8_t b;257258count = kevent(sc->kq, NULL, 0, &kev, 1, NULL);259ATF_REQUIRE_MSG(count == 1,260"kevent() failed: %s", strerror(errno));261262s = (int)kev.ident;263cs = accept(s, NULL, NULL);264ATF_REQUIRE_MSG(cs >= 0,265"accept() failed: %s", strerror(errno));266267b = 'M';268n = write(cs, &b, sizeof(b));269ATF_REQUIRE_MSG(n >= 0, "write() failed: %s", strerror(errno));270ATF_REQUIRE(n == 1);271272error = close(cs);273ATF_REQUIRE_MSG(error == 0 || errno == ECONNRESET,274"close() failed: %s", strerror(errno));275}276}277278static void *279connector(void *arg)280{281for (struct concurrent_add_softc *sc = arg;;) {282ssize_t n;283int error, s;284uint8_t b;285286s = socket(sc->ss.ss_family, SOCK_STREAM, 0);287ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));288289error = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (int[]){1},290sizeof(int));291292error = connect(s, (struct sockaddr *)&sc->ss, sc->ss.ss_len);293ATF_REQUIRE_MSG(error == 0, "connect() failed: %s",294strerror(errno));295296n = read(s, &b, sizeof(b));297ATF_REQUIRE_MSG(n >= 0, "read() failed: %s",298strerror(errno));299ATF_REQUIRE(n == 1);300ATF_REQUIRE(b == 'M');301error = close(s);302ATF_REQUIRE_MSG(error == 0,303"close() failed: %s", strerror(errno));304}305}306307/*308* Run three threads. One accepts connections from listening sockets on a309* kqueue, while the other makes connections. The third thread slowly adds310* sockets to the LB group. This is meant to help flush out race conditions.311*/312ATF_TC_WITHOUT_HEAD(concurrent_add);313ATF_TC_BODY(concurrent_add, tc)314{315struct concurrent_add_softc sc;316struct sockaddr_in *sin;317pthread_t threads[4];318int error;319320sc.kq = kqueue();321ATF_REQUIRE_MSG(sc.kq >= 0, "kqueue() failed: %s", strerror(errno));322323error = pthread_create(&threads[0], NULL, listener, &sc);324ATF_REQUIRE_MSG(error == 0, "pthread_create() failed: %s",325strerror(error));326327sin = (struct sockaddr_in *)&sc.ss;328memset(sin, 0, sizeof(*sin));329sin->sin_len = sizeof(*sin);330sin->sin_family = AF_INET;331sin->sin_port = htons(0);332sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);333334for (size_t i = 0; i < nitems(sc.socks); i++) {335struct kevent kev;336int s;337338sc.socks[i] = s = socket(AF_INET, SOCK_STREAM, 0);339ATF_REQUIRE_MSG(s >= 0, "socket() failed: %s", strerror(errno));340341error = setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},342sizeof(int));343ATF_REQUIRE_MSG(error == 0,344"setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));345346error = bind(s, (struct sockaddr *)sin, sizeof(*sin));347ATF_REQUIRE_MSG(error == 0, "bind() failed: %s",348strerror(errno));349350error = listen(s, 5);351ATF_REQUIRE_MSG(error == 0, "listen() failed: %s",352strerror(errno));353354EV_SET(&kev, s, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0);355error = kevent(sc.kq, &kev, 1, NULL, 0, NULL);356ATF_REQUIRE_MSG(error == 0, "kevent() failed: %s",357strerror(errno));358359if (i == 0) {360socklen_t slen = sizeof(sc.ss);361362error = getsockname(sc.socks[i],363(struct sockaddr *)&sc.ss, &slen);364ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",365strerror(errno));366ATF_REQUIRE(sc.ss.ss_family == AF_INET);367368for (size_t j = 1; j < nitems(threads); j++) {369error = pthread_create(&threads[j], NULL,370connector, &sc);371ATF_REQUIRE_MSG(error == 0,372"pthread_create() failed: %s",373strerror(error));374}375}376377usleep(20000);378}379380for (size_t j = nitems(threads); j > 0; j--) {381ATF_REQUIRE(pthread_cancel(threads[j - 1]) == 0);382ATF_REQUIRE(pthread_join(threads[j - 1], NULL) == 0);383}384}385386/*387* Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.388*/389ATF_TC_WITHOUT_HEAD(double_listen_ipv4);390ATF_TC_BODY(double_listen_ipv4, tc)391{392struct sockaddr_in sin;393int error, s;394395s = lb_listen_socket(PF_INET, 0);396397memset(&sin, 0, sizeof(sin));398sin.sin_len = sizeof(sin);399sin.sin_family = AF_INET;400sin.sin_port = htons(0);401sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);402error = bind(s, (struct sockaddr *)&sin, sizeof(sin));403ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));404405error = listen(s, 1);406ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));407error = listen(s, 2);408ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));409410error = close(s);411ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));412}413414/*415* Try calling listen(2) twice on a socket with SO_REUSEPORT_LB set.416*/417ATF_TC_WITHOUT_HEAD(double_listen_ipv6);418ATF_TC_BODY(double_listen_ipv6, tc)419{420struct sockaddr_in6 sin6;421int error, s;422423s = lb_listen_socket(PF_INET6, 0);424425memset(&sin6, 0, sizeof(sin6));426sin6.sin6_len = sizeof(sin6);427sin6.sin6_family = AF_INET6;428sin6.sin6_port = htons(0);429sin6.sin6_addr = in6addr_loopback;430error = bind(s, (struct sockaddr *)&sin6, sizeof(sin6));431ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));432433error = listen(s, 1);434ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));435error = listen(s, 2);436ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));437438error = close(s);439ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));440}441442/*443* Try binding many sockets to the same lbgroup without calling listen(2) on444* them.445*/446ATF_TC_WITHOUT_HEAD(bind_without_listen);447ATF_TC_BODY(bind_without_listen, tc)448{449const int nsockets = 100;450struct sockaddr_in sin;451socklen_t socklen;452int error, s, s2[nsockets];453454s = lb_listen_socket(PF_INET, 0);455456memset(&sin, 0, sizeof(sin));457sin.sin_len = sizeof(sin);458sin.sin_family = AF_INET;459sin.sin_port = htons(0);460sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);461error = bind(s, (struct sockaddr *)&sin, sizeof(sin));462ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));463464socklen = sizeof(sin);465error = getsockname(s, (struct sockaddr *)&sin, &socklen);466ATF_REQUIRE_MSG(error == 0, "getsockname() failed: %s",467strerror(errno));468469for (int i = 0; i < nsockets; i++) {470s2[i] = lb_listen_socket(PF_INET, 0);471error = bind(s2[i], (struct sockaddr *)&sin, sizeof(sin));472ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));473}474for (int i = 0; i < nsockets; i++) {475error = listen(s2[i], 1);476ATF_REQUIRE_MSG(error == 0, "listen() failed: %s", strerror(errno));477}478for (int i = 0; i < nsockets; i++) {479error = close(s2[i]);480ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));481}482483error = close(s);484ATF_REQUIRE_MSG(error == 0, "close() failed: %s", strerror(errno));485}486487/*488* Check that SO_REUSEPORT_LB doesn't mess with connect(2).489* Two sockets:490* 1) auxiliary peer socket 'p', where we connect to491* 2) test socket 's', that sets SO_REUSEPORT_LB and then connect(2)s to 'p'492*/493ATF_TC_WITHOUT_HEAD(connect_not_bound);494ATF_TC_BODY(connect_not_bound, tc)495{496struct sockaddr_in sin = {497.sin_family = AF_INET,498.sin_len = sizeof(sin),499.sin_addr = { htonl(INADDR_LOOPBACK) },500};501socklen_t slen = sizeof(struct sockaddr_in);502int p, s, rv;503504ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);505ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);506ATF_REQUIRE(listen(p, 1) == 0);507ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);508509s = lb_listen_socket(PF_INET, 0);510rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));511ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,512"Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",513rv, errno);514rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,515sizeof(sin));516ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,517"Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",518rv, errno);519520close(p);521close(s);522}523524/*525* Same as above, but we also bind(2) between setsockopt(2) of SO_REUSEPORT_LB526* and the connect(2).527*/528ATF_TC_WITHOUT_HEAD(connect_bound);529ATF_TC_BODY(connect_bound, tc)530{531struct sockaddr_in sin = {532.sin_family = AF_INET,533.sin_len = sizeof(sin),534.sin_addr = { htonl(INADDR_LOOPBACK) },535};536socklen_t slen = sizeof(struct sockaddr_in);537int p, s, rv;538539ATF_REQUIRE((p = socket(PF_INET, SOCK_STREAM, 0)) > 0);540ATF_REQUIRE(bind(p, (struct sockaddr *)&sin, sizeof(sin)) == 0);541ATF_REQUIRE(listen(p, 1) == 0);542543s = lb_listen_socket(PF_INET, 0);544ATF_REQUIRE(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);545ATF_REQUIRE(getsockname(p, (struct sockaddr *)&sin, &slen) == 0);546rv = connect(s, (struct sockaddr *)&sin, sizeof(sin));547ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,548"Expected EOPNOTSUPP on connect(2) not met. Got %d, errno %d",549rv, errno);550rv = sendto(s, "test", 4, 0, (struct sockaddr *)&sin,551sizeof(sin));552ATF_REQUIRE_MSG(rv == -1 && errno == EOPNOTSUPP,553"Expected EOPNOTSUPP on sendto(2) not met. Got %d, errno %d",554rv, errno);555556close(p);557close(s);558}559560/*561* The kernel erroneously permits calling connect() on a UDP socket with562* SO_REUSEPORT_LB set. Verify that packets sent to the bound address are563* dropped unless they come from the connected address.564*/565ATF_TC_WITHOUT_HEAD(connect_udp);566ATF_TC_BODY(connect_udp, tc)567{568struct sockaddr_in sin = {569.sin_family = AF_INET,570.sin_len = sizeof(sin),571.sin_addr = { htonl(INADDR_LOOPBACK) },572};573ssize_t n;574int error, len, s1, s2, s3;575char ch;576577s1 = socket(PF_INET, SOCK_DGRAM, 0);578ATF_REQUIRE(s1 >= 0);579s2 = socket(PF_INET, SOCK_DGRAM, 0);580ATF_REQUIRE(s2 >= 0);581s3 = socket(PF_INET, SOCK_DGRAM, 0);582ATF_REQUIRE(s3 >= 0);583584error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},585sizeof(int));586ATF_REQUIRE_MSG(error == 0,587"setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));588error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));589ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));590591error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));592ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));593594error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));595ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));596597/* Connect to an address not owned by s2. */598error = getsockname(s3, (struct sockaddr *)&sin,599(socklen_t[]){sizeof(sin)});600ATF_REQUIRE(error == 0);601error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));602ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));603604/* Try to send a packet to s1 from s2. */605error = getsockname(s1, (struct sockaddr *)&sin,606(socklen_t[]){sizeof(sin)});607ATF_REQUIRE(error == 0);608609ch = 42;610n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,611sizeof(sin));612ATF_REQUIRE(n == 1);613614/* Give the packet some time to arrive. */615usleep(100000);616617/* s1 is connected to s3 and shouldn't receive from s2. */618error = ioctl(s1, FIONREAD, &len);619ATF_REQUIRE(error == 0);620ATF_REQUIRE_MSG(len == 0, "unexpected data available");621622/* ... but s3 can of course send to s1. */623n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,624sizeof(sin));625ATF_REQUIRE(n == 1);626usleep(100000);627error = ioctl(s1, FIONREAD, &len);628ATF_REQUIRE(error == 0);629ATF_REQUIRE_MSG(len == 1, "expected data available");630}631632/*633* The kernel erroneously permits calling connect() on a UDP socket with634* SO_REUSEPORT_LB set. Verify that packets sent to the bound address are635* dropped unless they come from the connected address.636*/637ATF_TC_WITHOUT_HEAD(connect_udp6);638ATF_TC_BODY(connect_udp6, tc)639{640struct sockaddr_in6 sin6 = {641.sin6_family = AF_INET6,642.sin6_len = sizeof(sin6),643.sin6_addr = IN6ADDR_LOOPBACK_INIT,644};645ssize_t n;646int error, len, s1, s2, s3;647char ch;648649s1 = socket(PF_INET6, SOCK_DGRAM, 0);650ATF_REQUIRE(s1 >= 0);651s2 = socket(PF_INET6, SOCK_DGRAM, 0);652ATF_REQUIRE(s2 >= 0);653s3 = socket(PF_INET6, SOCK_DGRAM, 0);654ATF_REQUIRE(s3 >= 0);655656error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},657sizeof(int));658ATF_REQUIRE_MSG(error == 0,659"setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));660error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));661ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));662663error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));664ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));665666error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));667ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));668669/* Connect to an address not owned by s2. */670error = getsockname(s3, (struct sockaddr *)&sin6,671(socklen_t[]){sizeof(sin6)});672ATF_REQUIRE(error == 0);673error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));674ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));675676/* Try to send a packet to s1 from s2. */677error = getsockname(s1, (struct sockaddr *)&sin6,678(socklen_t[]){sizeof(sin6)});679ATF_REQUIRE(error == 0);680681ch = 42;682n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,683sizeof(sin6));684ATF_REQUIRE(n == 1);685686/* Give the packet some time to arrive. */687usleep(100000);688689/* s1 is connected to s3 and shouldn't receive from s2. */690error = ioctl(s1, FIONREAD, &len);691ATF_REQUIRE(error == 0);692ATF_REQUIRE_MSG(len == 0, "unexpected data available");693694/* ... but s3 can of course send to s1. */695n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,696sizeof(sin6));697ATF_REQUIRE(n == 1);698usleep(100000);699error = ioctl(s1, FIONREAD, &len);700ATF_REQUIRE(error == 0);701ATF_REQUIRE_MSG(len == 1, "expected data available");702}703704ATF_TP_ADD_TCS(tp)705{706ATF_TP_ADD_TC(tp, basic_ipv4);707ATF_TP_ADD_TC(tp, basic_ipv6);708ATF_TP_ADD_TC(tp, concurrent_add);709ATF_TP_ADD_TC(tp, double_listen_ipv4);710ATF_TP_ADD_TC(tp, double_listen_ipv6);711ATF_TP_ADD_TC(tp, bind_without_listen);712ATF_TP_ADD_TC(tp, connect_not_bound);713ATF_TP_ADD_TC(tp, connect_bound);714ATF_TP_ADD_TC(tp, connect_udp);715ATF_TP_ADD_TC(tp, connect_udp6);716717return (atf_no_error());718}719720721