mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Handle all codes for ICMPv4 destination unreachable message for TCP
Some hosts use iptables and are configured to respond an ICMP destination
unreachable message with a {host/net/admin}prohibited code instead of a RST.
Linux handles these codes correctly by aborting the handshake. This commit
adds handling of all possible codes of an ICMPv4 destination unreachable message
to bring gVisor to parity.
PiperOrigin-RevId: 496703813
This commit is contained in:
@@ -85,7 +85,7 @@ const (
|
||||
ENODATA
|
||||
ETIME
|
||||
ENOSR
|
||||
_ // Skip for ENOENT = ENONET.
|
||||
ENONET
|
||||
ENOPKG
|
||||
EREMOTE
|
||||
ENOLINK
|
||||
@@ -161,7 +161,6 @@ const (
|
||||
const (
|
||||
EWOULDBLOCK = EAGAIN
|
||||
EDEADLOCK = EDEADLK
|
||||
ENONET = ENOENT
|
||||
)
|
||||
|
||||
// errnos for internal errors.
|
||||
|
||||
@@ -54,6 +54,7 @@ var (
|
||||
ErrInvalidPortRange = New((&tcpip.ErrInvalidPortRange{}).String(), errno.EINVAL)
|
||||
ErrMulticastInputCannotBeOutput = New((&tcpip.ErrMulticastInputCannotBeOutput{}).String(), errno.EINVAL)
|
||||
ErrMissingRequiredFields = New((&tcpip.ErrMissingRequiredFields{}).String(), errno.EINVAL)
|
||||
ErrNoNet = New((&tcpip.ErrNoNet{}).String(), errno.ENONET)
|
||||
)
|
||||
|
||||
// TranslateNetstackError converts an error from the tcpip package to a sentry
|
||||
@@ -76,6 +77,10 @@ func TranslateNetstackError(err tcpip.Error) *Error {
|
||||
return ErrDuplicateAddress
|
||||
case *tcpip.ErrHostUnreachable:
|
||||
return ErrHostUnreachable
|
||||
case *tcpip.ErrHostDown:
|
||||
return ErrHostDown
|
||||
case *tcpip.ErrNoNet:
|
||||
return ErrNoNet
|
||||
case *tcpip.ErrAlreadyBound:
|
||||
return ErrAlreadyBound
|
||||
case *tcpip.ErrInvalidEndpointState:
|
||||
|
||||
@@ -394,6 +394,32 @@ func (*ErrHostUnreachable) IgnoreStats() bool {
|
||||
}
|
||||
func (*ErrHostUnreachable) String() string { return "no route to host" }
|
||||
|
||||
// ErrHostDown indicates that a destination host is down.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrHostDown struct{}
|
||||
|
||||
func (*ErrHostDown) isError() {}
|
||||
|
||||
// IgnoreStats implements Error.
|
||||
func (*ErrHostDown) IgnoreStats() bool {
|
||||
return false
|
||||
}
|
||||
func (*ErrHostDown) String() string { return "host is down" }
|
||||
|
||||
// ErrNoNet indicates that the host is not on the network.
|
||||
//
|
||||
// +stateify savable
|
||||
type ErrNoNet struct{}
|
||||
|
||||
func (*ErrNoNet) isError() {}
|
||||
|
||||
// IgnoreStats implements Error.
|
||||
func (*ErrNoNet) IgnoreStats() bool {
|
||||
return false
|
||||
}
|
||||
func (*ErrNoNet) String() string { return "machine is not on the network" }
|
||||
|
||||
// ErrNoSuchFile is used to indicate that ENOENT should be returned the to
|
||||
// calling application.
|
||||
//
|
||||
|
||||
@@ -106,14 +106,22 @@ const (
|
||||
// ICMP codes for ICMPv4 Destination Unreachable messages as defined in RFC 792,
|
||||
// RFC 1122 section 3.2.2.1 and RFC 1812 section 5.2.7.1.
|
||||
const (
|
||||
ICMPv4NetUnreachable ICMPv4Code = 0
|
||||
ICMPv4HostUnreachable ICMPv4Code = 1
|
||||
ICMPv4ProtoUnreachable ICMPv4Code = 2
|
||||
ICMPv4PortUnreachable ICMPv4Code = 3
|
||||
ICMPv4FragmentationNeeded ICMPv4Code = 4
|
||||
ICMPv4NetProhibited ICMPv4Code = 9
|
||||
ICMPv4HostProhibited ICMPv4Code = 10
|
||||
ICMPv4AdminProhibited ICMPv4Code = 13
|
||||
ICMPv4NetUnreachable ICMPv4Code = 0
|
||||
ICMPv4HostUnreachable ICMPv4Code = 1
|
||||
ICMPv4ProtoUnreachable ICMPv4Code = 2
|
||||
ICMPv4PortUnreachable ICMPv4Code = 3
|
||||
ICMPv4FragmentationNeeded ICMPv4Code = 4
|
||||
ICMPv4SourceRouteFailed ICMPv4Code = 5
|
||||
ICMPv4DestinationNetworkUnknown ICMPv4Code = 6
|
||||
ICMPv4DestinationHostUnknown ICMPv4Code = 7
|
||||
ICMPv4SourceHostIsolated ICMPv4Code = 8
|
||||
ICMPv4NetProhibited ICMPv4Code = 9
|
||||
ICMPv4HostProhibited ICMPv4Code = 10
|
||||
ICMPv4NetUnreachableForTos ICMPv4Code = 11
|
||||
ICMPv4HostUnreachableForTos ICMPv4Code = 12
|
||||
ICMPv4AdminProhibited ICMPv4Code = 13
|
||||
ICMPv4HostPrecedenceViolation ICMPv4Code = 14
|
||||
ICMPv4PrecedenceCutInEffect ICMPv4Code = 15
|
||||
)
|
||||
|
||||
// ICMPv4UnusedCode is a code to use in ICMP messages where no code is needed.
|
||||
|
||||
@@ -68,6 +68,28 @@ func (*icmpv4DestinationHostUnreachableSockError) Kind() stack.TransportErrorKin
|
||||
return stack.DestinationHostUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationNetUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationNetUnreachableSockError is an ICMPv4 Destination Net
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet was not able to reach the destination network.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationNetUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationNetUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4NetUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationNetUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationNetworkUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationPortUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationPortUnreachableSockError is an ICMPv4 Destination Port
|
||||
@@ -91,6 +113,89 @@ func (*icmpv4DestinationPortUnreachableSockError) Kind() stack.TransportErrorKin
|
||||
return stack.DestinationPortUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationProtoUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationProtoUnreachableSockError is an ICMPv4 Destination Protocol
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet reached the destination host, but the transport
|
||||
// protocol was not reachable
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationProtoUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationProtoUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4ProtoUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationProtoUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationProtoUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4SourceRouteFailedSockError)(nil)
|
||||
|
||||
// icmpv4SourceRouteFailedSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to source route failed.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4SourceRouteFailedSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4SourceRouteFailedSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4SourceRouteFailed)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4SourceRouteFailedSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.SourceRouteFailedTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4SourceHostIsolatedSockError)(nil)
|
||||
|
||||
// icmpv4SourceHostIsolatedSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to source host isolated (not on the network).
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4SourceHostIsolatedSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4SourceHostIsolatedSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4SourceHostIsolated)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4SourceHostIsolatedSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.SourceHostIsolatedTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationHostUnknownSockError)(nil)
|
||||
|
||||
// icmpv4DestinationHostUnknownSockError is an ICMPv4 Destination Unreachable
|
||||
// error due to destination host unknown/down.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationHostUnknownSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationHostUnknownSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4DestinationHostUnknown)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationHostUnknownSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationHostDownTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4FragmentationNeededSockError)(nil)
|
||||
|
||||
// icmpv4FragmentationNeededSockError is an ICMPv4 Destination Unreachable error
|
||||
@@ -362,7 +467,17 @@ func (e *endpoint) handleICMP(pkt stack.PacketBufferPtr) {
|
||||
mtu := h.MTU()
|
||||
code := h.Code()
|
||||
switch code {
|
||||
case header.ICMPv4HostUnreachable:
|
||||
case header.ICMPv4NetUnreachable,
|
||||
header.ICMPv4DestinationNetworkUnknown,
|
||||
header.ICMPv4NetUnreachableForTos,
|
||||
header.ICMPv4NetProhibited:
|
||||
e.handleControl(&icmpv4DestinationNetUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4HostUnreachable,
|
||||
header.ICMPv4HostProhibited,
|
||||
header.ICMPv4AdminProhibited,
|
||||
header.ICMPv4HostUnreachableForTos,
|
||||
header.ICMPv4HostPrecedenceViolation,
|
||||
header.ICMPv4PrecedenceCutInEffect:
|
||||
e.handleControl(&icmpv4DestinationHostUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4PortUnreachable:
|
||||
e.handleControl(&icmpv4DestinationPortUnreachableSockError{}, pkt)
|
||||
@@ -372,6 +487,14 @@ func (e *endpoint) handleICMP(pkt stack.PacketBufferPtr) {
|
||||
networkMTU = 0
|
||||
}
|
||||
e.handleControl(&icmpv4FragmentationNeededSockError{mtu: networkMTU}, pkt)
|
||||
case header.ICMPv4ProtoUnreachable:
|
||||
e.handleControl(&icmpv4DestinationProtoUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4SourceRouteFailed:
|
||||
e.handleControl(&icmpv4SourceRouteFailedSockError{}, pkt)
|
||||
case header.ICMPv4SourceHostIsolated:
|
||||
e.handleControl(&icmpv4SourceHostIsolatedSockError{}, pkt)
|
||||
case header.ICMPv4DestinationHostUnknown:
|
||||
e.handleControl(&icmpv4DestinationHostUnknownSockError{}, pkt)
|
||||
}
|
||||
case header.ICMPv4SrcQuench:
|
||||
received.srcQuench.Increment()
|
||||
|
||||
@@ -84,6 +84,21 @@ const (
|
||||
// DestinationNetworkUnreachableTransportError indicates that the destination
|
||||
// network was unreachable.
|
||||
DestinationNetworkUnreachableTransportError
|
||||
|
||||
// DestinationProtoUnreachableTransportError indicates that the destination
|
||||
// protocol was unreachable.
|
||||
DestinationProtoUnreachableTransportError
|
||||
|
||||
// SourceRouteFailedTransportError indicates that the source route failed.
|
||||
SourceRouteFailedTransportError
|
||||
|
||||
// SourceHostIsolatedTransportError indicates that the source machine is not
|
||||
// on the network.
|
||||
SourceHostIsolatedTransportError
|
||||
|
||||
// DestinationHostDownTransportError indicates that the destination host is
|
||||
// down.
|
||||
DestinationHostDownTransportError
|
||||
)
|
||||
|
||||
// TransportError is a marker interface for errors that may be handled by the
|
||||
|
||||
@@ -128,9 +128,15 @@ func maybeFailTimerHandler(e *endpoint, f func() tcpip.Error) func() {
|
||||
e.mu.Lock()
|
||||
if err := f(); err != nil {
|
||||
e.lastErrorMu.Lock()
|
||||
// If the handler timed out and we have a lastError recorded (maybe due
|
||||
// to an ICMP message received), promote it to be the hard error.
|
||||
if _, isTimeout := err.(*tcpip.ErrTimeout); e.lastError != nil && isTimeout {
|
||||
e.hardError = e.lastError
|
||||
} else {
|
||||
e.hardError = err
|
||||
}
|
||||
e.lastError = err
|
||||
e.lastErrorMu.Unlock()
|
||||
e.hardError = err
|
||||
e.cleanupLocked()
|
||||
e.setEndpointState(StateError)
|
||||
e.mu.Unlock()
|
||||
|
||||
@@ -2909,6 +2909,16 @@ func (e *endpoint) HandleError(transErr stack.TransportError, pkt stack.PacketBu
|
||||
e.onICMPError(&tcpip.ErrHostUnreachable{}, transErr, pkt)
|
||||
case stack.DestinationNetworkUnreachableTransportError:
|
||||
e.onICMPError(&tcpip.ErrNetworkUnreachable{}, transErr, pkt)
|
||||
case stack.DestinationPortUnreachableTransportError:
|
||||
e.onICMPError(&tcpip.ErrConnectionRefused{}, transErr, pkt)
|
||||
case stack.DestinationProtoUnreachableTransportError:
|
||||
e.onICMPError(&tcpip.ErrUnknownProtocolOption{}, transErr, pkt)
|
||||
case stack.SourceRouteFailedTransportError:
|
||||
e.onICMPError(&tcpip.ErrNotSupported{}, transErr, pkt)
|
||||
case stack.SourceHostIsolatedTransportError:
|
||||
e.onICMPError(&tcpip.ErrNoNet{}, transErr, pkt)
|
||||
case stack.DestinationHostDownTransportError:
|
||||
e.onICMPError(&tcpip.ErrHostDown{}, transErr, pkt)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,43 +33,131 @@ func init() {
|
||||
// an ICMP destination unreachable message is sent in response to the inital
|
||||
// SYN.
|
||||
func TestTCPSynSentUnreachable(t *testing.T) {
|
||||
// Create the DUT and connection.
|
||||
dut := testbench.NewDUT(t)
|
||||
clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
|
||||
port := uint16(9001)
|
||||
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port})
|
||||
defer conn.Close(t)
|
||||
for _, tt := range []struct {
|
||||
desc string
|
||||
code header.ICMPv4Code
|
||||
wantErrno unix.Errno
|
||||
}{
|
||||
{desc: "net_unreachable", code: header.ICMPv4NetUnreachable, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_unreachable", code: header.ICMPv4HostUnreachable, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "proto_unreachable", code: header.ICMPv4ProtoUnreachable, wantErrno: unix.ENOPROTOOPT},
|
||||
{desc: "port_unreachable", code: header.ICMPv4PortUnreachable, wantErrno: unix.ECONNREFUSED},
|
||||
{desc: "source_route_failed", code: header.ICMPv4SourceRouteFailed, wantErrno: unix.EOPNOTSUPP},
|
||||
{desc: "dest_net_unknown", code: header.ICMPv4DestinationNetworkUnknown, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "dest_host_unknown", code: header.ICMPv4DestinationHostUnknown, wantErrno: unix.EHOSTDOWN},
|
||||
{desc: "src_host_isolated", code: header.ICMPv4SourceHostIsolated, wantErrno: unix.ENONET},
|
||||
{desc: "net_prohibited", code: header.ICMPv4NetProhibited, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_prohibited", code: header.ICMPv4HostProhibited, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "net_unreachable_tos", code: header.ICMPv4NetUnreachableForTos, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_unreachable_tos", code: header.ICMPv4HostUnreachableForTos, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "admin_prohibited", code: header.ICMPv4AdminProhibited, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "precedence_violation", code: header.ICMPv4HostPrecedenceViolation, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "precedence_cut", code: header.ICMPv4PrecedenceCutInEffect, wantErrno: unix.EHOSTUNREACH},
|
||||
} {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
// Create the DUT and connection.
|
||||
dut := testbench.NewDUT(t)
|
||||
clientFD, clientPort := dut.CreateBoundSocket(t, unix.SOCK_STREAM|unix.SOCK_NONBLOCK, unix.IPPROTO_TCP, dut.Net.RemoteIPv4)
|
||||
port := uint16(9001)
|
||||
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{SrcPort: &port, DstPort: &clientPort}, testbench.TCP{SrcPort: &clientPort, DstPort: &port})
|
||||
defer conn.Close(t)
|
||||
|
||||
// Bring the DUT to SYN-SENT state with a non-blocking connect.
|
||||
sa := unix.SockaddrInet4{Port: int(port)}
|
||||
copy(sa.Addr[:], dut.Net.LocalIPv4)
|
||||
if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
|
||||
t.Errorf("got connect() = %v, want EINPROGRESS", err)
|
||||
sa := unix.SockaddrInet4{Port: int(port)}
|
||||
copy(sa.Addr[:], dut.Net.LocalIPv4)
|
||||
// Bring the DUT to SYN-SENT state with a non-blocking connect.
|
||||
if _, err := dut.ConnectWithErrno(context.Background(), t, clientFD, &sa); err != unix.EINPROGRESS {
|
||||
t.Errorf("got connect() = %v, want EINPROGRESS", err)
|
||||
}
|
||||
|
||||
// Get the SYN.
|
||||
tcp, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected SYN: %s", err)
|
||||
}
|
||||
|
||||
// Send a host unreachable message.
|
||||
icmpPayload := testbench.Layers{tcp.Prev(), tcp}
|
||||
bytes, err := icmpPayload.ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
|
||||
}
|
||||
|
||||
layers := conn.CreateFrame(t, nil)
|
||||
layers[len(layers)-1] = &testbench.ICMPv4{
|
||||
Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
|
||||
Code: testbench.ICMPv4Code(tt.code),
|
||||
Payload: bytes,
|
||||
}
|
||||
conn.SendFrameStateless(t, layers)
|
||||
|
||||
if err := getConnectError(t, &dut, clientFD); err != tt.wantErrno {
|
||||
t.Errorf("got connect() = %s(%d), want %s(%d)", err, err, tt.wantErrno, tt.wantErrno)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Get the SYN.
|
||||
tcp, err := conn.Expect(t, testbench.TCP{Flags: testbench.TCPFlags(header.TCPFlagSyn)}, time.Second)
|
||||
if err != nil {
|
||||
t.Fatalf("expected SYN: %s", err)
|
||||
}
|
||||
func TestTCPEstablishedUnreachable(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
desc string
|
||||
code header.ICMPv4Code
|
||||
wantErrno unix.Errno
|
||||
}{
|
||||
{desc: "net_unreachable", code: header.ICMPv4NetUnreachable, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_unreachable", code: header.ICMPv4HostUnreachable, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "proto_unreachable", code: header.ICMPv4ProtoUnreachable, wantErrno: unix.ENOPROTOOPT},
|
||||
{desc: "port_unreachable", code: header.ICMPv4PortUnreachable, wantErrno: unix.ECONNREFUSED},
|
||||
{desc: "source_route_failed", code: header.ICMPv4SourceRouteFailed, wantErrno: unix.EOPNOTSUPP},
|
||||
{desc: "dest_net_unknown", code: header.ICMPv4DestinationNetworkUnknown, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "dest_host_unknown", code: header.ICMPv4DestinationHostUnknown, wantErrno: unix.EHOSTDOWN},
|
||||
{desc: "src_host_isolated", code: header.ICMPv4SourceHostIsolated, wantErrno: unix.ENONET},
|
||||
{desc: "net_prohibited", code: header.ICMPv4NetProhibited, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_prohibited", code: header.ICMPv4HostProhibited, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "net_unreachable_tos", code: header.ICMPv4NetUnreachableForTos, wantErrno: unix.ENETUNREACH},
|
||||
{desc: "host_unreachable_tos", code: header.ICMPv4HostUnreachableForTos, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "admin_prohibited", code: header.ICMPv4AdminProhibited, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "precedence_violation", code: header.ICMPv4HostPrecedenceViolation, wantErrno: unix.EHOSTUNREACH},
|
||||
{desc: "precedence_cut", code: header.ICMPv4PrecedenceCutInEffect, wantErrno: unix.EHOSTUNREACH},
|
||||
} {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
dut := testbench.NewDUT(t)
|
||||
listenerFd, listenerPort := dut.CreateListener(t, unix.SOCK_STREAM, unix.IPPROTO_TCP, 1)
|
||||
defer dut.Close(t, listenerFd)
|
||||
conn := dut.Net.NewTCPIPv4(t, testbench.TCP{DstPort: &listenerPort}, testbench.TCP{SrcPort: &listenerPort})
|
||||
defer conn.Close(t)
|
||||
conn.Connect(t)
|
||||
acceptedFd, addr := dut.Accept(t, listenerFd)
|
||||
_ = addr
|
||||
|
||||
// Send a host unreachable message.
|
||||
icmpPayload := testbench.Layers{tcp.Prev(), tcp}
|
||||
bytes, err := icmpPayload.ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
|
||||
}
|
||||
dut.SetSockOptInt(t, acceptedFd, unix.IPPROTO_TCP, unix.TCP_USER_TIMEOUT, 50)
|
||||
dut.Send(t, acceptedFd, []byte("Hello"), 0)
|
||||
|
||||
layers := conn.CreateFrame(t, nil)
|
||||
layers[len(layers)-1] = &testbench.ICMPv4{
|
||||
Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
|
||||
Code: testbench.ICMPv4Code(header.ICMPv4HostUnreachable),
|
||||
Payload: bytes,
|
||||
}
|
||||
conn.SendFrameStateless(t, layers)
|
||||
if received, err := conn.ExpectData(t, &testbench.TCP{}, &testbench.Payload{Bytes: []byte("Hello")}, time.Second); err != nil {
|
||||
t.Fatalf("Expected data from DUT, got none: %s", err)
|
||||
} else {
|
||||
// Send a host unreachable message.
|
||||
icmpPayload := received[len(received)-3 : len(received)-1]
|
||||
bytes, err := icmpPayload.ToBytes()
|
||||
if err != nil {
|
||||
t.Fatalf("got icmpPayload.ToBytes() = (_, %s), want = (_, nil)", err)
|
||||
}
|
||||
|
||||
if err := getConnectError(t, &dut, clientFD); err != unix.EHOSTUNREACH {
|
||||
t.Errorf("got connect() = %v, want EHOSTUNREACH", err)
|
||||
layers := conn.CreateFrame(t, nil)
|
||||
layers[len(layers)-1] = &testbench.ICMPv4{
|
||||
Type: testbench.ICMPv4Type(header.ICMPv4DstUnreachable),
|
||||
Code: testbench.ICMPv4Code(tt.code),
|
||||
Payload: bytes,
|
||||
}
|
||||
conn.SendFrameStateless(t, layers)
|
||||
}
|
||||
|
||||
dut.Send(t, acceptedFd, []byte("Hello"), 0)
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
if _, err := dut.SendWithErrno(context.Background(), t, acceptedFd, []byte("Hello"), 0); err != tt.wantErrno {
|
||||
t.Fatalf("got send() = %s(%d), want %s(%d)", err, err, tt.wantErrno, tt.wantErrno)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user