mirror of
https://github.com/slackhq/nebula.git
synced 2026-05-16 04:47:38 +02:00
change Queue.Read signature
This commit is contained in:
21
interface.go
21
interface.go
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/rcrowley/go-metrics"
|
"github.com/rcrowley/go-metrics"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
|
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"github.com/slackhq/nebula/firewall"
|
"github.com/slackhq/nebula/firewall"
|
||||||
@@ -372,45 +373,45 @@ func (f *Interface) listenOut(i int) {
|
|||||||
f.l.Debug("underlay reader is done", "reader", i)
|
f.l.Debug("underlay reader is done", "reader", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Interface) listenIn(reader tio.Queue, i int) {
|
func (f *Interface) listenIn(reader tio.Queue, q int) {
|
||||||
// Pinning this thread (and goroutine) to a single CPU keeps every sendmmsg from this goroutine going through the
|
// Pinning this thread (and goroutine) to a single CPU keeps every sendmmsg from this goroutine going through the
|
||||||
// same TX ring on the nic, so the wire sees per-flow order.
|
// same TX ring on the nic, so the wire sees per-flow order.
|
||||||
cpu := i % runtime.NumCPU()
|
cpu := q % runtime.NumCPU()
|
||||||
if n := len(f.cpuAffinity); n > 0 {
|
if n := len(f.cpuAffinity); n > 0 {
|
||||||
cpu = f.cpuAffinity[i%n]
|
cpu = f.cpuAffinity[q%n]
|
||||||
}
|
}
|
||||||
if err := util.PinThreadToCPU(cpu); err != nil {
|
if err := util.PinThreadToCPU(cpu); err != nil {
|
||||||
f.l.Warn("failed to pin tun reader to CPU", "queue", i, "cpu", cpu, "err", err)
|
f.l.Warn("failed to pin tun reader to CPU", "queue", q, "cpu", cpu, "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rejectBuf := make([]byte, mtu)
|
rejectBuf := make([]byte, mtu)
|
||||||
arenaSize := batch.SendBatchCap * (udp.MTU + 32)
|
arenaSize := batch.SendBatchCap * (udp.MTU + 32)
|
||||||
sb := batch.NewSendBatch(f.writers[i], batch.SendBatchCap, arenaSize)
|
sb := batch.NewSendBatch(f.writers[q], batch.SendBatchCap, arenaSize)
|
||||||
fwPacket := &firewall.Packet{}
|
fwPacket := &firewall.Packet{}
|
||||||
nb := make([]byte, 12, 12)
|
nb := make([]byte, 12, 12)
|
||||||
|
|
||||||
conntrackCache := firewall.NewConntrackCacheTicker(f.ctx, f.l, f.conntrackCacheTimeout)
|
conntrackCache := firewall.NewConntrackCacheTicker(f.ctx, f.l, f.conntrackCacheTimeout)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
pkts, err := reader.Read()
|
n, err := reader.Read(packets, packetMem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !f.closed.Load() {
|
if !f.closed.Load() {
|
||||||
f.l.Error("Error while reading outbound packet, closing", "error", err, "reader", i)
|
f.l.Error("Error while reading outbound packet, closing", "error", err, "reader", q)
|
||||||
f.onFatal(err)
|
f.onFatal(err)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
ctCache := conntrackCache.Get()
|
ctCache := conntrackCache.Get()
|
||||||
for _, pkt := range pkts {
|
for i := range n {
|
||||||
f.consumeInsidePacket(pkt, fwPacket, nb, sb, rejectBuf, i, ctCache)
|
f.consumeInsidePacket(packets[i], fwPacket, nb, sb, rejectBuf, q, ctCache)
|
||||||
}
|
}
|
||||||
if err := sb.Flush(); err != nil {
|
if err := sb.Flush(); err != nil {
|
||||||
f.l.Error("Failed to write outgoing batch", "error", err, "writer", i)
|
f.l.Error("Failed to write outgoing batch", "error", err, "writer", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
f.l.Debug("overlay reader is done", "reader", i)
|
f.l.Debug("overlay reader is done", "reader", q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) {
|
func (f *Interface) RegisterConfigChangeCallbacks(c *config.C) {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package tio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueueSet holds one or many Queue objects and helps close them in an orderly way.
|
// QueueSet holds one or many Queue objects and helps close them in an orderly way.
|
||||||
@@ -13,10 +15,8 @@ type QueueSet interface {
|
|||||||
Add(fd int) error
|
Add(fd int) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Capabilities advertises which kernel offload features a Queue
|
// Capabilities advertises which kernel offload features a Queue successfully negotiated.
|
||||||
// successfully negotiated. Callers consult this to decide which coalescers
|
// Callers consult this to decide which coalescers to wire onto the write path.
|
||||||
// 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.
|
|
||||||
type Capabilities struct {
|
type Capabilities struct {
|
||||||
// TSO means the FD was opened with IFF_VNET_HDR and the kernel agreed
|
// 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.
|
// to TUN_F_TSO4|TSO6 — i.e. WriteGSO with GSOProtoTCP is safe.
|
||||||
@@ -31,14 +31,13 @@ type Capabilities struct {
|
|||||||
type Queue interface {
|
type Queue interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
|
|
||||||
// Read returns one or more packets. The returned Packet.Bytes slices
|
// Read will read at least 1 packet from the tun (up to len(p))
|
||||||
// are borrowed from the Queue's internal buffer and are only valid
|
// mem will be used to provide the backing for each of p[n].Bytes
|
||||||
// until the next Read or Close on this Queue - callers must encrypt
|
// Returns the number of packets actually read, or error
|
||||||
// or copy each slice before the next call.
|
Read(p []wire.TunPacket, mem []byte) (int, error)
|
||||||
Read() ([]Packet, error)
|
|
||||||
|
|
||||||
// Write emits a single packet on the plaintext (outside→inside)
|
// Write emits a single packet on the plaintext (outside→inside)
|
||||||
// delivery path. Not safe for concurrent Writes.
|
// delivery path.
|
||||||
Write(p []byte) (int, error)
|
Write(p []byte) (int, error)
|
||||||
|
|
||||||
// Capabilities returns the Queue's negotiated offload capabilities,
|
// Capabilities returns the Queue's negotiated offload capabilities,
|
||||||
@@ -46,15 +45,6 @@ type Queue interface {
|
|||||||
Capabilities() Capabilities
|
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.
|
// GSOInfo describes a kernel-supplied superpacket sitting in Packet.Bytes.
|
||||||
// The zero value means "not a superpacket" — Bytes is one regular IP
|
// The zero value means "not a superpacket" — Bytes is one regular IP
|
||||||
@@ -120,16 +110,12 @@ type GSOWriter interface {
|
|||||||
// queue advertises the negotiated capability for `want`. A writer that
|
// queue advertises the negotiated capability for `want`. A writer that
|
||||||
// implements GSOWriter but not CapsProvider is treated as permissive
|
// implements GSOWriter but not CapsProvider is treated as permissive
|
||||||
// (used by tests and fakes that don't negotiate).
|
// (used by tests and fakes that don't negotiate).
|
||||||
func SupportsGSO(w any, want GSOProto) (GSOWriter, bool) {
|
func SupportsGSO(w Queue, want GSOProto) (GSOWriter, bool) {
|
||||||
gw, ok := w.(GSOWriter)
|
gw, ok := w.(GSOWriter)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
cp, ok := w.(CapsProvider)
|
caps := w.Capabilities()
|
||||||
if !ok {
|
|
||||||
return gw, true
|
|
||||||
}
|
|
||||||
caps := cp.Capabilities()
|
|
||||||
switch want {
|
switch want {
|
||||||
case GSOProtoTCP:
|
case GSOProtoTCP:
|
||||||
return gw, caps.TSO
|
return gw, caps.TSO
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tun struct {
|
type tun struct {
|
||||||
@@ -25,18 +26,19 @@ type tun struct {
|
|||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *slog.Logger
|
l *slog.Logger
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.rwc.Read(t.readBuf)
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.rwc.Read(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Write(p []byte) (int, error) {
|
func (t *tun) Write(p []byte) (int, error) {
|
||||||
@@ -57,7 +59,6 @@ func newTunFromFd(c *config.C, l *slog.Logger, deviceFd int, vpnNetworks []netip
|
|||||||
fd: deviceFd,
|
fd: deviceFd,
|
||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
l: l,
|
l: l,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := t.reload(c, true)
|
err := t.reload(c, true)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
netroute "golang.org/x/net/route"
|
netroute "golang.org/x/net/route"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -35,9 +36,6 @@ type tun struct {
|
|||||||
|
|
||||||
// cache out buffer since we need to prepend 4 bytes for tun metadata
|
// cache out buffer since we need to prepend 4 bytes for tun metadata
|
||||||
out []byte
|
out []byte
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ifReq struct {
|
type ifReq struct {
|
||||||
@@ -133,7 +131,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*t
|
|||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
DefaultMTU: c.GetInt("tun.mtu", DefaultMTU),
|
DefaultMTU: c.GetInt("tun.mtu", DefaultMTU),
|
||||||
l: l,
|
l: l,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.reload(c, true)
|
err = t.reload(c, true)
|
||||||
@@ -507,22 +504,17 @@ func delRoute(prefix netip.Prefix, gateway netroute.Addr) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) readOne(to []byte) (int, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
buf := make([]byte, len(to)+4)
|
if len(p) == 0 || len(mem) <= 4 {
|
||||||
|
return 0, nil //todo should this be an err?
|
||||||
n, err := t.rwc.Read(buf)
|
|
||||||
|
|
||||||
copy(to, buf[4:])
|
|
||||||
return n - 4, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
|
||||||
n, err := t.readOne(t.readBuf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.rwc.Read(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[4:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write is only valid for single threaded use
|
// Write is only valid for single threaded use
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/iputil"
|
"github.com/slackhq/nebula/iputil"
|
||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type disabledTun struct {
|
type disabledTun struct {
|
||||||
@@ -21,47 +22,8 @@ type disabledTun struct {
|
|||||||
// Track these metrics since we don't have the tun device to do it for us
|
// Track these metrics since we don't have the tun device to do it for us
|
||||||
tx metrics.Counter
|
tx metrics.Counter
|
||||||
rx metrics.Counter
|
rx metrics.Counter
|
||||||
l *slog.Logger
|
|
||||||
numReaders int
|
numReaders int
|
||||||
}
|
l *slog.Logger
|
||||||
|
|
||||||
// disabledQueue is one tio.Queue view onto a shared disabledTun. Each queue
|
|
||||||
// owns a private batchRet so concurrent Read calls from different reader
|
|
||||||
// goroutines do not race on the returned slice.
|
|
||||||
type disabledQueue struct {
|
|
||||||
parent *disabledTun
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *disabledQueue) Capabilities() tio.Capabilities {
|
|
||||||
return tio.Capabilities{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *disabledQueue) Read() ([]tio.Packet, error) {
|
|
||||||
r, ok := <-q.parent.read
|
|
||||||
if !ok {
|
|
||||||
return nil, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
q.parent.tx.Inc(1)
|
|
||||||
if q.parent.l.Enabled(context.Background(), slog.LevelDebug) {
|
|
||||||
q.parent.l.Debug("Write payload", "raw", prettyPacket(r))
|
|
||||||
}
|
|
||||||
|
|
||||||
q.batchRet[0] = tio.Packet{Bytes: r}
|
|
||||||
return q.batchRet[:], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write on a queue forwards to the underlying disabledTun. All queues share
|
|
||||||
// one ICMP-handling/log path so this is a thin pass-through.
|
|
||||||
func (q *disabledQueue) Write(b []byte) (int, error) {
|
|
||||||
return q.parent.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close on a queue is a no-op. The shared channel and metrics are owned by
|
|
||||||
// the disabledTun; Close on the device tears them down once for everybody.
|
|
||||||
func (q *disabledQueue) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDisabledTun(vpnNetworks []netip.Prefix, queueLen int, metricsEnabled bool, l *slog.Logger) *disabledTun {
|
func newDisabledTun(vpnNetworks []netip.Prefix, queueLen int, metricsEnabled bool, l *slog.Logger) *disabledTun {
|
||||||
@@ -99,6 +61,37 @@ func (*disabledTun) Name() string {
|
|||||||
return "disabled"
|
return "disabled"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *disabledTun) readOne(b []byte) (int, error) {
|
||||||
|
r, ok := <-t.read
|
||||||
|
if !ok {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(r) > len(b) {
|
||||||
|
return 0, fmt.Errorf("packet larger than mtu: %d > %d bytes", len(r), len(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
t.tx.Inc(1)
|
||||||
|
if t.l.Enabled(context.Background(), slog.LevelDebug) {
|
||||||
|
t.l.Debug("Write payload", "raw", prettyPacket(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy(b, r), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *disabledTun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
|
return 0, nil //todo should this be an err?
|
||||||
|
}
|
||||||
|
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 *disabledTun) handleICMPEchoRequest(b []byte) bool {
|
func (t *disabledTun) handleICMPEchoRequest(b []byte) bool {
|
||||||
out := make([]byte, len(b))
|
out := make([]byte, len(b))
|
||||||
out = iputil.CreateICMPEchoResponse(b, out)
|
out = iputil.CreateICMPEchoResponse(b, out)
|
||||||
@@ -142,11 +135,15 @@ func (t *disabledTun) NewMultiQueueReader() error {
|
|||||||
func (t *disabledTun) Readers() []tio.Queue {
|
func (t *disabledTun) Readers() []tio.Queue {
|
||||||
out := make([]tio.Queue, t.numReaders)
|
out := make([]tio.Queue, t.numReaders)
|
||||||
for i := range t.numReaders {
|
for i := range t.numReaders {
|
||||||
out[i] = &disabledQueue{parent: t}
|
out[i] = t
|
||||||
}
|
}
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *disabledTun) Capabilities() tio.Capabilities {
|
||||||
|
return tio.Capabilities{}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *disabledTun) Close() error {
|
func (t *disabledTun) Close() error {
|
||||||
if t.read != nil {
|
if t.read != nil {
|
||||||
close(t.read)
|
close(t.read)
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
|
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
@@ -102,9 +103,6 @@ type tun struct {
|
|||||||
readPoll [2]unix.PollFd
|
readPoll [2]unix.PollFd
|
||||||
writePoll [2]unix.PollFd
|
writePoll [2]unix.PollFd
|
||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// blockOnRead waits until the tun fd is readable or shutdown has been signaled.
|
// blockOnRead waits until the tun fd is readable or shutdown has been signaled.
|
||||||
@@ -159,13 +157,17 @@ func (t *tun) blockOnWrite() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.readOne(t.readBuf)
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.readOne(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[4:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) readOne(to []byte) (int, error) {
|
func (t *tun) readOne(to []byte) (int, error) {
|
||||||
@@ -386,7 +388,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*t
|
|||||||
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
||||||
l: l,
|
l: l,
|
||||||
fd: fd,
|
fd: fd,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
shutdownR: shutdownR,
|
shutdownR: shutdownR,
|
||||||
shutdownW: shutdownW,
|
shutdownW: shutdownW,
|
||||||
readPoll: [2]unix.PollFd{
|
readPoll: [2]unix.PollFd{
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tun struct {
|
type tun struct {
|
||||||
@@ -27,18 +28,19 @@ type tun struct {
|
|||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *slog.Logger
|
l *slog.Logger
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.rwc.Read(t.readBuf)
|
if len(p) == 0 || len(mem) <= 4 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.rwc.Read(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[4:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Write(p []byte) (int, error) {
|
func (t *tun) Write(p []byte) (int, error) {
|
||||||
@@ -59,7 +61,6 @@ func newTunFromFd(c *config.C, l *slog.Logger, deviceFd int, vpnNetworks []netip
|
|||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
rwc: &tunReadCloser{f: file},
|
rwc: &tunReadCloser{f: file},
|
||||||
l: l,
|
l: l,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := t.reload(c, true)
|
err := t.reload(c, true)
|
||||||
@@ -118,18 +119,9 @@ type tunReadCloser struct {
|
|||||||
wBuf []byte
|
wBuf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read returns a packet with the BSD 4-byte header, watch out!
|
||||||
func (tr *tunReadCloser) Read(to []byte) (int, error) {
|
func (tr *tunReadCloser) Read(to []byte) (int, error) {
|
||||||
tr.rMu.Lock()
|
return tr.f.Read(to)
|
||||||
defer tr.rMu.Unlock()
|
|
||||||
|
|
||||||
if cap(tr.rBuf) < len(to)+4 {
|
|
||||||
tr.rBuf = make([]byte, len(to)+4)
|
|
||||||
}
|
|
||||||
tr.rBuf = tr.rBuf[:len(to)+4]
|
|
||||||
|
|
||||||
n, err := tr.f.Read(tr.rBuf)
|
|
||||||
copy(to, tr.rBuf[4:])
|
|
||||||
return n - 4, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tr *tunReadCloser) Write(from []byte) (int, error) {
|
func (tr *tunReadCloser) Write(from []byte) (int, error) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
netroute "golang.org/x/net/route"
|
netroute "golang.org/x/net/route"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -66,18 +67,19 @@ type tun struct {
|
|||||||
l *slog.Logger
|
l *slog.Logger
|
||||||
f *os.File
|
f *os.File
|
||||||
fd int
|
fd int
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.readOne(t.readBuf)
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.readOne(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Readers() []tio.Queue {
|
func (t *tun) Readers() []tio.Queue {
|
||||||
@@ -122,7 +124,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*t
|
|||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
||||||
l: l,
|
l: l,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.reload(c, true)
|
err = t.reload(c, true)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
netroute "golang.org/x/net/route"
|
netroute "golang.org/x/net/route"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@@ -59,18 +60,19 @@ type tun struct {
|
|||||||
fd int
|
fd int
|
||||||
// cache out buffer since we need to prepend 4 bytes for tun metadata
|
// cache out buffer since we need to prepend 4 bytes for tun metadata
|
||||||
out []byte
|
out []byte
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) Read() ([]tio.Packet, error) {
|
func (t *tun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.readOne(t.readBuf)
|
if len(p) == 0 || len(mem) <= 4 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.f.Read(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[4:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`)
|
var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`)
|
||||||
@@ -107,7 +109,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*t
|
|||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
||||||
l: l,
|
l: l,
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.reload(c, true)
|
err = t.reload(c, true)
|
||||||
@@ -137,15 +138,6 @@ func (t *tun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) readOne(to []byte) (int, error) {
|
|
||||||
buf := make([]byte, len(to)+4)
|
|
||||||
|
|
||||||
n, err := t.f.Read(buf)
|
|
||||||
|
|
||||||
copy(to, buf[4:])
|
|
||||||
return n - 4, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write is only valid for single threaded use
|
// Write is only valid for single threaded use
|
||||||
func (t *tun) Write(from []byte) (int, error) {
|
func (t *tun) Write(from []byte) (int, error) {
|
||||||
buf := t.out
|
buf := t.out
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/udp"
|
"github.com/slackhq/nebula/udp"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestTun struct {
|
type TestTun struct {
|
||||||
@@ -29,8 +30,6 @@ type TestTun struct {
|
|||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
rxPackets chan []byte // Packets to receive into nebula
|
rxPackets chan []byte // Packets to receive into nebula
|
||||||
TxPackets chan []byte // Packets transmitted outside by nebula
|
TxPackets chan []byte // Packets transmitted outside by nebula
|
||||||
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*TestTun, error) {
|
func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*TestTun, error) {
|
||||||
@@ -51,9 +50,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*T
|
|||||||
l: l,
|
l: l,
|
||||||
rxPackets: make(chan []byte, 10),
|
rxPackets: make(chan []byte, 10),
|
||||||
TxPackets: make(chan []byte, 10),
|
TxPackets: make(chan []byte, 10),
|
||||||
batchRet: [1]tio.Packet{
|
|
||||||
tio.Packet{Bytes: make([]byte, udp.MTU)},
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,14 +164,17 @@ func (t *TestTun) Close() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestTun) Read() ([]tio.Packet, error) {
|
func (t *TestTun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
t.batchRet[0].Bytes = t.batchRet[0].Bytes[:udp.MTU]
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
n, err := t.read(t.batchRet[0].Bytes)
|
return 0, nil //todo should this be an err?
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0].Bytes = t.batchRet[0].Bytes[:n]
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.read(mem)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TestTun) read(b []byte) (int, error) {
|
func (t *TestTun) read(b []byte) (int, error) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
"github.com/slackhq/nebula/wintun"
|
"github.com/slackhq/nebula/wintun"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
|
||||||
)
|
)
|
||||||
@@ -45,18 +46,19 @@ type winTun struct {
|
|||||||
l *slog.Logger
|
l *slog.Logger
|
||||||
|
|
||||||
tun *wintun.NativeTun
|
tun *wintun.NativeTun
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *winTun) Read() ([]tio.Packet, error) {
|
func (t *winTun) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
n, err := t.tun.Read(t.readBuf, 0)
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
if err != nil {
|
return 0, nil //todo should this be an err?
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
t.batchRet[0] = tio.Packet{Bytes: t.readBuf[:n]}
|
p[0].Meta = struct{}{}
|
||||||
return t.batchRet[:], nil
|
n, err := t.tun.Read(mem, 0)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p[0].Bytes = mem[:n]
|
||||||
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (Device, error) {
|
func newTunFromFd(_ *config.C, _ *slog.Logger, _ int, _ []netip.Prefix) (Device, error) {
|
||||||
@@ -81,7 +83,6 @@ func newTun(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, _ bool) (*w
|
|||||||
}
|
}
|
||||||
|
|
||||||
t := &winTun{
|
t := &winTun{
|
||||||
readBuf: make([]byte, defaultBatchBufSize),
|
|
||||||
Device: deviceName,
|
Device: deviceName,
|
||||||
vpnNetworks: vpnNetworks,
|
vpnNetworks: vpnNetworks,
|
||||||
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
MTU: c.GetInt("tun.mtu", DefaultMTU),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"github.com/slackhq/nebula/overlay/tio"
|
"github.com/slackhq/nebula/overlay/tio"
|
||||||
"github.com/slackhq/nebula/routing"
|
"github.com/slackhq/nebula/routing"
|
||||||
|
"github.com/slackhq/nebula/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewUserDeviceFromConfig(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, routines int) (Device, error) {
|
func NewUserDeviceFromConfig(c *config.C, l *slog.Logger, vpnNetworks []netip.Prefix, routines int) (Device, error) {
|
||||||
@@ -37,25 +38,23 @@ type UserDevice struct {
|
|||||||
|
|
||||||
inboundReader *io.PipeReader
|
inboundReader *io.PipeReader
|
||||||
inboundWriter *io.PipeWriter
|
inboundWriter *io.PipeWriter
|
||||||
|
|
||||||
readBuf []byte
|
|
||||||
batchRet [1]tio.Packet
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *UserDevice) Capabilities() tio.Capabilities {
|
func (d *UserDevice) Capabilities() tio.Capabilities {
|
||||||
return tio.Capabilities{}
|
return tio.Capabilities{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *UserDevice) Read() ([]tio.Packet, error) {
|
func (d *UserDevice) Read(p []wire.TunPacket, mem []byte) (int, error) {
|
||||||
if d.readBuf == nil {
|
if len(p) == 0 || len(mem) == 0 {
|
||||||
d.readBuf = make([]byte, defaultBatchBufSize)
|
return 0, nil //todo should this be an err?
|
||||||
}
|
}
|
||||||
n, err := d.outboundReader.Read(d.readBuf)
|
p[0].Meta = struct{}{}
|
||||||
|
n, err := d.outboundReader.Read(mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
d.batchRet[0] = tio.Packet{Bytes: d.readBuf[:n]}
|
p[0].Bytes = mem[:n]
|
||||||
return d.batchRet[:], nil
|
return 1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *UserDevice) Activate() error {
|
func (d *UserDevice) Activate() error {
|
||||||
|
|||||||
11
wire/wire.go
Normal file
11
wire/wire.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package wire
|
||||||
|
|
||||||
|
// TunPacket is the unit a read from a tun device returns.
|
||||||
|
// On supported platforms, it may be a superpacket, but a single TunPacket will never have more than one destination.
|
||||||
|
type TunPacket struct {
|
||||||
|
// Bytes contains the actual packet
|
||||||
|
Bytes []byte
|
||||||
|
// Meta contains other information to help process the packet correctly, such as offsets for segmentation offloads
|
||||||
|
// Fields in Meta should be as portable/platform-agnostic as possible.
|
||||||
|
Meta struct{}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user