# `Linx.Netfilter.Ruleset`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/netfilter/ruleset.ex#L1)

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.

# `t`

```elixir
@type t() :: %Linx.Netfilter.Ruleset{
  tables: %{
    optional({Linx.Netfilter.Table.family(), String.t()}) =&gt;
      Linx.Netfilter.Table.t()
  }
}
```

# `table_ref`

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

# `add_chain`

```elixir
@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!`

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

Bang variant.

# `add_flowtable`

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

Adds a flowtable to a table.

# `add_flowtable!`

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

Bang variant.

# `add_map`

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

Adds a map (or vmap) to a table.

# `add_map!`

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

Bang variant.

# `add_object`

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

Adds an object to a table.

# `add_object!`

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

Bang variant.

# `add_rule`

```elixir
@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!`

```elixir
@spec add_rule!(
  t(),
  table_ref(),
  String.t(),
  Linx.Netfilter.Rule.t() | [Linx.Netfilter.Rule.expression_input()],
  keyword()
) :: t()
```

Bang variant.

# `add_set`

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

Adds a set to a table.

# `add_set!`

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

Bang variant.

# `add_table`

```elixir
@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!`

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

Bang variant of `add_table/4`.

# `delete_table`

```elixir
@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!`

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

Bang variant of `delete_table/2`.

# `fetch_table`

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

Fetches a table by reference.

# `new`

```elixir
@spec new() :: t()
```

An empty ruleset.

# `put_chain`

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

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

# `put_chain!`

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

Bang variant.

# `tables`

```elixir
@spec tables(t()) :: [
  {Linx.Netfilter.Table.family(), String.t(), Linx.Netfilter.Table.t()}
]
```

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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
