A GenServer that owns a multicast nfnetlink socket subscribed to
NFNLGRP_NFTABLES, decodes each broadcast message into a
%Linx.Netfilter.Event{}, and forwards it to the owner pid.
Lifecycle
{:ok, monitor} = Linx.Netfilter.subscribe()
# → caller process receives:
# {:linx_netfilter, :event, %Linx.Netfilter.Event{...}}
# {:linx_netfilter, :resync_needed} (on ENOBUFS)
:ok = Linx.Netfilter.unsubscribe(monitor)Event grouping
The kernel broadcasts one NEW_GEN message at the start of each
committed transaction, then one event per entity in that commit.
The Monitor tracks the most recent NEW_GEN and attaches its
gen_id / proc_pid / proc_name to every subsequent entity
event, so each %Event{} carries full provenance.
ENOBUFS recovery
If the multicast traffic outpaces the consumer, the kernel
drops messages and the next recv returns :enobufs. The
Monitor emits {:linx_netfilter, :resync_needed} to the owner
and continues reading; the owner is responsible for re-running
pull/1..2 to re-sync state.
Default SO_RCVBUF is bumped to 4 MiB at start to reduce
the likelihood of overflow.
Snapshot+tail
subscribe/1 accepts a :since_gen option — events with
gen_id <= since_gen are silently dropped. The canonical
pattern (no race with the kernel):
{:ok, m} = Linx.Netfilter.subscribe()
{:ok, gen} = Linx.Netlink.Nfnl.Codec.get_gen(some_socket)
Linx.Netfilter.Monitor.set_min_gen(m, gen.id)
{:ok, snapshot} = Linx.Netfilter.pull(some_socket)
# → all events with gen_id > gen.id flow to the owner
# (events already captured in the snapshot are filtered)Linx.Netfilter.pull/1..2 exposes a :subscribe_first shortcut
that does this whole dance in one call.
Summary
Functions
Returns a specification to start this module under a supervisor.
Sets the minimum gen — subsequent events whose gen_id is
greater than this value will be delivered; events at or below
are filtered out. Used by pull(..., subscribe_first: monitor)
to drop events already captured in the snapshot.
Starts a Monitor linked to the caller, subscribed to
NFNLGRP_NFTABLES.
Stops the Monitor (closes its socket).
Types
@type opt() :: {:owner, pid()} | {:netns, Linx.Netlink.Socket.netns()} | {:since_gen, non_neg_integer()} | {:rcvbuf, pos_integer()}
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec set_min_gen(pid(), non_neg_integer()) :: :ok
Sets the minimum gen — subsequent events whose gen_id is
greater than this value will be delivered; events at or below
are filtered out. Used by pull(..., subscribe_first: monitor)
to drop events already captured in the snapshot.
@spec start_link([opt()]) :: GenServer.on_start()
Starts a Monitor linked to the caller, subscribed to
NFNLGRP_NFTABLES.
Options:
:owner(required) — pid that receives{:linx_netfilter, _}messages.:netns— namespace to monitor; defaults to:host.:since_gen— initial floor; events withgen_id <=this value are dropped. Defaults to0(everything flows).:rcvbuf—SO_RCVBUFsize in bytes; default 4 MiB.
@spec stop(pid()) :: :ok
Stops the Monitor (closes its socket).