dataplane

package
v0.2.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 5, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Overview

Package dataplane owns everything AF_XDP: opening the fleet, the per-queue transmit and receive loops, and the safety checks that run before an XDP program is attached to a live interface.

It is the only package that imports go-afxdp. Everything above it — configuration, the TUI, the CLI — deals in plain Go types.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func MemoryNeeded

func MemoryNeeded(queues, numFrames, frameSize int) uint64

MemoryNeeded estimates the locked memory a run will need: one UMEM per queue, plus slack for the rings and BPF maps.

func RaiseMemlock

func RaiseMemlock() (uint64, error)

RaiseMemlock tries to lift this process's RLIMIT_MEMLOCK soft limit to the hard limit. That is a change to Wireblast's own process, not to the host, so it is done automatically; anything beyond it is the user's call.

Types

type Check

type Check struct {
	Level  Level
	Title  string
	Detail string
	// Fix is a command the user could run. Wireblast only ever suggests these:
	// it never reconfigures the host itself.
	Fix string
}

Check is one preflight finding.

type DefaultFilterBuilder

type DefaultFilterBuilder struct{}

DefaultFilterBuilder maps Wireblast's receive modes onto the matches go-afxdp provides today.

func (DefaultFilterBuilder) Plan

Plan builds the filter for a run.

type Environment

type Environment struct {
	// Euid is the effective user ID; 0 means root.
	Euid int
	// HasNetRaw reports whether the process holds CAP_NET_RAW.
	HasNetRaw bool
	// MemlockCur and MemlockMax are the RLIMIT_MEMLOCK soft and hard limits in
	// bytes. MemlockUnlimited short-circuits both.
	MemlockCur       uint64
	MemlockMax       uint64
	MemlockUnlimited bool
	// SSHConnection is the value of $SSH_CONNECTION, if any.
	SSHConnection string
	// DefaultRouteLink is the interface index owning the default route.
	DefaultRouteLink int
	HasDefaultRoute  bool
}

Environment is the host state preflight inspects, injected so the checks can be unit-tested without root, a real NIC or a real SSH session.

func HostEnvironment

func HostEnvironment(src discovery.Source) Environment

HostEnvironment reads the real environment.

type FilterBuilder

type FilterBuilder interface {
	Plan(cfg *config.Config, res *discovery.Resolved) (FilterPlan, error)
}

FilterBuilder turns a receive mode into an XDP filter.

It is an interface on purpose, so an alternative mapping can be dropped in without touching the rest of Wireblast. go-afxdp's matches combine with OR; the one exclusion it offers is WithKeepManagement, which the keep-management mode uses to take everything except the traffic that keeps the box reachable.

type FilterPlan

type FilterPlan struct {
	// Matches is what gets handed to afxdp.WithFilter.
	Matches []afxdp.Match

	// Summary is the short form for the dashboard, e.g. "udp/9000".
	Summary string
	// Redirects describes in plain language what leaves the kernel stack.
	Redirects string
	// Limitations are honest notes about what the filter does NOT do, so the
	// UI never claims more precision than the filter really has.
	Limitations []string
	// Warnings are things the user should think about before saying yes.
	Warnings []string
	// Dangerous marks the match-all filter, which needs an extra confirmation
	// on top of any --yes.
	Dangerous bool
	// KeepManagement asks the library to spare the traffic that keeps the box
	// reachable (ARP, IPv6 ND, SSH and DNS to and from this host) even though
	// the filter otherwise redirects everything. Set only by the
	// keep-management receive mode.
	KeepManagement bool
	// contains filtered or unexported fields
}

FilterPlan is the XDP filter a run will install, together with a plain description of what it takes away from the kernel.

Receiving through AF_XDP means those packets stop reaching the normal network stack. That is a big enough deal that Wireblast always states, in words, exactly what will be redirected before it attaches anything.

func (FilterPlan) Receives

func (p FilterPlan) Receives() bool

Receives reports whether a plan takes any traffic from the kernel at all.

type Info

