Support getsockopt for SO_ACCEPTCONN.

The SO_ACCEPTCONN option is used only on getsockopt(). When this option is
specified, getsockopt() indicates whether socket listening is enabled for
the socket. A value of zero indicates that socket listening is disabled;
non-zero that it is enabled.

PiperOrigin-RevId: 338703206
This commit is contained in:
Nayana Bidari
2020-10-23 10:48:24 -07:00
committed by gVisor bot
parent dad08229b8
commit 39e9b3bb8a
13 changed files with 142 additions and 4 deletions
+12
View File
@@ -1244,6 +1244,18 @@ func getSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, fam
vP := primitive.Int32(boolToInt32(v))
return &vP, nil
case linux.SO_ACCEPTCONN:
if outLen < sizeOfInt32 {
return nil, syserr.ErrInvalidArgument
}
v, err := ep.GetSockOptBool(tcpip.AcceptConnOption)
if err != nil {
return nil, syserr.TranslateNetstackError(err)
}
vP := primitive.Int32(boolToInt32(v))
return &vP, nil
default:
socket.GetSockOptEmitUnimplementedEvent(t, name)
}
+1 -1
View File
@@ -879,7 +879,7 @@ func (e *baseEndpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
func (e *baseEndpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
switch opt {
case tcpip.KeepaliveEnabledOption:
case tcpip.KeepaliveEnabledOption, tcpip.AcceptConnOption:
return false, nil
case tcpip.PasscredOption:
+4
View File
@@ -763,6 +763,10 @@ const (
// endpoint that all packets being written have an IP header and the
// endpoint should not attach an IP header.
IPHdrIncludedOption
// AcceptConnOption is used by GetSockOptBool to indicate if the
// socket is a listening socket.
AcceptConnOption
)
// SockOptInt represents socket options which values have the int type.
+1 -1
View File
@@ -378,7 +378,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
// GetSockOptBool implements tcpip.Endpoint.GetSockOptBool.
func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
switch opt {
case tcpip.KeepaliveEnabledOption:
case tcpip.KeepaliveEnabledOption, tcpip.AcceptConnOption:
return false, nil
default:
+6 -1
View File
@@ -389,7 +389,12 @@ func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
// GetSockOptBool implements tcpip.Endpoint.GetSockOptBool.
func (*endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
return false, tcpip.ErrNotSupported
switch opt {
case tcpip.AcceptConnOption:
return false, nil
default:
return false, tcpip.ErrNotSupported
}
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
+1 -1
View File
@@ -601,7 +601,7 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
// GetSockOptBool implements tcpip.Endpoint.GetSockOptBool.
func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
switch opt {
case tcpip.KeepaliveEnabledOption:
case tcpip.KeepaliveEnabledOption, tcpip.AcceptConnOption:
return false, nil
case tcpip.IPHdrIncludedOption:
+6
View File
@@ -1999,6 +1999,12 @@ func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
case tcpip.MulticastLoopOption:
return true, nil
case tcpip.AcceptConnOption:
e.LockUser()
defer e.UnlockUser()
return e.EndpointState() == StateListen, nil
default:
return false, tcpip.ErrUnknownProtocolOption
}
+3
View File
@@ -895,6 +895,9 @@ func (e *endpoint) GetSockOptBool(opt tcpip.SockOptBool) (bool, *tcpip.Error) {
return v, nil
case tcpip.AcceptConnOption:
return false, nil
default:
return false, tcpip.ErrUnknownProtocolOption
}
+11
View File
@@ -664,6 +664,17 @@ TEST_P(RawPacketTest, SetAndGetSocketLinger) {
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
TEST_P(RawPacketTest, GetSocketAcceptConn) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest,
::testing::Values(ETH_P_IP, ETH_P_ALL));
+13
View File
@@ -438,6 +438,19 @@ TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
// Test getsockopt for SO_ACCEPTCONN.
TEST_F(RawSocketICMPTest, GetSocketAcceptConn) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
void RawSocketICMPTest::ExpectICMPSuccess(const struct icmphdr& icmp) {
// We're going to receive both the echo request and reply, but the order is
// indeterminate.
@@ -472,5 +472,19 @@ TEST_P(UDPSocketPairTest, SetAndGetSocketLinger) {
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
// Test getsockopt for SO_ACCEPTCONN on udp socket.
TEST_P(UDPSocketPairTest, GetSocketAcceptConn) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
} // namespace testing
} // namespace gvisor
+13
View File
@@ -121,6 +121,19 @@ TEST_P(StreamUnixSocketPairTest, SetAndGetSocketLinger) {
EXPECT_EQ(0, memcmp(&got_linger, &sl, length));
}
TEST_P(StreamUnixSocketPairTest, GetSocketAcceptConn) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(
getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
INSTANTIATE_TEST_SUITE_P(
AllUnixDomainSockets, StreamUnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>(
+57
View File
@@ -1725,6 +1725,63 @@ TEST_P(SimpleTcpSocketTest, CloseNonConnectedLingerOption) {
ASSERT_LT((end_time - start_time), absl::Seconds(kLingerTimeout));
}
// Tests that SO_ACCEPTCONN returns non zero value for listening sockets.
TEST_P(TcpSocketTest, GetSocketAcceptConnListener) {
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(getsockopt(listener_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceeds());
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 1);
}
// Tests that SO_ACCEPTCONN returns zero value for not listening sockets.
TEST_P(TcpSocketTest, GetSocketAcceptConnNonListener) {
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceeds());
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
ASSERT_THAT(getsockopt(t_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceeds());
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) {
// TODO(b/171345701): Fix the TCP state for listening socket on shutdown.
SKIP_IF(IsRunningOnGvisor());
FileDescriptor s =
ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
// Initialize address to the loopback one.
sockaddr_storage addr =
ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
socklen_t addrlen = sizeof(addr);
// Bind to some port then start listening.
ASSERT_THAT(bind(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen),
SyscallSucceeds());
ASSERT_THAT(listen(s.get(), SOMAXCONN), SyscallSucceeds());
int got = -1;
socklen_t length = sizeof(got);
ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceeds());
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 1);
EXPECT_THAT(shutdown(s.get(), SHUT_RD), SyscallSucceeds());
ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length),
SyscallSucceeds());
ASSERT_EQ(length, sizeof(got));
EXPECT_EQ(got, 0);
}
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));