A GenServer that owns a multicast rtnetlink socket, decodes each broadcast
into a Linx.Netlink.Rtnl.Monitor.Event, and forwards it to an owner pid —
the ip monitor equivalent.
Lifecycle
{:ok, mon} = Linx.Netlink.Rtnl.Monitor.subscribe()
# → the owner receives:
# {:linx_rtnl, :event, %Linx.Netlink.Rtnl.Monitor.Event{...}}
# {:linx_rtnl, :resync_needed} (on ENOBUFS)
:ok = Linx.Netlink.Rtnl.Monitor.unsubscribe(mon)Events are wake-ups, not deltas
Netlink multicast is lossy by design: on a busy system the kernel's send
buffer fills, frames are dropped, and the next recv returns ENOBUFS. So the
Monitor is a latency layer over reconcile, never a source of truth — a
level-triggered consumer treats every event (and every :resync_needed) as
"look now", then re-reads and re-diffs full state. It must not act on an
event's :resource directly. RTM_NEW*/RTM_DEL* decode through the same
codecs as list/1, so the structs are identical to what a re-read returns.
Unlike Linx.Netfilter.Monitor, there is no generation counter or
snapshot-then-tail handshake — rtnetlink has no transaction id. The
level-triggered resync (re-list, diff, apply) is what makes a missed event
harmless.
Groups
By default it joins links, neighbours, and IPv4/IPv6 addresses, routes, and
rules. Override with :groups (a list of RTNLGRP_* numbers).
ENOBUFS recovery
On ENOBUFS the Monitor emits {:linx_rtnl, :resync_needed} and keeps
reading; the owner re-syncs by reconciling. SO_RCVBUF defaults to 4 MiB to
reduce overflow.
Summary
Functions
Returns a specification to start this module under a supervisor.
Starts a Monitor linked to the caller and subscribed to the rtnetlink multicast groups.
Stops the Monitor (closes its socket).
Convenience: start a Monitor with owner (default the calling process) and
the rest of opts. Returns {:ok, monitor}.
Alias for stop/1.
Types
@type opt() :: {:owner, pid()} | {:netns, Linx.Netlink.Socket.netns()} | {:groups, [pos_integer()]} | {:rcvbuf, pos_integer()}
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec start_link([opt()]) :: GenServer.on_start()
Starts a Monitor linked to the caller and subscribed to the rtnetlink multicast groups.
Options:
:owner(required) — pid that receives{:linx_rtnl, _}messages.:netns— namespace to monitor; defaults to:host.:groups—RTNLGRP_*group numbers; defaults to links, neighbours, and IPv4/IPv6 addresses, routes, and rules.:rcvbuf—SO_RCVBUFsize in bytes; default 4 MiB.
@spec stop(pid()) :: :ok
Stops the Monitor (closes its socket).
@spec subscribe(pid(), [opt()]) :: GenServer.on_start()
Convenience: start a Monitor with owner (default the calling process) and
the rest of opts. Returns {:ok, monitor}.
@spec unsubscribe(pid()) :: :ok
Alias for stop/1.