Linx.Netfilter.Ruleset (Linx v0.1.0)

Copy Markdown View Source

The top-level netfilter value type — a netns-shaped collection of tables (and everything inside them) as plain data.

A %Ruleset{} is what push/2 writes to the kernel, what pull/1..2 reads back, and what diff/2 takes pairs of. The pipeline DSL — new/0, add_table/3, add_chain/4, add_rule/4, add_set/3, add_map/3, add_object/3, add_flowtable/3 — is how authoring-time code builds one. The ~NFT sigil compiles to the same DSL calls.

Fields

  • :tables%{{family, name} => %Linx.Netfilter.Table{}}. Tables are uniquely identified by (family, name) at the kernel level — inet mytable and ip mytable coexist as distinct tables.

Pipeline DSL

Two flavours of every mutator:

  • Plain (e.g. add_table/3) — returns {:ok, Ruleset.t()} | {:error, {:bad_X, reason}}. Validator- setter shape; compose via with.

  • Bang (e.g. add_table!/3) — returns Ruleset.t() or raises ArgumentError. Pipeline-friendly for inline construction.

    ruleset = Ruleset.new() |> Ruleset.add_table!(:inet, "myapp") |> Ruleset.add_chain!("myapp", "input",

       type: :filter, hook: :input, priority: 0, policy: :drop)

    |> Ruleset.add_rule!("myapp", "input",

       Rule.build!([Expr.immediate(:accept)], tag: :allow_all))

Table references

Most mutators accept either a bare name string (when unambiguous) or a {family, name} tuple. A bare name raises / errors with :ambiguous_table_name if the ruleset has multiple tables of that name across families.

add_chain!(rs, "myapp", "input", ...)          # name → unambiguous
add_chain!(rs, {:inet, "myapp"}, "input", ...) # tuple → explicit

Errors

Validator-setter functions return tagged-tuple errors so callers can pattern-match on the kind of failure:

  • {:bad_table, _} — bad family / flag / duplicate name.
  • {:bad_chain, _} — bad hook/type, missing device on ingress, etc. (from Chain.new/2 and Chain.validate_for_family/2).
  • {:bad_rule, _} — bad expression list, duplicate tag.
  • {:bad_set, _} / {:bad_set_element, _} — set shape / element type mismatch.
  • {:bad_map, _} / {:bad_map_element, _} — map shape / element shape (including non-verdict data in vmaps).
  • {:bad_object, _} / {:bad_flowtable, _}.
  • {:no_such_table, ref} / {:no_such_chain, ref} / {:ambiguous_table_name, name} — navigation failures.

Kernel-rejection errors come back as %Linx.Netfilter.Error{} — always distinct from the value-type errors above.

Summary

Functions

Adds a chain to a table, building it inline via Chain.new/2.

Adds a flowtable to a table.

Adds a map (or vmap) to a table.

Adds an object to a table.

Appends a rule to a chain inside a table.

Adds a set to a table.

Adds a new table to the ruleset. Builds the table via Table.new/3, checks (family, name) uniqueness, inserts.

Removes a table from the ruleset by reference. Returns {:error, {:no_such_table, ref}} if absent.

Fetches a table by reference.

An empty ruleset.

Inserts a pre-built %Chain{} into a table.

Lists tables — [{family, name, %Table{}}, ...].

Types

t()

@type t() :: %Linx.Netfilter.Ruleset{
  tables: %{
    optional({Linx.Netfilter.Table.family(), String.t()}) =>
      Linx.Netfilter.Table.t()
  }
}

table_ref()

@type table_ref() :: String.t() | {Linx.Netfilter.Table.family(), String.t()}

Functions

add_chain(rs, table_ref, chain_name, opts \\ [])

@spec add_chain(t(), table_ref(), String.t(), keyword()) ::
  {:ok, t()} | {:error, term()}

Adds a chain to a table, building it inline via Chain.new/2.

table_ref is a bare name string (unambiguous case) or a {family, name} tuple.

add_chain!(rs, table_ref, chain_name, opts \\ [])

