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}—positionis:append|{:after, handle}|{:before, handle}|{:at_index, n}.{:replace_rule, family, table_name, chain_name, handle, %Rule{}}— in-place replace viaNLM_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)- replace rules (chains+sets already exist)
- 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
Types
@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()}
@type position() :: :append | {:after, pos_integer()} | {:before, pos_integer()} | {:at_index, non_neg_integer()}
@type t() :: %Linx.Netfilter.Patch{ops: [op()]}
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.