Linx.NFT.Runtime (Linx v0.1.0)

Copy Markdown View Source

Runtime helpers for ~NFT sigils that contain #{...} interpolations.

When the sigil body has no interpolations, Linx.NFT.Compiler produces a %Linx.Netfilter.Ruleset{} at compile time and the macro emits it as a literal. When there are interpolations, the static value isn't knowable until runtime, so Linx.NFT.RuntimeCompiler emits Elixir code that constructs the Ruleset at runtime. At each #{expr} position, it calls into one of the helpers here — they take an evaluated Elixir value plus the field kind the surrounding nft syntax expects ({:int, 1|2|4|8}, :ipv4, :ipv6, :ifname), validate, and return either an encoded %Expr{} ready to splice into the rule's expression list or the raw bytes to use as a comparison value.

Errors at runtime raise ArgumentError with a message naming the expected kind and the actual value — there's no source-location context here (the macro keeps the AST node's meta on the static side, but the runtime helper just sees the value), so test the interpolated values close to the sigil call site.

Supported value kinds

  • {:int, width}width1, 2, 4, 8 bytes, big-endian encoded. Accepts any non-negative integer that fits.
  • :ipv4Linx.IP.parse/1-able string, 4-tuple {a, b, c, d}, raw 4-byte binary, or %Linx.IP{family: :inet}. Returns 4 bytes.
  • :ipv6 — same shape extended: parse-able string, 8-tuple of 0..0xFFFF, raw 16-byte binary, or %Linx.IP{family: :inet6}. Returns 16 bytes.
  • :ifname — binary, padded/truncated to IFNAMSIZ (16 bytes) with trailing zero bytes.

Each kind has a matching encode_*!/1 helper that returns the raw bytes, plus the convenience cmp!/3 that wraps the bytes in an %Expr{name: :cmp} ready to splice into a rule.

Summary

Functions

Encodes value for the given kind, then builds an %Expr{name: :cmp} with op. Returns the %Expr{}. Raises ArgumentError if value doesn't match the expected kind.

Encodes value to a binary suitable for the given kind. Raises ArgumentError on type mismatch / out-of-range.

Pads an interface-name binary out to IFNAMSIZ (16 bytes) with trailing zeros. Truncates if longer (matches kernel behaviour — the trailing bytes are ignored).

Encodes an integer to a width-byte big-endian binary.

Encodes an IPv4 input to its 4-byte big-endian form.

Encodes an IPv6 input to its 16-byte big-endian form.

Functions

cmp!(op, value, kind)

@spec cmp!(atom(), term(), {:int, 1 | 2 | 4 | 8} | :ipv4 | :ipv6 | :ifname) ::
  Linx.Netfilter.Expr.t()

Encodes value for the given kind, then builds an %Expr{name: :cmp} with op. Returns the %Expr{}. Raises ArgumentError if value doesn't match the expected kind.

encode!(value, kind)

@spec encode!(term(), {:int, 1 | 2 | 4 | 8} | :ipv4 | :ipv6 | :ifname) :: binary()

Encodes value to a binary suitable for the given kind. Raises ArgumentError on type mismatch / out-of-range.

encode_ifname!(s)

@spec encode_ifname!(binary()) :: <<_::128>>

Pads an interface-name binary out to IFNAMSIZ (16 bytes) with trailing zeros. Truncates if longer (matches kernel behaviour — the trailing bytes are ignored).

encode_int!(n, width)

@spec encode_int!(integer(), 1 | 2 | 4 | 8) :: binary()

Encodes an integer to a width-byte big-endian binary.

encode_ipv4!(bin)

@spec encode_ipv4!(term()) :: <<_::32>>

Encodes an IPv4 input to its 4-byte big-endian form.

encode_ipv6!(bin)

@spec encode_ipv6!(term()) :: <<_::128>>

Encodes an IPv6 input to its 16-byte big-endian form.