A production-grade reference implementation of Deterministic Out-of-Band (OOB) Context Binding for legacy OT/ICS protocols (e.g., DNP3, Modbus TCP, IEC 61850).
📄 Whitepaper / Preprint: Read on Zenodo | DOI: 10.5281/zenodo.21731823
This mechanism solves the fundamental visibility gap in Industrial Control Systems: enabling end-to-end W3C Trace Context (OpenTelemetry) correlation across IT/OT boundaries without modifying binary OT network packets or violating protocol specifications.
In modern IT environments, W3C Trace Context headers (such as traceparent) are injected directly into HTTP/gRPC headers to trace requests across microservices.
However, in Industrial Control Systems (ICS / SCADA):
- No Header Fields: Protocols like DNP3 (IEEE 1815), Modbus TCP, and IEC 61850 GOOSE have rigid binary structures without extensible key-value header fields.
- Strict Payload Specifications: Mutating packet payloads breaks CRC/checksum verification and causes legacy Remote Terminal Units (RTUs) or IEDs to reject packets as malformed or drop connections.
- Loss of Causality: When an IT/HMI system issues a command, passive network monitoring tools see raw packets but cannot correlate them to the originating IT user session or APM trace ID.
Instead of mutating the binary protocol, Deterministic OOB Context Binding decouples context propagation into an Out-of-Band (OOB) control plane using a deterministic hash key lookup:
[ IT / HMI Application (SCADA) ]
│
├─────── 1. Pre-registers W3C Trace Context via Webdis REST API ──► [ OOB KV Store (Redis/Webdis) ]
│ Key: SHA256(src_ip + dst_ip + dst_port + function_code) │
│ TTL: Short-lived (5 seconds) │ 3. Non-blocking Atomic
│ │ CSV Sync & Reload
└─────── 2. Sends Unmodified Binary OT Packet (DNP3/Modbus) ──┐ ▼
│ [ Python Sidecar ] ──► [ Vector Router ]
▼ │ │
[ Passive Sensor / TAP ] │ 4. Emits Enriched
│ ▼ Fat Spans
└────────────────────────► [ OpenTelemetry / SOC ]
- Zero Payload Overhead (0.0% In-Band Mutation): Leaves raw DNP3/Modbus binary frames untouched. Guaranteed compatibility with legacy RTUs.
- Stateless Stream Processing & OOM Safety: Vector log router remains completely stateless. The Python Sidecar atomically syncs Redis keys into an in-memory CSV lookup table (
webdis_cache.csv) and triggers Vector's/enrichment_tables/webdis_table/reloadAPI without blocking the streaming engine. - Fat Spans for Security AI & SOC Analysis: Encapsulates raw Zeek logs, DNP3 function codes, network IPs, and pipeline processing delays (
processing_delay_ms) as OTLP span attributes for automated AI threat hunting.
oob-context-binding/
├── docker-compose.yml # Orchestrates Redis, Webdis, Vector, Sidecar, and SCADA Emulator
├── README.md # Project Documentation
├── LICENSE # MIT License
├── scada_emulator/
│ ├── send_dnp3_with_oob_trace.py # SCADA client: Pre-registers W3C trace & sends raw DNP3 frame
│ └── target_rtu.py # Mock DNP3 Outstation / RTU server
├── sidecar/
│ └── sync_redis_to_csv.py # Async sidecar: Syncs Webdis keys to CSV & reloads Vector table
├── vector_config/
│ ├── vector.toml # Vector VRL pipeline configuration for OOB context stitching
│ └── webdis_cache.csv # Shared CSV lookup table
└── docs/
└── evaluation.md # Quantitative benchmarking results & latency breakdowns
- Docker & Docker Compose V2
- (For eBPF Vanguard Mode): A native Linux host or Windows WSL2 with
/sys/kernel/btf/vmlinux.- Note: Running the full eBPF pipeline on Docker Desktop for Windows is now fully supported! By extracting the native BTF file from your WSL2 kernel and mounting it to the container, our custom CO-RE loader bypasses virtualized kernel constraints via the
CUSTOM_BTF_PATHenvironment variable.
- Note: Running the full eBPF pipeline on Docker Desktop for Windows is now fully supported! By extracting the native BTF file from your WSL2 kernel and mounting it to the container, our custom CO-RE loader bypasses virtualized kernel constraints via the
-
Clone the repository:
git clone https://github.com/schutzz/oob-context-binding.git cd oob-context-binding -
Launch the pipeline:
docker compose up --build
-
Observe the output:
scada_sender: Generates active W3Ctraceparent(00-{trace_id}-{span_id}-01), pre-registers it in Redis via Webdis withsha256("10.0.1.10:10.0.1.20:20000:5"), and sends an unmodified DNP3Direct Operatepacket.sidecar: Synchronizes the key towebdis_cache.csvand signals Vector.vector: Receives the packet log, computes the matching SHA256 key in VRL, stitches thetraceparent, and outputs the enrichedFat Span!
See docs/evaluation.md for full benchmarking details.
| Metric | Measured Value | Standard Target | Status |
|---|---|---|---|
| In-Band Overhead | 0.0% (0 bytes added) | 0 bytes | PERFECT |
| Trace Stitching Rate | 100.0% (up to 5,000 pps) | >99.9% | PASS |
| Pipeline Latency | ~0.41 ms | <1.0 ms | PASS |
| OOM Resilience | Stateless / No Memory Leak | Zero OOM | PASS |
This repository includes a fully automated CI/CD pipeline using GitHub Actions (.github/workflows/ebpf-full-ci.yml) to guarantee the reproducibility of the research results.
Anyone can easily reproduce the eBPF Vanguard Mode benchmarks without preparing a complex native Linux environment. Just fork this repository, and GitHub Actions will automatically spin up an Ubuntu 22.04 runner and execute the tests on every push!
- eBPF Compilation: Compiles the XDP program from scratch using
clangand a standalonebpftool. - Full Pipeline Boot: Starts Redis, Vector, Sidecar, and the eBPF module via
docker compose up --buildwith all necessary kernel capabilities (privileged: true,/sys/kernel/btf). - High-Speed Stress Test: Fires continuous DNP3 packets to rigorously test the stream processing engine.
- Zero Overhead & 100% Success Guarantee: The test script automatically asserts that the OOB context stitching succeeded perfectly (
Success Rate: 100%) and that the DNP3 payload was never modified (Overhead: 0 bytes). The CI build is strictly configured to fail if these conditions are not met.
If you use this project or architecture in your academic work, please cite our whitepaper/dataset:
@misc{oob_context_binding_2027,
author = {Daichi Terayama (schutzz)},
title = {Unbreaking the Kill Chain: OOB Deterministic Binding for Legacy OT Protocols},
year = 2026,
publisher = {Zenodo},
doi = {10.5281/zenodo.21731823},
url = {https://github.com/schutzz/oob-context-binding}
}This project is licensed under the MIT License.