mirror of
https://github.com/slackhq/nebula.git
synced 2026-05-16 04:47:38 +02:00
93 lines
3.5 KiB
Go
93 lines
3.5 KiB
Go
package tio
|
|
|
|
import (
|
|
"io"
|
|
|
|
"github.com/slackhq/nebula/wire"
|
|
)
|
|
|
|
// QueueSet holds one or many Queue objects and helps close them in an orderly way.
|
|
type QueueSet interface {
|
|
io.Closer
|
|
Queues() []Queue
|
|
|
|
// Add takes a tun fd, adds it to the set, and prepares it for use as a Queue.
|
|
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.
|
|
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
|
|
}
|
|
|
|
// Queue is a readable/writable Poll queue. One Queue is driven by a single
|
|
// read goroutine plus a single writer (see Write below).
|
|
type Queue interface {
|
|
io.Closer
|
|
|
|
// 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.
|
|
// Callers should size mem and p to avoid exhausting mem before p.
|
|
// 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.
|
|
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
|
|
}
|
|
|
|
// GSOWriter is implemented by Queues that can emit a TCP or UDP superpacket
|
|
// assembled from a header prefix plus one or more borrowed payload
|
|
// fragments, in a single vectored write (writev with a leading
|
|
// virtio_net_hdr). This lets the coalescer avoid copying payload bytes
|
|
// between the caller's decrypt buffer and the TUN. Backends without GSO
|
|
// support do not implement this interface and coalescing is skipped.
|
|
//
|
|
// hdr contains the IPv4/IPv6 header prefix (mutable - callers will have
|
|
// filled in total length and IP csum). transportHdr is the TCP or UDP
|
|
// header (mutable - the L4 checksum field must hold the pseudo-header
|
|
// partial, single-fold not inverted, per virtio NEEDS_CSUM semantics).
|
|
// pays are non-overlapping payload fragments whose concatenation is the
|
|
// full superpacket payload; they are read-only from the writer's
|
|
// perspective and must remain valid until the call returns. Every segment
|
|
// in pays except possibly the last is exactly the same size. proto picks
|
|
// the L4 protocol so the writer knows which GSOType / CsumOffset to set.
|
|
//
|
|
// Callers should also consult CapsProvider (via SupportsGSO or
|
|
// QueueCapabilities) for the per-protocol negotiated capability; an
|
|
// implementation of GSOWriter is necessary but not sufficient since USO
|
|
// may not have been negotiated even when TSO was.
|
|
type GSOWriter interface {
|
|
WriteGSO(hdr []byte, transportHdr []byte, pays [][]byte, proto wire.GSOProto) error
|
|
}
|
|
|
|
// SupportsGSO reports whether w implements GSOWriter and the underlying
|
|
// queue advertises the negotiated capability for `want`. A writer that
|
|
// implements GSOWriter but not CapsProvider is treated as permissive
|
|
// (used by tests and fakes that don't negotiate).
|
|
func SupportsGSO(w Queue, want wire.GSOProto) (GSOWriter, bool) {
|
|
gw, ok := w.(GSOWriter)
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
caps := w.Capabilities()
|
|
switch want {
|
|
case wire.GSOProtoTCP:
|
|
return gw, caps.TSO
|
|
case wire.GSOProtoUDP:
|
|
return gw, caps.USO
|
|
default:
|
|
return gw, false
|
|
}
|
|
}
|