mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Define tcpip.Payloader in terms of io.Reader
Fixes #1509. PiperOrigin-RevId: 353295589
This commit is contained in:
committed by
gVisor bot
parent
527ef5fc03
commit
6c0e1d9cfe
@@ -69,7 +69,7 @@ func (t *Task) BlockWithTimeout(C chan struct{}, haveTimeout bool, timeout time.
|
||||
// syserror.ErrInterrupted if t is interrupted.
|
||||
//
|
||||
// Preconditions: The caller must be running on the task goroutine.
|
||||
func (t *Task) BlockWithDeadline(C chan struct{}, haveDeadline bool, deadline ktime.Time) error {
|
||||
func (t *Task) BlockWithDeadline(C <-chan struct{}, haveDeadline bool, deadline ktime.Time) error {
|
||||
if !haveDeadline {
|
||||
return t.block(C, nil)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ go_library(
|
||||
"//pkg/syserr",
|
||||
"//pkg/syserror",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/network/ipv4",
|
||||
"//pkg/tcpip/network/ipv6",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
// be used to expose certain endpoints to the sentry while leaving others out,
|
||||
// for example, TCP endpoints and Unix-domain endpoints.
|
||||
//
|
||||
// Lock ordering: netstack => mm: ioSequencePayload copies user memory inside
|
||||
// Lock ordering: netstack => mm: ioSequenceReadWriter copies user memory inside
|
||||
// tcpip.Endpoint.Write(). Netstack is allowed to (and does) hold locks during
|
||||
// this operation.
|
||||
package netstack
|
||||
@@ -55,7 +55,6 @@ import (
|
||||
"gvisor.dev/gvisor/pkg/syserr"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
@@ -440,45 +439,10 @@ func (s *SocketOperations) WriteTo(ctx context.Context, _ *fs.File, dst io.Write
|
||||
return int64(res.Count), nil
|
||||
}
|
||||
|
||||
// ioSequencePayload implements tcpip.Payload.
|
||||
//
|
||||
// t copies user memory bytes on demand based on the requested size.
|
||||
type ioSequencePayload struct {
|
||||
ctx context.Context
|
||||
src usermem.IOSequence
|
||||
}
|
||||
|
||||
// FullPayload implements tcpip.Payloader.FullPayload
|
||||
func (i *ioSequencePayload) FullPayload() ([]byte, *tcpip.Error) {
|
||||
return i.Payload(int(i.src.NumBytes()))
|
||||
}
|
||||
|
||||
// Payload implements tcpip.Payloader.Payload.
|
||||
func (i *ioSequencePayload) Payload(size int) ([]byte, *tcpip.Error) {
|
||||
if max := int(i.src.NumBytes()); size > max {
|
||||
size = max
|
||||
}
|
||||
v := buffer.NewView(size)
|
||||
if _, err := i.src.CopyIn(i.ctx, v); err != nil {
|
||||
// EOF can be returned only if src is a file and this means it
|
||||
// is in a splice syscall and the error has to be ignored.
|
||||
if err == io.EOF {
|
||||
return v, nil
|
||||
}
|
||||
return nil, tcpip.ErrBadAddress
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// DropFirst drops the first n bytes from underlying src.
|
||||
func (i *ioSequencePayload) DropFirst(n int) {
|
||||
i.src = i.src.DropFirst(int(n))
|
||||
}
|
||||
|
||||
// Write implements fs.FileOperations.Write.
|
||||
func (s *SocketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IOSequence, _ int64) (int64, error) {
|
||||
f := &ioSequencePayload{ctx: ctx, src: src}
|
||||
n, err := s.Endpoint.Write(f, tcpip.WriteOptions{})
|
||||
r := src.Reader(ctx)
|
||||
n, err := s.Endpoint.Write(r, tcpip.WriteOptions{})
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
return 0, syserror.ErrWouldBlock
|
||||
}
|
||||
@@ -486,69 +450,40 @@ func (s *SocketOperations) Write(ctx context.Context, _ *fs.File, src usermem.IO
|
||||
return 0, syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
|
||||
if int64(n) < src.NumBytes() {
|
||||
return int64(n), syserror.ErrWouldBlock
|
||||
if n < src.NumBytes() {
|
||||
return n, syserror.ErrWouldBlock
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// readerPayload implements tcpip.Payloader.
|
||||
//
|
||||
// It allocates a view and reads from a reader on-demand, based on available
|
||||
// capacity in the endpoint.
|
||||
type readerPayload struct {
|
||||
ctx context.Context
|
||||
r io.Reader
|
||||
count int64
|
||||
err error
|
||||
var _ tcpip.Payloader = (*limitedPayloader)(nil)
|
||||
|
||||
type limitedPayloader struct {
|
||||
io.LimitedReader
|
||||
}
|
||||
|
||||
// FullPayload implements tcpip.Payloader.FullPayload.
|
||||
func (r *readerPayload) FullPayload() ([]byte, *tcpip.Error) {
|
||||
return r.Payload(int(r.count))
|
||||
}
|
||||
|
||||
// Payload implements tcpip.Payloader.Payload.
|
||||
func (r *readerPayload) Payload(size int) ([]byte, *tcpip.Error) {
|
||||
if size > int(r.count) {
|
||||
size = int(r.count)
|
||||
}
|
||||
v := buffer.NewView(size)
|
||||
n, err := r.r.Read(v)
|
||||
if n > 0 {
|
||||
// We ignore the error here. It may re-occur on subsequent
|
||||
// reads, but for now we can enqueue some amount of data.
|
||||
r.count -= int64(n)
|
||||
return v[:n], nil
|
||||
}
|
||||
if err == syserror.ErrWouldBlock {
|
||||
return nil, tcpip.ErrWouldBlock
|
||||
} else if err != nil {
|
||||
r.err = err // Save for propation.
|
||||
return nil, tcpip.ErrBadAddress
|
||||
}
|
||||
|
||||
// There is no data and no error. Return an error, which will propagate
|
||||
// r.err, which will be nil. This is the desired result: (0, nil).
|
||||
return nil, tcpip.ErrBadAddress
|
||||
func (l limitedPayloader) Len() int {
|
||||
return int(l.N)
|
||||
}
|
||||
|
||||
// ReadFrom implements fs.FileOperations.ReadFrom.
|
||||
func (s *SocketOperations) ReadFrom(ctx context.Context, _ *fs.File, r io.Reader, count int64) (int64, error) {
|
||||
f := &readerPayload{ctx: ctx, r: r, count: count}
|
||||
n, err := s.Endpoint.Write(f, tcpip.WriteOptions{
|
||||
f := limitedPayloader{
|
||||
LimitedReader: io.LimitedReader{
|
||||
R: r,
|
||||
N: count,
|
||||
},
|
||||
}
|
||||
n, err := s.Endpoint.Write(&f, tcpip.WriteOptions{
|
||||
// Reads may be destructive but should be very fast,
|
||||
// so we can't release the lock while copying data.
|
||||
Atomic: true,
|
||||
})
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
return n, syserror.ErrWouldBlock
|
||||
} else if err != nil {
|
||||
return int64(n), f.err // Propagate error.
|
||||
if err == tcpip.ErrBadBuffer {
|
||||
err = nil
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
return n, syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
|
||||
// Readiness returns a mask of ready events for socket s.
|
||||
@@ -2836,45 +2771,46 @@ func (s *socketOpsCommon) SendMsg(t *kernel.Task, src usermem.IOSequence, to []b
|
||||
EndOfRecord: flags&linux.MSG_EOR != 0,
|
||||
}
|
||||
|
||||
v := &ioSequencePayload{t, src}
|
||||
n, err := s.Endpoint.Write(v, opts)
|
||||
dontWait := flags&linux.MSG_DONTWAIT != 0
|
||||
if err == nil && (n >= v.src.NumBytes() || dontWait) {
|
||||
// Complete write.
|
||||
return int(n), nil
|
||||
}
|
||||
if err != nil && (err != tcpip.ErrWouldBlock || dontWait) {
|
||||
return int(n), syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
// We'll have to block. Register for notification and keep trying to
|
||||
// send all the data.
|
||||
e, ch := waiter.NewChannelEntry(nil)
|
||||
s.EventRegister(&e, waiter.EventOut)
|
||||
defer s.EventUnregister(&e)
|
||||
|
||||
v.DropFirst(int(n))
|
||||
total := n
|
||||
r := src.Reader(t)
|
||||
var (
|
||||
total int64
|
||||
entry waiter.Entry
|
||||
ch <-chan struct{}
|
||||
)
|
||||
for {
|
||||
n, err = s.Endpoint.Write(v, opts)
|
||||
v.DropFirst(int(n))
|
||||
n, err := s.Endpoint.Write(r, opts)
|
||||
total += n
|
||||
|
||||
if err != nil && err != tcpip.ErrWouldBlock && total == 0 {
|
||||
return 0, syserr.TranslateNetstackError(err)
|
||||
if flags&linux.MSG_DONTWAIT != 0 {
|
||||
return int(total), syserr.TranslateNetstackError(err)
|
||||
}
|
||||
|
||||
if err == nil && v.src.NumBytes() == 0 || err != nil && err != tcpip.ErrWouldBlock {
|
||||
return int(total), nil
|
||||
}
|
||||
|
||||
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
|
||||
if err == syserror.ETIMEDOUT {
|
||||
return int(total), syserr.ErrTryAgain
|
||||
switch err {
|
||||
case nil:
|
||||
if total == src.NumBytes() {
|
||||
break
|
||||
}
|
||||
// handleIOError will consume errors from t.Block if needed.
|
||||
return int(total), syserr.FromError(err)
|
||||
fallthrough
|
||||
case tcpip.ErrWouldBlock:
|
||||
if ch == nil {
|
||||
// We'll have to block. Register for notification and keep trying to
|
||||
// send all the data.
|
||||
entry, ch = waiter.NewChannelEntry(nil)
|
||||
s.EventRegister(&entry, waiter.EventOut)
|
||||
defer s.EventUnregister(&entry)
|
||||
} else {
|
||||
// Don't wait immediately after registration in case more data
|
||||
// became available between when we last checked and when we setup
|
||||
// the notification.
|
||||
if err := t.BlockWithDeadline(ch, haveDeadline, deadline); err != nil {
|
||||
if err == syserror.ETIMEDOUT {
|
||||
return int(total), syserr.ErrTryAgain
|
||||
}
|
||||
// handleIOError will consume errors from t.Block if needed.
|
||||
return int(total), syserr.FromError(err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
return int(total), syserr.TranslateNetstackError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,8 +128,8 @@ func (s *SocketVFS2) Write(ctx context.Context, src usermem.IOSequence, opts vfs
|
||||
return 0, syserror.EOPNOTSUPP
|
||||
}
|
||||
|
||||
f := &ioSequencePayload{ctx: ctx, src: src}
|
||||
n, err := s.Endpoint.Write(f, tcpip.WriteOptions{})
|
||||
r := src.Reader(ctx)
|
||||
n, err := s.Endpoint.Write(r, tcpip.WriteOptions{})
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
return 0, syserror.ErrWouldBlock
|
||||
}
|
||||
@@ -137,11 +137,11 @@ func (s *SocketVFS2) Write(ctx context.Context, src usermem.IOSequence, opts vfs
|
||||
return 0, syserr.TranslateNetstackError(err).ToError()
|
||||
}
|
||||
|
||||
if int64(n) < src.NumBytes() {
|
||||
return int64(n), syserror.ErrWouldBlock
|
||||
if n < src.NumBytes() {
|
||||
return n, syserror.ErrWouldBlock
|
||||
}
|
||||
|
||||
return int64(n), nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Accept implements the linux syscall accept(2) for sockets backed by
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package gonet
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -354,8 +355,6 @@ func (c *TCPConn) Write(b []byte) (int, error) {
|
||||
default:
|
||||
}
|
||||
|
||||
v := buffer.NewViewFromBytes(b)
|
||||
|
||||
// We must handle two soft failure conditions simultaneously:
|
||||
// 1. Write may write nothing and return tcpip.ErrWouldBlock.
|
||||
// If this happens, we need to register for notifications if we have
|
||||
@@ -368,22 +367,23 @@ func (c *TCPConn) Write(b []byte) (int, error) {
|
||||
// There is no guarantee that all of the condition #1s will occur before
|
||||
// all of the condition #2s or visa-versa.
|
||||
var (
|
||||
err *tcpip.Error
|
||||
nbytes int
|
||||
reg bool
|
||||
notifyCh chan struct{}
|
||||
r bytes.Reader
|
||||
nbytes int
|
||||
entry waiter.Entry
|
||||
ch <-chan struct{}
|
||||
)
|
||||
for nbytes < len(b) && (err == tcpip.ErrWouldBlock || err == nil) {
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
if !reg {
|
||||
// Only register once.
|
||||
reg = true
|
||||
for nbytes != len(b) {
|
||||
r.Reset(b[nbytes:])
|
||||
n, err := c.ep.Write(&r, tcpip.WriteOptions{})
|
||||
nbytes += int(n)
|
||||
switch err {
|
||||
case nil:
|
||||
case tcpip.ErrWouldBlock:
|
||||
if ch == nil {
|
||||
entry, ch = waiter.NewChannelEntry(nil)
|
||||
|
||||
// Create wait queue entry that notifies a channel.
|
||||
var waitEntry waiter.Entry
|
||||
waitEntry, notifyCh = waiter.NewChannelEntry(nil)
|
||||
c.wq.EventRegister(&waitEntry, waiter.EventOut)
|
||||
defer c.wq.EventUnregister(&waitEntry)
|
||||
c.wq.EventRegister(&entry, waiter.EventOut)
|
||||
defer c.wq.EventUnregister(&entry)
|
||||
} else {
|
||||
// Don't wait immediately after registration in case more data
|
||||
// became available between when we last checked and when we setup
|
||||
@@ -391,22 +391,15 @@ func (c *TCPConn) Write(b []byte) (int, error) {
|
||||
select {
|
||||
case <-deadline:
|
||||
return nbytes, c.newOpError("write", &timeoutError{})
|
||||
case <-notifyCh:
|
||||
case <-ch:
|
||||
continue
|
||||
}
|
||||
}
|
||||
default:
|
||||
return nbytes, c.newOpError("write", errors.New(err.String()))
|
||||
}
|
||||
|
||||
var n int64
|
||||
n, err = c.ep.Write(tcpip.SlicePayload(v), tcpip.WriteOptions{})
|
||||
nbytes += int(n)
|
||||
v.TrimFront(int(n))
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nbytes, nil
|
||||
}
|
||||
|
||||
return nbytes, c.newOpError("write", errors.New(err.String()))
|
||||
return nbytes, nil
|
||||
}
|
||||
|
||||
// Close implements net.Conn.Close.
|
||||
@@ -644,16 +637,18 @@ func (c *UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
}
|
||||
|
||||
// If we're being called by Write, there is no addr
|
||||
wopts := tcpip.WriteOptions{}
|
||||
writeOptions := tcpip.WriteOptions{}
|
||||
if addr != nil {
|
||||
ua := addr.(*net.UDPAddr)
|
||||
wopts.To = &tcpip.FullAddress{Addr: tcpip.Address(ua.IP), Port: uint16(ua.Port)}
|
||||
writeOptions.To = &tcpip.FullAddress{
|
||||
Addr: tcpip.Address(ua.IP),
|
||||
Port: uint16(ua.Port),
|
||||
}
|
||||
}
|
||||
|
||||
v := buffer.NewView(len(b))
|
||||
copy(v, b)
|
||||
|
||||
n, err := c.ep.Write(tcpip.SlicePayload(v), wopts)
|
||||
var r bytes.Reader
|
||||
r.Reset(b)
|
||||
n, err := c.ep.Write(&r, writeOptions)
|
||||
if err == tcpip.ErrWouldBlock {
|
||||
// Create wait queue entry that notifies a channel.
|
||||
waitEntry, notifyCh := waiter.NewChannelEntry(nil)
|
||||
@@ -666,7 +661,7 @@ func (c *UDPConn) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
case <-notifyCh:
|
||||
}
|
||||
|
||||
n, err = c.ep.Write(tcpip.SlicePayload(v), wopts)
|
||||
n, err = c.ep.Write(&r, writeOptions)
|
||||
if err != tcpip.ErrWouldBlock {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package ipv6
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"net"
|
||||
"reflect"
|
||||
@@ -638,7 +639,6 @@ func TestLinkResolution(t *testing.T) {
|
||||
pkt := header.ICMPv6(hdr.Prepend(header.ICMPv6EchoMinimumSize))
|
||||
pkt.SetType(header.ICMPv6EchoRequest)
|
||||
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
|
||||
// doesn't provoke NDP discovery.
|
||||
@@ -648,8 +648,12 @@ func TestLinkResolution(t *testing.T) {
|
||||
t.Fatalf("NewEndpoint(_) = (_, %s), want = (_, nil)", err)
|
||||
}
|
||||
|
||||
if _, err := ep.Write(payload, tcpip.WriteOptions{To: &tcpip.FullAddress{NIC: nicID, Addr: lladdr1}}); err != nil {
|
||||
t.Fatalf("ep.Write(_): %s", err)
|
||||
{
|
||||
var r bytes.Reader
|
||||
r.Reset(hdr.View())
|
||||
if _, err := ep.Write(&r, tcpip.WriteOptions{To: &tcpip.FullAddress{NIC: nicID, Addr: lladdr1}}); err != nil {
|
||||
t.Fatalf("ep.Write(_): %s", err)
|
||||
}
|
||||
}
|
||||
for _, args := range []routeArgs{
|
||||
{src: c.linkEP0, dst: c.linkEP1, typ: header.ICMPv6NeighborSolicit, remoteLinkAddr: header.EthernetAddressFromMulticastIPv6Address(header.SolicitedNodeAddr(lladdr1))},
|
||||
|
||||
@@ -8,7 +8,6 @@ go_binary(
|
||||
visibility = ["//:sandbox"],
|
||||
deps = [
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/fdbased",
|
||||
"//pkg/tcpip/link/rawfile",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
@@ -51,7 +51,6 @@ import (
|
||||
"time"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/fdbased"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/rawfile"
|
||||
@@ -71,24 +70,21 @@ func writer(ch chan struct{}, ep tcpip.Endpoint) {
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
r := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
v := buffer.NewView(1024)
|
||||
n, err := r.Read(v)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v.CapLength(n)
|
||||
for len(v) > 0 {
|
||||
n, err := ep.Write(tcpip.SlicePayload(v), tcpip.WriteOptions{})
|
||||
if err != nil {
|
||||
fmt.Println("Write failed:", err)
|
||||
return
|
||||
var b bytes.Buffer
|
||||
if err := func() error {
|
||||
for {
|
||||
if _, err := b.ReadFrom(os.Stdin); err != nil {
|
||||
return fmt.Errorf("b.ReadFrom failed: %w", err)
|
||||
}
|
||||
|
||||
v.TrimFront(int(n))
|
||||
for b.Len() != 0 {
|
||||
if _, err := ep.Write(&b, tcpip.WriteOptions{Atomic: true}); err != nil {
|
||||
return fmt.Errorf("ep.Write failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"io"
|
||||
"log"
|
||||
@@ -58,7 +59,9 @@ func (e *tcpipError) Error() string {
|
||||
}
|
||||
|
||||
func (e *endpointWriter) Write(p []byte) (int, error) {
|
||||
n, err := e.ep.Write(tcpip.SlicePayload(p), tcpip.WriteOptions{})
|
||||
var r bytes.Reader
|
||||
r.Reset(p)
|
||||
n, err := e.ep.Write(&r, tcpip.WriteOptions{})
|
||||
if err != nil {
|
||||
return int(n), &tcpipError{
|
||||
inner: err,
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package stack_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
@@ -95,10 +96,11 @@ func (f *fakeTransportEndpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions
|
||||
return 0, tcpip.ErrNoRoute
|
||||
}
|
||||
|
||||
v, err := p.FullPayload()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
v := make([]byte, p.Len())
|
||||
if _, err := io.ReadFull(p, v); err != nil {
|
||||
return 0, tcpip.ErrBadBuffer
|
||||
}
|
||||
|
||||
pkt := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
ReserveHeaderBytes: int(f.route.MaxHeaderLength()) + fakeTransHeaderLen,
|
||||
Data: buffer.View(v).ToVectorisedView(),
|
||||
@@ -520,8 +522,10 @@ func TestTransportSend(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create buffer that will hold the payload.
|
||||
view := buffer.NewView(30)
|
||||
if _, err := ep.Write(tcpip.SlicePayload(view), tcpip.WriteOptions{}); err != nil {
|
||||
b := make([]byte, 30)
|
||||
var r bytes.Reader
|
||||
r.Reset(b)
|
||||
if _, err := ep.Write(&r, tcpip.WriteOptions{}); err != nil {
|
||||
t.Fatalf("write failed: %v", err)
|
||||
}
|
||||
|
||||
|
||||
+7
-21
@@ -29,6 +29,7 @@
|
||||
package tcpip
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -471,30 +472,15 @@ type FullAddress struct {
|
||||
// This interface allows the endpoint to request the amount of data it needs
|
||||
// based on internal buffers without exposing them.
|
||||
type Payloader interface {
|
||||
// FullPayload returns all available bytes.
|
||||
FullPayload() ([]byte, *Error)
|
||||
io.Reader
|
||||
|
||||
// Payload returns a slice containing at most size bytes.
|
||||
Payload(size int) ([]byte, *Error)
|
||||
// Len returns the number of bytes of the unread portion of the
|
||||
// Reader.
|
||||
Len() int
|
||||
}
|
||||
|
||||
// SlicePayload implements Payloader for slices.
|
||||
//
|
||||
// This is typically used for tests.
|
||||
type SlicePayload []byte
|
||||
|
||||
// FullPayload implements Payloader.FullPayload.
|
||||
func (s SlicePayload) FullPayload() ([]byte, *Error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Payload implements Payloader.Payload.
|
||||
func (s SlicePayload) Payload(size int) ([]byte, *Error) {
|
||||
if size > len(s) {
|
||||
size = len(s)
|
||||
}
|
||||
return s[:size], nil
|
||||
}
|
||||
var _ Payloader = (*bytes.Buffer)(nil)
|
||||
var _ Payloader = (*bytes.Reader)(nil)
|
||||
|
||||
var _ io.Writer = (*SliceWriter)(nil)
|
||||
|
||||
|
||||
@@ -436,9 +436,10 @@ func TestForwarding(t *testing.T) {
|
||||
write := func(ep tcpip.Endpoint, data []byte) {
|
||||
t.Helper()
|
||||
|
||||
dataPayload := tcpip.SlicePayload(data)
|
||||
var r bytes.Reader
|
||||
r.Reset(data)
|
||||
var wOpts tcpip.WriteOptions
|
||||
n, err := ep.Write(dataPayload, wOpts)
|
||||
n, err := ep.Write(&r, wOpts)
|
||||
if err != nil {
|
||||
t.Fatalf("ep.Write(_, %#v): %s", wOpts, err)
|
||||
}
|
||||
@@ -486,7 +487,7 @@ func TestForwarding(t *testing.T) {
|
||||
|
||||
read(serverCH, serverEP, data, clientAddr)
|
||||
|
||||
data = tcpip.SlicePayload([]byte{5, 6, 7, 8, 9, 10, 11, 12})
|
||||
data = []byte{5, 6, 7, 8, 9, 10, 11, 12}
|
||||
write(serverEP, data)
|
||||
read(epsAndAddrs.clientReadableCH, epsAndAddrs.clientEP, data, serverAddr)
|
||||
})
|
||||
|
||||
@@ -209,8 +209,10 @@ func TestPing(t *testing.T) {
|
||||
defer ep.Close()
|
||||
|
||||
icmpBuf := test.icmpBuf(t)
|
||||
var r bytes.Reader
|
||||
r.Reset(icmpBuf)
|
||||
wOpts := tcpip.WriteOptions{To: &tcpip.FullAddress{Addr: test.remoteAddr}}
|
||||
if n, err := ep.Write(tcpip.SlicePayload(icmpBuf), wOpts); err != nil {
|
||||
if n, err := ep.Write(&r, wOpts); err != nil {
|
||||
t.Fatalf("ep.Write(_, _): %s", err)
|
||||
} else if want := int64(len(icmpBuf)); n != want {
|
||||
t.Fatalf("got ep.Write(_, _) = (%d, _), want = (%d, _)", n, want)
|
||||
@@ -360,9 +362,11 @@ func TestTCPLinkResolutionFailure(t *testing.T) {
|
||||
// Wait for an error due to link resolution failing, or the endpoint to be
|
||||
// writable.
|
||||
<-ch
|
||||
var r bytes.Reader
|
||||
r.Reset([]byte{0})
|
||||
var wOpts tcpip.WriteOptions
|
||||
if n, err := clientEP.Write(tcpip.SlicePayload(nil), wOpts); err != test.expectedWriteErr {
|
||||
t.Errorf("got clientEP.Write(nil, %#v) = (%d, %s), want = (_, %s)", wOpts, n, err, test.expectedWriteErr)
|
||||
if n, err := clientEP.Write(&r, wOpts); err != test.expectedWriteErr {
|
||||
t.Errorf("got clientEP.Write(_, %#v) = (%d, %s), want = (_, %s)", wOpts, n, err, test.expectedWriteErr)
|
||||
}
|
||||
|
||||
if test.expectedWriteErr == nil {
|
||||
|
||||
@@ -232,7 +232,9 @@ func TestLoopbackAcceptAllInSubnetUDP(t *testing.T) {
|
||||
Port: localPort,
|
||||
},
|
||||
}
|
||||
n, err := sep.Write(tcpip.SlicePayload(data), wopts)
|
||||
var r bytes.Reader
|
||||
r.Reset(data)
|
||||
n, err := sep.Write(&r, wopts)
|
||||
if err != nil {
|
||||
t.Fatalf("sep.Write(_, _): %s", err)
|
||||
}
|
||||
|
||||
@@ -586,8 +586,10 @@ func TestReuseAddrAndBroadcast(t *testing.T) {
|
||||
Port: localPort,
|
||||
},
|
||||
}
|
||||
data := tcpip.SlicePayload([]byte{byte(i), 2, 3, 4})
|
||||
if n, err := wep.ep.Write(data, writeOpts); err != nil {
|
||||
data := []byte{byte(i), 2, 3, 4}
|
||||
var r bytes.Reader
|
||||
r.Reset(data)
|
||||
if n, err := wep.ep.Write(&r, writeOpts); err != nil {
|
||||
t.Fatalf("eps[%d].Write(_, _): %s", i, err)
|
||||
} else if want := int64(len(data)); n != want {
|
||||
t.Fatalf("got eps[%d].Write(_, _) = (%d, nil), want = (%d, nil)", i, n, want)
|
||||
|
||||
@@ -194,9 +194,11 @@ func TestLocalPing(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
payload := tcpip.SlicePayload(test.icmpBuf(t))
|
||||
payload := test.icmpBuf(t)
|
||||
var r bytes.Reader
|
||||
r.Reset(payload)
|
||||
var wOpts tcpip.WriteOptions
|
||||
if n, err := ep.Write(payload, wOpts); err != nil {
|
||||
if n, err := ep.Write(&r, wOpts); err != nil {
|
||||
t.Fatalf("ep.Write(%#v, %#v): %s", payload, wOpts, err)
|
||||
} else if n != int64(len(payload)) {
|
||||
t.Fatalf("got ep.Write(%#v, %#v) = (%d, nil), want = (%d, nil)", payload, wOpts, n, len(payload))
|
||||
@@ -329,12 +331,14 @@ func TestLocalUDP(t *testing.T) {
|
||||
Port: 80,
|
||||
}
|
||||
|
||||
clientPayload := tcpip.SlicePayload([]byte{1, 2, 3, 4})
|
||||
clientPayload := []byte{1, 2, 3, 4}
|
||||
{
|
||||
var r bytes.Reader
|
||||
r.Reset(clientPayload)
|
||||
wOpts := tcpip.WriteOptions{
|
||||
To: &serverAddr,
|
||||
}
|
||||
if n, err := client.Write(clientPayload, wOpts); err != subTest.expectedWriteErr {
|
||||
if n, err := client.Write(&r, wOpts); err != subTest.expectedWriteErr {
|
||||
t.Fatalf("got client.Write(%#v, %#v) = (%d, %s), want = (_, %s)", clientPayload, wOpts, n, err, subTest.expectedWriteErr)
|
||||
} else if subTest.expectedWriteErr != nil {
|
||||
// Nothing else to test if we expected not to be able to send the
|
||||
@@ -376,12 +380,14 @@ func TestLocalUDP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
serverPayload := tcpip.SlicePayload([]byte{1, 2, 3, 4})
|
||||
serverPayload := []byte{1, 2, 3, 4}
|
||||
{
|
||||
var r bytes.Reader
|
||||
r.Reset(serverPayload)
|
||||
wOpts := tcpip.WriteOptions{
|
||||
To: &clientAddr,
|
||||
}
|
||||
if n, err := server.Write(serverPayload, wOpts); err != nil {
|
||||
if n, err := server.Write(&r, wOpts); err != nil {
|
||||
t.Fatalf("server.Write(%#v, %#v): %s", serverPayload, wOpts, err)
|
||||
} else if n != int64(len(serverPayload)) {
|
||||
t.Fatalf("got server.Write(%#v, %#v) = (%d, nil), want = (%d, nil)", serverPayload, wOpts, n, len(serverPayload))
|
||||
|
||||
@@ -313,11 +313,12 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
|
||||
route = r
|
||||
}
|
||||
|
||||
v, err := p.FullPayload()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
v := make([]byte, p.Len())
|
||||
if _, err := io.ReadFull(p, v); err != nil {
|
||||
return 0, tcpip.ErrBadBuffer
|
||||
}
|
||||
|
||||
var err *tcpip.Error
|
||||
switch e.NetProto {
|
||||
case header.IPv4ProtocolNumber:
|
||||
err = send4(route, e.ID.LocalPort, v, e.ttl, e.owner)
|
||||
|
||||
@@ -207,7 +207,7 @@ func (ep *endpoint) Read(dst io.Writer, opts tcpip.ReadOptions) (tcpip.ReadResul
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (*endpoint) Write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tcpip.Error) {
|
||||
func (*endpoint) Write(tcpip.Payloader, tcpip.WriteOptions) (int64, *tcpip.Error) {
|
||||
// TODO(gvisor.dev/issue/173): Implement.
|
||||
return 0, tcpip.ErrInvalidOptionValue
|
||||
}
|
||||
|
||||
@@ -280,9 +280,9 @@ func (e *endpoint) write(p tcpip.Payloader, opts tcpip.WriteOptions) (int64, *tc
|
||||
return 0, tcpip.ErrInvalidEndpointState
|
||||
}
|
||||
|
||||
payloadBytes, err := p.FullPayload()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
payloadBytes := make([]byte, p.Len())
|
||||
if _, err := io.ReadFull(p, payloadBytes); err != nil {
|
||||
return 0, tcpip.ErrBadBuffer
|
||||
}
|
||||
|
||||
// If this is an unassociated socket and callee provided a nonzero
|
||||
|
||||
@@ -99,7 +99,6 @@ go_test(
|
||||
"//pkg/rand",
|
||||
"//pkg/sync",
|
||||
"//pkg/tcpip",
|
||||
"//pkg/tcpip/buffer",
|
||||
"//pkg/tcpip/checker",
|
||||
"//pkg/tcpip/header",
|
||||
"//pkg/tcpip/link/loopback",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user