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

Converts kernel-side `%Linx.Netlink.Message{}` payloads back into
`%Linx.Netfilter.*{}` value structs.

The shape mirrors `Linx.Netfilter.Encoder` — one decode function
per entity. `from_msgs/3` groups a stream of decoded entities
into a `%Ruleset{}`.

## Wire format quirks

Same as `Linx.Netfilter.Encoder`: nftables NLA_U32 / NLA_U64 are
**big-endian**, attribute IDs are namespaced.

# `chain`

```elixir
@spec chain(binary()) :: {Linx.Netfilter.Table.family(), Linx.Netfilter.Chain.t()}
```

Decodes a `NEWCHAIN` message body into a `%Linx.Netfilter.Chain{}`.

The chain's `:family` comes from the nfgenmsg header in the same
message; it isn't stored on the Chain struct (it lives on the
enclosing Table) but is needed here to map the wire hook number
back to the family-specific hook atom.

Returns `{family, chain}` so the caller (typically `from_msgs/3`)
can attach the chain to the right table.

# `event`

```elixir
@spec event(Linx.Netlink.Message.t()) :: Linx.Netfilter.Event.t()
```

Decodes a `NFNLGRP_NFTABLES` multicast message into a partial
`%Linx.Netfilter.Event{}` — `gen_id` / `proc_pid` / `proc_name`
are left nil; the Monitor GenServer fills them in from the most
recent `NEW_GEN` event.

For NEW_GEN events, the gen / pid / name come from the body
itself.

Dispatches on the low byte of `nlmsghdr.type` (the per-subsys
message opcode).

# `from_msgs`

```elixir
@spec from_msgs(
  [Linx.Netfilter.Table.t()],
  [{Linx.Netfilter.Table.family(), Linx.Netfilter.Chain.t()}],
  [
    {Linx.Netfilter.Table.family(), String.t(), String.t(),
     Linx.Netfilter.Rule.t()}
  ],
  [
    {Linx.Netfilter.Table.family(),
     Linx.Netfilter.Set.t() | Linx.Netfilter.Map.t()}
  ],
  [{Linx.Netfilter.Table.family(), String.t(), String.t(), [term()]}]
) :: Linx.Netfilter.Ruleset.t()
```

Builds a `%Ruleset{}` from separate lists of decoded entries.

  * `tables` — `[%Table{}]` from a `NFT_MSG_GETTABLE` dump.
  * `chains` — `[{family, %Chain{}}]` from a `NFT_MSG_GETCHAIN` dump.
  * `rules` — `[{family, table_name, chain_name, %Rule{}}]` from
    a `NFT_MSG_GETRULE` dump.
  * `sets` — `[{family, %Set{} | %Map{}}]` from a
    `NFT_MSG_GETSET` dump.
  * `set_elements` — `[{family, table_name, set_name, [elem]}]`
    from per-set `NFT_MSG_GETSETELEM` calls.

Chains, sets, and rules are attached to their parents by
`(family, table_name)`. Set elements are materialised against
the parent set's `key_type` / `data_type` and attached to the
set in dump order.

Entities that reference a missing parent are silently dropped.

# `materialize_elements`

```elixir
@spec materialize_elements([{binary(), term()} | binary()], atom(), atom() | nil) :: [
  term()
]
```

Materialises a raw set-element list (from `set_elements/1`) into
the value shape the parent set expects. Plain sets keep raw key
binaries (the codec doesn't know to expand `<<10, 0, 0, 5>>` back
to `{10, 0, 0, 5}` without context). Maps preserve `{key, value}`.

# `rule`

```elixir
@spec rule(binary()) ::
  {Linx.Netfilter.Table.family(), String.t(), String.t(),
   Linx.Netfilter.Rule.t()}
```

Decodes a `NEWRULE` message body into a `%Linx.Netfilter.Rule{}`.

Returns `{family, table_name, chain_name, rule}` so the caller
can attach to the right table+chain.

# `set`

```elixir
@spec set(binary()) ::
  {Linx.Netfilter.Table.family(),
   Linx.Netfilter.Set.t() | Linx.Netfilter.Map.t()}
```

Decodes a `NEWSET` body into either a `%Linx.Netfilter.Set{}`
(plain set — no `NFT_SET_F_MAP` flag) or a `%Linx.Netfilter.Map{}`
(map / vmap).

Returns `{family, set_or_map}` for downstream assembly.

# `set_elements`

```elixir
@spec set_elements(binary()) ::
  {Linx.Netfilter.Table.family(), String.t(), String.t(),
   [{binary(), term()} | binary()]}
```

Decodes a `NEWSETELEM` body into a list of elements attached to
a `(family, table_name, set_name)`.

Returns `{family, table_name, set_name, elements}` where
`elements` is a list of either raw key terms or `{key, value}`
tuples (the caller resolves which based on whether the parent
set is plain or a map).

For now we return the elements with KEY binary unparsed (raw
binary) and DATA either a raw binary or a `%Verdict{}` for
verdict data. Higher-level conversion (binary → tuple, etc.)
happens at assembly time when we know the parent set's
key_type.

# `table`

```elixir
@spec table(binary()) :: Linx.Netfilter.Table.t()
```

Decodes a `NEWTABLE` message body into a `%Linx.Netfilter.Table{}`.

`body` is `%Message{payload: body}`'s payload — `nfgenmsg` header
followed by NLAs.

---

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