Skip to content
FreeBSDDeep Dive Published Updated 3 min readViews unavailable

Netgraph on FreeBSD: Building Kernel Networking Graphs from Reusable Nodes

A practical model of netgraph nodes, hooks, control messages, data paths, ngctl inspection, socket integration, and safe teardown on FreeBSD.

FreeBSD netgraph is a kernel networking framework whose components are nodes connected through named hooks. Packets flow along hook connections, while control messages configure and inspect nodes. This graph model underlies facilities ranging from Bluetooth and PPP-related paths to virtual Ethernet pairs and userland sockets.

Nodes separate behavior from topology

Each node has a type such as ng_ether, ng_bridge, ng_eiface, ng_socket, or a protocol-specific module. A type defines supported hooks and control messages. Instances receive names or numeric IDs. Connecting two hooks forms an edge in the graph; disconnecting a hook can trigger node-specific shutdown behavior.

The topology is not the same as the ordinary interface/routing view. Some nodes expose an ifnet interface visible to ifconfig, while others operate entirely inside netgraph. Diagnose both layers when a packet disappears.

Load support and inspect the current graph without changing it:

kldload netgraph
ngctl list
ngctl dot

ngctl dot emits a Graphviz description that is useful for change review and incident capture. Exact node availability depends on loaded modules and the kernel build.

ngctl has a control socket and a data socket

Interactive ngctl creates a userland control endpoint. Commands such as mkpeer, name, connect, msg, and shutdown manipulate the graph. Their arguments are type-specific; always read the target node’s section 4 manual page.

An illustrative ephemeral pair using ng_eiface might create a netgraph-backed Ethernet interface, after which ifconfig configures the resulting interface. Names and hooks must be confirmed from command output rather than assumed:

ngctl mkpeer . eiface hook ether
ngctl list
ifconfig -a

Run experiments on a console-accessible lab host. Creating a graph is easy; accidentally disconnecting the hook that carries remote administration is equally easy.

ng_socket(4) lets a process attach sockets to the graph. The control socket sends netgraph messages; data sockets exchange packets through hooks. This enables userland protocol components without forcing every packet through a conventional IP socket, but it also creates a privileged kernel/user boundary that needs input validation and bounded queues.

Control messages are typed operations

Nodes implement generic and type-specific commands. Generic messages can retrieve node information, list hooks, assign names, or request shutdown; a bridge node adds commands for link and forwarding state. ngctl msg can encode ASCII forms using the type’s parser.

Capture configuration through supported get* messages and graph inspection. A shell transcript of mkpeer commands is not always a complete state export because nodes can learn runtime tables and other subsystems can create or remove nodes.

Packet ownership and cycles matter

Netgraph passes kernel mbufs between nodes. A node must obey the framework’s ownership rules when forwarding, consuming, or rejecting data. Custom kernel nodes run in a high-trust context: malformed length handling, recursion, lock ordering, or unbounded queues can crash or stall the host.

Graph cycles may be intentional for a protocol design but can also create packet loops. Bridge-like nodes need the same loop and broadcast-storm thinking as physical switching. Rate limits, interface flags, and firewall policy do not automatically appear at every hook just because the host has pf enabled elsewhere.

Teardown is part of the design

ngctl shutdown nodename: requests node shutdown, but persistent nodes and nodes owned by other subsystems may behave differently. Disconnecting one hook can cascade. Record ngctl list, ngctl dot, ifconfig, routes, firewall state, and module list before changing a production graph.

Test module unload, process death, link loss, repeated creation, partial configuration failure, and boot ordering. Persistent graphs should be built by a reviewed rc.d service that checks existing state and is safe to rerun—not an opaque sequence pasted into rc.local.

Netgraph’s power is composability: a small node does one job and the graph determines the path. Operational safety comes from making that otherwise invisible path observable, versioned, and reversible.

Related:

Sources:

Comments