What is netgraph?
Netgraph(4) in FreeBSD is a powerful and flexible in-kernel networking subsystem. It offers a method for combining protocol and link-level drivers, a modular approach to implementing new protocols, and a common framework for kernel entities to communicate.
The fundamental concept in Netgraph(4) is the use of nodes and hooks, where each node has a specific type, and nodes are interconnected through hooks. Each node type is documented in its man pages. $ apropos netgraph OR $ man -k netgraph.
In this article, we will explore ng_ether(4)
, ng_bridge(4)
, and ng_eiface(4)
, along with some hands-on examples.
What do you need to follow along?
FreeBSD installed on a laptop, desktop, or in a VM and an extra network interface. If using physical hardware or a VM, you'll need at least two interfaces. One will be the default interface, and the second interface will be used for Netgraph. If your second interface is not connected, please use a virtual NIC.
Let’s Start...
Before we dive into Netgraph, note that ngctl(8)
utility is provided, which is used to create, query, and connect hooks.
kldstat
First, check if the Netgraph kernel module is loaded.
$ kldstat
If you haven’t run ngctl(8)
previously, you may not see any kernel modules starting with ng_*
. However, if you’re using a desktop environment, you might see modules like ng_ubt
and ng_bluetooth
already loaded.
Let’s run ngctl(8)
now and observe its output, as well as the output of kldstat
:
$ doas ngctl list OR $ doas ngctl lists
The output:
There are no netgraph nodes
Below, modules related to Netgraph are dynamically loaded:
$ kldstat
Id Refs Address Size Name
1 34 0xffffffff80200000 24e8d8 kernel
2 1 0xffffffff822f2000 34230 ng_socket.ko
The ng_socket(4)
node type enables user-mode processes to interact with the kernel’s Netgraph(4) networking subsystem through the BSD socket interface. In this case, ngctl(8)
utility has created the socket for us.
Let’s load the ng_ether(4)
kernel module and observe its status:
$ doas kldload ng_ether.ko
$ kldstat
Output:
Id Refs Address Size Name
1 35 0xffffffff80200000 24e8d8 kernel
2 1 0xffffffff822f2000 34230 ng_socket.ko
3 1 0xffffffff82326000 22520 ng_ether.ko
Run the list command again:
$ doas ngctl list OR $ doas ngctl ls
Output:
There are 3 total nodes:
Name: ngctl15427 Type: ether ID: 00000001 Num hooks: 0
Each node has a type, which is a static property of the node determined at node creation time.
Nodes are connected by pairs of hooks, allowing bidirectional data flow. Each node can have multiple hooks and assign its own meaning to them.
Once the ng_ether
module is loaded into the kernel, a node is automatically created for each Ethernet interface on the system. Each node will try to name itself after the associated interface.
We can now run commands non-interactively or in interactive mode.
$ doas ngctl OR $ ngctl
You’ll see an interactive prompt ready for commands.
Let’s learn ng_ether(4)
, ng_bridge(4)
, and ng_eiface(4)
.
Let’s make productive use of ng_ether(4)
by creating ng_bridge(4)
and linking it to ng_ether’s lower hook.
First, let’s take some time to understand what ng_ether and ng_bridge are.
ng_ether(4)
provides three hooks: lower, upper, and orphans.
Illustration:
Lower Hook – Incoming Flow
Connected to the raw Ethernet device.
Upper Hook – Outgoing Flow
Connected to upper protocol layers (e.g., the kernel).
ng_bridge(4)
provides unlimited hooks: link0
, link1
, ... linkN
.
Let’s do hands-on ng_ether(4)
and ng_bridge(4)
$ doas ngctl mkpeer vtnet1: bridge lower link0
$ doas ngctl list
$ doas ngctl show vtnet1:
Output:
Name: ngctl00001 Type: bridge ID: 00000002
Hook: lower -> bridge0
Interactive Mode
Assign a name to the bridge:
$ doas ngctl name vtnet1:lower bridge0
$ show bridge0: OR $ show firstbridge
Non-Interactive Mode
Connecting to another hook:
$ doas ngctl connect vtnet1: bridge0: upper link1
$ doas ngctl show bridge0:
$ doas ngctl show vtnet1:
Creating an ng_eiface
Now, create an eiface to connect a user-space process:
$ doas ngctl mkpeer bridge: eiface ether ether
$ doas ifconfig ngeth0
Additional Notes:
Use jail
to test the setup and bridge interfaces:
$ doas jail -c name=first host.hostname=first.home.arpa persist
Assign IPs for testing.
Source:
Fiddling with FreeBSD - Netgraph https://x.com/padukajorat/status/1868566280523137408/photo/1