Industrial Rule Engine¶
The L2Proxy Rule Engine turns decoded industrial traffic into an explicit operational decision. It is designed for policies that must be understandable by control engineering, OT operations, and cybersecurity—not hidden inside compiled signatures.
Why this differs from a port-based firewall¶
| Network-only control | L2Proxy industrial policy |
|---|---|
| “Allow TCP 20000” | “Allow DNP3 monitoring, but require a valid Select before Operate” |
| “Allow TCP 502” | “Log or block a write to the protected register range” |
| “Allow this source IP” | “Permit this operation only for the expected session, device, and process phase” |
| “Alert on packet count” | “Count restart or operate events per device and emit one threshold event” |
Decision inputs¶
A rule can combine:

Figure — Rule Engine decisions remain linked from policy intent through enforcement to evidence.
- Ethernet, MAC, IP, port, direction, and protocol layers;
- decoded protocol fields and repeated field values;
- semantic protocol helpers such as DNP3 control or point predicates;
- optional L2Proxy Connect access domain, authenticated user, session, and connection-kind identity;
- current state, state age, and prior transition outcome;
- customer-specific thresholds, point mappings, identities, and timing.
Customer-facing actions¶
Log¶
Observe without changing forwarding. Use this for baselining, audit, policy tuning, and staged rollout.
Accept¶
Explicitly permit the operation. In multi-state enforcement, acceptance should follow an atomic transition that proves the prerequisite was present and consumed.
Drop¶
Reject a policy violation at an inline boundary. A drop policy should have a clear asset scope, operational owner, test fixture, false-positive review, and rollback plan.
State actions¶
set_state, delete_state, transition_state, and increment_state record observation,
consume one-time authorization, manage lifecycle, and count events. They do not hard-code
any industrial protocol or customer process policy.
Example: protocol-aware decision with evidence¶
version: 1
rules:
- name: log-dnp3-direct-operate
order: 10
condition: >
dnp3.IsDNP3() && dnp3.IsRequest() &&
dnp3.IsDirectOperate() && dnp3.HasCanonicalEndpoints()
action: log
log: true
meta:
policy: '"direct_operate_review"'
master: 'dnp3.Master()'
outstation: 'dnp3.Outstation()'
targets: 'dnp3.ControlTargetCount()'
fingerprint: 'dnp3.ControlFingerprint()'
Representative decision event:
{
"source": "rule_engine",
"event": "rule_match",
"rule": "log-dnp3-direct-operate",
"order": 10,
"frame_number": 1842,
"verdict": "accept",
"stop": true,
"src_ip": "10.20.1.10",
"dst_ip": "10.20.2.100",
"layers": ["Ethernet", "IPv4", "TCP", "DNP3"],
"meta": {
"policy": "direct_operate_review",
"master": 3,
"outstation": 100,
"targets": 1,
"fingerprint": "sha256:..."
}
}
The hash is a correlation identity, not a substitute for the human-readable metadata. Production logs should retain the device, phase, subject, result, and reason required by the customer's investigation process.
Evaluation model¶
- Conditions compile once when the rule file is loaded.
- Rules execute in ascending
order; equal values preserve file order. - A matching verdict rule stops by default.
stop: falsepermits later verdicts to override the current decision.- State actions always continue because they do not produce a forwarding verdict.
- No match defaults to
accept; a fail-closed policy must include an explicit final drop. - Runtime expression errors follow a configurable continue, accept, or drop policy.
Dynamic metadata¶
Up to 32 metadata expressions can be attached to a logged verdict rule. They are evaluated only after the rule matches and logging is enabled. This limits hot-path work while allowing customer-selected evidence such as device address, point, value, sequence, phase, policy identifier, and violation reason.
State-action logs have their own fixed schema. To attach dynamic metadata to a state
transition, follow it with a guarded log, accept, or drop rule.
Bounded and concurrent by design¶
- key length, value length, and total entries are bounded;
- expired-state cleanup has a per-operation budget;
- compare-and-transition is atomic across concurrent packets;
- numeric increment is atomic and detects overflow or configured ceilings;
- state statistics expose live entries, expiration, rejection, transition, and counter activity;
- optional timeout timers are bounded by the StateStore capacity.
Extensibility model¶
l2proxy-dissector
↓
scoped protocol model / semantic helper
↓
generic Rule Engine and StateStore
↓
customer YAML policy
New helpers extract and canonicalize protocol facts. They must not encode customer asset numbers, permitted operators, process transitions, or final verdicts. This keeps the engine reusable while allowing protocol-specific industrial precision.
Guided authoring instead of memorizing the API¶
Customers do not need to memorize field abbreviations, helper signatures, or valid enumerated values. The local Rule Authoring Workbench exposes the parser catalog, protocol-aware suggestions, helper documentation, industrial templates, lint feedback, and compilation against the same expression environment used by the Rule Engine.
This is especially valuable for dynamic meta: an engineer can discover a typed
metadata accessor, review its meaning and missing-value behavior, and validate the
expression before using it as structured evidence. See
Guided Rule Authoring.
Operational rollout¶
- Write the policy as
logand attach investigation metadata. - Validate against normal production captures and abnormal fixtures.
- Measure duplicate, retry, timeout, reconnect, and maintenance behavior.
- Review the policy with operations and control engineering.
- Convert only the proven violation branch to
drop. - Monitor rule, state, timeout, and capacity events after deployment.
For implementation details, see Writing Multi-State Rules.