Internal change.

PiperOrigin-RevId: 286003946
This commit is contained in:
gVisor bot
2019-12-17 10:10:06 -08:00
parent 67000b929b
commit 3f4d8fefb4
3 changed files with 145 additions and 0 deletions
+8
View File
@@ -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
+65
View File
@@ -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()
+72
View File
@@ -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(