Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F110004239
D36328.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D36328.diff
View Options
diff --git a/lib/libc/sys/getsockopt.2 b/lib/libc/sys/getsockopt.2
--- a/lib/libc/sys/getsockopt.2
+++ b/lib/libc/sys/getsockopt.2
@@ -28,7 +28,7 @@
.\" @(#)getsockopt.2 8.4 (Berkeley) 5/2/95
.\" $FreeBSD$
.\"
-.Dd February 8, 2021
+.Dd August 24, 2022
.Dt GETSOCKOPT 2
.Os
.Sh NAME
@@ -160,6 +160,7 @@
.It Dv SO_OOBINLINE Ta "enables reception of out-of-band data in band"
.It Dv SO_SNDBUF Ta "set buffer size for output"
.It Dv SO_RCVBUF Ta "set buffer size for input"
+.It Dv SO_RCVBUFFORCE Ta "set buffer size for input ignoring system limit"
.It Dv SO_SNDLOWAT Ta "set minimum count for output"
.It Dv SO_RCVLOWAT Ta "set minimum count for input"
.It Dv SO_SNDTIMEO Ta "set timeout value for output"
@@ -294,6 +295,10 @@
.Xr sysctl 3
MIB variable
.Dq Li kern.ipc.maxsockbuf .
+.Dv SO_RCVBUFFORCE
+is a priviledged version of
+.Dv SO_RCVBUF
+adjusting the input buffer size ignoring system-wide limits.
.Pp
.Dv SO_SNDLOWAT
is an option to set the minimum count for output operations.
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -528,8 +528,9 @@
case LINUX_SO_SNDBUFFORCE:
return (SO_SNDBUF);
case LINUX_SO_RCVBUF:
- case LINUX_SO_RCVBUFFORCE:
return (SO_RCVBUF);
+ case LINUX_SO_RCVBUFFORCE:
+ return (SO_RCVBUFFORCE);
case LINUX_SO_KEEPALIVE:
return (SO_KEEPALIVE);
case LINUX_SO_OOBINLINE:
diff --git a/sys/kern/uipc_sockbuf.c b/sys/kern/uipc_sockbuf.c
--- a/sys/kern/uipc_sockbuf.c
+++ b/sys/kern/uipc_sockbuf.c
@@ -650,6 +650,26 @@
return (true);
}
+/*
+ * Sets socket bytes/mbufs limits ignoring sb_max default.
+ */
+static bool
+sbreserve_force_locked(struct socket *so, sb_which which, u_long cc,
+ struct thread *td)
+{
+ struct sockbuf *sb = sobuf(so, which);
+ rlim_t sbsize_limit;
+
+ SOCK_BUF_LOCK_ASSERT(so, which);
+
+ sbsize_limit = td != NULL ? lim_cur(td, RLIMIT_SBSIZE) : RLIM_INFINITY;
+ if (!chgsbsize(so->so_cred->cr_uidinfo, &sb->sb_hiwat, cc, sbsize_limit))
+ return (false);
+ sb->sb_mbmax = cc * sb_efficiency;
+ if (sb->sb_lowat > sb->sb_hiwat)
+ sb->sb_lowat = sb->sb_hiwat;
+ return (true);
+}
int
sbsetopt(struct socket *so, int cmd, u_long cc)
{
@@ -671,6 +691,7 @@
break;
case SO_RCVLOWAT:
case SO_RCVBUF:
+ case SO_RCVBUFFORCE:
lowat = &so->sol_sbrcv_lowat;
hiwat = &so->sol_sbrcv_hiwat;
flags = &so->sol_sbrcv_flags;
@@ -685,6 +706,7 @@
break;
case SO_RCVLOWAT:
case SO_RCVBUF:
+ case SO_RCVBUFFORCE:
sb = &so->so_rcv;
wh = SO_RCV;
break;
@@ -714,6 +736,18 @@
if (error == 0)
*flags &= ~SB_AUTOSIZE;
break;
+ case SO_RCVBUFFORCE:
+ if (SOLISTENING(so)) {
+ *hiwat = cc;
+ if (*lowat > *hiwat)
+ *lowat = *hiwat;
+ } else {
+ if (!sbreserve_force_locked(so, wh, cc, curthread))
+ error = ENOBUFS;
+ }
+ if (error == 0)
+ *flags &= ~SB_AUTOSIZE;
+ break;
case SO_SNDLOWAT:
case SO_RCVLOWAT:
/*
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -129,6 +129,7 @@
#include <sys/event.h>
#include <sys/eventhandler.h>
#include <sys/poll.h>
+#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/sbuf.h>
@@ -3098,6 +3099,7 @@
case SO_SNDBUF:
case SO_RCVBUF:
+ case SO_RCVBUFFORCE:
case SO_SNDLOWAT:
case SO_RCVLOWAT:
error = sooptcopyin(sopt, &optval, sizeof optval,
@@ -3114,6 +3116,14 @@
goto bad;
}
+ if (sopt->sopt_name == SO_RCVBUFFORCE) {
+ if (sopt->sopt_td != NULL)
+ error = priv_check(sopt->sopt_td,
+ PRIV_NETINET_RCVBUFFORCE);
+ if (error != 0)
+ goto bad;
+ }
+
error = sbsetopt(so, sopt->sopt_name, optval);
break;
diff --git a/sys/sys/priv.h b/sys/sys/priv.h
--- a/sys/sys/priv.h
+++ b/sys/sys/priv.h
@@ -403,6 +403,7 @@
#define PRIV_NETINET_SETHDROPTS 505 /* Set certain IPv4/6 header options. */
#define PRIV_NETINET_BINDANY 506 /* Allow bind to any address. */
#define PRIV_NETINET_HASHKEY 507 /* Get and set hash keys for IPv4/6. */
+#define PRIV_NETINET_RCVBUFFORCE 508 /* Allow setting SO_RCVBUFFORCE option. */
/*
* Placeholders for IPX/SPX privileges, not supported any more.
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -173,6 +173,7 @@
#define SO_TS_CLOCK 0x1017 /* clock type used for SO_TIMESTAMP */
#define SO_MAX_PACING_RATE 0x1018 /* socket's max TX pacing rate (Linux name) */
#define SO_DOMAIN 0x1019 /* get socket domain */
+#define SO_RCVBUFFORCE 0x1020 /* receive buffer size, ignoring limits */
#endif
#if __BSD_VISIBLE
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -31,6 +31,7 @@
ATF_TESTS_C+= sigwait
ATF_TESTS_C+= socket_accf
ATF_TESTS_C+= socket_msg_trunc
+ATF_TESTS_C+= socket_rcvbufforce
TEST_METADATA.sigwait+= is_exclusive="true"
.if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH:Mpowerpc*} == ""
ATF_TESTS_C+= subr_physmem_test
diff --git a/tests/sys/kern/socket_rcvbufforce.c b/tests/sys/kern/socket_rcvbufforce.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/socket_rcvbufforce.c
@@ -0,0 +1,282 @@
+/*-
+ * 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();
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Feb 13, 8:40 AM (20 h, 43 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16625254
Default Alt Text
D36328.diff (13 KB)
Attached To
Mode
D36328: sockets: add SO_RCVBUFFORCE to allow maxsockbuf value bypassing.
Attached
Detach File
Event Timeline
Log In to Comment