Page MenuHomeFreeBSD

D48665.id149927.diff
No OneTemporary

D48665.id149927.diff

diff --git a/tests/sys/netinet/Makefile b/tests/sys/netinet/Makefile
--- a/tests/sys/netinet/Makefile
+++ b/tests/sys/netinet/Makefile
@@ -5,7 +5,8 @@
TESTS_SUBDIRS+= libalias
-ATF_TESTS_C= ip_reass_test \
+ATF_TESTS_C= fibs_multibind_test \
+ ip_reass_test \
ip6_v4mapped_test \
so_reuseport_lb_test \
socket_afinet \
diff --git a/tests/sys/netinet/fibs_multibind_test.c b/tests/sys/netinet/fibs_multibind_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/netinet/fibs_multibind_test.c
@@ -0,0 +1,609 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024-2025 Stormshield
+ */
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#define MAKETEST_TCP(name) \
+ATF_TC_WITHOUT_HEAD(name ## _tcp); \
+ATF_TC_BODY(name ## _tcp, tc) \
+{ \
+ name(PF_INET, SOCK_STREAM, tc); \
+} \
+ATF_TC_WITHOUT_HEAD(name ## _tcp6); \
+ATF_TC_BODY(name ## _tcp6, tc) \
+{ \
+ name(PF_INET6, SOCK_STREAM, tc); \
+}
+#define MAKETEST_UDP(name) \
+ATF_TC_WITHOUT_HEAD(name ## _udp); \
+ATF_TC_BODY(name ## _udp, tc) \
+{ \
+ name(PF_INET, SOCK_DGRAM, tc); \
+} \
+ATF_TC_WITHOUT_HEAD(name ## _udp6); \
+ATF_TC_BODY(name ## _udp6, tc) \
+{ \
+ name(PF_INET6, SOCK_DGRAM, tc); \
+}
+#define MAKETEST(name) \
+ MAKETEST_TCP(name) \
+ MAKETEST_UDP(name)
+
+#define LISTTEST_TCP(name) \
+ ATF_TP_ADD_TC(tp, name ## _tcp); \
+ ATF_TP_ADD_TC(tp, name ## _tcp6);
+#define LISTTEST_UDP(name) \
+ ATF_TP_ADD_TC(tp, name ## _udp); \
+ ATF_TP_ADD_TC(tp, name ## _udp6);
+#define LISTTEST(name) \
+ LISTTEST_TCP(name) \
+ LISTTEST_UDP(name)
+
+static void
+checked_close(int s)
+{
+ int error;
+
+ error = close(s);
+ ATF_REQUIRE_MSG(error == 0, "close failed: %s", strerror(errno));
+}
+
+static int
+mksock(int domain, int type, int fib)
+{
+ int error, s;
+
+ s = socket(domain, type, 0);
+ ATF_REQUIRE(s != -1);
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &fib, sizeof(fib));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ return (s);
+}
+
+static void
+require_fibs_multibind(int socktype, int minfibs)
+{
+ const char *sysctl;
+ size_t sz;
+ int error, fibs, multibind;
+
+ fibs = 0;
+ sz = sizeof(fibs);
+ error = sysctlbyname("net.fibs", &fibs, &sz, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(fibs >= 1, "strange FIB count %d", fibs);
+ if (fibs == 1)
+ atf_tc_skip("multiple FIBs not enabled");
+ if (fibs < minfibs)
+ atf_tc_skip("not enough FIBs, need %d", minfibs);
+
+ switch (socktype) {
+ case SOCK_STREAM:
+ sysctl = "net.inet.tcp.bind_all_fibs";
+ break;
+ case SOCK_DGRAM:
+ sysctl = "net.inet.udp.bind_all_fibs";
+ break;
+ case SOCK_RAW:
+ sysctl = "net.inet.rip.bind_all_fibs";
+ break;
+ default:
+ atf_tc_fail("unknown socket type %d", socktype);
+ break;
+ }
+
+ multibind = -1;
+ sz = sizeof(multibind);
+ error = sysctlbyname(sysctl, &multibind, &sz, NULL, 0);
+ ATF_REQUIRE_MSG(error == 0, "sysctlbyname failed: %s", strerror(errno));
+ if (multibind != 0)
+ atf_tc_skip("FIB multibind not configured (%s)", sysctl);
+}
+
+/*
+ * Make sure that different users can't bind to the same port from different
+ * FIBs.
+ */
+static void
+multibind_different_user(int domain, int type, const atf_tc_t *tc)
+{
+ struct sockaddr_storage ss;
+ struct passwd *passwd;
+ const char *user;
+ socklen_t sslen;
+ int error, s[2];
+
+ if (geteuid() != 0)
+ atf_tc_skip("need root privileges");
+ if (!atf_tc_has_config_var(tc, "unprivileged_user"))
+ atf_tc_skip("unprivileged_user not set");
+
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ /*
+ * Create a second socket in a different FIB, and bind it to the same
+ * address/port tuple. This should succeed if done as the same user as
+ * the first socket, and should fail otherwise.
+ */
+ s[1] = mksock(domain, type, 1);
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));
+
+ user = atf_tc_get_config_var(tc, "unprivileged_user");
+ passwd = getpwnam(user);
+ ATF_REQUIRE(passwd != NULL);
+ error = seteuid(passwd->pw_uid);
+ ATF_REQUIRE_MSG(error == 0, "seteuid failed: %s", strerror(errno));
+
+ /* Repeat the bind as a different user. */
+ s[1] = mksock(domain, type, 1);
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_ERRNO(EADDRINUSE, error == -1);
+ ATF_REQUIRE_MSG(close(s[1]) == 0, "close failed: %s", strerror(errno));
+}
+MAKETEST(multibind_different_user);
+
+/*
+ * Verify that a listening socket only accepts connections originating from the
+ * same FIB.
+ */
+static void
+per_fib_listening_socket(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int cs1, cs2, error, fib1, fib2, ls1, ls2, ns;
+
+ ATF_REQUIRE(type == SOCK_STREAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ fib1 = 0;
+ fib2 = 1;
+
+ ls1 = mksock(domain, type, fib1);
+ ls2 = mksock(domain, type, fib2);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(ls1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(ls1, (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = listen(ls1, 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ cs1 = mksock(domain, type, fib1);
+ cs2 = mksock(domain, type, fib2);
+
+ /*
+ * Make sure we can connect from the same FIB.
+ */
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ ns = accept(ls1, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+ checked_close(ns);
+ checked_close(cs1);
+ cs1 = mksock(domain, type, fib1);
+
+ /*
+ * ... but not from a different FIB.
+ */
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");
+ ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);
+ checked_close(cs2);
+ cs2 = mksock(domain, type, fib2);
+
+ /*
+ * ... but if there are multiple listening sockets, we always connect to
+ * the same FIB.
+ */
+ error = bind(ls2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(ls2, 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ for (int i = 0; i < 10; i++) {
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+ ns = accept(ls1, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+
+ checked_close(ns);
+ checked_close(cs1);
+ cs1 = mksock(domain, type, fib1);
+ }
+ for (int i = 0; i < 10; i++) {
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+ ns = accept(ls2, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+
+ checked_close(ns);
+ checked_close(cs2);
+ cs2 = mksock(domain, type, fib2);
+ }
+
+ /*
+ * ... and if we close one of the listening sockets, we're back to only
+ * being able to connect from the same FIB.
+ */
+ checked_close(ls1);
+ error = connect(cs1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == -1, "connect succeeded unexpectedly");
+ ATF_REQUIRE_MSG(errno == ECONNREFUSED, "unexpected error %d", errno);
+ checked_close(cs1);
+
+ error = connect(cs2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s", strerror(errno));
+ ns = accept(ls2, NULL, NULL);
+ ATF_REQUIRE_MSG(ns != -1, "accept failed: %s", strerror(errno));
+ checked_close(ns);
+ checked_close(cs2);
+ checked_close(ls2);
+}
+MAKETEST_TCP(per_fib_listening_socket);
+
+/*
+ * Verify that a bound datagram socket only accepts data from the same FIB.
+ */
+static void
+per_fib_dgram_socket(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in6 *sin6p;
+ socklen_t sslen;
+ ssize_t n;
+ int error, cs1, cs2, fib1, fib2, ss1, ss2;
+ char b;
+
+ ATF_REQUIRE(type == SOCK_DGRAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ fib1 = 0;
+ fib2 = 1;
+
+ cs1 = mksock(domain, type, fib1);
+ cs2 = mksock(domain, type, fib2);
+
+ ss1 = mksock(domain, type, fib1);
+ ss2 = mksock(domain, type, fib2);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(ss1, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = getsockname(ss1, (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ if (domain == PF_INET6) {
+ sin6p = (struct sockaddr_in6 *)&ss;
+ sin6p->sin6_addr = in6addr_loopback;
+ }
+
+ /* If we send a byte from cs1, it should be recieved by ss1. */
+ b = 42;
+ n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss1, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ /* If we send a byte from cs2, it should not be received by ss1. */
+ b = 42;
+ n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(10000);
+ n = recv(ss1, &b, sizeof(b), MSG_DONTWAIT);
+ ATF_REQUIRE_ERRNO(EWOULDBLOCK, n == -1);
+
+ error = bind(ss2, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ /* Repeat now that ss2 is bound. */
+ b = 42;
+ n = sendto(cs1, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss1, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ b = 42;
+ n = sendto(cs2, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ n = recv(ss2, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+
+ checked_close(ss1);
+ checked_close(ss2);
+ checked_close(cs1);
+ checked_close(cs2);
+}
+MAKETEST_UDP(per_fib_dgram_socket);
+
+/*
+ * Create a pair of load-balancing listening socket groups, one in each FIB, and
+ * make sure that connections to the group are only load-balanced within the
+ * same FIB.
+ */
+static void
+multibind_lbgroup_stream(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int error, as, cs, s[3];
+
+ ATF_REQUIRE(type == SOCK_STREAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ ATF_REQUIRE(fcntl(s[0], F_SETFL, O_NONBLOCK) == 0);
+ s[1] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ ATF_REQUIRE(fcntl(s[1], F_SETFL, O_NONBLOCK) == 0);
+ s[2] = mksock(domain, type, 1);
+ ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[0], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[1], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ error = bind(s[2], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = listen(s[2], 5);
+ ATF_REQUIRE_MSG(error == 0, "listen failed: %s", strerror(errno));
+
+ /*
+ * Initiate connections from FIB 0, make sure they go to s[0] or s[1].
+ */
+ for (int count = 0; count < 100; count++) {
+ cs = mksock(domain, type, 0);
+ error = connect(cs, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+
+ do {
+ as = accept(s[0], NULL, NULL);
+ if (as == -1) {
+ ATF_REQUIRE_MSG(errno == EWOULDBLOCK,
+ "accept failed: %s", strerror(errno));
+ as = accept(s[1], NULL, NULL);
+ if (as == -1) {
+ ATF_REQUIRE_MSG(errno == EWOULDBLOCK,
+ "accept failed: %s",
+ strerror(errno));
+ }
+ }
+ } while (as == -1);
+ checked_close(as);
+ checked_close(cs);
+ }
+
+ /*
+ * Initiate connections from FIB 1, make sure they go to s[2].
+ */
+ for (int count = 0; count < 100; count++) {
+ cs = mksock(domain, type, 1);
+ error = connect(cs, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "connect failed: %s",
+ strerror(errno));
+
+ as = accept(s[2], NULL, NULL);
+ ATF_REQUIRE_MSG(as != -1, "accept failed: %s", strerror(errno));
+ checked_close(as);
+ checked_close(cs);
+ }
+
+ checked_close(s[0]);
+ checked_close(s[1]);
+ checked_close(s[2]);
+}
+MAKETEST_TCP(multibind_lbgroup_stream);
+
+static void
+multibind_lbgroup_dgram(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in6 *sin6p;
+ socklen_t sslen;
+ ssize_t n;
+ int error, cs, s[3];
+ char b;
+
+ ATF_REQUIRE(type == SOCK_DGRAM);
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s[0] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[0], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ s[1] = mksock(domain, type, 0);
+ ATF_REQUIRE(setsockopt(s[1], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+ s[2] = mksock(domain, type, 1);
+ ATF_REQUIRE(setsockopt(s[2], SOL_SOCKET, SO_REUSEPORT_LB, &(int){1},
+ sizeof(int)) == 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s[0], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = getsockname(s[0], (struct sockaddr *)&ss, &sslen);
+ ATF_REQUIRE_MSG(error == 0, "getsockname failed: %s", strerror(errno));
+
+ error = bind(s[1], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+ error = bind(s[2], (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ if (domain == PF_INET6) {
+ sin6p = (struct sockaddr_in6 *)&ss;
+ sin6p->sin6_addr = in6addr_loopback;
+ }
+
+ /*
+ * Send a packet from FIB 0, make sure it goes to s[0] or s[1].
+ */
+ cs = mksock(domain, type, 0);
+ for (int count = 0; count < 100; count++) {
+ int bytes, rs;
+
+ b = 42;
+ n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(1000);
+
+ error = ioctl(s[0], FIONREAD, &bytes);
+ ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",
+ strerror(errno));
+ if (bytes == 0) {
+ error = ioctl(s[1], FIONREAD, &bytes);
+ ATF_REQUIRE_MSG(error == 0, "ioctl failed: %s",
+ strerror(errno));
+ rs = s[1];
+ } else {
+ rs = s[0];
+ }
+ n = recv(rs, &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+ ATF_REQUIRE(bytes == 1);
+ }
+ checked_close(cs);
+
+ /*
+ * Send a packet from FIB 1, make sure it goes to s[2].
+ */
+ cs = mksock(domain, type, 1);
+ for (int count = 0; count < 100; count++) {
+ b = 42;
+ n = sendto(cs, &b, sizeof(b), 0, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(n == 1, "sendto failed: %s", strerror(errno));
+ usleep(1000);
+
+ n = recv(s[2], &b, sizeof(b), 0);
+ ATF_REQUIRE(n == 1);
+ ATF_REQUIRE(b == 42);
+ }
+ checked_close(cs);
+
+ checked_close(s[0]);
+ checked_close(s[1]);
+ checked_close(s[2]);
+}
+MAKETEST_UDP(multibind_lbgroup_dgram);
+
+/*
+ * Make sure that we can't change the FIB of a bound socket.
+ */
+static void
+no_setfib_after_bind(int domain, int type, const atf_tc_t *tc __unused)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen;
+ int error, s;
+
+ ATF_REQUIRE(domain == PF_INET || domain == PF_INET6);
+ require_fibs_multibind(type, 2);
+
+ s = mksock(domain, type, 0);
+
+ sslen = domain == PF_INET ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ memset(&ss, 0, sizeof(ss));
+ ss.ss_family = domain;
+ ss.ss_len = sslen;
+ error = bind(s, (struct sockaddr *)&ss, sslen);
+ ATF_REQUIRE_MSG(error == 0, "bind failed: %s", strerror(errno));
+
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){1}, sizeof(int));
+ ATF_REQUIRE_ERRNO(EISCONN, error == -1);
+
+ /* It's ok to set the FIB number to its current value. */
+ error = setsockopt(s, SOL_SOCKET, SO_SETFIB, &(int){0}, sizeof(int));
+ ATF_REQUIRE_MSG(error == 0, "setsockopt failed: %s", strerror(errno));
+
+ checked_close(s);
+}
+MAKETEST(no_setfib_after_bind);
+
+ATF_TP_ADD_TCS(tp)
+{
+ LISTTEST(multibind_different_user);
+ LISTTEST_TCP(per_fib_listening_socket);
+ LISTTEST_UDP(per_fib_dgram_socket);
+ LISTTEST_TCP(multibind_lbgroup_stream);
+ LISTTEST_UDP(multibind_lbgroup_dgram);
+ LISTTEST(no_setfib_after_bind);
+
+ return (atf_no_error());
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Feb 5, 3:56 AM (5 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16466578
Default Alt Text
D48665.id149927.diff (18 KB)

Event Timeline