NFTables.Port (NFTables.Port v0.4.2)

View Source

GenServer managing the nftables port process with JSON communication.

This module provides the low-level interface to nftables via a native Zig port executable. All communication uses JSON format through the official libnftables library, providing a simple, performant, and safe interface to the kernel firewall.

Architecture

flowchart TD
    GenServer[NFTables.Port<br/>GenServer]
    Port[Erlang Port<br/>Zig executable]
    Lib[libnftables<br/>C library]
    Kernel[Linux Kernel<br/>nftables]

    GenServer --> Port
    Port --> Lib
    Lib --> Kernel

Communication Flow

Request: Elixir JSON → [4-byte length][JSON bytes] → Zig → libnftables → kernel

Response: kernel → libnftables → Zig → [4-byte length][JSON bytes] → Elixir JSON

Protocol

The port uses 4-byte big-endian length-prefixed packets for framing:

[4 bytes: packet length][N bytes: JSON string]

This framing is handled automatically by Erlang's {:packet, 4} option.

Port Binary Location

The native port executable is located using the following resolution order:

  1. PORT_NFTABLES_PATH environment variable (if set and file exists)
  2. /usr/local/sbin/port_nftables (system-wide installation)
  3. /usr/sbin/port_nftables (system-wide installation)
  4. priv/port_nftables (development or application-bundled)

For production deployments, set the PORT_NFTABLES_PATH environment variable to specify a custom location, or install to /usr/local/sbin/port_nftables.

Capabilities

The port executable requires CAP_NET_ADMIN capability to communicate with the kernel firewall. Set it with:

sudo setcap cap_net_admin=ep /path/to/port_nftables

Usage Example

# Start the port process
{:ok, pid} = NFTables.Port.start_link()

# Send a request to list tables
request = ~s({"nftables": [{"list": {"tables": {}}}]})
{:ok, response} = NFTables.Port.commit(pid, request)

# Parse the response
{:ok, data} = JSON.decode(response)

Direct Usage vs High-Level APIs

This module is typically used indirectly through NFTables high-level APIs (Table, Chain, Rule, Set, etc.) which handle JSON construction and parsing. Direct usage is appropriate for:

  • Custom nftables operations not covered by high-level APIs
  • Performance-critical code paths
  • Advanced nftables features
  • Testing and debugging

Summary

Functions

Returns a specification to start this module under a supervisor.

Commit a request to nftables and wait for response.

Start the nftables port GenServer.

Stop the port GenServer.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

commit(pid, request, timeout \\ 5000)

Commit a request to nftables and wait for response.

Sends a JSON-formatted nftables request to the port process, which forwards it to the native port executable that communicates with libnftables. The function blocks until a response is received or the timeout expires.

Parameters

  • pid - The port GenServer PID
  • request - JSON string containing nftables commands
  • timeout - Timeout in milliseconds (default: 5000)

Returns

  • {:ok, json_string} - Success, returns JSON string response from nftables
  • {:error, reason} - Error occurred during request processing

Examples

# List all tables
request = ~s({"nftables": [{"list": {"tables": {}}}]})
{:ok, response} = NFTables.Port.commit(pid, request)

# Add a table
request = ~s({"nftables": [{"add": {"table": {"family": "inet", "name": "filter"}}}]})
{:ok, response} = NFTables.Port.commit(pid, request)

# With custom timeout
{:ok, response} = NFTables.Port.commit(pid, request, 10_000)

start_link(opts \\ [])

Start the nftables port GenServer.

Spawns the native Zig port executable and establishes JSON communication. The port process will remain running until explicitly stopped or until it crashes (in which case this GenServer will also terminate).

Options

  • :check_capabilities - Check for CAP_NET_ADMIN capability on startup (default: true)
  • :name - Register the GenServer with a name (optional)

Returns

  • {:ok, pid} - Successfully started port GenServer
  • {:error, reason} - Failed to start

Examples

# Start with default options
{:ok, pid} = NFTables.Port.start_link()

# Start with named registration
{:ok, pid} = NFTables.Port.start_link(name: MyApp.NFTablesPort)

# Skip capability check (not recommended for production)
{:ok, pid} = NFTables.Port.start_link(check_capabilities: false)

stop(pid)

Stop the port GenServer.

Gracefully shuts down the port process and closes the connection to the native port executable. Any pending requests will fail.

Parameters

  • pid - The port GenServer PID

Returns

  • :ok

Examples

{:ok, pid} = NFTables.Port.start_link()
# ... use the port ...
:ok = NFTables.Port.stop(pid)