change Queue.Read signature

This commit is contained in:
JackDoan
2026-05-14 11:42:59 -05:00
parent 1b59636028
commit c61de54ec3
14 changed files with 181 additions and 242 deletions

View File

@@ -2,6 +2,8 @@ package tio
import (
"io"
"github.com/slackhq/nebula/wire"
)
// QueueSet holds one or many Queue objects and helps close them in an orderly way.
@@ -13,17 +15,10 @@ type QueueSet interface {
Add(fd int) error
}
// Capabilities advertises which kernel offload features a Queue
// successfully negotiated. Callers consult this to decide which coalescers
// to wire onto the write path — a Queue without TSO can't usefully accept a
// TCPCoalescer, and a Queue without USO can't accept a UDPCoalescer.
// Capabilities advertises which kernel offload features a Queue successfully negotiated.
// Callers consult this to decide which coalescers to wire onto the write path.
type Capabilities struct {
// TSO means the FD was opened with IFF_VNET_HDR and the kernel agreed
// to TUN_F_TSO4|TSO6 — i.e. WriteGSO with GSOProtoTCP is safe.
TSO bool
// USO means the kernel additionally agreed to TUN_F_USO4|USO6, so
// WriteGSO with GSOProtoUDP is safe. Linux ≥ 6.2.
USO bool
//none yet!
}
// Queue is a readable/writable Poll queue. One Queue is driven by a single
@@ -31,62 +26,16 @@ type Capabilities struct {
type Queue interface {
io.Closer
// Read returns one or more packets. The returned Packet.Bytes slices
// are borrowed from the Queue's internal buffer and are only valid
// until the next Read or Close on this Queue - callers must encrypt
// or copy each slice before the next call.
Read() ([]Packet, error)
// Read will read at least 1 packet from the tun (up to len(p))
// mem will be used to provide the backing for each of p[n].Bytes
// Returns the number of packets actually read, or error
Read(p []wire.TunPacket, mem []byte) (int, error)
// Write emits a single packet on the plaintext (outside→inside)
// delivery path. Not safe for concurrent Writes.
// delivery path.
Write(p []byte) (int, error)
// Capabilities returns the Queue's negotiated offload capabilities,
// or the zero value when q does not advertise any.
Capabilities() Capabilities
}
// Packet is the unit Queue.Read returns. Bytes points into the queue's
// internal buffer and is only valid until the next Read or Close on the
// queue that produced it. GSO is the zero value for an already-segmented
// IP datagram; when non-zero it describes a kernel-supplied TSO/USO
// superpacket the caller must segment before consuming.
type Packet struct {
Bytes []byte
GSO GSOInfo
}
// GSOInfo describes a kernel-supplied superpacket sitting in Packet.Bytes.
// The zero value means "not a superpacket" — Bytes is one regular IP
// datagram and no segmentation is required.
type GSOInfo struct {
// Size is the GSO segment size: max payload bytes per segment
// (== TCP MSS for TSO, == UDP payload chunk for USO). Zero means
// not a superpacket.
Size uint16
// HdrLen is the total L3+L4 header length within Bytes (already
// corrected via correctHdrLen, so safe to slice on).
HdrLen uint16
// CsumStart is the L4 header offset inside Bytes (== L3 header
// length).
CsumStart uint16
// Proto picks the L4 protocol (TCP or UDP) so the segmenter knows
// which checksum/header layout to apply.
Proto GSOProto
}
// IsSuperpacket reports whether g describes a multi-segment GSO/USO
// superpacket that needs segmentation before its bytes can be encrypted
// and sent on the wire.
func (g GSOInfo) IsSuperpacket() bool { return g.Size > 0 }
// GSOProto selects the L4 protocol for a GSO superpacket. Determines which
// VIRTIO_NET_HDR_GSO_* type the writer stamps and which checksum offset
// inside the transport header virtio NEEDS_CSUM expects.
type GSOProto uint8
const (
GSOProtoNone GSOProto = iota
GSOProtoTCP
GSOProtoUDP
)

View File

@@ -6,6 +6,7 @@ import (
"sync"
"sync/atomic"
"github.com/slackhq/nebula/wire"
"golang.org/x/sys/unix"
)
@@ -16,9 +17,6 @@ type Poll struct {
writePoll [2]unix.PollFd
writeLock sync.Mutex
closed atomic.Bool
readBuf []byte
batchRet [1]Packet
}
func newPoll(fd int, shutdownFd int) (*Poll, error) {
@@ -28,8 +26,7 @@ func newPoll(fd int, shutdownFd int) (*Poll, error) {
}
out := &Poll{
fd: fd,
readBuf: make([]byte, 65535),
fd: fd,
readPoll: [2]unix.PollFd{
{Fd: int32(fd), Events: unix.POLLIN},
{Fd: int32(shutdownFd), Events: unix.POLLIN},
@@ -97,13 +94,17 @@ func (t *Poll) blockOnWrite() error {
return nil
}
func (t *Poll) Read() ([]Packet, error) {
n, err := t.readOne(t.readBuf)
if err != nil {
return nil, err
func (t *Poll) Read(p []wire.TunPacket, mem []byte) (int, error) {
if len(p) == 0 || len(mem) == 0 {
return 0, nil //todo should this be an err?
}
t.batchRet[0] = Packet{Bytes: t.readBuf[:n]}
return t.batchRet[:], nil
p[0].Meta = struct{}{}
n, err := t.readOne(mem)
if err != nil {
return 0, err
}
p[0].Bytes = mem[:n]
return 1, nil
}
func (t *Poll) readOne(to []byte) (int, error) {
@@ -163,5 +164,5 @@ func (t *Poll) Close() error {
}
func (t *Poll) Capabilities() Capabilities {
return Capabilities{TSO: false, USO: false}
return Capabilities{}
}