Remove generic ICMP errors

Generic ICMP errors were required because the transport dispatcher was
given the responsibility of sending ICMP errors in response to transport
packet delivery failures. Instead, the transport dispatcher should let
network layer know it failed to deliver a packet (and why) and let the
network layer make the decision as to what error to send (if any).

Fixes #4068

PiperOrigin-RevId: 333962333
This commit is contained in:
Ghanan Gowripalan
2020-09-26 19:24:41 -07:00
committed by gVisor bot
parent ebc81fadfc
commit a376a0baf3
13 changed files with 65 additions and 98 deletions
-6
View File
@@ -238,12 +238,6 @@ func (*protocol) Parse(pkt *stack.PacketBuffer) (proto tcpip.TransportProtocolNu
return 0, false, parse.ARP(pkt)
}
// ReturnError implements stack.TransportProtocol.ReturnError.
func (*protocol) ReturnError(*stack.Route, tcpip.ICMPReason, *stack.PacketBuffer) *tcpip.Error {
// In ARP, there is no such response so do nothing.
return nil
}
// NewProtocol returns an ARP network protocol.
func NewProtocol() stack.NetworkProtocol {
return &protocol{}
+2 -1
View File
@@ -98,9 +98,10 @@ func (t *testObject) checkValues(protocol tcpip.TransportProtocolNumber, vv buff
// DeliverTransportPacket is called by network endpoints after parsing incoming
// packets. This is used by the test object to verify that the results of the
// parsing are expected.
func (t *testObject) DeliverTransportPacket(r *stack.Route, protocol tcpip.TransportProtocolNumber, pkt *stack.PacketBuffer) {
func (t *testObject) DeliverTransportPacket(r *stack.Route, protocol tcpip.TransportProtocolNumber, pkt *stack.PacketBuffer) stack.TransportPacketDisposition {
t.checkValues(protocol, pkt.Data, r.RemoteAddress, r.LocalAddress)
t.dataCalls++
return stack.TransportPacketHandled
}
// DeliverTransportControlPacket is called by network endpoints after parsing
-10
View File
@@ -200,16 +200,6 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer) {
// ======= ICMP Error packet generation =========
// ReturnError implements stack.TransportProtocol.ReturnError.
func (p *protocol) ReturnError(r *stack.Route, reason tcpip.ICMPReason, pkt *stack.PacketBuffer) *tcpip.Error {
switch reason.(type) {
case *tcpip.ICMPReasonPortUnreachable:
return returnError(r, &icmpReasonPortUnreachable{}, pkt)
default:
return tcpip.ErrNotSupported
}
}
// icmpReason is a marker interface for IPv4 specific ICMP errors.
type icmpReason interface {
isICMPReason()
+14 -1
View File
@@ -21,6 +21,7 @@
package ipv4
import (
"fmt"
"sync/atomic"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -463,7 +464,19 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
return
}
r.Stats().IP.PacketsDelivered.Increment()
e.dispatcher.DeliverTransportPacket(r, p, pkt)
switch res := e.dispatcher.DeliverTransportPacket(r, p, pkt); res {
case stack.TransportPacketHandled:
case stack.TransportPacketDestinationPortUnreachable:
// As per RFC: 1122 Section 3.2.2.1 A host SHOULD generate Destination
// Unreachable messages with code:
// 3 (Port Unreachable), when the designated transport protocol
// (e.g., UDP) is unable to demultiplex the datagram but has no
// protocol mechanism to inform the sender.
_ = returnError(r, &icmpReasonPortUnreachable{}, pkt)
default:
panic(fmt.Sprintf("unrecognized result from DeliverTransportPacket = %d", res))
}
}
// Close cleans up resources associated with the endpoint.
-10
View File
@@ -671,16 +671,6 @@ func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bo
// ======= ICMP Error packet generation =========
// ReturnError implements stack.TransportProtocol.ReturnError.
func (p *protocol) ReturnError(r *stack.Route, reason tcpip.ICMPReason, pkt *stack.PacketBuffer) *tcpip.Error {
switch reason.(type) {
case *tcpip.ICMPReasonPortUnreachable:
return returnError(r, &icmpReasonPortUnreachable{}, pkt)
default:
return tcpip.ErrNotSupported
}
}
// icmpReason is a marker interface for IPv6 specific ICMP errors.
type icmpReason interface {
isICMPReason()
+2 -1
View File
@@ -75,7 +75,8 @@ type stubDispatcher struct {
stack.TransportDispatcher
}
func (*stubDispatcher) DeliverTransportPacket(*stack.Route, tcpip.TransportProtocolNumber, *stack.PacketBuffer) {
func (*stubDispatcher) DeliverTransportPacket(*stack.Route, tcpip.TransportProtocolNumber, *stack.PacketBuffer) stack.TransportPacketDisposition {
return stack.TransportPacketHandled
}
type stubLinkAddressCache struct {
+12 -1
View File
@@ -482,7 +482,18 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
r.Stats().IP.PacketsDelivered.Increment()
// TODO(b/152019344): Send an ICMPv6 Parameter Problem, Code 1 error
// in response to unrecognized next header values.
e.dispatcher.DeliverTransportPacket(r, p, pkt)
switch res := e.dispatcher.DeliverTransportPacket(r, p, pkt); res {
case stack.TransportPacketHandled:
case stack.TransportPacketDestinationPortUnreachable:
// As per RFC 4443 section 3.1:
// A destination node SHOULD originate a Destination Unreachable
// message with Code 4 in response to a packet for which the
// transport protocol (e.g., UDP) has no listener, if that transport
// protocol has no alternative means to inform the sender.
_ = returnError(r, &icmpReasonPortUnreachable{}, pkt)
default:
panic(fmt.Sprintf("unrecognized result from DeliverTransportPacket = %d", res))
}
}
default:
-4
View File
@@ -145,10 +145,6 @@ func (*fwdTestNetworkProtocol) Parse(pkt *PacketBuffer) (tcpip.TransportProtocol
return tcpip.TransportProtocolNumber(netHeader[protocolNumberOffset]), true, true
}
func (*fwdTestNetworkProtocol) ReturnError(*Route, tcpip.ICMPReason, *PacketBuffer) *tcpip.Error {
return nil
}
func (f *fwdTestNetworkProtocol) NewEndpoint(nicID tcpip.NICID, _ LinkAddressCache, _ NUDHandler, dispatcher TransportDispatcher, ep LinkEndpoint, _ *Stack) NetworkEndpoint {
return &fwdTestNetworkEndpoint{
nicID: nicID,
+19 -29
View File
@@ -1398,11 +1398,13 @@ func (n *NIC) forwardPacket(r *Route, protocol tcpip.NetworkProtocolNumber, pkt
// DeliverTransportPacket delivers the packets to the appropriate transport
// protocol endpoint.
func (n *NIC) DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) {
func (n *NIC) DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) TransportPacketDisposition {
state, ok := n.stack.transportProtocols[protocol]
if !ok {
// TODO(gvisor.dev/issue/4365): Let the caller know that the transport
// protocol is unrecognized.
n.stack.stats.UnknownProtocolRcvdPackets.Increment()
return
return TransportPacketHandled
}
transProto := state.proto
@@ -1423,59 +1425,47 @@ func (n *NIC) DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolN
// we parse it using the minimum size.
if _, ok := pkt.TransportHeader().Consume(transProto.MinimumPacketSize()); !ok {
n.stack.stats.MalformedRcvdPackets.Increment()
return
// We consider a malformed transport packet handled because there is
// nothing the caller can do.
return TransportPacketHandled
}
} else {
// This is either a bad packet or was re-assembled from fragments.
transProto.Parse(pkt)
} else if !transProto.Parse(pkt) {
n.stack.stats.MalformedRcvdPackets.Increment()
return TransportPacketHandled
}
}
if pkt.TransportHeader().View().Size() < transProto.MinimumPacketSize() {
n.stack.stats.MalformedRcvdPackets.Increment()
return
}
srcPort, dstPort, err := transProto.ParsePorts(pkt.TransportHeader().View())
if err != nil {
n.stack.stats.MalformedRcvdPackets.Increment()
return
return TransportPacketHandled
}
id := TransportEndpointID{dstPort, r.LocalAddress, srcPort, r.RemoteAddress}
if n.stack.demux.deliverPacket(r, protocol, pkt, id) {
return
return TransportPacketHandled
}
// Try to deliver to per-stack default handler.
if state.defaultHandler != nil {
if state.defaultHandler(r, id, pkt) {
return
return TransportPacketHandled
}
}
// We could not find an appropriate destination for this packet so
// give the protocol specific error handler a chance to handle it.
// If it doesn't handle it then we should do so.
switch transProto.HandleUnknownDestinationPacket(r, id, pkt) {
switch res := transProto.HandleUnknownDestinationPacket(r, id, pkt); res {
case UnknownDestinationPacketMalformed:
n.stack.stats.MalformedRcvdPackets.Increment()
return TransportPacketHandled
case UnknownDestinationPacketUnhandled:
// As per RFC: 1122 Section 3.2.2.1 A host SHOULD generate Destination
// Unreachable messages with code:
// 3 (Port Unreachable), when the designated transport protocol
// (e.g., UDP) is unable to demultiplex the datagram but has no
// protocol mechanism to inform the sender.
np, ok := n.stack.networkProtocols[r.NetProto]
if !ok {
// For this to happen stack.makeRoute() must have been called with the
// incorrect protocol number. Since we have successfully completed
// network layer processing this should be impossible.
panic(fmt.Sprintf("expected stack to have a NetworkProtocol for proto = %d", r.NetProto))
}
_ = np.ReturnError(r, &tcpip.ICMPReasonPortUnreachable{}, pkt)
return TransportPacketDestinationPortUnreachable
case UnknownDestinationPacketHandled:
return TransportPacketHandled
default:
panic(fmt.Sprintf("unrecognized result from HandleUnknownDestinationPacket = %d", res))
}
}
-5
View File
@@ -221,11 +221,6 @@ func (*testIPv6Protocol) Parse(*PacketBuffer) (tcpip.TransportProtocolNumber, bo
return 0, false, false
}
// ReturnError implements NetworkProtocol.ReturnError.
func (*testIPv6Protocol) ReturnError(*Route, tcpip.ICMPReason, *PacketBuffer) *tcpip.Error {
return nil
}
var _ LinkAddressResolver = (*testIPv6Protocol)(nil)
// LinkAddressProtocol implements LinkAddressResolver.
+16 -14
View File
@@ -197,6 +197,21 @@ type TransportProtocol interface {
Parse(pkt *PacketBuffer) (ok bool)
}
// TransportPacketDisposition is the result from attempting to deliver a packet
// to the transport layer.
type TransportPacketDisposition int
const (
// TransportPacketHandled indicates that a transport packet was handled by the
// transport layer and callers need not take any further action.
TransportPacketHandled TransportPacketDisposition = iota
// TransportPacketDestinationPortUnreachable indicates that there weren't any
// listeners interested in the packet and the transport protocol has no means
// to notify the sender.
TransportPacketDestinationPortUnreachable
)
// TransportDispatcher contains the methods used by the network stack to deliver
// packets to the appropriate transport endpoint after it has been handled by
// the network layer.
@@ -207,7 +222,7 @@ type TransportDispatcher interface {
// pkt.NetworkHeader must be set before calling DeliverTransportPacket.
//
// DeliverTransportPacket takes ownership of pkt.
DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer)
DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolNumber, pkt *PacketBuffer) TransportPacketDisposition
// DeliverTransportControlPacket delivers control packets to the
// appropriate transport protocol endpoint.
@@ -342,19 +357,6 @@ type NetworkProtocol interface {
// does not encapsulate anything).
// - Whether pkt.Data was large enough to parse and set pkt.NetworkHeader.
Parse(pkt *PacketBuffer) (proto tcpip.TransportProtocolNumber, hasTransportHdr bool, ok bool)
// ReturnError attempts to send a suitable error message to the sender
// of a received packet.
// - pkt holds the problematic packet.
// - reason indicates what the reason for wanting a message is.
// - route is the routing information for the received packet
// ReturnError returns an error if the send failed and nil on success.
// Note that deciding to deliberately send no message is a success.
//
// TODO(gvisor.dev/issues/3871): This method should be removed or simplified
// after all (or all but one) of the ICMP error dispatch occurs through the
// protocol specific modules. May become SendPortNotFound(r, pkt).
ReturnError(r *Route, reason tcpip.ICMPReason, pkt *PacketBuffer) *tcpip.Error
}
// NetworkDispatcher contains the methods used by the network stack to deliver
-5
View File
@@ -216,11 +216,6 @@ func (f *fakeNetworkProtocol) Option(option tcpip.GettableNetworkProtocolOption)
}
}
// ReturnError implements NetworkProtocol.ReturnError
func (*fakeNetworkProtocol) ReturnError(*stack.Route, tcpip.ICMPReason, *stack.PacketBuffer) *tcpip.Error {
return nil
}
// Close implements NetworkProtocol.Close.
func (*fakeNetworkProtocol) Close() {}
-11
View File
@@ -1987,14 +1987,3 @@ func DeleteDanglingEndpoint(e Endpoint) {
// AsyncLoading is the global barrier for asynchronous endpoint loading
// activities.
var AsyncLoading sync.WaitGroup
// ICMPReason is a marker interface for network protocol agnostic ICMP errors.
type ICMPReason interface {
isICMP()
}
// ICMPReasonPortUnreachable is an error where the transport protocol has no
// listener and no alternative means to inform the sender.
type ICMPReasonPortUnreachable struct{}
func (*ICMPReasonPortUnreachable) isICMP() {}