netstack: support SO_RCVBUFFORCE

TCP is fully supported. As with SO_RCVBUF, other transport protocols perform
no-ops per DefaultSocketOptionsHandler.OnSetReceiveBufferSize.

PiperOrigin-RevId: 385023239
This commit is contained in:
Kevin Krakauer
2021-07-15 15:34:34 -07:00
committed by gVisor bot
parent 67d9050752
commit cd45d7b6c8
7 changed files with 75 additions and 6 deletions
+20 -4
View File
@@ -49,6 +49,7 @@ import (
"gvisor.dev/gvisor/pkg/sentry/fs/fsutil"
"gvisor.dev/gvisor/pkg/sentry/inet"
"gvisor.dev/gvisor/pkg/sentry/kernel"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/socket"
"gvisor.dev/gvisor/pkg/sentry/socket/netfilter"
@@ -1682,12 +1683,12 @@ func SetSockOpt(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, level int
return nil
}
func clampBufSize(newSz, min, max int64) int64 {
func clampBufSize(newSz, min, max int64, ignoreMax bool) int64 {
// packetOverheadFactor is used to multiply the value provided by the user on
// a setsockopt(2) for setting the send/receive buffer sizes sockets.
const packetOverheadFactor = 2
if newSz > max {
if !ignoreMax && newSz > max {
newSz = max
}
@@ -1712,7 +1713,7 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
v := hostarch.ByteOrder.Uint32(optVal)
min, max := ep.SocketOptions().SendBufferLimits()
clamped := clampBufSize(int64(v), min, max)
clamped := clampBufSize(int64(v), min, max, false /* ignoreMax */)
ep.SocketOptions().SetSendBufferSize(clamped, true /* notify */)
return nil
@@ -1723,7 +1724,22 @@ func setSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, nam
v := hostarch.ByteOrder.Uint32(optVal)
min, max := ep.SocketOptions().ReceiveBufferLimits()
clamped := clampBufSize(int64(v), min, max)
clamped := clampBufSize(int64(v), min, max, false /* ignoreMax */)
ep.SocketOptions().SetReceiveBufferSize(clamped, true /* notify */)
return nil
case linux.SO_RCVBUFFORCE:
if len(optVal) < sizeOfInt32 {
return syserr.ErrInvalidArgument
}
if creds := auth.CredentialsFromContext(t); !creds.HasCapability(linux.CAP_NET_ADMIN) {
return syserr.ErrNotPermitted
}
v := hostarch.ByteOrder.Uint32(optVal)
min, max := ep.SocketOptions().ReceiveBufferLimits()
clamped := clampBufSize(int64(v), min, max, true /* ignoreMax */)
ep.SocketOptions().SetReceiveBufferSize(clamped, true /* notify */)
return nil
-1
View File
@@ -509,7 +509,6 @@ func SetSockOptEmitUnimplementedEvent(t *kernel.Task, name int) {
linux.SO_ATTACH_REUSEPORT_EBPF,
linux.SO_CNX_ADVICE,
linux.SO_DETACH_FILTER,
linux.SO_RCVBUFFORCE,
linux.SO_SNDBUFFORCE:
t.Kernel().EmitUnimplementedEvent(t)
+1 -1
View File
@@ -54,7 +54,7 @@ type SocketOptionsHandler interface {
// buffer size. It also returns the newly set value.
OnSetSendBufferSize(v int64) (newSz int64)
// OnSetReceiveBufferSize is invoked to set the SO_RCVBUFSIZE.
// OnSetReceiveBufferSize is invoked by SO_RCVBUF and SO_RCVBUFFORCE.
OnSetReceiveBufferSize(v, oldSz int64) (newSz int64)
}
+1
View File
@@ -2395,6 +2395,7 @@ cc_library(
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
gtest,
"//test/util:capability_util",
"//test/util:test_util",
],
alwayslink = 1,
@@ -14,6 +14,9 @@
#include "test/syscalls/linux/socket_generic.h"
#ifdef __linux__
#include <linux/capability.h>
#endif // __linux__
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -24,6 +27,7 @@
#include "absl/strings/string_view.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/capability_util.h"
#include "test/util/test_util.h"
// This file is a generic socket test file. It must be built with another file
@@ -400,6 +404,46 @@ TEST_P(AllSocketPairTest, RcvBufSucceeds) {
EXPECT_GT(size, 0);
}
#ifdef __linux__
// Check that setting SO_RCVBUFFORCE above max is not clamped to the maximum
// receive buffer size.
TEST_P(AllSocketPairTest, SetSocketRecvBufForceAboveMax) {
std::unique_ptr<SocketPair> sockets =
ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
// Discover maxmimum buffer size by setting to a really large value.
constexpr int kRcvBufSz = 0xffffffff;
ASSERT_THAT(setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &kRcvBufSz,
sizeof(kRcvBufSz)),
SyscallSucceeds());
int max = 0;
socklen_t max_len = sizeof(max);
ASSERT_THAT(
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &max, &max_len),
SyscallSucceeds());
int above_max = max + 1;
int sso = setsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUFFORCE,
&above_max, sizeof(above_max));
if (!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))) {
ASSERT_THAT(sso, SyscallFailsWithErrno(EPERM));
return;
}
ASSERT_THAT(sso, SyscallSucceeds());
int val = 0;
socklen_t val_len = sizeof(val);
ASSERT_THAT(
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_RCVBUF, &val, &val_len),
SyscallSucceeds());
// The system doubles the passed-in maximum.
ASSERT_EQ(above_max * 2, val);
}
#endif // __linux__
TEST_P(AllSocketPairTest, GetSndBufSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int size = 0;
+4
View File
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifdef __linux__
#include "test/util/capability_util.h"
#include <linux/capability.h>
@@ -79,3 +81,5 @@ PosixErrorOr<bool> CanCreateUserNamespace() {
} // namespace testing
} // namespace gvisor
#endif // __linux__
+5
View File
@@ -17,6 +17,8 @@
#ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
#define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_
#ifdef __linux__
#include <errno.h>
#include <linux/capability.h>
#include <sys/syscall.h>
@@ -120,4 +122,7 @@ class AutoCapability {
} // namespace testing
} // namespace gvisor
#endif // __linux__
#endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_