@spec add_chain!(t(), table_ref(), String.t(), keyword()) :: t()

Bang variant.

add_flowtable(rs, table_ref, ft)

@spec add_flowtable(t(), table_ref(), Linx.Netfilter.Flowtable.t()) ::
  {:ok, t()} | {:error, term()}

Adds a flowtable to a table.

add_flowtable!(rs, table_ref, ft)

@spec add_flowtable!(t(), table_ref(), Linx.Netfilter.Flowtable.t()) :: t()

Bang variant.

add_map(rs, table_ref, map)

@spec add_map(t(), table_ref(), Linx.Netfilter.Map.t()) ::
  {:ok, t()} | {:error, term()}

Adds a map (or vmap) to a table.

add_map!(rs, table_ref, map)

@spec add_map!(t(), table_ref(), Linx.Netfilter.Map.t()) :: t()

Bang variant.

add_object(rs, table_ref, obj)

@spec add_object(t(), table_ref(), Linx.Netfilter.Object.t()) ::
  {:ok, t()} | {:error, term()}

Adds an object to a table.

add_object!(rs, table_ref, obj)

@spec add_object!(t(), table_ref(), Linx.Netfilter.Object.t()) :: t()

Bang variant.

add_rule(rs, table_ref, chain_name, rule_or_exprs, opts \\ [])

@spec add_rule(
  t(),
  table_ref(),
  String.t(),
  Linx.Netfilter.Rule.t() | [Linx.Netfilter.Rule.expression_input()],
  keyword()
) :: {:ok, t()} | {:error, term()}

Appends a rule to a chain inside a table.

rule_or_exprs is either a pre-built %Rule{} or a list of expressions (passed to Rule.build/2). When rule_or_exprs is a list, opts are forwarded to Rule.build/2 (tag / comment / handle).

Tag uniqueness within the chain is enforced — a rule with a duplicate tag returns {:error, {:bad_rule, {:duplicate_tag, _}}}. Untagged rules are always accepted.

add_rule!(rs, table_ref, chain_name, rule_or_exprs, opts \\ [])

Bang variant.

add_set(rs, table_ref, set)

@spec add_set(t(), table_ref(), Linx.Netfilter.Set.t()) ::
  {:ok, t()} | {:error, term()}

Adds a set to a table.

add_set!(rs, table_ref, set)

@spec add_set!(t(), table_ref(), Linx.Netfilter.Set.t()) :: t()

Bang variant.

add_table(rs, family, name, opts \\ [])

@spec add_table(t(), Linx.Netfilter.Table.family(), String.t(), keyword()) ::
  {:ok, t()} | {:error, {:bad_table, term()}}

Adds a new table to the ruleset. Builds the table via Table.new/3, checks (family, name) uniqueness, inserts.

add_table!(rs, family, name, opts \\ [])

@spec add_table!(t(), Linx.Netfilter.Table.family(), String.t(), keyword()) :: t()

Bang variant of add_table/4.

delete_table(rs, ref)

@spec delete_table(t(), table_ref()) :: {:ok, t()} | {:error, term()}

Removes a table from the ruleset by reference. Returns {:error, {:no_such_table, ref}} if absent.

delete_table!(rs, ref)

@spec delete_table!(t(), table_ref()) :: t()

Bang variant of delete_table/2.

fetch_table(rs, ref)

@spec fetch_table(t(), table_ref()) ::
  {:ok, Linx.Netfilter.Table.t()} | {:error, term()}

Fetches a table by reference.

new()

@spec new() :: t()

An empty ruleset.

put_chain(rs, table_ref, chain)

@spec put_chain(t(), table_ref(), Linx.Netfilter.Chain.t()) ::
  {:ok, t()} | {:error, term()}

Inserts a pre-built %Chain{} into a table.

put_chain!(rs, table_ref, chain)

@spec put_chain!(t(), table_ref(), Linx.Netfilter.Chain.t()) :: t()

Bang variant.

tables(ruleset)

Lists tables — [{family, name, %Table{}}, ...].