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

An error returned by a `Linx.Netfilter` operation.

Built from a failed netlink request or a rejected batch (`NLMSG_ERROR`
reply from the kernel). The shape mirrors `Linx.Mount.Error` /
`Linx.User.Error` / `Linx.Sysctl.Error` for `:operation`, `:errno`,
`:code`, and `:message`, plus netfilter-specific context fields
that are populated when the kernel surfaces them:

  * `:operation` — what we were trying to do, as an atom. Covers
    every public verb and every wire-level stage:

    * Public-API stages: `:open`, `:close`, `:get_gen`, `:push`,
      `:pull`, `:diff`, `:subscribe`, `:log_listen`.
    * Wire-level batch stages: `:batch_begin`, `:batch_end`,
      `:newtable`, `:newchain`, `:newrule`, `:newset`,
      `:newsetelem`, `:newobj`, `:newflowtable`, `:delgen`.
    * Namespace-acquisition stages (when opening a socket in
      another netns): `:open_ns`, `:setns`, `:unshare`, `:thread`.

  * `:errno` — the POSIX errno as an atom (`:enoent`, `:eperm`,
    `:eacces`, `:einval`, `:erestart`, `:eopnotsupp`, …).

  * `:code` — the matching positive errno integer, or `nil` if we
    don't have a mapping for this atom. Symmetric with the other
    Linx error structs.

  * `:message` — kernel's extended-ack message (NLMSGERR_ATTR_MSG),
    a human-readable string explaining what was wrong with the
    offending message. `nil` when the kernel didn't include one.

  * `:subsys` — `:nftables` | `:ctnetlink` | `:queue` | `:ulog` |
    `nil`. Which nfnetlink sub-subsystem the failure was for.

  * `:msg_type` — the low-byte nfnetlink message type that
    failed, when the error came from a specific inner message
    (e.g. `0x06` for NFT_MSG_NEWRULE). `nil` for non-batch
    failures.

  * `:batch_seq` — sequence number of the offending inner message
    within the batch. `nil` for non-batch failures.

  * `:attr_offset` — byte offset into the offending message at
    which the kernel detected the problem (from
    NLMSGERR_ATTR_OFFS, kernel ≥ 4.12). `nil` when not provided.

  * `:ruleset_gen` — the ruleset generation at which the error
    occurred. Useful for `ERESTART` (BATCH_GENID mismatch)
    diagnostics. `nil` when not relevant.

Pattern-match on `:errno` and `:operation` to handle specific
failures:

    case Linx.Netfilter.push(nfnl, ruleset) do
      :ok ->
        :ok

      {:error, %Linx.Netfilter.Error{errno: :eacces}} ->
        # No CAP_NET_ADMIN; can't mutate the kernel's nftables.
        :no_perm

      {:error, %Linx.Netfilter.Error{errno: :erestart, ruleset_gen: gen}} ->
        # BATCH_GENID mismatch — another writer committed between
        # our pull and our push. Re-pull, recompute, retry.
        {:concurrent_modification, gen}

      {:error, %Linx.Netfilter.Error{errno: :eopnotsupp, msg_type: t}} ->
        # The kernel doesn't support this op on this entity —
        # typically an in-place modification that requires
        # delete+recreate.
        {:not_supported, t}
    end

Implements `Exception`, so an error can be `raise`d or rendered
with `Exception.message/1`.

Caller-side input mistakes (invalid Ruleset shape, bad opts) come
back as tagged tuples (`{:error, {:bad_chain, _}}`,
`{:error, {:bad_rule, _}}`, `{:error, {:bad_set_element, _}}`,
`{:error, {:tag_required, _}}`) — distinct from kernel rejections,
matching the convention every other Linx subsystem uses.

# `operation`

```elixir
@type operation() ::
  :open
  | :close
  | :get_gen
  | :push
  | :pull
  | :diff
  | :subscribe
  | :log_listen
  | :batch_begin
  | :batch_end
  | :newtable
  | :newchain
  | :newrule
  | :newset
  | :newsetelem
  | :newobj
  | :newflowtable
  | :delgen
  | :open_ns
  | :setns
  | :unshare
  | :thread
```

# `subsys`

```elixir
@type subsys() :: :nftables | :ctnetlink | :queue | :ulog | nil
```

# `t`

```elixir
@type t() :: %Linx.Netfilter.Error{
  __exception__: true,
  attr_offset: non_neg_integer() | nil,
  batch_seq: non_neg_integer() | nil,
  code: pos_integer() | nil,
  errno: atom(),
  message: String.t() | nil,
  msg_type: 0..255 | nil,
  operation: operation(),
  ruleset_gen: non_neg_integer() | nil,
  subsys: subsys()
}
```

# `from_posix`

```elixir
@spec from_posix(atom(), operation(), keyword()) :: t()
```

Builds a `%Linx.Netfilter.Error{}` from a posix-atom errno and the
operation we attempted.

Optional opts populate the netfilter-specific context fields when
the caller has them (typically populated from decoded `NLMSG_ERROR`
attributes by the request engine).

---

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