NFTables.Expr.Verdict (NFTables v0.8.2)

View Source

Verdict and control flow functions for Expr.

Provides terminal verdicts (accept, drop, reject), non-terminal actions (continue, notrack), advanced features (queue, flow offload), and chain control flow (jump, goto, return). Verdicts determine the final fate of packets and control how rules are processed.

Import

import NFTables.Expr.Verdict

Examples

# Basic verdicts
tcp() |> dport(22) |> accept()
source_ip("10.0.0.0/8") |> drop()
tcp() |> dport(23) |> reject(:tcp_reset)

# Non-terminal actions
tcp() |> dport(80) |> log("HTTP: ") |> continue()
tcp() |> dport(443) |> notrack() |> accept()

# Chain control flow
source_ip("192.168.1.0/24") |> jump("trusted_chain")
tcp() |> dport(8080) |> goto("app_chain")

# Advanced features
tcp() |> dport(80) |> tcp_flags([:syn], [:syn, :ack, :rst, :fin]) |> synproxy()
ct_state([:established]) |> flow_offload()

For more information, see the nftables verdicts wiki.

Summary

Functions

Continue to next rule.

Drop packets silently

Duplicate packet to another interface.

Enable flow offloading to hardware.

Go to another chain (non-returning jump).

Disable connection tracking for packets.

Queue packets to userspace for inspection.

Functions

accept(builder \\ Expr.expr())

@spec accept(NFTables.Expr.t()) :: NFTables.Expr.t()

Accept packets

continue(builder \\ Expr.expr())

@spec continue(NFTables.Expr.t()) :: NFTables.Expr.t()

Continue to next rule.

Unlike accept/drop/reject, this verdict continues rule evaluation. Useful for complex rule flows where you want to apply actions but continue processing.

Example

# Log and continue (don't stop processing)
builder
|> tcp()
|> dport(22)
|> log("SSH: ")
|> continue()

# Apply action and continue
builder
|> source_ip("192.168.1.0/24")
|> set_mark(100)
|> continue()

Use Cases

  • Logging without terminal verdict
  • Multi-stage packet processing
  • Complex action chains
  • Audit trails with continued filtering

drop(builder \\ Expr.expr())

@spec drop(NFTables.Expr.t()) :: NFTables.Expr.t()

Drop packets silently

duplicate_to(builder \\ Expr.expr(), interface)

@spec duplicate_to(NFTables.Expr.t(), String.t()) :: NFTables.Expr.t()

Duplicate packet to another interface.

Sends a copy of the packet to a different interface while the original continues normal processing. Used for traffic mirroring and monitoring.

Example

# Mirror to monitoring interface
builder
|> tcp()
|> dport(443)
|> duplicate_to("monitor0")
|> accept()

# Mirror suspicious traffic to IDS
builder
|> source_ip("203.0.113.0/24")
|> duplicate_to("ids0")
|> continue()

Use Cases

  • Network traffic monitoring
  • IDS/IPS analysis
  • Traffic analysis and debugging
  • Compliance and auditing

flow_offload(builder \\ Expr.expr(), opts \\ [])

@spec flow_offload(
  NFTables.Expr.t(),
  keyword()
) :: NFTables.Expr.t()

Enable flow offloading to hardware.

Offloads established connections to hardware for fast-path processing. Dramatically improves throughput for forwarded traffic on supported hardware.

Options

  • :table - Flowtable name (required if using named flowtable)

Example

# Basic flow offload
builder
|> ct_state([:established])
|> flow_offload()

# Named flowtable
builder
|> ct_state([:established])
|> flow_offload(table: "fastpath")

Use Cases

  • Router throughput optimization
  • Hardware acceleration (if supported)
  • Multi-gigabit routing
  • Reducing CPU load on forwarding

Requirements

  • Hardware support (not all NICs support offloading)
  • Flowtable must be created first
  • Only works for ESTABLISHED connections

goto(builder \\ Expr.expr(), chain_name)

@spec goto(NFTables.Expr.t(), String.t()) :: NFTables.Expr.t()

Go to another chain (non-returning jump).

Transfers control to the specified chain permanently. Unlike jump, control never returns to the current chain.

Example

# Permanent transfer to specialized chain
builder
|> tcp()
|> dport(443)
|> goto("https_chain")

Difference from jump/1

  • jump/1: Returns after chain processing (like a function call)
  • goto/1: Never returns (like a goto statement)

jump(builder \\ Expr.expr(), chain_name)

@spec jump(NFTables.Expr.t(), String.t()) :: NFTables.Expr.t()

Jump to another chain.

Transfers control to the specified chain. If the chain accepts the packet, processing continues in the current chain after the jump. If the chain drops/rejects the packet, it terminates immediately.

Example

# Jump to custom logging chain
builder
|> tcp()
|> dport(22)
|> jump("ssh_logging")
|> accept()

# Complex rule organization
builder
|> source_ip("192.168.1.0/24")
|> jump("internal_rules")

Use Cases

  • Organize complex rulesets into logical chains
  • Reusable rule groups
  • Conditional rule application

notrack(builder \\ Expr.expr())

@spec notrack(NFTables.Expr.t()) :: NFTables.Expr.t()

Disable connection tracking for packets.

Marks packets as untracked, bypassing the connection tracking system. This improves performance but disables stateful features.

Example

# Disable tracking for high-volume traffic
builder
|> tcp()
|> dport(443)
|> notrack()

# Skip tracking for local traffic
builder
|> source_ip("127.0.0.0/8")
|> notrack()

Use Cases

  • High-throughput servers (performance optimization)
  • Stateless firewalls
  • Reducing conntrack table load
  • Local/loopback traffic optimization

WARNING

Disabling connection tracking means:

  • No stateful filtering (NEW/ESTABLISHED states)
  • No NAT for these packets
  • No connection limits

queue_to_userspace(builder \\ Expr.expr(), queue_num, opts \\ [])

@spec queue_to_userspace(NFTables.Expr.t(), non_neg_integer(), keyword()) ::
  NFTables.Expr.t()

Queue packets to userspace for inspection.

Sends packets to a userspace program (IDS/IPS) via NFQUEUE. The userspace program decides the final verdict.

Options

  • :bypass - If queue is full, accept the packet (default: drop)
  • :fanout - Distribute packets across multiple queues

Example

# Queue to IDS on queue 0
builder
|> tcp()
|> dport(80)
|> queue_to_userspace(0)

# Queue with bypass (don't drop on queue full)
builder
|> tcp()
|> dport(443)
|> queue_to_userspace(1, bypass: true)

# Queue with fanout
builder
|> protocol(:tcp)
|> queue_to_userspace(0, fanout: true)

Use Cases

  • IDS/IPS integration (Suricata, Snort)
  • Custom packet inspection
  • Deep packet inspection
  • Application-level filtering

reject(builder \\ Expr.expr(), type \\ :icmp_port_unreachable)

@spec reject(NFTables.Expr.t(), atom()) :: NFTables.Expr.t()

Reject packets with ICMP error.

Example

builder |> reject()
builder |> reject(:tcp_reset)

return_from_chain(builder \\ Expr.expr())

@spec return_from_chain(NFTables.Expr.t()) :: NFTables.Expr.t()

Return from chain.

Returns control to the calling chain. Only valid in chains that were entered via jump (not base chains).

Example

# In a custom chain, return early
builder
|> source_ip("192.168.1.100")
|> return_from_chain()

# Continue processing in calling chain