Linx.Netfilter.Monitor (Linx v0.1.0)

Copy Markdown View Source

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

opt()

@type opt() ::
  {:owner, pid()}
  | {:netns, Linx.Netlink.Socket.netns()}
  | {:since_gen, non_neg_integer()}
  | {:rcvbuf, pos_integer()}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

set_min_gen(monitor, gen)

@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.

start_link(opts)

@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 with gen_id <= this value are dropped. Defaults to 0 (everything flows).
  • :rcvbufSO_RCVBUF size in bytes; default 4 MiB.

stop(monitor)

@spec stop(pid()) :: :ok

Stops the Monitor (closes its socket).