Linx.Netfilter.Patch (Linx v0.1.0)

Copy Markdown View Source

An ordered sequence of mutations that transforms one %Linx.Netfilter.Ruleset{} into another.

Patches are produced by Linx.Netfilter.diff/2 and consumed by Linx.Netfilter.push/2 (mode :reconcile) — together they implement minimal-change updates: only the entities that actually differ between current and desired state hit the wire.

Ops

A patch is a list of %Op{} values. Op kinds:

  • {:create_table, family, %Table{}} — kernel doesn't have this table.
  • {:delete_table, family, name} — kernel has it, desired doesn't.
  • {:create_chain, family, table_name, %Chain{}}.
  • {:delete_chain, family, table_name, chain_name}.
  • {:create_set, family, %Set{} | %Map{}} — covers maps and vmaps too (dispatch on struct).

  • {:delete_set, family, table_name, set_name}.
  • {:add_set_elements, family, table_name, set_name, [elem]}.
  • {:delete_set_elements, family, table_name, set_name, [elem]}.
  • {:create_rule, family, table_name, chain_name, %Rule{}, position}position is :append | {:after, handle} | {:before, handle} | {:at_index, n}.
  • {:replace_rule, family, table_name, chain_name, handle, %Rule{}} — in-place replace via NLM_F_REPLACE. Requires the kernel-assigned handle (carried by the rule pulled from the current ruleset).
  • {:delete_rule, family, table_name, chain_name, handle}.

Ordering

Patch ops are topologically sorted so creates of dependencies come before creates of dependents, and deletes happen in reverse:

1. delete rules
2. delete set elements
3. delete chains   (no orphan-rule reference issue)
4. delete sets
5. delete tables
6. create tables
7. create sets / maps
8. create chains
9. add set elements   (chain may be jump target)
  1. replace rules (chains+sets already exist)
  2. create rules

This is the order from_patch/1 emits inside one BATCH.

Inspect

Renders compactly:

#Linx.Netfilter.Patch<7 ops: 2 creates, 4 replaces, 1 delete>

Summary

Functions

Returns true iff the patch contains no ops.

An empty patch — same value as diff(r, r).

Topologically sorts a patch's ops into the canonical create/ delete order (see moduledoc). Idempotent — sort/1 on an already- sorted patch is a no-op.

Types

op()

@type op() ::
  {:create_table, atom(), Linx.Netfilter.Table.t()}
  | {:delete_table, atom(), String.t()}
  | {:create_chain, atom(), String.t(), Linx.Netfilter.Chain.t()}
  | {:delete_chain, atom(), String.t(), String.t()}
  | {:create_set, atom(), Linx.Netfilter.Set.t() | Linx.Netfilter.Map.t()}
  | {:delete_set, atom(), String.t(), String.t()}
  | {:add_set_elements, atom(), String.t(), String.t(), [term()]}
  | {:delete_set_elements, atom(), String.t(), String.t(), [term()]}
  | {:create_rule, atom(), String.t(), String.t(), Linx.Netfilter.Rule.t(),
     position()}
  | {:replace_rule, atom(), String.t(), String.t(), pos_integer(),
     Linx.Netfilter.Rule.t()}
  | {:delete_rule, atom(), String.t(), String.t(), pos_integer()}

position()

@type position() ::
  :append
  | {:after, pos_integer()}
  | {:before, pos_integer()}
  | {:at_index, non_neg_integer()}

t()

@type t() :: %Linx.Netfilter.Patch{ops: [op()]}

Functions

empty?(patch)

@spec empty?(t()) :: boolean()

Returns true iff the patch contains no ops.

new(ops \\ [])

@spec new([op()]) :: t()

An empty patch — same value as diff(r, r).

sort(patch)

@spec sort(t()) :: t()

Topologically sorts a patch's ops into the canonical create/ delete order (see moduledoc). Idempotent — sort/1 on an already- sorted patch is a no-op.