# `Linx.Netlink.Nfnl.Codec`
[🔗](https://github.com/oshlabs/linx/blob/v0.2.0/lib/linx/netlink/nfnl/codec.ex#L1)

Wire-format helpers for nfnetlink (`NETLINK_NETFILTER`, protocol 12).

Three pieces of nfnetlink machinery live here:

## 1. The `nfgenmsg` header

Every nfnetlink message body starts with a fixed 4-byte header
(`include/uapi/linux/netfilter/nfnetlink.h`):

    struct nfgenmsg {
      __u8  nfgen_family;  /* AF_* address family */
      __u8  version;       /* NFNETLINK_V0 = 0 */
      __be16 res_id;       /* subsystem-specific, BIG-ENDIAN on the wire */
    };

`Linx.Netlink.Codec` defaults to native byte order, but `res_id` is
documented as `__be16` and is interpreted that way by the kernel —
notably as the **target subsystem id** on `NFNL_MSG_BATCH_BEGIN` /
`_END` and as the **group number** for `NFNL_SUBSYS_ULOG` config
messages. `encode_nfgenmsg/3` and `decode_nfgenmsg/1` handle the
byte-order asymmetry directly so the generic codec stays
byte-order-uniform.

## 2. Subsys-id multiplexing on `nlmsghdr.type`

Inside nfnetlink, the netlink message type is
`(subsys_id << 8) | msg_type` — the high byte selects which
sub-subsystem the message targets, the low byte selects the operation
within that subsystem. `nlmsg_type/2` and `split_type/1` are the
conversion helpers; `subsys_*` constants are the well-known subsys
ids.

## 3. Batched transactions

Every nf_tables ruleset mutation must sit between an
`NFNL_MSG_BATCH_BEGIN` and an `NFNL_MSG_BATCH_END` (the global
envelope types `0x10` / `0x11`, defined in `nfnetlink.h`). The kernel
applies the inner messages atomically or rejects the batch whole.
`batch_begin/1..2` and `batch_end/1` produce the envelope messages —
the caller stitches them around the inner request stream.

## Why this is its own module, not part of `Linx.Netlink.Codec`

The general codec is byte-order-uniform (native) and per-family
agnostic. nfnetlink's `__be16 res_id` and high-byte subsys
multiplexing are netfilter-specific quirks; keeping them here keeps
the shared codec clean.

# `batch_begin`

```elixir
@spec batch_begin(
  :nftables | :ctnetlink | :queue | :ulog | 0..255,
  keyword()
) :: Linx.Netlink.Message.t()
```

Constructs a `NFNL_MSG_BATCH_BEGIN` envelope message targeting the
named sub-subsystem.

Returns a `%Message{}` with `type` = `NFNL_MSG_BATCH_BEGIN` (0x10),
`payload` = the 4-byte `nfgenmsg` carrying the target subsys id as
`res_id`. Caller fills in `seq` before encoding.

When `genid` is provided, appends `NFNL_BATCH_GENID` (attribute id
1, u32 BE) to the payload — the kernel rejects the batch with
`-ERESTART` at commit time if the netns ruleset generation has
advanced since `genid` was read. Used by `:reconcile`-mode pushes
for optimistic concurrency.

## Examples

    # Begin a batch targeting nf_tables:
    iex> msg = Linx.Netlink.Nfnl.Codec.batch_begin(:nftables)
    iex> msg.type
    16  # NFNL_MSG_BATCH_BEGIN
    iex> msg.payload
    <<0, 0, 0, 10>>  # nfgenmsg with res_id = NFNL_SUBSYS_NFTABLES (10)

# `batch_end`

```elixir
@spec batch_end(:nftables | :ctnetlink | :queue | :ulog | 0..255) ::
  Linx.Netlink.Message.t()
```

Constructs a `NFNL_MSG_BATCH_END` envelope message closing the batch.

Returns a `%Message{}` with `type` = `NFNL_MSG_BATCH_END` (0x11). The
`res_id` mirrors the begin message but the kernel only acts on
end-of-batch signalling; the value is informational.

# `decode_nfgenmsg`

```elixir
@spec decode_nfgenmsg(binary()) :: {0..255, 0..255, 0..65535, binary()}
```

Decodes the leading 4 bytes of a binary as a `nfgenmsg` header.

Returns `{family, version, res_id, rest}`. `family` is returned as
the raw integer (caller maps to atom if useful); `res_id` is decoded
from big-endian.

Raises `ArgumentError` if the binary is shorter than 4 bytes.

# `encode_nfgenmsg`

```elixir
@spec encode_nfgenmsg(atom() | 0..255, 0..65535) :: &lt;&lt;_::32&gt;&gt;
```

Encodes a 4-byte `nfgenmsg` header.

`family` is one of the `NFPROTO_*` constants (see `nfproto/1`);
`res_id` is sub-subsystem-specific and is `__be16` on the wire.
Defaults: `family: :unspec`, `res_id: 0`.

## Examples

    iex> Linx.Netlink.Nfnl.Codec.encode_nfgenmsg()
    <<0, 0, 0, 0>>

    # BATCH_BEGIN targeting NFTABLES (res_id = 10, big-endian):
    iex> Linx.Netlink.Nfnl.Codec.encode_nfgenmsg(:unspec, 10)
    <<0, 0, 0, 10>>

    # NFLOG (ULOG) config for group 5000 (res_id big-endian):
    iex> Linx.Netlink.Nfnl.Codec.encode_nfgenmsg(:unspec, 5000)
    <<0, 0, 19, 136>>

# `get_gen`

```elixir
@spec get_gen(Linx.Netlink.Socket.t()) ::
  {:ok,
   %{
     id: non_neg_integer(),
     proc_pid: non_neg_integer() | nil,
     proc_name: String.t() | nil
   }}
  | {:error, term()}
```

Sends `NFT_MSG_GETGEN` and returns the kernel's reply.

The reply is a single `NFT_MSG_NEWGEN` message carrying `NFTA_GEN_ID`
(32-bit monotonic generation counter) and — when the kernel was
built with the relevant config — `NFTA_GEN_PROC_PID` /
`NFTA_GEN_PROC_NAME` attributes attributing the most recent commit.

## Examples

    iex> {:ok, sock} = Linx.Netlink.Nfnl.open()
    iex> {:ok, gen} = Linx.Netlink.Nfnl.Codec.get_gen(sock)
    iex> is_integer(gen.id) and gen.id >= 0
    true

# `nfproto`

```elixir
@spec nfproto(atom()) :: 0..255
```

NFPROTO_* address-family constants for the `nfgen_family` header
field.

# `nlmsg_type`

```elixir
@spec nlmsg_type(0..255, 0..255) :: 0..65535
```

Packs a nfnetlink `(subsys_id, msg_type)` pair into the 16-bit
`nlmsghdr.type` the wire format uses.

Inside `NETLINK_NETFILTER`, the high byte is the sub-subsystem id
and the low byte is the operation. `nlmsg_type(10, 0x10)` (NFTABLES,
NEWGEN) produces `0x0a10`.

# `split_type`

```elixir
@spec split_type(0..65535) :: {0..255, 0..255}
```

Splits a `nlmsghdr.type` from nfnetlink back into `{subsys_id, msg_type}`.

Inverse of `nlmsg_type/2`.

# `subsys_ctnetlink`

```elixir
@spec subsys_ctnetlink() :: 1
```

NFNL_SUBSYS_CTNETLINK — the conntrack sub-subsystem id (1).

# `subsys_nftables`

```elixir
@spec subsys_nftables() :: 10
```

NFNL_SUBSYS_NFTABLES — the nf_tables sub-subsystem id (10).

# `subsys_queue`

```elixir
@spec subsys_queue() :: 3
```

NFNL_SUBSYS_QUEUE — the NFQUEUE sub-subsystem id (3).

# `subsys_ulog`

```elixir
@spec subsys_ulog() :: 4
```

NFNL_SUBSYS_ULOG — the NFLOG sub-subsystem id (4).

---

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