mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
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:
committed by
gVisor bot
parent
eb94066ef2
commit
3789c34b22
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user