mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Refactor netstack to use bufferv2 instead of buffer.
This change has significant performance implications. bufferv2 is reference counted and pooled, which alleviates heap/GC pressure. Below are the results from running the iperf benchmark. HEAD: BenchmarkIperf/operation.Upload-16 1552 ns/op 46.6GiB total allocations BenchmarkIperf/operation.Download-16 1114 ns/op 68.6GiB total allocations w/ change: BenchmarkIperf/operation.Upload-16 1139 ns/op (-27%) 1.41GiB total allocations (-97%) BenchmarkIperf/operation.Download-16 753.2 ns/op (-33%) 706MiB total allocations (-99%) PiperOrigin-RevId: 462453185
This commit is contained in:
committed by
gVisor bot
parent
da267f435f
commit
1f2b30d70c
@@ -36,7 +36,7 @@ _templates:
|
||||
./pkg/tcpip/transport/icmp
|
||||
./pkg/tcpip/transport/tcp
|
||||
./pkg/tcpip/transport/udp
|
||||
./pkg/buffer
|
||||
./pkg/bufferv2
|
||||
./pkg/waiter
|
||||
env:
|
||||
# Force a clean checkout every time to avoid reuse of files between runs.
|
||||
|
||||
@@ -206,6 +206,10 @@ func (b *Buffer) Prepend(src *View) error {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
if src.Size() == 0 {
|
||||
src.Release()
|
||||
return nil
|
||||
}
|
||||
// If the first buffer does not have room just prepend the view.
|
||||
v := b.data.Front()
|
||||
if v == nil || v.read == 0 {
|
||||
@@ -251,6 +255,10 @@ func (b *Buffer) Append(src *View) error {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
if src.Size() == 0 {
|
||||
src.Release()
|
||||
return nil
|
||||
}
|
||||
// If the last buffer is full, just append the view.
|
||||
v := b.data.Back()
|
||||
if v.Full() {
|
||||
@@ -377,8 +385,6 @@ func (b *Buffer) PullUp(offset, length int) (View, bool) {
|
||||
func (b *Buffer) Flatten() []byte {
|
||||
if v := b.data.Front(); v == nil {
|
||||
return nil // No data at all.
|
||||
} else if v.Next() == nil {
|
||||
return v.AsSlice() // Only one buffer.
|
||||
}
|
||||
data := make([]byte, 0, b.size) // Need to flatten.
|
||||
for v := b.data.Front(); v != nil; v = v.Next() {
|
||||
|
||||
@@ -14,13 +14,12 @@
|
||||
|
||||
package bufferv2
|
||||
|
||||
// saveBuf is invoked by stateify.
|
||||
// saveData is invoked by stateify.
|
||||
func (b *Buffer) saveData() []byte {
|
||||
return b.Flatten()
|
||||
}
|
||||
|
||||
// loadBuf is invoked by stateify.
|
||||
// loadData is invoked by stateify.
|
||||
func (b *Buffer) loadData(data []byte) {
|
||||
v := NewViewWithData(data)
|
||||
b.Append(v)
|
||||
*b = MakeWithData(data)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,8 @@ func getChunkPool(size int) *sync.Pool {
|
||||
}
|
||||
|
||||
// Chunk represents a slice of pooled memory.
|
||||
//
|
||||
// +stateify savable
|
||||
type chunk struct {
|
||||
chunkRefs
|
||||
data []byte
|
||||
|
||||
+26
-5
@@ -45,11 +45,13 @@ var viewPool = sync.Pool{
|
||||
// Users must not write directly to slices returned by AsSlice. Instead, they
|
||||
// must use Write/WriteAt/CopyIn to modify the underlying View. This preserves
|
||||
// the safety guarantees of copy-on-write.
|
||||
//
|
||||
// +stateify savable
|
||||
type View struct {
|
||||
viewEntry
|
||||
read int
|
||||
write int
|
||||
chunk *chunk
|
||||
viewEntry `state:"nosave"`
|
||||
read int
|
||||
write int
|
||||
chunk *chunk
|
||||
}
|
||||
|
||||
// NewView creates a new view with capacity at least as big as cap. It is
|
||||
@@ -107,6 +109,15 @@ func (v *View) Release() {
|
||||
viewPool.Put(v)
|
||||
}
|
||||
|
||||
// Reset sets the view's read and write indices back to zero.
|
||||
func (v *View) Reset() {
|
||||
if v == nil {
|
||||
panic("cannot reset a nil view")
|
||||
}
|
||||
v.read = 0
|
||||
v.write = 0
|
||||
}
|
||||
|
||||
func (v *View) sharesChunk() bool {
|
||||
return v.chunk.refCount.Load() > 1
|
||||
}
|
||||
@@ -144,7 +155,7 @@ func (v *View) TrimFront(n int) {
|
||||
|
||||
// AsSlice returns a slice of the data written to this view.
|
||||
func (v *View) AsSlice() []byte {
|
||||
if v == nil {
|
||||
if v.Size() == 0 {
|
||||
return nil
|
||||
}
|
||||
return v.chunk.data[v.read:v.write]
|
||||
@@ -173,6 +184,16 @@ func (v *View) Read(p []byte) (int, error) {
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// ReadByte implements the io.ByteReader interface.
|
||||
func (v *View) ReadByte() (byte, error) {
|
||||
if v.Size() == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
b := v.AsSlice()[0]
|
||||
v.read++
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteTo writes data to w until the view is empty or an error occurs. The
|
||||
// return value n is the number of bytes written.
|
||||
//
|
||||
|
||||
@@ -8,6 +8,7 @@ go_library(
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/hostarch",
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
package tundev
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
@@ -125,8 +128,11 @@ func (fd *tunFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.Read
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err := dst.CopyOut(ctx, data)
|
||||
if n > 0 && n < len(data) {
|
||||
defer data.Release()
|
||||
|
||||
size := data.Size()
|
||||
n, err := io.CopyN(dst.Writer(ctx), data, dst.NumBytes())
|
||||
if n > 0 && n < int64(size) {
|
||||
// Not an error for partial copying. Packet truncated.
|
||||
err = nil
|
||||
}
|
||||
@@ -150,8 +156,8 @@ func (fd *tunFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.Wri
|
||||
if int64(mtu) < src.NumBytes() {
|
||||
return 0, unix.EMSGSIZE
|
||||
}
|
||||
data := make([]byte, src.NumBytes())
|
||||
if _, err := src.CopyIn(ctx, data); err != nil {
|
||||
data := bufferv2.NewView(int(src.NumBytes()))
|
||||
if _, err := io.CopyN(data, src.Reader(ctx), src.NumBytes()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return fd.device.Write(data)
|
||||
|
||||
@@ -17,6 +17,7 @@ go_library(
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/hostarch",
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/errors/linuxerr"
|
||||
"gvisor.dev/gvisor/pkg/hostarch"
|
||||
@@ -135,8 +138,8 @@ func (n *netTunFileOperations) Write(ctx context.Context, file *fs.File, src use
|
||||
if src.NumBytes() == 0 {
|
||||
return 0, unix.EINVAL
|
||||
}
|
||||
data := make([]byte, src.NumBytes())
|
||||
if _, err := src.CopyIn(ctx, data); err != nil {
|
||||
data := bufferv2.NewView(int(src.NumBytes()))
|
||||
if _, err := io.CopyN(data, src.Reader(ctx), src.NumBytes()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n.device.Write(data)
|
||||
@@ -148,8 +151,10 @@ func (n *netTunFileOperations) Read(ctx context.Context, file *fs.File, dst user
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
bytesCopied, err := dst.CopyOut(ctx, data)
|
||||
if bytesCopied > 0 && bytesCopied < len(data) {
|
||||
defer data.Release()
|
||||
dataSize := data.Size()
|
||||
bytesCopied, err := io.CopyN(dst.Writer(ctx), data, dst.NumBytes())
|
||||
if bytesCopied > 0 && bytesCopied < int64(dataSize) {
|
||||
// Not an error for partial copying. Packet truncated.
|
||||
err = nil
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (*TCPMatcher) name() string {
|
||||
func (tm *TCPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ string) (bool, bool) {
|
||||
switch pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().View())
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
if netHeader.TransportProtocol() != header.TCPProtocolNumber {
|
||||
return false, false
|
||||
}
|
||||
@@ -115,7 +115,7 @@ func (tm *TCPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ strin
|
||||
// As in Linux, we do not perform an IPv6 fragment check. See
|
||||
// xt_action_param.fragoff in
|
||||
// include/linux/netfilter/x_tables.h.
|
||||
if header.IPv6(pkt.NetworkHeader().View()).TransportProtocol() != header.TCPProtocolNumber {
|
||||
if header.IPv6(pkt.NetworkHeader().Slice()).TransportProtocol() != header.TCPProtocolNumber {
|
||||
return false, false
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func (tm *TCPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ strin
|
||||
return false, false
|
||||
}
|
||||
|
||||
tcpHeader := header.TCP(pkt.TransportHeader().View())
|
||||
tcpHeader := header.TCP(pkt.TransportHeader().Slice())
|
||||
if len(tcpHeader) < header.TCPMinimumSize {
|
||||
// There's no valid TCP header here, so we drop the packet immediately.
|
||||
return false, true
|
||||
|
||||
@@ -95,7 +95,7 @@ func (*UDPMatcher) name() string {
|
||||
func (um *UDPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ string) (bool, bool) {
|
||||
switch pkt.NetworkProtocolNumber {
|
||||
case header.IPv4ProtocolNumber:
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().View())
|
||||
netHeader := header.IPv4(pkt.NetworkHeader().Slice())
|
||||
if netHeader.TransportProtocol() != header.UDPProtocolNumber {
|
||||
return false, false
|
||||
}
|
||||
@@ -112,7 +112,7 @@ func (um *UDPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ strin
|
||||
// As in Linux, we do not perform an IPv6 fragment check. See
|
||||
// xt_action_param.fragoff in
|
||||
// include/linux/netfilter/x_tables.h.
|
||||
if header.IPv6(pkt.NetworkHeader().View()).TransportProtocol() != header.UDPProtocolNumber {
|
||||
if header.IPv6(pkt.NetworkHeader().Slice()).TransportProtocol() != header.UDPProtocolNumber {
|
||||
return false, false
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (um *UDPMatcher) Match(hook stack.Hook, pkt *stack.PacketBuffer, _, _ strin
|
||||
return false, false
|
||||
}
|
||||
|
||||
udpHeader := header.UDP(pkt.TransportHeader().View())
|
||||
udpHeader := header.UDP(pkt.TransportHeader().Slice())
|
||||
if len(udpHeader) < header.UDPMinimumSize {
|
||||
// There's no valid UDP header here, so we drop the packet immediately.
|
||||
return false, true
|
||||
|
||||
@@ -3001,14 +3001,17 @@ func (s *socketOpsCommon) recvErr(t *kernel.Task, dst usermem.IOSequence) (int,
|
||||
if sockErr == nil {
|
||||
return 0, 0, nil, 0, socket.ControlMessages{}, syserr.ErrTryAgain
|
||||
}
|
||||
if sockErr.Payload != nil {
|
||||
defer sockErr.Payload.Release()
|
||||
}
|
||||
|
||||
// The payload of the original packet that caused the error is passed as
|
||||
// normal data via msg_iovec. -- recvmsg(2)
|
||||
msgFlags := linux.MSG_ERRQUEUE
|
||||
if int(dst.NumBytes()) < len(sockErr.Payload) {
|
||||
if int(dst.NumBytes()) < sockErr.Payload.Size() {
|
||||
msgFlags |= linux.MSG_TRUNC
|
||||
}
|
||||
n, err := dst.CopyOut(t, sockErr.Payload)
|
||||
n, err := dst.CopyOut(t, sockErr.Payload.AsSlice())
|
||||
|
||||
// The original destination address of the datagram that caused the error is
|
||||
// supplied via msg_name. -- recvmsg(2)
|
||||
|
||||
@@ -94,7 +94,7 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/context",
|
||||
"//pkg/errors/linuxerr",
|
||||
"//pkg/fdnotifier",
|
||||
|
||||
+4
-1
@@ -31,6 +31,7 @@ go_library(
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/sync",
|
||||
"//pkg/waiter",
|
||||
],
|
||||
@@ -47,14 +48,16 @@ deps_test(
|
||||
allowed = [
|
||||
# gVisor deps.
|
||||
"//pkg/atomicbitops",
|
||||
"//pkg/bits",
|
||||
"//pkg/context",
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/cpuid",
|
||||
"//pkg/gohacks",
|
||||
"//pkg/goid",
|
||||
"//pkg/ilist",
|
||||
"//pkg/linewriter",
|
||||
"//pkg/log",
|
||||
"//pkg/pool",
|
||||
"//pkg/rand",
|
||||
"//pkg/refs",
|
||||
"//pkg/refsvfs2",
|
||||
|
||||
@@ -8,7 +8,7 @@ go_library(
|
||||
srcs = ["checker.go"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/seqnum",
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/seqnum"
|
||||
@@ -43,13 +43,13 @@ type ControlMessagesChecker func(*testing.T, tcpip.ReceivableControlMessages)
|
||||
// properties. For example, to check the source and destination address, one
|
||||
// would call:
|
||||
//
|
||||
// checker.IPv4(t, b, checker.SrcAddr(x), checker.DstAddr(y))
|
||||
func IPv4(t *testing.T, b []byte, checkers ...NetworkChecker) {
|
||||
// checker.IPv4(t, v, checker.SrcAddr(x), checker.DstAddr(y))
|
||||
func IPv4(t *testing.T, v *bufferv2.View, checkers ...NetworkChecker) {
|
||||
t.Helper()
|
||||
|
||||
ipv4 := header.IPv4(b)
|
||||
ipv4 := header.IPv4(v.AsSlice())
|
||||
|
||||
if !ipv4.IsValid(len(b)) {
|
||||
if !ipv4.IsValid(len(v.AsSlice())) {
|
||||
t.Fatalf("Not a valid IPv4 packet: %x", ipv4)
|
||||
}
|
||||
|
||||
@@ -67,11 +67,11 @@ func IPv4(t *testing.T, b []byte, checkers ...NetworkChecker) {
|
||||
|
||||
// IPv6 checks the validity and properties of the given IPv6 packet. The usage
|
||||
// is similar to IPv4.
|
||||
func IPv6(t *testing.T, b []byte, checkers ...NetworkChecker) {
|
||||
func IPv6(t *testing.T, v *bufferv2.View, checkers ...NetworkChecker) {
|
||||
t.Helper()
|
||||
|
||||
ipv6 := header.IPv6(b)
|
||||
if !ipv6.IsValid(len(b)) {
|
||||
ipv6 := header.IPv6(v.AsSlice())
|
||||
if !ipv6.IsValid(len(v.AsSlice())) {
|
||||
t.Fatalf("Not a valid IPv6 packet: %x", ipv6)
|
||||
}
|
||||
|
||||
@@ -1535,19 +1535,20 @@ func IGMPGroupAddress(want tcpip.Address) TransportChecker {
|
||||
type IPv6ExtHdrChecker func(*testing.T, header.IPv6PayloadHeader)
|
||||
|
||||
// IPv6WithExtHdr is like IPv6 but allows IPv6 packets with extension headers.
|
||||
func IPv6WithExtHdr(t *testing.T, b []byte, checkers ...NetworkChecker) {
|
||||
func IPv6WithExtHdr(t *testing.T, v *bufferv2.View, checkers ...NetworkChecker) {
|
||||
t.Helper()
|
||||
|
||||
ipv6 := header.IPv6(b)
|
||||
if !ipv6.IsValid(len(b)) {
|
||||
ipv6 := header.IPv6(v.AsSlice())
|
||||
if !ipv6.IsValid(len(v.AsSlice())) {
|
||||
t.Error("not a valid IPv6 packet")
|
||||
return
|
||||
}
|
||||
|
||||
payloadIterator := header.MakeIPv6PayloadIterator(
|
||||
header.IPv6ExtensionHeaderIdentifier(ipv6.NextHeader()),
|
||||
buffer.NewWithData(ipv6.Payload()),
|
||||
bufferv2.MakeWithData(ipv6.Payload()),
|
||||
)
|
||||
defer payloadIterator.Release()
|
||||
|
||||
var rawPayloadHeader header.IPv6RawPayloadHeader
|
||||
for {
|
||||
@@ -1560,6 +1561,7 @@ func IPv6WithExtHdr(t *testing.T, b []byte, checkers ...NetworkChecker) {
|
||||
t.Errorf("got payloadIterator.Next() = (%T, %t, _), want = (_, true, _)", h, done)
|
||||
return
|
||||
}
|
||||
defer h.Release()
|
||||
r, ok := h.(header.IPv6RawPayloadHeader)
|
||||
if ok {
|
||||
rawPayloadHeader = r
|
||||
@@ -1594,8 +1596,9 @@ func IPv6ExtHdr(headers ...IPv6ExtHdrChecker) NetworkChecker {
|
||||
|
||||
payloadIterator := header.MakeIPv6PayloadIterator(
|
||||
header.IPv6ExtensionHeaderIdentifier(extHdrs.IPv6.NextHeader()),
|
||||
buffer.NewWithData(extHdrs.IPv6.Payload()),
|
||||
bufferv2.MakeWithData(extHdrs.IPv6.Payload()),
|
||||
)
|
||||
defer payloadIterator.Release()
|
||||
|
||||
for _, check := range headers {
|
||||
h, done, err := payloadIterator.Next()
|
||||
@@ -1608,6 +1611,7 @@ func IPv6ExtHdr(headers ...IPv6ExtHdrChecker) NetworkChecker {
|
||||
return
|
||||
}
|
||||
check(t, h)
|
||||
h.Release()
|
||||
}
|
||||
// Validate we consumed all headers.
|
||||
//
|
||||
@@ -1630,6 +1634,8 @@ func IPv6ExtHdr(headers ...IPv6ExtHdrChecker) NetworkChecker {
|
||||
if _, ok := h.(header.IPv6RawPayloadHeader); !ok {
|
||||
t.Errorf("got payloadIterator.Next() = (%T, _, _), want = (header.IPv6RawPayloadHeader, _, _)", h)
|
||||
continue
|
||||
} else {
|
||||
h.Release()
|
||||
}
|
||||
wantDone = true
|
||||
}
|
||||
@@ -1684,6 +1690,9 @@ func IPv6HopByHopExtensionHeader(checkers ...IPv6ExtHdrOptionChecker) IPv6ExtHdr
|
||||
t.Errorf("got optionsIterator.Next() = (%T, %t, _), want = (_, false, _)", opt, done)
|
||||
}
|
||||
f(t, opt)
|
||||
if uo, ok := opt.(*header.IPv6UnknownExtHdrOption); ok {
|
||||
uo.Data.Release()
|
||||
}
|
||||
}
|
||||
// Validate all options were consumed.
|
||||
for {
|
||||
@@ -1698,6 +1707,9 @@ func IPv6HopByHopExtensionHeader(checkers ...IPv6ExtHdrOptionChecker) IPv6ExtHdr
|
||||
if done {
|
||||
break
|
||||
}
|
||||
if uo, ok := opt.(*header.IPv6UnknownExtHdrOption); ok {
|
||||
uo.Data.Release()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ go_library(
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/seqnum",
|
||||
"@com_github_google_btree//:go_default_library",
|
||||
@@ -50,7 +50,7 @@ go_test(
|
||||
],
|
||||
deps = [
|
||||
":header",
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/rand",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/prependable",
|
||||
@@ -70,7 +70,7 @@ go_test(
|
||||
],
|
||||
library = ":header",
|
||||
deps = [
|
||||
"//pkg/buffer",
|
||||
"//pkg/bufferv2",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/testutil",
|
||||
"@com_github_google_go_cmp//cmp:go_default_library",
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
@@ -196,10 +196,10 @@ func Checksum(buf []byte, initial uint16) uint16 {
|
||||
// bytes in the given Buffer.
|
||||
//
|
||||
// The initial checksum must have been computed on an even number of bytes.
|
||||
func ChecksumBuffer(buf buffer.Buffer, initial uint16) uint16 {
|
||||
func ChecksumBuffer(buf bufferv2.Buffer, initial uint16) uint16 {
|
||||
var c Checksumer
|
||||
buf.Apply(func(b []byte) {
|
||||
c.Add(b)
|
||||
buf.Apply(func(v *bufferv2.View) {
|
||||
c.Add(v.AsSlice())
|
||||
})
|
||||
return ChecksumCombine(initial, c.Checksum())
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
)
|
||||
@@ -193,6 +193,8 @@ func testICMPChecksum(t *testing.T, headerChecksum func() uint16, icmpChecksum f
|
||||
close(start)
|
||||
}
|
||||
|
||||
// TODO(b/239732156): Replace magic constants with names corresponding to what
|
||||
// they represent ICMP.
|
||||
func TestICMPv4Checksum(t *testing.T) {
|
||||
rnd := rand.New(rand.NewSource(42))
|
||||
|
||||
@@ -206,8 +208,8 @@ func TestICMPv4Checksum(t *testing.T) {
|
||||
if _, err := rnd.Read(buf); err != nil {
|
||||
t.Fatalf("rnd.Read failed: %v", err)
|
||||
}
|
||||
b := buffer.NewWithData(buf[:5])
|
||||
b.AppendOwned(buf[5:])
|
||||
b := bufferv2.MakeWithData(buf[:5])
|
||||
b.Append(bufferv2.NewViewWithData(buf[5:]))
|
||||
|
||||
want := header.Checksum(b.Flatten(), 0)
|
||||
want = ^header.Checksum(h, want)
|
||||
@@ -231,9 +233,9 @@ func TestICMPv6Checksum(t *testing.T) {
|
||||
if _, err := rnd.Read(buf); err != nil {
|
||||
t.Fatalf("rnd.Read failed: %v", err)
|
||||
}
|
||||
b := buffer.NewWithData(buf[:7])
|
||||
b.AppendOwned(buf[7:10])
|
||||
b.AppendOwned(buf[10:])
|
||||
b := bufferv2.MakeWithData(buf[:7])
|
||||
b.Append(bufferv2.NewViewWithData(buf[7:10]))
|
||||
b.Append(bufferv2.NewViewWithData(buf[10:]))
|
||||
|
||||
dst := header.IPv6Loopback
|
||||
src := header.IPv6Loopback
|
||||
|
||||
@@ -15,15 +15,13 @@
|
||||
package header
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/buffer"
|
||||
"gvisor.dev/gvisor/pkg/bufferv2"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
)
|
||||
|
||||
@@ -154,26 +152,44 @@ func ipv6OptionsAlignmentPadding(headerOffset int, align int, alignOffset int) i
|
||||
// These headers include IPv6 extension headers or upper layer data.
|
||||
type IPv6PayloadHeader interface {
|
||||
isIPv6PayloadHeader()
|
||||
|
||||
// Release frees all resources held by the header.
|
||||
Release()
|
||||
}
|
||||
|
||||
// IPv6RawPayloadHeader the remainder of an IPv6 payload after an iterator
|
||||
// encounters a Next Header field it does not recognize as an IPv6 extension
|
||||
// header.
|
||||
// header. The caller is responsible for releasing the underlying buffer after
|
||||
// it's no longer needed.
|
||||
type IPv6RawPayloadHeader struct {
|
||||
Identifier IPv6ExtensionHeaderIdentifier
|
||||
Buf buffer.Buffer
|
||||
Buf bufferv2.Buffer
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6RawPayloadHeader) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (i IPv6RawPayloadHeader) Release() {
|
||||
i.Buf.Release()
|
||||
}
|
||||
|
||||
// ipv6OptionsExtHdr is an IPv6 extension header that holds options.
|
||||
type ipv6OptionsExtHdr []byte
|
||||
type ipv6OptionsExtHdr struct {
|
||||
buf *bufferv2.View
|
||||
}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (i ipv6OptionsExtHdr) Release() {
|
||||
if i.buf != nil {
|
||||
i.buf.Release()
|
||||
}
|
||||
}
|
||||
|
||||
// Iter returns an iterator over the IPv6 extension header options held in b.
|
||||
func (b ipv6OptionsExtHdr) Iter() IPv6OptionsExtHdrOptionsIterator {
|
||||
func (i ipv6OptionsExtHdr) Iter() IPv6OptionsExtHdrOptionsIterator {
|
||||
it := IPv6OptionsExtHdrOptionsIterator{}
|
||||
it.reader.Reset(b)
|
||||
it.reader = i.buf
|
||||
return it
|
||||
}
|
||||
|
||||
@@ -187,7 +203,7 @@ func (b ipv6OptionsExtHdr) Iter() IPv6OptionsExtHdrOptionsIterator {
|
||||
// modify the backing payload so long as the IPv6OptionsExtHdrOptionsIterator
|
||||
// obtained before modification is no longer used.
|
||||
type IPv6OptionsExtHdrOptionsIterator struct {
|
||||
reader bytes.Reader
|
||||
reader *bufferv2.View
|
||||
|
||||
// optionOffset is the number of bytes from the first byte of the
|
||||
// options field to the beginning of the current option.
|
||||
@@ -283,7 +299,7 @@ var ErrMalformedIPv6ExtHdrOption = errors.New("malformed IPv6 extension header o
|
||||
// header option that is unknown by the parsing utilities.
|
||||
type IPv6UnknownExtHdrOption struct {
|
||||
Identifier IPv6ExtHdrOptionIdentifier
|
||||
Data []byte
|
||||
Data *bufferv2.View
|
||||
}
|
||||
|
||||
// UnknownAction implements IPv6OptionUnknownAction.UnknownAction.
|
||||
@@ -335,9 +351,9 @@ func (i *IPv6OptionsExtHdrOptionsIterator) Next() (IPv6ExtHdrOption, bool, error
|
||||
}
|
||||
|
||||
// Do we have enough bytes in the reader for the next option?
|
||||
if n := i.reader.Len(); n < int(length) {
|
||||
// Reset the reader to effectively consume the remaining buffer.
|
||||
i.reader.Reset(nil)
|
||||
if n := i.reader.Size(); n < int(length) {
|
||||
// Consume the remaining buffer.
|
||||
i.reader.TrimFront(i.reader.Size())
|
||||
|
||||
// We return the same error as if we failed to read a non-padding option
|
||||
// so consumers of this iterator don't need to differentiate between
|
||||
@@ -350,13 +366,11 @@ func (i *IPv6OptionsExtHdrOptionsIterator) Next() (IPv6ExtHdrOption, bool, error
|
||||
switch id {
|
||||
case ipv6PadNExtHdrOptionIdentifier:
|
||||
// Special-case the variable length padding option to avoid a copy.
|
||||
if _, err := i.reader.Seek(int64(length), io.SeekCurrent); err != nil {
|
||||
panic(fmt.Sprintf("error when skipping PadN (N = %d) option's data bytes: %s", length, err))
|
||||
}
|
||||
i.reader.TrimFront(int(length))
|
||||
continue
|
||||
case ipv6RouterAlertHopByHopOptionIdentifier:
|
||||
var routerAlertValue [ipv6RouterAlertPayloadLength]byte
|
||||
if n, err := io.ReadFull(&i.reader, routerAlertValue[:]); err != nil {
|
||||
if n, err := io.ReadFull(i.reader, routerAlertValue[:]); err != nil {
|
||||
switch err {
|
||||
case io.EOF, io.ErrUnexpectedEOF:
|
||||
return nil, true, fmt.Errorf("got invalid length (%d) for router alert option (want = %d): %w", length, ipv6RouterAlertPayloadLength, ErrMalformedIPv6ExtHdrOption)
|
||||
@@ -368,11 +382,8 @@ func (i *IPv6OptionsExtHdrOptionsIterator) Next() (IPv6ExtHdrOption, bool, error
|
||||
}
|
||||
return &IPv6RouterAlertOption{Value: IPv6RouterAlertValue(binary.BigEndian.Uint16(routerAlertValue[:]))}, false, nil
|
||||
default:
|
||||
bytes := make([]byte, length)
|
||||
if n, err := io.ReadFull(&i.reader, bytes); err != nil {
|
||||
// io.ReadFull may return io.EOF if i.reader has been exhausted. We use
|
||||
// io.ErrUnexpectedEOF instead as the io.EOF is unexpected given the
|
||||
// Length field found in the option.
|
||||
bytes := bufferv2.NewView(int(length))
|
||||
if n, err := io.CopyN(bytes, i.reader, int64(length)); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
@@ -404,14 +415,21 @@ func (IPv6DestinationOptionsExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// IPv6RoutingExtHdr is a buffer holding the Routing extension header specific
|
||||
// data as outlined in RFC 8200 section 4.4.
|
||||
type IPv6RoutingExtHdr []byte
|
||||
type IPv6RoutingExtHdr struct {
|
||||
Buf *bufferv2.View
|
||||
}
|
||||
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6RoutingExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (b IPv6RoutingExtHdr) Release() {
|
||||
b.Buf.Release()
|
||||
}
|
||||
|
||||
// SegmentsLeft returns the Segments Left field.
|
||||
func (b IPv6RoutingExtHdr) SegmentsLeft() uint8 {
|
||||
return b[ipv6RoutingExtHdrSegmentsLeftIdx]
|
||||
return b.Buf.AsSlice()[ipv6RoutingExtHdrSegmentsLeftIdx]
|
||||
}
|
||||
|
||||
// IPv6FragmentExtHdr is a buffer holding the Fragment extension header specific
|
||||
@@ -423,6 +441,9 @@ type IPv6FragmentExtHdr [6]byte
|
||||
// isIPv6PayloadHeader implements IPv6PayloadHeader.isIPv6PayloadHeader.
|
||||
func (IPv6FragmentExtHdr) isIPv6PayloadHeader() {}
|
||||
|
||||
// Release implements IPv6PayloadHeader.Release.
|
||||
func (IPv6FragmentExtHdr) Release() {}
|
||||
|
||||
// FragmentOffset returns the Fragment Offset field.
|
||||
//
|
||||
// This value indicates where the buffer following the Fragment extension header
|
||||
@@ -467,9 +488,7 @@ type IPv6PayloadIterator struct {
|
||||
// The identifier of the next header to parse.
|
||||
nextHdrIdentifier IPv6ExtensionHeaderIdentifier
|
||||
|
||||
// reader is an io.Reader over payload.
|
||||
reader bufio.Reader
|
||||
payload buffer.Buffer
|
||||
payload bufferv2.Buffer
|
||||
|
||||
// Indicates to the iterator that it should return the remaining payload as a
|
||||
// raw payload on the next call to Next.
|
||||
@@ -500,32 +519,31 @@ func (i IPv6PayloadIterator) ParseOffset() uint32 {
|
||||
}
|
||||
|
||||
// MakeIPv6PayloadIterator returns an iterator over the IPv6 payload containing
|
||||
// extension headers, or a raw payload if the payload cannot be parsed.
|
||||
func MakeIPv6PayloadIterator(nextHdrIdentifier IPv6ExtensionHeaderIdentifier, payload buffer.Buffer) IPv6PayloadIterator {
|
||||
readers := payload.Readers()
|
||||
readerPs := make([]io.Reader, 0, len(readers))
|
||||
for i := range readers {
|
||||
readerPs = append(readerPs, &readers[i])
|
||||
}
|
||||
|
||||
// extension headers, or a raw payload if the payload cannot be parsed. The
|
||||
// iterator takes ownership of the payload.
|
||||
func MakeIPv6PayloadIterator(nextHdrIdentifier IPv6ExtensionHeaderIdentifier, payload bufferv2.Buffer) IPv6PayloadIterator {
|
||||
return IPv6PayloadIterator{
|
||||
nextHdrIdentifier: nextHdrIdentifier,
|
||||
payload: payload.Clone(),
|
||||
// We need a buffer of size 1 for calls to bufio.Reader.ReadByte.
|
||||
reader: *bufio.NewReaderSize(io.MultiReader(readerPs...), 1),
|
||||
nextOffset: IPv6FixedHeaderSize,
|
||||
payload: payload,
|
||||
nextOffset: IPv6FixedHeaderSize,
|
||||
}
|
||||
}
|
||||
|
||||
// Release frees the resources owned by the iterator.
|
||||
func (i *IPv6PayloadIterator) Release() {
|
||||
i.payload.Release()
|
||||
}
|
||||
|
||||
// AsRawHeader returns the remaining payload of i as a raw header and
|
||||
// optionally consumes the iterator.
|
||||
//
|
||||
// If consume is true, calls to Next after calling AsRawHeader on i will
|
||||
// indicate that the iterator is done.
|
||||
// indicate that the iterator is done. The returned header takes ownership of
|
||||
// its payload.
|
||||
func (i *IPv6PayloadIterator) AsRawHeader(consume bool) IPv6RawPayloadHeader {
|
||||
identifier := i.nextHdrIdentifier
|
||||
|
||||
var buf buffer.Buffer
|
||||
var buf bufferv2.Buffer
|
||||
if consume {
|
||||
// Since we consume the iterator, we return the payload as is.
|
||||
buf = i.payload
|
||||
@@ -564,21 +582,21 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
// Is the header we are parsing a known extension header?
|
||||
switch i.nextHdrIdentifier {
|
||||
case IPv6HopByHopOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, bytes, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr: bytes}, false, nil
|
||||
return IPv6HopByHopOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6RoutingExtHdrIdentifier:
|
||||
nextHdrIdentifier, bytes, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6RoutingExtHdr(bytes), false, nil
|
||||
return IPv6RoutingExtHdr{view}, false, nil
|
||||
case IPv6FragmentExtHdrIdentifier:
|
||||
var data [6]byte
|
||||
// We ignore the returned bytes because we know the fragment extension
|
||||
@@ -602,13 +620,13 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return fragmentExtHdr, false, nil
|
||||
case IPv6DestinationOptionsExtHdrIdentifier:
|
||||
nextHdrIdentifier, bytes, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
nextHdrIdentifier, view, err := i.nextHeaderData(false /* fragmentHdr */, nil)
|
||||
if err != nil {
|
||||
return nil, true, err
|
||||
}
|
||||
|
||||
i.nextHdrIdentifier = nextHdrIdentifier
|
||||
return IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr: bytes}, false, nil
|
||||
return IPv6DestinationOptionsExtHdr{ipv6OptionsExtHdr{view}}, false, nil
|
||||
case IPv6NoNextHeaderIdentifier:
|
||||
// This indicates the end of the IPv6 payload.
|
||||
return nil, true, nil
|
||||
@@ -629,21 +647,20 @@ func (i *IPv6PayloadIterator) Next() (IPv6PayloadHeader, bool, error) {
|
||||
// If bytes is not nil, extension header specific data will be read into bytes
|
||||
// if it has enough capacity. If bytes is provided but does not have enough
|
||||
// capacity for the data, nextHeaderData will panic.
|
||||
func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, []byte, error) {
|
||||
func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IPv6ExtensionHeaderIdentifier, *bufferv2.View, error) {
|
||||
// We ignore the number of bytes read because we know we will only ever read
|
||||
// at max 1 bytes since rune has a length of 1. If we read 0 bytes, the Read
|
||||
// would return io.EOF to indicate that io.Reader has reached the end of the
|
||||
// payload.
|
||||
nextHdrIdentifier, err := i.reader.ReadByte()
|
||||
i.payload.TrimFront(1)
|
||||
rdr := i.payload.AsBufferReader()
|
||||
nextHdrIdentifier, err := rdr.ReadByte()
|
||||
if err != nil {
|
||||
return 0, nil, fmt.Errorf("error when reading the Next Header field for extension header with id = %d: %w", i.nextHdrIdentifier, err)
|
||||
}
|
||||
i.parseOffset++
|
||||
|
||||
var length uint8
|
||||
length, err = i.reader.ReadByte()
|
||||
i.payload.TrimFront(1)
|
||||
length, err = rdr.ReadByte()
|
||||
|
||||
if err != nil {
|
||||
if fragmentHdr {
|
||||
@@ -668,19 +685,24 @@ func (i *IPv6PayloadIterator) nextHeaderData(fragmentHdr bool, bytes []byte) (IP
|
||||
i.nextOffset += uint32((length + 1) * ipv6ExtHdrLenBytesPerUnit)
|
||||
|
||||
bytesLen := int(length)*ipv6ExtHdrLenBytesPerUnit + ipv6ExtHdrLenBytesExcluded
|
||||
if bytes == nil {
|
||||
bytes = make([]byte, bytesLen)
|
||||
} else if n := len(bytes); n < bytesLen {
|
||||
panic(fmt.Sprintf("bytes only has space for %d bytes but need space for %d bytes (length = %d) for extension header with id = %d", n, bytesLen, length, i.nextHdrIdentifier))
|
||||
if fragmentHdr {
|
||||
if n := len(bytes); n < bytesLen {
|
||||
panic(fmt.Sprintf("bytes only has space for %d bytes but need space for %d bytes (length = %d) for extension header with id = %d", n, bytesLen, length, i.nextHdrIdentifier))
|
||||
}
|
||||
if n, err := io.ReadFull(&rdr, bytes); err != nil {
|
||||
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
||||
}
|
||||
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), nil, nil
|
||||
}
|
||||
|
||||
n, err := io.ReadFull(&i.reader, bytes)
|
||||
i.payload.TrimFront(int64(n))
|
||||
if err != nil {
|
||||
v := bufferv2.NewView(bytesLen)
|
||||
if n, err := io.CopyN(v, &rdr, int64(bytesLen)); err != nil {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
v.Release()
|
||||
return 0, nil, fmt.Errorf("read %d out of %d extension header data bytes (length = %d) for header with id = %d: %w", n, bytesLen, length, i.nextHdrIdentifier, err)
|
||||
}
|
||||
|
||||
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), bytes, nil
|
||||
return IPv6ExtensionHeaderIdentifier(nextHdrIdentifier), v, nil
|
||||
}
|
||||
|
||||
// IPv6SerializableExtHdr provides serialization for IPv6 extension
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user