{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:
parent
c0b74be54c
commit
d34bda0273
|
@ -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 {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>(
|
||||
|
|
Loading…
Reference in New Issue