Linx.Netlink.Rtnl.Monitor (Linx v0.1.0)

Copy Markdown View Source

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

Types

opt()

@type opt() ::
  {:owner, pid()}
  | {:netns, Linx.Netlink.Socket.netns()}
  | {:groups, [pos_integer()]}
  | {:rcvbuf, pos_integer()}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

start_link(opts)

@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.
  • :groupsRTNLGRP_* group numbers; defaults to links, neighbours, and IPv4/IPv6 addresses, routes, and rules.
  • :rcvbufSO_RCVBUF size in bytes; default 4 MiB.

stop(monitor)

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

Stops the Monitor (closes its socket).

subscribe(owner \\ self(), opts \\ [])

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

unsubscribe(monitor)

@spec unsubscribe(pid()) :: :ok

Alias for stop/1.