ADR 0040 — HyperBridge.Art NetFlow/IPFIX Synthesis¶
- Status: Accepted (2026-05-13)
- Date: 2026-05-13
- Deciders: TLSStress.Art project
- Targets: v4.x — HB-1.8 wave
- Builds on: ADR 0036 (HyperBridge.Art), ADR 0037 (
pkg/oobi/client/), ADR 0038 (DSCP marking), ADR 0024 (SPAN.Art), ADR 0034 (3-way fusion correlator)
Context¶
HyperBridge.Art (ADR 0036) was designed to compensate for one limitation of the operator's DUT — lack of a SPAN port. With HB-1.6/1.7 shipping end-to-end (capture → SPAN-1), we can now address a second, parallel limitation with the same packet copies:
Many DUTs lack NetFlow/IPFIX export capability, for one of three reasons:
- Licensing — vendor charges extra for flow export (Fortinet FortiAnalyzer tier, Cisco DNA Premier, Palo Alto Panorama add-on)
- CPU/RAM headroom — entry-level DUTs measure CPU in single digits and can't sustain flow record generation alongside inspection load
- Firmware constraints — older DUT firmware doesn't support modern IPFIX (v9 only, or no flow export at all)
Today, an operator with a flow-incapable DUT gets a 2-of-3 SPAN.Art
3-way fusion correlator (ADR 0034): wire + syslog, but no NetFlow.
The dut_silent verdict (the most damning DUT-effectiveness signal) is
unavailable to them.
Decision¶
Add a NetFlow/IPFIX synthesis capability to MÓDULO HyperBridge.Art.
When the operator enables it (Dashboard checkbox; EnableNetFlowExport
in BridgeConfig), HB synthesizes IPFIX records from its existing
capture stream and ships them to MÓDULO FLOW.Art over the OOBI fabric.
The MÓDULO is still HyperBridge.Art (no new slot). The Dashboard surfaces a checkbox "Generate NetFlow/IPFIX from captured traffic → FLOW.Art". From SPAN-3's perspective, the synthesized flow records are indistinguishable from real DUT-exported NetFlow.
Architecture¶
PortA ──► Linux bridge ──► PortB (line-rate forwarding)
│
└─► AF_PACKET capture (HB-1.7)
│
├─► forwarder.Inputs() → SPAN-1 (existing)
│
└─► netflow.Inputs() → IPFIX aggregator
(NEW, HB-1.8 — this ADR)
│
▼ flow cache (5-tuple keyed)
▼ idle + active timeout expiration
▼ IPFIX template + data records (RFC 7011)
▼ UDP send, source-bind oobi0
▼ DSCP CS7 (ADR 0038)
▼ primary slot .234 → standby .235 on failure
FLOW.Art (existing) → TSDB → SPAN-3 correlator
Protocol: IPFIX (RFC 7011), not NetFlow v5/v9¶
IPFIX is the modern Diffserv-aware standard, accepted by every modern collector (FLOW.Art listens for both per its module doc). v9 + IPFIX share the same template-then-data model; we pick IPFIX because: - Variable-length fields (better for future v6 extension) - Better tooling (Wireshark IPFIX dissector is more complete) - IETF-standardized vs Cisco-proprietary v9 details
Template fields (MVP — 11 fields)¶
| IE ID | Name | Length | Source |
|---|---|---|---|
| 8 | sourceIPv4Address | 4 | Tuple.SrcIP |
| 12 | destinationIPv4Address | 4 | Tuple.DstIP |
| 7 | sourceTransportPort | 2 | Tuple.SrcPort |
| 11 | destinationTransportPort | 2 | Tuple.DstPort |
| 4 | protocolIdentifier | 1 | 6=TCP, 17=UDP |
| 5 | ipClassOfService | 1 | 0 (MVP; track in v2) |
| 2 | packetDeltaCount | 8 | flow packet counter |
| 1 | octetDeltaCount | 8 | PayloadLength + 60 (header est) |
| 152 | flowStartMilliseconds | 8 | first-seen |
| 153 | flowEndMilliseconds | 8 | last-seen |
| 6 | tcpControlBits | 2 | 0 (MVP) |
IPv6 MVP gap: IPv6 packets are counted via IPv6FlowsSkipped but
not exported. v2 will add a parallel template with IE 27/28 (v6
addresses, 16 bytes each). Documented limitation.
Octet count precision: MVP uses PayloadLength + 60 (avg ethernet
14 + IP 20 + TCP 26 header overhead). v2 will track per-packet frame
length from gopacket.CaptureInfo.Length. Documented limitation.
Aggregation policy¶
| Knob | Default | Semantic |
|---|---|---|
| Flow key | 5-tuple | (srcIP, dstIP, srcPort, dstPort, proto) — bidirectional collapsed downstream |
| Idle timeout | 15s | Flow with no packets for N seconds → export + evict |
| Active timeout | 60s | Flow alive ≥ N seconds → export + counter reset (rolling cut) |
| Template interval | 30s | Re-send template every N seconds (collector resync) |
| Cache capacity | 65536 flows | Bounded; oldest evicted on cache-full |
| Datagram MTU | 1400 bytes | Safe under most underlays; batches multiple records per datagram |
These match Cisco's ip flow-cache timeout defaults — operators
familiar with NetFlow tuning don't need to relearn.
Transport¶
- UDP to canonical FLOW.Art slot
100.127.252.234:2055(primary) - Failover to
.235:2055after 5 consecutive send errors - Source-bind
oobi0(OOBI overlay) IP_TOS = 0xe0(CS7, ADR 0038 — MGMT plane MÓDULO traffic)NetFlowLocalOverridefield bypasses slot resolution (lab dev, pattern frompkg/oobi/client/)
Why UDP (not the HTTP-based pkg/oobi/client/)¶
NetFlow/IPFIX is UDP-native — every collector in the industry expects UDP. Wrapping IPFIX in HTTP would require a custom FLOW.Art ingester fork and break compatibility with operator's own NetFlow tooling (splunk, elastic, kafka NetFlow connectors) if they want to send our synthesized flow to their SIEM in parallel.
The reflector itself replicates the pkg/oobi/client/ setsockopt
contract (SO_BINDTODEVICE + IP_TOS=CS7) — the wire protocol differs
but the OOBI fabric routing + DSCP marking are identical.
Consequences¶
Pros¶
- Closes the 3-way fusion gap for DUTs without NetFlow capability.
dut_silentverdict (ADR 0034) is now reachable via synthesized data: wire YES + syslog NO + netflow NO (operator-DUT-original) → becomes wire YES + syslog NO + netflow YES (our synthesized) + internal_disagreement detection. - Composable with the existing FLOW.Art / SPAN-3 pipeline — no changes downstream. The IPFIX collector treats our records as if they came from a real DUT.
- Operator can still send our synthesized flow to their own SIEM by configuring an additional reflection target (out of MVP — same pattern as SyslogReflector.Art if extended).
- Opt-in — default
EnableNetFlowExport: false. Operators who have a real DUT NetFlow source don't get duplicates.
Cons / risks¶
- One-sided fidelity: synthesized records lack tcpControlBits, per-flow DSCP, application metadata that real DUT NetFlow includes. Operator sees a SPAN-3 verdict but loses some richness. Documented.
- IPv6 not supported in MVP — must be highlighted in Dashboard enable-checkbox; users with IPv6-heavy traffic should defer.
- Octet count is an estimate — for accurate byte-level reporting (regulatory billing scenarios), real DUT NetFlow is still required.
- Additional load on HB pod — 64K-flow cache + sweeper goroutine. Empirical measurement: ~+5% CPU, ~+30 MB RAM at 10 Gbps capture.
- Failover is best-effort — UDP send error detection is a heuristic (icmp-unreachable propagation varies per kernel). Operators who need reliable failover should keep both primary AND standby receiving and dedupe downstream.
Compatibility¶
- Backwards compatible — feature is opt-in. Existing HB deployments (HB-1.6/1.7) keep current behavior.
- No new MÓDULO — slot allocation unchanged. FLOW.Art canonical
slots
.234(primary) +.235(standby) are now declared in canon (Go + TS twin), but were already documented in the FLOW.Art module README. - Dashboard checkbox ships in a follow-up PR (UI tracked separately).
References¶
- Code:
pkg/hyperbridge-art/internal/netflow/ - Module doc:
docs/modules/flow-art.md - ADR 0036 — HyperBridge.Art design
- ADR 0037 —
pkg/oobi/client/service mesh (UDP wrapper deferred) - ADR 0038 — DSCP marking (CS7 applied on UDP send)
- ADR 0024 — SPAN.Art (parallel parallel pipeline)
- ADR 0034 — 3-way fusion correlator (the consumer of synthesized data)
- RFC 7011 — IPFIX protocol
- RFC 7012 — IPFIX information elements