type Info struct {
	Interface string
	Driver    string
	Queues    int
	XDPMode   string // "native", "generic", ...
	ZeroCopy  bool
	Filter    string
	FrameSize int
	NumFrames int
	// Tuning is what the library did to the interface's NAPI settings to make
	// the receive path keep up, e.g. "defer=2 flush=200ms", or "untuned". These
	// are host settings the library restores on close; worth showing.
	Tuning string
	// Pattern and PacketSizes describe the traffic, e.g. "udp" and
	// "fixed 64-byte frames".
	Pattern     string
	PacketSizes string
	// LinkWait is how long the interface actually took to come back after the
	// XDP attach. The library polls for carrier rather than sleeping a fixed
	// amount, so this is the driver's real renegotiation time. Zero when the
	// attachment was reused and no wait was needed.
	LinkWait time.Duration
	// Reused is true when this run inherited an XDP program that was already
	// attached, and so started instantly.
	Reused bool
}

Info describes how the fleet ended up running, for the dashboard and the startup banner.

func (Info) String

func (i Info) String() string

String renders Info as the single line printed at startup.

type Level

type Level int

Level is how serious a preflight finding is.

const (
	// LevelInfo is worth knowing but needs no decision.
	LevelInfo Level = iota
	// LevelWarn is something the user should see before starting.
	LevelWarn
	// LevelDanger could cut the user off from the machine. It requires an
	// explicit confirmation that --yes does not provide.
	LevelDanger
	// LevelFatal cannot be continued past.
	LevelFatal
)

func (Level) String

func (l Level) String() string

type Options

type Options struct {
	// Frames is the loaded capture for --mode pcap.
	Frames generator.FrameSource
	// Logf receives occasional dataplane messages. Optional.
	Logf func(format string, args ...any)
	// NumFrames and FrameSize override the UMEM geometry. Zero means default.
	NumFrames int
	FrameSize int
	// Session keeps the XDP program attached across runs. Set it for an
	// interactive session; leave it nil for a one-shot run.
	Session *Session
}

Options configures a Runner.

type Preflight

type Preflight struct {
	Checks []Check
}

Preflight is the result of every check run before an XDP program is attached.

func RunPreflight

func RunPreflight(in PreflightInput) *Preflight

RunPreflight performs every check that must happen before an XDP program touches the interface.

It reads and reports; it never changes the host. Where a change would help, it prints the command for the user to run.

func (*Preflight) Dangerous

func (p *Preflight) Dangerous() []Check

Dangerous returns the checks that need an explicit confirmation.

func (*Preflight) Err

func (p *Preflight) Err() error

Err renders the fatal checks as a single error, or nil when there are none.

func (*Preflight) Fatal

func (p *Preflight) Fatal() []Check

Fatal returns the checks that make the run impossible.

func (*Preflight) NeedsConfirmation

func (p *Preflight) NeedsConfirmation() bool

NeedsConfirmation reports whether a human has to say yes before starting, regardless of --yes.

func (*Preflight) OK

func (p *Preflight) OK() bool

OK reports whether the run may proceed at all.

func (*Preflight) Warnings

func (p *Preflight) Warnings() []Check

Warnings returns the advisory checks.

type PreflightInput

type PreflightInput struct {
	Cfg    *config.Config
	Res    *discovery.Resolved
	Plan   FilterPlan
	Env    Environment
	Queues int
	// MaxFrameLen is the largest frame the generator will emit, in bytes
	// written (excluding the FCS).
	MaxFrameLen int
	NumFrames   int
	FrameSize   int
	// AllLinks is every interface on the host, used to place the SSH session.
	AllLinks []discovery.Link
}

PreflightInput is everything the checks need to make their decisions.

type Runner

type Runner struct {
	// contains filtered or unexported fields
}

Runner owns an AF_XDP run end to end: it opens the fleet, drives one transmit goroutine (and optionally one receive goroutine) per queue, and tears everything down cleanly.

Exactly one goroutine owns each socket's transmit side and at most one owns its receive side, which is the concurrency contract go-afxdp requires.

