Changeset View
Changeset View
Standalone View
Standalone View
tests/sys/kern/socket_rcvbufforce.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright (c) 2022 Alexander V. Chernikov | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <sys/errno.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/sysctl.h> | |||||
#include <sys/un.h> | |||||
#include <netinet/in.h> | |||||
#include <poll.h> | |||||
#include <atf-c.h> | |||||
#define TEST_PORT 35471 | |||||
static unsigned long | |||||
get_maxsockbuf(void) | |||||
{ | |||||
unsigned long maxsockbuf; | |||||
size_t llen = sizeof(maxsockbuf); | |||||
ATF_REQUIRE(sysctlbyname("kern.ipc.maxsockbuf", &maxsockbuf, &llen, NULL, 0) == 0); | |||||
return (maxsockbuf); | |||||
} | |||||
ATF_TC(socket_rcvbufforce_noroot); | |||||
ATF_TC_HEAD(socket_rcvbufforce_noroot, tc) | |||||
{ | |||||
atf_tc_set_md_var(tc, "descr", "Checks that unpriviledged used can't use SO_RCVBUFFORCE"); | |||||
atf_tc_set_md_var(tc, "require.user", "unprivileged"); | |||||
} | |||||
ATF_TC_BODY(socket_rcvbufforce_noroot, tc) | |||||
{ | |||||
unsigned long maxsockbuf = get_maxsockbuf(); | |||||
int families[] = { AF_INET, AF_INET6, 0 }; | |||||
int protocols[] = { SOCK_STREAM, SOCK_DGRAM, 0 }; | |||||
for (int *pfam = families; *pfam != 0; pfam++) { | |||||
for (int *pproto = protocols; *pproto != 0; pproto++) { | |||||
int ss = socket(*pfam, *pproto, 0); | |||||
ATF_REQUIRE(ss >= 0); | |||||
size_t buf = maxsockbuf * 2; | |||||
ATF_REQUIRE_ERRNO(EPERM, setsockopt(ss, SOL_SOCKET, | |||||
SO_RCVBUFFORCE, &buf, sizeof(buf)) != 0); | |||||
close(ss); | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
check_buffer_nostream(const char *test_name) | |||||
{ | |||||
int ss, cs, rc; | |||||
struct sockaddr *sa; | |||||
struct sockaddr_in sin; | |||||
struct sockaddr_in6 sin6; | |||||
int one = 1; | |||||
unsigned long maxsockbuf; | |||||
size_t llen = sizeof(maxsockbuf); | |||||
ATF_REQUIRE(sysctlbyname("kern.ipc.maxsockbuf", &maxsockbuf, &llen, NULL, 0) == 0); | |||||
if (!strcmp(test_name, "udp")) { | |||||
ss = socket(PF_INET, SOCK_DGRAM, 0); | |||||
ATF_CHECK(ss >= 0); | |||||
rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); | |||||
ATF_CHECK_EQ(0, rc); | |||||
bzero(&sin, sizeof(sin)); | |||||
sin.sin_family = AF_INET; | |||||
sin.sin_len = sizeof(sin); | |||||
sin.sin_port = htons(TEST_PORT); | |||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |||||
sa = (struct sockaddr *)&sin; | |||||
rc = bind(ss, sa, sa->sa_len); | |||||
ATF_CHECK_EQ(0, rc); | |||||
cs = socket(PF_INET, SOCK_DGRAM, 0); | |||||
ATF_CHECK(cs >= 0); | |||||
} else if (!strcmp(test_name, "udp6")) { | |||||
ss = socket(PF_INET6, SOCK_DGRAM, 0); | |||||
ATF_CHECK(ss >= 0); | |||||
rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); | |||||
ATF_CHECK_EQ(0, rc); | |||||
bzero(&sin6, sizeof(sin6)); | |||||
sin6.sin6_family = AF_INET6; | |||||
sin6.sin6_len = sizeof(sin6); | |||||
sin6.sin6_port = htons(TEST_PORT); | |||||
const struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT; | |||||
sin6.sin6_addr = in6loopback; | |||||
sa = (struct sockaddr *)&sin6; | |||||
rc = bind(ss, sa, sa->sa_len); | |||||
ATF_CHECK_EQ(0, rc); | |||||
cs = socket(PF_INET6, SOCK_DGRAM, 0); | |||||
ATF_CHECK(cs >= 0); | |||||
} else | |||||
return; | |||||
/* Set large buffer on the server socket */ | |||||
unsigned long sz = get_maxsockbuf() * 2; | |||||
ATF_REQUIRE_ERRNO(0, setsockopt(ss, SOL_SOCKET, | |||||
SO_RCVBUFFORCE, &sz, sizeof(sz)) == 0); | |||||
int chunk_size = 1024; | |||||
char *buf = malloc(chunk_size); | |||||
memset(buf, 0xFE, chunk_size); | |||||
for (unsigned long i = chunk_size; i < sz; i += chunk_size) { | |||||
rc = sendto(cs, buf, chunk_size, 0, sa, sa->sa_len); | |||||
ATF_REQUIRE_EQ(rc, chunk_size); | |||||
} | |||||
close(ss); | |||||
close(cs); | |||||
} | |||||
static void | |||||
check_buffer_stream(const char *test_name) | |||||
{ | |||||
int ss, cs, rc; | |||||
struct sockaddr *sa; | |||||
struct sockaddr_in sin; | |||||
struct sockaddr_in6 sin6; | |||||
struct sockaddr_in6 sa_from; | |||||
int one = 1; | |||||
unsigned long maxsockbuf; | |||||
size_t llen = sizeof(maxsockbuf); | |||||
ATF_REQUIRE(sysctlbyname("kern.ipc.maxsockbuf", &maxsockbuf, &llen, NULL, 0) == 0); | |||||
if (!strcmp(test_name, "udp")) { | |||||
ss = socket(PF_INET, SOCK_STREAM, 0); | |||||
ATF_CHECK(ss >= 0); | |||||
rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); | |||||
ATF_CHECK_EQ(0, rc); | |||||
bzero(&sin, sizeof(sin)); | |||||
sin.sin_family = AF_INET; | |||||
sin.sin_len = sizeof(sin); | |||||
sin.sin_port = htons(TEST_PORT); | |||||
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |||||
sa = (struct sockaddr *)&sin; | |||||
rc = bind(ss, sa, sa->sa_len); | |||||
ATF_CHECK_EQ(0, rc); | |||||
cs = socket(PF_INET, SOCK_STREAM, 0); | |||||
ATF_CHECK(cs >= 0); | |||||
} else if (!strcmp(test_name, "udp6")) { | |||||
ss = socket(PF_INET6, SOCK_STREAM, 0); | |||||
ATF_CHECK(ss >= 0); | |||||
rc = setsockopt(ss, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)); | |||||
ATF_CHECK_EQ(0, rc); | |||||
bzero(&sin6, sizeof(sin6)); | |||||
sin6.sin6_family = AF_INET6; | |||||
sin6.sin6_len = sizeof(sin6); | |||||
sin6.sin6_port = htons(TEST_PORT); | |||||
const struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT; | |||||
sin6.sin6_addr = in6loopback; | |||||
sa = (struct sockaddr *)&sin6; | |||||
rc = bind(ss, sa, sa->sa_len); | |||||
ATF_CHECK_EQ(0, rc); | |||||
cs = socket(PF_INET6, SOCK_STREAM, 0); | |||||
ATF_CHECK(cs >= 0); | |||||
} else | |||||
return; | |||||
/* Connect first */ | |||||
ATF_REQUIRE_ERRNO(0, connect(cs, sa, sa->sa_len) == 0); | |||||
socklen_t sa_size = sizeof(sa_from); | |||||
int as = accept(ss, (struct sockaddr *)&sa_from, &sa_size); | |||||
ATF_REQUIRE(as >= 0); | |||||
ssize_t sz = get_maxsockbuf() * 2; | |||||
ATF_REQUIRE_ERRNO(0, setsockopt(as, SOL_SOCKET, | |||||
SO_RCVBUFFORCE, &sz, sizeof(sz)) == 0); | |||||
char *buf = malloc(sz); | |||||
memset(buf, 0xFE, sz); | |||||
ATF_REQUIRE_EQ(send(cs, buf, sz, 0), sz); | |||||
close(ss); | |||||
close(cs); | |||||
close(as); | |||||
} | |||||
ATF_TC(socket_afinet_udp_rcvbufforce); | |||||
ATF_TC_HEAD(socket_afinet_udp_rcvbufforce, tc) | |||||
{ | |||||
atf_tc_set_md_var(tc, "descr", "Checks SO_RCVBUFFORCE with inet/udp"); | |||||
atf_tc_set_md_var(tc, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(socket_afinet_udp_rcvbufforce, tc) | |||||
{ | |||||
check_buffer_nostream("udp"); | |||||
} | |||||
ATF_TC(socket_afinet6_udp_rcvbufforce); | |||||
ATF_TC_HEAD(socket_afinet6_udp_rcvbufforce, tc) | |||||
{ | |||||
atf_tc_set_md_var(tc, "descr", "Checks SO_RCVBUFFORCE with inet6/udp"); | |||||
atf_tc_set_md_var(tc, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(socket_afinet6_udp_rcvbufforce, tc) | |||||
{ | |||||
check_buffer_nostream("udp6"); | |||||
} | |||||
ATF_TC(socket_afinet_tcp_rcvbufforce); | |||||
ATF_TC_HEAD(socket_afinet_tcp_rcvbufforce, tc) | |||||
{ | |||||
atf_tc_set_md_var(tc, "descr", "Checks SO_RCVBUFFORCE with inet/tcp"); | |||||
atf_tc_set_md_var(tc, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(socket_afinet_tcp_rcvbufforce, tc) | |||||
{ | |||||
check_buffer_stream("tcp"); | |||||
} | |||||
ATF_TC(socket_afinet6_tcp_rcvbufforce); | |||||
ATF_TC_HEAD(socket_afinet6_tcp_rcvbufforce, tc) | |||||
{ | |||||
atf_tc_set_md_var(tc, "descr", "Checks SO_RCVBUFFORCE with inet6/tcp"); | |||||
atf_tc_set_md_var(tc, "require.user", "root"); | |||||
} | |||||
ATF_TC_BODY(socket_afinet6_tcp_rcvbufforce, tc) | |||||
{ | |||||
check_buffer_stream("tcp6"); | |||||
} | |||||
ATF_TP_ADD_TCS(tp) | |||||
{ | |||||
ATF_TP_ADD_TC(tp, socket_afinet_udp_rcvbufforce); | |||||
ATF_TP_ADD_TC(tp, socket_afinet6_udp_rcvbufforce); | |||||
ATF_TP_ADD_TC(tp, socket_afinet_tcp_rcvbufforce); | |||||
ATF_TP_ADD_TC(tp, socket_afinet6_tcp_rcvbufforce); | |||||
ATF_TP_ADD_TC(tp, socket_rcvbufforce_noroot); | |||||
return atf_no_error(); | |||||
} |