mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Internal change.
PiperOrigin-RevId: 286003946
This commit is contained in:
@@ -218,6 +218,14 @@ func (h *handshake) synSentState(s *segment) *tcpip.Error {
|
||||
// acceptable if the ack field acknowledges the SYN.
|
||||
if s.flagIsSet(header.TCPFlagRst) {
|
||||
if s.flagIsSet(header.TCPFlagAck) && s.ackNumber == h.iss+1 {
|
||||
// RFC 793, page 67, states that "If the RST bit is set [and] If the ACK
|
||||
// was acceptable then signal the user "error: connection reset", drop
|
||||
// the segment, enter CLOSED state, delete TCB, and return."
|
||||
h.ep.mu.Lock()
|
||||
h.ep.workerCleanup = true
|
||||
h.ep.mu.Unlock()
|
||||
// Although the RFC above calls out ECONNRESET, Linux actually returns
|
||||
// ECONNREFUSED here so we do as well.
|
||||
return tcpip.ErrConnectionRefused
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1140,6 +1140,71 @@ func TestConnectBindToDevice(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRstOnSynSent(t *testing.T) {
|
||||
c := context.New(t, defaultMTU)
|
||||
defer c.Cleanup()
|
||||
|
||||
// Create an endpoint, don't handshake because we want to interfere with the
|
||||
// handshake process.
|
||||
c.Create(-1)
|
||||
|
||||
// Start connection attempt.
|
||||
waitEntry, ch := waiter.NewChannelEntry(nil)
|
||||
c.WQ.EventRegister(&waitEntry, waiter.EventOut)
|
||||
defer c.WQ.EventUnregister(&waitEntry)
|
||||
|
||||
addr := tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}
|
||||
if err := c.EP.Connect(addr); err != tcpip.ErrConnectStarted {
|
||||
t.Fatalf("got Connect(%+v) = %v, want %s", addr, err, tcpip.ErrConnectStarted)
|
||||
}
|
||||
|
||||
// Receive SYN packet.
|
||||
b := c.GetPacket()
|
||||
checker.IPv4(t, b,
|
||||
checker.TCP(
|
||||
checker.DstPort(context.TestPort),
|
||||
checker.TCPFlags(header.TCPFlagSyn),
|
||||
),
|
||||
)
|
||||
|
||||
// Ensure that we've reached SynSent state
|
||||
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateSynSent; got != want {
|
||||
t.Fatalf("got State() = %s, want %s", got, want)
|
||||
}
|
||||
tcpHdr := header.TCP(header.IPv4(b).Payload())
|
||||
c.IRS = seqnum.Value(tcpHdr.SequenceNumber())
|
||||
|
||||
// Send a packet with a proper ACK and a RST flag to cause the socket
|
||||
// to Error and close out
|
||||
iss := seqnum.Value(789)
|
||||
rcvWnd := seqnum.Size(30000)
|
||||
c.SendPacket(nil, &context.Headers{
|
||||
SrcPort: tcpHdr.DestinationPort(),
|
||||
DstPort: tcpHdr.SourcePort(),
|
||||
Flags: header.TCPFlagRst | header.TCPFlagAck,
|
||||
SeqNum: iss,
|
||||
AckNum: c.IRS.Add(1),
|
||||
RcvWnd: rcvWnd,
|
||||
TCPOpts: nil,
|
||||
})
|
||||
|
||||
// Wait for receive to be notified.
|
||||
select {
|
||||
case <-ch:
|
||||
case <-time.After(3 * time.Second):
|
||||
t.Fatal("timed out waiting for packet to arrive")
|
||||
}
|
||||
|
||||
if _, _, err := c.EP.Read(nil); err != tcpip.ErrConnectionRefused {
|
||||
t.Fatalf("got c.EP.Read(nil) = %v, want = %s", err, tcpip.ErrConnectionRefused)
|
||||
}
|
||||
|
||||
// Due to the RST the endpoint should be in an error state.
|
||||
if got, want := tcp.EndpointState(c.EP.State()), tcp.StateError; got != want {
|
||||
t.Fatalf("got State() = %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOutOfOrderReceive(t *testing.T) {
|
||||
c := context.New(t, defaultMTU)
|
||||
defer c.Cleanup()
|
||||
|
||||
@@ -967,6 +967,78 @@ TEST_P(SimpleTcpSocketTest, BlockingConnectRefused) {
|
||||
EXPECT_THAT(close(s.release()), SyscallSucceeds());
|
||||
}
|
||||
|
||||
// Test that connecting to a non-listening port and thus receiving a RST is
|
||||
// handled appropriately by the socket - the port that the socket was bound to
|
||||
// is released and the expected error is returned.
|
||||
TEST_P(SimpleTcpSocketTest, CleanupOnConnectionRefused) {
|
||||
// Create a socket that is known to not be listening. As is it bound but not
|
||||
// listening, when another socket connects to the port, it will refuse..
|
||||
FileDescriptor bound_s =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
|
||||
|
||||
sockaddr_storage bound_addr =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
|
||||
socklen_t bound_addrlen = sizeof(bound_addr);
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(bound_s.get(), reinterpret_cast<struct sockaddr*>(&bound_addr),
|
||||
bound_addrlen),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Get the addresses the socket is bound to because the port is chosen by the
|
||||
// stack.
|
||||
ASSERT_THAT(getsockname(bound_s.get(),
|
||||
reinterpret_cast<struct sockaddr*>(&bound_addr),
|
||||
&bound_addrlen),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Create, initialize, and bind the socket that is used to test connecting to
|
||||
// the non-listening port.
|
||||
FileDescriptor client_s =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
|
||||
// Initialize client address to the loopback one.
|
||||
sockaddr_storage client_addr =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam()));
|
||||
socklen_t client_addrlen = sizeof(client_addr);
|
||||
|
||||
ASSERT_THAT(
|
||||
bind(client_s.get(), reinterpret_cast<struct sockaddr*>(&client_addr),
|
||||
client_addrlen),
|
||||
SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(getsockname(client_s.get(),
|
||||
reinterpret_cast<struct sockaddr*>(&client_addr),
|
||||
&client_addrlen),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Now the test: connect to the bound but not listening socket with the
|
||||
// client socket. The bound socket should return a RST and cause the client
|
||||
// socket to return an error and clean itself up immediately.
|
||||
// The error being ECONNREFUSED diverges with RFC 793, page 37, but does what
|
||||
// Linux does.
|
||||
ASSERT_THAT(connect(client_s.get(),
|
||||
reinterpret_cast<const struct sockaddr*>(&bound_addr),
|
||||
bound_addrlen),
|
||||
SyscallFailsWithErrno(ECONNREFUSED));
|
||||
|
||||
FileDescriptor new_s =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
|
||||
|
||||
// Test binding to the address from the client socket. This should be okay
|
||||
// if it was dropped correctly.
|
||||
ASSERT_THAT(
|
||||
bind(new_s.get(), reinterpret_cast<struct sockaddr*>(&client_addr),
|
||||
client_addrlen),
|
||||
SyscallSucceeds());
|
||||
|
||||
// Attempt #2, with the new socket and reused addr our connect should fail in
|
||||
// the same way as before, not with an EADDRINUSE.
|
||||
ASSERT_THAT(connect(client_s.get(),
|
||||
reinterpret_cast<const struct sockaddr*>(&bound_addr),
|
||||
bound_addrlen),
|
||||
SyscallFailsWithErrno(ECONNREFUSED));
|
||||
}
|
||||
|
||||
// Test that we get an ECONNREFUSED with a nonblocking socket.
|
||||
TEST_P(SimpleTcpSocketTest, NonBlockingConnectRefused) {
|
||||
FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE(
|
||||
|
||||
Reference in New Issue
Block a user