summaryrefslogtreecommitdiffhomepage
path: root/pkg/tcpip/transport
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/tcpip/transport')
-rw-r--r--pkg/tcpip/transport/icmp/endpoint.go121
-rw-r--r--pkg/tcpip/transport/icmp/endpoint_state.go4
-rw-r--r--pkg/tcpip/transport/icmp/protocol.go18
-rw-r--r--pkg/tcpip/transport/packet/endpoint.go78
-rw-r--r--pkg/tcpip/transport/packet/endpoint_state.go20
-rw-r--r--pkg/tcpip/transport/raw/endpoint.go100
-rw-r--r--pkg/tcpip/transport/raw/endpoint_state.go4
-rw-r--r--pkg/tcpip/transport/raw/protocol.go4
-rw-r--r--pkg/tcpip/transport/tcp/accept.go14
-rw-r--r--pkg/tcpip/transport/tcp/connect.go97
-rw-r--r--pkg/tcpip/transport/tcp/cubic.go4
-rw-r--r--pkg/tcpip/transport/tcp/dual_stack_test.go16
-rw-r--r--pkg/tcpip/transport/tcp/endpoint.go276
-rw-r--r--pkg/tcpip/transport/tcp/endpoint_state.go46
-rw-r--r--pkg/tcpip/transport/tcp/forwarder.go4
-rw-r--r--pkg/tcpip/transport/tcp/protocol.go26
-rw-r--r--pkg/tcpip/transport/tcp/rack.go156
-rw-r--r--pkg/tcpip/transport/tcp/rcv.go12
-rw-r--r--pkg/tcpip/transport/tcp/reno.go8
-rw-r--r--pkg/tcpip/transport/tcp/snd.go95
-rw-r--r--pkg/tcpip/transport/tcp/tcp_rack_test.go62
-rw-r--r--pkg/tcpip/transport/tcp/tcp_test.go254
-rw-r--r--pkg/tcpip/transport/tcp/testing/context/context.go13
-rw-r--r--pkg/tcpip/transport/udp/endpoint.go171
-rw-r--r--pkg/tcpip/transport/udp/endpoint_state.go22
-rw-r--r--pkg/tcpip/transport/udp/forwarder.go2
-rw-r--r--pkg/tcpip/transport/udp/protocol.go14
-rw-r--r--pkg/tcpip/transport/udp/udp_test.go96
28 files changed, 985 insertions, 752 deletions
diff --git a/pkg/tcpip/transport/icmp/endpoint.go b/pkg/tcpip/transport/icmp/endpoint.go
index e4bcd3120..f5e1a6e45 100644
--- a/pkg/tcpip/transport/icmp/endpoint.go
+++ b/pkg/tcpip/transport/icmp/endpoint.go
@@ -84,7 +84,7 @@ type endpoint struct {
ops tcpip.SocketOptions
}
-func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
ep := &endpoint{
stack: s,
TransportEndpointInfo: stack.TransportEndpointInfo{
@@ -159,14 +159,14 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
}
// Read implements tcpip.Endpoint.Read.
-func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, *tcpip.Error) {
+func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
e.rcvMu.Lock()
if e.rcvList.Empty() {
- err := tcpip.ErrWouldBlock
+ var err tcpip.Error = &tcpip.ErrWouldBlock{}
if e.rcvClosed {
e.stats.ReadErrors.ReadClosed.Increment()
- err = tcpip.ErrClosedForReceive
+ err = &tcpip.ErrClosedForReceive{}
}
e.rcvMu.Unlock()
return tcpip.ReadResult{}, err
@@ -193,7 +193,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
n, err := p.data.ReadTo(dst, opts.Peek)
if n == 0 && err != nil {
- return res, tcpip.ErrBadBuffer
+ return res, &tcpip.ErrBadBuffer{}
}
res.Count = n
return res, nil
@@ -204,7 +204,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// reacquire the mutex in exclusive mode.
//
// Returns true for retry if preparation should be retried.
-func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpip.Error) {
+func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip.Error) {
switch e.state {
case stateInitial:
case stateConnected:
@@ -212,11 +212,11 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpi
case stateBound:
if to == nil {
- return false, tcpip.ErrDestinationRequired
+ return false, &tcpip.ErrDestinationRequired{}
}
return false, nil
default:
- return false, tcpip.ErrInvalidEndpointState
+ return false, &tcpip.ErrInvalidEndpointState{}
}
e.mu.RUnlock()
@@ -241,18 +241,18 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpi
// Write writes data to the endpoint's peer. This method does not block
// if the data cannot be written.
-func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
n, err := e.write(p, opts)
- switch err {
+ switch err.(type) {
case nil:
e.stats.PacketsSent.Increment()
- case tcpip.ErrMessageTooLong, tcpip.ErrInvalidOptionValue:
+ case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
e.stats.WriteErrors.InvalidArgs.Increment()
- case tcpip.ErrClosedForSend:
+ case *tcpip.ErrClosedForSend:
e.stats.WriteErrors.WriteClosed.Increment()
- case tcpip.ErrInvalidEndpointState:
+ case *tcpip.ErrInvalidEndpointState:
e.stats.WriteErrors.InvalidEndpointState.Increment()
- case tcpip.ErrNoRoute, tcpip.ErrBroadcastDisabled, tcpip.ErrNetworkUnreachable:
+ case *tcpip.ErrNoRoute, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
// Errors indicating any problem with IP routing of the packet.
e.stats.SendErrors.NoRoute.Increment()
default:
@@ -262,10 +262,10 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
return n, err
}
-func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
// MSG_MORE is unimplemented. (This also means that MSG_EOR is a no-op.)
if opts.More {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
to := opts.To
@@ -275,7 +275,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// If we've shutdown with SHUT_WR we are in an invalid state for sending.
if e.shutdownFlags&tcpip.ShutdownWrite != 0 {
- return 0, tcpip.ErrClosedForSend
+ return 0, &tcpip.ErrClosedForSend{}
}
// Prepare for write.
@@ -297,7 +297,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
nicID := to.NIC
if e.BindNICID != 0 {
if nicID != 0 && nicID != e.BindNICID {
- return 0, tcpip.ErrNoRoute
+ return 0, &tcpip.ErrNoRoute{}
}
nicID = e.BindNICID
@@ -320,10 +320,10 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
v := make([]byte, p.Len())
if _, err := io.ReadFull(p, v); err != nil {
- return 0, tcpip.ErrBadBuffer
+ return 0, &tcpip.ErrBadBuffer{}
}
- var err *tcpip.Error
+ var err tcpip.Error
switch e.NetProto {
case header.IPv4ProtocolNumber:
err = send4(route, e.ID.LocalPort, v, e.ttl, e.owner)
@@ -340,12 +340,12 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
}
// SetSockOpt sets a socket option.
-func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
+func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
return nil
}
// SetSockOptInt sets a socket option. Currently not supported.
-func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
+func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
switch opt {
case tcpip.TTLOption:
e.mu.Lock()
@@ -357,7 +357,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
-func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
+func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
switch opt {
case tcpip.ReceiveQueueSizeOption:
v := 0
@@ -382,18 +382,18 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
return v, nil
default:
- return -1, tcpip.ErrUnknownProtocolOption
+ return -1, &tcpip.ErrUnknownProtocolOption{}
}
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
-func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
-func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpip.PacketOwner) *tcpip.Error {
+func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpip.PacketOwner) tcpip.Error {
if len(data) < header.ICMPv4MinimumSize {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
@@ -411,7 +411,7 @@ func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpi
// Linux performs these basic checks.
if icmpv4.Type() != header.ICMPv4Echo || icmpv4.Code() != 0 {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
icmpv4.SetChecksum(0)
@@ -425,9 +425,9 @@ func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpi
return r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv4ProtocolNumber, TTL: ttl, TOS: stack.DefaultTOS}, pkt)
}
-func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Error {
+func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) tcpip.Error {
if len(data) < header.ICMPv6EchoMinimumSize {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
@@ -442,7 +442,7 @@ func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Err
data = data[header.ICMPv6MinimumSize:]
if icmpv6.Type() != header.ICMPv6EchoRequest || icmpv6.Code() != 0 {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
dataVV := data.ToVectorisedView()
@@ -457,7 +457,7 @@ func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Err
// checkV4MappedLocked determines the effective network protocol and converts
// addr to its canonical form.
-func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
+func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, false /* v6only */)
if err != nil {
return tcpip.FullAddress{}, 0, err
@@ -466,12 +466,12 @@ func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddres
}
// Disconnect implements tcpip.Endpoint.Disconnect.
-func (*endpoint) Disconnect() *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Disconnect() tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
-func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
@@ -486,12 +486,12 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
if nicID != 0 && nicID != e.BindNICID {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
nicID = e.BindNICID
default:
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4MappedLocked(addr)
@@ -536,19 +536,19 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
// ConnectEndpoint is not supported.
-func (*endpoint) ConnectEndpoint(tcpip.Endpoint) *tcpip.Error {
- return tcpip.ErrInvalidEndpointState
+func (*endpoint) ConnectEndpoint(tcpip.Endpoint) tcpip.Error {
+ return &tcpip.ErrInvalidEndpointState{}
}
// Shutdown closes the read and/or write end of the endpoint connection
// to its peer.
-func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
+func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
e.shutdownFlags |= flags
if e.state != stateConnected {
- return tcpip.ErrNotConnected
+ return &tcpip.ErrNotConnected{}
}
if flags&tcpip.ShutdownRead != 0 {
@@ -566,16 +566,16 @@ func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
}
// Listen is not supported by UDP, it just fails.
-func (*endpoint) Listen(int) *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Listen(int) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Accept is not supported by UDP, it just fails.
-func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
- return nil, nil, tcpip.ErrNotSupported
+func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
+ return nil, nil, &tcpip.ErrNotSupported{}
}
-func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, *tcpip.Error) {
+func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.Error) {
if id.LocalPort != 0 {
// The endpoint already has a local port, just attempt to
// register it.
@@ -584,13 +584,13 @@ func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.Networ
}
// We need to find a port for the endpoint.
- _, err := e.stack.PickEphemeralPort(func(p uint16) (bool, *tcpip.Error) {
+ _, err := e.stack.PickEphemeralPort(func(p uint16) (bool, tcpip.Error) {
id.LocalPort = p
err := e.stack.RegisterTransportEndpoint(netProtos, e.TransProto, id, e, ports.Flags{}, 0 /* bindtodevice */)
- switch err {
+ switch err.(type) {
case nil:
return true, nil
- case tcpip.ErrPortInUse:
+ case *tcpip.ErrPortInUse:
return false, nil
default:
return false, err
@@ -600,11 +600,11 @@ func (e *endpoint) registerWithStack(nicID tcpip.NICID, netProtos []tcpip.Networ
return id, err
}
-func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
// Don't allow binding once endpoint is not in the initial state
// anymore.
if e.state != stateInitial {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4MappedLocked(addr)
@@ -620,7 +620,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
if len(addr.Addr) != 0 {
// A local address was specified, verify that it's valid.
if e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr) == 0 {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
}
@@ -648,7 +648,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
// Bind binds the endpoint to a specific local address and port.
// Specifying a NIC is optional.
-func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
@@ -664,7 +664,7 @@ func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
}
// GetLocalAddress returns the address to which the endpoint is bound.
-func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
e.mu.RLock()
defer e.mu.RUnlock()
@@ -676,12 +676,12 @@ func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
}
// GetRemoteAddress returns the address to which the endpoint is connected.
-func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
e.mu.RLock()
defer e.mu.RUnlock()
if e.state != stateConnected {
- return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
}
return tcpip.FullAddress{
@@ -778,9 +778,8 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
}
}
-// HandleControlPacket implements stack.TransportEndpoint.HandleControlPacket.
-func (e *endpoint) HandleControlPacket(typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
-}
+// HandleError implements stack.TransportEndpoint.
+func (*endpoint) HandleError(stack.TransportError, *stack.PacketBuffer) {}
// State implements tcpip.Endpoint.State. The ICMP endpoint currently doesn't
// expose internal socket state.
@@ -806,7 +805,7 @@ func (e *endpoint) Stats() tcpip.EndpointStats {
func (*endpoint) Wait() {}
// LastError implements tcpip.Endpoint.LastError.
-func (*endpoint) LastError() *tcpip.Error {
+func (*endpoint) LastError() tcpip.Error {
return nil
}
diff --git a/pkg/tcpip/transport/icmp/endpoint_state.go b/pkg/tcpip/transport/icmp/endpoint_state.go
index afe96998a..c9fa9974a 100644
--- a/pkg/tcpip/transport/icmp/endpoint_state.go
+++ b/pkg/tcpip/transport/icmp/endpoint_state.go
@@ -75,7 +75,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
return
}
- var err *tcpip.Error
+ var err tcpip.Error
if e.state == stateConnected {
e.route, err = e.stack.FindRoute(e.RegisterNICID, e.BindAddr, e.ID.RemoteAddress, e.NetProto, false /* multicastLoop */)
if err != nil {
@@ -85,7 +85,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
e.ID.LocalAddress = e.route.LocalAddress
} else if len(e.ID.LocalAddress) != 0 { // stateBound
if e.stack.CheckLocalAddress(e.RegisterNICID, e.NetProto, e.ID.LocalAddress) == 0 {
- panic(tcpip.ErrBadLocalAddress)
+ panic(&tcpip.ErrBadLocalAddress{})
}
}
diff --git a/pkg/tcpip/transport/icmp/protocol.go b/pkg/tcpip/transport/icmp/protocol.go
index 3820e5dc7..47f7dd1cb 100644
--- a/pkg/tcpip/transport/icmp/protocol.go
+++ b/pkg/tcpip/transport/icmp/protocol.go
@@ -59,18 +59,18 @@ func (p *protocol) netProto() tcpip.NetworkProtocolNumber {
// NewEndpoint creates a new icmp endpoint. It implements
// stack.TransportProtocol.NewEndpoint.
-func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
if netProto != p.netProto() {
- return nil, tcpip.ErrUnknownProtocol
+ return nil, &tcpip.ErrUnknownProtocol{}
}
return newEndpoint(p.stack, netProto, p.number, waiterQueue)
}
// NewRawEndpoint creates a new raw icmp endpoint. It implements
// stack.TransportProtocol.NewRawEndpoint.
-func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
if netProto != p.netProto() {
- return nil, tcpip.ErrUnknownProtocol
+ return nil, &tcpip.ErrUnknownProtocol{}
}
return raw.NewEndpoint(p.stack, netProto, p.number, waiterQueue)
}
@@ -87,7 +87,7 @@ func (p *protocol) MinimumPacketSize() int {
}
// ParsePorts in case of ICMP sets src to 0, dst to ICMP ID, and err to nil.
-func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
+func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err tcpip.Error) {
switch p.number {
case ProtocolNumber4:
hdr := header.ICMPv4(v)
@@ -106,13 +106,13 @@ func (*protocol) HandleUnknownDestinationPacket(stack.TransportEndpointID, *stac
}
// SetOption implements stack.TransportProtocol.SetOption.
-func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
// Option implements stack.TransportProtocol.Option.
-func (*protocol) Option(tcpip.GettableTransportProtocolOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (*protocol) Option(tcpip.GettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
// Close implements stack.TransportProtocol.Close.
diff --git a/pkg/tcpip/transport/packet/endpoint.go b/pkg/tcpip/transport/packet/endpoint.go
index d48877677..73bb66830 100644
--- a/pkg/tcpip/transport/packet/endpoint.go
+++ b/pkg/tcpip/transport/packet/endpoint.go
@@ -86,15 +86,15 @@ type endpoint struct {
boundNIC tcpip.NICID
// lastErrorMu protects lastError.
- lastErrorMu sync.Mutex `state:"nosave"`
- lastError *tcpip.Error `state:".(string)"`
+ lastErrorMu sync.Mutex `state:"nosave"`
+ lastError tcpip.Error
// ops is used to get socket level options.
ops tcpip.SocketOptions
}
// NewEndpoint returns a new packet endpoint.
-func NewEndpoint(s *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func NewEndpoint(s *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
ep := &endpoint{
stack: s,
TransportEndpointInfo: stack.TransportEndpointInfo{
@@ -159,16 +159,16 @@ func (ep *endpoint) Close() {
func (ep *endpoint) ModerateRecvBuf(copied int) {}
// Read implements tcpip.Endpoint.Read.
-func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, *tcpip.Error) {
+func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
ep.rcvMu.Lock()
// If there's no data to read, return that read would block or that the
// endpoint is closed.
if ep.rcvList.Empty() {
- err := tcpip.ErrWouldBlock
+ var err tcpip.Error = &tcpip.ErrWouldBlock{}
if ep.rcvClosed {
ep.stats.ReadErrors.ReadClosed.Increment()
- err = tcpip.ErrClosedForReceive
+ err = &tcpip.ErrClosedForReceive{}
}
ep.rcvMu.Unlock()
return tcpip.ReadResult{}, err
@@ -198,49 +198,49 @@ func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResul
n, err := packet.data.ReadTo(dst, opts.Peek)
if n == 0 && err != nil {
- return res, tcpip.ErrBadBuffer
+ return res, &tcpip.ErrBadBuffer{}
}
res.Count = n
return res, nil
}
-func (*endpoint) Write(tcpip.Payloader, tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (*endpoint) Write(tcpip.Payloader, tcpip.WriteOptions) (int64, tcpip.Error) {
// TODO(gvisor.dev/issue/173): Implement.
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
// Disconnect implements tcpip.Endpoint.Disconnect. Packet sockets cannot be
// disconnected, and this function always returns tpcip.ErrNotSupported.
-func (*endpoint) Disconnect() *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Disconnect() tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Connect implements tcpip.Endpoint.Connect. Packet sockets cannot be
-// connected, and this function always returnes tcpip.ErrNotSupported.
-func (*endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
- return tcpip.ErrNotSupported
+// connected, and this function always returnes *tcpip.ErrNotSupported.
+func (*endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Shutdown implements tcpip.Endpoint.Shutdown. Packet sockets cannot be used
-// with Shutdown, and this function always returns tcpip.ErrNotSupported.
-func (*endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
- return tcpip.ErrNotSupported
+// with Shutdown, and this function always returns *tcpip.ErrNotSupported.
+func (*endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Listen implements tcpip.Endpoint.Listen. Packet sockets cannot be used with
-// Listen, and this function always returns tcpip.ErrNotSupported.
-func (*endpoint) Listen(backlog int) *tcpip.Error {
- return tcpip.ErrNotSupported
+// Listen, and this function always returns *tcpip.ErrNotSupported.
+func (*endpoint) Listen(backlog int) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Accept implements tcpip.Endpoint.Accept. Packet sockets cannot be used with
-// Accept, and this function always returns tcpip.ErrNotSupported.
-func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
- return nil, nil, tcpip.ErrNotSupported
+// Accept, and this function always returns *tcpip.ErrNotSupported.
+func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
+ return nil, nil, &tcpip.ErrNotSupported{}
}
// Bind implements tcpip.Endpoint.Bind.
-func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
+func (ep *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
// TODO(gvisor.dev/issue/173): Add Bind support.
// "By default, all packets of the specified protocol type are passed
@@ -274,14 +274,14 @@ func (ep *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
}
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
-func (*endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
- return tcpip.FullAddress{}, tcpip.ErrNotSupported
+func (*endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
+ return tcpip.FullAddress{}, &tcpip.ErrNotSupported{}
}
// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
-func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
// Even a connected socket doesn't return a remote address.
- return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
}
// Readiness implements tcpip.Endpoint.Readiness.
@@ -303,19 +303,19 @@ func (ep *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
// SetSockOpt implements tcpip.Endpoint.SetSockOpt. Packet sockets cannot be
// used with SetSockOpt, and this function always returns
-// tcpip.ErrNotSupported.
-func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
+// *tcpip.ErrNotSupported.
+func (ep *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
switch opt.(type) {
case *tcpip.SocketDetachFilterOption:
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
// SetSockOptInt implements tcpip.Endpoint.SetSockOptInt.
-func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
+func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
switch opt {
case tcpip.ReceiveBufferSizeOption:
// Make sure the receive buffer size is within the min and max
@@ -336,11 +336,11 @@ func (ep *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
-func (ep *endpoint) LastError() *tcpip.Error {
+func (ep *endpoint) LastError() tcpip.Error {
ep.lastErrorMu.Lock()
defer ep.lastErrorMu.Unlock()
@@ -350,19 +350,19 @@ func (ep *endpoint) LastError() *tcpip.Error {
}
// UpdateLastError implements tcpip.SocketOptionsHandler.UpdateLastError.
-func (ep *endpoint) UpdateLastError(err *tcpip.Error) {
+func (ep *endpoint) UpdateLastError(err tcpip.Error) {
ep.lastErrorMu.Lock()
ep.lastError = err
ep.lastErrorMu.Unlock()
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
-func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
- return tcpip.ErrNotSupported
+func (ep *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
-func (ep *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
+func (ep *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
switch opt {
case tcpip.ReceiveQueueSizeOption:
v := 0
@@ -381,7 +381,7 @@ func (ep *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
return v, nil
default:
- return -1, tcpip.ErrUnknownProtocolOption
+ return -1, &tcpip.ErrUnknownProtocolOption{}
}
}
diff --git a/pkg/tcpip/transport/packet/endpoint_state.go b/pkg/tcpip/transport/packet/endpoint_state.go
index 4d98fb051..ece662c0d 100644
--- a/pkg/tcpip/transport/packet/endpoint_state.go
+++ b/pkg/tcpip/transport/packet/endpoint_state.go
@@ -68,24 +68,6 @@ func (ep *endpoint) afterLoad() {
// TODO(gvisor.dev/173): Once bind is supported, choose the right NIC.
if err := ep.stack.RegisterPacketEndpoint(0, ep.netProto, ep); err != nil {
- panic(*err)
+ panic(err)
}
}
-
-// saveLastError is invoked by stateify.
-func (ep *endpoint) saveLastError() string {
- if ep.lastError == nil {
- return ""
- }
-
- return ep.lastError.String()
-}
-
-// loadLastError is invoked by stateify.
-func (ep *endpoint) loadLastError(s string) {
- if s == "" {
- return
- }
-
- ep.lastError = tcpip.StringToError(s)
-}
diff --git a/pkg/tcpip/transport/raw/endpoint.go b/pkg/tcpip/transport/raw/endpoint.go
index 6c6d45188..9c9ccc0ff 100644
--- a/pkg/tcpip/transport/raw/endpoint.go
+++ b/pkg/tcpip/transport/raw/endpoint.go
@@ -93,13 +93,13 @@ type endpoint struct {
}
// NewEndpoint returns a raw endpoint for the given protocols.
-func NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func NewEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return newEndpoint(stack, netProto, transProto, waiterQueue, true /* associated */)
}
-func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue, associated bool) (tcpip.Endpoint, *tcpip.Error) {
+func newEndpoint(s *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue, associated bool) (tcpip.Endpoint, tcpip.Error) {
if netProto != header.IPv4ProtocolNumber && netProto != header.IPv6ProtocolNumber {
- return nil, tcpip.ErrUnknownProtocol
+ return nil, &tcpip.ErrUnknownProtocol{}
}
e := &endpoint{
@@ -189,16 +189,16 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
}
// Read implements tcpip.Endpoint.Read.
-func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, *tcpip.Error) {
+func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
e.rcvMu.Lock()
// If there's no data to read, return that read would block or that the
// endpoint is closed.
if e.rcvList.Empty() {
- err := tcpip.ErrWouldBlock
+ var err tcpip.Error = &tcpip.ErrWouldBlock{}
if e.rcvClosed {
e.stats.ReadErrors.ReadClosed.Increment()
- err = tcpip.ErrClosedForReceive
+ err = &tcpip.ErrClosedForReceive{}
}
e.rcvMu.Unlock()
return tcpip.ReadResult{}, err
@@ -225,37 +225,37 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
n, err := pkt.data.ReadTo(dst, opts.Peek)
if n == 0 && err != nil {
- return res, tcpip.ErrBadBuffer
+ return res, &tcpip.ErrBadBuffer{}
}
res.Count = n
return res, nil
}
// Write implements tcpip.Endpoint.Write.
-func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
// We can create, but not write to, unassociated IPv6 endpoints.
if !e.associated && e.TransportEndpointInfo.NetProto == header.IPv6ProtocolNumber {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
if opts.To != nil {
// Raw sockets do not support sending to a IPv4 address on a IPv6 endpoint.
if e.TransportEndpointInfo.NetProto == header.IPv6ProtocolNumber && len(opts.To.Addr) != header.IPv6AddressSize {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
}
n, err := e.write(p, opts)
- switch err {
+ switch err.(type) {
case nil:
e.stats.PacketsSent.Increment()
- case tcpip.ErrMessageTooLong, tcpip.ErrInvalidOptionValue:
+ case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
e.stats.WriteErrors.InvalidArgs.Increment()
- case tcpip.ErrClosedForSend:
+ case *tcpip.ErrClosedForSend:
e.stats.WriteErrors.WriteClosed.Increment()
- case tcpip.ErrInvalidEndpointState:
+ case *tcpip.ErrInvalidEndpointState:
e.stats.WriteErrors.InvalidEndpointState.Increment()
- case tcpip.ErrNoRoute, tcpip.ErrBroadcastDisabled, tcpip.ErrNetworkUnreachable:
+ case *tcpip.ErrNoRoute, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
// Errors indicating any problem with IP routing of the packet.
e.stats.SendErrors.NoRoute.Increment()
default:
@@ -265,22 +265,22 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
return n, err
}
-func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
// MSG_MORE is unimplemented. This also means that MSG_EOR is a no-op.
if opts.More {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
e.mu.RLock()
defer e.mu.RUnlock()
if e.closed {
- return 0, tcpip.ErrInvalidEndpointState
+ return 0, &tcpip.ErrInvalidEndpointState{}
}
payloadBytes := make([]byte, p.Len())
if _, err := io.ReadFull(p, payloadBytes); err != nil {
- return 0, tcpip.ErrBadBuffer
+ return 0, &tcpip.ErrBadBuffer{}
}
// If this is an unassociated socket and callee provided a nonzero
@@ -288,7 +288,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
if e.ops.GetHeaderIncluded() {
ip := header.IPv4(payloadBytes)
if !ip.IsValid(len(payloadBytes)) {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
dstAddr := ip.DestinationAddress()
// Update dstAddr with the address in the IP header, unless
@@ -309,7 +309,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// If the user doesn't specify a destination, they should have
// connected to another address.
if !e.connected {
- return 0, tcpip.ErrDestinationRequired
+ return 0, &tcpip.ErrDestinationRequired{}
}
return e.finishWrite(payloadBytes, e.route)
@@ -319,7 +319,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// goes through a different NIC than the endpoint was bound to.
nic := opts.To.NIC
if e.bound && nic != 0 && nic != e.BindNICID {
- return 0, tcpip.ErrNoRoute
+ return 0, &tcpip.ErrNoRoute{}
}
// Find the route to the destination. If BindAddress is 0,
@@ -336,7 +336,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// finishWrite writes the payload to a route. It resolves the route if
// necessary. It's really just a helper to make defer unnecessary in Write.
-func (e *endpoint) finishWrite(payloadBytes []byte, route *stack.Route) (int64, *tcpip.Error) {
+func (e *endpoint) finishWrite(payloadBytes []byte, route *stack.Route) (int64, tcpip.Error) {
if e.ops.GetHeaderIncluded() {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
Data: buffer.View(payloadBytes).ToVectorisedView(),
@@ -363,22 +363,22 @@ func (e *endpoint) finishWrite(payloadBytes []byte, route *stack.Route) (int64,
}
// Disconnect implements tcpip.Endpoint.Disconnect.
-func (*endpoint) Disconnect() *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Disconnect() tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Connect implements tcpip.Endpoint.Connect.
-func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
// Raw sockets do not support connecting to a IPv4 address on a IPv6 endpoint.
if e.TransportEndpointInfo.NetProto == header.IPv6ProtocolNumber && len(addr.Addr) != header.IPv6AddressSize {
- return tcpip.ErrAddressFamilyNotSupported
+ return &tcpip.ErrAddressFamilyNotSupported{}
}
e.mu.Lock()
defer e.mu.Unlock()
if e.closed {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
nic := addr.NIC
@@ -393,7 +393,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
} else if addr.NIC != e.BindNICID {
// We're bound and addr specifies a NIC. They must be
// the same.
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
}
@@ -424,34 +424,34 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
// Shutdown implements tcpip.Endpoint.Shutdown. It's a noop for raw sockets.
-func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
+func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
if !e.connected {
- return tcpip.ErrNotConnected
+ return &tcpip.ErrNotConnected{}
}
return nil
}
// Listen implements tcpip.Endpoint.Listen.
-func (*endpoint) Listen(backlog int) *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Listen(backlog int) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Accept implements tcpip.Endpoint.Accept.
-func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
- return nil, nil, tcpip.ErrNotSupported
+func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
+ return nil, nil, &tcpip.ErrNotSupported{}
}
// Bind implements tcpip.Endpoint.Bind.
-func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
// If a local address was specified, verify that it's valid.
if len(addr.Addr) != 0 && e.stack.CheckLocalAddress(e.RegisterNICID, e.NetProto, addr.Addr) == 0 {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
if e.associated {
@@ -471,14 +471,14 @@ func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
}
// GetLocalAddress implements tcpip.Endpoint.GetLocalAddress.
-func (*endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
- return tcpip.FullAddress{}, tcpip.ErrNotSupported
+func (*endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
+ return tcpip.FullAddress{}, &tcpip.ErrNotSupported{}
}
// GetRemoteAddress implements tcpip.Endpoint.GetRemoteAddress.
-func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (*endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
// Even a connected socket doesn't return a remote address.
- return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
}
// Readiness implements tcpip.Endpoint.Readiness.
@@ -499,18 +499,18 @@ func (e *endpoint) Readiness(mask waiter.EventMask) waiter.EventMask {
}
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
-func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
+func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
switch opt.(type) {
case *tcpip.SocketDetachFilterOption:
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
// SetSockOptInt implements tcpip.Endpoint.SetSockOptInt.
-func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
+func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
switch opt {
case tcpip.ReceiveBufferSizeOption:
// Make sure the receive buffer size is within the min and max
@@ -531,17 +531,17 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
-func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
-func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
+func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
switch opt {
case tcpip.ReceiveQueueSizeOption:
v := 0
@@ -560,7 +560,7 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
return v, nil
default:
- return -1, tcpip.ErrUnknownProtocolOption
+ return -1, &tcpip.ErrUnknownProtocolOption{}
}
}
@@ -680,7 +680,7 @@ func (e *endpoint) Stats() tcpip.EndpointStats {
func (*endpoint) Wait() {}
// LastError implements tcpip.Endpoint.LastError.
-func (*endpoint) LastError() *tcpip.Error {
+func (*endpoint) LastError() tcpip.Error {
return nil
}
diff --git a/pkg/tcpip/transport/raw/endpoint_state.go b/pkg/tcpip/transport/raw/endpoint_state.go
index 65c64d99f..263ec5146 100644
--- a/pkg/tcpip/transport/raw/endpoint_state.go
+++ b/pkg/tcpip/transport/raw/endpoint_state.go
@@ -73,7 +73,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
// If the endpoint is connected, re-connect.
if e.connected {
- var err *tcpip.Error
+ var err tcpip.Error
// TODO(gvisor.dev/issue/4906): Properly restore the route with the right
// remote address. We used to pass e.remote.RemoteAddress which was
// effectively the empty address but since moving e.route to hold a pointer
@@ -89,7 +89,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
// If the endpoint is bound, re-bind.
if e.bound {
if e.stack.CheckLocalAddress(e.RegisterNICID, e.NetProto, e.BindAddr) == 0 {
- panic(tcpip.ErrBadLocalAddress)
+ panic(&tcpip.ErrBadLocalAddress{})
}
}
diff --git a/pkg/tcpip/transport/raw/protocol.go b/pkg/tcpip/transport/raw/protocol.go
index f30aa2a4a..e393b993d 100644
--- a/pkg/tcpip/transport/raw/protocol.go
+++ b/pkg/tcpip/transport/raw/protocol.go
@@ -25,11 +25,11 @@ import (
type EndpointFactory struct{}
// NewUnassociatedEndpoint implements stack.RawFactory.NewUnassociatedEndpoint.
-func (EndpointFactory) NewUnassociatedEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (EndpointFactory) NewUnassociatedEndpoint(stack *stack.Stack, netProto tcpip.NetworkProtocolNumber, transProto tcpip.TransportProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return newEndpoint(stack, netProto, transProto, waiterQueue, false /* associated */)
}
// NewPacketEndpoint implements stack.RawFactory.NewPacketEndpoint.
-func (EndpointFactory) NewPacketEndpoint(stack *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (EndpointFactory) NewPacketEndpoint(stack *stack.Stack, cooked bool, netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return packet.NewEndpoint(stack, cooked, netProto, waiterQueue)
}
diff --git a/pkg/tcpip/transport/tcp/accept.go b/pkg/tcpip/transport/tcp/accept.go
index e475c36f3..842c1622b 100644
--- a/pkg/tcpip/transport/tcp/accept.go
+++ b/pkg/tcpip/transport/tcp/accept.go
@@ -199,7 +199,7 @@ func (l *listenContext) isCookieValid(id stack.TransportEndpointID, cookie seqnu
// createConnectingEndpoint creates a new endpoint in a connecting state, with
// the connection parameters given by the arguments.
-func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, irs seqnum.Value, rcvdSynOpts *header.TCPSynOptions, queue *waiter.Queue) (*endpoint, *tcpip.Error) {
+func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, irs seqnum.Value, rcvdSynOpts *header.TCPSynOptions, queue *waiter.Queue) (*endpoint, tcpip.Error) {
// Create a new endpoint.
netProto := l.netProto
if netProto == 0 {
@@ -242,7 +242,7 @@ func (l *listenContext) createConnectingEndpoint(s *segment, iss seqnum.Value, i
// On success, a handshake h is returned with h.ep.mu held.
//
// Precondition: if l.listenEP != nil, l.listenEP.mu must be locked.
-func (l *listenContext) startHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*handshake, *tcpip.Error) {
+func (l *listenContext) startHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*handshake, tcpip.Error) {
// Create new endpoint.
irs := s.sequenceNumber
isn := generateSecureISN(s.id, l.stack.Seed())
@@ -267,7 +267,7 @@ func (l *listenContext) startHandshake(s *segment, opts *header.TCPSynOptions, q
ep.mu.Unlock()
ep.Close()
- return nil, tcpip.ErrConnectionAborted
+ return nil, &tcpip.ErrConnectionAborted{}
}
l.addPendingEndpoint(ep)
@@ -281,7 +281,7 @@ func (l *listenContext) startHandshake(s *segment, opts *header.TCPSynOptions, q
l.removePendingEndpoint(ep)
- return nil, tcpip.ErrConnectionAborted
+ return nil, &tcpip.ErrConnectionAborted{}
}
deferAccept = l.listenEP.deferAccept
@@ -313,7 +313,7 @@ func (l *listenContext) startHandshake(s *segment, opts *header.TCPSynOptions, q
// established endpoint is returned with e.mu held.
//
// Precondition: if l.listenEP != nil, l.listenEP.mu must be locked.
-func (l *listenContext) performHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*endpoint, *tcpip.Error) {
+func (l *listenContext) performHandshake(s *segment, opts *header.TCPSynOptions, queue *waiter.Queue, owner tcpip.PacketOwner) (*endpoint, tcpip.Error) {
h, err := l.startHandshake(s, opts, queue, owner)
if err != nil {
return nil, err
@@ -467,7 +467,7 @@ func (e *endpoint) notifyAborted() {
// cookies to accept connections.
//
// Precondition: if ctx.listenEP != nil, ctx.listenEP.mu must be locked.
-func (e *endpoint) handleSynSegment(ctx *listenContext, s *segment, opts *header.TCPSynOptions) *tcpip.Error {
+func (e *endpoint) handleSynSegment(ctx *listenContext, s *segment, opts *header.TCPSynOptions) tcpip.Error {
defer s.decRef()
h, err := ctx.startHandshake(s, opts, &waiter.Queue{}, e.owner)
@@ -522,7 +522,7 @@ func (e *endpoint) acceptQueueIsFull() bool {
// and needs to handle it.
//
// Precondition: if ctx.listenEP != nil, ctx.listenEP.mu must be locked.
-func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) *tcpip.Error {
+func (e *endpoint) handleListenSegment(ctx *listenContext, s *segment) tcpip.Error {
e.rcvListMu.Lock()
rcvClosed := e.rcvClosed
e.rcvListMu.Unlock()
diff --git a/pkg/tcpip/transport/tcp/connect.go b/pkg/tcpip/transport/tcp/connect.go
index 62954d7e4..34a631b53 100644
--- a/pkg/tcpip/transport/tcp/connect.go
+++ b/pkg/tcpip/transport/tcp/connect.go
@@ -226,7 +226,7 @@ func (h *handshake) checkAck(s *segment) bool {
// synSentState handles a segment received when the TCP 3-way handshake is in
// the SYN-SENT state.
-func (h *handshake) synSentState(s *segment) *tcpip.Error {
+func (h *handshake) synSentState(s *segment) tcpip.Error {
// RFC 793, page 37, states that in the SYN-SENT state, a reset is
// acceptable if the ack field acknowledges the SYN.
if s.flagIsSet(header.TCPFlagRst) {
@@ -237,7 +237,7 @@ func (h *handshake) synSentState(s *segment) *tcpip.Error {
h.ep.workerCleanup = true
// Although the RFC above calls out ECONNRESET, Linux actually returns
// ECONNREFUSED here so we do as well.
- return tcpip.ErrConnectionRefused
+ return &tcpip.ErrConnectionRefused{}
}
return nil
}
@@ -314,12 +314,12 @@ func (h *handshake) synSentState(s *segment) *tcpip.Error {
// synRcvdState handles a segment received when the TCP 3-way handshake is in
// the SYN-RCVD state.
-func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
+func (h *handshake) synRcvdState(s *segment) tcpip.Error {
if s.flagIsSet(header.TCPFlagRst) {
// RFC 793, page 37, states that in the SYN-RCVD state, a reset
// is acceptable if the sequence number is in the window.
if s.sequenceNumber.InWindow(h.ackNum, h.rcvWnd) {
- return tcpip.ErrConnectionRefused
+ return &tcpip.ErrConnectionRefused{}
}
return nil
}
@@ -333,7 +333,9 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
// number and "After sending the acknowledgment, drop the unacceptable
// segment and return."
if !s.sequenceNumber.InWindow(h.ackNum, h.rcvWnd) {
- h.ep.sendRaw(buffer.VectorisedView{}, header.TCPFlagAck, h.iss+1, h.ackNum, h.rcvWnd)
+ if h.ep.allowOutOfWindowAck() {
+ h.ep.sendRaw(buffer.VectorisedView{}, header.TCPFlagAck, h.iss+1, h.ackNum, h.rcvWnd)
+ }
return nil
}
@@ -349,7 +351,7 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
h.ep.sendRaw(buffer.VectorisedView{}, header.TCPFlagRst|header.TCPFlagAck, seq, ack, 0)
if !h.active {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
h.resetState()
@@ -412,7 +414,7 @@ func (h *handshake) synRcvdState(s *segment) *tcpip.Error {
return nil
}
-func (h *handshake) handleSegment(s *segment) *tcpip.Error {
+func (h *handshake) handleSegment(s *segment) tcpip.Error {
h.sndWnd = s.window
if !s.flagIsSet(header.TCPFlagSyn) && h.sndWndScale > 0 {
h.sndWnd <<= uint8(h.sndWndScale)
@@ -429,7 +431,7 @@ func (h *handshake) handleSegment(s *segment) *tcpip.Error {
// processSegments goes through the segment queue and processes up to
// maxSegmentsPerWake (if they're available).
-func (h *handshake) processSegments() *tcpip.Error {
+func (h *handshake) processSegments() tcpip.Error {
for i := 0; i < maxSegmentsPerWake; i++ {
s := h.ep.segmentQueue.dequeue()
if s == nil {
@@ -505,7 +507,7 @@ func (h *handshake) start() {
}
// complete completes the TCP 3-way handshake initiated by h.start().
-func (h *handshake) complete() *tcpip.Error {
+func (h *handshake) complete() tcpip.Error {
// Set up the wakers.
var s sleep.Sleeper
resendWaker := sleep.Waker{}
@@ -555,7 +557,7 @@ func (h *handshake) complete() *tcpip.Error {
case wakerForNotification:
n := h.ep.fetchNotifications()
if (n&notifyClose)|(n&notifyAbort) != 0 {
- return tcpip.ErrAborted
+ return &tcpip.ErrAborted{}
}
if n&notifyDrain != 0 {
for !h.ep.segmentQueue.empty() {
@@ -593,19 +595,19 @@ type backoffTimer struct {
t *time.Timer
}
-func newBackoffTimer(timeout, maxTimeout time.Duration, f func()) (*backoffTimer, *tcpip.Error) {
+func newBackoffTimer(timeout, maxTimeout time.Duration, f func()) (*backoffTimer, tcpip.Error) {
if timeout > maxTimeout {
- return nil, tcpip.ErrTimeout
+ return nil, &tcpip.ErrTimeout{}
}
bt := &backoffTimer{timeout: timeout, maxTimeout: maxTimeout}
bt.t = time.AfterFunc(timeout, f)
return bt, nil
}
-func (bt *backoffTimer) reset() *tcpip.Error {
+func (bt *backoffTimer) reset() tcpip.Error {
bt.timeout *= 2
if bt.timeout > MaxRTO {
- return tcpip.ErrTimeout
+ return &tcpip.ErrTimeout{}
}
bt.t.Reset(bt.timeout)
return nil
@@ -706,7 +708,7 @@ type tcpFields struct {
txHash uint32
}
-func (e *endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOptions) *tcpip.Error {
+func (e *endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOptions) tcpip.Error {
tf.opts = makeSynOptions(opts)
// We ignore SYN send errors and let the callers re-attempt send.
if err := e.sendTCP(r, tf, buffer.VectorisedView{}, nil); err != nil {
@@ -716,7 +718,7 @@ func (e *endpoint) sendSynTCP(r *stack.Route, tf tcpFields, opts header.TCPSynOp
return nil
}
-func (e *endpoint) sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) *tcpip.Error {
+func (e *endpoint) sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO) tcpip.Error {
tf.txHash = e.txHash
if err := sendTCP(r, tf, data, gso, e.owner); err != nil {
e.stats.SendErrors.SegmentSendToNetworkFailed.Increment()
@@ -755,7 +757,7 @@ func buildTCPHdr(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso *sta
}
}
-func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) *tcpip.Error {
+func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) tcpip.Error {
// We need to shallow clone the VectorisedView here as ReadToView will
// split the VectorisedView and Trim underlying views as it splits. Not
// doing the clone here will cause the underlying views of data itself
@@ -803,7 +805,7 @@ func sendTCPBatch(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso
// sendTCP sends a TCP segment with the provided options via the provided
// network endpoint and under the provided identity.
-func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) *tcpip.Error {
+func sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedView, gso *stack.GSO, owner tcpip.PacketOwner) tcpip.Error {
optLen := len(tf.opts)
if tf.rcvWnd > math.MaxUint16 {
tf.rcvWnd = math.MaxUint16
@@ -875,7 +877,7 @@ func (e *endpoint) makeOptions(sackBlocks []header.SACKBlock) []byte {
}
// sendRaw sends a TCP segment to the endpoint's peer.
-func (e *endpoint) sendRaw(data buffer.VectorisedView, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size) *tcpip.Error {
+func (e *endpoint) sendRaw(data buffer.VectorisedView, flags byte, seq, ack seqnum.Value, rcvWnd seqnum.Size) tcpip.Error {
var sackBlocks []header.SACKBlock
if e.EndpointState() == StateEstablished && e.rcv.pendingRcvdSegments.Len() > 0 && (flags&header.TCPFlagAck != 0) {
sackBlocks = e.sack.Blocks[:e.sack.NumBlocks]
@@ -941,12 +943,14 @@ func (e *endpoint) handleClose() {
// error code and sends a RST if and only if the error is not ErrConnectionReset
// indicating that the connection is being reset due to receiving a RST. This
// method must only be called from the protocol goroutine.
-func (e *endpoint) resetConnectionLocked(err *tcpip.Error) {
+func (e *endpoint) resetConnectionLocked(err tcpip.Error) {
// Only send a reset if the connection is being aborted for a reason
// other than receiving a reset.
e.setEndpointState(StateError)
e.hardError = err
- if err != tcpip.ErrConnectionReset && err != tcpip.ErrTimeout {
+ switch err.(type) {
+ case *tcpip.ErrConnectionReset, *tcpip.ErrTimeout:
+ default:
// The exact sequence number to be used for the RST is the same as the
// one used by Linux. We need to handle the case of window being shrunk
// which can cause sndNxt to be outside the acceptable window on the
@@ -1056,7 +1060,7 @@ func (e *endpoint) drainClosingSegmentQueue() {
}
}
-func (e *endpoint) handleReset(s *segment) (ok bool, err *tcpip.Error) {
+func (e *endpoint) handleReset(s *segment) (ok bool, err tcpip.Error) {
if e.rcv.acceptable(s.sequenceNumber, 0) {
// RFC 793, page 37 states that "in all states
// except SYN-SENT, all reset (RST) segments are
@@ -1084,7 +1088,7 @@ func (e *endpoint) handleReset(s *segment) (ok bool, err *tcpip.Error) {
// delete the TCB, and return.
case StateCloseWait:
e.transitionToStateCloseLocked()
- e.hardError = tcpip.ErrAborted
+ e.hardError = &tcpip.ErrAborted{}
e.notifyProtocolGoroutine(notifyTickleWorker)
return false, nil
default:
@@ -1097,14 +1101,14 @@ func (e *endpoint) handleReset(s *segment) (ok bool, err *tcpip.Error) {
// handleSegment is invoked from the processor goroutine
// rather than the worker goroutine.
e.notifyProtocolGoroutine(notifyResetByPeer)
- return false, tcpip.ErrConnectionReset
+ return false, &tcpip.ErrConnectionReset{}
}
}
return true, nil
}
// handleSegments processes all inbound segments.
-func (e *endpoint) handleSegments(fastPath bool) *tcpip.Error {
+func (e *endpoint) handleSegments(fastPath bool) tcpip.Error {
checkRequeue := true
for i := 0; i < maxSegmentsPerWake; i++ {
if e.EndpointState().closed() {
@@ -1151,7 +1155,7 @@ func (e *endpoint) probeSegment() {
// handleSegment handles a given segment and notifies the worker goroutine if
// if the connection should be terminated.
-func (e *endpoint) handleSegment(s *segment) (cont bool, err *tcpip.Error) {
+func (e *endpoint) handleSegment(s *segment) (cont bool, err tcpip.Error) {
// Invoke the tcp probe if installed. The tcp probe function will update
// the TCPEndpointState after the segment is processed.
defer e.probeSegment()
@@ -1183,8 +1187,7 @@ func (e *endpoint) handleSegment(s *segment) (cont bool, err *tcpip.Error) {
// endpoint MUST terminate its connection. The local TCP endpoint
// should then rely on SYN retransmission from the remote end to
// re-establish the connection.
-
- e.snd.sendAck()
+ e.snd.maybeSendOutOfWindowAck(s)
} else if s.flagIsSet(header.TCPFlagAck) {
// Patch the window size in the segment according to the
// send window scale.
@@ -1225,7 +1228,7 @@ func (e *endpoint) handleSegment(s *segment) (cont bool, err *tcpip.Error) {
// keepaliveTimerExpired is called when the keepaliveTimer fires. We send TCP
// keepalive packets periodically when the connection is idle. If we don't hear
// from the other side after a number of tries, we terminate the connection.
-func (e *endpoint) keepaliveTimerExpired() *tcpip.Error {
+func (e *endpoint) keepaliveTimerExpired() tcpip.Error {
userTimeout := e.userTimeout
e.keepalive.Lock()
@@ -1239,13 +1242,13 @@ func (e *endpoint) keepaliveTimerExpired() *tcpip.Error {
if userTimeout != 0 && time.Since(e.rcv.lastRcvdAckTime) >= userTimeout && e.keepalive.unacked > 0 {
e.keepalive.Unlock()
e.stack.Stats().TCP.EstablishedTimedout.Increment()
- return tcpip.ErrTimeout
+ return &tcpip.ErrTimeout{}
}
if e.keepalive.unacked >= e.keepalive.count {
e.keepalive.Unlock()
e.stack.Stats().TCP.EstablishedTimedout.Increment()
- return tcpip.ErrTimeout
+ return &tcpip.ErrTimeout{}
}
// RFC1122 4.2.3.6: TCP keepalive is a dataless ACK with
@@ -1289,7 +1292,7 @@ func (e *endpoint) disableKeepaliveTimer() {
// protocolMainLoop is the main loop of the TCP protocol. It runs in its own
// goroutine and is responsible for sending segments and handling received
// segments.
-func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{}) *tcpip.Error {
+func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{}) tcpip.Error {
e.mu.Lock()
var closeTimer *time.Timer
var closeWaker sleep.Waker
@@ -1335,6 +1338,14 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
}
}
+ // Reaching this point means that we successfully completed the 3-way
+ // handshake with our peer.
+ //
+ // Completing the 3-way handshake is an indication that the route is valid
+ // and the remote is reachable as the only way we can complete a handshake
+ // is if our SYN reached the remote and their ACK reached us.
+ e.route.ConfirmReachable()
+
drained := e.drainDone != nil
if drained {
close(e.drainDone)
@@ -1347,25 +1358,25 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
// wakes up.
funcs := []struct {
w *sleep.Waker
- f func() *tcpip.Error
+ f func() tcpip.Error
}{
{
w: &e.sndWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
e.handleWrite()
return nil
},
},
{
w: &e.sndCloseWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
e.handleClose()
return nil
},
},
{
w: &closeWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
// This means the socket is being closed due
// to the TCP-FIN-WAIT2 timeout was hit. Just
// mark the socket as closed.
@@ -1376,10 +1387,10 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
},
{
w: &e.snd.resendWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
if !e.snd.retransmitTimerExpired() {
e.stack.Stats().TCP.EstablishedTimedout.Increment()
- return tcpip.ErrTimeout
+ return &tcpip.ErrTimeout{}
}
return nil
},
@@ -1390,7 +1401,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
},
{
w: &e.newSegmentWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
return e.handleSegments(false /* fastPath */)
},
},
@@ -1400,7 +1411,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
},
{
w: &e.notificationWaker,
- f: func() *tcpip.Error {
+ f: func() tcpip.Error {
n := e.fetchNotifications()
if n&notifyNonZeroReceiveWindow != 0 {
e.rcv.nonZeroWindow()
@@ -1417,11 +1428,11 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
}
if n&notifyReset != 0 || n&notifyAbort != 0 {
- return tcpip.ErrConnectionAborted
+ return &tcpip.ErrConnectionAborted{}
}
if n&notifyResetByPeer != 0 {
- return tcpip.ErrConnectionReset
+ return &tcpip.ErrConnectionReset{}
}
if n&notifyClose != 0 && closeTimer == nil {
@@ -1500,7 +1511,7 @@ func (e *endpoint) protocolMainLoop(handshake bool, wakerInitDone chan<- struct{
// Main loop. Handle segments until both send and receive ends of the
// connection have completed.
- cleanupOnError := func(err *tcpip.Error) {
+ cleanupOnError := func(err tcpip.Error) {
e.stack.Stats().TCP.CurrentConnected.Decrement()
e.workerCleanup = true
if err != nil {
diff --git a/pkg/tcpip/transport/tcp/cubic.go b/pkg/tcpip/transport/tcp/cubic.go
index 7b1f5e763..1975f1a44 100644
--- a/pkg/tcpip/transport/tcp/cubic.go
+++ b/pkg/tcpip/transport/tcp/cubic.go
@@ -178,8 +178,8 @@ func (c *cubicState) getCwnd(packetsAcked, sndCwnd int, srtt time.Duration) int
return int(cwnd)
}
-// HandleNDupAcks implements congestionControl.HandleNDupAcks.
-func (c *cubicState) HandleNDupAcks() {
+// HandleLossDetected implements congestionControl.HandleLossDetected.
+func (c *cubicState) HandleLossDetected() {
// See: https://tools.ietf.org/html/rfc8312#section-4.5
c.numCongestionEvents++
c.t = time.Now()
diff --git a/pkg/tcpip/transport/tcp/dual_stack_test.go b/pkg/tcpip/transport/tcp/dual_stack_test.go
index 809c88732..2d90246e4 100644
--- a/pkg/tcpip/transport/tcp/dual_stack_test.go
+++ b/pkg/tcpip/transport/tcp/dual_stack_test.go
@@ -37,7 +37,7 @@ func TestV4MappedConnectOnV6Only(t *testing.T) {
// Start connection attempt, it must fail.
err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV4MappedAddr, Port: context.TestPort})
- if err != tcpip.ErrNoRoute {
+ if _, ok := err.(*tcpip.ErrNoRoute); !ok {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
}
@@ -49,7 +49,7 @@ func testV4Connect(t *testing.T, c *context.Context, checkers ...checker.Network
defer c.WQ.EventUnregister(&we)
err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV4MappedAddr, Port: context.TestPort})
- if err != tcpip.ErrConnectStarted {
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
@@ -156,7 +156,7 @@ func testV6Connect(t *testing.T, c *context.Context, checkers ...checker.Network
defer c.WQ.EventUnregister(&we)
err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestV6Addr, Port: context.TestPort})
- if err != tcpip.ErrConnectStarted {
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
t.Fatalf("Unexpected return value from Connect: %v", err)
}
@@ -391,7 +391,7 @@ func testV4Accept(t *testing.T, c *context.Context) {
defer c.WQ.EventUnregister(&we)
nep, _, err := c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -525,7 +525,7 @@ func TestV6AcceptOnV6(t *testing.T) {
defer c.WQ.EventUnregister(&we)
var addr tcpip.FullAddress
_, _, err := c.EP.Accept(&addr)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -549,7 +549,7 @@ func TestV4AcceptOnV4(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ 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)
@@ -613,7 +613,7 @@ func testV4ListenClose(t *testing.T, c *context.Context) {
c.WQ.EventRegister(&we, waiter.EventIn)
defer c.WQ.EventUnregister(&we)
nep, _, err := c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -635,7 +635,7 @@ func TestV4ListenCloseOnV4(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ 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)
diff --git a/pkg/tcpip/transport/tcp/endpoint.go b/pkg/tcpip/transport/tcp/endpoint.go
index b6bd6d455..4e5a6089f 100644
--- a/pkg/tcpip/transport/tcp/endpoint.go
+++ b/pkg/tcpip/transport/tcp/endpoint.go
@@ -386,12 +386,12 @@ type endpoint struct {
// hardError is meaningful only when state is stateError. It stores the
// error to be returned when read/write syscalls are called and the
// endpoint is in this state. hardError is protected by endpoint mu.
- hardError *tcpip.Error `state:".(string)"`
+ hardError tcpip.Error
// lastError represents the last error that the endpoint reported;
// access to it is protected by the following mutex.
- lastErrorMu sync.Mutex `state:"nosave"`
- lastError *tcpip.Error `state:".(string)"`
+ lastErrorMu sync.Mutex `state:"nosave"`
+ lastError tcpip.Error
// rcvReadMu synchronizes calls to Read.
//
@@ -688,6 +688,10 @@ type endpoint struct {
// ops is used to get socket level options.
ops tcpip.SocketOptions
+
+ // lastOutOfWindowAckTime is the time at which the an ACK was sent in response
+ // to an out of window segment being received by this endpoint.
+ lastOutOfWindowAckTime time.Time `state:".(unixTime)"`
}
// UniqueID implements stack.TransportEndpoint.UniqueID.
@@ -1059,7 +1063,7 @@ func (e *endpoint) Close() {
if isResetState {
// Close the endpoint without doing full shutdown and
// send a RST.
- e.resetConnectionLocked(tcpip.ErrConnectionAborted)
+ e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
e.closeNoShutdownLocked()
// Wake up worker to close the endpoint.
@@ -1293,14 +1297,14 @@ func (e *endpoint) SetOwner(owner tcpip.PacketOwner) {
}
// Preconditions: e.mu must be held to call this function.
-func (e *endpoint) hardErrorLocked() *tcpip.Error {
+func (e *endpoint) hardErrorLocked() tcpip.Error {
err := e.hardError
e.hardError = nil
return err
}
// Preconditions: e.mu must be held to call this function.
-func (e *endpoint) lastErrorLocked() *tcpip.Error {
+func (e *endpoint) lastErrorLocked() tcpip.Error {
e.lastErrorMu.Lock()
defer e.lastErrorMu.Unlock()
err := e.lastError
@@ -1309,7 +1313,7 @@ func (e *endpoint) lastErrorLocked() *tcpip.Error {
}
// LastError implements tcpip.Endpoint.LastError.
-func (e *endpoint) LastError() *tcpip.Error {
+func (e *endpoint) LastError() tcpip.Error {
e.LockUser()
defer e.UnlockUser()
if err := e.hardErrorLocked(); err != nil {
@@ -1319,7 +1323,7 @@ func (e *endpoint) LastError() *tcpip.Error {
}
// UpdateLastError implements tcpip.SocketOptionsHandler.UpdateLastError.
-func (e *endpoint) UpdateLastError(err *tcpip.Error) {
+func (e *endpoint) UpdateLastError(err tcpip.Error) {
e.LockUser()
e.lastErrorMu.Lock()
e.lastError = err
@@ -1328,7 +1332,7 @@ func (e *endpoint) UpdateLastError(err *tcpip.Error) {
}
// Read implements tcpip.Endpoint.Read.
-func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, *tcpip.Error) {
+func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
e.rcvReadMu.Lock()
defer e.rcvReadMu.Unlock()
@@ -1337,7 +1341,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// can remove segments from the list through commitRead().
first, last, serr := e.startRead()
if serr != nil {
- if serr == tcpip.ErrClosedForReceive {
+ if _, ok := serr.(*tcpip.ErrClosedForReceive); ok {
e.stats.ReadErrors.ReadClosed.Increment()
}
return tcpip.ReadResult{}, serr
@@ -1377,7 +1381,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// If something is read, we must report it. Report error when nothing is read.
if done == 0 && err != nil {
- return tcpip.ReadResult{}, tcpip.ErrBadBuffer
+ return tcpip.ReadResult{}, &tcpip.ErrBadBuffer{}
}
return tcpip.ReadResult{
Count: done,
@@ -1389,7 +1393,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// inclusive range of segments that can be read.
//
// Precondition: e.rcvReadMu must be held.
-func (e *endpoint) startRead() (first, last *segment, err *tcpip.Error) {
+func (e *endpoint) startRead() (first, last *segment, err tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
@@ -1398,7 +1402,7 @@ func (e *endpoint) startRead() (first, last *segment, err *tcpip.Error) {
// on a receive. It can expect to read any data after the handshake
// is complete. RFC793, section 3.9, p58.
if e.EndpointState() == StateSynSent {
- return nil, nil, tcpip.ErrWouldBlock
+ return nil, nil, &tcpip.ErrWouldBlock{}
}
// The endpoint can be read if it's connected, or if it's already closed
@@ -1414,17 +1418,17 @@ func (e *endpoint) startRead() (first, last *segment, err *tcpip.Error) {
if err := e.hardErrorLocked(); err != nil {
return nil, nil, err
}
- return nil, nil, tcpip.ErrClosedForReceive
+ return nil, nil, &tcpip.ErrClosedForReceive{}
}
e.stats.ReadErrors.NotConnected.Increment()
- return nil, nil, tcpip.ErrNotConnected
+ return nil, nil, &tcpip.ErrNotConnected{}
}
if e.rcvBufUsed == 0 {
if e.rcvClosed || !e.EndpointState().connected() {
- return nil, nil, tcpip.ErrClosedForReceive
+ return nil, nil, &tcpip.ErrClosedForReceive{}
}
- return nil, nil, tcpip.ErrWouldBlock
+ return nil, nil, &tcpip.ErrWouldBlock{}
}
return e.rcvList.Front(), e.rcvList.Back(), nil
@@ -1476,39 +1480,39 @@ func (e *endpoint) commitRead(done int) *segment {
// moment. If the endpoint is not writable then it returns an error
// indicating the reason why it's not writable.
// Caller must hold e.mu and e.sndBufMu
-func (e *endpoint) isEndpointWritableLocked() (int, *tcpip.Error) {
+func (e *endpoint) isEndpointWritableLocked() (int, tcpip.Error) {
// The endpoint cannot be written to if it's not connected.
switch s := e.EndpointState(); {
case s == StateError:
if err := e.hardErrorLocked(); err != nil {
return 0, err
}
- return 0, tcpip.ErrClosedForSend
+ return 0, &tcpip.ErrClosedForSend{}
case !s.connecting() && !s.connected():
- return 0, tcpip.ErrClosedForSend
+ return 0, &tcpip.ErrClosedForSend{}
case s.connecting():
// As per RFC793, page 56, a send request arriving when in connecting
// state, can be queued to be completed after the state becomes
// connected. Return an error code for the caller of endpoint Write to
// try again, until the connection handshake is complete.
- return 0, tcpip.ErrWouldBlock
+ return 0, &tcpip.ErrWouldBlock{}
}
// Check if the connection has already been closed for sends.
if e.sndClosed {
- return 0, tcpip.ErrClosedForSend
+ return 0, &tcpip.ErrClosedForSend{}
}
sndBufSize := e.getSendBufferSize()
avail := sndBufSize - e.sndBufUsed
if avail <= 0 {
- return 0, tcpip.ErrWouldBlock
+ return 0, &tcpip.ErrWouldBlock{}
}
return avail, nil
}
// Write writes data to the endpoint's peer.
-func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
// Linux completely ignores any address passed to sendto(2) for TCP sockets
// (without the MSG_FASTOPEN flag). Corking is unimplemented, so opts.More
// and opts.EndOfRecord are also ignored.
@@ -1516,7 +1520,7 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
e.LockUser()
defer e.UnlockUser()
- nextSeg, n, err := func() (*segment, int, *tcpip.Error) {
+ nextSeg, n, err := func() (*segment, int, tcpip.Error) {
e.sndBufMu.Lock()
defer e.sndBufMu.Unlock()
@@ -1526,7 +1530,7 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
return nil, 0, err
}
- v, err := func() ([]byte, *tcpip.Error) {
+ v, err := func() ([]byte, tcpip.Error) {
// We can release locks while copying data.
//
// This is not possible if atomic is set, because we can't allow the
@@ -1549,7 +1553,7 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
}
v := make([]byte, avail)
if _, err := io.ReadFull(p, v); err != nil {
- return nil, tcpip.ErrBadBuffer
+ return nil, &tcpip.ErrBadBuffer{}
}
return v, nil
}()
@@ -1702,7 +1706,7 @@ func (e *endpoint) getSendBufferSize() int {
}
// SetSockOptInt sets a socket option.
-func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
+func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
// Lower 2 bits represents ECN bits. RFC 3168, section 23.1
const inetECNMask = 3
@@ -1730,7 +1734,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
case tcpip.MaxSegOption:
userMSS := v
if userMSS < header.TCPMinimumMSS || userMSS > header.TCPMaximumMSS {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
e.LockUser()
e.userMSS = uint16(userMSS)
@@ -1741,7 +1745,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
// Return not supported if attempting to set this option to
// anything other than path MTU discovery disabled.
if v != tcpip.PMTUDiscoveryDont {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
case tcpip.ReceiveBufferSizeOption:
@@ -1801,7 +1805,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
case tcpip.TCPSynCountOption:
if v < 1 || v > 255 {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
e.LockUser()
e.maxSynRetries = uint8(v)
@@ -1817,7 +1821,7 @@ func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
return nil
default:
e.UnlockUser()
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
}
var rs tcpip.TCPReceiveBufferSizeRangeOption
@@ -1838,7 +1842,7 @@ func (e *endpoint) HasNIC(id int32) bool {
}
// SetSockOpt sets a socket option.
-func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
+func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
switch v := opt.(type) {
case *tcpip.KeepaliveIdleOption:
e.keepalive.Lock()
@@ -1884,7 +1888,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
// Linux returns ENOENT when an invalid congestion
// control algorithm is specified.
- return tcpip.ErrNoSuchFile
+ return &tcpip.ErrNoSuchFile{}
case *tcpip.TCPLingerTimeoutOption:
e.LockUser()
@@ -1927,13 +1931,13 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
}
// readyReceiveSize returns the number of bytes ready to be received.
-func (e *endpoint) readyReceiveSize() (int, *tcpip.Error) {
+func (e *endpoint) readyReceiveSize() (int, tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
// The endpoint cannot be in listen state.
if e.EndpointState() == StateListen {
- return 0, tcpip.ErrInvalidEndpointState
+ return 0, &tcpip.ErrInvalidEndpointState{}
}
e.rcvListMu.Lock()
@@ -1943,7 +1947,7 @@ func (e *endpoint) readyReceiveSize() (int, *tcpip.Error) {
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
-func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
+func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
switch opt {
case tcpip.KeepaliveCountOption:
e.keepalive.Lock()
@@ -2007,24 +2011,38 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
return 1, nil
default:
- return -1, tcpip.ErrUnknownProtocolOption
+ return -1, &tcpip.ErrUnknownProtocolOption{}
}
}
+func (e *endpoint) getTCPInfo() tcpip.TCPInfoOption {
+ info := tcpip.TCPInfoOption{}
+ e.LockUser()
+ snd := e.snd
+ if snd != nil {
+ // We do not calculate RTT before sending the data packets. If
+ // the connection did not send and receive data, then RTT will
+ // be zero.
+ snd.rtt.Lock()
+ info.RTT = snd.rtt.srtt
+ info.RTTVar = snd.rtt.rttvar
+ snd.rtt.Unlock()
+
+ info.RTO = snd.rto
+ info.CcState = snd.state
+ info.SndSsthresh = uint32(snd.sndSsthresh)
+ info.SndCwnd = uint32(snd.sndCwnd)
+ info.ReorderSeen = snd.rc.reorderSeen
+ }
+ e.UnlockUser()
+ return info
+}
+
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
-func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
+func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
switch o := opt.(type) {
case *tcpip.TCPInfoOption:
- *o = tcpip.TCPInfoOption{}
- e.LockUser()
- snd := e.snd
- e.UnlockUser()
- if snd != nil {
- snd.rtt.Lock()
- o.RTT = snd.rtt.srtt
- o.RTTVar = snd.rtt.rttvar
- snd.rtt.Unlock()
- }
+ *o = e.getTCPInfo()
case *tcpip.KeepaliveIdleOption:
e.keepalive.Lock()
@@ -2070,14 +2088,14 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
}
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
return nil
}
// checkV4MappedLocked determines the effective network protocol and converts
// addr to its canonical form.
-func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
+func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.ops.GetV6Only())
if err != nil {
return tcpip.FullAddress{}, 0, err
@@ -2086,18 +2104,20 @@ func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddres
}
// Disconnect implements tcpip.Endpoint.Disconnect.
-func (*endpoint) Disconnect() *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Disconnect() tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Connect connects the endpoint to its peer.
-func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
err := e.connect(addr, true, true)
- if err != nil && !err.IgnoreStats() {
- // Connect failed. Let's wake up any waiters.
- e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
- e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
- e.stats.FailedConnectionAttempts.Increment()
+ if err != nil {
+ if !err.IgnoreStats() {
+ // Connect failed. Let's wake up any waiters.
+ e.waiterQueue.Notify(waiter.EventHUp | waiter.EventErr | waiter.EventIn | waiter.EventOut)
+ e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
+ e.stats.FailedConnectionAttempts.Increment()
+ }
}
return err
}
@@ -2108,7 +2128,7 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
// created (so no new handshaking is done); for stack-accepted connections not
// yet accepted by the app, they are restored without running the main goroutine
// here.
-func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tcpip.Error {
+func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) tcpip.Error {
e.LockUser()
defer e.UnlockUser()
@@ -2127,7 +2147,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
return nil
}
// Otherwise return that it's already connected.
- return tcpip.ErrAlreadyConnected
+ return &tcpip.ErrAlreadyConnected{}
}
nicID := addr.NIC
@@ -2140,7 +2160,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
}
if nicID != 0 && nicID != e.boundNICID {
- return tcpip.ErrNoRoute
+ return &tcpip.ErrNoRoute{}
}
nicID = e.boundNICID
@@ -2152,16 +2172,16 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
case StateConnecting, StateSynSent, StateSynRecv:
// A connection request has already been issued but hasn't completed
// yet.
- return tcpip.ErrAlreadyConnecting
+ return &tcpip.ErrAlreadyConnecting{}
case StateError:
if err := e.hardErrorLocked(); err != nil {
return err
}
- return tcpip.ErrConnectionAborted
+ return &tcpip.ErrConnectionAborted{}
default:
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
// Find a route to the desired destination.
@@ -2217,12 +2237,12 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
}
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
- if _, err := e.stack.PickEphemeralPortStable(portOffset, func(p uint16) (bool, *tcpip.Error) {
+ if _, err := e.stack.PickEphemeralPortStable(portOffset, func(p uint16) (bool, tcpip.Error) {
if sameAddr && p == e.ID.RemotePort {
return false, nil
}
if _, err := e.stack.ReservePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, bindToDevice, addr, nil /* testPort */); err != nil {
- if err != tcpip.ErrPortInUse || !reuse {
+ if _, ok := err.(*tcpip.ErrPortInUse); !ok || !reuse {
return false, nil
}
transEPID := e.ID
@@ -2268,7 +2288,7 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
id.LocalPort = p
if err := e.stack.RegisterTransportEndpoint(netProtos, ProtocolNumber, id, e, e.portFlags, bindToDevice); err != nil {
e.stack.ReleasePort(netProtos, ProtocolNumber, e.ID.LocalAddress, p, e.portFlags, bindToDevice, addr)
- if err == tcpip.ErrPortInUse {
+ if _, ok := err.(*tcpip.ErrPortInUse); ok {
return false, nil
}
return false, err
@@ -2323,23 +2343,23 @@ func (e *endpoint) connect(addr tcpip.FullAddress, handshake bool, run bool) *tc
go e.protocolMainLoop(handshake, nil) // S/R-SAFE: will be drained before save.
}
- return tcpip.ErrConnectStarted
+ return &tcpip.ErrConnectStarted{}
}
// ConnectEndpoint is not supported.
-func (*endpoint) ConnectEndpoint(tcpip.Endpoint) *tcpip.Error {
- return tcpip.ErrInvalidEndpointState
+func (*endpoint) ConnectEndpoint(tcpip.Endpoint) tcpip.Error {
+ return &tcpip.ErrInvalidEndpointState{}
}
// Shutdown closes the read and/or write end of the endpoint connection to its
// peer.
-func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
+func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
e.LockUser()
defer e.UnlockUser()
return e.shutdownLocked(flags)
}
-func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) *tcpip.Error {
+func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) tcpip.Error {
e.shutdownFlags |= flags
switch {
case e.EndpointState().connected():
@@ -2354,7 +2374,7 @@ func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) *tcpip.Error {
// If we're fully closed and we have unread data we need to abort
// the connection with a RST.
if e.shutdownFlags&tcpip.ShutdownWrite != 0 && rcvBufUsed > 0 {
- e.resetConnectionLocked(tcpip.ErrConnectionAborted)
+ e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
// Wake up worker to terminate loop.
e.notifyProtocolGoroutine(notifyTickleWorker)
return nil
@@ -2368,7 +2388,7 @@ func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) *tcpip.Error {
// Already closed.
e.sndBufMu.Unlock()
if e.EndpointState() == StateTimeWait {
- return tcpip.ErrNotConnected
+ return &tcpip.ErrNotConnected{}
}
return nil
}
@@ -2401,22 +2421,24 @@ func (e *endpoint) shutdownLocked(flags tcpip.ShutdownFlags) *tcpip.Error {
}
return nil
default:
- return tcpip.ErrNotConnected
+ return &tcpip.ErrNotConnected{}
}
}
// Listen puts the endpoint in "listen" mode, which allows it to accept
// new connections.
-func (e *endpoint) Listen(backlog int) *tcpip.Error {
+func (e *endpoint) Listen(backlog int) tcpip.Error {
err := e.listen(backlog)
- if err != nil && !err.IgnoreStats() {
- e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
- e.stats.FailedConnectionAttempts.Increment()
+ if err != nil {
+ if !err.IgnoreStats() {
+ e.stack.Stats().TCP.FailedConnectionAttempts.Increment()
+ e.stats.FailedConnectionAttempts.Increment()
+ }
}
return err
}
-func (e *endpoint) listen(backlog int) *tcpip.Error {
+func (e *endpoint) listen(backlog int) tcpip.Error {
e.LockUser()
defer e.UnlockUser()
@@ -2434,7 +2456,7 @@ func (e *endpoint) listen(backlog int) *tcpip.Error {
// Adjust the size of the channel iff we can fix
// existing pending connections into the new one.
if len(e.acceptedChan) > backlog {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
if cap(e.acceptedChan) == backlog {
return nil
@@ -2466,7 +2488,7 @@ func (e *endpoint) listen(backlog int) *tcpip.Error {
// Endpoint must be bound before it can transition to listen mode.
if e.EndpointState() != StateBound {
e.stats.ReadErrors.InvalidEndpointState.Increment()
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
// Register the endpoint.
@@ -2506,7 +2528,7 @@ func (e *endpoint) startAcceptedLoop() {
// to an endpoint previously set to listen mode.
//
// addr if not-nil will contain the peer address of the returned endpoint.
-func (e *endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
+func (e *endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
@@ -2515,7 +2537,7 @@ func (e *endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.
e.rcvListMu.Unlock()
// Endpoint must be in listen state before it can accept connections.
if rcvClosed || e.EndpointState() != StateListen {
- return nil, nil, tcpip.ErrInvalidEndpointState
+ return nil, nil, &tcpip.ErrInvalidEndpointState{}
}
// Get the new accepted endpoint.
@@ -2526,7 +2548,7 @@ func (e *endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.
case n = <-e.acceptedChan:
e.acceptCond.Signal()
default:
- return nil, nil, tcpip.ErrWouldBlock
+ return nil, nil, &tcpip.ErrWouldBlock{}
}
if peerAddr != nil {
*peerAddr = n.getRemoteAddress()
@@ -2535,19 +2557,19 @@ func (e *endpoint) Accept(peerAddr *tcpip.FullAddress) (tcpip.Endpoint, *waiter.
}
// Bind binds the endpoint to a specific local port and optionally address.
-func (e *endpoint) Bind(addr tcpip.FullAddress) (err *tcpip.Error) {
+func (e *endpoint) Bind(addr tcpip.FullAddress) (err tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
return e.bindLocked(addr)
}
-func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
+func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err tcpip.Error) {
// Don't allow binding once endpoint is not in the initial state
// anymore. This is because once the endpoint goes into a connected or
// listen state, it is already bound.
if e.EndpointState() != StateInitial {
- return tcpip.ErrAlreadyBound
+ return &tcpip.ErrAlreadyBound{}
}
e.BindAddr = addr.Addr
@@ -2575,7 +2597,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
if len(addr.Addr) != 0 {
nic = e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr)
if nic == 0 {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
e.ID.LocalAddress = addr.Addr
}
@@ -2616,7 +2638,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) (err *tcpip.Error) {
}
// GetLocalAddress returns the address to which the endpoint is bound.
-func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
@@ -2628,12 +2650,12 @@ func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
}
// GetRemoteAddress returns the address to which the endpoint is connected.
-func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
e.LockUser()
defer e.UnlockUser()
if !e.EndpointState().connected() {
- return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
}
return e.getRemoteAddress(), nil
@@ -2665,7 +2687,7 @@ func (e *endpoint) enqueueSegment(s *segment) bool {
return true
}
-func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, extra uint32, pkt *stack.PacketBuffer) {
+func (e *endpoint) onICMPError(err tcpip.Error, transErr stack.TransportError, pkt *stack.PacketBuffer) {
// Update last error first.
e.lastErrorMu.Lock()
e.lastError = err
@@ -2674,11 +2696,8 @@ func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, ext
// Update the error queue if IP_RECVERR is enabled.
if e.SocketOptions().GetRecvError() {
e.SocketOptions().QueueErr(&tcpip.SockError{
- Err: err,
- ErrOrigin: header.ICMPOriginFromNetProto(pkt.NetworkProtocolNumber),
- ErrType: errType,
- ErrCode: errCode,
- ErrInfo: extra,
+ Err: err,
+ Cause: transErr,
// Linux passes the payload with the TCP header. We don't know if the TCP
// header even exists, it may not for fragmented packets.
Payload: pkt.Data.ToView(),
@@ -2700,27 +2719,26 @@ func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, ext
e.notifyProtocolGoroutine(notifyError)
}
-// HandleControlPacket implements stack.TransportEndpoint.HandleControlPacket.
-func (e *endpoint) HandleControlPacket(typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
- switch typ {
- case stack.ControlPacketTooBig:
+// HandleError implements stack.TransportEndpoint.
+func (e *endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
+ handlePacketTooBig := func(mtu uint32) {
e.sndBufMu.Lock()
e.packetTooBigCount++
- if v := int(extra); v < e.sndMTU {
+ if v := int(mtu); v < e.sndMTU {
e.sndMTU = v
}
e.sndBufMu.Unlock()
-
e.notifyProtocolGoroutine(notifyMTUChanged)
+ }
- case stack.ControlNoRoute:
- e.onICMPError(tcpip.ErrNoRoute, byte(header.ICMPv4DstUnreachable), byte(header.ICMPv4HostUnreachable), extra, pkt)
-
- case stack.ControlAddressUnreachable:
- e.onICMPError(tcpip.ErrNoRoute, byte(header.ICMPv6DstUnreachable), byte(header.ICMPv6AddressUnreachable), extra, pkt)
-
- case stack.ControlNetworkUnreachable:
- e.onICMPError(tcpip.ErrNetworkUnreachable, byte(header.ICMPv6DstUnreachable), byte(header.ICMPv6NetworkUnreachable), extra, pkt)
+ // TODO(gvisor.dev/issues/5270): Handle all transport errors.
+ switch transErr.Kind() {
+ case stack.PacketTooBigTransportError:
+ handlePacketTooBig(transErr.Info())
+ case stack.DestinationHostUnreachableTransportError:
+ e.onICMPError(&tcpip.ErrNoRoute{}, transErr, pkt)
+ case stack.DestinationNetworkUnreachableTransportError:
+ e.onICMPError(&tcpip.ErrNetworkUnreachable{}, transErr, pkt)
}
}
@@ -3013,12 +3031,16 @@ func (e *endpoint) completeState() stack.TCPEndpointState {
rc := &e.snd.rc
s.Sender.RACKState = stack.TCPRACKState{
- XmitTime: rc.xmitTime,
- EndSequence: rc.endSequence,
- FACK: rc.fack,
- RTT: rc.rtt,
- Reord: rc.reorderSeen,
- DSACKSeen: rc.dsackSeen,
+ XmitTime: rc.xmitTime,
+ EndSequence: rc.endSequence,
+ FACK: rc.fack,
+ RTT: rc.rtt,
+ Reord: rc.reorderSeen,
+ DSACKSeen: rc.dsackSeen,
+ ReoWnd: rc.reoWnd,
+ ReoWndIncr: rc.reoWndIncr,
+ ReoWndPersist: rc.reoWndPersist,
+ RTTSeq: rc.rttSeq,
}
return s
}
@@ -3107,3 +3129,19 @@ func GetTCPSendBufferLimits(s tcpip.StackHandler) tcpip.SendBufferSizeOption {
Max: ss.Max,
}
}
+
+// allowOutOfWindowAck returns true if an out-of-window ACK can be sent now.
+func (e *endpoint) allowOutOfWindowAck() bool {
+ var limit stack.TCPInvalidRateLimitOption
+ if err := e.stack.Option(&limit); err != nil {
+ panic(fmt.Sprintf("e.stack.Option(%+v) failed with error: %s", limit, err))
+ }
+
+ now := time.Now()
+ if now.Sub(e.lastOutOfWindowAckTime) < time.Duration(limit) {
+ return false
+ }
+
+ e.lastOutOfWindowAckTime = now
+ return true
+}
diff --git a/pkg/tcpip/transport/tcp/endpoint_state.go b/pkg/tcpip/transport/tcp/endpoint_state.go
index 4a01c81b4..e4368026f 100644
--- a/pkg/tcpip/transport/tcp/endpoint_state.go
+++ b/pkg/tcpip/transport/tcp/endpoint_state.go
@@ -59,7 +59,7 @@ func (e *endpoint) beforeSave() {
Err: fmt.Errorf("endpoint cannot be saved in connected state: local %s:%d, remote %s:%d", e.ID.LocalAddress, e.ID.LocalPort, e.ID.RemoteAddress, e.ID.RemotePort),
})
}
- e.resetConnectionLocked(tcpip.ErrConnectionAborted)
+ e.resetConnectionLocked(&tcpip.ErrConnectionAborted{})
e.mu.Unlock()
e.Close()
e.mu.Lock()
@@ -232,7 +232,8 @@ func (e *endpoint) Resume(s *stack.Stack) {
// Reset the scoreboard to reinitialize the sack information as
// we do not restore SACK information.
e.scoreboard.Reset()
- if err := e.connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.ID.RemotePort}, false, e.workerRunning); err != tcpip.ErrConnectStarted {
+ err := e.connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.ID.RemotePort}, false, e.workerRunning)
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
panic("endpoint connecting failed: " + err.String())
}
e.mu.Lock()
@@ -269,7 +270,8 @@ func (e *endpoint) Resume(s *stack.Stack) {
connectedLoading.Wait()
listenLoading.Wait()
bind()
- if err := e.Connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.ID.RemotePort}); err != tcpip.ErrConnectStarted {
+ err := e.Connect(tcpip.FullAddress{NIC: e.boundNICID, Addr: e.connectingAddress, Port: e.ID.RemotePort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
panic("endpoint connecting failed: " + err.String())
}
connectingLoading.Done()
@@ -296,24 +298,6 @@ func (e *endpoint) Resume(s *stack.Stack) {
}
}
-// saveLastError is invoked by stateify.
-func (e *endpoint) saveLastError() string {
- if e.lastError == nil {
- return ""
- }
-
- return e.lastError.String()
-}
-
-// loadLastError is invoked by stateify.
-func (e *endpoint) loadLastError(s string) {
- if s == "" {
- return
- }
-
- e.lastError = tcpip.StringToError(s)
-}
-
// saveRecentTSTime is invoked by stateify.
func (e *endpoint) saveRecentTSTime() unixTime {
return unixTime{e.recentTSTime.Unix(), e.recentTSTime.UnixNano()}
@@ -324,22 +308,14 @@ func (e *endpoint) loadRecentTSTime(unix unixTime) {
e.recentTSTime = time.Unix(unix.second, unix.nano)
}
-// saveHardError is invoked by stateify.
-func (e *endpoint) saveHardError() string {
- if e.hardError == nil {
- return ""
- }
-
- return e.hardError.String()
+// saveLastOutOfWindowAckTime is invoked by stateify.
+func (e *endpoint) saveLastOutOfWindowAckTime() unixTime {
+ return unixTime{e.lastOutOfWindowAckTime.Unix(), e.lastOutOfWindowAckTime.UnixNano()}
}
-// loadHardError is invoked by stateify.
-func (e *endpoint) loadHardError(s string) {
- if s == "" {
- return
- }
-
- e.hardError = tcpip.StringToError(s)
+// loadLastOutOfWindowAckTime is invoked by stateify.
+func (e *endpoint) loadLastOutOfWindowAckTime(unix unixTime) {
+ e.lastOutOfWindowAckTime = time.Unix(unix.second, unix.nano)
}
// saveMeasureTime is invoked by stateify.
diff --git a/pkg/tcpip/transport/tcp/forwarder.go b/pkg/tcpip/transport/tcp/forwarder.go
index 596178625..2f9fe7ee0 100644
--- a/pkg/tcpip/transport/tcp/forwarder.go
+++ b/pkg/tcpip/transport/tcp/forwarder.go
@@ -143,12 +143,12 @@ func (r *ForwarderRequest) Complete(sendReset bool) {
// CreateEndpoint creates a TCP endpoint for the connection request, performing
// the 3-way handshake in the process.
-func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.segment == nil {
- return nil, tcpip.ErrInvalidEndpointState
+ return nil, &tcpip.ErrInvalidEndpointState{}
}
f := r.forwarder
diff --git a/pkg/tcpip/transport/tcp/protocol.go b/pkg/tcpip/transport/tcp/protocol.go
index 1720370c9..04012cd40 100644
--- a/pkg/tcpip/transport/tcp/protocol.go
+++ b/pkg/tcpip/transport/tcp/protocol.go
@@ -161,13 +161,13 @@ func (*protocol) Number() tcpip.TransportProtocolNumber {
}
// NewEndpoint creates a new tcp endpoint.
-func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return newEndpoint(p.stack, netProto, waiterQueue), nil
}
// NewRawEndpoint creates a new raw TCP endpoint. Raw TCP sockets are currently
// unsupported. It implements stack.TransportProtocol.NewRawEndpoint.
-func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return raw.NewEndpoint(p.stack, netProto, header.TCPProtocolNumber, waiterQueue)
}
@@ -178,7 +178,7 @@ func (*protocol) MinimumPacketSize() int {
// ParsePorts returns the source and destination ports stored in the given tcp
// packet.
-func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
+func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err tcpip.Error) {
h := header.TCP(v)
return h.SourcePort(), h.DestinationPort(), nil
}
@@ -216,7 +216,7 @@ func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID,
// replyWithReset replies to the given segment with a reset segment.
//
// If the passed TTL is 0, then the route's default TTL will be used.
-func replyWithReset(stack *stack.Stack, s *segment, tos, ttl uint8) *tcpip.Error {
+func replyWithReset(stack *stack.Stack, s *segment, tos, ttl uint8) tcpip.Error {
route, err := stack.FindRoute(s.nicID, s.dstAddr, s.srcAddr, s.netProto, false /* multicastLoop */)
if err != nil {
return err
@@ -261,7 +261,7 @@ func replyWithReset(stack *stack.Stack, s *segment, tos, ttl uint8) *tcpip.Error
}
// SetOption implements stack.TransportProtocol.SetOption.
-func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpip.Error {
+func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) tcpip.Error {
switch v := option.(type) {
case *tcpip.TCPSACKEnabled:
p.mu.Lock()
@@ -283,7 +283,7 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
case *tcpip.TCPSendBufferSizeRangeOption:
if v.Min <= 0 || v.Default < v.Min || v.Default > v.Max {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
p.mu.Lock()
p.sendBufferSize = *v
@@ -292,7 +292,7 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
case *tcpip.TCPReceiveBufferSizeRangeOption:
if v.Min <= 0 || v.Default < v.Min || v.Default > v.Max {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
p.mu.Lock()
p.recvBufferSize = *v
@@ -310,7 +310,7 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
}
// linux returns ENOENT when an invalid congestion control
// is specified.
- return tcpip.ErrNoSuchFile
+ return &tcpip.ErrNoSuchFile{}
case *tcpip.TCPModerateReceiveBufferOption:
p.mu.Lock()
@@ -340,7 +340,7 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
case *tcpip.TCPTimeWaitReuseOption:
if *v < tcpip.TCPTimeWaitReuseDisabled || *v > tcpip.TCPTimeWaitReuseLoopbackOnly {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
p.mu.Lock()
p.timeWaitReuse = *v
@@ -381,7 +381,7 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
case *tcpip.TCPSynRetriesOption:
if *v < 1 || *v > 255 {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
p.mu.Lock()
p.synRetries = uint8(*v)
@@ -389,12 +389,12 @@ func (p *protocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpi
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
// Option implements stack.TransportProtocol.Option.
-func (p *protocol) Option(option tcpip.GettableTransportProtocolOption) *tcpip.Error {
+func (p *protocol) Option(option tcpip.GettableTransportProtocolOption) tcpip.Error {
switch v := option.(type) {
case *tcpip.TCPSACKEnabled:
p.mu.RLock()
@@ -493,7 +493,7 @@ func (p *protocol) Option(option tcpip.GettableTransportProtocolOption) *tcpip.E
return nil
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
}
diff --git a/pkg/tcpip/transport/tcp/rack.go b/pkg/tcpip/transport/tcp/rack.go
index 307bacca5..e862f159e 100644
--- a/pkg/tcpip/transport/tcp/rack.go
+++ b/pkg/tcpip/transport/tcp/rack.go
@@ -22,12 +22,21 @@ import (
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
)
-// wcDelayedACKTimeout is the recommended maximum delayed ACK timer value as
-// defined in https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.
-// It stands for worst case delayed ACK timer (WCDelAckT). When FlightSize is
-// 1, PTO is inflated by WCDelAckT time to compensate for a potential long
-// delayed ACK timer at the receiver.
-const wcDelayedACKTimeout = 200 * time.Millisecond
+const (
+ // wcDelayedACKTimeout is the recommended maximum delayed ACK timer
+ // value as defined in the RFC. It stands for worst case delayed ACK
+ // timer (WCDelAckT). When FlightSize is 1, PTO is inflated by
+ // WCDelAckT time to compensate for a potential long delayed ACK timer
+ // at the receiver.
+ // See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.
+ wcDelayedACKTimeout = 200 * time.Millisecond
+
+ // tcpRACKRecoveryThreshold is the number of loss recoveries for which
+ // the reorder window is inflated and after that the reorder window is
+ // reset to its initial value of minRTT/4.
+ // See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2.
+ tcpRACKRecoveryThreshold = 16
+)
// RACK is a loss detection algorithm used in TCP to detect packet loss and
// reordering using transmission timestamp of the packets instead of packet or
@@ -44,6 +53,11 @@ type rackControl struct {
// endSequence is the ending TCP sequence number of rackControl.seg.
endSequence seqnum.Value
+ // exitedRecovery indicates if the connection is exiting loss recovery.
+ // This flag is set if the sender is leaving the recovery after
+ // receiving an ACK and is reset during updating of reorder window.
+ exitedRecovery bool
+
// fack is the highest selectively or cumulatively acknowledged
// sequence.
fack seqnum.Value
@@ -51,15 +65,30 @@ type rackControl struct {
// minRTT is the estimated minimum RTT of the connection.
minRTT time.Duration
+ // reorderSeen indicates if reordering has been detected on this
+ // connection.
+ reorderSeen bool
+
+ // reoWnd is the reordering window time used for recording packet
+ // transmission times. It is used to defer the moment at which RACK
+ // marks a packet lost.
+ reoWnd time.Duration
+
+ // reoWndIncr is the multiplier applied to adjust reorder window.
+ reoWndIncr uint8
+
+ // reoWndPersist is the number of loss recoveries before resetting
+ // reorder window.
+ reoWndPersist int8
+
// rtt is the RTT of the most recently delivered packet on the
// connection (either cumulatively acknowledged or selectively
// acknowledged) that was not marked invalid as a possible spurious
// retransmission.
rtt time.Duration
- // reorderSeen indicates if reordering has been detected on this
- // connection.
- reorderSeen bool
+ // rttSeq is the SND.NXT when rtt is updated.
+ rttSeq seqnum.Value
// xmitTime is the latest transmission timestamp of rackControl.seg.
xmitTime time.Time `state:".(unixTime)"`
@@ -75,29 +104,36 @@ type rackControl struct {
// tlpHighRxt the value of sender.sndNxt at the time of sending
// a TLP retransmission.
tlpHighRxt seqnum.Value
+
+ // snd is a reference to the sender.
+ snd *sender
}
// init initializes RACK specific fields.
-func (rc *rackControl) init() {
+func (rc *rackControl) init(snd *sender, iss seqnum.Value) {
+ rc.fack = iss
+ rc.reoWndIncr = 1
+ rc.snd = snd
rc.probeTimer.init(&rc.probeWaker)
}
// update will update the RACK related fields when an ACK has been received.
-// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
-func (rc *rackControl) update(seg *segment, ackSeg *segment, offset uint32) {
+// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-09#section-6.2
+func (rc *rackControl) update(seg *segment, ackSeg *segment) {
rtt := time.Now().Sub(seg.xmitTime)
+ tsOffset := rc.snd.ep.tsOffset
// If the ACK is for a retransmitted packet, do not update if it is a
// spurious inference which is determined by below checks:
- // 1. When Timestamping option is available, if the TSVal is less than the
- // transmit time of the most recent retransmitted packet.
+ // 1. When Timestamping option is available, if the TSVal is less than
+ // the transmit time of the most recent retransmitted packet.
// 2. When RTT calculated for the packet is less than the smoothed RTT
// for the connection.
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
// step 2
if seg.xmitCount > 1 {
if ackSeg.parsedOptions.TS && ackSeg.parsedOptions.TSEcr != 0 {
- if ackSeg.parsedOptions.TSEcr < tcpTimeStamp(seg.xmitTime, offset) {
+ if ackSeg.parsedOptions.TSEcr < tcpTimeStamp(seg.xmitTime, tsOffset) {
return
}
}
@@ -149,9 +185,8 @@ func (rc *rackControl) detectReorder(seg *segment) {
}
}
-// setDSACKSeen updates rack control if duplicate SACK is seen by the connection.
-func (rc *rackControl) setDSACKSeen() {
- rc.dsackSeen = true
+func (rc *rackControl) setDSACKSeen(dsackSeen bool) {
+ rc.dsackSeen = dsackSeen
}
// shouldSchedulePTO dictates whether we should schedule a PTO or not.
@@ -162,7 +197,7 @@ func (s *sender) shouldSchedulePTO() bool {
// The connection supports SACK.
s.ep.sackPermitted &&
// The connection is not in loss recovery.
- (s.state != RTORecovery && s.state != SACKRecovery) &&
+ (s.state != tcpip.RTORecovery && s.state != tcpip.SACKRecovery) &&
// The connection has no SACKed sequences in the SACK scoreboard.
s.ep.scoreboard.Sacked() == 0
}
@@ -193,7 +228,7 @@ func (s *sender) schedulePTO() {
// probeTimerExpired is the same as TLP_send_probe() as defined in
// https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.5.2.
-func (s *sender) probeTimerExpired() *tcpip.Error {
+func (s *sender) probeTimerExpired() tcpip.Error {
if !s.rc.probeTimer.checkExpiration() {
return nil
}
@@ -266,9 +301,88 @@ func (s *sender) detectTLPRecovery(ack seqnum.Value, rcvdSeg *segment) {
// Step 2. Either the original packet or the retransmission (in the
// form of a probe) was lost. Invoke a congestion control response
// equivalent to fast recovery.
- s.cc.HandleNDupAcks()
+ s.cc.HandleLossDetected()
s.enterRecovery()
s.leaveRecovery()
}
}
}
+
+// updateRACKReorderWindow updates the reorder window.
+// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
+// * Step 4: Update RACK reordering window
+// To handle the prevalent small degree of reordering, RACK.reo_wnd serves as
+// an allowance for settling time before marking a packet lost. RACK starts
+// initially with a conservative window of min_RTT/4. If no reordering has
+// been observed RACK uses reo_wnd of zero during loss recovery, in order to
+// retransmit quickly, or when the number of DUPACKs exceeds the classic
+// DUPACKthreshold.
+func (rc *rackControl) updateRACKReorderWindow(ackSeg *segment) {
+ dsackSeen := rc.dsackSeen
+ snd := rc.snd
+
+ // React to DSACK once per round trip.
+ // If SND.UNA < RACK.rtt_seq:
+ // RACK.dsack = false
+ if snd.sndUna.LessThan(rc.rttSeq) {
+ dsackSeen = false
+ }
+
+ // If RACK.dsack:
+ // RACK.reo_wnd_incr += 1
+ // RACK.dsack = false
+ // RACK.rtt_seq = SND.NXT
+ // RACK.reo_wnd_persist = 16
+ if dsackSeen {
+ rc.reoWndIncr++
+ dsackSeen = false
+ rc.rttSeq = snd.sndNxt
+ rc.reoWndPersist = tcpRACKRecoveryThreshold
+ } else if rc.exitedRecovery {
+ // Else if exiting loss recovery:
+ // RACK.reo_wnd_persist -= 1
+ // If RACK.reo_wnd_persist <= 0:
+ // RACK.reo_wnd_incr = 1
+ rc.reoWndPersist--
+ if rc.reoWndPersist <= 0 {
+ rc.reoWndIncr = 1
+ }
+ rc.exitedRecovery = false
+ }
+
+ // Reorder window is zero during loss recovery, or when the number of
+ // DUPACKs exceeds the classic DUPACKthreshold.
+ // If RACK.reord is FALSE:
+ // If in loss recovery: (If in fast or timeout recovery)
+ // RACK.reo_wnd = 0
+ // Return
+ // Else if RACK.pkts_sacked >= RACK.dupthresh:
+ // RACK.reo_wnd = 0
+ // return
+ if !rc.reorderSeen {
+ if snd.state == tcpip.RTORecovery || snd.state == tcpip.SACKRecovery {
+ rc.reoWnd = 0
+ return
+ }
+
+ if snd.sackedOut >= nDupAckThreshold {
+ rc.reoWnd = 0
+ return
+ }
+ }
+
+ // Calculate reorder window.
+ // RACK.reo_wnd = RACK.min_RTT / 4 * RACK.reo_wnd_incr
+ // RACK.reo_wnd = min(RACK.reo_wnd, SRTT)
+ snd.rtt.Lock()
+ srtt := snd.rtt.srtt
+ snd.rtt.Unlock()
+ rc.reoWnd = time.Duration((int64(rc.minRTT) / 4) * int64(rc.reoWndIncr))
+ if srtt < rc.reoWnd {
+ rc.reoWnd = srtt
+ }
+}
+
+func (rc *rackControl) exitRecovery() {
+ rc.exitedRecovery = true
+}
diff --git a/pkg/tcpip/transport/tcp/rcv.go b/pkg/tcpip/transport/tcp/rcv.go
index 405a6dce7..a5c82b8fa 100644
--- a/pkg/tcpip/transport/tcp/rcv.go
+++ b/pkg/tcpip/transport/tcp/rcv.go
@@ -347,7 +347,7 @@ func (r *receiver) updateRTT() {
r.ep.rcvListMu.Unlock()
}
-func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, closed bool) (drop bool, err *tcpip.Error) {
+func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, closed bool) (drop bool, err tcpip.Error) {
r.ep.rcvListMu.Lock()
rcvClosed := r.ep.rcvClosed || r.closed
r.ep.rcvListMu.Unlock()
@@ -385,7 +385,7 @@ func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, clo
// fails, we ignore the packet:
// https://github.com/torvalds/linux/blob/v5.8/net/ipv4/tcp_input.c#L5591
if r.ep.snd.sndNxt.LessThan(s.ackNumber) {
- r.ep.snd.sendAck()
+ r.ep.snd.maybeSendOutOfWindowAck(s)
return true, nil
}
@@ -395,7 +395,7 @@ func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, clo
// trigger a RST.
endDataSeq := s.sequenceNumber.Add(seqnum.Size(s.data.Size()))
if state != StateCloseWait && rcvClosed && r.rcvNxt.LessThan(endDataSeq) {
- return true, tcpip.ErrConnectionAborted
+ return true, &tcpip.ErrConnectionAborted{}
}
if state == StateFinWait1 {
break
@@ -424,7 +424,7 @@ func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, clo
// the last actual data octet in a segment in
// which it occurs.
if closed && (!s.flagIsSet(header.TCPFlagFin) || s.sequenceNumber.Add(s.logicalLen()) != r.rcvNxt+1) {
- return true, tcpip.ErrConnectionAborted
+ return true, &tcpip.ErrConnectionAborted{}
}
}
@@ -443,7 +443,7 @@ func (r *receiver) handleRcvdSegmentClosing(s *segment, state EndpointState, clo
// handleRcvdSegment handles TCP segments directed at the connection managed by
// r as they arrive. It is called by the protocol main loop.
-func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err *tcpip.Error) {
+func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err tcpip.Error) {
state := r.ep.EndpointState()
closed := r.ep.closed
@@ -454,7 +454,7 @@ func (r *receiver) handleRcvdSegment(s *segment) (drop bool, err *tcpip.Error) {
// send an ACK and stop further processing of the segment.
// This is according to RFC 793, page 68.
if !r.acceptable(segSeq, segLen) {
- r.ep.snd.sendAck()
+ r.ep.snd.maybeSendOutOfWindowAck(s)
return true, nil
}
diff --git a/pkg/tcpip/transport/tcp/reno.go b/pkg/tcpip/transport/tcp/reno.go
index f83ebc717..ff39780a5 100644
--- a/pkg/tcpip/transport/tcp/reno.go
+++ b/pkg/tcpip/transport/tcp/reno.go
@@ -79,10 +79,10 @@ func (r *renoState) Update(packetsAcked int) {
r.updateCongestionAvoidance(packetsAcked)
}
-// HandleNDupAcks implements congestionControl.HandleNDupAcks.
-func (r *renoState) HandleNDupAcks() {
- // A retransmit was triggered due to nDupAckThreshold
- // being hit. Reduce our slow start threshold.
+// HandleLossDetected implements congestionControl.HandleLossDetected.
+func (r *renoState) HandleLossDetected() {
+ // A retransmit was triggered due to nDupAckThreshold or when RACK
+ // detected loss. Reduce our slow start threshold.
r.reduceSlowStartThreshold()
}
diff --git a/pkg/tcpip/transport/tcp/snd.go b/pkg/tcpip/transport/tcp/snd.go
index 079d90848..463a259b7 100644
--- a/pkg/tcpip/transport/tcp/snd.go
+++ b/pkg/tcpip/transport/tcp/snd.go
@@ -48,34 +48,13 @@ const (
MaxRetries = 15
)
-// ccState indicates the current congestion control state for this sender.
-type ccState int
-
-const (
- // Open indicates that the sender is receiving acks in order and
- // no loss or dupACK's etc have been detected.
- Open ccState = iota
- // RTORecovery indicates that an RTO has occurred and the sender
- // has entered an RTO based recovery phase.
- RTORecovery
- // FastRecovery indicates that the sender has entered FastRecovery
- // based on receiving nDupAck's. This state is entered only when
- // SACK is not in use.
- FastRecovery
- // SACKRecovery indicates that the sender has entered SACK based
- // recovery.
- SACKRecovery
- // Disorder indicates the sender either received some SACK blocks
- // or dupACK's.
- Disorder
-)
-
// congestionControl is an interface that must be implemented by any supported
// congestion control algorithm.
type congestionControl interface {
- // HandleNDupAcks is invoked when sender.dupAckCount >= nDupAckThreshold
- // just before entering fast retransmit.
- HandleNDupAcks()
+ // HandleLossDetected is invoked when the loss is detected by RACK or
+ // sender.dupAckCount >= nDupAckThreshold just before entering fast
+ // retransmit.
+ HandleLossDetected()
// HandleRTOExpired is invoked when the retransmit timer expires.
HandleRTOExpired()
@@ -204,7 +183,7 @@ type sender struct {
maxSentAck seqnum.Value
// state is the current state of congestion control for this endpoint.
- state ccState
+ state tcpip.CongestionControlState
// cc is the congestion control algorithm in use for this sender.
cc congestionControl
@@ -280,14 +259,9 @@ func newSender(ep *endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint
highRxt: iss,
rescueRxt: iss,
},
- rc: rackControl{
- fack: iss,
- },
gso: ep.gso != nil,
}
- s.rc.init()
-
if s.gso {
s.ep.gso.MSS = uint16(maxPayloadSize)
}
@@ -295,6 +269,7 @@ func newSender(ep *endpoint, iss, irs seqnum.Value, sndWnd seqnum.Size, mss uint
s.cc = s.initCongestionControl(ep.cc)
s.lr = s.initLossRecovery()
+ s.rc.init(s, iss)
// A negative sndWndScale means that no scaling is in use, otherwise we
// store the scaling value.
@@ -593,7 +568,7 @@ func (s *sender) retransmitTimerExpired() bool {
s.leaveRecovery()
}
- s.state = RTORecovery
+ s.state = tcpip.RTORecovery
s.cc.HandleRTOExpired()
// Mark the next segment to be sent as the first unacknowledged one and
@@ -1018,7 +993,7 @@ func (s *sender) sendData() {
// "A TCP SHOULD set cwnd to no more than RW before beginning
// transmission if the TCP has not sent data in the interval exceeding
// the retrasmission timeout."
- if !s.fr.active && s.state != RTORecovery && time.Now().Sub(s.lastSendTime) > s.rto {
+ if !s.fr.active && s.state != tcpip.RTORecovery && time.Now().Sub(s.lastSendTime) > s.rto {
if s.sndCwnd > InitialCwnd {
s.sndCwnd = InitialCwnd
}
@@ -1062,14 +1037,14 @@ func (s *sender) enterRecovery() {
s.fr.highRxt = s.sndUna
s.fr.rescueRxt = s.sndUna
if s.ep.sackPermitted {
- s.state = SACKRecovery
+ s.state = tcpip.SACKRecovery
s.ep.stack.Stats().TCP.SACKRecovery.Increment()
// Set TLPRxtOut to false according to
// https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.6.1.
s.rc.tlpRxtOut = false
return
}
- s.state = FastRecovery
+ s.state = tcpip.FastRecovery
s.ep.stack.Stats().TCP.FastRecovery.Increment()
}
@@ -1080,7 +1055,6 @@ func (s *sender) leaveRecovery() {
// Deflate cwnd. It had been artificially inflated when new dups arrived.
s.sndCwnd = s.sndSsthresh
-
s.cc.PostRecovery()
}
@@ -1166,7 +1140,7 @@ func (s *sender) detectLoss(seg *segment) (fastRetransmit bool) {
s.fr.highRxt = s.sndUna - 1
// Do run SetPipe() to calculate the outstanding segments.
s.SetPipe()
- s.state = Disorder
+ s.state = tcpip.Disorder
return false
}
@@ -1179,7 +1153,7 @@ func (s *sender) detectLoss(seg *segment) (fastRetransmit bool) {
s.dupAckCount = 0
return false
}
- s.cc.HandleNDupAcks()
+ s.cc.HandleLossDetected()
s.enterRecovery()
s.dupAckCount = 0
return true
@@ -1217,11 +1191,13 @@ func (s *sender) isDupAck(seg *segment) bool {
// See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
// steps 2 and 3.
func (s *sender) walkSACK(rcvdSeg *segment) {
+ s.rc.setDSACKSeen(false)
+
// Look for DSACK block.
idx := 0
n := len(rcvdSeg.parsedOptions.SACKBlocks)
if checkDSACK(rcvdSeg) {
- s.rc.setDSACKSeen()
+ s.rc.setDSACKSeen(true)
idx = 1
n--
}
@@ -1242,7 +1218,7 @@ func (s *sender) walkSACK(rcvdSeg *segment) {
for _, sb := range sackBlocks {
for seg != nil && seg.sequenceNumber.LessThan(sb.End) && seg.xmitCount != 0 {
if sb.Start.LessThanEq(seg.sequenceNumber) && !seg.acked {
- s.rc.update(seg, rcvdSeg, s.ep.tsOffset)
+ s.rc.update(seg, rcvdSeg)
s.rc.detectReorder(seg)
seg.acked = true
s.sackedOut += s.pCount(seg, s.maxPayloadSize)
@@ -1412,6 +1388,17 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
acked := s.sndUna.Size(ack)
s.sndUna = ack
+ // The remote ACK-ing at least 1 byte is an indication that we have a
+ // full-duplex connection to the remote as the only way we will receive an
+ // ACK is if the remote received data that we previously sent.
+ //
+ // As of writing, linux seems to only confirm a route as reachable when
+ // forward progress is made which is indicated by an ACK that removes data
+ // from the retransmit queue.
+ if acked > 0 {
+ s.ep.route.ConfirmReachable()
+ }
+
ackLeft := acked
originalOutstanding := s.outstanding
for ackLeft > 0 {
@@ -1435,7 +1422,7 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
// Update the RACK fields if SACK is enabled.
if s.ep.sackPermitted && !seg.acked {
- s.rc.update(seg, rcvdSeg, s.ep.tsOffset)
+ s.rc.update(seg, rcvdSeg)
s.rc.detectReorder(seg)
}
@@ -1464,7 +1451,11 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
if !s.fr.active {
s.cc.Update(originalOutstanding - s.outstanding)
if s.fr.last.LessThan(s.sndUna) {
- s.state = Open
+ s.state = tcpip.Open
+ // Update RACK when we are exiting fast or RTO
+ // recovery as described in the RFC
+ // draft-ietf-tcpm-rack-08 Section-7.2 Step 4.
+ s.rc.exitRecovery()
}
}
@@ -1488,6 +1479,12 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
}
}
+ // Update RACK reorder window.
+ // See: https://tools.ietf.org/html/draft-ietf-tcpm-rack-08#section-7.2
+ // * Upon receiving an ACK:
+ // * Step 4: Update RACK reordering window
+ s.rc.updateRACKReorderWindow(rcvdSeg)
+
// Now that we've popped all acknowledged data from the retransmit
// queue, retransmit if needed.
if s.fr.active {
@@ -1508,7 +1505,7 @@ func (s *sender) handleRcvdSegment(rcvdSeg *segment) {
}
// sendSegment sends the specified segment.
-func (s *sender) sendSegment(seg *segment) *tcpip.Error {
+func (s *sender) sendSegment(seg *segment) tcpip.Error {
if seg.xmitCount > 0 {
s.ep.stack.Stats().TCP.Retransmits.Increment()
s.ep.stats.SendErrors.Retransmits.Increment()
@@ -1539,7 +1536,7 @@ func (s *sender) sendSegment(seg *segment) *tcpip.Error {
// sendSegmentFromView sends a new segment containing the given payload, flags
// and sequence number.
-func (s *sender) sendSegmentFromView(data buffer.VectorisedView, flags byte, seq seqnum.Value) *tcpip.Error {
+func (s *sender) sendSegmentFromView(data buffer.VectorisedView, flags byte, seq seqnum.Value) tcpip.Error {
s.lastSendTime = time.Now()
if seq == s.rttMeasureSeqNum {
s.rttMeasureTime = s.lastSendTime
@@ -1552,3 +1549,13 @@ func (s *sender) sendSegmentFromView(data buffer.VectorisedView, flags byte, seq
return s.ep.sendRaw(data, flags, seq, rcvNxt, rcvWnd)
}
+
+// maybeSendOutOfWindowAck sends an ACK if we are not being rate limited
+// currently.
+func (s *sender) maybeSendOutOfWindowAck(seg *segment) {
+ // Data packets are unlikely to be part of an ACK loop. So always send
+ // an ACK for a packet w/ data.
+ if seg.payloadSize() > 0 || s.ep.allowOutOfWindowAck() {
+ s.sendAck()
+ }
+}
diff --git a/pkg/tcpip/transport/tcp/tcp_rack_test.go b/pkg/tcpip/transport/tcp/tcp_rack_test.go
index af915203b..a6a26b705 100644
--- a/pkg/tcpip/transport/tcp/tcp_rack_test.go
+++ b/pkg/tcpip/transport/tcp/tcp_rack_test.go
@@ -16,6 +16,7 @@ package tcp_test
import (
"bytes"
+ "fmt"
"testing"
"time"
@@ -534,3 +535,64 @@ func TestRACKWithInvalidDSACKBlock(t *testing.T) {
// ACK before the test completes.
<-probeDone
}
+
+func addReorderWindowCheckerProbe(c *context.Context, numACK int, probeDone chan error) {
+ var n int
+ c.Stack().AddTCPProbe(func(state stack.TCPEndpointState) {
+ // Validate that RACK detects DSACK.
+ n++
+ if n < numACK {
+ return
+ }
+
+ if state.Sender.RACKState.ReoWnd == 0 || state.Sender.RACKState.ReoWnd > state.Sender.SRTT {
+ probeDone <- fmt.Errorf("got RACKState.ReoWnd: %v, expected it to be greater than 0 and less than %v", state.Sender.RACKState.ReoWnd, state.Sender.SRTT)
+ return
+ }
+
+ if state.Sender.RACKState.ReoWndIncr != 1 {
+ probeDone <- fmt.Errorf("got RACKState.ReoWndIncr: %v, want: 1", state.Sender.RACKState.ReoWndIncr)
+ return
+ }
+
+ if state.Sender.RACKState.ReoWndPersist > 0 {
+ probeDone <- fmt.Errorf("got RACKState.ReoWndPersist: %v, want: greater than 0", state.Sender.RACKState.ReoWndPersist)
+ return
+ }
+ probeDone <- nil
+ })
+}
+
+func TestRACKCheckReorderWindow(t *testing.T) {
+ c := context.New(t, uint32(mtu))
+ defer c.Cleanup()
+
+ probeDone := make(chan error)
+ const ackNumToVerify = 3
+ addReorderWindowCheckerProbe(c, ackNumToVerify, probeDone)
+
+ const numPackets = 7
+ sendAndReceive(t, c, numPackets)
+
+ // Send ACK for #1 packet.
+ bytesRead := maxPayload
+ seq := seqnum.Value(context.TestInitialSequenceNumber).Add(1)
+ c.SendAck(seq, bytesRead)
+
+ // Missing [2-6] packets and SACK #7 packet.
+ seq = seqnum.Value(context.TestInitialSequenceNumber).Add(1)
+ start := c.IRS.Add(1 + seqnum.Size(6*maxPayload))
+ end := start.Add(seqnum.Size(maxPayload))
+ c.SendAckWithSACK(seq, bytesRead, []header.SACKBlock{{start, end}})
+
+ // Received delayed packets [2-6] which indicates there is reordering
+ // in the connection.
+ bytesRead += 6 * maxPayload
+ c.SendAck(seq, bytesRead)
+
+ // Wait for the probe function to finish processing the ACK before the
+ // test completes.
+ if err := <-probeDone; err != nil {
+ t.Fatalf("unexpected values for RACK variables: %v", err)
+ }
+}
diff --git a/pkg/tcpip/transport/tcp/tcp_test.go b/pkg/tcpip/transport/tcp/tcp_test.go
index 87ff2b909..cd3c4a027 100644
--- a/pkg/tcpip/transport/tcp/tcp_test.go
+++ b/pkg/tcpip/transport/tcp/tcp_test.go
@@ -48,7 +48,7 @@ type endpointTester struct {
}
// CheckReadError issues a read to the endpoint and checking for an error.
-func (e *endpointTester) CheckReadError(t *testing.T, want *tcpip.Error) {
+func (e *endpointTester) CheckReadError(t *testing.T, want tcpip.Error) {
t.Helper()
res, got := e.ep.Read(ioutil.Discard, tcpip.ReadOptions{})
if got != want {
@@ -87,7 +87,7 @@ func (e *endpointTester) CheckReadFull(t *testing.T, count int, notifyRead <-cha
}
for w.N != 0 {
_, err := e.ep.Read(&w, tcpip.ReadOptions{})
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for receive to be notified.
select {
case <-notifyRead:
@@ -128,8 +128,11 @@ func TestGiveUpConnect(t *testing.T) {
wq.EventRegister(&waitEntry, waiter.EventHUp)
defer wq.EventUnregister(&waitEntry)
- if err := ep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrConnectStarted {
- t.Fatalf("got ep.Connect(...) = %s, want = %s", err, tcpip.ErrConnectStarted)
+ {
+ err := ep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("got ep.Connect(...) = %v, want = %s", err, &tcpip.ErrConnectStarted{})
+ }
}
// Close the connection, wait for completion.
@@ -140,8 +143,11 @@ func TestGiveUpConnect(t *testing.T) {
// Call Connect again to retreive the handshake failure status
// and stats updates.
- if err := ep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrAborted {
- t.Fatalf("got ep.Connect(...) = %s, want = %s", err, tcpip.ErrAborted)
+ {
+ err := ep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrAborted); !ok {
+ t.Fatalf("got ep.Connect(...) = %v, want = %s", err, &tcpip.ErrAborted{})
+ }
}
if got := c.Stack().Stats().TCP.FailedConnectionAttempts.Value(); got != 1 {
@@ -194,8 +200,11 @@ func TestActiveFailedConnectionAttemptIncrement(t *testing.T) {
c.EP = ep
want := stats.TCP.FailedConnectionAttempts.Value() + 1
- if err := c.EP.Connect(tcpip.FullAddress{NIC: 2, Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrNoRoute {
- t.Errorf("got c.EP.Connect(...) = %s, want = %s", err, tcpip.ErrNoRoute)
+ {
+ err := c.EP.Connect(tcpip.FullAddress{NIC: 2, Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrNoRoute); !ok {
+ t.Errorf("got c.EP.Connect(...) = %v, want = %s", err, &tcpip.ErrNoRoute{})
+ }
}
if got := stats.TCP.FailedConnectionAttempts.Value(); got != want {
@@ -211,7 +220,7 @@ func TestCloseWithoutConnect(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -384,7 +393,7 @@ func TestTCPResetSentForACKWhenNotUsingSynCookies(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -925,8 +934,11 @@ func TestUserSuppliedMSSOnConnect(t *testing.T) {
ws := tcp.FindWndScale(seqnum.Size(rcvBufSize))
connectAddr := tcpip.FullAddress{Addr: ip.connectAddr, Port: context.TestPort}
- if err := c.EP.Connect(connectAddr); err != tcpip.ErrConnectStarted {
- t.Fatalf("Connect(%+v): %s", connectAddr, err)
+ {
+ err := c.EP.Connect(connectAddr)
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("Connect(%+v): %s", connectAddr, err)
+ }
}
// Receive SYN packet with our user supplied MSS.
@@ -1442,7 +1454,8 @@ func TestConnectBindToDevice(t *testing.T) {
c.WQ.EventRegister(&waitEntry, waiter.EventOut)
defer c.WQ.EventUnregister(&waitEntry)
- if err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrConnectStarted {
+ err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
t.Fatalf("unexpected return value from Connect: %s", err)
}
@@ -1502,8 +1515,9 @@ func TestSynSent(t *testing.T) {
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) = %s, want %s", addr, err, tcpip.ErrConnectStarted)
+ err := c.EP.Connect(addr)
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("got Connect(%+v) = %v, want %s", addr, err, &tcpip.ErrConnectStarted{})
}
// Receive SYN packet.
@@ -1548,9 +1562,9 @@ func TestSynSent(t *testing.T) {
ept := endpointTester{c.EP}
if test.reset {
- ept.CheckReadError(t, tcpip.ErrConnectionRefused)
+ ept.CheckReadError(t, &tcpip.ErrConnectionRefused{})
} else {
- ept.CheckReadError(t, tcpip.ErrAborted)
+ ept.CheckReadError(t, &tcpip.ErrAborted{})
}
if got := c.Stack().Stats().TCP.CurrentConnected.Value(); got != 0 {
@@ -1576,7 +1590,7 @@ func TestOutOfOrderReceive(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Send second half of data first, with seqnum 3 ahead of expected.
data := []byte{1, 2, 3, 4, 5, 6}
@@ -1601,7 +1615,7 @@ func TestOutOfOrderReceive(t *testing.T) {
// Wait 200ms and check that no data has been received.
time.Sleep(200 * time.Millisecond)
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Send the first 3 bytes now.
c.SendPacket(data[:3], &context.Headers{
@@ -1640,7 +1654,7 @@ func TestOutOfOrderFlood(t *testing.T) {
c.CreateConnected(789, 30000, rcvBufSz)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Send 100 packets before the actual one that is expected.
data := []byte{1, 2, 3, 4, 5, 6}
@@ -1716,7 +1730,7 @@ func TestRstOnCloseWithUnreadData(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
data := []byte{1, 2, 3}
c.SendPacket(data, &context.Headers{
@@ -1784,7 +1798,7 @@ func TestRstOnCloseWithUnreadDataFinConvertRst(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
data := []byte{1, 2, 3}
c.SendPacket(data, &context.Headers{
@@ -1866,13 +1880,13 @@ func TestShutdownRead(t *testing.T) {
c.CreateConnected(789, 30000, -1 /* epRcvBuf */)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
if err := c.EP.Shutdown(tcpip.ShutdownRead); err != nil {
t.Fatalf("Shutdown failed: %s", err)
}
- ept.CheckReadError(t, tcpip.ErrClosedForReceive)
+ ept.CheckReadError(t, &tcpip.ErrClosedForReceive{})
var want uint64 = 1
if got := c.EP.Stats().(*tcp.Stats).ReadErrors.ReadClosed.Value(); got != want {
t.Fatalf("got EP stats Stats.ReadErrors.ReadClosed got %d want %d", got, want)
@@ -1891,7 +1905,7 @@ func TestFullWindowReceive(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Fill up the window w/ tcp.SegOverheadFactor*rcvBufSz as netstack multiplies
// the provided buffer value by tcp.SegOverheadFactor to calculate the actual
@@ -2052,7 +2066,7 @@ func TestNoWindowShrinking(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Send a 1 byte payload so that we can record the current receive window.
// Send a payload of half the size of rcvBufSize.
@@ -2370,7 +2384,7 @@ func TestScaledWindowAccept(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -2443,7 +2457,7 @@ func TestNonScaledWindowAccept(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -2958,7 +2972,7 @@ func TestSetTTL(t *testing.T) {
c := context.New(t, 65535)
defer c.Cleanup()
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -2968,8 +2982,11 @@ func TestSetTTL(t *testing.T) {
t.Fatalf("SetSockOptInt(TTLOption, %d) failed: %s", wantTTL, err)
}
- if err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrConnectStarted {
- t.Fatalf("unexpected return value from Connect: %s", err)
+ {
+ err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("unexpected return value from Connect: %s", err)
+ }
}
// Receive SYN packet.
@@ -3029,7 +3046,7 @@ func TestPassiveSendMSSLessThanMTU(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -3085,7 +3102,7 @@ func TestSynCookiePassiveSendMSSLessThanMTU(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -3110,9 +3127,9 @@ func TestForwarderSendMSSLessThanMTU(t *testing.T) {
defer c.Cleanup()
s := c.Stack()
- ch := make(chan *tcpip.Error, 1)
+ ch := make(chan tcpip.Error, 1)
f := tcp.NewForwarder(s, 65536, 10, func(r *tcp.ForwarderRequest) {
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = r.CreateEndpoint(&c.WQ)
ch <- err
})
@@ -3141,7 +3158,7 @@ func TestSynOptionsOnActiveConnect(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -3160,8 +3177,11 @@ func TestSynOptionsOnActiveConnect(t *testing.T) {
c.WQ.EventRegister(&we, waiter.EventOut)
defer c.WQ.EventUnregister(&we)
- if err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrConnectStarted {
- t.Fatalf("got c.EP.Connect(...) = %s, want = %s", err, tcpip.ErrConnectStarted)
+ {
+ err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("got c.EP.Connect(...) = %v, want = %s", err, &tcpip.ErrConnectStarted{})
+ }
}
// Receive SYN packet.
@@ -3271,22 +3291,23 @@ func TestReceiveOnResetConnection(t *testing.T) {
loop:
for {
- switch _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{}); err {
- case tcpip.ErrWouldBlock:
+ switch _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{}); err.(type) {
+ case *tcpip.ErrWouldBlock:
select {
case <-ch:
// Expect the state to be StateError and subsequent Reads to fail with HardError.
- if _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{}); err != tcpip.ErrConnectionReset {
- t.Fatalf("got c.EP.Read() = %s, want = %s", err, tcpip.ErrConnectionReset)
+ _, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{})
+ if _, ok := err.(*tcpip.ErrConnectionReset); !ok {
+ t.Fatalf("got c.EP.Read() = %v, want = %s", err, &tcpip.ErrConnectionReset{})
}
break loop
case <-time.After(1 * time.Second):
t.Fatalf("Timed out waiting for reset to arrive")
}
- case tcpip.ErrConnectionReset:
+ case *tcpip.ErrConnectionReset:
break loop
default:
- t.Fatalf("got c.EP.Read(nil) = %s, want = %s", err, tcpip.ErrConnectionReset)
+ t.Fatalf("got c.EP.Read(nil) = %v, want = %s", err, &tcpip.ErrConnectionReset{})
}
}
@@ -3325,8 +3346,9 @@ func TestSendOnResetConnection(t *testing.T) {
// Try to write.
var r bytes.Reader
r.Reset(make([]byte, 10))
- if _, err := c.EP.Write(&r, tcpip.WriteOptions{}); err != tcpip.ErrConnectionReset {
- t.Fatalf("got c.EP.Write(...) = %s, want = %s", err, tcpip.ErrConnectionReset)
+ _, err := c.EP.Write(&r, tcpip.WriteOptions{})
+ if _, ok := err.(*tcpip.ErrConnectionReset); !ok {
+ t.Fatalf("got c.EP.Write(...) = %v, want = %s", err, &tcpip.ErrConnectionReset{})
}
}
@@ -4184,7 +4206,7 @@ func TestReadAfterClosedState(t *testing.T) {
defer c.WQ.EventUnregister(&we)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Shutdown immediately for write, check that we get a FIN.
if err := c.EP.Shutdown(tcpip.ShutdownWrite); err != nil {
@@ -4263,10 +4285,13 @@ func TestReadAfterClosedState(t *testing.T) {
// Now that we drained the queue, check that functions fail with the
// right error code.
- ept.CheckReadError(t, tcpip.ErrClosedForReceive)
+ ept.CheckReadError(t, &tcpip.ErrClosedForReceive{})
var buf bytes.Buffer
- if _, err := c.EP.Read(&buf, tcpip.ReadOptions{Peek: true}); err != tcpip.ErrClosedForReceive {
- t.Fatalf("c.EP.Read(_, {Peek: true}) = %v, %s; want _, %s", res, err, tcpip.ErrClosedForReceive)
+ {
+ _, err := c.EP.Read(&buf, tcpip.ReadOptions{Peek: true})
+ if _, ok := err.(*tcpip.ErrClosedForReceive); !ok {
+ t.Fatalf("c.EP.Read(_, {Peek: true}) = %v, %s; want _, %s", res, err, &tcpip.ErrClosedForReceive{})
+ }
}
}
@@ -4277,7 +4302,7 @@ func TestReusePort(t *testing.T) {
defer c.Cleanup()
// First case, just an endpoint that was bound.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &waiter.Queue{})
if err != nil {
t.Fatalf("NewEndpoint failed; %s", err)
@@ -4307,8 +4332,11 @@ func TestReusePort(t *testing.T) {
if err := c.EP.Bind(tcpip.FullAddress{Port: context.StackPort}); err != nil {
t.Fatalf("Bind failed: %s", err)
}
- if err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrConnectStarted {
- t.Fatalf("got c.EP.Connect(...) = %s, want = %s", err, tcpip.ErrConnectStarted)
+ {
+ err := c.EP.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("got c.EP.Connect(...) = %v, want = %s", err, &tcpip.ErrConnectStarted{})
+ }
}
c.EP.Close()
@@ -4515,11 +4543,11 @@ func TestBindToDeviceOption(t *testing.T) {
testActions := []struct {
name string
setBindToDevice *tcpip.NICID
- setBindToDeviceError *tcpip.Error
+ setBindToDeviceError tcpip.Error
getBindToDevice int32
}{
{"GetDefaultValue", nil, nil, 0},
- {"BindToNonExistent", nicIDPtr(999), tcpip.ErrUnknownDevice, 0},
+ {"BindToNonExistent", nicIDPtr(999), &tcpip.ErrUnknownDevice{}, 0},
{"BindToExistent", nicIDPtr(321), nil, 321},
{"UnbindToDevice", nicIDPtr(0), nil, 0},
}
@@ -4539,7 +4567,7 @@ func TestBindToDeviceOption(t *testing.T) {
}
}
-func makeStack() (*stack.Stack, *tcpip.Error) {
+func makeStack() (*stack.Stack, tcpip.Error) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{
ipv4.NewProtocol,
@@ -4609,8 +4637,11 @@ func TestSelfConnect(t *testing.T) {
wq.EventRegister(&waitEntry, waiter.EventOut)
defer wq.EventUnregister(&waitEntry)
- if err := ep.Connect(tcpip.FullAddress{Addr: context.StackAddr, Port: context.StackPort}); err != tcpip.ErrConnectStarted {
- t.Fatalf("got ep.Connect(...) = %s, want = %s", err, tcpip.ErrConnectStarted)
+ {
+ err := ep.Connect(tcpip.FullAddress{Addr: context.StackAddr, Port: context.StackPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
+ t.Fatalf("got ep.Connect(...) = %v, want = %s", err, &tcpip.ErrConnectStarted{})
+ }
}
<-notifyCh
@@ -4762,9 +4793,9 @@ func TestConnectAvoidsBoundPorts(t *testing.T) {
t.Fatalf("Bind(%d) failed: %s", i, err)
}
}
- want := tcpip.ErrConnectStarted
+ var want tcpip.Error = &tcpip.ErrConnectStarted{}
if collides {
- want = tcpip.ErrNoPortAvailable
+ want = &tcpip.ErrNoPortAvailable{}
}
if err := makeEP(candidateNetwork).Connect(tcpip.FullAddress{Addr: address(t, candidateAddressType, false), Port: 31337}); err != want {
t.Fatalf("got ep.Connect(..) = %s, want = %s", err, want)
@@ -4889,11 +4920,11 @@ func TestTCPEndpointProbe(t *testing.T) {
func TestStackSetCongestionControl(t *testing.T) {
testCases := []struct {
cc tcpip.CongestionControlOption
- err *tcpip.Error
+ err tcpip.Error
}{
{"reno", nil},
{"cubic", nil},
- {"blahblah", tcpip.ErrNoSuchFile},
+ {"blahblah", &tcpip.ErrNoSuchFile{}},
}
for _, tc := range testCases {
@@ -4975,11 +5006,11 @@ func TestStackSetAvailableCongestionControl(t *testing.T) {
func TestEndpointSetCongestionControl(t *testing.T) {
testCases := []struct {
cc tcpip.CongestionControlOption
- err *tcpip.Error
+ err tcpip.Error
}{
{"reno", nil},
{"cubic", nil},
- {"blahblah", tcpip.ErrNoSuchFile},
+ {"blahblah", &tcpip.ErrNoSuchFile{}},
}
for _, connected := range []bool{false, true} {
@@ -4989,7 +5020,7 @@ func TestEndpointSetCongestionControl(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5085,7 +5116,7 @@ func TestKeepalive(t *testing.T) {
// Check that the connection is still alive.
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Send some data and wait before ACKing it. Keepalives should be disabled
// during this period.
@@ -5176,7 +5207,7 @@ func TestKeepalive(t *testing.T) {
t.Errorf("got c.Stack().Stats().TCP.EstablishedTimedout.Value() = %d, want = 1", got)
}
- ept.CheckReadError(t, tcpip.ErrTimeout)
+ ept.CheckReadError(t, &tcpip.ErrTimeout{})
if got := c.Stack().Stats().TCP.CurrentEstablished.Value(); got != 0 {
t.Errorf("got stats.TCP.CurrentEstablished.Value() = %d, want = 0", got)
@@ -5283,7 +5314,7 @@ func TestListenBacklogFull(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5326,7 +5357,7 @@ func TestListenBacklogFull(t *testing.T) {
for i := 0; i < listenBacklog; i++ {
_, _, err = c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -5343,7 +5374,7 @@ func TestListenBacklogFull(t *testing.T) {
// Now verify that there are no more connections that can be accepted.
_, _, err = c.EP.Accept(nil)
- if err != tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
select {
case <-ch:
t.Fatalf("unexpected endpoint delivered on Accept: %+v", c.EP)
@@ -5355,7 +5386,7 @@ func TestListenBacklogFull(t *testing.T) {
executeHandshake(t, c, context.TestPort+lastPortOffset, false /*synCookieInUse */)
newEP, _, err := c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -5598,7 +5629,7 @@ func TestListenSynRcvdQueueFull(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5673,7 +5704,7 @@ func TestListenSynRcvdQueueFull(t *testing.T) {
defer c.WQ.EventUnregister(&we)
newEP, _, err := c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -5709,7 +5740,7 @@ func TestListenBacklogFullSynCookieInUse(t *testing.T) {
}
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5750,7 +5781,7 @@ func TestListenBacklogFullSynCookieInUse(t *testing.T) {
defer c.WQ.EventUnregister(&we)
_, _, err = c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -5766,7 +5797,7 @@ func TestListenBacklogFullSynCookieInUse(t *testing.T) {
// Now verify that there are no more connections that can be accepted.
_, _, err = c.EP.Accept(nil)
- if err != tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
select {
case <-ch:
t.Fatalf("unexpected endpoint delivered on Accept: %+v", c.EP)
@@ -5780,7 +5811,7 @@ func TestSYNRetransmit(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5824,7 +5855,7 @@ func TestSynRcvdBadSeqNumber(t *testing.T) {
defer c.Cleanup()
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.Stack().NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
t.Fatalf("NewEndpoint failed: %s", err)
@@ -5899,12 +5930,13 @@ func TestSynRcvdBadSeqNumber(t *testing.T) {
})
newEP, _, err := c.EP.Accept(nil)
-
- if err != nil && err != tcpip.ErrWouldBlock {
+ switch err.(type) {
+ case nil, *tcpip.ErrWouldBlock:
+ default:
t.Fatalf("Accept failed: %s", err)
}
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Try to accept the connections in the backlog.
we, ch := waiter.NewChannelEntry(nil)
c.WQ.EventRegister(&we, waiter.EventIn)
@@ -5972,7 +6004,7 @@ func TestPassiveConnectionAttemptIncrement(t *testing.T) {
// Verify that there is only one acceptable connection at this point.
_, _, err = c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6042,7 +6074,7 @@ func TestPassiveFailedConnectionAttemptIncrement(t *testing.T) {
// Now check that there is one acceptable connections.
_, _, err = c.EP.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6074,7 +6106,7 @@ func TestEndpointBindListenAcceptState(t *testing.T) {
}
ept := endpointTester{ep}
- ept.CheckReadError(t, tcpip.ErrNotConnected)
+ ept.CheckReadError(t, &tcpip.ErrNotConnected{})
if got := ep.Stats().(*tcp.Stats).ReadErrors.NotConnected.Value(); got != 1 {
t.Errorf("got EP stats Stats.ReadErrors.NotConnected got %d want %d", got, 1)
}
@@ -6094,7 +6126,7 @@ func TestEndpointBindListenAcceptState(t *testing.T) {
defer wq.EventUnregister(&we)
aep, _, err := ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6110,8 +6142,11 @@ func TestEndpointBindListenAcceptState(t *testing.T) {
if got, want := tcp.EndpointState(aep.State()), tcp.StateEstablished; got != want {
t.Errorf("unexpected endpoint state: want %s, got %s", want, got)
}
- if err := aep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort}); err != tcpip.ErrAlreadyConnected {
- t.Errorf("unexpected error attempting to call connect on an established endpoint, got: %s, want: %s", err, tcpip.ErrAlreadyConnected)
+ {
+ err := aep.Connect(tcpip.FullAddress{Addr: context.TestAddr, Port: context.TestPort})
+ if _, ok := err.(*tcpip.ErrAlreadyConnected); !ok {
+ t.Errorf("unexpected error attempting to call connect on an established endpoint, got: %v, want: %s", err, &tcpip.ErrAlreadyConnected{})
+ }
}
// Listening endpoint remains in listen state.
if got, want := tcp.EndpointState(ep.State()), tcp.StateListen; got != want {
@@ -6230,7 +6265,7 @@ func TestReceiveBufferAutoTuningApplicationLimited(t *testing.T) {
// window increases to the full available buffer size.
for {
_, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{})
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
break
}
}
@@ -6267,6 +6302,13 @@ func TestReceiveBufferAutoTuning(t *testing.T) {
// Enable Auto-tuning.
stk := c.Stack()
+ // Disable out of window rate limiting for this test by setting it to 0 as we
+ // use out of window ACKs to measure the advertised window.
+ var tcpInvalidRateLimit stack.TCPInvalidRateLimitOption
+ if err := stk.SetOption(tcpInvalidRateLimit); err != nil {
+ t.Fatalf("e.stack.SetOption(%#v) = %s", tcpInvalidRateLimit, err)
+ }
+
const receiveBufferSize = 80 << 10 // 80KB.
const maxReceiveBufferSize = receiveBufferSize * 10
{
@@ -6354,7 +6396,7 @@ func TestReceiveBufferAutoTuning(t *testing.T) {
totalCopied := 0
for {
res, err := c.EP.Read(ioutil.Discard, tcpip.ReadOptions{})
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
break
}
totalCopied += res.Count
@@ -6546,7 +6588,7 @@ func TestTCPTimeWaitRSTIgnored(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6665,7 +6707,7 @@ func TestTCPTimeWaitOutOfOrder(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6772,7 +6814,7 @@ func TestTCPTimeWaitNewSyn(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6862,7 +6904,7 @@ func TestTCPTimeWaitNewSyn(t *testing.T) {
// Try to accept the connection.
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -6936,7 +6978,7 @@ func TestTCPTimeWaitDuplicateFINExtendsTimeWait(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -7086,7 +7128,7 @@ func TestTCPCloseWithData(t *testing.T) {
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
@@ -7277,7 +7319,7 @@ func TestTCPUserTimeout(t *testing.T) {
)
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrTimeout)
+ ept.CheckReadError(t, &tcpip.ErrTimeout{})
if got, want := c.Stack().Stats().TCP.EstablishedTimedout.Value(), origEstablishedTimedout+1; got != want {
t.Errorf("got c.Stack().Stats().TCP.EstablishedTimedout = %d, want = %d", got, want)
@@ -7321,7 +7363,7 @@ func TestKeepaliveWithUserTimeout(t *testing.T) {
// Check that the connection is still alive.
ept := endpointTester{c.EP}
- ept.CheckReadError(t, tcpip.ErrWouldBlock)
+ ept.CheckReadError(t, &tcpip.ErrWouldBlock{})
// Now receive 1 keepalives, but don't ACK it.
b := c.GetPacket()
@@ -7360,7 +7402,7 @@ func TestKeepaliveWithUserTimeout(t *testing.T) {
),
)
- ept.CheckReadError(t, tcpip.ErrTimeout)
+ ept.CheckReadError(t, &tcpip.ErrTimeout{})
if got, want := c.Stack().Stats().TCP.EstablishedTimedout.Value(), origEstablishedTimedout+1; got != want {
t.Errorf("got c.Stack().Stats().TCP.EstablishedTimedout = %d, want = %d", got, want)
}
@@ -7515,8 +7557,9 @@ func TestTCPDeferAccept(t *testing.T) {
irs, iss := executeHandshake(t, c, context.TestPort, false /* synCookiesInUse */)
- if _, _, err := c.EP.Accept(nil); err != tcpip.ErrWouldBlock {
- t.Fatalf("got c.EP.Accept(nil) = %s, want: %s", err, tcpip.ErrWouldBlock)
+ _, _, err := c.EP.Accept(nil)
+ if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
+ t.Fatalf("got c.EP.Accept(nil) = %v, want: %s", err, &tcpip.ErrWouldBlock{})
}
// Send data. This should result in an acceptable endpoint.
@@ -7573,8 +7616,9 @@ func TestTCPDeferAcceptTimeout(t *testing.T) {
irs, iss := executeHandshake(t, c, context.TestPort, false /* synCookiesInUse */)
- if _, _, err := c.EP.Accept(nil); err != tcpip.ErrWouldBlock {
- t.Fatalf("got c.EP.Accept(nil) = %s, want: %s", err, tcpip.ErrWouldBlock)
+ _, _, err := c.EP.Accept(nil)
+ if _, ok := err.(*tcpip.ErrWouldBlock); !ok {
+ t.Fatalf("got c.EP.Accept(nil) = %v, want: %s", err, &tcpip.ErrWouldBlock{})
}
// Sleep for a little of the tcpDeferAccept timeout.
@@ -7696,13 +7740,13 @@ func TestSetStackTimeWaitReuse(t *testing.T) {
s := c.Stack()
testCases := []struct {
v int
- err *tcpip.Error
+ err tcpip.Error
}{
{int(tcpip.TCPTimeWaitReuseDisabled), nil},
{int(tcpip.TCPTimeWaitReuseGlobal), nil},
{int(tcpip.TCPTimeWaitReuseLoopbackOnly), nil},
- {int(tcpip.TCPTimeWaitReuseLoopbackOnly) + 1, tcpip.ErrInvalidOptionValue},
- {int(tcpip.TCPTimeWaitReuseDisabled) - 1, tcpip.ErrInvalidOptionValue},
+ {int(tcpip.TCPTimeWaitReuseLoopbackOnly) + 1, &tcpip.ErrInvalidOptionValue{}},
+ {int(tcpip.TCPTimeWaitReuseDisabled) - 1, &tcpip.ErrInvalidOptionValue{}},
}
for _, tc := range testCases {
diff --git a/pkg/tcpip/transport/tcp/testing/context/context.go b/pkg/tcpip/transport/tcp/testing/context/context.go
index ee55f030c..b1cb9a324 100644
--- a/pkg/tcpip/transport/tcp/testing/context/context.go
+++ b/pkg/tcpip/transport/tcp/testing/context/context.go
@@ -586,7 +586,7 @@ func (c *Context) ReceiveNonBlockingAndCheckPacket(data []byte, offset, size int
// is true then it sets the IP_V6ONLY option on the socket to make it a IPv6
// only endpoint instead of a default dual stack socket.
func (c *Context) CreateV6Endpoint(v6only bool) {
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.s.NewEndpoint(tcp.ProtocolNumber, ipv6.ProtocolNumber, &c.WQ)
if err != nil {
c.t.Fatalf("NewEndpoint failed: %v", err)
@@ -689,7 +689,8 @@ func (c *Context) Connect(iss seqnum.Value, rcvWnd seqnum.Size, options []byte)
c.WQ.EventRegister(&waitEntry, waiter.EventOut)
defer c.WQ.EventUnregister(&waitEntry)
- if err := c.EP.Connect(tcpip.FullAddress{Addr: TestAddr, Port: TestPort}); err != tcpip.ErrConnectStarted {
+ err := c.EP.Connect(tcpip.FullAddress{Addr: TestAddr, Port: TestPort})
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
c.t.Fatalf("Unexpected return value from Connect: %v", err)
}
@@ -749,7 +750,7 @@ func (c *Context) Connect(iss seqnum.Value, rcvWnd seqnum.Size, options []byte)
// Create creates a TCP endpoint.
func (c *Context) Create(epRcvBuf int) {
// Create TCP endpoint.
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
c.t.Fatalf("NewEndpoint failed: %v", err)
@@ -887,7 +888,7 @@ func (r *RawEndpoint) VerifyACKHasSACK(sackBlocks []header.SACKBlock) {
// It also verifies where required(eg.Timestamp) that the ACK to the SYN-ACK
// does not carry an option that was not requested.
func (c *Context) CreateConnectedWithOptions(wantOptions header.TCPSynOptions) *RawEndpoint {
- var err *tcpip.Error
+ var err tcpip.Error
c.EP, err = c.s.NewEndpoint(tcp.ProtocolNumber, ipv4.ProtocolNumber, &c.WQ)
if err != nil {
c.t.Fatalf("c.s.NewEndpoint(tcp, ipv4...) = %v", err)
@@ -903,7 +904,7 @@ func (c *Context) CreateConnectedWithOptions(wantOptions header.TCPSynOptions) *
testFullAddr := tcpip.FullAddress{Addr: TestAddr, Port: TestPort}
err = c.EP.Connect(testFullAddr)
- if err != tcpip.ErrConnectStarted {
+ if _, ok := err.(*tcpip.ErrConnectStarted); !ok {
c.t.Fatalf("c.ep.Connect(%v) = %v", testFullAddr, err)
}
// Receive SYN packet.
@@ -1054,7 +1055,7 @@ func (c *Context) AcceptWithOptions(wndScale int, synOptions header.TCPSynOption
defer wq.EventUnregister(&we)
c.EP, _, err = ep.Accept(nil)
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for connection to be established.
select {
case <-ch:
diff --git a/pkg/tcpip/transport/udp/endpoint.go b/pkg/tcpip/transport/udp/endpoint.go
index 4988ba29b..afd8f4d39 100644
--- a/pkg/tcpip/transport/udp/endpoint.go
+++ b/pkg/tcpip/transport/udp/endpoint.go
@@ -109,8 +109,8 @@ type endpoint struct {
multicastNICID tcpip.NICID
portFlags ports.Flags
- lastErrorMu sync.Mutex `state:"nosave"`
- lastError *tcpip.Error `state:".(string)"`
+ lastErrorMu sync.Mutex `state:"nosave"`
+ lastError tcpip.Error
// Values used to reserve a port or register a transport endpoint.
// (which ever happens first).
@@ -215,7 +215,7 @@ func (e *endpoint) UniqueID() uint64 {
return e.uniqueID
}
-func (e *endpoint) LastError() *tcpip.Error {
+func (e *endpoint) LastError() tcpip.Error {
e.lastErrorMu.Lock()
defer e.lastErrorMu.Unlock()
@@ -225,7 +225,7 @@ func (e *endpoint) LastError() *tcpip.Error {
}
// UpdateLastError implements tcpip.SocketOptionsHandler.UpdateLastError.
-func (e *endpoint) UpdateLastError(err *tcpip.Error) {
+func (e *endpoint) UpdateLastError(err tcpip.Error) {
e.lastErrorMu.Lock()
e.lastError = err
e.lastErrorMu.Unlock()
@@ -282,7 +282,7 @@ func (e *endpoint) Close() {
func (e *endpoint) ModerateRecvBuf(copied int) {}
// Read implements tcpip.Endpoint.Read.
-func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, *tcpip.Error) {
+func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult, tcpip.Error) {
if err := e.LastError(); err != nil {
return tcpip.ReadResult{}, err
}
@@ -290,10 +290,10 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
e.rcvMu.Lock()
if e.rcvList.Empty() {
- err := tcpip.ErrWouldBlock
+ var err tcpip.Error = &tcpip.ErrWouldBlock{}
if e.rcvClosed {
e.stats.ReadErrors.ReadClosed.Increment()
- err = tcpip.ErrClosedForReceive
+ err = &tcpip.ErrClosedForReceive{}
}
e.rcvMu.Unlock()
return tcpip.ReadResult{}, err
@@ -340,7 +340,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
n, err := p.data.ReadTo(dst, opts.Peek)
if n == 0 && err != nil {
- return res, tcpip.ErrBadBuffer
+ return res, &tcpip.ErrBadBuffer{}
}
res.Count = n
return res, nil
@@ -351,7 +351,7 @@ func (e *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResult
// reacquire the mutex in exclusive mode.
//
// Returns true for retry if preparation should be retried.
-func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpip.Error) {
+func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err tcpip.Error) {
switch e.EndpointState() {
case StateInitial:
case StateConnected:
@@ -359,11 +359,11 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpi
case StateBound:
if to == nil {
- return false, tcpip.ErrDestinationRequired
+ return false, &tcpip.ErrDestinationRequired{}
}
return false, nil
default:
- return false, tcpip.ErrInvalidEndpointState
+ return false, &tcpip.ErrInvalidEndpointState{}
}
e.mu.RUnlock()
@@ -389,7 +389,7 @@ func (e *endpoint) prepareForWrite(to *tcpip.FullAddress) (retry bool, err *tcpi
// connectRoute establishes a route to the specified interface or the
// configured multicast interface if no interface is specified and the
// specified address is a multicast address.
-func (e *endpoint) connectRoute(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, *tcpip.Error) {
+func (e *endpoint) connectRoute(nicID tcpip.NICID, addr tcpip.FullAddress, netProto tcpip.NetworkProtocolNumber) (*stack.Route, tcpip.NICID, tcpip.Error) {
localAddr := e.ID.LocalAddress
if e.isBroadcastOrMulticast(nicID, netProto, localAddr) {
// A packet can only originate from a unicast address (i.e., an interface).
@@ -415,18 +415,18 @@ func (e *endpoint) connectRoute(nicID tcpip.NICID, addr tcpip.FullAddress, netPr
// Write writes data to the endpoint's peer. This method does not block
// if the data cannot be written.
-func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
n, err := e.write(p, opts)
- switch err {
+ switch err.(type) {
case nil:
e.stats.PacketsSent.Increment()
- case tcpip.ErrMessageTooLong, tcpip.ErrInvalidOptionValue:
+ case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
e.stats.WriteErrors.InvalidArgs.Increment()
- case tcpip.ErrClosedForSend:
+ case *tcpip.ErrClosedForSend:
e.stats.WriteErrors.WriteClosed.Increment()
- case tcpip.ErrInvalidEndpointState:
+ case *tcpip.ErrInvalidEndpointState:
e.stats.WriteErrors.InvalidEndpointState.Increment()
- case tcpip.ErrNoRoute, tcpip.ErrBroadcastDisabled, tcpip.ErrNetworkUnreachable:
+ case *tcpip.ErrNoRoute, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
// Errors indicating any problem with IP routing of the packet.
e.stats.SendErrors.NoRoute.Increment()
default:
@@ -436,14 +436,14 @@ func (e *endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
return n, err
}
-func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
+func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, tcpip.Error) {
if err := e.LastError(); err != nil {
return 0, err
}
// MSG_MORE is unimplemented. (This also means that MSG_EOR is a no-op.)
if opts.More {
- return 0, tcpip.ErrInvalidOptionValue
+ return 0, &tcpip.ErrInvalidOptionValue{}
}
to := opts.To
@@ -459,7 +459,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// If we've shutdown with SHUT_WR we are in an invalid state for sending.
if e.shutdownFlags&tcpip.ShutdownWrite != 0 {
- return 0, tcpip.ErrClosedForSend
+ return 0, &tcpip.ErrClosedForSend{}
}
// Prepare for write.
@@ -480,9 +480,12 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
// Reject destination address if it goes through a different
// NIC than the endpoint was bound to.
nicID := to.NIC
+ if nicID == 0 {
+ nicID = tcpip.NICID(e.ops.GetBindToDevice())
+ }
if e.BindNICID != 0 {
if nicID != 0 && nicID != e.BindNICID {
- return 0, tcpip.ErrNoRoute
+ return 0, &tcpip.ErrNoRoute{}
}
nicID = e.BindNICID
@@ -490,7 +493,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
if to.Port == 0 {
// Port 0 is an invalid port to send to.
- return 0, tcpip.ErrInvalidEndpointState
+ return 0, &tcpip.ErrInvalidEndpointState{}
}
dst, netProto, err := e.checkV4MappedLocked(*to)
@@ -509,19 +512,19 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
}
if !e.ops.GetBroadcast() && route.IsOutboundBroadcast() {
- return 0, tcpip.ErrBroadcastDisabled
+ return 0, &tcpip.ErrBroadcastDisabled{}
}
v := make([]byte, p.Len())
if _, err := io.ReadFull(p, v); err != nil {
- return 0, tcpip.ErrBadBuffer
+ return 0, &tcpip.ErrBadBuffer{}
}
if len(v) > header.UDPMaximumPacketSize {
// Payload can't possibly fit in a packet.
so := e.SocketOptions()
if so.GetRecvError() {
so.QueueLocalErr(
- tcpip.ErrMessageTooLong,
+ &tcpip.ErrMessageTooLong{},
route.NetProto,
header.UDPMaximumPacketSize,
tcpip.FullAddress{
@@ -532,7 +535,7 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
v,
)
}
- return 0, tcpip.ErrMessageTooLong
+ return 0, &tcpip.ErrMessageTooLong{}
}
ttl := e.ttl
@@ -582,13 +585,13 @@ func (e *endpoint) OnReusePortSet(v bool) {
}
// SetSockOptInt implements tcpip.Endpoint.SetSockOptInt.
-func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) *tcpip.Error {
+func (e *endpoint) SetSockOptInt(opt tcpip.SockOptInt, v int) tcpip.Error {
switch opt {
case tcpip.MTUDiscoverOption:
// Return not supported if the value is not disabling path
// MTU discovery.
if v != tcpip.PMTUDiscoveryDont {
- return tcpip.ErrNotSupported
+ return &tcpip.ErrNotSupported{}
}
case tcpip.MulticastTTLOption:
@@ -640,7 +643,7 @@ func (e *endpoint) HasNIC(id int32) bool {
}
// SetSockOpt implements tcpip.Endpoint.SetSockOpt.
-func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
+func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) tcpip.Error {
switch v := opt.(type) {
case *tcpip.MulticastInterfaceOption:
e.mu.Lock()
@@ -662,17 +665,17 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
if nic != 0 {
if !e.stack.CheckNIC(nic) {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
} else {
nic = e.stack.CheckLocalAddress(0, netProto, addr)
if nic == 0 {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
}
if e.BindNICID != 0 && e.BindNICID != nic {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
e.multicastNICID = nic
@@ -680,7 +683,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
case *tcpip.AddMembershipOption:
if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
nicID := v.NIC
@@ -696,7 +699,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
nicID = e.stack.CheckLocalAddress(nicID, e.NetProto, v.InterfaceAddr)
}
if nicID == 0 {
- return tcpip.ErrUnknownDevice
+ return &tcpip.ErrUnknownDevice{}
}
memToInsert := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
@@ -705,7 +708,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
defer e.mu.Unlock()
if _, ok := e.multicastMemberships[memToInsert]; ok {
- return tcpip.ErrPortInUse
+ return &tcpip.ErrPortInUse{}
}
if err := e.stack.JoinGroup(e.NetProto, nicID, v.MulticastAddr); err != nil {
@@ -716,7 +719,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
case *tcpip.RemoveMembershipOption:
if !header.IsV4MulticastAddress(v.MulticastAddr) && !header.IsV6MulticastAddress(v.MulticastAddr) {
- return tcpip.ErrInvalidOptionValue
+ return &tcpip.ErrInvalidOptionValue{}
}
nicID := v.NIC
@@ -731,7 +734,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
nicID = e.stack.CheckLocalAddress(nicID, e.NetProto, v.InterfaceAddr)
}
if nicID == 0 {
- return tcpip.ErrUnknownDevice
+ return &tcpip.ErrUnknownDevice{}
}
memToRemove := multicastMembership{nicID: nicID, multicastAddr: v.MulticastAddr}
@@ -740,7 +743,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
defer e.mu.Unlock()
if _, ok := e.multicastMemberships[memToRemove]; !ok {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
if err := e.stack.LeaveGroup(e.NetProto, nicID, v.MulticastAddr); err != nil {
@@ -756,7 +759,7 @@ func (e *endpoint) SetSockOpt(opt tcpip.SettableSocketOption) *tcpip.Error {
}
// GetSockOptInt implements tcpip.Endpoint.GetSockOptInt.
-func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
+func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, tcpip.Error) {
switch opt {
case tcpip.IPv4TOSOption:
e.mu.RLock()
@@ -803,12 +806,12 @@ func (e *endpoint) GetSockOptInt(opt tcpip.SockOptInt) (int, *tcpip.Error) {
return v, nil
default:
- return -1, tcpip.ErrUnknownProtocolOption
+ return -1, &tcpip.ErrUnknownProtocolOption{}
}
}
// GetSockOpt implements tcpip.Endpoint.GetSockOpt.
-func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
+func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) tcpip.Error {
switch o := opt.(type) {
case *tcpip.MulticastInterfaceOption:
e.mu.Lock()
@@ -819,14 +822,14 @@ func (e *endpoint) GetSockOpt(opt tcpip.GettableSocketOption) *tcpip.Error {
e.mu.Unlock()
default:
- return tcpip.ErrUnknownProtocolOption
+ return &tcpip.ErrUnknownProtocolOption{}
}
return nil
}
// sendUDP sends a UDP segment via the provided network endpoint and under the
// provided identity.
-func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8, owner tcpip.PacketOwner, noChecksum bool) *tcpip.Error {
+func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort uint16, ttl uint8, useDefaultTTL bool, tos uint8, owner tcpip.PacketOwner, noChecksum bool) tcpip.Error {
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: header.UDPMinimumSize + int(r.MaxHeaderLength()),
Data: data,
@@ -876,7 +879,7 @@ func sendUDP(r *stack.Route, data buffer.VectorisedView, localPort, remotePort u
// checkV4MappedLocked determines the effective network protocol and converts
// addr to its canonical form.
-func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, *tcpip.Error) {
+func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddress, tcpip.NetworkProtocolNumber, tcpip.Error) {
unwrapped, netProto, err := e.TransportEndpointInfo.AddrNetProtoLocked(addr, e.ops.GetV6Only())
if err != nil {
return tcpip.FullAddress{}, 0, err
@@ -885,7 +888,7 @@ func (e *endpoint) checkV4MappedLocked(addr tcpip.FullAddress) (tcpip.FullAddres
}
// Disconnect implements tcpip.Endpoint.Disconnect.
-func (e *endpoint) Disconnect() *tcpip.Error {
+func (e *endpoint) Disconnect() tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
@@ -903,7 +906,7 @@ func (e *endpoint) Disconnect() *tcpip.Error {
// Exclude ephemerally bound endpoints.
if e.BindNICID != 0 || e.ID.LocalAddress == "" {
- var err *tcpip.Error
+ var err tcpip.Error
id = stack.TransportEndpointID{
LocalPort: e.ID.LocalPort,
LocalAddress: e.ID.LocalAddress,
@@ -934,10 +937,10 @@ func (e *endpoint) Disconnect() *tcpip.Error {
}
// Connect connects the endpoint to its peer. Specifying a NIC is optional.
-func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Connect(addr tcpip.FullAddress) tcpip.Error {
if addr.Port == 0 {
// We don't support connecting to port zero.
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
e.mu.Lock()
@@ -954,12 +957,12 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
if nicID != 0 && nicID != e.BindNICID {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
nicID = e.BindNICID
default:
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4MappedLocked(addr)
@@ -1029,20 +1032,20 @@ func (e *endpoint) Connect(addr tcpip.FullAddress) *tcpip.Error {
}
// ConnectEndpoint is not supported.
-func (*endpoint) ConnectEndpoint(tcpip.Endpoint) *tcpip.Error {
- return tcpip.ErrInvalidEndpointState
+func (*endpoint) ConnectEndpoint(tcpip.Endpoint) tcpip.Error {
+ return &tcpip.ErrInvalidEndpointState{}
}
// Shutdown closes the read and/or write end of the endpoint connection
// to its peer.
-func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
+func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
// A socket in the bound state can still receive multicast messages,
// so we need to notify waiters on shutdown.
if state := e.EndpointState(); state != StateBound && state != StateConnected {
- return tcpip.ErrNotConnected
+ return &tcpip.ErrNotConnected{}
}
e.shutdownFlags |= flags
@@ -1062,16 +1065,16 @@ func (e *endpoint) Shutdown(flags tcpip.ShutdownFlags) *tcpip.Error {
}
// Listen is not supported by UDP, it just fails.
-func (*endpoint) Listen(int) *tcpip.Error {
- return tcpip.ErrNotSupported
+func (*endpoint) Listen(int) tcpip.Error {
+ return &tcpip.ErrNotSupported{}
}
// Accept is not supported by UDP, it just fails.
-func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, *tcpip.Error) {
- return nil, nil, tcpip.ErrNotSupported
+func (*endpoint) Accept(*tcpip.FullAddress) (tcpip.Endpoint, *waiter.Queue, tcpip.Error) {
+ return nil, nil, &tcpip.ErrNotSupported{}
}
-func (e *endpoint) registerWithStack(netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.NICID, *tcpip.Error) {
+func (e *endpoint) registerWithStack(netProtos []tcpip.NetworkProtocolNumber, id stack.TransportEndpointID) (stack.TransportEndpointID, tcpip.NICID, tcpip.Error) {
bindToDevice := tcpip.NICID(e.ops.GetBindToDevice())
if e.ID.LocalPort == 0 {
port, err := e.stack.ReservePort(netProtos, ProtocolNumber, id.LocalAddress, id.LocalPort, e.portFlags, bindToDevice, tcpip.FullAddress{}, nil /* testPort */)
@@ -1090,11 +1093,11 @@ func (e *endpoint) registerWithStack(netProtos []tcpip.NetworkProtocolNumber, id
return id, bindToDevice, err
}
-func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) bindLocked(addr tcpip.FullAddress) tcpip.Error {
// Don't allow binding once endpoint is not in the initial state
// anymore.
if e.EndpointState() != StateInitial {
- return tcpip.ErrInvalidEndpointState
+ return &tcpip.ErrInvalidEndpointState{}
}
addr, netProto, err := e.checkV4MappedLocked(addr)
@@ -1118,7 +1121,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
// A local unicast address was specified, verify that it's valid.
nicID = e.stack.CheckLocalAddress(addr.NIC, netProto, addr.Addr)
if nicID == 0 {
- return tcpip.ErrBadLocalAddress
+ return &tcpip.ErrBadLocalAddress{}
}
}
@@ -1148,7 +1151,7 @@ func (e *endpoint) bindLocked(addr tcpip.FullAddress) *tcpip.Error {
// Bind binds the endpoint to a specific local address and port.
// Specifying a NIC is optional.
-func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
+func (e *endpoint) Bind(addr tcpip.FullAddress) tcpip.Error {
e.mu.Lock()
defer e.mu.Unlock()
@@ -1164,7 +1167,7 @@ func (e *endpoint) Bind(addr tcpip.FullAddress) *tcpip.Error {
}
// GetLocalAddress returns the address to which the endpoint is bound.
-func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, tcpip.Error) {
e.mu.RLock()
defer e.mu.RUnlock()
@@ -1181,12 +1184,12 @@ func (e *endpoint) GetLocalAddress() (tcpip.FullAddress, *tcpip.Error) {
}
// GetRemoteAddress returns the address to which the endpoint is connected.
-func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, *tcpip.Error) {
+func (e *endpoint) GetRemoteAddress() (tcpip.FullAddress, tcpip.Error) {
e.mu.RLock()
defer e.mu.RUnlock()
if e.EndpointState() != StateConnected {
- return tcpip.FullAddress{}, tcpip.ErrNotConnected
+ return tcpip.FullAddress{}, &tcpip.ErrNotConnected{}
}
return tcpip.FullAddress{
@@ -1319,7 +1322,7 @@ func (e *endpoint) HandlePacket(id stack.TransportEndpointID, pkt *stack.PacketB
}
}
-func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, extra uint32, pkt *stack.PacketBuffer) {
+func (e *endpoint) onICMPError(err tcpip.Error, transErr stack.TransportError, pkt *stack.PacketBuffer) {
// Update last error first.
e.lastErrorMu.Lock()
e.lastError = err
@@ -1335,12 +1338,9 @@ func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, ext
}
e.SocketOptions().QueueErr(&tcpip.SockError{
- Err: err,
- ErrOrigin: header.ICMPOriginFromNetProto(pkt.NetworkProtocolNumber),
- ErrType: errType,
- ErrCode: errCode,
- ErrInfo: extra,
- Payload: payload,
+ Err: err,
+ Cause: transErr,
+ Payload: payload,
Dst: tcpip.FullAddress{
NIC: pkt.NICID,
Addr: e.ID.RemoteAddress,
@@ -1359,24 +1359,13 @@ func (e *endpoint) onICMPError(err *tcpip.Error, errType byte, errCode byte, ext
e.waiterQueue.Notify(waiter.EventErr)
}
-// HandleControlPacket implements stack.TransportEndpoint.HandleControlPacket.
-func (e *endpoint) HandleControlPacket(typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
- if typ == stack.ControlPortUnreachable {
+// HandleError implements stack.TransportEndpoint.
+func (e *endpoint) HandleError(transErr stack.TransportError, pkt *stack.PacketBuffer) {
+ // TODO(gvisor.dev/issues/5270): Handle all transport errors.
+ switch transErr.Kind() {
+ case stack.DestinationPortUnreachableTransportError:
if e.EndpointState() == StateConnected {
- var errType byte
- var errCode byte
- switch pkt.NetworkProtocolNumber {
- case header.IPv4ProtocolNumber:
- errType = byte(header.ICMPv4DstUnreachable)
- errCode = byte(header.ICMPv4PortUnreachable)
- case header.IPv6ProtocolNumber:
- errType = byte(header.ICMPv6DstUnreachable)
- errCode = byte(header.ICMPv6PortUnreachable)
- default:
- panic(fmt.Sprintf("unsupported net proto for infering ICMP type and code: %d", pkt.NetworkProtocolNumber))
- }
- e.onICMPError(tcpip.ErrConnectionRefused, errType, errCode, extra, pkt)
- return
+ e.onICMPError(&tcpip.ErrConnectionRefused{}, transErr, pkt)
}
}
}
diff --git a/pkg/tcpip/transport/udp/endpoint_state.go b/pkg/tcpip/transport/udp/endpoint_state.go
index feb53b553..21a6aa460 100644
--- a/pkg/tcpip/transport/udp/endpoint_state.go
+++ b/pkg/tcpip/transport/udp/endpoint_state.go
@@ -37,24 +37,6 @@ func (u *udpPacket) loadData(data buffer.VectorisedView) {
u.data = data
}
-// saveLastError is invoked by stateify.
-func (e *endpoint) saveLastError() string {
- if e.lastError == nil {
- return ""
- }
-
- return e.lastError.String()
-}
-
-// loadLastError is invoked by stateify.
-func (e *endpoint) loadLastError(s string) {
- if s == "" {
- return
- }
-
- e.lastError = tcpip.StringToError(s)
-}
-
// beforeSave is invoked by stateify.
func (e *endpoint) beforeSave() {
// Stop incoming packets from being handled (and mutate endpoint state).
@@ -114,7 +96,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
netProto = header.IPv6ProtocolNumber
}
- var err *tcpip.Error
+ var err tcpip.Error
if state == StateConnected {
e.route, err = e.stack.FindRoute(e.RegisterNICID, e.ID.LocalAddress, e.ID.RemoteAddress, netProto, e.ops.GetMulticastLoop())
if err != nil {
@@ -123,7 +105,7 @@ func (e *endpoint) Resume(s *stack.Stack) {
} else if len(e.ID.LocalAddress) != 0 && !e.isBroadcastOrMulticast(e.RegisterNICID, netProto, e.ID.LocalAddress) { // stateBound
// A local unicast address is specified, verify that it's valid.
if e.stack.CheckLocalAddress(e.RegisterNICID, netProto, e.ID.LocalAddress) == 0 {
- panic(tcpip.ErrBadLocalAddress)
+ panic(&tcpip.ErrBadLocalAddress{})
}
}
diff --git a/pkg/tcpip/transport/udp/forwarder.go b/pkg/tcpip/transport/udp/forwarder.go
index aae794506..705ad1f64 100644
--- a/pkg/tcpip/transport/udp/forwarder.go
+++ b/pkg/tcpip/transport/udp/forwarder.go
@@ -69,7 +69,7 @@ func (r *ForwarderRequest) ID() stack.TransportEndpointID {
}
// CreateEndpoint creates a connected UDP endpoint for the session request.
-func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (r *ForwarderRequest) CreateEndpoint(queue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
netHdr := r.pkt.Network()
route, err := r.stack.FindRoute(r.pkt.NICID, netHdr.DestinationAddress(), netHdr.SourceAddress(), r.pkt.NetworkProtocolNumber, false /* multicastLoop */)
if err != nil {
diff --git a/pkg/tcpip/transport/udp/protocol.go b/pkg/tcpip/transport/udp/protocol.go
index 91420edd3..427fdd0c9 100644
--- a/pkg/tcpip/transport/udp/protocol.go
+++ b/pkg/tcpip/transport/udp/protocol.go
@@ -54,13 +54,13 @@ func (*protocol) Number() tcpip.TransportProtocolNumber {
}
// NewEndpoint creates a new udp endpoint.
-func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return newEndpoint(p.stack, netProto, waiterQueue), nil
}
// NewRawEndpoint creates a new raw UDP endpoint. It implements
// stack.TransportProtocol.NewRawEndpoint.
-func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, *tcpip.Error) {
+func (p *protocol) NewRawEndpoint(netProto tcpip.NetworkProtocolNumber, waiterQueue *waiter.Queue) (tcpip.Endpoint, tcpip.Error) {
return raw.NewEndpoint(p.stack, netProto, header.UDPProtocolNumber, waiterQueue)
}
@@ -71,7 +71,7 @@ func (*protocol) MinimumPacketSize() int {
// ParsePorts returns the source and destination ports stored in the given udp
// packet.
-func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
+func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err tcpip.Error) {
h := header.UDP(v)
return h.SourcePort(), h.DestinationPort(), nil
}
@@ -94,13 +94,13 @@ func (p *protocol) HandleUnknownDestinationPacket(id stack.TransportEndpointID,
}
// SetOption implements stack.TransportProtocol.SetOption.
-func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (*protocol) SetOption(tcpip.SettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
// Option implements stack.TransportProtocol.Option.
-func (*protocol) Option(tcpip.GettableTransportProtocolOption) *tcpip.Error {
- return tcpip.ErrUnknownProtocolOption
+func (*protocol) Option(tcpip.GettableTransportProtocolOption) tcpip.Error {
+ return &tcpip.ErrUnknownProtocolOption{}
}
// Close implements stack.TransportProtocol.Close.
diff --git a/pkg/tcpip/transport/udp/udp_test.go b/pkg/tcpip/transport/udp/udp_test.go
index c4794e876..5d81dbb94 100644
--- a/pkg/tcpip/transport/udp/udp_test.go
+++ b/pkg/tcpip/transport/udp/udp_test.go
@@ -353,7 +353,7 @@ func (c *testContext) cleanup() {
func (c *testContext) createEndpoint(proto tcpip.NetworkProtocolNumber) {
c.t.Helper()
- var err *tcpip.Error
+ var err tcpip.Error
c.ep, err = c.s.NewEndpoint(udp.ProtocolNumber, proto, &c.wq)
if err != nil {
c.t.Fatal("NewEndpoint failed: ", err)
@@ -555,11 +555,11 @@ func TestBindToDeviceOption(t *testing.T) {
testActions := []struct {
name string
setBindToDevice *tcpip.NICID
- setBindToDeviceError *tcpip.Error
+ setBindToDeviceError tcpip.Error
getBindToDevice int32
}{
{"GetDefaultValue", nil, nil, 0},
- {"BindToNonExistent", nicIDPtr(999), tcpip.ErrUnknownDevice, 0},
+ {"BindToNonExistent", nicIDPtr(999), &tcpip.ErrUnknownDevice{}, 0},
{"BindToExistent", nicIDPtr(321), nil, 321},
{"UnbindToDevice", nicIDPtr(0), nil, 0},
}
@@ -599,7 +599,7 @@ func testReadInternal(c *testContext, flow testFlow, packetShouldBeDropped, expe
var buf bytes.Buffer
res, err := c.ep.Read(&buf, tcpip.ReadOptions{NeedRemoteAddr: true})
- if err == tcpip.ErrWouldBlock {
+ if _, ok := err.(*tcpip.ErrWouldBlock); ok {
// Wait for data to become available.
select {
case <-ch:
@@ -703,8 +703,11 @@ func TestBindReservedPort(t *testing.T) {
t.Fatalf("NewEndpoint failed: %s", err)
}
defer ep.Close()
- if got, want := ep.Bind(addr), tcpip.ErrPortInUse; got != want {
- t.Fatalf("got ep.Bind(...) = %s, want = %s", got, want)
+ {
+ err := ep.Bind(addr)
+ if _, ok := err.(*tcpip.ErrPortInUse); !ok {
+ t.Fatalf("got ep.Bind(...) = %s, want = %s", err, &tcpip.ErrPortInUse{})
+ }
}
}
@@ -716,8 +719,11 @@ func TestBindReservedPort(t *testing.T) {
defer ep.Close()
// We can't bind ipv4-any on the port reserved by the connected endpoint
// above, since the endpoint is dual-stack.
- if got, want := ep.Bind(tcpip.FullAddress{Port: addr.Port}), tcpip.ErrPortInUse; got != want {
- t.Fatalf("got ep.Bind(...) = %s, want = %s", got, want)
+ {
+ err := ep.Bind(tcpip.FullAddress{Port: addr.Port})
+ if _, ok := err.(*tcpip.ErrPortInUse); !ok {
+ t.Fatalf("got ep.Bind(...) = %s, want = %s", err, &tcpip.ErrPortInUse{})
+ }
}
// We can bind an ipv4 address on this port, though.
if err := ep.Bind(tcpip.FullAddress{Addr: stackAddr, Port: addr.Port}); err != nil {
@@ -806,11 +812,11 @@ func TestV4ReadSelfSource(t *testing.T) {
for _, tt := range []struct {
name string
handleLocal bool
- wantErr *tcpip.Error
+ wantErr tcpip.Error
wantInvalidSource uint64
}{
{"HandleLocal", false, nil, 0},
- {"NoHandleLocal", true, tcpip.ErrWouldBlock, 1},
+ {"NoHandleLocal", true, &tcpip.ErrWouldBlock{}, 1},
} {
t.Run(tt.name, func(t *testing.T) {
c := newDualTestContextWithOptions(t, defaultMTU, stack.Options{
@@ -959,7 +965,7 @@ func TestV4ReadBroadcastOnBoundToWildcard(t *testing.T) {
// testFailingWrite sends a packet of the given test flow into the UDP endpoint
// and verifies it fails with the provided error code.
-func testFailingWrite(c *testContext, flow testFlow, wantErr *tcpip.Error) {
+func testFailingWrite(c *testContext, flow testFlow, wantErr tcpip.Error) {
c.t.Helper()
// Take a snapshot of the stats to validate them at the end of the test.
epstats := c.ep.Stats().(*tcpip.TransportEndpointStats).Clone()
@@ -1092,7 +1098,7 @@ func TestDualWriteConnectedToV6(t *testing.T) {
testWrite(c, unicastV6)
// Write to V4 mapped address.
- testFailingWrite(c, unicastV4in6, tcpip.ErrNetworkUnreachable)
+ testFailingWrite(c, unicastV4in6, &tcpip.ErrNetworkUnreachable{})
const want = 1
if got := c.ep.Stats().(*tcpip.TransportEndpointStats).SendErrors.NoRoute.Value(); got != want {
c.t.Fatalf("Endpoint stat not updated. got %d want %d", got, want)
@@ -1113,7 +1119,7 @@ func TestDualWriteConnectedToV4Mapped(t *testing.T) {
testWrite(c, unicastV4in6)
// Write to v6 address.
- testFailingWrite(c, unicastV6, tcpip.ErrInvalidEndpointState)
+ testFailingWrite(c, unicastV6, &tcpip.ErrInvalidEndpointState{})
}
func TestV4WriteOnV6Only(t *testing.T) {
@@ -1123,7 +1129,7 @@ func TestV4WriteOnV6Only(t *testing.T) {
c.createEndpointForFlow(unicastV6Only)
// Write to V4 mapped address.
- testFailingWrite(c, unicastV4in6, tcpip.ErrNoRoute)
+ testFailingWrite(c, unicastV4in6, &tcpip.ErrNoRoute{})
}
func TestV6WriteOnBoundToV4Mapped(t *testing.T) {
@@ -1138,7 +1144,7 @@ func TestV6WriteOnBoundToV4Mapped(t *testing.T) {
}
// Write to v6 address.
- testFailingWrite(c, unicastV6, tcpip.ErrInvalidEndpointState)
+ testFailingWrite(c, unicastV6, &tcpip.ErrInvalidEndpointState{})
}
func TestV6WriteOnConnected(t *testing.T) {
@@ -1197,8 +1203,11 @@ func TestWriteOnConnectedInvalidPort(t *testing.T) {
c.t.Fatalf("c.ep.Write(...) wrote %d bytes, want %d bytes", got, want)
}
- if err := c.ep.LastError(); err != tcpip.ErrConnectionRefused {
- c.t.Fatalf("expected c.ep.LastError() == ErrConnectionRefused, got: %+v", err)
+ {
+ err := c.ep.LastError()
+ if _, ok := err.(*tcpip.ErrConnectionRefused); !ok {
+ c.t.Fatalf("expected c.ep.LastError() == ErrConnectionRefused, got: %+v", err)
+ }
}
})
}
@@ -1605,7 +1614,7 @@ func TestTTL(t *testing.T) {
s := stack.New(stack.Options{
NetworkProtocols: []stack.NetworkProtocolFactory{p},
})
- ep := s.NetworkProtocolInstance(n).NewEndpoint(&testInterface{}, nil, nil, nil)
+ ep := s.NetworkProtocolInstance(n).NewEndpoint(&testInterface{}, nil)
wantTTL = ep.DefaultTTL()
ep.Close()
}
@@ -2308,21 +2317,21 @@ func TestShutdownWrite(t *testing.T) {
t.Fatalf("Shutdown failed: %s", err)
}
- testFailingWrite(c, unicastV6, tcpip.ErrClosedForSend)
+ testFailingWrite(c, unicastV6, &tcpip.ErrClosedForSend{})
}
-func (c *testContext) checkEndpointWriteStats(incr uint64, want tcpip.TransportEndpointStats, err *tcpip.Error) {
+func (c *testContext) checkEndpointWriteStats(incr uint64, want tcpip.TransportEndpointStats, err tcpip.Error) {
got := c.ep.Stats().(*tcpip.TransportEndpointStats).Clone()
- switch err {
+ switch err.(type) {
case nil:
want.PacketsSent.IncrementBy(incr)
- case tcpip.ErrMessageTooLong, tcpip.ErrInvalidOptionValue:
+ case *tcpip.ErrMessageTooLong, *tcpip.ErrInvalidOptionValue:
want.WriteErrors.InvalidArgs.IncrementBy(incr)
- case tcpip.ErrClosedForSend:
+ case *tcpip.ErrClosedForSend:
want.WriteErrors.WriteClosed.IncrementBy(incr)
- case tcpip.ErrInvalidEndpointState:
+ case *tcpip.ErrInvalidEndpointState:
want.WriteErrors.InvalidEndpointState.IncrementBy(incr)
- case tcpip.ErrNoRoute, tcpip.ErrBroadcastDisabled, tcpip.ErrNetworkUnreachable:
+ case *tcpip.ErrNoRoute, *tcpip.ErrBroadcastDisabled, *tcpip.ErrNetworkUnreachable:
want.SendErrors.NoRoute.IncrementBy(incr)
default:
want.SendErrors.SendToNetworkFailed.IncrementBy(incr)
@@ -2332,11 +2341,11 @@ func (c *testContext) checkEndpointWriteStats(incr uint64, want tcpip.TransportE
}
}
-func (c *testContext) checkEndpointReadStats(incr uint64, want tcpip.TransportEndpointStats, err *tcpip.Error) {
+func (c *testContext) checkEndpointReadStats(incr uint64, want tcpip.TransportEndpointStats, err tcpip.Error) {
got := c.ep.Stats().(*tcpip.TransportEndpointStats).Clone()
- switch err {
- case nil, tcpip.ErrWouldBlock:
- case tcpip.ErrClosedForReceive:
+ switch err.(type) {
+ case nil, *tcpip.ErrWouldBlock:
+ case *tcpip.ErrClosedForReceive:
want.ReadErrors.ReadClosed.IncrementBy(incr)
default:
c.t.Errorf("Endpoint error missing stats update err %v", err)
@@ -2509,14 +2518,26 @@ func TestOutgoingSubnetBroadcast(t *testing.T) {
Port: 80,
}
opts := tcpip.WriteOptions{To: &to}
- expectedErrWithoutBcastOpt := tcpip.ErrBroadcastDisabled
+ expectedErrWithoutBcastOpt := func(err tcpip.Error) tcpip.Error {
+ if _, ok := err.(*tcpip.ErrBroadcastDisabled); ok {
+ return nil
+ }
+ return &tcpip.ErrBroadcastDisabled{}
+ }
if !test.requiresBroadcastOpt {
expectedErrWithoutBcastOpt = nil
}
r.Reset(data)
- if n, err := ep.Write(&r, opts); err != expectedErrWithoutBcastOpt {
- t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, %s)", opts, n, err, expectedErrWithoutBcastOpt)
+ {
+ n, err := ep.Write(&r, opts)
+ if expectedErrWithoutBcastOpt != nil {
+ if want := expectedErrWithoutBcastOpt(err); want != nil {
+ t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, %s)", opts, n, err, want)
+ }
+ } else if err != nil {
+ t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, nil)", opts, n, err)
+ }
}
ep.SocketOptions().SetBroadcast(true)
@@ -2529,8 +2550,15 @@ func TestOutgoingSubnetBroadcast(t *testing.T) {
ep.SocketOptions().SetBroadcast(false)
r.Reset(data)
- if n, err := ep.Write(&r, opts); err != expectedErrWithoutBcastOpt {
- t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, %s)", opts, n, err, expectedErrWithoutBcastOpt)
+ {
+ n, err := ep.Write(&r, opts)
+ if expectedErrWithoutBcastOpt != nil {
+ if want := expectedErrWithoutBcastOpt(err); want != nil {
+ t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, %s)", opts, n, err, want)
+ }
+ } else if err != nil {
+ t.Fatalf("got ep.Write(_, %#v) = (%d, %s), want = (_, nil)", opts, n, err)
+ }
}
})
}