Make UDP traceroute work.

Adds support to generate Port Unreachable messages for UDP
datagrams received on a port for which there is no valid
endpoint.

Fixes #703

PiperOrigin-RevId: 267034418
This commit is contained in:
Bhasker Hariharan
2019-09-03 16:01:17 -07:00
committed by gVisor bot
parent eb94066ef2
commit 3789c34b22
23 changed files with 717 additions and 94 deletions
+100
View File
@@ -586,3 +586,103 @@ func Payload(want []byte) TransportChecker {
}
}
}
// ICMPv4 creates a checker that checks that the transport protocol is ICMPv4 and
// potentially additional ICMPv4 header fields.
func ICMPv4(checkers ...TransportChecker) NetworkChecker {
return func(t *testing.T, h []header.Network) {
t.Helper()
last := h[len(h)-1]
if p := last.TransportProtocol(); p != header.ICMPv4ProtocolNumber {
t.Fatalf("Bad protocol, got %d, want %d", p, header.ICMPv4ProtocolNumber)
}
icmp := header.ICMPv4(last.Payload())
for _, f := range checkers {
f(t, icmp)
}
if t.Failed() {
t.FailNow()
}
}
}
// ICMPv4Type creates a checker that checks the ICMPv4 Type field.
func ICMPv4Type(want header.ICMPv4Type) TransportChecker {
return func(t *testing.T, h header.Transport) {
t.Helper()
icmpv4, ok := h.(header.ICMPv4)
if !ok {
t.Fatalf("unexpected transport header passed to checker got: %+v, want: header.ICMPv4", h)
}
if got := icmpv4.Type(); got != want {
t.Fatalf("unexpected icmp type got: %d, want: %d", got, want)
}
}
}
// ICMPv4Code creates a checker that checks the ICMPv4 Code field.
func ICMPv4Code(want byte) TransportChecker {
return func(t *testing.T, h header.Transport) {
t.Helper()
icmpv4, ok := h.(header.ICMPv4)
if !ok {
t.Fatalf("unexpected transport header passed to checker got: %+v, want: header.ICMPv4", h)
}
if got := icmpv4.Code(); got != want {
t.Fatalf("unexpected ICMP code got: %d, want: %d", got, want)
}
}
}
// ICMPv6 creates a checker that checks that the transport protocol is ICMPv6 and
// potentially additional ICMPv6 header fields.
func ICMPv6(checkers ...TransportChecker) NetworkChecker {
return func(t *testing.T, h []header.Network) {
t.Helper()
last := h[len(h)-1]
if p := last.TransportProtocol(); p != header.ICMPv6ProtocolNumber {
t.Fatalf("Bad protocol, got %d, want %d", p, header.ICMPv6ProtocolNumber)
}
icmp := header.ICMPv6(last.Payload())
for _, f := range checkers {
f(t, icmp)
}
if t.Failed() {
t.FailNow()
}
}
}
// ICMPv6Type creates a checker that checks the ICMPv6 Type field.
func ICMPv6Type(want header.ICMPv6Type) TransportChecker {
return func(t *testing.T, h header.Transport) {
t.Helper()
icmpv6, ok := h.(header.ICMPv6)
if !ok {
t.Fatalf("unexpected transport header passed to checker got: %+v, want: header.ICMPv6", h)
}
if got := icmpv6.Type(); got != want {
t.Fatalf("unexpected icmp type got: %d, want: %d", got, want)
}
}
}
// ICMPv6Code creates a checker that checks the ICMPv6 Code field.
func ICMPv6Code(want byte) TransportChecker {
return func(t *testing.T, h header.Transport) {
t.Helper()
icmpv6, ok := h.(header.ICMPv6)
if !ok {
t.Fatalf("unexpected transport header passed to checker got: %+v, want: header.ICMPv6", h)
}
if got := icmpv6.Code(); got != want {
t.Fatalf("unexpected ICMP code got: %d, want: %d", got, want)
}
}
}
+68 -3
View File
@@ -18,6 +18,7 @@ import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
)
// ICMPv4 represents an ICMPv4 header stored in a byte array.
@@ -25,13 +26,29 @@ type ICMPv4 []byte
const (
// ICMPv4PayloadOffset defines the start of ICMP payload.
ICMPv4PayloadOffset = 4
ICMPv4PayloadOffset = 8
// ICMPv4MinimumSize is the minimum size of a valid ICMP packet.
ICMPv4MinimumSize = 8
// ICMPv4ProtocolNumber is the ICMP transport protocol number.
ICMPv4ProtocolNumber tcpip.TransportProtocolNumber = 1
// icmpv4ChecksumOffset is the offset of the checksum field
// in an ICMPv4 message.
icmpv4ChecksumOffset = 2
// icmpv4MTUOffset is the offset of the MTU field
// in a ICMPv4FragmentationNeeded message.
icmpv4MTUOffset = 6
// icmpv4IdentOffset is the offset of the ident field
// in a ICMPv4EchoRequest/Reply message.
icmpv4IdentOffset = 4
// icmpv4SequenceOffset is the offset of the sequence field
// in a ICMPv4EchoRequest/Reply message.
icmpv4SequenceOffset = 6
)
// ICMPv4Type is the ICMP type field described in RFC 792.
@@ -72,12 +89,12 @@ func (b ICMPv4) SetCode(c byte) { b[1] = c }
// Checksum is the ICMP checksum field.
func (b ICMPv4) Checksum() uint16 {
return binary.BigEndian.Uint16(b[2:])
return binary.BigEndian.Uint16(b[icmpv4ChecksumOffset:])
}
// SetChecksum sets the ICMP checksum field.
func (b ICMPv4) SetChecksum(checksum uint16) {
binary.BigEndian.PutUint16(b[2:], checksum)
binary.BigEndian.PutUint16(b[icmpv4ChecksumOffset:], checksum)
}
// SourcePort implements Transport.SourcePort.
@@ -102,3 +119,51 @@ func (ICMPv4) SetDestinationPort(uint16) {
func (b ICMPv4) Payload() []byte {
return b[ICMPv4PayloadOffset:]
}
// MTU retrieves the MTU field from an ICMPv4 message.
func (b ICMPv4) MTU() uint16 {
return binary.BigEndian.Uint16(b[icmpv4MTUOffset:])
}
// SetMTU sets the MTU field from an ICMPv4 message.
func (b ICMPv4) SetMTU(mtu uint16) {
binary.BigEndian.PutUint16(b[icmpv4MTUOffset:], mtu)
}
// Ident retrieves the Ident field from an ICMPv4 message.
func (b ICMPv4) Ident() uint16 {
return binary.BigEndian.Uint16(b[icmpv4IdentOffset:])
}
// SetIdent sets the Ident field from an ICMPv4 message.
func (b ICMPv4) SetIdent(ident uint16) {
binary.BigEndian.PutUint16(b[icmpv4IdentOffset:], ident)
}
// Sequence retrieves the Sequence field from an ICMPv4 message.
func (b ICMPv4) Sequence() uint16 {
return binary.BigEndian.Uint16(b[icmpv4SequenceOffset:])
}
// SetSequence sets the Sequence field from an ICMPv4 message.
func (b ICMPv4) SetSequence(sequence uint16) {
binary.BigEndian.PutUint16(b[icmpv4SequenceOffset:], sequence)
}
// ICMPv4Checksum calculates the ICMP checksum over the provided ICMP header,
// and payload.
func ICMPv4Checksum(h ICMPv4, vv buffer.VectorisedView) uint16 {
// Calculate the IPv6 pseudo-header upper-layer checksum.
xsum := uint16(0)
for _, v := range vv.Views() {
xsum = Checksum(v, xsum)
}
// h[2:4] is the checksum itself, set it aside to avoid checksumming the checksum.
h2, h3 := h[2], h[3]
h[2], h[3] = 0, 0
xsum = ^Checksum(h, xsum)
h[2], h[3] = h2, h3
return xsum
}
+81 -7
View File
@@ -18,6 +18,7 @@ import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
)
// ICMPv6 represents an ICMPv6 header stored in a byte array.
@@ -25,14 +26,18 @@ type ICMPv6 []byte
const (
// ICMPv6MinimumSize is the minimum size of a valid ICMP packet.
ICMPv6MinimumSize = 4
ICMPv6MinimumSize = 8
// ICMPv6PayloadOffset is the offset of the payload in an
// ICMP packet.
ICMPv6PayloadOffset = 8
// ICMPv6ProtocolNumber is the ICMP transport protocol number.
ICMPv6ProtocolNumber tcpip.TransportProtocolNumber = 58
// ICMPv6NeighborSolicitMinimumSize is the minimum size of a
// neighbor solicitation packet.
ICMPv6NeighborSolicitMinimumSize = ICMPv6MinimumSize + 4 + 16
ICMPv6NeighborSolicitMinimumSize = ICMPv6MinimumSize + 16
// ICMPv6NeighborAdvertSize is size of a neighbor advertisement.
ICMPv6NeighborAdvertSize = 32
@@ -42,11 +47,27 @@ const (
// ICMPv6DstUnreachableMinimumSize is the minimum size of a valid ICMP
// destination unreachable packet.
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize + 4
ICMPv6DstUnreachableMinimumSize = ICMPv6MinimumSize
// ICMPv6PacketTooBigMinimumSize is the minimum size of a valid ICMP
// packet-too-big packet.
ICMPv6PacketTooBigMinimumSize = ICMPv6MinimumSize + 4
ICMPv6PacketTooBigMinimumSize = ICMPv6MinimumSize
// icmpv6ChecksumOffset is the offset of the checksum field
// in an ICMPv6 message.
icmpv6ChecksumOffset = 2
// icmpv6MTUOffset is the offset of the MTU field in an ICMPv6
// PacketTooBig message.
icmpv6MTUOffset = 4
// icmpv6IdentOffset is the offset of the ident field
// in a ICMPv6 Echo Request/Reply message.
icmpv6IdentOffset = 4
// icmpv6SequenceOffset is the offset of the sequence field
// in a ICMPv6 Echo Request/Reply message.
icmpv6SequenceOffset = 6
)
// ICMPv6Type is the ICMP type field described in RFC 4443 and friends.
@@ -89,12 +110,12 @@ func (b ICMPv6) SetCode(c byte) { b[1] = c }
// Checksum is the ICMP checksum field.
func (b ICMPv6) Checksum() uint16 {
return binary.BigEndian.Uint16(b[2:])
return binary.BigEndian.Uint16(b[icmpv6ChecksumOffset:])
}
// SetChecksum calculates and sets the ICMP checksum field.
func (b ICMPv6) SetChecksum(checksum uint16) {
binary.BigEndian.PutUint16(b[2:], checksum)
binary.BigEndian.PutUint16(b[icmpv6ChecksumOffset:], checksum)
}
// SourcePort implements Transport.SourcePort.
@@ -115,7 +136,60 @@ func (ICMPv6) SetSourcePort(uint16) {
func (ICMPv6) SetDestinationPort(uint16) {
}
// MTU retrieves the MTU field from an ICMPv6 message.
func (b ICMPv6) MTU() uint32 {
return binary.BigEndian.Uint32(b[icmpv6MTUOffset:])
}
// SetMTU sets the MTU field from an ICMPv6 message.
func (b ICMPv6) SetMTU(mtu uint32) {
binary.BigEndian.PutUint32(b[icmpv6MTUOffset:], mtu)
}
// Ident retrieves the Ident field from an ICMPv6 message.
func (b ICMPv6) Ident() uint16 {
return binary.BigEndian.Uint16(b[icmpv6IdentOffset:])
}
// SetIdent sets the Ident field from an ICMPv6 message.
func (b ICMPv6) SetIdent(ident uint16) {
binary.BigEndian.PutUint16(b[icmpv6IdentOffset:], ident)
}
// Sequence retrieves the Sequence field from an ICMPv6 message.
func (b ICMPv6) Sequence() uint16 {
return binary.BigEndian.Uint16(b[icmpv6SequenceOffset:])
}
// SetSequence sets the Sequence field from an ICMPv6 message.
func (b ICMPv6) SetSequence(sequence uint16) {
binary.BigEndian.PutUint16(b[icmpv6SequenceOffset:], sequence)
}
// Payload implements Transport.Payload.
func (b ICMPv6) Payload() []byte {
return b[ICMPv6MinimumSize:]
return b[ICMPv6PayloadOffset:]
}
// ICMPv6Checksum calculates the ICMP checksum over the provided ICMP header,
// IPv6 src/dst addresses and the payload.
func ICMPv6Checksum(h ICMPv6, src, dst tcpip.Address, vv buffer.VectorisedView) uint16 {
// Calculate the IPv6 pseudo-header upper-layer checksum.
xsum := Checksum([]byte(src), 0)
xsum = Checksum([]byte(dst), xsum)
var upperLayerLength [4]byte
binary.BigEndian.PutUint32(upperLayerLength[:], uint32(len(h)+vv.Size()))
xsum = Checksum(upperLayerLength[:], xsum)
xsum = Checksum([]byte{0, 0, 0, uint8(ICMPv6ProtocolNumber)}, xsum)
for _, v := range vv.Views() {
xsum = Checksum(v, xsum)
}
// h[2:4] is the checksum itself, set it aside to avoid checksumming the checksum.
h2, h3 := h[2], h[3]
h[2], h[3] = 0, 0
xsum = ^Checksum(h, xsum)
h[2], h[3] = h2, h3
return xsum
}
+20 -13
View File
@@ -21,16 +21,18 @@ import (
)
const (
versIHL = 0
tos = 1
totalLen = 2
id = 4
flagsFO = 6
ttl = 8
protocol = 9
checksum = 10
srcAddr = 12
dstAddr = 16
versIHL = 0
tos = 1
// IPv4TotalLenOffset is the offset of the total length field in the
// IPv4 header.
IPv4TotalLenOffset = 2
id = 4
flagsFO = 6
ttl = 8
protocol = 9
checksum = 10
srcAddr = 12
dstAddr = 16
)
// IPv4Fields contains the fields of an IPv4 packet. It is used to describe the
@@ -103,6 +105,11 @@ const (
// IPv4Any is the non-routable IPv4 "any" meta address.
IPv4Any tcpip.Address = "\x00\x00\x00\x00"
// IPv4MinimumProcessableDatagramSize is the minimum size of an IP
// packet that every IPv4 capable host must be able to
// process/reassemble.
IPv4MinimumProcessableDatagramSize = 576
)
// Flags that may be set in an IPv4 packet.
@@ -163,7 +170,7 @@ func (b IPv4) FragmentOffset() uint16 {
// TotalLength returns the "total length" field of the ipv4 header.
func (b IPv4) TotalLength() uint16 {
return binary.BigEndian.Uint16(b[totalLen:])
return binary.BigEndian.Uint16(b[IPv4TotalLenOffset:])
}
// Checksum returns the checksum field of the ipv4 header.
@@ -209,7 +216,7 @@ func (b IPv4) SetTOS(v uint8, _ uint32) {
// SetTotalLength sets the "total length" field of the ipv4 header.
func (b IPv4) SetTotalLength(totalLength uint16) {
binary.BigEndian.PutUint16(b[totalLen:], totalLength)
binary.BigEndian.PutUint16(b[IPv4TotalLenOffset:], totalLength)
}
// SetChecksum sets the checksum field of the ipv4 header.
@@ -265,7 +272,7 @@ func (b IPv4) Encode(i *IPv4Fields) {
// packets are produced.
func (b IPv4) EncodePartial(partialChecksum, totalLength uint16) {
b.SetTotalLength(totalLength)
checksum := Checksum(b[totalLen:totalLen+2], partialChecksum)
checksum := Checksum(b[IPv4TotalLenOffset:IPv4TotalLenOffset+2], partialChecksum)
b.SetChecksum(^checksum)
}
+10 -8
View File
@@ -22,12 +22,14 @@ import (
)
const (
versTCFL = 0
payloadLen = 4
nextHdr = 6
hopLimit = 7
v6SrcAddr = 8
v6DstAddr = v6SrcAddr + IPv6AddressSize
versTCFL = 0
// IPv6PayloadLenOffset is the offset of the PayloadLength field in
// IPv6 header.
IPv6PayloadLenOffset = 4
nextHdr = 6
hopLimit = 7
v6SrcAddr = 8
v6DstAddr = v6SrcAddr + IPv6AddressSize
)
// IPv6Fields contains the fields of an IPv6 packet. It is used to describe the
@@ -94,7 +96,7 @@ var IPv6EmptySubnet = func() tcpip.Subnet {
// PayloadLength returns the value of the "payload length" field of the ipv6
// header.
func (b IPv6) PayloadLength() uint16 {
return binary.BigEndian.Uint16(b[payloadLen:])
return binary.BigEndian.Uint16(b[IPv6PayloadLenOffset:])
}
// HopLimit returns the value of the "hop limit" field of the ipv6 header.
@@ -148,7 +150,7 @@ func (b IPv6) SetTOS(t uint8, l uint32) {
// SetPayloadLength sets the "payload length" field of the ipv6 header.
func (b IPv6) SetPayloadLength(payloadLength uint16) {
binary.BigEndian.PutUint16(b[payloadLen:], payloadLength)
binary.BigEndian.PutUint16(b[IPv6PayloadLenOffset:], payloadLength)
}
// SetSourceAddress sets the "source address" field of the ipv6 header.
+7 -5
View File
@@ -319,7 +319,8 @@ func TestIPv4ReceiveControl(t *testing.T) {
icmp := header.ICMPv4(view[header.IPv4MinimumSize:])
icmp.SetType(header.ICMPv4DstUnreachable)
icmp.SetCode(c.code)
copy(view[header.IPv4MinimumSize+header.ICMPv4PayloadOffset:], []byte{0xde, 0xad, 0xbe, 0xef})
icmp.SetIdent(0xdead)
icmp.SetSequence(0xbeef)
// Create the inner IPv4 header.
ip = header.IPv4(view[header.IPv4MinimumSize+header.ICMPv4MinimumSize:])
@@ -539,7 +540,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
defer ep.Close()
dataOffset := header.IPv6MinimumSize*2 + header.ICMPv6MinimumSize + 4
dataOffset := header.IPv6MinimumSize*2 + header.ICMPv6MinimumSize
if c.fragmentOffset != nil {
dataOffset += header.IPv6FragmentHeaderSize
}
@@ -559,10 +560,11 @@ func TestIPv6ReceiveControl(t *testing.T) {
icmp := header.ICMPv6(view[header.IPv6MinimumSize:])
icmp.SetType(c.typ)
icmp.SetCode(c.code)
copy(view[header.IPv6MinimumSize+header.ICMPv6MinimumSize:], []byte{0xde, 0xad, 0xbe, 0xef})
icmp.SetIdent(0xdead)
icmp.SetSequence(0xbeef)
// Create the inner IPv6 header.
ip = header.IPv6(view[header.IPv6MinimumSize+header.ICMPv6MinimumSize+4:])
ip = header.IPv6(view[header.IPv6MinimumSize+header.ICMPv6PayloadOffset:])
ip.Encode(&header.IPv6Fields{
PayloadLength: 100,
NextHeader: 10,
@@ -574,7 +576,7 @@ func TestIPv6ReceiveControl(t *testing.T) {
// Build the fragmentation header if needed.
if c.fragmentOffset != nil {
ip.SetNextHeader(header.IPv6FragmentHeader)
frag := header.IPv6Fragment(view[2*header.IPv6MinimumSize+header.ICMPv6MinimumSize+4:])
frag := header.IPv6Fragment(view[2*header.IPv6MinimumSize+header.ICMPv6MinimumSize:])
frag.Encode(&header.IPv6FragmentFields{
NextHeader: 10,
FragmentOffset: *c.fragmentOffset,
+1 -3
View File
@@ -15,8 +15,6 @@
package ipv4
import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
"gvisor.dev/gvisor/pkg/tcpip/stack"
@@ -117,7 +115,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
e.handleControl(stack.ControlPortUnreachable, 0, vv)
case header.ICMPv4FragmentationNeeded:
mtu := uint32(binary.BigEndian.Uint16(v[header.ICMPv4PayloadOffset+2:]))
mtu := uint32(h.MTU())
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
}
+4 -27
View File
@@ -15,8 +15,6 @@
package ipv6
import (
"encoding/binary"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
"gvisor.dev/gvisor/pkg/tcpip/header"
@@ -82,7 +80,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
return
}
vv.TrimFront(header.ICMPv6PacketTooBigMinimumSize)
mtu := binary.BigEndian.Uint32(v[header.ICMPv6MinimumSize:])
mtu := h.MTU()
e.handleControl(stack.ControlPacketTooBig, calculateMTU(mtu), vv)
case header.ICMPv6DstUnreachable:
@@ -130,7 +128,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
r := r.Clone()
defer r.Release()
r.LocalAddress = targetAddr
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
if err := r.WritePacket(nil /* gso */, hdr, buffer.VectorisedView{}, header.ICMPv6ProtocolNumber, r.DefaultTTL()); err != nil {
sent.Dropped.Increment()
@@ -162,7 +160,7 @@ func (e *endpoint) handleICMP(r *stack.Route, netHeader buffer.View, vv buffer.V
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
copy(pkt, h)
pkt.SetType(header.ICMPv6EchoReply)
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, vv))
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, vv))
if err := r.WritePacket(nil /* gso */, hdr, vv, header.ICMPv6ProtocolNumber, r.DefaultTTL()); err != nil {
sent.Dropped.Increment()
return
@@ -233,7 +231,7 @@ func (*protocol) LinkAddressRequest(addr, localAddr tcpip.Address, linkEP stack.
pkt[icmpV6OptOffset] = ndpOptSrcLinkAddr
pkt[icmpV6LengthOffset] = 1
copy(pkt[icmpV6LengthOffset+1:], linkEP.LinkAddress())
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
length := uint16(hdr.UsedLength())
ip := header.IPv6(hdr.Prepend(header.IPv6MinimumSize))
@@ -272,24 +270,3 @@ func (*protocol) ResolveStaticAddress(addr tcpip.Address) (tcpip.LinkAddress, bo
}
return "", false
}
func icmpChecksum(h header.ICMPv6, src, dst tcpip.Address, vv buffer.VectorisedView) uint16 {
// Calculate the IPv6 pseudo-header upper-layer checksum.
xsum := header.Checksum([]byte(src), 0)
xsum = header.Checksum([]byte(dst), xsum)
var upperLayerLength [4]byte
binary.BigEndian.PutUint32(upperLayerLength[:], uint32(len(h)+vv.Size()))
xsum = header.Checksum(upperLayerLength[:], xsum)
xsum = header.Checksum([]byte{0, 0, 0, uint8(header.ICMPv6ProtocolNumber)}, xsum)
for _, v := range vv.Views() {
xsum = header.Checksum(v, xsum)
}
// h[2:4] is the checksum itself, set it aside to avoid checksumming the checksum.
h2, h3 := h[2], h[3]
h[2], h[3] = 0, 0
xsum = ^header.Checksum(h, xsum)
h[2], h[3] = h2, h3
return xsum
}
+2 -2
View File
@@ -153,7 +153,7 @@ func TestICMPCounts(t *testing.T) {
hdr := buffer.NewPrependable(header.IPv6MinimumSize + typ.size)
pkt := header.ICMPv6(hdr.Prepend(typ.size))
pkt.SetType(typ.typ)
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
handleIPv6Payload(hdr)
}
@@ -321,7 +321,7 @@ func TestLinkResolution(t *testing.T) {
hdr := buffer.NewPrependable(int(r.MaxHeaderLength()) + header.IPv6MinimumSize + header.ICMPv6EchoMinimumSize)
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
pkt.SetType(header.ICMPv6EchoRequest)
pkt.SetChecksum(icmpChecksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
pkt.SetChecksum(header.ICMPv6Checksum(pkt, r.LocalAddress, r.RemoteAddress, buffer.VectorisedView{}))
payload := tcpip.SlicePayload(hdr.View())
// We can't send our payload directly over the route because that
+2
View File
@@ -18,6 +18,7 @@ go_template_instance(
go_library(
name = "stack",
srcs = [
"icmp_rate_limit.go",
"linkaddrcache.go",
"linkaddrentry_list.go",
"nic.go",
@@ -42,6 +43,7 @@ go_library(
"//pkg/tcpip/ports",
"//pkg/tcpip/seqnum",
"//pkg/waiter",
"@org_golang_x_time//rate:go_default_library",
],
)
+86
View File
@@ -0,0 +1,86 @@
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stack
import (
"sync"
"golang.org/x/time/rate"
)
const (
// icmpLimit is the default maximum number of ICMP messages permitted by this
// rate limiter.
icmpLimit = 1000
// icmpBurst is the default number of ICMP messages that can be sent in a single
// burst.
icmpBurst = 50
)
// ICMPRateLimiter is a global rate limiter that controls the generation of
// ICMP messages generated by the stack.
type ICMPRateLimiter struct {
mu sync.RWMutex
l *rate.Limiter
}
// NewICMPRateLimiter returns a global rate limiter for controlling the rate
// at which ICMP messages are generated by the stack.
func NewICMPRateLimiter() *ICMPRateLimiter {
return &ICMPRateLimiter{l: rate.NewLimiter(icmpLimit, icmpBurst)}
}
// Allow returns true if we are allowed to send at least 1 message at the
// moment.
func (i *ICMPRateLimiter) Allow() bool {
i.mu.RLock()
allow := i.l.Allow()
i.mu.RUnlock()
return allow
}
// Limit returns the maximum number of ICMP messages that can be sent in one
// second.
func (i *ICMPRateLimiter) Limit() rate.Limit {
i.mu.RLock()
defer i.mu.RUnlock()
return i.l.Limit()
}
// SetLimit sets the maximum number of ICMP messages that can be sent in one
// second.
func (i *ICMPRateLimiter) SetLimit(newLimit rate.Limit) {
i.mu.RLock()
defer i.mu.RUnlock()
i.l.SetLimit(newLimit)
}
// Burst returns how many ICMP messages can be sent at any single instant.
func (i *ICMPRateLimiter) Burst() int {
i.mu.RLock()
defer i.mu.RUnlock()
return i.l.Burst()
}
// SetBurst sets the maximum number of ICMP messages allowed at any single
// instant.
//
// NOTE: Changing Burst causes the underlying rate limiter to be recreated.
func (i *ICMPRateLimiter) SetBurst(burst int) {
i.mu.Lock()
i.l = rate.NewLimiter(i.l.Limit(), burst)
i.mu.Unlock()
}
+11 -1
View File
@@ -679,7 +679,7 @@ 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, vv) {
if !transProto.HandleUnknownDestinationPacket(r, id, netHeader, vv) {
n.stack.stats.MalformedRcvdPackets.Increment()
}
}
@@ -720,6 +720,11 @@ func (n *NIC) ID() tcpip.NICID {
return n.id
}
// Stack returns the instance of the Stack that owns this NIC.
func (n *NIC) Stack() *Stack {
return n.stack
}
type networkEndpointKind int32
const (
@@ -823,3 +828,8 @@ func (r *referencedNetworkEndpoint) tryIncRef() bool {
}
}
}
// stack returns the Stack instance that owns the underlying endpoint.
func (r *referencedNetworkEndpoint) stack() *Stack {
return r.nic.stack
}
+1 -1
View File
@@ -109,7 +109,7 @@ type TransportProtocol interface {
//
// The return value indicates whether the packet was well-formed (for
// stats purposes only).
HandleUnknownDestinationPacket(r *Route, id TransportEndpointID, vv buffer.VectorisedView) bool
HandleUnknownDestinationPacket(r *Route, id TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) bool
// SetOption allows enabling/disabling protocol specific features.
// SetOption returns an error if the option is not supported or the
+5
View File
@@ -217,3 +217,8 @@ func (r *Route) MakeLoopedRoute() Route {
}
return l
}
// Stack returns the instance of the Stack that owns this route.
func (r *Route) Stack() *Stack {
return r.ref.stack()
}
+36
View File
@@ -28,6 +28,7 @@ import (
"sync"
"time"
"golang.org/x/time/rate"
"gvisor.dev/gvisor/pkg/sleep"
"gvisor.dev/gvisor/pkg/tcpip"
"gvisor.dev/gvisor/pkg/tcpip/buffer"
@@ -389,6 +390,10 @@ type Stack struct {
// resumableEndpoints is a list of endpoints that need to be resumed if the
// stack is being restored.
resumableEndpoints []ResumableEndpoint
// icmpRateLimiter is a global rate limiter for all ICMP messages generated
// by the stack.
icmpRateLimiter *ICMPRateLimiter
}
// Options contains optional Stack configuration.
@@ -434,6 +439,7 @@ func New(network []string, transport []string, opts Options) *Stack {
stats: opts.Stats.FillIn(),
handleLocal: opts.HandleLocal,
raw: opts.Raw,
icmpRateLimiter: NewICMPRateLimiter(),
}
// Add specified network protocols.
@@ -1215,3 +1221,33 @@ func (s *Stack) IPTables() iptables.IPTables {
func (s *Stack) SetIPTables(ipt iptables.IPTables) {
s.tables = ipt
}
// ICMPLimit returns the maximum number of ICMP messages that can be sent
// in one second.
func (s *Stack) ICMPLimit() rate.Limit {
return s.icmpRateLimiter.Limit()
}
// SetICMPLimit sets the maximum number of ICMP messages that be sent
// in one second.
func (s *Stack) SetICMPLimit(newLimit rate.Limit) {
s.icmpRateLimiter.SetLimit(newLimit)
}
// ICMPBurst returns the maximum number of ICMP messages that can be sent
// in a single burst.
func (s *Stack) ICMPBurst() int {
return s.icmpRateLimiter.Burst()
}
// SetICMPBurst sets the maximum number of ICMP messages that can be sent
// in a single burst.
func (s *Stack) SetICMPBurst(burst int) {
s.icmpRateLimiter.SetBurst(burst)
}
// AllowICMPMessage returns true if we the rate limiter allows at least one
// ICMP message to be sent at this instant.
func (s *Stack) AllowICMPMessage() bool {
return s.icmpRateLimiter.Allow()
}
+1 -1
View File
@@ -251,7 +251,7 @@ func (*fakeTransportProtocol) ParsePorts(buffer.View) (src, dst uint16, err *tcp
return 0, 0, nil
}
func (*fakeTransportProtocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, buffer.VectorisedView) bool {
func (*fakeTransportProtocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, buffer.View, buffer.VectorisedView) bool {
return true
}
+8
View File
@@ -720,6 +720,10 @@ type ICMPv4SentPacketStats struct {
// Dropped is the total number of ICMPv4 packets dropped due to link
// layer errors.
Dropped *StatCounter
// RateLimited is the total number of ICMPv6 packets dropped due to
// rate limit being exceeded.
RateLimited *StatCounter
}
// ICMPv4ReceivedPacketStats collects inbound ICMPv4-specific stats.
@@ -738,6 +742,10 @@ type ICMPv6SentPacketStats struct {
// Dropped is the total number of ICMPv6 packets dropped due to link
// layer errors.
Dropped *StatCounter
// RateLimited is the total number of ICMPv6 packets dropped due to
// rate limit being exceeded.
RateLimited *StatCounter
}
// ICMPv6ReceivedPacketStats collects inbound ICMPv6-specific stats.
+8 -11
View File
@@ -15,7 +15,6 @@
package icmp
import (
"encoding/binary"
"sync"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -368,14 +367,13 @@ func send4(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error {
return tcpip.ErrInvalidEndpointState
}
// Set the ident to the user-specified port. Sequence number should
// already be set by the user.
binary.BigEndian.PutUint16(data[header.ICMPv4PayloadOffset:], ident)
hdr := buffer.NewPrependable(header.ICMPv4MinimumSize + int(r.MaxHeaderLength()))
icmpv4 := header.ICMPv4(hdr.Prepend(header.ICMPv4MinimumSize))
copy(icmpv4, data)
// Set the ident to the user-specified port. Sequence number should
// already be set by the user.
icmpv4.SetIdent(ident)
data = data[header.ICMPv4MinimumSize:]
// Linux performs these basic checks.
@@ -394,14 +392,13 @@ func send6(r *stack.Route, ident uint16, data buffer.View) *tcpip.Error {
return tcpip.ErrInvalidEndpointState
}
// Set the ident. Sequence number is provided by the user.
binary.BigEndian.PutUint16(data[header.ICMPv6MinimumSize:], ident)
hdr := buffer.NewPrependable(header.ICMPv6MinimumSize + int(r.MaxHeaderLength()))
hdr := buffer.NewPrependable(header.ICMPv6EchoMinimumSize + int(r.MaxHeaderLength()))
icmpv6 := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
icmpv6 := header.ICMPv6(hdr.Prepend(header.ICMPv6MinimumSize))
copy(icmpv6, data)
data = data[header.ICMPv6EchoMinimumSize:]
// Set the ident. Sequence number is provided by the user.
icmpv6.SetIdent(ident)
data = data[header.ICMPv6MinimumSize:]
if icmpv6.Type() != header.ICMPv6EchoRequest || icmpv6.Code() != 0 {
return tcpip.ErrInvalidEndpointState
+6 -5
View File
@@ -23,7 +23,6 @@
package icmp
import (
"encoding/binary"
"fmt"
"gvisor.dev/gvisor/pkg/tcpip"
@@ -92,7 +91,7 @@ func (p *protocol) MinimumPacketSize() int {
case ProtocolNumber4:
return header.ICMPv4MinimumSize
case ProtocolNumber6:
return header.ICMPv6EchoMinimumSize
return header.ICMPv6MinimumSize
}
panic(fmt.Sprint("unknown protocol number: ", p.number))
}
@@ -101,16 +100,18 @@ func (p *protocol) MinimumPacketSize() int {
func (p *protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
switch p.number {
case ProtocolNumber4:
return 0, binary.BigEndian.Uint16(v[header.ICMPv4PayloadOffset:]), nil
hdr := header.ICMPv4(v)
return 0, hdr.Ident(), nil
case ProtocolNumber6:
return 0, binary.BigEndian.Uint16(v[header.ICMPv6MinimumSize:]), nil
hdr := header.ICMPv6(v)
return 0, hdr.Ident(), nil
}
panic(fmt.Sprint("unknown protocol number: ", p.number))
}
// HandleUnknownDestinationPacket handles packets targeted at this protocol but
// that don't match any existing endpoint.
func (p *protocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, buffer.VectorisedView) bool {
func (p *protocol) HandleUnknownDestinationPacket(*stack.Route, stack.TransportEndpointID, buffer.View, buffer.VectorisedView) bool {
return true
}
+1 -1
View File
@@ -129,7 +129,7 @@ func (*protocol) ParsePorts(v buffer.View) (src, dst uint16, err *tcpip.Error) {
// 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, vv buffer.VectorisedView) bool {
func (*protocol) HandleUnknownDestinationPacket(r *stack.Route, id stack.TransportEndpointID, netHeader buffer.View, vv buffer.VectorisedView) bool {
s := newSegment(r, id, vv)
defer s.decRef()

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