Documentation
¶
Overview ¶
Package pmark tags Linux process lifetimes with 64-bit marks selected by userspace Go callbacks and inherited by children through eBPF.
A Daemon loads tracepoint eBPF programs, pins the shared processes map under a bpffs directory, traverses /proc, and keeps a userspace mirror reconciled with fork, exec, and exit events. Callers provide a CheckFunc to choose explicit marks from ProcessInfo and optional callbacks to observe process events or effective map updates.
Process identity is ProcessKey: TGID plus the process start time in procfs USER_HZ ticks. ProcessValue stores whether the process is live or a tombstone, whether it has an effective mark, mark priority, checker generation, mark value, and a CLOCK_BOOTTIME timestamp. Other eBPF programs can reuse the pinned processes map as a kernel-space communication point, but should treat missing entries, tombstones, and entries with HasMark false as unmarked.
Index ¶
- Constants
- type Callbacks
- type CheckFunc
- type Daemon
- func (d *Daemon) Close() error
- func (d *Daemon) CurrentGeneration() uint64
- func (d *Daemon) Done() <-chan error
- func (d *Daemon) ForceBumpGeneration() (uint64, error)
- func (d *Daemon) ForceProcessTraversal() error
- func (d *Daemon) Run() error
- func (d *Daemon) SetChecker(check CheckFunc) (uint64, error)
- func (d *Daemon) SetProcessMark(key ProcessKey, priority int8, mark uint64)
- func (d *Daemon) Stop() error
- func (d *Daemon) UpdateHooks(callbacks Callbacks)
- type KernelMark
- type ProcessEvent
- type ProcessInfo
- type ProcessKey
- type ProcessMapState
- type ProcessUpdate
- type ProcessValue
Constants ¶
const ( // DefaultTombCollectionEvents bounds tombstone cleanup work by doing it // after a fixed number of processed ring events instead of on every event. DefaultTombCollectionEvents = 1024 // DefaultTombTTL is the grace period during which exited process identities // remain in both userspace and kernel mirrors as tombstones. DefaultTombTTL = time.Minute )
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Callbacks ¶
type Callbacks struct {
// Check selects explicit marks for processes. Nil means no process is
// explicitly marked by userspace, though inheritance from existing marks can
// still occur.
Check CheckFunc
// ProcessEvent observes fork, exec, exit, and unknown BPF events after
// userspace reconciliation. It is for transition logging or side effects
// that need event context such as ParentKey and Kernel.
ProcessEvent func(ProcessEvent)
// ProcessUpdate observes effective map state changes after the daemon has
// attempted to write them to the kernel map. It is for consumers that care
// about the current mark state rather than the transition that caused it.
ProcessUpdate func(ProcessUpdate)
// Logf receives recoverable daemon diagnostics such as parse errors,
// traversal failures, map sync failures, and tombstone cleanup failures.
Logf func(format string, args ...any)
}
Callbacks contains optional hooks used by Daemon.
All callbacks are invoked synchronously by daemon code. Check and ProcessUpdate can run during Run before it returns because initial process traversal may create marks. After Run starts the event loop, Check, ProcessEvent, ProcessUpdate, and Logf can run on the event goroutine. Implementations should avoid long blocking work and should not call Stop, Close, or other methods that wait for the daemon from inside a callback.
type CheckFunc ¶
type CheckFunc func(ProcessInfo) (int8, uint64, bool)
CheckFunc decides whether a process should receive an explicit mark.
It is called while the daemon walks the current /proc tree before attaching BPF programs, again immediately after attach to cover the race window, and on each fork event for the child process. It is not called on exit events.
Returning (priority, mark, true) creates or refreshes an explicit live mark for the supplied process in the current checker generation. Returning (_, _, false) leaves the process unmarked unless it can inherit a live HasMark value from its parent. If the process already had an entry, the daemon keeps a live HasMark=false entry for the rest of that process lifetime. CheckFunc should be quick and must not call back into the same Daemon; after Run starts event processing, a blocked CheckFunc blocks ring-buffer consumption.
func CombineChecks ¶
type Daemon ¶
type Daemon struct {
// contains filtered or unexported fields
}
Daemon owns loaded eBPF objects, links, ring reader, and userspace mirror.
func NewDaemon ¶
func NewDaemon( pinPath string, callbacks Callbacks, tcev uint64, tttl time.Duration, ) (*Daemon, error)
NewDaemon prepares a daemon instance. Call Run to attach programs and start consuming events, and Stop to detach and close resources.
func (*Daemon) CurrentGeneration ¶
CurrentGeneration returns the active checker generation.
func (*Daemon) ForceBumpGeneration ¶
ForceBumpGeneration increments the checker generation, immediately traverses /proc with the current checker, and returns the resulting generation.
It has the same reconciliation effect as SetChecker with the existing checker, but does not replace the checker function. Use it when the checker closes over proving state that changed internally and all live processes need re-checking.
func (*Daemon) ForceProcessTraversal ¶
ForceProcessTraversal immediately traverses /proc with the current checker.
This is useful when a caller needs to refresh process state outside the daemon's normal startup, event, and cleanup ordering. It does not advance the checker generation.
func (*Daemon) SetChecker ¶
SetChecker installs a new checker, increments the checker generation, immediately traverses /proc with the new checker, and returns the resulting generation.
Processes matched by the new checker receive live HasMark=true entries at the new generation. Existing live entries that no longer match and cannot inherit a live mark are updated to HasMark=false at the new generation and kept until that process exits and its tombstone is collected.
func (*Daemon) SetProcessMark ¶
func (d *Daemon) SetProcessMark(key ProcessKey, priority int8, mark uint64)
SetProcessMark explicitly sets a live mark for one process lifetime.
The mark is applied with the current checker generation and the same merge, kernel-sync, and ProcessUpdate behavior used for marks found in BPF events or returned by Check. Higher-priority, newer-generation, or tombstone values that already exist may still win according to the normal ProcessValue merge rules.
func (*Daemon) UpdateHooks ¶
UpdateHooks replaces the non-check callbacks used by the daemon.
The Check field is intentionally ignored; use SetChecker so checker changes always advance the generation and trigger a full traversal. Existing in-flight callbacks are synchronous, so this method is best called from outside daemon callbacks.
type KernelMark ¶
type KernelMark struct {
// HasMark reports whether BPF saw a live mark for the process.
HasMark bool
// Inherited reports whether the mark seen by BPF was inherited from a
// parent rather than explicitly selected by Check.
Inherited bool
// Mark is meaningful when HasMark is true.
Mark uint64
// Priority is meaningful when HasMark is true. Higher values win when
// userspace merges otherwise comparable process updates.
Priority int8
}
KernelMark is the BPF program's view of a process mark at event time.
type ProcessEvent ¶
type ProcessEvent struct {
Type string
Key ProcessKey
ParentKey *ProcessKey
Kernel KernelMark
Process ProcessInfo
Value *ProcessValue
}
ProcessEvent describes a process transition that produced an effective mark state worth reporting.
Type is "fork", "exec", "exit", or "unknown". Fork and exec events are reported only when the process has an effective live mark after userspace reconciliation: a BPF mark, an existing userspace mirror entry, a new Check match, or fork inheritance from a marked parent. Unmarked fork and exec events are intentionally suppressed. Exit events are reported when BPF saw a live mark or userspace had a live mirror entry for the exiting process; the Value is a tombstone. Unknown events are reported for unexpected BPF event types and may have nil Value.
Process contains the best process metadata available at callback time. ParentKey is set for fork events and nil otherwise. Kernel is the raw BPF mark state from the event, before userspace Check and mirror reconciliation. Value is the effective userspace value after reconciliation; it is non-nil for reported fork and exit events.
type ProcessInfo ¶
type ProcessInfo struct {
Key ProcessKey
PPID uint32
Comm string
Cmdline string
Exe string
}
ProcessInfo is the userspace-enriched process view passed to callbacks.
During process-tree traversal it is read from /proc and normally includes PPID, Comm, Cmdline, and Exe. During fork and exit events the daemon first tries to refresh the data from /proc; if the process is already gone or the identity no longer matches, only Key, PPID, and Comm from the BPF event are guaranteed to be present. Cmdline and Exe may be empty in that case.
func ListProcesses ¶
func ListProcesses() ([]ProcessInfo, error)
ListProcesses returns the current /proc process tree using the same process identity parsing as the daemon.
type ProcessKey ¶
type ProcessKey = markProcessKey // public re-export from generated code
ProcessKey is the per-boot process-lifetime identity shared with BPF.
TGID is the process ID. StartTime is /proc/<pid>/stat field 22 expressed in USER_HZ ticks since boot. The pair is used instead of TGID alone because PIDs can be reused after exit.
type ProcessMapState ¶
type ProcessMapState struct {
Alive int
Tombstones int
Latest uint64
Entries map[ProcessKey]ProcessValue
Procs []ProcessInfo
}
ProcessMapState is a point-in-time view of the pinned process map plus procfs.
func GrabProcessMapState ¶
func GrabProcessMapState(pinPath string) (ProcessMapState, error)
GrabProcessMapState reads the pinned processes map under pinPath.
type ProcessUpdate ¶
type ProcessUpdate struct {
Key ProcessKey
Value ProcessValue
}
ProcessUpdate is emitted after the userspace mirror and kernel map have been updated for a process key.
Updates can come from initial traversal, fork handling, exec handling, exit tombstoning, or event reconciliation. They are not emitted for unchanged values, for suppressed unmarked events, or when old tombstones are physically deleted during periodic cleanup.
type ProcessValue ¶
type ProcessValue = markProcessValue // public re-export from generated code
ProcessValue is the effective mark state stored in the pinned process map.
A missing map entry, a live value with HasMark false, and a Tombstone value all mean "unmarked" for policy purposes. Live no-mark entries are retained after a process had an effective mark once, allowing newer checker rules to remove a mark without losing the process lifetime record. Tombstones are retained for a short grace period after exit so late events for an old process lifetime do not revive stale marks. Tombstone collection is the only operation that physically deletes entries.
The generated Inheritance field is true for an explicit/original mark chosen by Check and false for a mark inherited from a parent. HasMark gates whether Mark and Priority are currently meaningful and whether this value can be inherited. Generation records the checker generation that last reconciled the entry. When competing updates are merged, tombstones win first, higher generations win second, higher priorities win third, and timestamps order otherwise-equivalent values. Timestamp is in CLOCK_BOOTTIME nanoseconds and is also used to expire tombstones.
Directories
¶
| Path | Synopsis |
|---|---|
|
Command pmark runs the process-marking daemon or watches the daemon's pinned process map.
|
Command pmark runs the process-marking daemon or watches the daemon's pinned process map. |
|
Package fwmark applies pmark process marks to Linux socket fwmarks.
|
Package fwmark applies pmark process marks to Linux socket fwmarks. |
|
Package multirule provides a userspace-only rule tracker for pmark process callbacks.
|
Package multirule provides a userspace-only rule tracker for pmark process callbacks. |
