NFTables.NAT (NFTables v0.8.2)

View Source

High-level Network Address Translation (NAT) operations.

This module provides convenient functions for common NAT scenarios like internet sharing (masquerade), port forwarding (DNAT), and source NAT.

All functions follow a builder-first pattern, taking a Builder as the first parameter and returning a modified Builder. This allows composing multiple NAT rules before submitting them in a single transaction.

Quick Examples

{:ok, pid} = NFTables.start_link()

# Single rule
Builder.new()
|> NFTables.NAT.setup_masquerade("wan0")
|> NFTables.submit(pid: pid)

# Compose multiple NAT rules
Builder.new()
|> NFTables.NAT.setup_masquerade("wan0", table: "nat")
|> NFTables.NAT.port_forward(80, "192.168.1.100", 8080, table: "nat")
|> NFTables.NAT.static_nat("203.0.113.1", "192.168.1.100", table: "nat")
|> NFTables.submit(pid: pid)

Prerequisites

NAT operations require a NAT table and appropriate chains:

# Create NAT table and chains using Builder
Builder.new()
|> NFTables.add(table: "nat", family: :inet)
|> NFTables.add(
  table: "nat",
  chain: "prerouting",
  family: :inet,
  type: :nat,
  hook: :prerouting,
  priority: -100,
  policy: :accept
)
|> NFTables.add(
  table: "nat",
  chain: "postrouting",
  family: :inet,
  type: :nat,
  hook: :postrouting,
  priority: 100,
  policy: :accept
)
|> NFTables.submit(pid: pid)

Summary

Functions

Set up destination NAT for incoming traffic.

Redirect a port to a different port on the same host (local port redirect).

Set up internet sharing (masquerade) on an interface.

Set up source NAT for a specific source IP or subnet.

Set up static (1:1) NAT between two IP addresses.

Types

family()

@type family() :: :inet | :ip | :ip6

Functions

destination_nat(builder \\ Builder.new(), dest, nat_ip, opts \\ [])

@spec destination_nat(NFTables.Builder.t(), String.t(), String.t(), keyword()) ::
  NFTables.Builder.t()

Set up destination NAT for incoming traffic.

Parameters

  • builder - Builder to add the rule to (defaults to new builder)
  • dest - Destination IP to match
  • nat_ip - IP to NAT to
  • opts - Options:
    • :table - NAT table name (default: "nat")
    • :chain - Chain name (default: "prerouting")
    • :family - Protocol family (default: :inet)
    • :interface - Limit to specific interface (optional)

Examples

# Redirect traffic to virtual IP to actual server
Builder.new()
|> NFTables.NAT.destination_nat("203.0.113.100", "192.168.1.100")
|> NFTables.submit(pid: pid)

# With interface restriction
Builder.new()
|> NFTables.NAT.destination_nat("203.0.113.100", "192.168.1.100", interface: "wan0")
|> NFTables.submit(pid: pid)

port_forward(builder \\ Builder.new(), external_port, internal_ip, internal_port, opts \\ [])

Forward a port to an internal host (DNAT).

This redirects incoming traffic on a specific port to an internal host and optionally a different port.

Parameters

  • builder - Builder to add the rule to (defaults to new builder)
  • external_port - Port to listen on
  • internal_ip - Destination IP address
  • internal_port - Destination port (defaults to external_port)
  • opts - Options:
    • :protocol - :tcp or :udp (default: :tcp)
    • :table - NAT table name (default: "nat")
    • :chain - Chain name (default: "prerouting")
    • :family - Protocol family (default: :inet)
    • :interface - Limit to specific interface (optional)

Examples

# Forward external port 80 to internal web server
Builder.new()
|> NFTables.NAT.port_forward(80, "192.168.1.100", 8080)
|> NFTables.submit(pid: pid)

# Forward SSH to internal host
Builder.new()
|> NFTables.NAT.port_forward(2222, "192.168.1.10", 22)
|> NFTables.submit(pid: pid)

# Forward UDP DNS
Builder.new()
|> NFTables.NAT.port_forward(53, "192.168.1.1", 53, protocol: :udp)
|> NFTables.submit(pid: pid)

# Compose multiple port forwards
Builder.new()
|> NFTables.NAT.port_forward(80, "192.168.1.100", 8080, table: "nat")
|> NFTables.NAT.port_forward(443, "192.168.1.100", 8443, table: "nat")
|> NFTables.submit(pid: pid)

redirect_port(builder \\ Builder.new(), from_port, to_port, opts \\ [])

