blob: b1839f895cb1af7709549c0d0ecfd07b0a0ff9fb [file] [log] [blame]
// Copyright 2016 The Netstack Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tcp_test
import (
"bytes"
"testing"
"time"
"github.com/google/netstack/tcpip"
"github.com/google/netstack/tcpip/buffer"
"github.com/google/netstack/tcpip/checker"
"github.com/google/netstack/tcpip/header"
"github.com/google/netstack/tcpip/network/ipv4"
"github.com/google/netstack/tcpip/seqnum"
"github.com/google/netstack/tcpip/transport/tcp"
"github.com/google/netstack/tcpip/transport/tcp/testing/context"
"github.com/google/netstack/waiter"
)
const (
// defaultMTU is the MTU, in bytes, used throughout the tests, except
// where another value is explicitly used. It is chosen to match the MTU
// of loopback interfaces on linux systems.
defaultMTU = 65535
// defaultIPv4MSS is the MSS sent by the network stack in SYN/SYN-ACK for an
// IPv4 endpoint when the MTU is set to defaultMTU in the test.
defaultIPv4MSS = defaultMTU - header.IPv4MinimumSize - header.TCPMinimumSize
)
func TestGiveUpConnect(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
var wq waiter.Queue
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
if err != nil {
t.Fatalf("NeEndpoint failed: %v", err)
}
// Register for notification, then start connection attempt.
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
wq.EventRegister(&waitEntry, waiter.EventOut)
defer wq.EventUnregister(&waitEntry)
err = ep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
if err != tcpip.ErrConnectStarted {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
// Close the connection, wait for completion.
ep.Close()
// Wait for ep to become writable.
<-notifyCh
err = ep.GetSockOpt(tcpip.ErrorOption{})
}
func TestActiveHandshake(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
}
func TestNonBlockingClose(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
ep := c.EP
c.EP = nil
// Close the endpoint and measure how long it takes.
t0 := time.Now()
ep.Close()
if diff := time.Now().Sub(t0); diff > 3*time.Second {
t.Fatalf("Took too long to close: %v", diff)
}
}
func TestConnectResetAfterClose(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
ep := c.EP
c.EP = nil
// Close the endpoint, make sure we get a FIN segment, then acknowledge
// to complete closure of sender, but don't send our own FIN.
ep.Close()
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Wait for the ep to give up waiting for a FIN, and send a RST.
time.Sleep(3 * time.Second)
for {
b := c.GetPacket()
tcp := header.TCP(header.IPv4(b).Payload())
if tcp.Flags() == header.TCPFlagAck|header.TCPFlagFin {
// This is a retransmit of the FIN, ignore it.
continue
}
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagRst),
),
)
break
}
}
func TestSimpleReceive(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
if _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
data := []byte{1, 2, 3}
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
// Receive data.
v, err := c.EP.Read(nil)
if err != nil {
t.Fatalf("Unexpected error from Read: %v", err)
}
if bytes.Compare(data, v) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, v)
}
// Check that ACK is received.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestOutOfOrderReceive(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
if _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Send second half of data first, with seqnum 3 ahead of expected.
data := []byte{1, 2, 3, 4, 5, 6}
c.SendPacket(data[3:], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 793,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Check that we get an ACK specifying which seqnum is expected.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck),
),
)
// Wait 200ms and check that no data has been received.
time.Sleep(200 * time.Millisecond)
if _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Send the first 3 bytes now.
c.SendPacket(data[:3], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Receive data.
read := make([]byte, 0, 6)
for len(read) < len(data) {
v, err := c.EP.Read(nil)
if err != nil {
if err == tcpip.ErrWouldBlock {
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(5 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
continue
}
t.Fatalf("Unexpected error from Read: %v", err)
}
read = append(read, v...)
}
// Check that we received the data in proper order.
if bytes.Compare(data, read) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, read)
}
// Check that the whole data is acknowledged.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestOutOfOrderFlood(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create a new connection with initial window size of 10.
opt := tcpip.ReceiveBufferSizeOption(10)
c.CreateConnected(789, 30000, &opt)
if _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Send 100 packets before the actual one that is expected.
data := []byte{1, 2, 3, 4, 5, 6}
for i := 0; i < 100; i++ {
c.SendPacket(data[3:], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 796,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
// Send packet with seqnum 793. It must be discarded because the
// out-of-order buffer was filled by the previous packets.
c.SendPacket(data[3:], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 793,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck),
),
)
// Now send the expected packet, seqnum 790.
c.SendPacket(data[:3], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Check that only packet 790 is acknowledged.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(793),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFullWindowReceive(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
opt := tcpip.ReceiveBufferSizeOption(10)
c.CreateConnected(789, 30000, &opt)
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
_, err := c.EP.Read(nil)
if err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Fill up the window.
data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(5 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
// Check that data is acknowledged, and window goes to zero.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
checker.Window(0),
),
)
// Receive data and check it.
v, err := c.EP.Read(nil)
if err != nil {
t.Fatalf("Unexpected error from Read: %v", err)
}
if bytes.Compare(data, v) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, v)
}
// Check that we get an ACK for the newly non-zero window.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
checker.Window(10),
),
)
}
func TestNoWindowShrinking(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Start off with a window size of 10, then shrink it to 5.
opt := tcpip.ReceiveBufferSizeOption(10)
c.CreateConnected(789, 30000, &opt)
opt = 5
if err := c.EP.SetSockOpt(opt); err != nil {
t.Fatalf("SetSockOpt failed: %v", err)
}
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
_, err := c.EP.Read(nil)
if err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Send 3 bytes, check that the peer acknowledges them.
data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
c.SendPacket(data[:3], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(5 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
// Check that data is acknowledged, and that window doesn't go to zero
// just yet because it was previously set to 10. It must go to 7 now.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(793),
checker.TCPFlags(header.TCPFlagAck),
checker.Window(7),
),
)
// Send 7 more bytes, check that the window fills up.
c.SendPacket(data[3:], &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 793,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
select {
case <-ch:
case <-time.After(5 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
checker.Window(0),
),
)
// Receive data and check it.
read := make([]byte, 0, 10)
for len(read) < len(data) {
v, err := c.EP.Read(nil)
if err != nil {
t.Fatalf("Unexpected error from Read: %v", err)
}
read = append(read, v...)
}
if bytes.Compare(data, read) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, read)
}
// Check that we get an ACK for the newly non-zero window, which is the
// new size.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+len(data))),
checker.TCPFlags(header.TCPFlagAck),
checker.Window(5),
),
)
}
func TestSimpleSend(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
if p := b[header.IPv4MinimumSize+header.TCPMinimumSize:]; bytes.Compare(data, p) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, p)
}
// Acknowledge the data.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1 + seqnum.Size(len(data))),
RcvWnd: 30000,
})
}
func TestZeroWindowSend(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 0, nil)
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
_, err := c.EP.Write(view, nil)
if err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Since the window is currently zero, check that no packet is received.
c.CheckNoPacket("Packet received when window is zero")
// Open up the window. Data should be received now.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Check that data is received.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
if p := b[header.IPv4MinimumSize+header.TCPMinimumSize:]; bytes.Compare(data, p) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, p)
}
// Acknowledge the data.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1 + seqnum.Size(len(data))),
RcvWnd: 30000,
})
}
func TestScaledWindowConnect(t *testing.T) {
// This test ensures that window scaling is used when the peer
// does advertise it and connection is established with Connect().
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Set the window size greater than the maximum non-scaled window.
opt := tcpip.ReceiveBufferSizeOption(65535 * 3)
c.CreateConnectedWithRawOptions(789, 30000, &opt, []byte{
header.TCPOptionWS, 3, 0, header.TCPOptionNOP,
})
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received, and that advertised window is 0xbfff,
// that is, that it is scaled.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.Window(0xbfff),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
}
func TestNonScaledWindowConnect(t *testing.T) {
// This test ensures that window scaling is not used when the peer
// doesn't advertise it and connection is established with Connect().
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Set the window size greater than the maximum non-scaled window.
opt := tcpip.ReceiveBufferSizeOption(65535 * 3)
c.CreateConnected(789, 30000, &opt)
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received, and that advertised window is 0xffff,
// that is, that it's not scaled.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.Window(0xffff),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
}
func TestScaledWindowAccept(t *testing.T) {
// This test ensures that window scaling is used when the peer
// does advertise it and connection is established with Accept().
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create EP and start listening.
wq := &waiter.Queue{}
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
defer ep.Close()
// Set the window size greater than the maximum non-scaled window.
if err := ep.SetSockOpt(tcpip.ReceiveBufferSizeOption(65535 * 3)); err != nil {
t.Fatalf("SetSockOpt failed failed: %v", err)
}
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Do 3-way handshake.
c.PassiveConnectWithOptions(100, 2, header.TCPSynOptions{MSS: defaultIPv4MSS})
// Try to accept the connection.
we, ch := waiter.NewChannelEntry(nil)
wq.EventRegister(&we, waiter.EventIn)
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept()
if err == tcpip.ErrWouldBlock {
// Wait for connection to be established.
select {
case <-ch:
c.EP, _, err = ep.Accept()
if err != nil {
t.Fatalf("Accept failed: %v", err)
}
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for accept")
}
}
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received, and that advertised window is 0xbfff,
// that is, that it is scaled.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.Window(0xbfff),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
}
func TestNonScaledWindowAccept(t *testing.T) {
// This test ensures that window scaling is not used when the peer
// doesn't advertise it and connection is established with Accept().
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create EP and start listening.
wq := &waiter.Queue{}
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
defer ep.Close()
// Set the window size greater than the maximum non-scaled window.
if err := ep.SetSockOpt(tcpip.ReceiveBufferSizeOption(65535 * 3)); err != nil {
t.Fatalf("SetSockOpt failed failed: %v", err)
}
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Do 3-way handshake.
c.PassiveConnect(100, 2, header.TCPSynOptions{MSS: defaultIPv4MSS})
// Try to accept the connection.
we, ch := waiter.NewChannelEntry(nil)
wq.EventRegister(&we, waiter.EventIn)
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept()
if err == tcpip.ErrWouldBlock {
// Wait for connection to be established.
select {
case <-ch:
c.EP, _, err = ep.Accept()
if err != nil {
t.Fatalf("Accept failed: %v", err)
}
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for accept")
}
}
data := []byte{1, 2, 3}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received, and that advertised window is 0xffff,
// that is, that it's not scaled.
b := c.GetPacket()
checker.IPv4(t, b,
checker.PayloadLen(len(data)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.Window(0xffff),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
}
func TestZeroScaledWindowReceive(t *testing.T) {
// This test ensures that the endpoint sends a non-zero window size
// advertisement when the scaled window transitions from 0 to non-zero,
// but the actual window (not scaled) hasn't gotten to zero.
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Set the window size such that a window scale of 4 will be used.
const wnd = 65535 * 10
const ws = uint32(4)
opt := tcpip.ReceiveBufferSizeOption(wnd)
c.CreateConnectedWithRawOptions(789, 30000, &opt, []byte{
header.TCPOptionWS, 3, 0, header.TCPOptionNOP,
})
// Write chunks of 50000 bytes.
remain := wnd
sent := 0
data := make([]byte, 50000)
for remain > len(data) {
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: seqnum.Value(790 + sent),
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
sent += len(data)
remain -= len(data)
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+sent)),
checker.Window(uint16(remain>>ws)),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
// Make the window non-zero, but the scaled window zero.
if remain >= 16 {
data = data[:remain-15]
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: seqnum.Value(790 + sent),
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
sent += len(data)
remain -= len(data)
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+sent)),
checker.Window(0),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
// Read some data. An ack should be sent in response to that.
v, err := c.EP.Read(nil)
if err != nil {
t.Fatalf("Unexpected error from Read: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(790+sent)),
checker.Window(uint16(len(v)>>ws)),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func testBrokenUpWrite(t *testing.T, c *context.Context, maxPayload int) {
payloadMultiplier := 10
dataLen := payloadMultiplier * maxPayload
data := make([]byte, dataLen)
for i := range data {
data[i] = byte(i)
}
view := buffer.NewView(len(data))
copy(view, data)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that data is received in chunks.
bytesReceived := 0
numPackets := 0
for bytesReceived != dataLen {
b := c.GetPacket()
numPackets++
tcp := header.TCP(header.IPv4(b).Payload())
payloadLen := len(tcp.Payload())
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1+uint32(bytesReceived)),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
pdata := data[bytesReceived : bytesReceived+payloadLen]
if p := tcp.Payload(); bytes.Compare(pdata, p) != 0 {
t.Fatalf("Data is different: expected %v, got %v", pdata, p)
}
bytesReceived += payloadLen
var options []byte
if c.TimeStampEnabled {
// If timestamp option is enabled, echo back the timestamp and increment
// the TSEcr value included in the packet and send that back as the TSVal.
parsedOpts := tcp.ParsedOptions()
tsOpt := header.EncodeTSOption(parsedOpts.TSEcr+1, parsedOpts.TSVal)
options = append(options, tsOpt[:]...)
}
// Acknowledge the data.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1 + seqnum.Size(bytesReceived)),
RcvWnd: 30000,
TCPOpts: options,
})
}
if numPackets == 1 {
t.Fatalf("expected write to be broken up into multiple packets, but got 1 packet")
}
}
func TestSendGreaterThanMTU(t *testing.T) {
const maxPayload = 100
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload))
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
testBrokenUpWrite(t, c, maxPayload)
}
func TestActiveSendMSSLessThanMTU(t *testing.T) {
const maxPayload = 100
c := context.New(t, 65535)
defer c.Cleanup()
c.CreateConnectedWithRawOptions(789, 30000, nil, []byte{
header.TCPOptionMSS, 4, byte(maxPayload / 256), byte(maxPayload % 256),
})
testBrokenUpWrite(t, c, maxPayload)
}
func TestPassiveSendMSSLessThanMTU(t *testing.T) {
const maxPayload = 100
const mtu = 1200
c := context.New(t, mtu)
defer c.Cleanup()
// Create EP and start listening.
wq := &waiter.Queue{}
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
defer ep.Close()
// Set the buffer size to a deterministic size so that we can check the
// window scaling option.
const rcvBufferSize = 0x20000
const wndScale = 2
if err := ep.SetSockOpt(tcpip.ReceiveBufferSizeOption(rcvBufferSize)); err != nil {
t.Fatalf("SetSockOpt failed failed: %v", err)
}
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Do 3-way handshake.
c.PassiveConnect(maxPayload, wndScale, header.TCPSynOptions{MSS: mtu - header.IPv4MinimumSize - header.TCPMinimumSize})
// Try to accept the connection.
we, ch := waiter.NewChannelEntry(nil)
wq.EventRegister(&we, waiter.EventIn)
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept()
if err == tcpip.ErrWouldBlock {
// Wait for connection to be established.
select {
case <-ch:
c.EP, _, err = ep.Accept()
if err != nil {
t.Fatalf("Accept failed: %v", err)
}
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for accept")
}
}
// Check that data gets properly segmented.
testBrokenUpWrite(t, c, maxPayload)
}
func TestSynCookiePassiveSendMSSLessThanMTU(t *testing.T) {
const maxPayload = 536
const mtu = 2000
c := context.New(t, mtu)
defer c.Cleanup()
// Set the SynRcvd threshold to zero to force a syn cookie based accept
// to happen.
saved := tcp.SynRcvdCountThreshold
defer func() {
tcp.SynRcvdCountThreshold = saved
}()
tcp.SynRcvdCountThreshold = 0
// Create EP and start listening.
wq := &waiter.Queue{}
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
defer ep.Close()
if err := ep.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Do 3-way handshake.
c.PassiveConnect(maxPayload, -1, header.TCPSynOptions{MSS: mtu - header.IPv4MinimumSize - header.TCPMinimumSize})
// Try to accept the connection.
we, ch := waiter.NewChannelEntry(nil)
wq.EventRegister(&we, waiter.EventIn)
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept()
if err == tcpip.ErrWouldBlock {
// Wait for connection to be established.
select {
case <-ch:
c.EP, _, err = ep.Accept()
if err != nil {
t.Fatalf("Accept failed: %v", err)
}
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for accept")
}
}
// Check that data gets properly segmented.
testBrokenUpWrite(t, c, maxPayload)
}
func TestForwarderSendMSSLessThanMTU(t *testing.T) {
const maxPayload = 100
const mtu = 1200
c := context.New(t, mtu)
defer c.Cleanup()
s := c.Stack()
ch := make(chan *tcpip.Error, 1)
f := tcp.NewForwarder(s, 65536, 10, func(r *tcp.ForwarderRequest) {
var err *tcpip.Error
c.EP, err = r.CreateEndpoint(&c.WQ)
ch <- err
})
s.SetTransportProtocolHandler(tcp.ProtocolNumber, f.HandlePacket)
// Do 3-way handshake.
c.PassiveConnect(maxPayload, 1, header.TCPSynOptions{MSS: mtu - header.IPv4MinimumSize - header.TCPMinimumSize})
// Wait for connection to be available.
select {
case err := <-ch:
if err != nil {
t.Fatalf("Error creating endpoint: %v", err)
}
case <-time.After(2 * time.Second):
t.Fatalf("Timed out waiting for connection")
}
// Check that data gets properly segmented.
testBrokenUpWrite(t, c, maxPayload)
}
func TestSynOptionsOnActiveConnect(t *testing.T) {
const mtu = 1400
c := context.New(t, mtu)
defer c.Cleanup()
// Create TCP endpoint.
var err *tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
// Set the buffer size to a deterministic size so that we can check the
// window scaling option.
const rcvBufferSize = 0x20000
const wndScale = 2
if err := c.EP.SetSockOpt(tcpip.ReceiveBufferSizeOption(rcvBufferSize)); err != nil {
t.Fatalf("SetSockOpt failed failed: %v", err)
}
// Start connection attempt.
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventOut)
defer c.WQ.EventUnregister(&we)
err = c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
if err != tcpip.ErrConnectStarted {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
// Receive SYN packet.
b := c.GetPacket()
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.TCPFlags(header.TCPFlagSyn),
checker.TCPSynOptions(header.TCPSynOptions{MSS: mtu - header.IPv4MinimumSize - header.TCPMinimumSize, WS: wndScale}),
),
)
tcp := header.TCP(header.IPv4(b).Payload())
c.IRS = seqnum.Value(tcp.SequenceNumber())
// Wait for retransmit.
time.Sleep(1 * time.Second)
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.TCPFlags(header.TCPFlagSyn),
checker.SrcPort(tcp.SourcePort()),
checker.SeqNum(tcp.SequenceNumber()),
checker.TCPSynOptions(header.TCPSynOptions{MSS: mtu - header.IPv4MinimumSize - header.TCPMinimumSize, WS: wndScale}),
),
)
// Send SYN-ACK.
iss := seqnum.Value(789)
c.SendPacket(nil, &context.Headers{
SrcPort: tcp.DestinationPort(),
DstPort: tcp.SourcePort(),
Flags: header.TCPFlagSyn | header.TCPFlagAck,
SeqNum: iss,
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
// Receive ACK packet.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.TCPFlags(header.TCPFlagAck),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(uint32(iss)+1),
),
)
// Wait for connection to be established.
select {
case <-ch:
err = c.EP.GetSockOpt(tcpip.ErrorOption{})
if err != nil {
t.Fatalf("Unexpected error when connecting: %v", err)
}
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for connection")
}
}
func TestCloseListener(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create listener.
var wq waiter.Queue
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
if err := ep.Bind(tcpip.FullAddress{}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Close the listener and measure how long it takes.
t0 := time.Now()
ep.Close()
if diff := time.Now().Sub(t0); diff > 3*time.Second {
t.Fatalf("Took too long to close: %v", diff)
}
}
func TestReceiveOnResetConnection(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Send RST segment.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagRst,
SeqNum: 790,
RcvWnd: 30000,
})
// Try to read.
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
loop:
for {
switch _, err := c.EP.Read(nil); err {
case nil:
t.Fatalf("Unexpected success.")
case tcpip.ErrWouldBlock:
select {
case <-ch:
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for reset to arrive")
}
case tcpip.ErrConnectionReset:
break loop
default:
t.Fatalf("Unexpected error: want %v, got %v", tcpip.ErrConnectionReset, err)
}
}
}
func TestSendOnResetConnection(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Send RST segment.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagRst,
SeqNum: 790,
RcvWnd: 30000,
})
// Wait for the RST to be received.
time.Sleep(1 * time.Second)
// Try to write.
view := buffer.NewView(10)
_, err := c.EP.Write(view, nil)
if err != tcpip.ErrConnectionReset {
t.Fatalf("Unexpected error from Write: want %v, got %v", tcpip.ErrConnectionReset, err)
}
}
func TestFinImmediately(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Shutdown immediately, check that we get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
// Ack and send FIN as well.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: c.IRS.Add(2),
RcvWnd: 30000,
})
// Check that the stack acks the FIN.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+2),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFinRetransmit(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Shutdown immediately, check that we get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
// Don't acknowledge yet. We should get a retransmit of the FIN.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
// Ack and send FIN as well.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: c.IRS.Add(2),
RcvWnd: 30000,
})
// Check that the stack acks the FIN.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+2),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFinWithNoPendingData(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Write something out, and have it acknowledged.
view := buffer.NewView(10)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
next := uint32(c.IRS) + 1
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
// Shutdown, check that we get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
next++
// Ack and send FIN as well.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
// Check that the stack acks the FIN.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFinWithPendingDataCwndFull(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Write something out but don't ACK it yet.
view := buffer.NewView(10)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
next := uint32(c.IRS) + 1
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
// Shutdown the connection, check that the FIN segment isn't sent
// because the congestion window doesn't allow it. Wait until a
// retransmit is received.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next-uint32(len(view))),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
// Send the ACK that will allow the FIN to be sent as well.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
next++
// Send a FIN that acknowledges everything. Get an ACK back.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFinWithPendingData(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Write something out, and acknowledge it to get cwnd to 2.
view := buffer.NewView(10)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
next := uint32(c.IRS) + 1
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
// Write new data, but don't acknowledge it.
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
// Shutdown the connection, check that we do get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
next++
// Send a FIN that acknowledges everything. Get an ACK back.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck),
),
)
}
func TestFinWithPartialAck(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Write something out, and acknowledge it to get cwnd to 2. Also send
// FIN from the test side.
view := buffer.NewView(10)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
next := uint32(c.IRS) + 1
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
// Check that we get an ACK for the fin.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
// Write new data, but don't acknowledge it.
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(len(view)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
next += uint32(len(view))
// Shutdown the connection, check that we do get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(next),
checker.AckNum(791),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
next++
// Send an ACK for the data, but not for the FIN yet.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 791,
AckNum: seqnum.Value(next - 1),
RcvWnd: 30000,
})
// Check that we don't get a retransmit of the FIN.
c.CheckNoPacketTimeout("FIN retransmitted when data was ack'd", 100*time.Millisecond)
// Ack the FIN.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 791,
AckNum: seqnum.Value(next),
RcvWnd: 30000,
})
}
func TestExponentialIncreaseDuringSlowStart(t *testing.T) {
maxPayload := 10
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload))
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
const iterations = 7
data := buffer.NewView(maxPayload * (1 << (iterations + 1)))
for i := range data {
data[i] = byte(i)
}
// Write all the data in one shot. Packets will only be written at the
// MTU size though.
if _, err := c.EP.Write(data, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
expected := 1
bytesRead := 0
for i := 0; i < iterations; i++ {
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
// Acknowledge all the data received so far.
c.SendAck(790, bytesRead)
// Double the number of expected packets for the next iteration.
expected *= 2
}
}
func TestCongestionAvoidance(t *testing.T) {
maxPayload := 10
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload))
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
const iterations = 7
data := buffer.NewView(2 * maxPayload * (1 << (iterations + 1)))
for i := range data {
data[i] = byte(i)
}
// Write all the data in one shot. Packets will only be written at the
// MTU size though.
if _, err := c.EP.Write(data, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Do slow start for a few iterations.
expected := 1
bytesRead := 0
for i := 0; i < iterations; i++ {
expected = 1 << uint(i)
if i > 0 {
// Acknowledge all the data received so far if not on
// first iteration.
c.SendAck(790, bytesRead)
}
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
}
// Don't acknowledge the first packet of the last packet train. Let's
// wait for them to time out, which will trigger a restart of slow
// start, and initialization of ssthresh to cwnd/2.
rtxOffset := bytesRead - maxPayload*expected
c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload)
// Acknowledge all the data received so far.
c.SendAck(790, bytesRead)
// This part is tricky: when the timeout happened, we had "expected"
// packets pending, cwnd reset to 1, and ssthresh set to expected/2.
// By acknowledging "expected" packets, the slow-start part will
// increase cwnd to expected/2 (which "consumes" expected/2-1 of the
// acknowledgements), then the congestion avoidance part will consume
// an extra expected/2 acks to take cwnd to expected/2 + 1. One ack
// remains in the "ack count" (which will cause cwnd to be incremented
// once it reaches cwnd acks).
//
// So we're straight into congestion avoidance with cwnd set to
// expected/2 + 1.
//
// Check that packets trains of cwnd packets are sent, and that cwnd is
// incremented by 1 after we acknowledge each packet.
expected = expected/2 + 1
for i := 0; i < iterations; i++ {
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
// Acknowledge all the data received so far.
c.SendAck(790, bytesRead)
// In cogestion avoidance, the packets trains increase by 1 in
// each iteration.
expected++
}
}
func TestFastRecovery(t *testing.T) {
maxPayload := 10
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload))
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
const iterations = 7
data := buffer.NewView(2 * maxPayload * (1 << (iterations + 1)))
for i := range data {
data[i] = byte(i)
}
// Write all the data in one shot. Packets will only be written at the
// MTU size though.
if _, err := c.EP.Write(data, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Do slow start for a few iterations.
expected := 1
bytesRead := 0
for i := 0; i < iterations; i++ {
expected = 1 << uint(i)
if i > 0 {
// Acknowledge all the data received so far if not on
// first iteration.
c.SendAck(790, bytesRead)
}
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
}
// Send 10 duplicate acks. This should force an immediate retransmit of
// the pending packet, and inflation of cwnd to expected/2+7.
rtxOffset := bytesRead - maxPayload*expected
for i := 0; i < 10; i++ {
c.SendAck(790, rtxOffset)
}
// Receive the retransmitted packet.
c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload)
// Acknowledge half of the pending data.
rtxOffset = bytesRead - expected*maxPayload/2
c.SendAck(790, rtxOffset)
// Receive the retransmit due to partial ack.
c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload)
// This part is tricky: when the retransmit happened, we had "expected"
// packets pending, cwnd reset to expected/2, and ssthresh set to
// expected/2. By acknowledging expected/2 packets, 7 new packets are
// allowed to be sent immediately.
for j := 0; j < 7; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
// Acknowledge all pending data.
c.SendAck(790, bytesRead)
// Now the inflation is removed, so cwnd is expected/2. But since we've
// received expected+7 packets since cwnd changed, it must now be set
// expected/2 + 2, given that floor((expected+7)/(expected/2)) == 2.
expected = expected/2 + 2
for i := 0; i < iterations; i++ {
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
// Acknowledge all the data received so far.
c.SendAck(790, bytesRead)
// In cogestion avoidance, the packets trains increase by 1 in
// each iteration.
expected++
}
}
func TestRetransmit(t *testing.T) {
maxPayload := 10
c := context.New(t, uint32(header.TCPMinimumSize+header.IPv4MinimumSize+maxPayload))
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
const iterations = 7
data := buffer.NewView(maxPayload * (1 << (iterations + 1)))
for i := range data {
data[i] = byte(i)
}
// Write all the data in two shots. Packets will only be written at the
// MTU size though.
if _, err := c.EP.Write(data[:len(data)/2], nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
if _, err := c.EP.Write(data[len(data)/2:], nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Do slow start for a few iterations.
expected := 1
bytesRead := 0
for i := 0; i < iterations; i++ {
expected = 1 << uint(i)
if i > 0 {
// Acknowledge all the data received so far if not on
// first iteration.
c.SendAck(790, bytesRead)
}
// Read all packets expected on this iteration. Don't
// acknowledge any of them just yet, so that we can measure the
// congestion window.
for j := 0; j < expected; j++ {
c.ReceiveAndCheckPacket(data, bytesRead, maxPayload)
bytesRead += maxPayload
}
// Check we don't receive any more packets on this iteration.
// The timeout can't be too high or we'll trigger a timeout.
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
}
// Wait for a timeout and retransmit.
rtxOffset := bytesRead - maxPayload*expected
c.ReceiveAndCheckPacket(data, rtxOffset, maxPayload)
// Acknowledge half of the pending data.
rtxOffset = bytesRead - expected*maxPayload/2
c.SendAck(790, rtxOffset)
// Receive the remaining data, making sure that acknowledge data is not
// retransmitted.
for offset := rtxOffset; offset < len(data); offset += maxPayload {
c.ReceiveAndCheckPacket(data, offset, maxPayload)
c.SendAck(790, offset+maxPayload)
}
c.CheckNoPacketTimeout("More packets received than expected for this cwnd.", 50*time.Millisecond)
}
func TestUpdateListenBacklog(t *testing.T) {
c := context.New(t, defaultMTU)
defer c.Cleanup()
// Create listener.
var wq waiter.Queue
ep, err := c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &wq)
if err != nil {
t.Fatalf("NewEndpoint failed: %v", err)
}
if err := ep.Bind(tcpip.FullAddress{}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
if err := ep.Listen(10); err != nil {
t.Fatalf("Listen failed: %v", err)
}
// Update the backlog with another Listen() on the same endpoint.
if err := ep.Listen(20); err != nil {
t.Fatalf("Listen failed to update backlog: %v", err)
}
ep.Close()
}
func scaledSendWindow(t *testing.T, scale uint8) {
// This test ensures that the endpoint is using the right scaling by
// sending a buffer that is larger than the window size, and ensuring
// that the endpoint doesn't send more than allowed.
c := context.New(t, defaultMTU)
defer c.Cleanup()
maxPayload := defaultMTU - header.IPv4MinimumSize - header.TCPMinimumSize
c.CreateConnectedWithRawOptions(789, 0, nil, []byte{
header.TCPOptionMSS, 4, byte(maxPayload / 256), byte(maxPayload % 256),
header.TCPOptionWS, 3, scale, header.TCPOptionNOP,
})
// Open up the window with a scaled value.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: 790,
AckNum: c.IRS.Add(1),
RcvWnd: 1,
})
// Send some data. Check that it's capped by the window size.
view := buffer.NewView(65535)
if _, err := c.EP.Write(view, nil); err != nil {
t.Fatalf("Unexpected error from Write: %v", err)
}
// Check that only data that fits in the scaled window is sent.
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen((1<<scale)+header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlagsMatch(header.TCPFlagAck, ^uint8(header.TCPFlagPsh)),
),
)
// Reset the connection to free resources.
c.SendPacket(nil, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagRst,
SeqNum: 790,
})
}
func TestScaledSendWindow(t *testing.T) {
for scale := uint8(0); scale <= 14; scale++ {
scaledSendWindow(t, scale)
}
}
func TestReceivedSegmentQueuing(t *testing.T) {
// This test sends 200 segments containing a few bytes each to an
// endpoint and checks that they're all received and acknowledged by
// the endpoint, that is, that none of the segments are dropped by
// internal queues.
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
// Send 200 segments.
data := []byte{1, 2, 3}
for i := 0; i < 200; i++ {
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck,
SeqNum: seqnum.Value(790 + i*len(data)),
AckNum: c.IRS.Add(1),
RcvWnd: 30000,
})
}
// Receive ACKs for all segments.
last := seqnum.Value(790 + 200*len(data))
for {
b := c.GetPacket()
checker.IPv4(t, b,
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.TCPFlags(header.TCPFlagAck),
),
)
tcp := header.TCP(header.IPv4(b).Payload())
ack := seqnum.Value(tcp.AckNumber())
if ack == last {
break
}
if last.LessThan(ack) {
t.Fatalf("Acknowledge (%v) beyond the expected (%v)", ack, last)
}
}
}
func TestReadAfterClosedState(t *testing.T) {
// This test ensures that calling Read() or Peek() after the endpoint
// has transitioned to closedState still works if there is pending
// data. To transition to stateClosed without calling Close(), we must
// shutdown the send path and the peer must send its own FIN.
c := context.New(t, defaultMTU)
defer c.Cleanup()
c.CreateConnected(789, 30000, nil)
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
if _, err := c.EP.Read(nil); err != tcpip.ErrWouldBlock {
t.Fatalf("Unexpected error from Read: %v", err)
}
// Shutdown immediately for write, check that we get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
t.Fatalf("Unexpected error from Shutdown: %v", err)
}
checker.IPv4(t, c.GetPacket(),
checker.PayloadLen(header.TCPMinimumSize),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+1),
checker.AckNum(790),
checker.TCPFlags(header.TCPFlagAck|header.TCPFlagFin),
),
)
// Send some data and acknowledge the FIN.
data := []byte{1, 2, 3}
c.SendPacket(data, &context.Headers{
SrcPort: context.TestPort,
DstPort: c.Port,
Flags: header.TCPFlagAck | header.TCPFlagFin,
SeqNum: 790,
AckNum: c.IRS.Add(2),
RcvWnd: 30000,
})
// Check that ACK is received.
checker.IPv4(t, c.GetPacket(),
checker.TCP(
checker.DstPort(context.TestPort),
checker.SeqNum(uint32(c.IRS)+2),
checker.AckNum(uint32(791+len(data))),
checker.TCPFlags(header.TCPFlagAck),
),
)
// Give the stack the chance to transition to closed state.
time.Sleep(1 * time.Second)
// Wait for receive to be notified.
select {
case <-ch:
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for data to arrive")
}
// Check that peek works.
peekBuf := make([]byte, 10)
n, err := c.EP.Peek([][]byte{peekBuf})
if err != nil {
t.Fatalf("Unexpected error from Peek: %v", err)
}
peekBuf = peekBuf[:n]
if bytes.Compare(data, peekBuf) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, peekBuf)
}
// Receive data.
v, err := c.EP.Read(nil)
if err != nil {
t.Fatalf("Unexpected error from Read: %v", err)
}
if bytes.Compare(data, v) != 0 {
t.Fatalf("Data is different: expected %v, got %v", data, v)
}
// Now that we drained the queue, check that functions fail with the
// right error code.
if _, err := c.EP.Read(nil); err != tcpip.ErrClosedForReceive {
t.Fatalf("Unexpected return from Read: got %v, want %v", err, tcpip.ErrClosedForReceive)
}
if _, err := c.EP.Peek([][]byte{peekBuf}); err != tcpip.ErrClosedForReceive {
t.Fatalf("Unexpected return from Peek: got %v, want %v", err, tcpip.ErrClosedForReceive)
}
}
func TestReusePort(t *testing.T) {
// This test ensures that ports are immediately available for reuse
// after Close on the endpoints using them returns.
c := context.New(t, defaultMTU)
defer c.Cleanup()
// First case, just an endpoint that was bound.
var err *tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
c.EP.Close()
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
c.EP.Close()
// Second case, an endpoint that was bound and is connecting..
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
err = c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
if err != tcpip.ErrConnectStarted {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
c.EP.Close()
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
c.EP.Close()
// Third case, an endpoint that was bound and is listening.
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
err = c.EP.Listen(10)
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
c.EP.Close()
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %v", err)
}
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}, nil); err != nil {
t.Fatalf("Bind failed: %v", err)
}
err = c.EP.Listen(10)
if err != nil {
t.Fatalf("Listen failed: %v", err)
}
}