Extract ICMP error sender from UDP

Store transport protocol number on packet buffers for use in ICMP error
generation.

Updates #2211.

PiperOrigin-RevId: 333252762
This commit is contained in:
Julian Elischer
2020-09-23 02:28:43 -07:00
committed by gVisor bot
parent b54dbdfdc6
commit 99decaadd6
25 changed files with 574 additions and 160 deletions
+46 -4
View File
@@ -31,6 +31,27 @@ const (
// ICMPv4MinimumSize is the minimum size of a valid ICMP packet.
ICMPv4MinimumSize = 8
// ICMPv4MinimumErrorPayloadSize Is the smallest number of bytes of an
// errant packet's transport layer that an ICMP error type packet should
// attempt to send as per RFC 792 (see each type) and RFC 1122
// section 3.2.2 which states:
// Every ICMP error message includes the Internet header and at
// least the first 8 data octets of the datagram that triggered
// the error; more than 8 octets MAY be sent; this header and data
// MUST be unchanged from the received datagram.
//
// RFC 792 shows:
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Code | Checksum |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | unused |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Internet Header + 64 bits of Original Data Datagram |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
ICMPv4MinimumErrorPayloadSize = 8
// ICMPv4ProtocolNumber is the ICMP transport protocol number.
ICMPv4ProtocolNumber tcpip.TransportProtocolNumber = 1
@@ -39,15 +60,19 @@ const (
icmpv4ChecksumOffset = 2
// icmpv4MTUOffset is the offset of the MTU field
// in a ICMPv4FragmentationNeeded message.
// in an ICMPv4FragmentationNeeded message.
icmpv4MTUOffset = 6
// icmpv4IdentOffset is the offset of the ident field
// in a ICMPv4EchoRequest/Reply message.
// in an ICMPv4EchoRequest/Reply message.
icmpv4IdentOffset = 4
// icmpv4PointerOffset is the offset of the pointer field
// in an ICMPv4ParamProblem message.
icmpv4PointerOffset = 4
// icmpv4SequenceOffset is the offset of the sequence field
// in a ICMPv4EchoRequest/Reply message.
// in an ICMPv4EchoRequest/Reply message.
icmpv4SequenceOffset = 6
)
@@ -72,15 +97,23 @@ const (
ICMPv4InfoReply ICMPv4Type = 16
)
// ICMP codes for ICMPv4 Time Exceeded messages as defined in RFC 792.
const (
ICMPv4TTLExceeded ICMPv4Code = 0
)
// ICMP codes for ICMPv4 Destination Unreachable messages as defined in RFC 792.
const (
ICMPv4TTLExceeded ICMPv4Code = 0
ICMPv4NetUnreachable ICMPv4Code = 0
ICMPv4HostUnreachable ICMPv4Code = 1
ICMPv4ProtoUnreachable ICMPv4Code = 2
ICMPv4PortUnreachable ICMPv4Code = 3
ICMPv4FragmentationNeeded ICMPv4Code = 4
)
// ICMPv4UnusedCode is a code to use in ICMP messages where no code is needed.
const ICMPv4UnusedCode ICMPv4Code = 0
// Type is the ICMP type field.
func (b ICMPv4) Type() ICMPv4Type { return ICMPv4Type(b[0]) }
@@ -93,6 +126,15 @@ func (b ICMPv4) Code() ICMPv4Code { return ICMPv4Code(b[1]) }
// SetCode sets the ICMP code field.
func (b ICMPv4) SetCode(c ICMPv4Code) { b[1] = byte(c) }
// SetPointer sets the pointer field in a Parameter error packet.
// This is the first byte of the type specific data field.
func (b ICMPv4) SetPointer(c byte) { b[icmpv4PointerOffset] = c }
// SetTypeSpecific sets the full 32 bit type specific data field.
func (b ICMPv4) SetTypeSpecific(val uint32) {
binary.BigEndian.PutUint32(b[icmpv4PointerOffset:], val)
}
// Checksum is the ICMP checksum field.
func (b ICMPv4) Checksum() uint16 {
return binary.BigEndian.Uint16(b[icmpv4ChecksumOffset:])
+32 -3
View File
@@ -54,9 +54,17 @@ const (
// address.
ICMPv6NeighborAdvertSize = ICMPv6HeaderSize + NDPNAMinimumSize + NDPLinkLayerAddressSize
// ICMPv6EchoMinimumSize is the minimum size of a valid ICMP echo packet.
// ICMPv6EchoMinimumSize is the minimum size of a valid echo packet.
ICMPv6EchoMinimumSize = 8
// ICMPv6ErrorHeaderSize is the size of an ICMP error packet header,
// as per RFC 4443, Apendix A, item 4 and the errata.
// ... all ICMP error messages shall have exactly
// 32 bits of type-specific data, so that receivers can reliably find
// the embedded invoking packet even when they don't recognize the
// ICMP message Type.
ICMPv6ErrorHeaderSize = 8
// ICMPv6DstUnreachableMinimumSize is the minimum size of a valid ICMP
// destination unreachable packet.
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize
@@ -69,6 +77,10 @@ const (
// in an ICMPv6 message.
icmpv6ChecksumOffset = 2
// icmpv6PointerOffset is the offset of the pointer
// in an ICMPv6 Parameter problem message.
icmpv6PointerOffset = 4
// icmpv6MTUOffset is the offset of the MTU field in an ICMPv6
// PacketTooBig message.
icmpv6MTUOffset = 4
@@ -89,9 +101,10 @@ const (
NDPHopLimit = 255
)
// ICMPv6Type is the ICMP type field described in RFC 4443 and friends.
// ICMPv6Type is the ICMP type field described in RFC 4443.
type ICMPv6Type byte
// Values for use in the Type field of ICMPv6 packet from RFC 4433.
const (
ICMPv6DstUnreachable ICMPv6Type = 1
ICMPv6PacketTooBig ICMPv6Type = 2
@@ -109,7 +122,18 @@ const (
ICMPv6RedirectMsg ICMPv6Type = 137
)
// ICMPv6Code is the ICMP code field described in RFC 4443.
// IsErrorType returns true if the receiver is an ICMP error type.
func (typ ICMPv6Type) IsErrorType() bool {
// Per RFC 4443 section 2.1:
// ICMPv6 messages are grouped into two classes: error messages and
// informational messages. Error messages are identified as such by a
// zero in the high-order bit of their message Type field values. Thus,
// error messages have message types from 0 to 127; informational
// messages have message types from 128 to 255.
return typ&0x80 == 0
}
// ICMPv6Code is the ICMP Code field described in RFC 4443.
type ICMPv6Code byte
// ICMP codes used with Destination Unreachable (Type 1). As per RFC 4443
@@ -153,6 +177,11 @@ func (b ICMPv6) Code() ICMPv6Code { return ICMPv6Code(b[1]) }
// SetCode sets the ICMP code field.
func (b ICMPv6) SetCode(c ICMPv6Code) { b[1] = byte(c) }
// SetTypeSpecific sets the full 32 bit type specific data field.
func (b ICMPv6) SetTypeSpecific(val uint32) {
binary.BigEndian.PutUint32(b[icmpv6PointerOffset:], val)
}
// Checksum is the ICMP checksum field.
func (b ICMPv6) Checksum() uint16 {
return binary.BigEndian.Uint16(b[icmpv6ChecksumOffset:])
+3 -2
View File
@@ -80,7 +80,8 @@ type IPv4Fields struct {
type IPv4 []byte
const (
// IPv4MinimumSize is the minimum size of a valid IPv4 packet.
// IPv4MinimumSize is the minimum size of a valid IPv4 packet;
// i.e. a packet header with no options.
IPv4MinimumSize = 20
// IPv4MaximumHeaderSize is the maximum size of an IPv4 header. Given
@@ -327,7 +328,7 @@ func IsV4MulticastAddress(addr tcpip.Address) bool {
}
// IsV4LoopbackAddress determines if the provided address is an IPv4 loopback
// address (belongs to 127.0.0.1/8 subnet).
// address (belongs to 127.0.0.0/8 subnet). See RFC 1122 section 3.2.1.3.
func IsV4LoopbackAddress(addr tcpip.Address) bool {
if len(addr) != IPv4AddressSize {
return false
+6
View File
@@ -238,6 +238,12 @@ 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{}
+178 -2
View File
@@ -15,6 +15,7 @@
package ipv4
import (
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -105,7 +106,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer) {
// source address MUST be one of its own IP addresses (but not a broadcast
// or multicast address).
localAddr := r.LocalAddress
if r.IsInboundBroadcast() || header.IsV4MulticastAddress(r.LocalAddress) {
if r.IsInboundBroadcast() || header.IsV4MulticastAddress(localAddr) {
localAddr = ""
}
@@ -131,7 +132,10 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer) {
ReserveHeaderBytes: int(r.MaxHeaderLength()),
Data: dataVV,
})
// TODO(gvisor.dev/issue/3810): When adding protocol numbers into the header
// information we will have to change this code to handle the ICMP header
// no longer being in the data buffer.
replyPkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
// Send out the reply packet.
sent := stats.ICMP.V4PacketsSent
if err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{
@@ -193,3 +197,175 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer) {
received.Invalid.Increment()
}
}
// ======= 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()
}
// icmpReasonPortUnreachable is an error where the transport protocol has no
// listener and no alternative means to inform the sender.
type icmpReasonPortUnreachable struct{}
func (*icmpReasonPortUnreachable) isICMPReason() {}
// returnError takes an error descriptor and generates the appropriate ICMP
// error packet for IPv4 and sends it back to the remote device that sent
// the problematic packet. It incorporates as much of that packet as
// possible as well as any error metadata as is available. returnError
// expects pkt to hold a valid IPv4 packet as per the wire format.
func returnError(r *stack.Route, reason icmpReason, pkt *stack.PacketBuffer) *tcpip.Error {
sent := r.Stats().ICMP.V4PacketsSent
if !r.Stack().AllowICMPMessage() {
sent.RateLimited.Increment()
return nil
}
// We check we are responding only when we are allowed to.
// See RFC 1812 section 4.3.2.7 (shown below).
//
// =========
// 4.3.2.7 When Not to Send ICMP Errors
//
// An ICMP error message MUST NOT be sent as the result of receiving:
//
// o An ICMP error message, or
//
// o A packet which fails the IP header validation tests described in
// Section [5.2.2] (except where that section specifically permits
// the sending of an ICMP error message), or
//
// o A packet destined to an IP broadcast or IP multicast address, or
//
// o A packet sent as a Link Layer broadcast or multicast, or
//
// o Any fragment of a datagram other then the first fragment (i.e., a
// packet for which the fragment offset in the IP header is nonzero).
//
// TODO(gvisor.dev/issues/4058): Make sure we don't send ICMP errors in
// response to a non-initial fragment, but it currently can not happen.
if r.IsInboundBroadcast() || header.IsV4MulticastAddress(r.LocalAddress) || r.RemoteAddress == header.IPv4Any {
return nil
}
networkHeader := pkt.NetworkHeader().View()
transportHeader := pkt.TransportHeader().View()
// Don't respond to icmp error packets.
if header.IPv4(networkHeader).Protocol() == uint8(header.ICMPv4ProtocolNumber) {
// TODO(gvisor.dev/issue/3810):
// Unfortunately the current stack pretty much always has ICMPv4 headers
// in the Data section of the packet but there is no guarantee that is the
// case. If this is the case grab the header to make it like all other
// packet types. When this is cleaned up the Consume should be removed.
if transportHeader.IsEmpty() {
var ok bool
transportHeader, ok = pkt.TransportHeader().Consume(header.ICMPv4MinimumSize)
if !ok {
return nil
}
} else if transportHeader.Size() < header.ICMPv4MinimumSize {
return nil
}
// We need to decide to explicitly name the packets we can respond to or
// the ones we can not respond to. The decision is somewhat arbitrary and
// if problems arise this could be reversed. It was judged less of a breach
// of protocol to not respond to unknown non-error packets than to respond
// to unknown error packets so we take the first approach.
switch header.ICMPv4(transportHeader).Type() {
case
header.ICMPv4EchoReply,
header.ICMPv4Echo,
header.ICMPv4Timestamp,
header.ICMPv4TimestampReply,
header.ICMPv4InfoRequest,
header.ICMPv4InfoReply:
default:
// Assume any type we don't know about may be an error type.
return nil
}
} else if transportHeader.IsEmpty() {
return nil
}
// Now work out how much of the triggering packet we should return.
// As per RFC 1812 Section 4.3.2.3
//
// ICMP datagram SHOULD contain as much of the original
// datagram as possible without the length of the ICMP
// datagram exceeding 576 bytes.
//
// NOTE: The above RFC referenced is different from the original
// recommendation in RFC 1122 and RFC 792 where it mentioned that at
// least 8 bytes of the payload must be included. Today linux and other
// systems implement the RFC 1812 definition and not the original
// requirement. We treat 8 bytes as the minimum but will try send more.
mtu := int(r.MTU())
if mtu > header.IPv4MinimumProcessableDatagramSize {
mtu = header.IPv4MinimumProcessableDatagramSize
}
headerLen := int(r.MaxHeaderLength()) + header.ICMPv4MinimumSize
available := int(mtu) - headerLen
if available < header.IPv4MinimumSize+header.ICMPv4MinimumErrorPayloadSize {
return nil
}
payloadLen := networkHeader.Size() + transportHeader.Size() + pkt.Data.Size()
if payloadLen > available {
payloadLen = available
}
// The buffers used by pkt may be used elsewhere in the system.
// For example, an AF_RAW or AF_PACKET socket may use what the transport
// protocol considers an unreachable destination. Thus we deep copy pkt to
// prevent multiple ownership and SR errors. The new copy is a vectorized
// view with the entire incoming IP packet reassembled and truncated as
// required. This is now the payload of the new ICMP packet and no longer
// considered a packet in its own right.
newHeader := append(buffer.View(nil), networkHeader...)
newHeader = append(newHeader, transportHeader...)
payload := newHeader.ToVectorisedView()
payload.AppendView(pkt.Data.ToView())
payload.CapLength(payloadLen)
icmpPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: headerLen,
Data: payload,
})
icmpPkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
icmpHdr := header.ICMPv4(icmpPkt.TransportHeader().Push(header.ICMPv4MinimumSize))
icmpHdr.SetType(header.ICMPv4DstUnreachable)
icmpHdr.SetCode(header.ICMPv4PortUnreachable)
counter := sent.DstUnreachable
icmpHdr.SetChecksum(header.ICMPv4Checksum(icmpHdr, icmpPkt.Data))
if err := r.WritePacket(
nil, /* gso */
stack.NetworkHeaderParams{
Protocol: header.ICMPv4ProtocolNumber,
TTL: r.DefaultTTL(),
TOS: stack.DefaultTOS,
},
icmpPkt,
); err != nil {
sent.Dropped.Increment()
return err
}
counter.Increment()
return nil
}
+4
View File
@@ -455,6 +455,10 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
}
p := h.TransportProtocol()
if p == header.ICMPv4ProtocolNumber {
// TODO(gvisor.dev/issues/3810): when we sort out ICMP and transport
// headers, the setting of the transport number here should be
// unnecessary and removed.
pkt.TransportProtocolNumber = p
e.handleICMP(r, pkt)
return
}
+123
View File
@@ -318,6 +318,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
ReserveHeaderBytes: int(r.MaxHeaderLength()) + header.ICMPv6NeighborAdvertMinimumSize + int(optsSerializer.Length()),
})
packet := header.ICMPv6(pkt.TransportHeader().Push(header.ICMPv6NeighborAdvertSize))
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
packet.SetType(header.ICMPv6NeighborAdvert)
na := header.NDPNeighborAdvert(packet.NDPPayload())
na.SetSolicitedFlag(solicited)
@@ -438,6 +439,7 @@ func (e *endpoint) handleICMP(r *stack.Route, pkt *stack.PacketBuffer, hasFragme
Data: pkt.Data,
})
packet := header.ICMPv6(replyPkt.TransportHeader().Push(header.ICMPv6EchoMinimumSize))
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
copy(packet, icmpHdr)
packet.SetType(header.ICMPv6EchoReply)
packet.SetChecksum(header.ICMPv6Checksum(packet, r.LocalAddress, r.RemoteAddress, pkt.Data))
@@ -637,6 +639,7 @@ func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, remoteLinkAdd
ReserveHeaderBytes: int(linkEP.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6NeighborAdvertSize,
})
icmpHdr := header.ICMPv6(pkt.TransportHeader().Push(header.ICMPv6NeighborAdvertSize))
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
icmpHdr.SetType(header.ICMPv6NeighborSolicit)
copy(icmpHdr[icmpV6OptOffset-len(addr):], addr)
icmpHdr[icmpV6OptOffset] = ndpOptSrcLinkAddr
@@ -665,3 +668,123 @@ func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bo
}
return tcpip.LinkAddress([]byte(nil)), false
}
// ======= 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()
}
// icmpReasonPortUnreachable is an error where the transport protocol has no
// listener and no alternative means to inform the sender.
type icmpReasonPortUnreachable struct{}
func (*icmpReasonPortUnreachable) isICMPReason() {}
// returnError takes an error descriptor and generates the appropriate ICMP
// error packet for IPv6 and sends it.
func returnError(r *stack.Route, reason icmpReason, pkt *stack.PacketBuffer) *tcpip.Error {
stats := r.Stats().ICMP
sent := stats.V6PacketsSent
if !r.Stack().AllowICMPMessage() {
sent.RateLimited.Increment()
return nil
}
// Only send ICMP error if the address is not a multicast v6
// address and the source is not the unspecified address.
//
// TODO(b/164522993) There are exceptions to this rule.
// See: point e.3) RFC 4443 section-2.4
//
// (e) An ICMPv6 error message MUST NOT be originated as a result of
// receiving the following:
//
// (e.1) An ICMPv6 error message.
//
// (e.2) An ICMPv6 redirect message [IPv6-DISC].
//
// (e.3) A packet destined to an IPv6 multicast address. (There are
// two exceptions to this rule: (1) the Packet Too Big Message
// (Section 3.2) to allow Path MTU discovery to work for IPv6
// multicast, and (2) the Parameter Problem Message, Code 2
// (Section 3.4) reporting an unrecognized IPv6 option (see
// Section 4.2 of [IPv6]) that has the Option Type highest-
// order two bits set to 10).
//
if header.IsV6MulticastAddress(r.LocalAddress) || r.RemoteAddress == header.IPv6Any {
return nil
}
network, transport := pkt.NetworkHeader().View(), pkt.TransportHeader().View()
if pkt.TransportProtocolNumber == header.ICMPv6ProtocolNumber {
// TODO(gvisor.dev/issues/3810): Sort this out when ICMP headers are stored.
// Unfortunately at this time ICMP Packets do not have a transport
// header separated out. It is in the Data part so we need to
// separate it out now. We will just pretend it is a minimal length
// ICMP packet as we don't really care if any later bits of a
// larger ICMP packet are in the header view or in the Data view.
transport, ok := pkt.TransportHeader().Consume(header.ICMPv6MinimumSize)
if !ok {
return nil
}
typ := header.ICMPv6(transport).Type()
if typ.IsErrorType() || typ == header.ICMPv6RedirectMsg {
return nil
}
}
// As per RFC 4443 section 2.4
//
// (c) Every ICMPv6 error message (type < 128) MUST include
// as much of the IPv6 offending (invoking) packet (the
// packet that caused the error) as possible without making
// the error message packet exceed the minimum IPv6 MTU
// [IPv6].
mtu := int(r.MTU())
if mtu > header.IPv6MinimumMTU {
mtu = header.IPv6MinimumMTU
}
headerLen := int(r.MaxHeaderLength()) + header.ICMPv6ErrorHeaderSize
available := int(mtu) - headerLen
if available < header.IPv6MinimumSize {
return nil
}
payloadLen := network.Size() + transport.Size() + pkt.Data.Size()
if payloadLen > available {
payloadLen = available
}
payload := buffer.NewVectorisedView(pkt.Size(), pkt.Views())
payload.CapLength(payloadLen)
newPkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
ReserveHeaderBytes: headerLen,
Data: payload,
})
newPkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
icmpHdr := header.ICMPv6(newPkt.TransportHeader().Push(header.ICMPv6DstUnreachableMinimumSize))
icmpHdr.SetCode(header.ICMPv6PortUnreachable)
icmpHdr.SetType(header.ICMPv6DstUnreachable)
icmpHdr.SetChecksum(header.ICMPv6Checksum(icmpHdr, r.LocalAddress, r.RemoteAddress, newPkt.Data))
counter := sent.DstUnreachable
err := r.WritePacket(nil /* gso */, stack.NetworkHeaderParams{Protocol: header.ICMPv6ProtocolNumber, TTL: r.DefaultTTL(), TOS: stack.DefaultTOS}, newPkt)
if err != nil {
sent.Dropped.Increment()
return err
}
counter.Increment()
return nil
}
+2 -1
View File
@@ -348,7 +348,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
it, done, err := it.Next()
if err != nil {
r.Stats().IP.MalformedPacketsReceived.Increment()
r.Stats().IP.MalformedPacketsReceived.Increment()
r.Stats().IP.MalformedFragmentsReceived.Increment()
return
}
if done {
@@ -476,6 +476,7 @@ func (e *endpoint) HandlePacket(r *stack.Route, pkt *stack.PacketBuffer) {
pkt.Data = extHdr.Buf
if p := tcpip.TransportProtocolNumber(extHdr.Identifier); p == header.ICMPv6ProtocolNumber {
pkt.TransportProtocolNumber = p
e.handleICMP(r, pkt, hasFragmentHeader)
} else {
r.Stats().IP.PacketsDelivered.Increment()
+4
View File
@@ -145,6 +145,10 @@ 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,
+24 -5
View File
@@ -1242,9 +1242,9 @@ func (n *NIC) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcp
local = n.linkEP.LinkAddress()
}
// Are any packet sockets listening for this network protocol?
// Are any packet type sockets listening for this network protocol?
packetEPs := n.mu.packetEPs[protocol]
// Add any other packet sockets that maybe listening for all protocols.
// Add any other packet type sockets that may be listening for all protocols.
packetEPs = append(packetEPs, n.mu.packetEPs[header.EthernetProtocolAll]...)
n.mu.RUnlock()
for _, ep := range packetEPs {
@@ -1265,6 +1265,7 @@ func (n *NIC) DeliverNetworkPacket(remote, local tcpip.LinkAddress, protocol tcp
return
}
if hasTransportHdr {
pkt.TransportProtocolNumber = transProtoNum
// Parse the transport header if present.
if state, ok := n.stack.transportProtocols[transProtoNum]; ok {
state.proto.Parse(pkt)
@@ -1453,10 +1454,28 @@ func (n *NIC) DeliverTransportPacket(r *Route, protocol tcpip.TransportProtocolN
}
}
// We could not find an appropriate destination for this packet, so
// deliver it to the global handler.
if !transProto.HandleUnknownDestinationPacket(r, id, pkt) {
// 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) {
case UnknownDestinationPacketMalformed:
n.stack.stats.MalformedRcvdPackets.Increment()
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)
case UnknownDestinationPacketHandled:
}
}
+5
View File
@@ -221,6 +221,11 @@ 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.
+18 -11
View File
@@ -80,11 +80,17 @@ type PacketBuffer struct {
// data are held in the same underlying buffer storage.
header buffer.Prependable
// NetworkProtocolNumber is only valid when NetworkHeader is set.
// NetworkProtocolNumber is only valid when NetworkHeader().View().IsEmpty()
// returns false.
// TODO(gvisor.dev/issue/3574): Remove the separately passed protocol
// numbers in registration APIs that take a PacketBuffer.
NetworkProtocolNumber tcpip.NetworkProtocolNumber
// TransportProtocol is only valid if it is non zero.
// TODO(gvisor.dev/issue/3810): This and the network protocol number should
// be moved into the headerinfo. This should resolve the validity issue.
TransportProtocolNumber tcpip.TransportProtocolNumber
// Hash is the transport layer hash of this packet. A value of zero
// indicates no valid hash has been set.
Hash uint32
@@ -234,16 +240,17 @@ func (pk *PacketBuffer) consume(typ headerType, size int) (v buffer.View, consum
// underlying packet payload.
func (pk *PacketBuffer) Clone() *PacketBuffer {
newPk := &PacketBuffer{
PacketBufferEntry: pk.PacketBufferEntry,
Data: pk.Data.Clone(nil),
headers: pk.headers,
header: pk.header,
Hash: pk.Hash,
Owner: pk.Owner,
EgressRoute: pk.EgressRoute,
GSOOptions: pk.GSOOptions,
NetworkProtocolNumber: pk.NetworkProtocolNumber,
NatDone: pk.NatDone,
PacketBufferEntry: pk.PacketBufferEntry,
Data: pk.Data.Clone(nil),
headers: pk.headers,
header: pk.header,
Hash: pk.Hash,
Owner: pk.Owner,
EgressRoute: pk.EgressRoute,
GSOOptions: pk.GSOOptions,
NetworkProtocolNumber: pk.NetworkProtocolNumber,
NatDone: pk.NatDone,
TransportProtocolNumber: pk.TransportProtocolNumber,
}
return newPk
}
+38 -7
View File
@@ -125,6 +125,26 @@ type PacketEndpoint interface {
HandlePacket(nicID tcpip.NICID, addr tcpip.LinkAddress, netProto tcpip.NetworkProtocolNumber, pkt *PacketBuffer)
}
// UnknownDestinationPacketDisposition enumerates the possible return vaues from
// HandleUnknownDestinationPacket().
type UnknownDestinationPacketDisposition int
const (
// UnknownDestinationPacketMalformed denotes that the packet was malformed
// and no further processing should be attempted other than updating
// statistics.
UnknownDestinationPacketMalformed UnknownDestinationPacketDisposition = iota
// UnknownDestinationPacketUnhandled tells the caller that the packet was
// well formed but that the issue was not handled and the stack should take
// the default action.
UnknownDestinationPacketUnhandled
// UnknownDestinationPacketHandled tells the caller that it should do
// no further processing.
UnknownDestinationPacketHandled
)
// TransportProtocol is the interface that needs to be implemented by transport
// protocols (e.g., tcp, udp) that want to be part of the networking stack.
type TransportProtocol interface {
@@ -147,14 +167,12 @@ type TransportProtocol interface {
ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error)
// HandleUnknownDestinationPacket handles packets targeted at this
// protocol but that don't match any existing endpoint. For example,
// it is targeted at a port that have no listeners.
// protocol that don't match any existing endpoint. For example,
// it is targeted at a port that has no listeners.
//
// The return value indicates whether the packet was well-formed (for
// stats purposes only).
//
// HandleUnknownDestinationPacket takes ownership of pkt.
HandleUnknownDestinationPacket(r *Route, id TransportEndpointID, pkt *PacketBuffer) bool
// HandleUnknownDestinationPacket takes ownership of pkt if it handles
// the issue.
HandleUnknownDestinationPacket(r *Route, id TransportEndpointID, pkt *PacketBuffer) UnknownDestinationPacketDisposition
// SetOption allows enabling/disabling protocol specific features.
// SetOption returns an error if the option is not supported or the
@@ -324,6 +342,19 @@ 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
+8 -3
View File
@@ -216,13 +216,18 @@ func (f *fakeNetworkProtocol) Option(option tcpip.GettableNetworkProtocolOption)
}
}
// Close implements TransportProtocol.Close.
// ReturnError implements NetworkProtocol.ReturnError
func (*fakeNetworkProtocol) ReturnError(*stack.Route, tcpip.ICMPReason, *stack.PacketBuffer) *tcpip.Error {
return nil
}
// Close implements NetworkProtocol.Close.
func (*fakeNetworkProtocol) Close() {}
// Wait implements TransportProtocol.Wait.
// Wait implements NetworkProtocol.Wait.
func (*fakeNetworkProtocol) Wait() {}
// Parse implements TransportProtocol.Parse.
// Parse implements NetworkProtocol.Parse.
func (*fakeNetworkProtocol) Parse(pkt *stack.PacketBuffer) (tcpip.TransportProtocolNumber, bool, bool) {
hdr, ok := pkt.NetworkHeader().Consume(fakeNetHeaderLen)
if !ok {
+2 -2
View File
@@ -287,8 +287,8 @@ func (*fakeTransportProtocol) ParsePorts(buffer.View) (src, dst uint16, err *tcp
return 0, 0, nil
}
func (*fakeTransportProtocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *stack.PacketBuffer) bool {
return true
func (*fakeTransportProtocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
return stack.UnknownDestinationPacketHandled
}
func (f *fakeTransportProtocol) SetOption(option tcpip.SettableTransportProtocolOption) *tcpip.Error {
+11
View File
@@ -1987,3 +1987,14 @@ 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() {}
+2
View File
@@ -446,6 +446,7 @@ func send4(r *stack.Route, ident uint16, data buffer.View, ttl uint8, owner tcpi
pkt.Owner = owner
icmpv4 := header.ICMPv4(pkt.TransportHeader().Push(header.ICMPv4MinimumSize))
pkt.TransportProtocolNumber = header.ICMPv4ProtocolNumber
copy(icmpv4, data)
// Set the ident to the user-specified port. Sequence number should
// already be set by the user.
@@ -478,6 +479,7 @@ func send6(r *stack.Route, ident uint16, data buffer.View, ttl uint8) *tcpip.Err
})
icmpv6 := header.ICMPv6(pkt.TransportHeader().Push(header.ICMPv6MinimumSize))
pkt.TransportProtocolNumber = header.ICMPv6ProtocolNumber
copy(icmpv6, data)
// Set the ident. Sequence number is provided by the user.
icmpv6.SetIdent(ident)
+2 -2
View File
@@ -104,8 +104,8 @@ func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error)
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
// that don't match any existing endpoint.
func (*protocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *stack.PacketBuffer) bool {
return true
func (*protocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
return stack.UnknownDestinationPacketHandled
}
// SetOption implements stack.TransportProtocol.SetOption.
+1
View File
@@ -747,6 +747,7 @@ func (e *endpoint) sendTCP(r *stack.Route, tf tcpFields, data buffer.VectorisedV
func buildTCPHdr(r *stack.Route, tf tcpFields, pkt *stack.PacketBuffer, gso *stack.GSO) {
optLen := len(tf.opts)
tcp := header.TCP(pkt.TransportHeader().Push(header.TCPMinimumSize + optLen))
pkt.TransportProtocolNumber = header.TCPProtocolNumber
tcp.Encode(&header.TCPFields{
SrcPort: tf.id.LocalPort,
DstPort: tf.id.RemotePort,
+6 -7
View File
@@ -201,21 +201,20 @@ func (p *protocol) QueuePacket(r *stack.Route, ep stack.TransportEndpoint, id st
// a reset is sent in response to any incoming segment except another reset. In
// particular, SYNs addressed to a non-existent connection are rejected by this
// means."
func (*protocol) HandleUnknownDestinationPacket(r *stack.Route, id stack.TransportEndpointID, pkt *stack.PacketBuffer) bool {
func (*protocol) HandleUnknownDestinationPacket(r *stack.Route, id stack.TransportEndpointID, pkt *stack.PacketBuffer) stack.UnknownDestinationPacketDisposition {
s := newSegment(r, id, pkt)
defer s.decRef()
if !s.parse() || !s.csumValid {
return false
return stack.UnknownDestinationPacketMalformed
}
// There's nothing to do if this is already a reset packet.
if s.flagIsSet(header.TCPFlagRst) {
return true
if !s.flagIsSet(header.TCPFlagRst) {
replyWithReset(s, stack.DefaultTOS, s.route.DefaultTTL())
}
replyWithReset(s, stack.DefaultTOS, s.route.DefaultTTL())
return true
return stack.UnknownDestinationPacketHandled
}
// replyWithReset replies to the given segment with a reset segment.

Some files were not shown because too many files have changed in this diff Show More