Redirect a port to a different port on the same host (local port redirect).

Useful for transparent proxying.

Parameters

  • builder - Builder to add the rule to (defaults to new builder)
  • from_port - Port to redirect from
  • to_port - Port to redirect to
  • opts - Options:
    • :protocol - :tcp or :udp (default: :tcp)
    • :table - NAT table name (default: "nat")
    • :chain - Chain name (default: "prerouting")
    • :family - Protocol family (default: :inet)

Examples

# Redirect HTTP to local proxy
Builder.new()
|> NFTables.NAT.redirect_port(80, 3128)
|> NFTables.submit(pid: pid)

# Redirect HTTPS to local proxy
Builder.new()
|> NFTables.NAT.redirect_port(443, 8443)
|> NFTables.submit(pid: pid)

# Multiple redirects
Builder.new()
|> NFTables.NAT.redirect_port(80, 3128, table: "nat")
|> NFTables.NAT.redirect_port(443, 8443, table: "nat")
|> NFTables.submit(pid: pid)

setup_masquerade(builder \\ Builder.new(), interface, opts \\ [])

@spec setup_masquerade(NFTables.Builder.t(), String.t(), keyword()) ::
  NFTables.Builder.t()

Set up internet sharing (masquerade) on an interface.

This enables NAT for all outgoing traffic on the specified interface, allowing internal hosts to share a single public IP address.

Parameters

  • builder - Builder to add the rule to (defaults to new builder)
  • interface - Outgoing interface name (e.g., "eth0", "wan0")
  • opts - Options:
    • :table - NAT table name (default: "nat")
    • :chain - Chain name (default: "postrouting")
    • :family - Protocol family (default: :inet)

Examples

# Share internet connection via eth0
Builder.new()
|> NFTables.NAT.setup_masquerade("eth0")
|> NFTables.submit(pid: pid)

# Compose with other rules
Builder.new()
|> NFTables.NAT.setup_masquerade("wan0", table: "nat")
|> NFTables.NAT.source_nat("10.0.0.0/24", "203.0.113.1", table: "nat")
|> NFTables.submit(pid: pid)

source_nat(builder \\ Builder.new(), source, nat_ip, opts \\ [])

@spec source_nat(NFTables.Builder.t(), String.t(), String.t(), keyword()) ::
  NFTables.Builder.t()

Set up source NAT for a specific source IP or subnet.

Parameters

  • builder - Builder to add the rule to (defaults to new builder)
  • source - Source IP or CIDR (e.g., "192.168.1.0/24")
  • nat_ip - IP to NAT to
  • opts - Options:
    • :table - NAT table name (default: "nat")
    • :chain - Chain name (default: "postrouting")
    • :family - Protocol family (default: :inet)
    • :interface - Limit to specific interface (optional)

Examples

# NAT internal subnet to public IP
Builder.new()
|> NFTables.NAT.source_nat("192.168.1.0/24", "203.0.113.1")
|> NFTables.submit(pid: pid)

# NAT specific host
Builder.new()
|> NFTables.NAT.source_nat("192.168.1.100", "203.0.113.1")
|> NFTables.submit(pid: pid)

# With interface restriction
Builder.new()
|> NFTables.NAT.source_nat("10.0.0.0/24", "203.0.113.1", interface: "wan0")
|> NFTables.submit(pid: pid)

static_nat(builder \\ Builder.new(), public_ip, private_ip, opts \\ [])

@spec static_nat(NFTables.Builder.t(), String.t(), String.t(), keyword()) ::
  NFTables.Builder.t()

Set up static (1:1) NAT between two IP addresses.

Maps all traffic for a public IP to a private IP and vice versa. This function adds both DNAT (prerouting) and SNAT (postrouting) rules.

Parameters

  • builder - Builder to add the rules to (defaults to new builder)
  • public_ip - External IP address
  • private_ip - Internal IP address
  • opts - Options:
    • :table - NAT table name (default: "nat")
    • :family - Protocol family (default: :inet)

Examples

# Map public IP to DMZ host
Builder.new()
|> NFTables.NAT.static_nat("203.0.113.100", "192.168.1.100")
|> NFTables.submit(pid: pid)

# Multiple static NAT mappings
Builder.new()
|> NFTables.NAT.static_nat("203.0.113.100", "192.168.1.100", table: "nat")
|> NFTables.NAT.static_nat("203.0.113.101", "192.168.1.101", table: "nat")
|> NFTables.submit(pid: pid)