Path: blob/main/tests/sys/netinet/fibs_multibind_test.c
39483 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2024-2025 Stormshield4*/56#include <sys/types.h>7#include <sys/ioctl.h>8#include <sys/socket.h>9#include <sys/sysctl.h>1011#include <netinet/in.h>12#include <netinet/ip.h>13#include <netinet/ip6.h>14#include <netinet/ip_icmp.h>15#include <netinet/icmp6.h>1617#include <errno.h>18#include <fcntl.h>19#include <pwd.h>20#include <stdio.h>21#include <string.h>2223#include <atf-c.h>2425#define MAKETEST_TCP(name) \26ATF_TC_WITHOUT_HEAD(name ## _tcp); \27ATF_TC_BODY(name ## _tcp, tc) \28{ \29name(PF_INET, SOCK_STREAM, tc); \30} \31ATF_TC_WITHOUT_HEAD(name ## _tcp6); \32ATF_TC_BODY(name ## _tcp6, tc) \33{ \34name(PF_INET6, SOCK_STREAM, tc); \35}36#define MAKETEST_UDP(name) \37ATF_TC_WITHOUT_HEAD(name ## _udp); \38ATF_TC_BODY(name ## _udp, tc) \39{ \40name(PF_INET, SOCK_DGRAM, tc); \41} \42ATF_TC_WITHOUT_HEAD(name ## _udp6); \43ATF_TC_BODY(name ## _udp6, tc) \44{ \45name(PF_INET6, SOCK_DGRAM, tc); \46}47#define MAKETEST_RAW(name) \48ATF_TC(name ## _raw); \49ATF_TC_HEAD(name ## _raw, tc) \50{ \51atf_tc_set_md_var(tc, "require.user", \52"root"); \53} \54ATF_TC_BODY(name ## _raw, tc) \55{ \56name(PF_INET, SOCK_RAW, tc); \57} \58ATF_TC(name ## _raw6); \59ATF_TC_HEAD(name ## _raw6, tc) \60{ \61atf_tc_set_md_var(tc, "require.user", \62"root"); \63} \64ATF_TC_BODY(name ## _raw6, tc) \65{ \66name(PF_INET6, SOCK_RAW, tc); \67}6869#define MAKETEST(name) \70MAKETEST_TCP(name) \71MAKETEST_UDP(name)7273#define LISTTEST_TCP(name) \74ATF_TP_ADD_TC(tp, name ## _tcp); \75ATF_TP_ADD_TC(tp, name ## _tcp6);76#define LISTTEST_UDP(name) \77ATF_TP_ADD_TC(tp, name ## _udp); \78ATF_TP_ADD_TC(tp, name ## _udp6);79#define LISTTEST_RAW(name) \80ATF_TP_ADD_TC(tp, name ## _raw); \81ATF_TP_ADD_TC(tp, name ## _raw6);82#define LISTTEST(name) \83LISTTEST_TCP(name) \84LISTTEST_UDP(name)8586static void87checked_close(int s)88{89int error;9091error = close(s);92ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));93}9495static int96mksockp(int domain, int type, int fib, int proto)97{98int error, s;99100s = socket(domain, type, proto);101ATF_REQUIRE(s != -1);102error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &fib, sizeof(fib));103ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));104105return (s);106}107108static int109mksock(int domain, int type, int fib)110{111return (mksockp(domain, type, fib, 0));112}113114static void115require_fibs_multibind(int socktype, int minfibs)116{117const char *sysctl;118size_t sz;119int error, fibs, multibind;120121fibs = 0;122sz = sizeof(fibs);123error = sysctlbyname("net.fibs", &fibs, &sz, NULL, 0);124ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));125ATF_REQUIRE_MSG(fibs >= 1, "strange FIB count %d", fibs);126if (fibs == 1)127atf_tc_skip("multiple FIBs not enabled");128if (fibs < minfibs)129atf_tc_skip("not enough FIBs, need %d", minfibs);130131switch (socktype) {132case SOCK_STREAM:133sysctl = "net.inet.tcp.bind_all_fibs";134break;135case SOCK_DGRAM:136sysctl = "net.inet.udp.bind_all_fibs";137break;138case SOCK_RAW:139sysctl = "net.inet.raw.bind_all_fibs";140break;141default:142atf_tc_fail("unknown socket type %d", socktype);143break;144}145146multibind = -1;147sz = sizeof(multibind);148error = sysctlbyname(sysctl, &multibind, &sz, NULL, 0);149ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));150if (multibind != 0)151atf_tc_skip("FIB multibind not configured (%s)", sysctl);152}153154/*155* Make sure that different users can't bind to the same port from different156* FIBs.157*/158static void159multibind_different_user(int domain, int type, const atf_tc_t *tc)160{161struct sockaddr_storage ss;162struct passwd *passwd;163const char *user;164socklen_t sslen;165int error, s[2];166167if (geteuid() != 0)168atf_tc_skip("need root privileges");169if (!atf_tc_has_config_var(tc, "unprivileged_user"))170atf_tc_skip("unprivileged_user not set");171172ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);173sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :174sizeof(struct sockaddr_in6);175176require_fibs_multibind(type, 2);177178s[0] = mksock(domain, type, 0);179180memset(&ss, 0, sizeof(ss));181ss.ss_family = domain;182ss.ss_len = sslen;183error = bind(s[0], (struct sockaddr *)&ss, sslen);184ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));185186error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);187ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));188189/*190* Create a second socket in a different FIB, and bind it to the same191* address/port tuple. This should succeed if done as the same user as192* the first socket, and should fail otherwise.193*/194s[1] = mksock(domain, type, 1);195error = bind(s[1], (struct sockaddr *)&ss, sslen);196ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));197ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));198199user = atf_tc_get_config_var(tc, "unprivileged_user");200passwd = getpwnam(user);201ATF_REQUIRE(passwd != NULL);202error = seteuid(passwd->pw_uid);203ATF_REQUIRE_MSG(error == 0, "seteuid failed: %s", strerror(errno));204205/* Repeat the bind as a different user. */206s[1] = mksock(domain, type, 1);207error = bind(s[1], (struct sockaddr *)&ss, sslen);208ATF_REQUIRE_ERRNO(EADDRINUSE, error == -1);209ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));210}211MAKETEST(multibind_different_user);212213/*214* Verify that a listening socket only accepts connections originating from the215* same FIB.216*/217static void218per_fib_listening_socket(int domain, int type, const atf_tc_t *tc __unused)219{220struct sockaddr_storage ss;221socklen_t sslen;222int cs1, cs2, error, fib1, fib2, ls1, ls2, ns;223224ATF_REQUIRE(type == SOCK_STREAM);225ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);226require_fibs_multibind(type, 2);227228fib1 = 0;229fib2 = 1;230231ls1 = mksock(domain, type, fib1);232ls2 = mksock(domain, type, fib2);233234sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :235sizeof(struct sockaddr_in6);236237memset(&ss, 0, sizeof(ss));238ss.ss_family = domain;239ss.ss_len = sslen;240error = bind(ls1, (struct sockaddr *)&ss, sslen);241ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));242243error = getsockname(ls1, (struct sockaddr *)&ss, &sslen);244ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));245246error = listen(ls1, 5);247ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));248249cs1 = mksock(domain, type, fib1);250cs2 = mksock(domain, type, fib2);251252/*253* Make sure we can connect from the same FIB.254*/255error = connect(cs1, (struct sockaddr *)&ss, sslen);256ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));257ns = accept(ls1, NULL, NULL);258ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));259checked_close(ns);260checked_close(cs1);261cs1 = mksock(domain, type, fib1);262263/*264* ... but not from a different FIB.265*/266error = connect(cs2, (struct sockaddr *)&ss, sslen);267ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");268ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);269checked_close(cs2);270cs2 = mksock(domain, type, fib2);271272/*273* ... but if there are multiple listening sockets, we always connect to274* the same FIB.275*/276error = bind(ls2, (struct sockaddr *)&ss, sslen);277ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));278error = listen(ls2, 5);279ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));280281for (int i = 0; i < 10; i++) {282error = connect(cs1, (struct sockaddr *)&ss, sslen);283ATF_REQUIRE_MSG(error == 0, "connect failed: %s",284strerror(errno));285ns = accept(ls1, NULL, NULL);286ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));287288checked_close(ns);289checked_close(cs1);290cs1 = mksock(domain, type, fib1);291}292for (int i = 0; i < 10; i++) {293error = connect(cs2, (struct sockaddr *)&ss, sslen);294ATF_REQUIRE_MSG(error == 0, "connect failed: %s",295strerror(errno));296ns = accept(ls2, NULL, NULL);297ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));298299checked_close(ns);300checked_close(cs2);301cs2 = mksock(domain, type, fib2);302}303304/*305* ... and if we close one of the listening sockets, we're back to only306* being able to connect from the same FIB.307*/308checked_close(ls1);309error = connect(cs1, (struct sockaddr *)&ss, sslen);310ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");311ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);312checked_close(cs1);313314error = connect(cs2, (struct sockaddr *)&ss, sslen);315ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));316ns = accept(ls2, NULL, NULL);317ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));318checked_close(ns);319checked_close(cs2);320checked_close(ls2);321}322MAKETEST_TCP(per_fib_listening_socket);323324/*325* Verify that a bound datagram socket only accepts data from the same FIB.326*/327static void328per_fib_dgram_socket(int domain, int type, const atf_tc_t *tc __unused)329{330struct sockaddr_storage ss;331struct sockaddr_in6 *sin6p;332socklen_t sslen;333ssize_t n;334int error, cs1, cs2, fib1, fib2, ss1, ss2;335char b;336337ATF_REQUIRE(type == SOCK_DGRAM);338ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);339require_fibs_multibind(type, 2);340341fib1 = 0;342fib2 = 1;343344cs1 = mksock(domain, type, fib1);345cs2 = mksock(domain, type, fib2);346347ss1 = mksock(domain, type, fib1);348ss2 = mksock(domain, type, fib2);349350sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :351sizeof(struct sockaddr_in6);352353memset(&ss, 0, sizeof(ss));354ss.ss_family = domain;355ss.ss_len = sslen;356error = bind(ss1, (struct sockaddr *)&ss, sslen);357ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));358359error = getsockname(ss1, (struct sockaddr *)&ss, &sslen);360ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));361362if (domain == PF_INET6) {363sin6p = (struct sockaddr_in6 *)&ss;364sin6p->sin6_addr = in6addr_loopback;365}366367/* If we send a byte from cs1, it should be recieved by ss1. */368b = 42;369n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);370ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));371n = recv(ss1, &b, sizeof(b), 0);372ATF_REQUIRE(n == 1);373ATF_REQUIRE(b == 42);374375/* If we send a byte from cs2, it should not be received by ss1. */376b = 42;377n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);378ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));379usleep(10000);380n = recv(ss1, &b, sizeof(b), MSG_DONTWAIT);381ATF_REQUIRE_ERRNO(EWOULDBLOCK, n == -1);382383error = bind(ss2, (struct sockaddr *)&ss, sslen);384ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));385386/* Repeat now that ss2 is bound. */387b = 42;388n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);389ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));390n = recv(ss1, &b, sizeof(b), 0);391ATF_REQUIRE(n == 1);392ATF_REQUIRE(b == 42);393394b = 42;395n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);396ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));397n = recv(ss2, &b, sizeof(b), 0);398ATF_REQUIRE(n == 1);399ATF_REQUIRE(b == 42);400401checked_close(ss1);402checked_close(ss2);403checked_close(cs1);404checked_close(cs2);405}406MAKETEST_UDP(per_fib_dgram_socket);407408static size_t409ping(int s, const struct sockaddr *sa, socklen_t salen)410{411struct {412struct icmphdr icmp;413char data[64];414} icmp;415ssize_t n;416417memset(&icmp, 0, sizeof(icmp));418icmp.icmp.icmp_type = ICMP_ECHO;419icmp.icmp.icmp_code = 0;420icmp.icmp.icmp_cksum = htons((unsigned short)~(ICMP_ECHO << 8));421n = sendto(s, &icmp, sizeof(icmp), 0, sa, salen);422ATF_REQUIRE_MSG(n == (ssize_t)sizeof(icmp), "sendto failed: %s",423strerror(errno));424425return (sizeof(icmp) + sizeof(struct ip));426}427428static size_t429ping6(int s, const struct sockaddr *sa, socklen_t salen)430{431struct {432struct icmp6_hdr icmp6;433char data[64];434} icmp6;435ssize_t n;436437memset(&icmp6, 0, sizeof(icmp6));438icmp6.icmp6.icmp6_type = ICMP6_ECHO_REQUEST;439icmp6.icmp6.icmp6_code = 0;440icmp6.icmp6.icmp6_cksum =441htons((unsigned short)~(ICMP6_ECHO_REQUEST << 8));442n = sendto(s, &icmp6, sizeof(icmp6), 0, sa, salen);443ATF_REQUIRE_MSG(n == (ssize_t)sizeof(icmp6), "sendto failed: %s",444strerror(errno));445446return (sizeof(icmp6));447}448449static void450per_fib_raw_socket(int domain, int type, const atf_tc_t *tc __unused)451{452struct sockaddr_in sin;453struct sockaddr_in6 sin6;454ssize_t n;455size_t sz;456int error, cs, s[2], proto;457uint8_t b[256];458459ATF_REQUIRE(type == SOCK_RAW);460ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);461require_fibs_multibind(type, 2);462463proto = domain == PF_INET ? IPPROTO_ICMP : IPPROTO_ICMPV6;464s[0] = mksockp(domain, type, 0, proto);465s[1] = mksockp(domain, type, 1, proto);466467if (domain == PF_INET) {468memset(&sin, 0, sizeof(sin));469sin.sin_family = domain;470sin.sin_len = sizeof(sin);471sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);472error = bind(s[0], (struct sockaddr *)&sin, sizeof(sin));473} else /* if (domain == PF_INET6) */ {474memset(&sin6, 0, sizeof(sin6));475sin6.sin6_family = domain;476sin6.sin6_len = sizeof(sin6);477sin6.sin6_addr = in6addr_loopback;478error = bind(s[0], (struct sockaddr *)&sin6, sizeof(sin6));479}480ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));481482for (int i = 0; i < 2; i++) {483cs = mksockp(domain, type, i, proto);484if (domain == PF_INET) {485sz = ping(cs, (struct sockaddr *)&sin, sizeof(sin));486} else /* if (domain == PF_INET6) */ {487sz = ping6(cs, (struct sockaddr *)&sin6, sizeof(sin6));488}489n = recv(s[i], b, sizeof(b), 0);490ATF_REQUIRE_MSG(n > 0, "recv failed: %s", strerror(errno));491ATF_REQUIRE_MSG(n == (ssize_t)sz,492"short packet received: %zd", n);493494if (domain == PF_INET6) {495/* Get the echo reply as well. */496n = recv(s[i], b, sizeof(b), 0);497ATF_REQUIRE_MSG(n > 0,498"recv failed: %s", strerror(errno));499ATF_REQUIRE_MSG(n == (ssize_t)sz,500"short packet received: %zd", n);501}502503/* Make sure that the other socket didn't receive anything. */504n = recv(s[1 - i], b, sizeof(b), MSG_DONTWAIT);505printf("n = %zd i = %d\n", n, i);506ATF_REQUIRE_ERRNO(EWOULDBLOCK, n == -1);507508checked_close(cs);509}510511checked_close(s[0]);512checked_close(s[1]);513}514MAKETEST_RAW(per_fib_raw_socket);515516/*517* Create a pair of load-balancing listening socket groups, one in each FIB, and518* make sure that connections to the group are only load-balanced within the519* same FIB.520*/521static void522multibind_lbgroup_stream(int domain, int type, const atf_tc_t *tc __unused)523{524struct sockaddr_storage ss;525socklen_t sslen;526int error, as, cs, s[3];527528ATF_REQUIRE(type == SOCK_STREAM);529ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);530require_fibs_multibind(type, 2);531532s[0] = mksock(domain, type, 0);533ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},534sizeof(int)) == 0);535ATF_REQUIRE(fcntl(s[0], F_SETFL, O_NONBLOCK) == 0);536s[1] = mksock(domain, type, 0);537ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},538sizeof(int)) == 0);539ATF_REQUIRE(fcntl(s[1], F_SETFL, O_NONBLOCK) == 0);540s[2] = mksock(domain, type, 1);541ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},542sizeof(int)) == 0);543544sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :545sizeof(struct sockaddr_in6);546memset(&ss, 0, sizeof(ss));547ss.ss_family = domain;548ss.ss_len = sslen;549error = bind(s[0], (struct sockaddr *)&ss, sslen);550ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));551error = listen(s[0], 5);552ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));553error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);554ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));555556error = bind(s[1], (struct sockaddr *)&ss, sslen);557ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));558error = listen(s[1], 5);559ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));560561error = bind(s[2], (struct sockaddr *)&ss, sslen);562ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));563error = listen(s[2], 5);564ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));565566/*567* Initiate connections from FIB 0, make sure they go to s[0] or s[1].568*/569for (int count = 0; count < 100; count++) {570cs = mksock(domain, type, 0);571error = connect(cs, (struct sockaddr *)&ss, sslen);572ATF_REQUIRE_MSG(error == 0, "connect failed: %s",573strerror(errno));574575do {576as = accept(s[0], NULL, NULL);577if (as == -1) {578ATF_REQUIRE_MSG(errno == EWOULDBLOCK,579"accept failed: %s", strerror(errno));580as = accept(s[1], NULL, NULL);581if (as == -1) {582ATF_REQUIRE_MSG(errno == EWOULDBLOCK,583"accept failed: %s",584strerror(errno));585}586}587} while (as == -1);588checked_close(as);589checked_close(cs);590}591592/*593* Initiate connections from FIB 1, make sure they go to s[2].594*/595for (int count = 0; count < 100; count++) {596cs = mksock(domain, type, 1);597error = connect(cs, (struct sockaddr *)&ss, sslen);598ATF_REQUIRE_MSG(error == 0, "connect failed: %s",599strerror(errno));600601as = accept(s[2], NULL, NULL);602ATF_REQUIRE_MSG(as != -1, "accept failed: %s", strerror(errno));603checked_close(as);604checked_close(cs);605}606607checked_close(s[0]);608checked_close(s[1]);609checked_close(s[2]);610}611MAKETEST_TCP(multibind_lbgroup_stream);612613static void614multibind_lbgroup_dgram(int domain, int type, const atf_tc_t *tc __unused)615{616struct sockaddr_storage ss;617struct sockaddr_in6 *sin6p;618socklen_t sslen;619ssize_t n;620int error, cs, s[3];621char b;622623ATF_REQUIRE(type == SOCK_DGRAM);624ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);625require_fibs_multibind(type, 2);626627s[0] = mksock(domain, type, 0);628ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},629sizeof(int)) == 0);630s[1] = mksock(domain, type, 0);631ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},632sizeof(int)) == 0);633s[2] = mksock(domain, type, 1);634ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},635sizeof(int)) == 0);636637sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :638sizeof(struct sockaddr_in6);639memset(&ss, 0, sizeof(ss));640ss.ss_family = domain;641ss.ss_len = sslen;642error = bind(s[0], (struct sockaddr *)&ss, sslen);643ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));644error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);645ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));646647error = bind(s[1], (struct sockaddr *)&ss, sslen);648ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));649error = bind(s[2], (struct sockaddr *)&ss, sslen);650ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));651652if (domain == PF_INET6) {653sin6p = (struct sockaddr_in6 *)&ss;654sin6p->sin6_addr = in6addr_loopback;655}656657/*658* Send a packet from FIB 0, make sure it goes to s[0] or s[1].659*/660cs = mksock(domain, type, 0);661for (int count = 0; count < 100; count++) {662int bytes, rs;663664b = 42;665n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);666ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));667usleep(1000);668669error = ioctl(s[0], FIONREAD, &bytes);670ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",671strerror(errno));672if (bytes == 0) {673error = ioctl(s[1], FIONREAD, &bytes);674ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",675strerror(errno));676rs = s[1];677} else {678rs = s[0];679}680n = recv(rs, &b, sizeof(b), 0);681ATF_REQUIRE(n == 1);682ATF_REQUIRE(b == 42);683ATF_REQUIRE(bytes == 1);684}685checked_close(cs);686687/*688* Send a packet from FIB 1, make sure it goes to s[2].689*/690cs = mksock(domain, type, 1);691for (int count = 0; count < 100; count++) {692b = 42;693n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);694ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));695usleep(1000);696697n = recv(s[2], &b, sizeof(b), 0);698ATF_REQUIRE(n == 1);699ATF_REQUIRE(b == 42);700}701checked_close(cs);702703checked_close(s[0]);704checked_close(s[1]);705checked_close(s[2]);706}707MAKETEST_UDP(multibind_lbgroup_dgram);708709/*710* Make sure that we can't change the FIB of a bound socket.711*/712static void713no_setfib_after_bind(int domain, int type, const atf_tc_t *tc __unused)714{715struct sockaddr_storage ss;716socklen_t sslen;717int error, s;718719ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);720require_fibs_multibind(type, 2);721722s = mksock(domain, type, 0);723724sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :725sizeof(struct sockaddr_in6);726memset(&ss, 0, sizeof(ss));727ss.ss_family = domain;728ss.ss_len = sslen;729error = bind(s, (struct sockaddr *)&ss, sslen);730ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));731732error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){1}, sizeof(int));733ATF_REQUIRE_ERRNO(EISCONN, error == -1);734735/* It's ok to set the FIB number to its current value. */736error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){0}, sizeof(int));737ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));738739checked_close(s);740}741MAKETEST(no_setfib_after_bind);742743ATF_TP_ADD_TCS(tp)744{745LISTTEST(multibind_different_user);746LISTTEST_TCP(per_fib_listening_socket);747LISTTEST_UDP(per_fib_dgram_socket);748LISTTEST_RAW(per_fib_raw_socket);749LISTTEST_TCP(multibind_lbgroup_stream);750LISTTEST_UDP(multibind_lbgroup_dgram);751LISTTEST(no_setfib_after_bind);752753return (atf_no_error());754}755756757