func New

func New(cfg *config.Config, res *discovery.Resolved, opts Options) (*Runner, error)

New prepares a run. It resolves the queue count, builds the filter plan and sizes the UMEM, but attaches nothing: call Runner.Preflight and then Runner.Run.

func (*Runner) AdjustRate

func (r *Runner) AdjustRate(factor float64) (pps, bps uint64)

AdjustRate scales both limits, for the +/- hotkeys. An unlimited limit stays unlimited, so which constraint is binding does not change.

func (*Runner) Collector

func (r *Runner) Collector() *stats.Collector

Collector exposes the stats collector, for a front end that wants to reset the interval counters.

func (*Runner) Info

func (r *Runner) Info() Info

Info returns how the run is configured, filled in with what the kernel actually granted once Runner.Run has opened the fleet.

func (*Runner) Paused

func (r *Runner) Paused() bool

Paused reports whether transmission is currently paused.

func (*Runner) Plan

func (r *Runner) Plan() FilterPlan

Plan returns the filter that will be installed.

func (*Runner) Preflight

func (r *Runner) Preflight(src discovery.Source) *Preflight

Preflight runs every safety check, before anything is attached.

func (*Runner) Queues

func (r *Runner) Queues() int

Queues is how many queues the run will use.

func (*Runner) Rate

func (r *Runner) Rate() (pps, bps uint64)

Rate returns the configured packet and bit rates, 0 meaning unlimited.

func (*Runner) Run

func (r *Runner) Run(ctx context.Context) error

Run is Start followed by Wait: it transmits until the duration expires or ctx is cancelled, then shuts everything down cleanly.

func (*Runner) SetPaused

func (r *Runner) SetPaused(p bool)

SetPaused stops or resumes transmission. Receiving and the dashboard keep going either way.

func (*Runner) SetRate

func (r *Runner) SetRate(pps, bps uint64)

SetRate changes the rate while the run is in flight.

func (*Runner) Start

func (r *Runner) Start(ctx context.Context) error

Start attaches the XDP program, waits for the link, and spawns the workers. It returns as soon as traffic is flowing, so a caller can display what the kernel actually granted (see Runner.Info) before waiting.

Every Start must be paired with a Runner.Wait, which is what tears everything down.

func (*Runner) Stats

func (r *Runner) Stats() *stats.Snapshot

Stats returns the latest published snapshot.

func (*Runner) Stop

func (r *Runner) Stop()

Stop asks the run to wind down. Runner.Wait then returns once everything has drained.

func (*Runner) Wait

func (r *Runner) Wait() error

Wait blocks until the run ends, then stops the workers, drains the transmit rings, detaches the XDP program and closes the sockets.

It always tears down what Start created — whether the run ended normally, on a signal, or on an error.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session keeps an AF_XDP fleet attached across several runs.

Without it, every rerun detaches the XDP program and attaches it again, which reinitialises the driver's queues and drops carrier for the better part of ten seconds on a 10G NIC. Since almost nothing people change between runs — the rate, the duration, the flow count, the packet contents — has any bearing on the attachment, that wait is nearly always avoidable.

A Session is owned by the interactive front end and closed when it exits. The one-shot --no-tui path does not use one: it runs once and leaves.

func NewSession

func NewSession() *Session

NewSession returns an empty session. Nothing is attached until the first run asks for a fleet.

func (*Session) Attached

func (s *Session) Attached() bool

Attached reports whether the session currently holds an open fleet.

func (*Session) Close

func (s *Session) Close() error

Close detaches whatever is currently held. It is safe to call more than once.

func (*Session) Fleet

func (s *Session) Fleet(key fleetKey, open func() (*afxdp.Fleet, error)) (*afxdp.Fleet, bool, error)

Fleet returns a fleet matching key, reusing the one already attached when it matches and opening a new one otherwise. It reports whether the fleet was reused, which is how the caller knows it can skip waiting for the link.

open is only called when a new attach is genuinely needed.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL