Skip to content

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:

  1. Licensing — vendor charges extra for flow export (Fortinet FortiAnalyzer tier, Cisco DNA Premier, Palo Alto Panorama add-on)
  2. CPU/RAM headroom — entry-level DUTs measure CPU in single digits and can't sustain flow record generation alongside inspection load
  3. 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:2055 after 5 consecutive send errors
  • Source-bind oobi0 (OOBI overlay)
  • IP_TOS = 0xe0 (CS7, ADR 0038 — MGMT plane MÓDULO traffic)
  • NetFlowLocalOverride field bypasses slot resolution (lab dev, pattern from pkg/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_silent verdict (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