{Set,Get} SO_LINGER on all endpoints.

SO_LINGER is a socket level option and should be stored on all endpoints even
though it is used to linger only for TCP endpoints.

PiperOrigin-RevId: 332369252
This commit is contained in:
Nayana Bidari 2020-09-17 19:58:27 -07:00 committed by gVisor bot
parent c0b74be54c
commit d34bda0273
10 changed files with 151 additions and 23 deletions

View File

@ -1185,7 +1185,7 @@ func getSockOptSocket(t *kernel.Task, s socket.SocketOps, ep commonEndpoint, fam
var v tcpip.LingerOption
var linger linux.Linger
if err := ep.GetSockOpt(&v); err != nil {
return &linger, nil
return nil, syserr.TranslateNetstackError(err)
}
if v.Enabled {

View File

@ -746,6 +746,9 @@ type baseEndpoint struct {
// path is not empty if the endpoint has been bound,
// or may be used if the endpoint is connected.
path string
// linger is used for SO_LINGER socket option.
linger tcpip.LingerOption
}
// EventRegister implements waiter.Waitable.EventRegister.
@ -841,8 +844,14 @@ func (e *baseEndpoint) SendMsg(ctx context.Context, data [][]byte, c ControlMess
return n, err
}
// SetSockOpt sets a socket option. Currently not supported.
func (e *baseEndpoint) SetSockOpt(tcpip.SettableSocketOption) *tcpip.Error {
// SetSockOpt sets a socket option.
func (e *baseEndpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
switch v := opt.(type) {
case *tcpip.LingerOption:
e.Lock()
e.linger = *v
e.Unlock()
}
return nil
}
@ -945,8 +954,11 @@ func (e *baseEndpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
func (e *baseEndpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
switch opt.(type) {
switch o := opt.(type) {
case *tcpip.LingerOption:
e.Lock()
*o = e.linger
e.Unlock()
return nil
default:

View File

@ -74,6 +74,8 @@ type endpoint struct {
route stack.Route `state:"manual"`
ttl uint8
stats tcpip.TransportEndpointStats `state:"nosave"`
// linger is used for SO_LINGER socket option.
linger tcpip.LingerOption
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
@ -344,9 +346,14 @@ func (e *endpoint) Peek([][]byte) (int64, tcpip.ControlMessages, *tcpip.Error) {
// SetSockOpt sets a socket option.
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
switch opt.(type) {
switch v := opt.(type) {
case *tcpip.SocketDetachFilterOption:
return nil
case *tcpip.LingerOption:
e.mu.Lock()
e.linger = *v
e.mu.Unlock()
}
return nil
}
@ -415,8 +422,17 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
func (*endpoint) GetSockOpt(tcpip.GettableSocketOption) *tcpip.Error {
return tcpip.ErrUnknownProtocolOption
func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
switch o := opt.(type) {
case *tcpip.LingerOption:
e.mu.Lock()
*o = e.linger
e.mu.Unlock()
return nil
default:
return tcpip.ErrUnknownProtocolOption
}
}
func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpip.PacketOwner) *tcpip.Error {

View File

@ -83,6 +83,8 @@ type endpoint struct {
stats tcpip.TransportEndpointStats `state:"nosave"`
bound bool
boundNIC tcpip.NICID
// linger is used for SO_LINGER socket option.
linger tcpip.LingerOption
// lastErrorMu protects lastError.
lastErrorMu sync.Mutex `state:"nosave"`
@ -298,10 +300,16 @@ func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
// used with SetSockOpt, and this function always returns
// tcpip.ErrNotSupported.
func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
switch opt.(type) {
switch v := opt.(type) {
case *tcpip.SocketDetachFilterOption:
return nil
case *tcpip.LingerOption:
ep.mu.Lock()
ep.linger = *v
ep.mu.Unlock()
return nil
default:
return tcpip.ErrUnknownProtocolOption
}
@ -366,8 +374,17 @@ func (ep *endpoint) LastError() *tcpip.Error {
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
func (*endpoint) GetSockOpt(tcpip.GettableSocketOption) *tcpip.Error {
return tcpip.ErrNotSupported
func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
switch o := opt.(type) {
case *tcpip.LingerOption:
ep.mu.Lock()
*o = ep.linger
ep.mu.Unlock()
return nil
default:
return tcpip.ErrNotSupported
}
}
// GetSockOptBool implements tcpip.Endpoint.GetSockOptBool.

View File

@ -84,6 +84,8 @@ type endpoint struct {
// Connect(), and is valid only when conneted is true.
route stack.Route `state:"manual"`
stats tcpip.TransportEndpointStats `state:"nosave"`
// linger is used for SO_LINGER socket option.
linger tcpip.LingerOption
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
@ -511,10 +513,16 @@ func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
switch opt.(type) {
switch v := opt.(type) {
case *tcpip.SocketDetachFilterOption:
return nil
case *tcpip.LingerOption:
e.mu.Lock()
e.linger = *v
e.mu.Unlock()
return nil
default:
return tcpip.ErrUnknownProtocolOption
}
@ -577,8 +585,17 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
func (*endpoint) GetSockOpt(tcpip.GettableSocketOption) *tcpip.Error {
return tcpip.ErrUnknownProtocolOption
func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
switch o := opt.(type) {
case *tcpip.LingerOption:
e.mu.Lock()
*o = e.linger
e.mu.Unlock()
return nil
default:
return tcpip.ErrUnknownProtocolOption
}
}
// GetSockOptBool implements tcpip.Endpoint.GetSockOptBool.

View File

@ -154,6 +154,9 @@ type endpoint struct {
// owner is used to get uid and gid of the packet.
owner tcpip.PacketOwner
// linger is used for SO_LINGER socket option.
linger tcpip.LingerOption
}
// +stateify savable
@ -810,6 +813,11 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
case *tcpip.SocketDetachFilterOption:
return nil
case *tcpip.LingerOption:
e.mu.Lock()
e.linger = *v
e.mu.Unlock()
}
return nil
}
@ -966,6 +974,11 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
*o = tcpip.BindToDeviceOption(e.bindToDevice)
e.mu.RUnlock()
case *tcpip.LingerOption:
e.mu.RLock()
*o = e.linger
e.mu.RUnlock()
default:
return tcpip.ErrUnknownProtocolOption
}

View File

@ -643,6 +643,27 @@ TEST_P(RawPacketTest, GetSocketDetachFilter) {
SyscallFailsWithErrno(ENOPROTOOPT));
}
TEST_P(RawPacketTest, SetAndGetSocketLinger) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int level = SOL_SOCKET;
int type = SO_LINGER;
struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 5;
ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)),
SyscallSucceedsWithValue(0));
struct linger got_linger = {};
socklen_t length = sizeof(sl);
ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got_linger));
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest,
::testing::Values(ETH_P_IP, ETH_P_ALL));

View File

@ -416,6 +416,28 @@ TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
}
// Set and get SO_LINGER.
TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
int level = SOL_SOCKET;
int type = SO_LINGER;
struct linger sl;
sl.l_onoff = 1;
sl.l_linger = 5;
ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)),
SyscallSucceedsWithValue(0));
struct linger got_linger = {};
socklen_t length = sizeof(sl);
ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got_linger));
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
void RawSocketICMPTest::ExpectICMPSuccess(const struct icmphdr& icmp) {
// We're going to receive both the echo request and reply, but the order is
// indeterminate.

View File

@ -451,7 +451,7 @@ TEST_P(UDPSocketPairTest, TClassRecvMismatch) {
}
// Test the SO_LINGER option can be set/get on udp socket.
TEST_P(UDPSocketPairTest, SoLingerFail) {
TEST_P(UDPSocketPairTest, SetAndGetSocketLinger) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int level = SOL_SOCKET;
int type = SO_LINGER;
@ -469,15 +469,7 @@ TEST_P(UDPSocketPairTest, SoLingerFail) {
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got_linger));
// Linux returns the values which are set in the SetSockOpt for SO_LINGER.
// In gVisor, we do not store the linger values for UDP as SO_LINGER for UDP
// is a no-op.
if (IsRunningOnGvisor()) {
struct linger want_linger = {};
EXPECT_EQ(0, memcmp(&want_linger, &got_linger, length));
} else {
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
} // namespace testing

View File

@ -103,6 +103,24 @@ TEST_P(StreamUnixSocketPairTest, Sendto) {
SyscallFailsWithErrno(EISCONN));
}
TEST_P(StreamUnixSocketPairTest, SetAndGetSocketLinger) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
struct linger sl = {1, 5};
EXPECT_THAT(
setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
SyscallSucceedsWithValue(0));
struct linger got_linger = {};
socklen_t length = sizeof(sl);
EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
&got_linger, &length),
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got_linger));
EXPECT_EQ(0, memcmp(&got_linger, &sl, length));
}
INSTANTIATE_TEST_SUITE_P(
AllUnixDomainSockets, StreamUnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>(