Refactor HandleControlPacket/SockError
...to remove the need for the transport layer to deduce the type of error it received. Rename HandleControlPacket to HandleError as HandleControlPacket only handles errors. tcpip.SockError now holds a tcpip.SockErrorCause interface that different errors can implement. PiperOrigin-RevId: 354994306
This commit is contained in:
parent
cbcebfea80
commit
ebd3912c0f
|
@ -156,3 +156,4 @@ analyzers:
|
|||
internal:
|
||||
exclude:
|
||||
- pkg/sentry/fs/fdpipe/pipe_opener_test.go # False positive.
|
||||
- pkg/tcpip/tests/integration/link_resolution_test.go # False positive.
|
||||
|
|
|
@ -2666,9 +2666,9 @@ func (s *socketOpsCommon) dequeueErr() *tcpip.SockError {
|
|||
}
|
||||
|
||||
// Update socket error to reflect ICMP errors in queue.
|
||||
if nextErr := so.PeekErr(); nextErr != nil && nextErr.ErrOrigin.IsICMPErr() {
|
||||
if nextErr := so.PeekErr(); nextErr != nil && nextErr.Cause.Origin().IsICMPErr() {
|
||||
so.SetLastError(nextErr.Err)
|
||||
} else if err.ErrOrigin.IsICMPErr() {
|
||||
} else if err.Cause.Origin().IsICMPErr() {
|
||||
so.SetLastError(nil)
|
||||
}
|
||||
return err
|
||||
|
|
|
@ -81,10 +81,10 @@ func sockErrCmsgToLinux(sockErr *tcpip.SockError) linux.SockErrCMsg {
|
|||
|
||||
ee := linux.SockExtendedErr{
|
||||
Errno: uint32(syserr.TranslateNetstackError(sockErr.Err).ToLinux().Number()),
|
||||
Origin: errOriginToLinux(sockErr.ErrOrigin),
|
||||
Type: sockErr.ErrType,
|
||||
Code: sockErr.ErrCode,
|
||||
Info: sockErr.ErrInfo,
|
||||
Origin: errOriginToLinux(sockErr.Cause.Origin()),
|
||||
Type: sockErr.Cause.Type(),
|
||||
Code: sockErr.Cause.Code(),
|
||||
Info: sockErr.Cause.Info(),
|
||||
}
|
||||
|
||||
switch sockErr.NetProto {
|
||||
|
|
|
@ -16,7 +16,6 @@ package header
|
|||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
|
@ -208,16 +207,3 @@ func ICMPv4Checksum(h ICMPv4, vv buffer.VectorisedView) uint16 {
|
|||
|
||||
return ^xsum
|
||||
}
|
||||
|
||||
// ICMPOriginFromNetProto returns the appropriate SockErrOrigin to use when
|
||||
// a packet having a `net` header causing an ICMP error.
|
||||
func ICMPOriginFromNetProto(net tcpip.NetworkProtocolNumber) tcpip.SockErrOrigin {
|
||||
switch net {
|
||||
case IPv4ProtocolNumber:
|
||||
return tcpip.SockExtErrorOriginICMP
|
||||
case IPv6ProtocolNumber:
|
||||
return tcpip.SockExtErrorOriginICMP6
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported net proto to extract ICMP error origin: %d", net))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,14 @@ var localIPv6AddrWithPrefix = tcpip.AddressWithPrefix{
|
|||
PrefixLen: 120,
|
||||
}
|
||||
|
||||
type transportError struct {
|
||||
origin tcpip.SockErrOrigin
|
||||
typ uint8
|
||||
code uint8
|
||||
info uint32
|
||||
kind stack.TransportErrorKind
|
||||
}
|
||||
|
||||
// testObject implements two interfaces: LinkEndpoint and TransportDispatcher.
|
||||
// The former is used to pretend that it's a link endpoint so that we can
|
||||
// inspect packets written by the network endpoints. The latter is used to
|
||||
|
@ -74,8 +82,7 @@ type testObject struct {
|
|||
srcAddr tcpip.Address
|
||||
dstAddr tcpip.Address
|
||||
v4 bool
|
||||
typ stack.ControlType
|
||||
extra uint32
|
||||
transErr transportError
|
||||
|
||||
dataCalls int
|
||||
controlCalls int
|
||||
|
@ -119,16 +126,23 @@ func (t *testObject) DeliverTransportPacket(protocol tcpip.TransportProtocolNumb
|
|||
return stack.TransportPacketHandled
|
||||
}
|
||||
|
||||
// DeliverTransportControlPacket is called by network endpoints after parsing
|
||||
// DeliverTransportError is called by network endpoints after parsing
|
||||
// incoming control (ICMP) packets. This is used by the test object to verify
|
||||
// that the results of the parsing are expected.
|
||||
func (t *testObject) DeliverTransportControlPacket(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
|
||||
func (t *testObject) DeliverTransportError(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, transErr stack.TransportError, pkt *stack.PacketBuffer) {
|
||||
t.checkValues(trans, pkt.Data, remote, local)
|
||||
if typ != t.typ {
|
||||
t.t.Errorf("typ = %v, want %v", typ, t.typ)
|
||||
}
|
||||
if extra != t.extra {
|
||||
t.t.Errorf("extra = %v, want %v", extra, t.extra)
|
||||
if diff := cmp.Diff(
|
||||
t.transErr,
|
||||
transportError{
|
||||
origin: transErr.Origin(),
|
||||
typ: transErr.Type(),
|
||||
code: transErr.Code(),
|
||||
info: transErr.Info(),
|
||||
kind: transErr.Kind(),
|
||||
},
|
||||
cmp.AllowUnexported(transportError{}),
|
||||
); diff != "" {
|
||||
t.t.Errorf("transport error mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
t.controlCalls++
|
||||
}
|
||||
|
@ -702,24 +716,81 @@ func TestReceive(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIPv4ReceiveControl(t *testing.T) {
|
||||
const mtu = 0xbeef - header.IPv4MinimumSize
|
||||
const (
|
||||
mtu = 0xbeef - header.IPv4MinimumSize
|
||||
dataLen = 8
|
||||
)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
expectedCount int
|
||||
fragmentOffset uint16
|
||||
code header.ICMPv4Code
|
||||
expectedTyp stack.ControlType
|
||||
expectedExtra uint32
|
||||
transErr transportError
|
||||
trunc int
|
||||
}{
|
||||
{"FragmentationNeeded", 1, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 0},
|
||||
{"Truncated (10 bytes missing)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 10},
|
||||
{"Truncated (missing IPv4 header)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, header.IPv4MinimumSize + 8},
|
||||
{"Truncated (missing 'extra info')", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, 4 + header.IPv4MinimumSize + 8},
|
||||
{"Truncated (missing ICMP header)", 0, 0, header.ICMPv4FragmentationNeeded, stack.ControlPacketTooBig, mtu, header.ICMPv4MinimumSize + header.IPv4MinimumSize + 8},
|
||||
{"Port unreachable", 1, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Non-zero fragment offset", 0, 100, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Zero-length packet", 0, 0, header.ICMPv4PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv4MinimumSize + header.ICMPv4MinimumSize + 8},
|
||||
{
|
||||
name: "FragmentationNeeded",
|
||||
expectedCount: 1,
|
||||
fragmentOffset: 0,
|
||||
code: header.ICMPv4FragmentationNeeded,
|
||||
transErr: transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP,
|
||||
typ: uint8(header.ICMPv4DstUnreachable),
|
||||
code: uint8(header.ICMPv4FragmentationNeeded),
|
||||
info: mtu,
|
||||
kind: stack.PacketTooBigTransportError,
|
||||
},
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Truncated (missing IPv4 header)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: 0,
|
||||
code: header.ICMPv4FragmentationNeeded,
|
||||
trunc: header.IPv4MinimumSize + header.ICMPv4MinimumSize,
|
||||
},
|
||||
{
|
||||
name: "Truncated (partial offending packet's IP header)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: 0,
|
||||
code: header.ICMPv4FragmentationNeeded,
|
||||
trunc: header.IPv4MinimumSize + header.ICMPv4MinimumSize + header.IPv4MinimumSize - 1,
|
||||
},
|
||||
{
|
||||
name: "Truncated (partial offending packet's data)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: 0,
|
||||
code: header.ICMPv4FragmentationNeeded,
|
||||
trunc: header.ICMPv4MinimumSize + header.ICMPv4MinimumSize + header.IPv4MinimumSize + dataLen - 1,
|
||||
},
|
||||
{
|
||||
name: "Port unreachable",
|
||||
expectedCount: 1,
|
||||
fragmentOffset: 0,
|
||||
code: header.ICMPv4PortUnreachable,
|
||||
transErr: transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP,
|
||||
typ: uint8(header.ICMPv4DstUnreachable),
|
||||
code: uint8(header.ICMPv4PortUnreachable),
|
||||
kind: stack.DestinationPortUnreachableTransportError,
|
||||
},
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Non-zero fragment offset",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: 100,
|
||||
code: header.ICMPv4PortUnreachable,
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Zero-length packet",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: 100,
|
||||
code: header.ICMPv4PortUnreachable,
|
||||
trunc: 2*header.IPv4MinimumSize + header.ICMPv4MinimumSize + dataLen,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
@ -738,7 +809,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
|
|||
}
|
||||
|
||||
const dataOffset = header.IPv4MinimumSize*2 + header.ICMPv4MinimumSize
|
||||
view := buffer.NewView(dataOffset + 8)
|
||||
view := buffer.NewView(dataOffset + dataLen)
|
||||
|
||||
// Create the outer IPv4 header.
|
||||
ip := header.IPv4(view)
|
||||
|
@ -785,8 +856,7 @@ func TestIPv4ReceiveControl(t *testing.T) {
|
|||
nic.testObject.srcAddr = remoteIPv4Addr
|
||||
nic.testObject.dstAddr = localIPv4Addr
|
||||
nic.testObject.contents = view[dataOffset:]
|
||||
nic.testObject.typ = c.expectedTyp
|
||||
nic.testObject.extra = c.expectedExtra
|
||||
nic.testObject.transErr = c.transErr
|
||||
|
||||
addressableEndpoint, ok := ep.(stack.AddressableEndpoint)
|
||||
if !ok {
|
||||
|
@ -953,30 +1023,112 @@ func TestIPv6Send(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIPv6ReceiveControl(t *testing.T) {
|
||||
const (
|
||||
mtu = 0xffff
|
||||
outerSrcAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa"
|
||||
dataLen = 8
|
||||
)
|
||||
|
||||
newUint16 := func(v uint16) *uint16 { return &v }
|
||||
|
||||
const mtu = 0xffff
|
||||
const outerSrcAddr = "\x0a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa"
|
||||
portUnreachableTransErr := transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP6,
|
||||
typ: uint8(header.ICMPv6DstUnreachable),
|
||||
code: uint8(header.ICMPv6PortUnreachable),
|
||||
kind: stack.DestinationPortUnreachableTransportError,
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
expectedCount int
|
||||
fragmentOffset *uint16
|
||||
typ header.ICMPv6Type
|
||||
code header.ICMPv6Code
|
||||
expectedTyp stack.ControlType
|
||||
expectedExtra uint32
|
||||
transErr transportError
|
||||
trunc int
|
||||
}{
|
||||
{"PacketTooBig", 1, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 0},
|
||||
{"Truncated (10 bytes missing)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 10},
|
||||
{"Truncated (missing IPv6 header)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, header.IPv6MinimumSize + 8},
|
||||
{"Truncated PacketTooBig (missing 'extra info')", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, 4 + header.IPv6MinimumSize + 8},
|
||||
{"Truncated (missing ICMP header)", 0, nil, header.ICMPv6PacketTooBig, 0, stack.ControlPacketTooBig, mtu, header.ICMPv6PacketTooBigMinimumSize + header.IPv6MinimumSize + 8},
|
||||
{"Port unreachable", 1, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Truncated DstUnreachable (missing 'extra info')", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 4 + header.IPv6MinimumSize + 8},
|
||||
{"Fragmented, zero offset", 1, newUint16(0), header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Non-zero fragment offset", 0, newUint16(100), header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 0},
|
||||
{"Zero-length packet", 0, nil, header.ICMPv6DstUnreachable, header.ICMPv6PortUnreachable, stack.ControlPortUnreachable, 0, 2*header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + 8},
|
||||
{
|
||||
name: "PacketTooBig",
|
||||
expectedCount: 1,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6PacketTooBig,
|
||||
code: header.ICMPv6UnusedCode,
|
||||
transErr: transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP6,
|
||||
typ: uint8(header.ICMPv6PacketTooBig),
|
||||
code: uint8(header.ICMPv6UnusedCode),
|
||||
info: mtu,
|
||||
kind: stack.PacketTooBigTransportError,
|
||||
},
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Truncated (missing offending packet's IPv6 header)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6PacketTooBig,
|
||||
code: header.ICMPv6UnusedCode,
|
||||
trunc: header.IPv6MinimumSize + header.ICMPv6PacketTooBigMinimumSize,
|
||||
},
|
||||
{
|
||||
name: "Truncated PacketTooBig (partial offending packet's IPv6 header)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6PacketTooBig,
|
||||
code: header.ICMPv6UnusedCode,
|
||||
trunc: header.IPv6MinimumSize + header.ICMPv6PacketTooBigMinimumSize + header.IPv6MinimumSize - 1,
|
||||
},
|
||||
{
|
||||
name: "Truncated (partial offending packet's data)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6PacketTooBig,
|
||||
code: header.ICMPv6UnusedCode,
|
||||
trunc: header.IPv6MinimumSize + header.ICMPv6PacketTooBigMinimumSize + header.IPv6MinimumSize + dataLen - 1,
|
||||
},
|
||||
{
|
||||
name: "Port unreachable",
|
||||
expectedCount: 1,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
code: header.ICMPv6PortUnreachable,
|
||||
transErr: portUnreachableTransErr,
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Truncated DstPortUnreachable (partial offending packet's IP header)",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
code: header.ICMPv6PortUnreachable,
|
||||
trunc: header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + header.IPv6MinimumSize - 1,
|
||||
},
|
||||
{
|
||||
name: "DstPortUnreachable for Fragmented, zero offset",
|
||||
expectedCount: 1,
|
||||
fragmentOffset: newUint16(0),
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
code: header.ICMPv6PortUnreachable,
|
||||
transErr: portUnreachableTransErr,
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "DstPortUnreachable for Non-zero fragment offset",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: newUint16(100),
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
code: header.ICMPv6PortUnreachable,
|
||||
transErr: portUnreachableTransErr,
|
||||
trunc: 0,
|
||||
},
|
||||
{
|
||||
name: "Zero-length packet",
|
||||
expectedCount: 0,
|
||||
fragmentOffset: nil,
|
||||
typ: header.ICMPv6DstUnreachable,
|
||||
code: header.ICMPv6PortUnreachable,
|
||||
trunc: 2*header.IPv6MinimumSize + header.ICMPv6DstUnreachableMinimumSize + dataLen,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
|
@ -998,7 +1150,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
|
|||
if c.fragmentOffset != nil {
|
||||
dataOffset += header.IPv6FragmentHeaderSize
|
||||
}
|
||||
view := buffer.NewView(dataOffset + 8)
|
||||
view := buffer.NewView(dataOffset + dataLen)
|
||||
|
||||
// Create the outer IPv6 header.
|
||||
ip := header.IPv6(view)
|
||||
|
@ -1049,8 +1201,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
|
|||
nic.testObject.srcAddr = remoteIPv6Addr
|
||||
nic.testObject.dstAddr = localIPv6Addr
|
||||
nic.testObject.contents = view[dataOffset:]
|
||||
nic.testObject.typ = c.expectedTyp
|
||||
nic.testObject.extra = c.expectedExtra
|
||||
nic.testObject.transErr = c.transErr
|
||||
|
||||
// Set ICMPv6 checksum.
|
||||
icmp.SetChecksum(header.ICMPv6Checksum(icmp, outerSrcAddr, localIPv6Addr, buffer.VectorisedView{}))
|
||||
|
|
|
@ -23,11 +23,108 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// icmpv4DestinationUnreachableSockError is a general ICMPv4 Destination
|
||||
// Unreachable error.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationUnreachableSockError struct{}
|
||||
|
||||
// Origin implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Origin() tcpip.SockErrOrigin {
|
||||
return tcpip.SockExtErrorOriginICMP
|
||||
}
|
||||
|
||||
// Type implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Type() uint8 {
|
||||
return uint8(header.ICMPv4DstUnreachable)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationUnreachableSockError) Info() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationHostUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationHostUnreachableSockError is an ICMPv4 Destination Host
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet was not able to reach the destination host.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationHostUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationHostUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4HostUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationHostUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationHostUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4DestinationPortUnreachableSockError)(nil)
|
||||
|
||||
// icmpv4DestinationPortUnreachableSockError is an ICMPv4 Destination Port
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet reached the destination host, but the transport
|
||||
// protocol was not active on the destination port.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4DestinationPortUnreachableSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4DestinationPortUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4PortUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4DestinationPortUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationPortUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv4FragmentationNeededSockError)(nil)
|
||||
|
||||
// icmpv4FragmentationNeededSockError is an ICMPv4 Destination Unreachable error
|
||||
// due to fragmentation being required but the packet was set to not be
|
||||
// fragmented.
|
||||
//
|
||||
// It indicates that a link exists on the path to the destination with an MTU
|
||||
// that is too small to carry the packet.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv4FragmentationNeededSockError struct {
|
||||
icmpv4DestinationUnreachableSockError
|
||||
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv4FragmentationNeededSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv4FragmentationNeeded)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (e *icmpv4FragmentationNeededSockError) Info() uint32 {
|
||||
return e.mtu
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv4FragmentationNeededSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.PacketTooBigTransportError
|
||||
}
|
||||
|
||||
// handleControl handles the case when an ICMP error packet contains the headers
|
||||
// of the original packet that caused the ICMP one to be sent. This information
|
||||
// is used to find out which transport endpoint must be notified about the ICMP
|
||||
// packet. We only expect the payload, not the enclosing ICMP packet.
|
||||
func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
|
||||
func (e *endpoint) handleControl(errInfo stack.TransportError, pkt *stack.PacketBuffer) {
|
||||
h, ok := pkt.Data.PullUp(header.IPv4MinimumSize)
|
||||
if !ok {
|
||||
return
|
||||
|
@ -54,10 +151,10 @@ func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, pkt *stack
|
|||
return
|
||||
}
|
||||
|
||||
// Skip the ip header, then deliver control message.
|
||||
// Skip the ip header, then deliver the error.
|
||||
pkt.Data.TrimFront(hlen)
|
||||
p := hdr.TransportProtocol()
|
||||
e.dispatcher.DeliverTransportControlPacket(srcAddr, hdr.DestinationAddress(), ProtocolNumber, p, typ, extra, pkt)
|
||||
e.dispatcher.DeliverTransportError(srcAddr, hdr.DestinationAddress(), ProtocolNumber, p, errInfo, pkt)
|
||||
}
|
||||
|
||||
func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
||||
|
@ -222,19 +319,16 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer) {
|
|||
pkt.Data.TrimFront(header.ICMPv4MinimumSize)
|
||||
switch h.Code() {
|
||||
case header.ICMPv4HostUnreachable:
|
||||
e.handleControl(stack.ControlNoRoute, 0, pkt)
|
||||
|
||||
e.handleControl(&icmpv4DestinationHostUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4PortUnreachable:
|
||||
e.handleControl(stack.ControlPortUnreachable, 0, pkt)
|
||||
|
||||
e.handleControl(&icmpv4DestinationPortUnreachableSockError{}, pkt)
|
||||
case header.ICMPv4FragmentationNeeded:
|
||||
networkMTU, err := calculateNetworkMTU(uint32(h.MTU()), header.IPv4MinimumSize)
|
||||
if err != nil {
|
||||
networkMTU = 0
|
||||
}
|
||||
e.handleControl(stack.ControlPacketTooBig, networkMTU, pkt)
|
||||
e.handleControl(&icmpv4FragmentationNeededSockError{mtu: networkMTU}, pkt)
|
||||
}
|
||||
|
||||
case header.ICMPv4SrcQuench:
|
||||
received.srcQuench.Increment()
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ func (e *endpoint) HandleLinkResolutionFailure(pkt *stack.PacketBuffer) {
|
|||
// Use the same control type as an ICMPv4 destination host unreachable error
|
||||
// since the host is considered unreachable if we cannot resolve the link
|
||||
// address to the next hop.
|
||||
e.handleControl(stack.ControlNoRoute, 0, pkt)
|
||||
e.handleControl(&icmpv4DestinationHostUnreachableSockError{}, pkt)
|
||||
}
|
||||
|
||||
// NewEndpoint creates a new ipv4 endpoint.
|
||||
|
|
|
@ -23,11 +23,136 @@ import (
|
|||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
)
|
||||
|
||||
// icmpv6DestinationUnreachableSockError is a general ICMPv6 Destination
|
||||
// Unreachable error.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv6DestinationUnreachableSockError struct{}
|
||||
|
||||
// Origin implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationUnreachableSockError) Origin() tcpip.SockErrOrigin {
|
||||
return tcpip.SockExtErrorOriginICMP6
|
||||
}
|
||||
|
||||
// Type implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationUnreachableSockError) Type() uint8 {
|
||||
return uint8(header.ICMPv6DstUnreachable)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationUnreachableSockError) Info() uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv6DestinationNetworkUnreachableSockError)(nil)
|
||||
|
||||
// icmpv6DestinationNetworkUnreachableSockError is an ICMPv6 Destination Network
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that the destination network is unreachable.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv6DestinationNetworkUnreachableSockError struct {
|
||||
icmpv6DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationNetworkUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv6NetworkUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv6DestinationNetworkUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationNetworkUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv6DestinationPortUnreachableSockError)(nil)
|
||||
|
||||
// icmpv6DestinationPortUnreachableSockError is an ICMPv6 Destination Port
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet reached the destination host, but the transport
|
||||
// protocol was not active on the destination port.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv6DestinationPortUnreachableSockError struct {
|
||||
icmpv6DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationPortUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv6PortUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv6DestinationPortUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationPortUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv6DestinationAddressUnreachableSockError)(nil)
|
||||
|
||||
// icmpv6DestinationAddressUnreachableSockError is an ICMPv6 Destination Address
|
||||
// Unreachable error.
|
||||
//
|
||||
// It indicates that a packet was not able to reach the destination.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv6DestinationAddressUnreachableSockError struct {
|
||||
icmpv6DestinationUnreachableSockError
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv6DestinationAddressUnreachableSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv6AddressUnreachable)
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv6DestinationAddressUnreachableSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.DestinationHostUnreachableTransportError
|
||||
}
|
||||
|
||||
var _ stack.TransportError = (*icmpv6PacketTooBigSockError)(nil)
|
||||
|
||||
// icmpv6PacketTooBigSockError is an ICMPv6 Packet Too Big error.
|
||||
//
|
||||
// It indicates that a link exists on the path to the destination with an MTU
|
||||
// that is too small to carry the packet.
|
||||
//
|
||||
// +stateify savable
|
||||
type icmpv6PacketTooBigSockError struct {
|
||||
mtu uint32
|
||||
}
|
||||
|
||||
// Origin implements tcpip.SockErrorCause.
|
||||
func (*icmpv6PacketTooBigSockError) Origin() tcpip.SockErrOrigin {
|
||||
return tcpip.SockExtErrorOriginICMP6
|
||||
}
|
||||
|
||||
// Type implements tcpip.SockErrorCause.
|
||||
func (*icmpv6PacketTooBigSockError) Type() uint8 {
|
||||
return uint8(header.ICMPv6PacketTooBig)
|
||||
}
|
||||
|
||||
// Code implements tcpip.SockErrorCause.
|
||||
func (*icmpv6PacketTooBigSockError) Code() uint8 {
|
||||
return uint8(header.ICMPv6UnusedCode)
|
||||
}
|
||||
|
||||
// Info implements tcpip.SockErrorCause.
|
||||
func (e *icmpv6PacketTooBigSockError) Info() uint32 {
|
||||
return e.mtu
|
||||
}
|
||||
|
||||
// Kind implements stack.TransportError.
|
||||
func (*icmpv6PacketTooBigSockError) Kind() stack.TransportErrorKind {
|
||||
return stack.PacketTooBigTransportError
|
||||
}
|
||||
|
||||
// handleControl handles the case when an ICMP packet contains the headers of
|
||||
// the original packet that caused the ICMP one to be sent. This information is
|
||||
// used to find out which transport endpoint must be notified about the ICMP
|
||||
// packet.
|
||||
func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, pkt *stack.PacketBuffer) {
|
||||
func (e *endpoint) handleControl(transErr stack.TransportError, pkt *stack.PacketBuffer) {
|
||||
h, ok := pkt.Data.PullUp(header.IPv6MinimumSize)
|
||||
if !ok {
|
||||
return
|
||||
|
@ -67,8 +192,7 @@ func (e *endpoint) handleControl(typ stack.ControlType, extra uint32, pkt *stack
|
|||
p = fragHdr.TransportProtocol()
|
||||
}
|
||||
|
||||
// Deliver the control packet to the transport endpoint.
|
||||
e.dispatcher.DeliverTransportControlPacket(src, hdr.DestinationAddress(), ProtocolNumber, p, typ, extra, pkt)
|
||||
e.dispatcher.DeliverTransportError(src, hdr.DestinationAddress(), ProtocolNumber, p, transErr, pkt)
|
||||
}
|
||||
|
||||
// getLinkAddrOption searches NDP options for a given link address option using
|
||||
|
@ -175,7 +299,7 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
|
|||
if err != nil {
|
||||
networkMTU = 0
|
||||
}
|
||||
e.handleControl(stack.ControlPacketTooBig, networkMTU, pkt)
|
||||
e.handleControl(&icmpv6PacketTooBigSockError{mtu: networkMTU}, pkt)
|
||||
|
||||
case header.ICMPv6DstUnreachable:
|
||||
received.dstUnreachable.Increment()
|
||||
|
@ -187,11 +311,10 @@ func (e *endpoint) handleICMP(pkt *stack.PacketBuffer, hasFragmentHeader bool) {
|
|||
pkt.Data.TrimFront(header.ICMPv6DstUnreachableMinimumSize)
|
||||
switch header.ICMPv6(hdr).Code() {
|
||||
case header.ICMPv6NetworkUnreachable:
|
||||
e.handleControl(stack.ControlNetworkUnreachable, 0, pkt)
|
||||
e.handleControl(&icmpv6DestinationNetworkUnreachableSockError{}, pkt)
|
||||
case header.ICMPv6PortUnreachable:
|
||||
e.handleControl(stack.ControlPortUnreachable, 0, pkt)
|
||||
e.handleControl(&icmpv6DestinationPortUnreachableSockError{}, pkt)
|
||||
}
|
||||
|
||||
case header.ICMPv6NeighborSolicit:
|
||||
received.neighborSolicit.Increment()
|
||||
if !isNDPValid() || pkt.Data.Size() < header.ICMPv6NeighborSolicitMinimumSize {
|
||||
|
|
|
@ -235,7 +235,7 @@ func (e *endpoint) HandleLinkResolutionFailure(pkt *stack.PacketBuffer) {
|
|||
})
|
||||
pkt.NICID = e.nic.ID()
|
||||
pkt.NetworkProtocolNumber = ProtocolNumber
|
||||
e.handleControl(stack.ControlAddressUnreachable, 0, pkt)
|
||||
e.handleControl(&icmpv6DestinationAddressUnreachableSockError{}, pkt)
|
||||
}
|
||||
|
||||
// onAddressAssignedLocked handles an address being assigned.
|
||||
|
|
|
@ -473,6 +473,48 @@ func (origin SockErrOrigin) IsICMPErr() bool {
|
|||
return origin == SockExtErrorOriginICMP || origin == SockExtErrorOriginICMP6
|
||||
}
|
||||
|
||||
// SockErrorCause is the cause of a socket error.
|
||||
type SockErrorCause interface {
|
||||
// Origin is the source of the error.
|
||||
Origin() SockErrOrigin
|
||||
|
||||
// Type is the origin specific type of error.
|
||||
Type() uint8
|
||||
|
||||
// Code is the origin and type specific error code.
|
||||
Code() uint8
|
||||
|
||||
// Info is any extra information about the error.
|
||||
Info() uint32
|
||||
}
|
||||
|
||||
// LocalSockError is a socket error that originated from the local host.
|
||||
//
|
||||
// +stateify savable
|
||||
type LocalSockError struct {
|
||||
info uint32
|
||||
}
|
||||
|
||||
// Origin implements SockErrorCause.
|
||||
func (*LocalSockError) Origin() SockErrOrigin {
|
||||
return SockExtErrorOriginLocal
|
||||
}
|
||||
|
||||
// Type implements SockErrorCause.
|
||||
func (*LocalSockError) Type() uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Code implements SockErrorCause.
|
||||
func (*LocalSockError) Code() uint8 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Info implements SockErrorCause.
|
||||
func (l *LocalSockError) Info() uint32 {
|
||||
return l.info
|
||||
}
|
||||
|
||||
// SockError represents a queue entry in the per-socket error queue.
|
||||
//
|
||||
// +stateify savable
|
||||
|
@ -481,14 +523,8 @@ type SockError struct {
|
|||
|
||||
// Err is the error caused by the errant packet.
|
||||
Err Error
|
||||
// ErrOrigin indicates the error origin.
|
||||
ErrOrigin SockErrOrigin
|
||||
// ErrType is the type in the ICMP header.
|
||||
ErrType uint8
|
||||
// ErrCode is the code in the ICMP header.
|
||||
ErrCode uint8
|
||||
// ErrInfo is additional info about the error.
|
||||
ErrInfo uint32
|
||||
// Cause is the detailed cause of the error.
|
||||
Cause SockErrorCause
|
||||
|
||||
// Payload is the errant packet's payload.
|
||||
Payload []byte
|
||||
|
@ -540,12 +576,11 @@ func (so *SocketOptions) QueueErr(err *SockError) {
|
|||
// QueueLocalErr queues a local error onto the local queue.
|
||||
func (so *SocketOptions) QueueLocalErr(err Error, net NetworkProtocolNumber, info uint32, dst FullAddress, payload []byte) {
|
||||
so.QueueErr(&SockError{
|
||||
Err: err,
|
||||
ErrOrigin: SockExtErrorOriginLocal,
|
||||
ErrInfo: info,
|
||||
Payload: payload,
|
||||
Dst: dst,
|
||||
NetProto: net,
|
||||
Err: err,
|
||||
Cause: &LocalSockError{info: info},
|
||||
Payload: payload,
|
||||
Dst: dst,
|
||||
NetProto: net,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -911,9 +911,8 @@ func (n *NIC) DeliverTransportPacket(protocol tcpip.TransportProtocolNumber, pkt
|
|||
}
|
||||
}
|
||||
|
||||
// DeliverTransportControlPacket delivers control packets to the appropriate
|
||||
// transport protocol endpoint.
|
||||
func (n *NIC) DeliverTransportControlPacket(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ ControlType, extra uint32, pkt *PacketBuffer) {
|
||||
// DeliverTransportError implements TransportDispatcher.
|
||||
func (n *NIC) DeliverTransportError(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, transErr TransportError, pkt *PacketBuffer) {
|
||||
state, ok := n.stack.transportProtocols[trans]
|
||||
if !ok {
|
||||
return
|
||||
|
@ -935,7 +934,7 @@ func (n *NIC) DeliverTransportControlPacket(local, remote tcpip.Address, net tcp
|
|||
}
|
||||
|
||||
id := TransportEndpointID{srcPort, local, dstPort, remote}
|
||||
if n.stack.demux.deliverControlPacket(n, net, trans, typ, extra, pkt, id) {
|
||||
if n.stack.demux.deliverError(n, net, trans, transErr, pkt, id) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,31 +49,6 @@ type TransportEndpointID struct {
|
|||
RemoteAddress tcpip.Address
|
||||
}
|
||||
|
||||
// ControlType is the type of network control message.
|
||||
type ControlType int
|
||||
|
||||
// The following are the allowed values for ControlType values.
|
||||
// TODO(http://gvisor.dev/issue/3210): Support time exceeded messages.
|
||||
const (
|
||||
// ControlAddressUnreachable indicates that an IPv6 packet did not reach its
|
||||
// destination as the destination address was unreachable.
|
||||
//
|
||||
// This maps to the ICMPv6 Destination Ureachable Code 3 error; see
|
||||
// RFC 4443 section 3.1 for more details.
|
||||
ControlAddressUnreachable ControlType = iota
|
||||
ControlNetworkUnreachable
|
||||
// ControlNoRoute indicates that an IPv4 packet did not reach its destination
|
||||
// because the destination host was unreachable.
|
||||
//
|
||||
// This maps to the ICMPv4 Destination Ureachable Code 1 error; see
|
||||
// RFC 791's Destination Unreachable Message section (page 4) for more
|
||||
// details.
|
||||
ControlNoRoute
|
||||
ControlPacketTooBig
|
||||
ControlPortUnreachable
|
||||
ControlUnknown
|
||||
)
|
||||
|
||||
// NetworkPacketInfo holds information about a network layer packet.
|
||||
type NetworkPacketInfo struct {
|
||||
// LocalAddressBroadcast is true if the packet's local address is a broadcast
|
||||
|
@ -81,6 +56,39 @@ type NetworkPacketInfo struct {
|
|||
LocalAddressBroadcast bool
|
||||
}
|
||||
|
||||
// TransportErrorKind enumerates error types that are handled by the transport
|
||||
// layer.
|
||||
type TransportErrorKind int
|
||||
|
||||
const (
|
||||
// PacketTooBigTransportError indicates that a packet did not reach its
|
||||
// destination because a link on the path to the destination had an MTU that
|
||||
// was too small to carry the packet.
|
||||
PacketTooBigTransportError TransportErrorKind = iota
|
||||
|
||||
// DestinationHostUnreachableTransportError indicates that the destination
|
||||
// host was unreachable.
|
||||
DestinationHostUnreachableTransportError
|
||||
|
||||
// DestinationPortUnreachableTransportError indicates that a packet reached
|
||||
// the destination host, but the transport protocol was not active on the
|
||||
// destination port.
|
||||
DestinationPortUnreachableTransportError
|
||||
|
||||
// DestinationNetworkUnreachableTransportError indicates that the destination
|
||||
// network was unreachable.
|
||||
DestinationNetworkUnreachableTransportError
|
||||
)
|
||||
|
||||
// TransportError is a marker interface for errors that may be handled by the
|
||||
// transport layer.
|
||||
type TransportError interface {
|
||||
tcpip.SockErrorCause
|
||||
|
||||
// Kind returns the type of the transport error.
|
||||
Kind() TransportErrorKind
|
||||
}
|
||||
|
||||
// TransportEndpoint is the interface that needs to be implemented by transport
|
||||
// protocol (e.g., tcp, udp) endpoints that can handle packets.
|
||||
type TransportEndpoint interface {
|
||||
|
@ -93,10 +101,10 @@ type TransportEndpoint interface {
|
|||
// HandlePacket takes ownership of the packet.
|
||||
HandlePacket(TransportEndpointID, *PacketBuffer)
|
||||
|
||||
// HandleControlPacket is called by the stack when new control (e.g.
|
||||
// ICMP) packets arrive to this transport endpoint.
|
||||
// HandleControlPacket takes ownership of pkt.
|
||||
HandleControlPacket(typ ControlType, extra uint32, pkt *PacketBuffer)
|
||||
// HandleError is called when the transport endpoint receives an error.
|
||||
//
|
||||
// HandleError takes ownership of the packet buffer.
|
||||
HandleError(TransportError, *PacketBuffer)
|
||||
|
||||
// Abort initiates an expedited endpoint teardown. It puts the endpoint
|
||||
// in a closed state and frees all resources associated with it. This
|
||||
|
@ -248,14 +256,11 @@ type TransportDispatcher interface {
|
|||
// DeliverTransportPacket takes ownership of the packet.
|
||||
DeliverTransportPacket(tcpip.TransportProtocolNumber, *PacketBuffer) TransportPacketDisposition
|
||||
|
||||
// DeliverTransportControlPacket delivers control packets to the
|
||||
// appropriate transport protocol endpoint.
|
||||
// DeliverTransportError delivers an error to the appropriate transport
|
||||
// endpoint.
|
||||
//
|
||||
// pkt.NetworkHeader must be set before calling
|
||||
// DeliverTransportControlPacket.
|
||||
//
|
||||
// DeliverTransportControlPacket takes ownership of pkt.
|
||||
DeliverTransportControlPacket(local, remote tcpip.Address, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ ControlType, extra uint32, pkt *PacketBuffer)
|
||||
// DeliverTransportError takes ownership of the packet buffer.
|
||||
DeliverTransportError(local, remote tcpip.Address, _ tcpip.NetworkProtocolNumber, _ tcpip.TransportProtocolNumber, _ TransportError, _ *PacketBuffer)
|
||||
}
|
||||
|
||||
// PacketLooping specifies where an outbound packet should be sent.
|
||||
|
|
|
@ -138,12 +138,15 @@ func (f *fakeNetworkEndpoint) HandlePacket(pkt *stack.PacketBuffer) {
|
|||
return
|
||||
}
|
||||
pkt.Data.TrimFront(fakeNetHeaderLen)
|
||||
f.dispatcher.DeliverTransportControlPacket(
|
||||
f.dispatcher.DeliverTransportError(
|
||||
tcpip.Address(nb[srcAddrOffset:srcAddrOffset+1]),
|
||||
tcpip.Address(nb[dstAddrOffset:dstAddrOffset+1]),
|
||||
fakeNetNumber,
|
||||
tcpip.TransportProtocolNumber(nb[protocolNumberOffset]),
|
||||
stack.ControlPortUnreachable, 0, pkt)
|
||||
// Nothing checks the error.
|
||||
nil, /* transport error */
|
||||
pkt,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -182,9 +182,8 @@ func (epsByNIC *endpointsByNIC) handlePacket(id TransportEndpointID, pkt *Packet
|
|||
epsByNIC.mu.RUnlock() // Don't use defer for performance reasons.
|
||||
}
|
||||
|
||||
// handleControlPacket delivers a control packet to the transport endpoint
|
||||
// identified by id.
|
||||
func (epsByNIC *endpointsByNIC) handleControlPacket(n *NIC, id TransportEndpointID, typ ControlType, extra uint32, pkt *PacketBuffer) {
|
||||
// handleError delivers an error to the transport endpoint identified by id.
|
||||
func (epsByNIC *endpointsByNIC) handleError(n *NIC, id TransportEndpointID, transErr TransportError, pkt *PacketBuffer) {
|
||||
epsByNIC.mu.RLock()
|
||||
defer epsByNIC.mu.RUnlock()
|
||||
|
||||
|
@ -200,7 +199,7 @@ func (epsByNIC *endpointsByNIC) handleControlPacket(n *NIC, id TransportEndpoint
|
|||
// broadcast like we are doing with handlePacket above?
|
||||
|
||||
// multiPortEndpoints are guaranteed to have at least one element.
|
||||
selectEndpoint(id, mpep, epsByNIC.seed).HandleControlPacket(typ, extra, pkt)
|
||||
selectEndpoint(id, mpep, epsByNIC.seed).HandleError(transErr, pkt)
|
||||
}
|
||||
|
||||
// registerEndpoint returns true if it succeeds. It fails and returns
|
||||
|
@ -596,9 +595,11 @@ func (d *transportDemuxer) deliverRawPacket(protocol tcpip.TransportProtocolNumb
|
|||
return foundRaw
|
||||
}
|
||||
|
||||
// deliverControlPacket attempts to deliver the given control packet. Returns
|
||||
// true if it found an endpoint, false otherwise.
|
||||
func (d *transportDemuxer) deliverControlPacket(n *NIC, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, typ ControlType, extra uint32, pkt *PacketBuffer, id TransportEndpointID) bool {
|
||||
// deliverError attempts to deliver the given error to the appropriate transport
|
||||
// endpoint.
|
||||
//
|
||||
// Returns true if the error was delivered.
|
||||
func (d *transportDemuxer) deliverError(n *NIC, net tcpip.NetworkProtocolNumber, trans tcpip.TransportProtocolNumber, transErr TransportError, pkt *PacketBuffer, id TransportEndpointID) bool {
|
||||
eps, ok := d.protocol[protocolIDs{net, trans}]
|
||||
if !ok {
|
||||
return false
|
||||
|
@ -611,7 +612,7 @@ func (d *transportDemuxer) deliverControlPacket(n *NIC, net tcpip.NetworkProtoco
|
|||
return false
|
||||
}
|
||||
|
||||
ep.handleControlPacket(n, id, typ, extra, pkt)
|
||||
ep.handleError(n, id, transErr, pkt)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -237,7 +237,7 @@ func (f *fakeTransportEndpoint) HandlePacket(id stack.TransportEndpointID, pkt *
|
|||
f.acceptQueue = append(f.acceptQueue, ep)
|
||||
}
|
||||
|
||||
func (f *fakeTransportEndpoint) HandleControlPacket(stack.ControlType, uint32, *stack.PacketBuffer) {
|
||||
func (f *fakeTransportEndpoint) HandleError(stack.TransportError, *stack.PacketBuffer) {
|
||||
// Increment the number of received control packets.
|
||||
f.proto.controlCount++
|
||||
}
|
||||
|
|
|
@ -247,6 +247,14 @@ func TestPing(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
type transportError struct {
|
||||
origin tcpip.SockErrOrigin
|
||||
typ uint8
|
||||
code uint8
|
||||
info uint32
|
||||
kind stack.TransportErrorKind
|
||||
}
|
||||
|
||||
func TestTCPLinkResolutionFailure(t *testing.T) {
|
||||
const (
|
||||
host1NICID = 1
|
||||
|
@ -259,6 +267,7 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
remoteAddr tcpip.Address
|
||||
expectedWriteErr tcpip.Error
|
||||
sockError tcpip.SockError
|
||||
transErr transportError
|
||||
}{
|
||||
{
|
||||
name: "IPv4 with resolvable remote",
|
||||
|
@ -278,10 +287,7 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
remoteAddr: ipv4Addr3.AddressWithPrefix.Address,
|
||||
expectedWriteErr: &tcpip.ErrNoRoute{},
|
||||
sockError: tcpip.SockError{
|
||||
Err: &tcpip.ErrNoRoute{},
|
||||
ErrType: byte(header.ICMPv4DstUnreachable),
|
||||
ErrCode: byte(header.ICMPv4HostUnreachable),
|
||||
ErrOrigin: tcpip.SockExtErrorOriginICMP,
|
||||
Err: &tcpip.ErrNoRoute{},
|
||||
Dst: tcpip.FullAddress{
|
||||
NIC: host1NICID,
|
||||
Addr: ipv4Addr3.AddressWithPrefix.Address,
|
||||
|
@ -293,6 +299,12 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
},
|
||||
NetProto: ipv4.ProtocolNumber,
|
||||
},
|
||||
transErr: transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP,
|
||||
typ: uint8(header.ICMPv4DstUnreachable),
|
||||
code: uint8(header.ICMPv4HostUnreachable),
|
||||
kind: stack.DestinationHostUnreachableTransportError,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "IPv6 without resolvable remote",
|
||||
|
@ -300,10 +312,7 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
remoteAddr: ipv6Addr3.AddressWithPrefix.Address,
|
||||
expectedWriteErr: &tcpip.ErrNoRoute{},
|
||||
sockError: tcpip.SockError{
|
||||
Err: &tcpip.ErrNoRoute{},
|
||||
ErrType: byte(header.ICMPv6DstUnreachable),
|
||||
ErrCode: byte(header.ICMPv6AddressUnreachable),
|
||||
ErrOrigin: tcpip.SockExtErrorOriginICMP6,
|
||||
Err: &tcpip.ErrNoRoute{},
|
||||
Dst: tcpip.FullAddress{
|
||||
NIC: host1NICID,
|
||||
Addr: ipv6Addr3.AddressWithPrefix.Address,
|
||||
|
@ -315,6 +324,12 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
},
|
||||
NetProto: ipv6.ProtocolNumber,
|
||||
},
|
||||
transErr: transportError{
|
||||
origin: tcpip.SockExtErrorOriginICMP6,
|
||||
typ: uint8(header.ICMPv6DstUnreachable),
|
||||
code: uint8(header.ICMPv6AddressUnreachable),
|
||||
kind: stack.DestinationHostUnreachableTransportError,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -393,9 +408,12 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
// are pre defined so we can simply compare pointers.
|
||||
return a == b
|
||||
}),
|
||||
// Ignore the payload since we do not know the TCP seq/ack numbers.
|
||||
checker.IgnoreCmpPath(
|
||||
// Ignore the payload since we do not know the TCP seq/ack numbers.
|
||||
"Payload",
|
||||
// Ignore the cause since we will compare its properties separately
|
||||
// since the concrete type of the cause is unknown.
|
||||
"Cause",
|
||||
),
|
||||
}
|
||||
|
||||
|
@ -407,6 +425,24 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
|||
if diff := cmp.Diff(&test.sockError, sockErr, sockErrCmpOpts...); diff != "" {
|
||||
t.Errorf("socket error mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
transErr, ok := sockErr.Cause.(stack.TransportError)
|
||||
if !ok {
|
||||
t.Fatalf("socket error cause is not a transport error; cause = %#v", sockErr.Cause)
|
||||
}
|
||||
if diff := cmp.Diff(
|
||||
test.transErr,
|
||||
transportError{
|
||||
origin: transErr.Origin(),
|
||||
typ: transErr.Type(),
|
||||
code: transErr.Code(),
|
||||
info: transErr.Info(),
|
||||
kind: transErr.Kind(),
|
||||
},
|
||||
cmp.AllowUnexported(transportError{}),
|
||||
); diff != "" {
|
||||
t.Errorf("socket error mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -2683,7 +2683,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
|
||||
|
@ -2692,11 +2692,8 @@ func (e *endpoint) onICMPError(err tcpip.Error, errType byte, errCode byte, extr
|
|||
// 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(),
|
||||
|
@ -2718,27 +2715,26 @@ func (e *endpoint) onICMPError(err tcpip.Error, errType byte, errCode byte, extr
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1322,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
|
||||
|
@ -1338,12 +1338,9 @@ func (e *endpoint) onICMPError(err tcpip.Error, errType byte, errCode byte, extr
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -1362,24 +1359,13 @@ func (e *endpoint) onICMPError(err tcpip.Error, errType byte, errCode byte, extr
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue